2 * Copyright (c) 2019 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <miscfs/mockfs/mockfs.h>
30 #include <miscfs/mockfs/mockfs_vnops.h>
32 #include <sys/mount_internal.h>
33 #include <sys/vnode_internal.h>
34 #include <vfs/vfs_support.h>
36 #include <libkern/libkern.h>
37 #include <kern/debug.h>
40 * VOPFUNC macro; why do we have so many distinct definitions of this?
42 #define VOPFUNC int (*)(void *)
45 * VNOP functions that mockfs implements. See xnu/bsd/sys/vnode_if.h for information on what
46 * each function does in generic terms.
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
);
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)
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:
79 * The executable vnode
82 * Returns 0 on success, or an error.
85 mockfs_lookup(struct vnop_lookup_args
* ap
)
90 mockfs_fsnode_t fsnode
;
91 mockfs_fsnode_t target_fsnode
;
95 struct componentname
* cnp
;
102 op
= cnp
->cn_nameiop
;
103 fsnode
= (mockfs_fsnode_t
) dvp
->v_data
;
104 target_fsnode
= NULL
;
106 if ((op
== LOOKUP
) && (fsnode
->type
== MOCKFS_ROOT
)) {
108 * Okay, we're looking in the root directory, so we aren't necessarily
109 * going to fail. What are we looking for?
112 held_char
= cnp
->cn_nameptr
[cnp
->cn_namelen
];
113 cnp
->cn_nameptr
[cnp
->cn_namelen
] = '\0';
116 * We'll resolve sbin to /, and launchd to the executable for the moment, so that I don't
117 * accidentally commit a change to the init_process pathname. We map from name to node type
118 * here, as mockfs doesn't current use names; just unique types.
120 if (!strncmp(cnp
->cn_nameptr
, "sbin", 5)) {
121 target_fsnode
= fsnode
;
122 } else if (!strncmp(cnp
->cn_nameptr
, "dev", 4)) {
123 mockfs_fsnode_child_by_type(fsnode
, MOCKFS_DEV
, &target_fsnode
);
124 } else if (!strncmp(cnp
->cn_nameptr
, "launchd", 8)) {
125 mockfs_fsnode_child_by_type(fsnode
, MOCKFS_FILE
, &target_fsnode
);
130 cnp
->cn_nameptr
[cnp
->cn_namelen
] = held_char
;
133 rvalue
= mockfs_fsnode_vnode(target_fsnode
, vpp
);
137 * We aren't looking in root; the query may actually be reasonable, but we're not
138 * going to support it.
147 * struct vnop_getattr_args {
148 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
149 * vnode_t a_vp; // Pointer to the vnode we are interested in
150 * struct vnode_attr *a_vap; // Details the requested attributes, and used to return attributes
151 * vfs_context_t a_context; // We don't care about this (for now)
155 * Given a vnode (a_vp), returns the attributes requested for that vnode (*a_vap). For mockfs, we don't care
156 * about the majority of attributes (we are not a fully featured filesystem). We will return a minimal set of
157 * attributes for any request, regardless of which attributes were requested, to ensure that we look like a sane
158 * file, and so that permissions are set appropriately to allow execution of the executable vnode.
160 * Returns 0 on success, or an error.
163 mockfs_getattr(struct vnop_getattr_args
* ap
)
166 * For the moment, we don't actually care about most attributes. We'll
167 * deal with actually managing attributes as part of the general cleanup.
170 mockfs_fsnode_t fsnode
;
171 struct vnode_attr
* vap
;
174 fsnode
= (mockfs_fsnode_t
)vp
->v_data
;
176 bzero(vap
, sizeof(*vap
));
177 VATTR_RETURN(vap
, va_nlink
, 1); /* Simply assert that someone has at least one link to us */
178 VATTR_RETURN(vap
, va_mode
, VREAD
| VWRITE
| VEXEC
);
179 VATTR_RETURN(vap
, va_fileid
, fsnode
->type
);
180 VATTR_RETURN(vap
, va_total_size
, fsnode
->size
);
181 VATTR_RETURN(vap
, va_total_alloc
, fsnode
->size
);
182 VATTR_RETURN(vap
, va_data_size
, fsnode
->size
);
183 VATTR_RETURN(vap
, va_data_alloc
, fsnode
->size
);
189 * struct vnop_read_args {
190 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
191 * vnode_t a_vp; // Pointer to the vnode we are interested in
192 * struct uio *a_uio; // Description of the request
193 * int a_ioflag; // IO flags (we don't care about these)
194 * vfs_context_t a_context; // We don't care about this (for now)
198 * Given a vnode (a_vp), a set of flags (a_ioflag), and a description of a read request (a_uio), executes the read
199 * request and returns the resulting data through the description (a_uio). mockfs has very little to do here; we
200 * merely mandate that any read attempt MUST be on VREG (our MOCKFS_FILE object), as it is the only vnode that has
201 * a backing store that can support a read (the other node types being purely in-memory hacks). Because we do not
202 * support VNOP_OPEN, we can probably assume that the kernel is the only entity that will ever issue a VNOP_READ
203 * (as part of the exec path) to a mockfs vnode.
205 * Returns 0 on success, or an error.
208 mockfs_read(struct vnop_read_args
* ap
)
212 mockfs_fsnode_t fsnode
;
215 fsnode
= (mockfs_fsnode_t
) vp
->v_data
;
218 * We're just an ugly frontend for the devnode, so we shouldn't need to do much for reads;
219 * pass the work to cluster_read.
221 if (vp
->v_type
== VREG
) {
222 rvalue
= cluster_read(vp
, ap
->a_uio
, fsnode
->size
, ap
->a_ioflag
);
225 * You've tried to read from a nonregular file; I hate you.
234 * struct vnop_reclaim_args {
235 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
236 * vnode_t a_vp; // Pointer to the vnode we are reclaiming
237 * vfs_context_t a_context; // We don't care about this (for now)
241 * Given a vnode (a_vp), performs any cleanup needed to allow VFS to reclaim the vnode. Because the mockfs tree
242 * is always in memory, we have very little to do as part of reclaim, so we'll just zero a few pointers and let
243 * VFS reclaim the vnode.
246 mockfs_reclaim(struct vnop_reclaim_args
* ap
)
250 mockfs_fsnode_t fsnode
;
253 fsnode
= (mockfs_fsnode_t
) vnode_fsnode(vp
);
254 rvalue
= mockfs_fsnode_drop_vnode(fsnode
);
260 * struct vnop_strategy_args {
261 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
262 * struct buf *a_bp; // Description of the desired IO
266 * Given an IO description (a_bp), does any preparations required by the filesystem, and then passes the IO off to
267 * the appropriate device. mockfs doesn't need to do anything to prepare for the IO, so we simply pass it off to
268 * our backing device.
270 * Returns 0 on success, or an error.
273 mockfs_strategy(struct vnop_strategy_args
* ap
)
279 * We'll avoid checking for a memory-backed device here; we already do this for blockmap, which will be
280 * called as part of the IO path.
283 dvp
= vfs_devvp(buf_vnode(ap
->a_bp
)->v_mount
);
286 rvalue
= buf_strategy(dvp
, ap
);
290 * I'm not certain this is the BEST error to return for this case.
299 * struct vnop_pagein_args {
300 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
301 * vnode_t a_vp; // Pointer to the vnode we are interested in
302 * upl_t a_pl; // Describes the pages that need to be paged in
303 * upl_offset_t a_pl_offset; // Offset in the UPL to start placing data at
304 * off_t a_f_offset; // File offset to begin paging in at
305 * size_t a_size; // Bytes of data to page in
306 * int a_flags; // UPL flags (we don't care about these)
307 * vfs_context_t a_context; // We don't care about this (for now)
311 * Given a vnode (a_vp), and a region, described by an offset (a_f_offset) and a size (a_size), pages the region
312 * into the given UPL (a_pl), starting at the UPL offset (a_pl_offset). For mockfs, we don't have anything significant
313 * to do for pagein, so we largely serve as a wrapper to the cluster_pagein routine.
315 * Returns 0 on success, or an error.
318 mockfs_pagein(struct vnop_pagein_args
* ap
)
320 mockfs_fsnode_t fsnode
;
321 mockfs_mount_t mockfs_mnt
;
324 * Nothing special needed from us; just nab the filesize and kick the work over to cluster_pagein.
326 fsnode
= (mockfs_fsnode_t
) ap
->a_vp
->v_data
;
327 mockfs_mnt
= ((mockfs_mount_t
) fsnode
->mnt
->mnt_data
);
330 * If we represent a memory backed device, we should be pointing directly to the backing store; we should never
331 * see a pagein in this case.
333 if (mockfs_mnt
->mockfs_memory_backed
) {
334 panic("mockfs_pagein called for a memory-backed device");
337 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
);
341 * struct vnop_blockmap_args {
342 * struct vnodeop_desc *a_desc; // We don't care about this (for now)
343 * vnode_t a_vp; // Pointer to the vnode we are interested in
344 * off_t a_foffset; // File offset we are interested in
345 * size_t a_size; // Size of the region we are interested in
346 * daddr64_t *a_bpn; // Return parameter: physical block number the region we are interest in starts at
347 * size_t *a_run; // Return parameter: number of contiguous bytes of data
348 * void *a_poff; // Unused, as far as I know
349 * int a_flags; // Used to distinguish reads and writes; we don't care
350 * vfs_context_t a_context; // We don't care about this (for now)
354 * Given a vnode (a_vp), and a region, described by an offset (a_foffset), and a size (a_size), tells the caller
355 * which physical block (on the backing device) the region begins at (*a_bpn), and how many bytes can be read
356 * before the first discontinuity (*a_run). For mockfs, because only VREG files are eligible for IO, and because
357 * all VREG files are simply a frontend for the backing device, this mapping will always be one to one, and all we
358 * need to do is convert the physical offset to the physical block number.
360 * Returns 0 on success, or an error.
363 mockfs_blockmap(struct vnop_blockmap_args
* ap
)
371 mockfs_fsnode_t fsnode
;
374 foffset
= ap
->a_foffset
;
378 fsnode
= (mockfs_fsnode_t
) vp
->v_data
;
379 blksize
= vp
->v_mount
->mnt_devblocksize
;
382 * If we represent a memory backed device, we should be pointing directly to the backing store; all IO should
383 * be satisfied from the UBC, and any called to blockmap (inidicating an attempted IO to the backing store)
384 * is therefore disallowed.
386 if (((mockfs_mount_t
) fsnode
->mnt
->mnt_data
)->mockfs_memory_backed
) {
387 printf("mockfs_blockmap called for a memory-backed device\n");
391 * This will ultimately be simple; the vnode must be VREG (init), and the mapping will be 1 to 1.
392 * This also means that their request should always be contiguous, so the run calculation is easy!
394 if (vp
->v_type
== VREG
) {
395 *bpn
= foffset
/ blksize
;
396 *run
= fsnode
->size
- foffset
;
398 if (ap
->a_size
> *run
) {
399 /* We've been asked for more data than the backing device can provide; we're done. */
400 panic("mockfs_blockmap was asked for a region that extended past the end of the backing device");
409 int(**mockfs_vnodeop_p
)(void *);
410 const struct vnodeopv_entry_desc mockfs_vnodeop_entries
[] = {
411 { .opve_op
= &vnop_default_desc
, .opve_impl
= (VOPFUNC
) vn_default_error
}, /* default */
412 { .opve_op
= &vnop_lookup_desc
, .opve_impl
= (VOPFUNC
) mockfs_lookup
}, /* lookup */
413 { .opve_op
= &vnop_create_desc
, .opve_impl
= (VOPFUNC
) err_create
},/* create */
414 { .opve_op
= &vnop_open_desc
, .opve_impl
= (VOPFUNC
) err_open
}, /* open */
415 { .opve_op
= &vnop_mknod_desc
, .opve_impl
= (VOPFUNC
) err_mknod
}, /* mknod */
416 { .opve_op
= &vnop_close_desc
, .opve_impl
= (VOPFUNC
) err_close
}, /* close */
417 { .opve_op
= &vnop_access_desc
, .opve_impl
= (VOPFUNC
) err_access
}, /* access */
418 { .opve_op
= &vnop_getattr_desc
, .opve_impl
= (VOPFUNC
) mockfs_getattr
}, /* getattr */
419 { .opve_op
= &vnop_setattr_desc
, .opve_impl
= (VOPFUNC
) err_setattr
}, /* setattr */
420 { .opve_op
= &vnop_read_desc
, .opve_impl
= (VOPFUNC
) mockfs_read
}, /* read */
421 { .opve_op
= &vnop_write_desc
, .opve_impl
= (VOPFUNC
) err_write
}, /* write */
422 { .opve_op
= &vnop_ioctl_desc
, .opve_impl
= (VOPFUNC
) err_ioctl
}, /* ioctl */
423 { .opve_op
= &vnop_select_desc
, .opve_impl
= (VOPFUNC
) err_select
}, /* select */
424 { .opve_op
= &vnop_mmap_desc
, .opve_impl
= (VOPFUNC
) err_mmap
}, /* mmap */
425 { .opve_op
= &vnop_fsync_desc
, .opve_impl
= (VOPFUNC
) nop_fsync
}, /* fsync */
426 { .opve_op
= &vnop_remove_desc
, .opve_impl
= (VOPFUNC
) err_remove
}, /* remove */
427 { .opve_op
= &vnop_link_desc
, .opve_impl
= (VOPFUNC
) err_link
}, /* link */
428 { .opve_op
= &vnop_rename_desc
, .opve_impl
= (VOPFUNC
) err_rename
}, /* rename */
429 { .opve_op
= &vnop_mkdir_desc
, .opve_impl
= (VOPFUNC
) err_mkdir
}, /* mkdir */
430 { .opve_op
= &vnop_rmdir_desc
, .opve_impl
= (VOPFUNC
) err_rmdir
}, /* rmdir */
431 { .opve_op
= &vnop_symlink_desc
, .opve_impl
= (VOPFUNC
) err_symlink
}, /* symlink */
432 { .opve_op
= &vnop_readdir_desc
, .opve_impl
= (VOPFUNC
) err_readdir
}, /* readdir */
433 { .opve_op
= &vnop_readlink_desc
, .opve_impl
= (VOPFUNC
) err_readlink
}, /* readlink */
434 { .opve_op
= &vnop_inactive_desc
, .opve_impl
= (VOPFUNC
) err_inactive
}, /* inactive */
435 { .opve_op
= &vnop_reclaim_desc
, .opve_impl
= (VOPFUNC
) mockfs_reclaim
}, /* reclaim */
436 { .opve_op
= &vnop_strategy_desc
, .opve_impl
= (VOPFUNC
) mockfs_strategy
}, /* strategy */
437 { .opve_op
= &vnop_pathconf_desc
, .opve_impl
= (VOPFUNC
) err_pathconf
}, /* pathconf */
438 { .opve_op
= &vnop_advlock_desc
, .opve_impl
= (VOPFUNC
) err_advlock
}, /* advlock */
439 { .opve_op
= &vnop_bwrite_desc
, .opve_impl
= (VOPFUNC
) err_bwrite
}, /* bwrite */
440 { .opve_op
= &vnop_pagein_desc
, .opve_impl
= (VOPFUNC
) mockfs_pagein
}, /* pagein */
441 { .opve_op
= &vnop_pageout_desc
, .opve_impl
= (VOPFUNC
) err_pageout
}, /* pageout */
442 { .opve_op
= &vnop_copyfile_desc
, .opve_impl
= (VOPFUNC
) err_copyfile
}, /* copyfile */
443 { .opve_op
= &vnop_blktooff_desc
, .opve_impl
= (VOPFUNC
) err_blktooff
}, /* blktooff */
444 { .opve_op
= &vnop_offtoblk_desc
, .opve_impl
= (VOPFUNC
) err_offtoblk
}, /* offtoblk */
445 { .opve_op
= &vnop_blockmap_desc
, .opve_impl
= (VOPFUNC
) mockfs_blockmap
}, /* blockmap */
446 { .opve_op
= (struct vnodeop_desc
*) NULL
, .opve_impl
= (VOPFUNC
) NULL
}
449 const struct vnodeopv_desc mockfs_vnodeop_opv_desc
= {
450 .opv_desc_vector_p
= &mockfs_vnodeop_p
,
451 .opv_desc_ops
= mockfs_vnodeop_entries