]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/mockfs/mockfs_vnops.c
xnu-2782.10.72.tar.gz
[apple/xnu.git] / bsd / miscfs / mockfs / mockfs_vnops.c
1 /*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <miscfs/mockfs/mockfs.h>
30 #include <miscfs/mockfs/mockfs_vnops.h>
31 #include <sys/ubc.h>
32 #include <sys/mount_internal.h>
33 #include <sys/vnode_internal.h>
34 #include <vfs/vfs_support.h>
35
36 #include <libkern/libkern.h>
37 #include <kern/debug.h>
38
39 /*
40 * VOPFUNC macro; why do we have so many distinct definitions of this?
41 */
42 #define VOPFUNC int (*)(void *)
43
44 /*
45 * VNOP functions that mockfs implements. See xnu/bsd/sys/vnode_if.h for information on what
46 * each function does in generic terms.
47 */
48 int mockfs_lookup(struct vnop_lookup_args * ap);
49 int mockfs_getattr(struct vnop_getattr_args * ap);
50 int mockfs_read(struct vnop_read_args * ap);
51 int mockfs_strategy(struct vnop_strategy_args * ap);
52 int mockfs_pagein(struct vnop_pagein_args * ap);
53 int mockfs_reclaim(__unused struct vnop_reclaim_args * ap);
54 int mockfs_blockmap(struct vnop_blockmap_args * ap);
55
56 /*
57 * struct vnop_lookup_args {
58 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
59 * vnode_t a_dvp; // vnode for the directory we are performing the lookup in
60 * vnode_t *a_vpp; // Return parameter: the vnode we matched the lookup to
61 * struct componentname *a_cnp; // Description of the file we are looking for
62 * vfs_context_t a_context; // We don't care about this (for now)
63 * };
64 *
65 * mockfs_lookup:
66 * Given a vnode for a directory (a_dvp) and a file description (a_cnp), looks for a file matching
67 * the description in the directory, and give a vnode with an iocount for the file (*a_vpp), if the
68 * file was found. For mockfs, because we realistically have 3 vnodes, the filesystem information
69 * is extremely sparse, so the details on naming are all implemented in mockfs_lookup; the generic VFS
70 * information is enough for us to distinguish between all 3 files. Any lookup not done in the root
71 * vnode fails, by definition. Each vnode has the following names in relation to the root vnode:
72 *
73 * The root vnode:
74 * "sbin"
75 *
76 * The devfs vnode:
77 * "dev"
78 *
79 * The executable vnode
80 * "launchd"
81 *
82 * Returns 0 on success, or an error.
83 */
84 int mockfs_lookup(struct vnop_lookup_args * ap)
85 {
86 char held_char;
87 int rvalue;
88 int op;
89 mockfs_fsnode_t fsnode;
90 mockfs_fsnode_t target_fsnode;
91 vnode_t dvp;
92 vnode_t * vpp;
93 vfs_context_t ctx;
94 struct componentname * cnp;
95
96 rvalue = 0;
97 dvp = ap->a_dvp;
98 vpp = ap->a_vpp;
99 cnp = ap->a_cnp;
100 ctx = ap->a_context;
101 op = cnp->cn_nameiop;
102 fsnode = (mockfs_fsnode_t) dvp->v_data;
103 target_fsnode = NULL;
104
105 if ((op == LOOKUP) && (fsnode->type == MOCKFS_ROOT)) {
106 /*
107 * Okay, we're looking in the root directory, so we aren't necessarily
108 * going to fail. What are we looking for?
109 */
110
111 held_char = cnp->cn_nameptr[cnp->cn_namelen];
112 cnp->cn_nameptr[cnp->cn_namelen] = '\0';
113
114 /*
115 * We'll resolve sbin to /, and launchd to the executable for the moment, so that I don't
116 * accidentally commit a change to the init_process pathname. We map from name to node type
117 * here, as mockfs doesn't current use names; just unique types.
118 */
119 if (!strncmp(cnp->cn_nameptr, "sbin", 5))
120 target_fsnode = fsnode;
121 else if (!strncmp(cnp->cn_nameptr, "dev", 4))
122 mockfs_fsnode_child_by_type(fsnode, MOCKFS_DEV, &target_fsnode);
123 else if (!strncmp(cnp->cn_nameptr, "launchd", 8))
124 mockfs_fsnode_child_by_type(fsnode, MOCKFS_FILE, &target_fsnode);
125 else
126 rvalue = ENOENT;
127
128 cnp->cn_nameptr[cnp->cn_namelen] = held_char;
129
130 if (target_fsnode)
131 rvalue = mockfs_fsnode_vnode(target_fsnode, vpp);
132 }
133 else {
134 /*
135 * We aren't looking in root; the query may actually be reasonable, but we're not
136 * going to support it.
137 */
138 rvalue = ENOENT;
139 }
140
141 return rvalue;
142 }
143
144 /*
145 * struct vnop_getattr_args {
146 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
147 * vnode_t a_vp; // Pointer to the vnode we are interested in
148 * struct vnode_attr *a_vap; // Details the requested attributes, and used to return attributes
149 * vfs_context_t a_context; // We don't care about this (for now)
150 * };
151 *
152 * mockfs_getattr:
153 * Given a vnode (a_vp), returns the attributes requested for that vnode (*a_vap). For mockfs, we don't care
154 * about the majority of attributes (we are not a fully featured filesystem). We will return a minimal set of
155 * attributes for any request, regardless of which attributes were requested, to ensure that we look like a sane
156 * file, and so that permissions are set appropriately to allow execution of the executable vnode.
157 *
158 * Returns 0 on success, or an error.
159 */
160 int mockfs_getattr(struct vnop_getattr_args * ap)
161 {
162 /*
163 * For the moment, we don't actually care about most attributes. We'll
164 * deal with actually managing attributes as part of the general cleanup.
165 */
166 vnode_t vp;
167 mockfs_fsnode_t fsnode;
168 struct vnode_attr * vap;
169
170 vp = ap->a_vp;
171 fsnode = (mockfs_fsnode_t)vp->v_data;
172 vap = ap->a_vap;
173 bzero(vap, sizeof(*vap));
174 VATTR_RETURN(vap, va_nlink, 1); /* Simply assert that someone has at least one link to us */
175 VATTR_RETURN(vap, va_mode, VREAD | VWRITE | VEXEC);
176 VATTR_RETURN(vap, va_fileid, fsnode->type);
177 VATTR_RETURN(vap, va_total_size, fsnode->size);
178 VATTR_RETURN(vap, va_total_alloc, fsnode->size);
179 VATTR_RETURN(vap, va_data_size, fsnode->size);
180 VATTR_RETURN(vap, va_data_alloc, fsnode->size);
181
182 return (0);
183 }
184
185 /*
186 * struct vnop_read_args {
187 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
188 * vnode_t a_vp; // Pointer to the vnode we are interested in
189 * struct uio *a_uio; // Description of the request
190 * int a_ioflag; // IO flags (we don't care about these)
191 * vfs_context_t a_context; // We don't care about this (for now)
192 * };
193 *
194 * mockfs_read:
195 * Given a vnode (a_vp), a set of flags (a_ioflag), and a description of a read request (a_uio), executes the read
196 * request and returns the resulting data through the description (a_uio). mockfs has very little to do here; we
197 * merely mandate that any read attempt MUST be on VREG (our MOCKFS_FILE object), as it is the only vnode that has
198 * a backing store that can support a read (the other node types being purely in-memory hacks). Because we do not
199 * support VNOP_OPEN, we can probably assume that the kernel is the only entity that will ever issue a VNOP_READ
200 * (as part of the exec path) to a mockfs vnode.
201 *
202 * Returns 0 on success, or an error.
203 */
204 int mockfs_read(struct vnop_read_args * ap)
205 {
206 int rvalue;
207 vnode_t vp;
208 mockfs_fsnode_t fsnode;
209
210 vp = ap->a_vp;
211 fsnode = (mockfs_fsnode_t) vp->v_data;
212
213 /*
214 * We're just an ugly frontend for the devnode, so we shouldn't need to do much for reads;
215 * pass the work to cluster_read.
216 */
217 if (vp->v_type == VREG) {
218 rvalue = cluster_read(vp, ap->a_uio, fsnode->size, ap->a_ioflag);
219 }
220 else {
221 /*
222 * You've tried to read from a nonregular file; I hate you.
223 */
224 rvalue = ENOTSUP;
225 }
226
227 return rvalue;
228 }
229
230 /*
231 * struct vnop_reclaim_args {
232 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
233 * vnode_t a_vp; // Pointer to the vnode we are reclaiming
234 * vfs_context_t a_context; // We don't care about this (for now)
235 * };
236 *
237 * mockfs_reclaim:
238 * Given a vnode (a_vp), performs any cleanup needed to allow VFS to reclaim the vnode. Because the mockfs tree
239 * is always in memory, we have very little to do as part of reclaim, so we'll just zero a few pointers and let
240 * VFS reclaim the vnode.
241 */
242 int mockfs_reclaim(struct vnop_reclaim_args * ap)
243 {
244 int rvalue;
245 vnode_t vp;
246 mockfs_fsnode_t fsnode;
247
248 vp = ap->a_vp;
249 fsnode = (mockfs_fsnode_t) vnode_fsnode(vp);
250 rvalue = mockfs_fsnode_drop_vnode(fsnode);
251
252 return rvalue;
253 }
254
255 /*
256 * struct vnop_strategy_args {
257 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
258 * struct buf *a_bp; // Description of the desired IO
259 * };
260 *
261 * mockfs_strategy:
262 * Given an IO description (a_bp), does any preparations required by the filesystem, and then passes the IO off to
263 * the appropriate device. mockfs doesn't need to do anything to prepare for the IO, so we simply pass it off to
264 * our backing device.
265 *
266 * Returns 0 on success, or an error.
267 */
268 int mockfs_strategy(struct vnop_strategy_args * ap)
269 {
270 int rvalue;
271 vnode_t dvp;
272
273 /*
274 * We'll avoid checking for a memory-backed device here; we already do this for blockmap, which will be
275 * called as part of the IO path.
276 */
277
278 dvp = vfs_devvp(buf_vnode(ap->a_bp)->v_mount);
279
280 if (dvp) {
281 rvalue = buf_strategy(dvp, ap);
282 vnode_put(dvp);
283 }
284 else {
285 /*
286 * I'm not certain this is the BEST error to return for this case.
287 */
288 rvalue = EIO;
289 }
290
291 return rvalue;
292 }
293
294 /*
295 * struct vnop_pagein_args {
296 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
297 * vnode_t a_vp; // Pointer to the vnode we are interested in
298 * upl_t a_pl; // Describes the pages that need to be paged in
299 * upl_offset_t a_pl_offset; // Offset in the UPL to start placing data at
300 * off_t a_f_offset; // File offset to begin paging in at
301 * size_t a_size; // Bytes of data to page in
302 * int a_flags; // UPL flags (we don't care about these)
303 * vfs_context_t a_context; // We don't care about this (for now)
304 * };
305 *
306 * mockfs_pagegin:
307 * Given a vnode (a_vp), and a region, described by an offset (a_f_offset) and a size (a_size), pages the region
308 * into the given UPL (a_pl), starting at the UPL offset (a_pl_offset). For mockfs, we don't have anything significant
309 * to do for pagein, so we largely serve as a wrapper to the cluster_pagein routine.
310 *
311 * Returns 0 on success, or an error.
312 */
313 int mockfs_pagein(struct vnop_pagein_args * ap)
314 {
315 mockfs_fsnode_t fsnode;
316 mockfs_mount_t mockfs_mnt;
317
318 /*
319 * Nothing special needed from us; just nab the filesize and kick the work over to cluster_pagein.
320 */
321 fsnode = (mockfs_fsnode_t) ap->a_vp->v_data;
322 mockfs_mnt = ((mockfs_mount_t) fsnode->mnt->mnt_data);
323
324 /*
325 * If we represent a memory backed device, we should be pointing directly to the backing store; we should never
326 * see a pagein in this case.
327 */
328 if (mockfs_mnt->mockfs_memory_backed)
329 panic("mockfs_pagein called for a memory-backed device");
330
331 return cluster_pagein(ap->a_vp, ap->a_pl, ap->a_pl_offset, ap->a_f_offset, ap->a_size, fsnode->size, ap->a_flags);
332 }
333
334 /*
335 * struct vnop_blockmap_args {
336 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
337 * vnode_t a_vp; // Pointer to the vnode we are interested in
338 * off_t a_foffset; // File offset we are interested in
339 * size_t a_size; // Size of the region we are interested in
340 * daddr64_t *a_bpn; // Return parameter: physical block number the region we are interest in starts at
341 * size_t *a_run; // Return parameter: number of contiguous bytes of data
342 * void *a_poff; // Unused, as far as I know
343 * int a_flags; // Used to distinguish reads and writes; we don't care
344 * vfs_context_t a_context; // We don't care about this (for now)
345 * };
346 *
347 * mockfs_blockmap:
348 * Given a vnode (a_vp), and a region, described by an offset (a_foffset), and a size (a_size), tells the caller
349 * which physical block (on the backing device) the region begins at (*a_bpn), and how many bytes can be read
350 * before the first discontinuity (*a_run). For mockfs, because only VREG files are eligible for IO, and because
351 * all VREG files are simply a frontend for the backing device, this mapping will always be one to one, and all we
352 * need to do is convert the physical offset to the physical block number.
353 *
354 * Returns 0 on success, or an error.
355 */
356 int mockfs_blockmap(struct vnop_blockmap_args * ap)
357 {
358 int rvalue;
359 off_t foffset;
360 size_t * run;
361 uint32_t blksize;
362 daddr64_t * bpn;
363 vnode_t vp;
364 mockfs_fsnode_t fsnode;
365
366 rvalue = 0;
367 foffset = ap->a_foffset;
368 run = ap->a_run;
369 bpn = ap->a_bpn;
370 vp = ap->a_vp;
371 fsnode = (mockfs_fsnode_t) vp->v_data;
372 blksize = vp->v_mount->mnt_devblocksize;
373
374 /*
375 * If we represent a memory backed device, we should be pointing directly to the backing store; all IO should
376 * be satisfied from the UBC, and any called to blockmap (inidicating an attempted IO to the backing store)
377 * is therefore disallowed.
378 */
379 if (((mockfs_mount_t) fsnode->mnt->mnt_data)->mockfs_memory_backed)
380 printf("mockfs_blockmap called for a memory-backed device\n");
381
382 /*
383 * This will ultimately be simple; the vnode must be VREG (init), and the mapping will be 1 to 1.
384 * This also means that their request should always be contiguous, so the run calculation is easy!
385 */
386 if (vp->v_type == VREG) {
387 *bpn = foffset / blksize;
388 *run = fsnode->size - foffset;
389
390 if (ap->a_size > *run) {
391 /* We've been asked for more data than the backing device can provide; we're done. */
392 panic("mockfs_blockmap was asked for a region that extended past the end of the backing device");
393 }
394 }
395 else {
396 rvalue = ENOTSUP;
397 }
398
399 return rvalue;
400 }
401
402 int (**mockfs_vnodeop_p)(void *);
403 struct vnodeopv_entry_desc mockfs_vnodeop_entries[] = {
404 { &vnop_default_desc, (VOPFUNC) vn_default_error }, /* default */
405 { &vnop_lookup_desc, (VOPFUNC) mockfs_lookup }, /* lookup */
406 { &vnop_create_desc, (VOPFUNC) err_create },/* create */
407 { &vnop_open_desc, (VOPFUNC) err_open }, /* open */
408 { &vnop_mknod_desc, (VOPFUNC) err_mknod }, /* mknod */
409 { &vnop_close_desc, (VOPFUNC) err_close }, /* close */
410 { &vnop_access_desc, (VOPFUNC) err_access }, /* access */
411 { &vnop_getattr_desc, (VOPFUNC) mockfs_getattr }, /* getattr */
412 { &vnop_setattr_desc, (VOPFUNC) err_setattr }, /* setattr */
413 { &vnop_read_desc, (VOPFUNC) mockfs_read }, /* read */
414 { &vnop_write_desc, (VOPFUNC) err_write }, /* write */
415 { &vnop_ioctl_desc, (VOPFUNC) err_ioctl }, /* ioctl */
416 { &vnop_select_desc, (VOPFUNC) err_select }, /* select */
417 { &vnop_mmap_desc, (VOPFUNC) err_mmap }, /* mmap */
418 { &vnop_fsync_desc, (VOPFUNC) nop_fsync }, /* fsync */
419 { &vnop_remove_desc, (VOPFUNC) err_remove }, /* remove */
420 { &vnop_link_desc, (VOPFUNC) err_link }, /* link */
421 { &vnop_rename_desc, (VOPFUNC) err_rename }, /* rename */
422 { &vnop_mkdir_desc, (VOPFUNC) err_mkdir }, /* mkdir */
423 { &vnop_rmdir_desc, (VOPFUNC) err_rmdir }, /* rmdir */
424 { &vnop_symlink_desc, (VOPFUNC) err_symlink }, /* symlink */
425 { &vnop_readdir_desc, (VOPFUNC) err_readdir }, /* readdir */
426 { &vnop_readlink_desc, (VOPFUNC) err_readlink }, /* readlink */
427 { &vnop_inactive_desc, (VOPFUNC) err_inactive }, /* inactive */
428 { &vnop_reclaim_desc, (VOPFUNC) mockfs_reclaim }, /* reclaim */
429 { &vnop_strategy_desc, (VOPFUNC) mockfs_strategy }, /* strategy */
430 { &vnop_pathconf_desc, (VOPFUNC) err_pathconf }, /* pathconf */
431 { &vnop_advlock_desc, (VOPFUNC) err_advlock }, /* advlock */
432 { &vnop_bwrite_desc, (VOPFUNC) err_bwrite }, /* bwrite */
433 { &vnop_pagein_desc, (VOPFUNC) mockfs_pagein }, /* pagein */
434 { &vnop_pageout_desc, (VOPFUNC) err_pageout }, /* pageout */
435 { &vnop_copyfile_desc, (VOPFUNC) err_copyfile }, /* copyfile */
436 { &vnop_blktooff_desc, (VOPFUNC) err_blktooff }, /* blktooff */
437 { &vnop_offtoblk_desc, (VOPFUNC) err_offtoblk }, /* offtoblk */
438 { &vnop_blockmap_desc, (VOPFUNC) mockfs_blockmap }, /* blockmap */
439 { (struct vnodeop_desc *) NULL, (VOPFUNC) NULL }
440 };
441
442 struct vnodeopv_desc mockfs_vnodeop_opv_desc = {
443 &mockfs_vnodeop_p,
444 mockfs_vnodeop_entries
445 };
446