]>
Commit | Line | Data |
---|---|---|
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | }; |