xref: /xnu-10002.1.13/bsd/miscfs/mockfs/mockfs_vnops.c (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
1 /*
2  * Copyright (c) 2019 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 mockfs_lookup(struct vnop_lookup_args * ap)
86 {
87 	char held_char;
88 	int rvalue;
89 	int op;
90 	mockfs_fsnode_t fsnode;
91 	mockfs_fsnode_t target_fsnode;
92 	vnode_t dvp;
93 	vnode_t * vpp;
94 	vfs_context_t ctx;
95 	struct componentname * cnp;
96 
97 	rvalue = 0;
98 	dvp = ap->a_dvp;
99 	vpp = ap->a_vpp;
100 	cnp = ap->a_cnp;
101 	ctx = ap->a_context;
102 	op = cnp->cn_nameiop;
103 	fsnode = (mockfs_fsnode_t) dvp->v_data;
104 	target_fsnode = NULL;
105 
106 	if ((op == LOOKUP) && (fsnode->type == MOCKFS_ROOT)) {
107 		/*
108 		 * Okay, we're looking in the root directory, so we aren't necessarily
109 		 *   going to fail.  What are we looking for?
110 		 */
111 
112 		held_char = cnp->cn_nameptr[cnp->cn_namelen];
113 		cnp->cn_nameptr[cnp->cn_namelen] = '\0';
114 
115 		/*
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.
119 		 */
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);
126 		} else {
127 			rvalue = ENOENT;
128 		}
129 
130 		cnp->cn_nameptr[cnp->cn_namelen] = held_char;
131 
132 		if (target_fsnode) {
133 			rvalue = mockfs_fsnode_vnode(target_fsnode, vpp);
134 		}
135 	} else {
136 		/*
137 		 * We aren't looking in root; the query may actually be reasonable, but we're not
138 		 *   going to support it.
139 		 */
140 		rvalue = ENOENT;
141 	}
142 
143 	return rvalue;
144 }
145 
146 /*
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)
152  * };
153  *
154  * mockfs_getattr:
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.
159  *
160  * Returns 0 on success, or an error.
161  */
162 int
mockfs_getattr(struct vnop_getattr_args * ap)163 mockfs_getattr(struct vnop_getattr_args * ap)
164 {
165 	/*
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.
168 	 */
169 	vnode_t vp;
170 	mockfs_fsnode_t fsnode;
171 	struct vnode_attr * vap;
172 
173 	vp = ap->a_vp;
174 	fsnode = (mockfs_fsnode_t)vp->v_data;
175 	vap = ap->a_vap;
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);
184 
185 	return 0;
186 }
187 
188 /*
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)
195  * };
196  *
197  * mockfs_read:
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.
204  *
205  * Returns 0 on success, or an error.
206  */
207 int
mockfs_read(struct vnop_read_args * ap)208 mockfs_read(struct vnop_read_args * ap)
209 {
210 	int rvalue;
211 	vnode_t vp;
212 	mockfs_fsnode_t fsnode;
213 
214 	vp = ap->a_vp;
215 	fsnode = (mockfs_fsnode_t) vp->v_data;
216 
217 	/*
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.
220 	 */
221 	if (vp->v_type == VREG) {
222 		rvalue = cluster_read(vp, ap->a_uio, fsnode->size, ap->a_ioflag);
223 	} else {
224 		/*
225 		 * You've tried to read from a nonregular file; I hate you.
226 		 */
227 		rvalue = ENOTSUP;
228 	}
229 
230 	return rvalue;
231 }
232 
233 /*
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)
238  * };
239  *
240  * mockfs_reclaim:
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.
244  */
245 int
mockfs_reclaim(struct vnop_reclaim_args * ap)246 mockfs_reclaim(struct vnop_reclaim_args * ap)
247 {
248 	int rvalue;
249 	vnode_t vp;
250 	mockfs_fsnode_t fsnode;
251 
252 	vp = ap->a_vp;
253 	fsnode = (mockfs_fsnode_t) vnode_fsnode(vp);
254 	rvalue = mockfs_fsnode_drop_vnode(fsnode);
255 
256 	return rvalue;
257 }
258 
259 /*
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
263  * };
264  *
265  * mockfs_strategy:
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.
269  *
270  * Returns 0 on success, or an error.
271  */
272 int
mockfs_strategy(struct vnop_strategy_args * ap)273 mockfs_strategy(struct vnop_strategy_args * ap)
274 {
275 	int rvalue;
276 	vnode_t dvp;
277 
278 	/*
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.
281 	 */
282 
283 	dvp = vfs_devvp(buf_vnode(ap->a_bp)->v_mount);
284 
285 	if (dvp) {
286 		rvalue = buf_strategy(dvp, ap);
287 		vnode_put(dvp);
288 	} else {
289 		/*
290 		 * I'm not certain this is the BEST error to return for this case.
291 		 */
292 		rvalue = EIO;
293 	}
294 
295 	return rvalue;
296 }
297 
298 /*
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)
308  * };
309  *
310  * mockfs_pagegin:
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.
314  *
315  * Returns 0 on success, or an error.
316  */
317 int
mockfs_pagein(struct vnop_pagein_args * ap)318 mockfs_pagein(struct vnop_pagein_args * ap)
319 {
320 	mockfs_fsnode_t fsnode;
321 	mockfs_mount_t mockfs_mnt;
322 
323 	/*
324 	 * Nothing special needed from us; just nab the filesize and kick the work over to cluster_pagein.
325 	 */
326 	fsnode = (mockfs_fsnode_t) ap->a_vp->v_data;
327 	mockfs_mnt = ((mockfs_mount_t) fsnode->mnt->mnt_data);
328 
329 	/*
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.
332 	 */
333 	if (mockfs_mnt->mockfs_memory_backed) {
334 		panic("mockfs_pagein called for a memory-backed device");
335 	}
336 
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);
338 }
339 
340 /*
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)
351  * };
352  *
353  * mockfs_blockmap:
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.
359  *
360  * Returns 0 on success, or an error.
361  */
362 int
mockfs_blockmap(struct vnop_blockmap_args * ap)363 mockfs_blockmap(struct vnop_blockmap_args * ap)
364 {
365 	int rvalue;
366 	off_t foffset;
367 	size_t * run;
368 	uint32_t blksize;
369 	daddr64_t * bpn;
370 	vnode_t vp;
371 	mockfs_fsnode_t fsnode;
372 
373 	rvalue = 0;
374 	foffset = ap->a_foffset;
375 	run = ap->a_run;
376 	bpn = ap->a_bpn;
377 	vp = ap->a_vp;
378 	fsnode = (mockfs_fsnode_t) vp->v_data;
379 	blksize = vp->v_mount->mnt_devblocksize;
380 
381 	/*
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.
385 	 */
386 	if (((mockfs_mount_t) fsnode->mnt->mnt_data)->mockfs_memory_backed) {
387 		printf("mockfs_blockmap called for a memory-backed device\n");
388 	}
389 
390 	/*
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!
393 	 */
394 	if (vp->v_type == VREG) {
395 		*bpn = foffset / blksize;
396 		*run = fsnode->size - foffset;
397 
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");
401 		}
402 	} else {
403 		rvalue = ENOTSUP;
404 	}
405 
406 	return rvalue;
407 }
408 
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 }
447 };
448 
449 const struct vnodeopv_desc mockfs_vnodeop_opv_desc = {
450 	.opv_desc_vector_p = &mockfs_vnodeop_p,
451 	.opv_desc_ops = mockfs_vnodeop_entries
452 };
453