]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
91447636 | 2 | * Copyright (c) 1998-2004 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
ff6e181a A |
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. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
1c79356b | 12 | * |
ff6e181a A |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
1c79356b A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
ff6e181a A |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
1c79356b A |
20 | * |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
1c79356b A |
23 | |
24 | #include <mach/mach_types.h> | |
25 | ||
26 | #include <sys/param.h> | |
27 | #include <sys/systm.h> | |
28 | #include <sys/resourcevar.h> | |
29 | #include <sys/kernel.h> | |
30 | #include <sys/file.h> | |
55e303ae | 31 | #include <sys/filedesc.h> |
1c79356b | 32 | #include <sys/stat.h> |
91447636 A |
33 | #include <sys/proc_internal.h> /* for p_fd */ |
34 | #include <sys/kauth.h> | |
1c79356b | 35 | #include <sys/conf.h> |
91447636 A |
36 | #include <sys/mount_internal.h> |
37 | #include <sys/vnode_internal.h> | |
1c79356b A |
38 | #include <sys/malloc.h> |
39 | #include <sys/dirent.h> | |
40 | #include <sys/namei.h> | |
41 | #include <sys/attr.h> | |
55e303ae A |
42 | #include <sys/kdebug.h> |
43 | #include <sys/queue.h> | |
91447636 | 44 | #include <sys/uio_internal.h> |
1c79356b A |
45 | |
46 | #include <sys/vm.h> | |
47 | #include <sys/errno.h> | |
48 | #include <vfs/vfs_support.h> | |
49 | ||
91447636 A |
50 | #include <kern/locks.h> |
51 | ||
1c79356b A |
52 | #include "volfs.h" |
53 | ||
54 | /* | |
55 | * volfs acts as a bridge between the requirements of the MacOS API and the Unix API. | |
56 | * MacOS applications describe files by a <Volume ID><Directory ID><File Name> triple. | |
57 | * The Unix API describes files by pathname. Volfs is a virtual file system that sits over | |
58 | * the HFS VFS interface and allows files to be described by a <Volume ID>/<Directory ID>/<File Name> | |
59 | * pathname. | |
60 | * | |
61 | * The root of the volfs filesystem consists of directories named the volume ID's of all the | |
62 | * currently mounted filesystems which support the VFS vget() routine. Each of those directories | |
63 | * supports the lookup by file ID of all files and directories within the filesystem. When a | |
64 | * file or directory is resolved its vnode from that filesystem rather than a volfs vnode is returned | |
65 | * allowing immediate access to the target file or directory. | |
66 | * | |
67 | * Readdir on the root of the volfs filesystem returns the list of available file systems. Readdir | |
68 | * on a filesystem node, however, returns only . and .. since it is not practical to list all | |
69 | * of the file ID's in a timely fashion and furthermore VFS does not provide a mechanism for | |
70 | * enumerating all of the file id's. | |
71 | * | |
72 | * Volume ID's are taken from the low 32 bits of the f_fsid field, formatted as a base 10 ASCII | |
73 | * string with no leading zeros (volume ID 1 is represented as "1"). | |
74 | * | |
75 | * File ID's are created in same manner, with their 32 bits formatted as a base 10 ASCII | |
76 | * string with no leading zeros. | |
77 | * | |
78 | * Volfs does create a security hole since it is possible to bypass directory permissions higher | |
79 | * in the namespace tree. This security hole is about the same as the one created by NFS which uses | |
80 | * a similar mechanism. | |
81 | */ | |
82 | ||
91447636 A |
83 | static int volfs_reclaim (struct vnop_reclaim_args*); |
84 | static int volfs_getattr (struct vnop_getattr_args *); | |
85 | static int volfs_select (struct vnop_select_args *); | |
86 | static int volfs_rmdir (struct vnop_rmdir_args *); | |
87 | static int volfs_readdir (struct vnop_readdir_args *); | |
88 | static int volfs_pathconf (struct vnop_pathconf_args *); | |
89 | static int volfs_lookup (struct vnop_lookup_args *); | |
90 | ||
91 | static int volfs_readdir_callback(mount_t, void *); | |
92 | static int get_filevnode(struct mount *parent_fs, u_int id, vnode_t *ret_vnode, vfs_context_t context); | |
93 | static int get_fsvnode(struct mount *our_mount, int id, vnode_t *ret_vnode); | |
94 | ||
95 | /* for the call back function in volfs_readdir */ | |
96 | struct volfs_rdstruct { | |
97 | int validindex; | |
98 | vnode_t vp; | |
99 | int rec_offset; | |
100 | struct uio * uio; | |
101 | }; | |
102 | ||
1c79356b A |
103 | #define VOPFUNC int (*)(void *) |
104 | ||
105 | /* Global vfs data structures for volfs. */ | |
106 | int (**volfs_vnodeop_p) (void *); | |
107 | struct vnodeopv_entry_desc volfs_vnodeop_entries[] = { | |
91447636 A |
108 | {&vnop_default_desc, (VOPFUNC)vn_default_error}, |
109 | {&vnop_strategy_desc, (VOPFUNC)err_strategy}, /* strategy */ | |
110 | {&vnop_bwrite_desc, (VOPFUNC)err_bwrite}, /* bwrite */ | |
111 | {&vnop_lookup_desc, (VOPFUNC)volfs_lookup}, /* lookup */ | |
112 | {&vnop_create_desc, (VOPFUNC)err_create}, /* create */ | |
113 | {&vnop_whiteout_desc, (VOPFUNC)err_whiteout}, /* whiteout */ | |
114 | {&vnop_mknod_desc, (VOPFUNC)err_mknod}, /* mknod */ | |
115 | {&vnop_open_desc, (VOPFUNC)nop_open}, /* open */ | |
116 | {&vnop_close_desc, (VOPFUNC)nop_close}, /* close */ | |
117 | {&vnop_getattr_desc, (VOPFUNC)volfs_getattr}, /* getattr */ | |
118 | {&vnop_setattr_desc, (VOPFUNC)err_setattr}, /* setattr */ | |
119 | {&vnop_getattrlist_desc, (VOPFUNC)err_getattrlist}, /* getattrlist */ | |
120 | {&vnop_setattrlist_desc, (VOPFUNC)err_setattrlist}, /* setattrlist */ | |
121 | {&vnop_read_desc, (VOPFUNC)err_read}, /* read */ | |
122 | {&vnop_write_desc, (VOPFUNC)err_write}, /* write */ | |
123 | {&vnop_ioctl_desc, (VOPFUNC)err_ioctl}, /* ioctl */ | |
124 | {&vnop_select_desc, (VOPFUNC)volfs_select}, /* select */ | |
125 | {&vnop_exchange_desc, (VOPFUNC)err_exchange}, /* exchange */ | |
126 | {&vnop_revoke_desc, (VOPFUNC)nop_revoke}, /* revoke */ | |
127 | {&vnop_mmap_desc, (VOPFUNC)err_mmap}, /* mmap */ | |
128 | {&vnop_fsync_desc, (VOPFUNC)err_fsync}, /* fsync */ | |
129 | {&vnop_remove_desc, (VOPFUNC)err_remove}, /* remove */ | |
130 | {&vnop_link_desc, (VOPFUNC)err_link}, /* link */ | |
131 | {&vnop_rename_desc, (VOPFUNC)err_rename}, /* rename */ | |
132 | {&vnop_mkdir_desc, (VOPFUNC)err_mkdir}, /* mkdir */ | |
133 | {&vnop_rmdir_desc, (VOPFUNC)volfs_rmdir}, /* rmdir */ | |
134 | {&vnop_symlink_desc, (VOPFUNC)err_symlink}, /* symlink */ | |
135 | {&vnop_readdir_desc, (VOPFUNC)volfs_readdir}, /* readdir */ | |
136 | {&vnop_readdirattr_desc, (VOPFUNC)err_readdirattr}, /* readdirattr */ | |
137 | {&vnop_readlink_desc, (VOPFUNC)err_readlink}, /* readlink */ | |
138 | {&vnop_inactive_desc, (VOPFUNC)err_inactive}, /* inactive */ | |
139 | {&vnop_reclaim_desc, (VOPFUNC)volfs_reclaim}, /* reclaim */ | |
140 | {&vnop_pathconf_desc, (VOPFUNC)volfs_pathconf}, /* pathconf */ | |
141 | {&vnop_advlock_desc, (VOPFUNC)err_advlock}, /* advlock */ | |
142 | {&vnop_allocate_desc, (VOPFUNC)err_allocate}, /* allocate */ | |
143 | {&vnop_pagein_desc, (VOPFUNC)err_pagein}, /* pagein */ | |
144 | {&vnop_pageout_desc, (VOPFUNC)err_pageout}, /* pageout */ | |
145 | {&vnop_devblocksize_desc, (VOPFUNC)err_devblocksize}, /* devblocksize */ | |
146 | {&vnop_searchfs_desc, (VOPFUNC)err_searchfs}, /* searchfs */ | |
147 | {&vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */ | |
148 | {&vnop_blktooff_desc, (VOPFUNC)err_blktooff}, /* blktooff */ | |
149 | {&vnop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */ | |
150 | {&vnop_blockmap_desc, (VOPFUNC)err_blockmap }, /* blockmap */ | |
1c79356b A |
151 | {(struct vnodeop_desc *) NULL, (int (*) ()) NULL} |
152 | }; | |
153 | ||
154 | /* | |
155 | * Oh what a tangled web we weave. This structure will be used by | |
156 | * bsd/vfs/vfs_conf.c to actually do the initialization of volfs_vnodeop_p | |
157 | */ | |
158 | struct vnodeopv_desc volfs_vnodeop_opv_desc = | |
159 | {&volfs_vnodeop_p, volfs_vnodeop_entries}; | |
160 | ||
55e303ae A |
161 | static char gDotDot[] = ".."; |
162 | ||
163 | struct finfo { | |
164 | fsobj_id_t parID; | |
165 | }; | |
166 | ||
167 | struct finfoattrbuf { | |
168 | unsigned long length; | |
169 | struct finfo fi; | |
170 | }; | |
1c79356b | 171 | |
55e303ae | 172 | |
91447636 | 173 | static int volfs_getattr_callback(mount_t, void *); |
55e303ae A |
174 | |
175 | ||
1c79356b A |
176 | /* |
177 | * volfs_reclaim - Reclaim a vnode so that it can be used for other purposes. | |
1c79356b | 178 | */ |
91447636 | 179 | static int |
1c79356b | 180 | volfs_reclaim(ap) |
91447636 | 181 | struct vnop_reclaim_args /* { struct vnode *a_vp; vfs_context_t a_context; } */ *ap; |
1c79356b | 182 | { |
91447636 A |
183 | struct vnode *vp = ap->a_vp; |
184 | void *data = vp->v_data; | |
1c79356b A |
185 | |
186 | vp->v_data = NULL; | |
91447636 | 187 | FREE(data, M_VOLFSNODE); |
1c79356b | 188 | |
91447636 | 189 | return (0); |
1c79356b A |
190 | } |
191 | ||
91447636 A |
192 | struct volfsgetattr_struct{ |
193 | int numMounts; | |
194 | vnode_t a_vp; | |
195 | }; | |
1c79356b | 196 | |
91447636 A |
197 | static int |
198 | volfs_getattr_callback(mount_t mp, void * arg) | |
199 | { | |
200 | struct volfsgetattr_struct *vstrp = (struct volfsgetattr_struct *)arg; | |
1c79356b | 201 | |
91447636 A |
202 | if (mp != vnode_mount(vstrp->a_vp) && validfsnode(mp)) |
203 | vstrp->numMounts++; | |
204 | return(VFS_RETURNED); | |
1c79356b A |
205 | } |
206 | ||
207 | /* | |
208 | * volfs_getattr - fill in the attributes for this vnode | |
1c79356b | 209 | */ |
91447636 | 210 | static int |
1c79356b | 211 | volfs_getattr(ap) |
91447636 A |
212 | struct vnop_getattr_args /* { struct vnode *a_vp; struct vnode_attr *a_vap; |
213 | vfs_context_t a_context; } */ *ap; | |
1c79356b A |
214 | { |
215 | struct volfs_vndata *priv_data; | |
91447636 A |
216 | struct vnode *a_vp; |
217 | struct vnode_attr *a_vap; | |
1c79356b | 218 | int numMounts = 0; |
91447636 A |
219 | struct volfsgetattr_struct vstr; |
220 | struct timespec ts; | |
1c79356b A |
221 | |
222 | a_vp = ap->a_vp; | |
223 | a_vap = ap->a_vap; | |
224 | ||
225 | priv_data = a_vp->v_data; | |
226 | ||
91447636 A |
227 | VATTR_RETURN(a_vap, va_type, VDIR); |
228 | VATTR_RETURN(a_vap, va_mode, 0555); | |
229 | VATTR_RETURN(a_vap, va_nlink, 2); | |
230 | VATTR_RETURN(a_vap, va_uid, 0); | |
231 | VATTR_RETURN(a_vap, va_gid, 0); | |
232 | VATTR_RETURN(a_vap, va_fsid, (int) a_vp->v_mount->mnt_vfsstat.f_fsid.val[0]); | |
233 | VATTR_RETURN(a_vap, va_fileid, (uint64_t)((u_long)priv_data->nodeID)); | |
234 | VATTR_RETURN(a_vap, va_acl, NULL); | |
1c79356b A |
235 | |
236 | /* | |
237 | * If it's the root vnode calculate its size based on the number of eligible | |
238 | * file systems | |
239 | */ | |
91447636 A |
240 | if (priv_data->vnode_type == VOLFS_ROOT) { |
241 | vstr.numMounts = 0; | |
242 | vstr.a_vp = a_vp; | |
1c79356b | 243 | |
91447636 | 244 | vfs_iterate(LK_NOWAIT, volfs_getattr_callback, (void *)&vstr); |
1c79356b | 245 | |
91447636 | 246 | numMounts = vstr.numMounts; |
1c79356b | 247 | |
91447636 A |
248 | VATTR_RETURN(a_vap, va_data_size, (numMounts + 2) * VLFSDIRENTLEN); |
249 | } else { | |
250 | VATTR_RETURN(a_vap, va_data_size, 2 * VLFSDIRENTLEN); | |
251 | } | |
1c79356b | 252 | |
91447636 A |
253 | VATTR_RETURN(a_vap, va_iosize, 512); |
254 | ts.tv_sec = boottime_sec(); | |
255 | ts.tv_nsec = 0; | |
256 | VATTR_RETURN(a_vap, va_access_time, ts); | |
257 | VATTR_RETURN(a_vap, va_modify_time, ts); | |
258 | VATTR_RETURN(a_vap, va_change_time, ts); | |
1c79356b | 259 | |
91447636 A |
260 | VATTR_RETURN(a_vap, va_gen, 0); |
261 | VATTR_RETURN(a_vap, va_flags, 0); | |
262 | VATTR_RETURN(a_vap, va_rdev, 0); | |
263 | VATTR_RETURN(a_vap, va_filerev, 0); | |
1c79356b | 264 | |
1c79356b A |
265 | return (0); |
266 | } | |
267 | ||
268 | /* | |
269 | * volfs_select - just say OK. Only possible op is readdir | |
1c79356b | 270 | */ |
91447636 A |
271 | static int |
272 | volfs_select(__unused struct vnop_select_args *ap) | |
1c79356b | 273 | { |
91447636 | 274 | return (1); |
1c79356b A |
275 | } |
276 | ||
277 | /* | |
278 | * vofls_rmdir - not possible to remove directories in volfs | |
1c79356b | 279 | */ |
91447636 | 280 | static int |
1c79356b | 281 | volfs_rmdir(ap) |
91447636 A |
282 | struct vnop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; |
283 | struct componentname *a_cnp; vfs_context_t a_context; } */ *ap; | |
1c79356b | 284 | { |
1c79356b A |
285 | if (ap->a_dvp == ap->a_vp) { |
286 | (void) nop_rmdir(ap); | |
287 | return (EINVAL); | |
288 | } else | |
289 | return (err_rmdir(ap)); | |
290 | } | |
291 | ||
91447636 A |
292 | |
293 | ||
294 | static int | |
295 | volfs_readdir_callback(mount_t mp, void * v) | |
296 | { | |
297 | struct volfs_rdstruct * vcsp = (struct volfs_rdstruct *)v; | |
298 | struct dirent local_dir; | |
299 | int error; | |
300 | ||
301 | if ((mp != vnode_mount(vcsp->vp)) && validfsnode(mp)) | |
302 | vcsp->validindex++; | |
303 | ||
304 | if (vcsp->rec_offset == vcsp->validindex) | |
305 | { | |
306 | local_dir.d_fileno = mp->mnt_vfsstat.f_fsid.val[0]; | |
307 | local_dir.d_type = DT_DIR; | |
308 | local_dir.d_reclen = VLFSDIRENTLEN; | |
309 | local_dir.d_namlen = sprintf(&local_dir.d_name[0], "%d", mp->mnt_vfsstat.f_fsid.val[0]); | |
310 | error = uiomove((char *) &local_dir, VLFSDIRENTLEN, vcsp->uio); | |
311 | vcsp->rec_offset++; | |
312 | } | |
313 | ||
314 | return(VFS_RETURNED); | |
315 | } | |
316 | ||
1c79356b A |
317 | /* |
318 | * volfs_readdir - Get directory entries | |
319 | * | |
320 | * Directory listings are only produced for the root volfs node. Filesystems | |
321 | * just return . & .. | |
322 | * Filesystems contained within the volfs root are named by the decimal | |
323 | * equivalent of the f_fsid.val[0] from their mount structure (typically | |
324 | * the device id of the volume). The maximum length for a name, then is | |
325 | * 10 characters. | |
1c79356b | 326 | */ |
91447636 | 327 | static int |
1c79356b | 328 | volfs_readdir(ap) |
91447636 A |
329 | struct vnop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; |
330 | * int *a_eofflag; int | |
331 | *ncookies; u_long **a_cookies; vfs_context_t a_context; } */ *ap; | |
1c79356b A |
332 | { |
333 | struct volfs_vndata *priv_data; | |
334 | register struct uio *uio = ap->a_uio; | |
335 | int error = 0; | |
336 | size_t count, lost; | |
337 | int rec_offset; | |
338 | struct dirent local_dir; | |
339 | int i; | |
340 | int starting_resid; | |
341 | off_t off; | |
91447636 A |
342 | struct volfs_rdstruct vcs; |
343 | ||
1c79356b A |
344 | off = uio->uio_offset; |
345 | priv_data = ap->a_vp->v_data; | |
91447636 A |
346 | // LP64todo - fix this! |
347 | starting_resid = count = uio_resid(uio); | |
1c79356b A |
348 | |
349 | /* Make sure we don't return partial entries. */ | |
350 | count -= (uio->uio_offset + count) & (VLFSDIRENTLEN - 1); | |
91447636 A |
351 | if (count <= 0) { |
352 | return (EINVAL); | |
353 | } | |
1c79356b A |
354 | /* |
355 | * Make sure we're starting on a directory boundary | |
356 | */ | |
91447636 A |
357 | if (off & (VLFSDIRENTLEN - 1)) { |
358 | return (EINVAL); | |
359 | } | |
1c79356b | 360 | rec_offset = off / VLFSDIRENTLEN; |
91447636 A |
361 | // LP64todo - fix this! |
362 | lost = uio_resid(uio) - count; | |
363 | uio_setresid(uio, count); | |
364 | uio_iov_len_set(uio, count); | |
365 | #if LP64_DEBUG | |
366 | if (IS_VALID_UIO_SEGFLG(uio->uio_segflg) == 0) { | |
367 | panic("%s :%d - invalid uio_segflg\n", __FILE__, __LINE__); | |
368 | } | |
369 | #endif /* LP64_DEBUG */ | |
1c79356b A |
370 | |
371 | local_dir.d_reclen = VLFSDIRENTLEN; | |
372 | /* | |
373 | * We must synthesize . and .. | |
374 | */ | |
91447636 | 375 | |
1c79356b A |
376 | if (rec_offset == 0) |
377 | { | |
1c79356b A |
378 | /* |
379 | * Synthesize . | |
380 | */ | |
381 | local_dir.d_fileno = priv_data->nodeID; | |
382 | local_dir.d_type = DT_DIR; | |
383 | local_dir.d_namlen = 1; | |
384 | local_dir.d_name[0] = '.'; | |
385 | for (i = 1; i < MAXVLFSNAMLEN; i++) | |
386 | local_dir.d_name[i] = 0; | |
387 | error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio); | |
1c79356b A |
388 | rec_offset++; |
389 | } | |
390 | if (rec_offset == 1) | |
391 | { | |
1c79356b A |
392 | /* |
393 | * Synthesize .. | |
394 | * We only have two levels in the volfs hierarchy. Root's | |
395 | * .. points to itself and the second level points to root, | |
396 | * hence we've hardcoded d_fileno for .. here | |
397 | */ | |
398 | local_dir.d_fileno = ROOT_DIRID; | |
399 | local_dir.d_type = DT_DIR; | |
400 | local_dir.d_namlen = 2; | |
401 | local_dir.d_name[0] = '.'; | |
402 | local_dir.d_name[1] = '.'; | |
403 | for (i = 2; i < MAXVLFSNAMLEN; i++) | |
404 | local_dir.d_name[i] = 0; | |
405 | error = uiomove((char *) &local_dir, VLFSDIRENTLEN, uio); | |
406 | rec_offset++; | |
1c79356b A |
407 | } |
408 | ||
409 | /* | |
410 | * OK, we've given them the . & .. entries. If this is a | |
411 | * filesystem node then we've gone as far as we're going | |
412 | * to go | |
413 | */ | |
414 | if (priv_data->vnode_type == VOLFS_FSNODE) | |
415 | { | |
416 | *ap->a_eofflag = 1; /* we got all the way to the end */ | |
1c79356b A |
417 | return (error); |
418 | } | |
419 | ||
420 | if (rec_offset > 1) { | |
91447636 A |
421 | vcs.validindex = 1; /* we always have "." and ".." */ |
422 | vcs.rec_offset = rec_offset; | |
423 | vcs.vp = ap->a_vp; | |
424 | vcs.uio = uio; | |
425 | ||
426 | ||
427 | vfs_iterate(0, volfs_readdir_callback, &vcs); | |
1c79356b | 428 | |
91447636 | 429 | //if (mp == (void *) &mountlist) |
1c79356b A |
430 | *ap->a_eofflag = 1; /* we got all the way to the end */ |
431 | } | |
91447636 | 432 | uio_setresid(uio, (uio_resid(uio) + lost)); |
1c79356b | 433 | |
91447636 | 434 | if (starting_resid == uio_resid(uio)) |
1c79356b A |
435 | uio->uio_offset = 0; |
436 | ||
1c79356b A |
437 | return (error); |
438 | } | |
439 | ||
440 | ||
441 | /* | |
442 | * validfsnode - test to see if a file system supports VGET | |
443 | * | |
444 | * This can cause context switching, so caller should be lock safe | |
445 | */ | |
91447636 | 446 | int |
1c79356b A |
447 | validfsnode(struct mount *fsnode) |
448 | { | |
449 | ||
450 | /* | |
451 | * Just check to see if the the mount flag is set, if it is we assume the | |
452 | * file system supports all of volfs symantecs | |
453 | */ | |
454 | ||
455 | if ((! (fsnode->mnt_kern_flag & MNTK_UNMOUNT)) && (fsnode->mnt_flag & MNT_DOVOLFS)) | |
456 | return 1; | |
457 | else | |
458 | return 0; | |
459 | } | |
460 | ||
1c79356b A |
461 | /* |
462 | * volfs_pathconf - Return POSIX pathconf information applicable to ufs filesystems. | |
1c79356b | 463 | */ |
91447636 | 464 | static int |
1c79356b | 465 | volfs_pathconf(ap) |
91447636 A |
466 | struct vnop_pathconf_args /* { struct vnode *a_vp; int a_name; int |
467 | *a_retval; vfs_context_t a_context; } */ *ap; | |
1c79356b | 468 | { |
1c79356b A |
469 | switch (ap->a_name) |
470 | { | |
471 | case _PC_LINK_MAX: | |
472 | *ap->a_retval = LINK_MAX; | |
473 | return (0); | |
474 | case _PC_NAME_MAX: | |
475 | *ap->a_retval = NAME_MAX; | |
476 | return (0); | |
477 | case _PC_PATH_MAX: | |
478 | *ap->a_retval = PATH_MAX; | |
479 | return (0); | |
480 | case _PC_PIPE_BUF: | |
481 | *ap->a_retval = PIPE_BUF; | |
482 | return (0); | |
483 | case _PC_CHOWN_RESTRICTED: | |
484 | *ap->a_retval = 1; | |
485 | return (0); | |
486 | case _PC_NO_TRUNC: | |
487 | *ap->a_retval = 1; | |
488 | return (0); | |
489 | default: | |
490 | return (EINVAL); | |
491 | } | |
492 | /* NOTREACHED */ | |
493 | } | |
494 | ||
55e303ae A |
495 | /* |
496 | * get_parentvp() - internal routine that tries to lookup the parent of vpp. | |
91447636 | 497 | * On success, *vpp is the parent vp and is returned with a reference. |
55e303ae A |
498 | */ |
499 | static int | |
91447636 | 500 | get_parentvp(struct vnode **vpp, struct mount *mp, vfs_context_t context) |
55e303ae A |
501 | { |
502 | int result; | |
91447636 | 503 | struct vnode_attr va; |
55e303ae | 504 | struct vnode *child_vp = *vpp; |
55e303ae | 505 | |
91447636 A |
506 | VATTR_INIT(&va); |
507 | VATTR_WANTED(&va, va_parentid); | |
508 | result = vnode_getattr(child_vp, &va, context); | |
509 | if (result) { | |
510 | return result; | |
511 | } | |
512 | ||
55e303ae | 513 | /* Shift attention to the parent directory vnode: */ |
91447636 A |
514 | result = VFS_VGET(mp, (ino64_t)va.va_parentid, vpp, context); |
515 | ||
516 | if (result == 0 && child_vp->v_parent != *vpp) { | |
517 | vnode_update_identity(child_vp, *vpp, NULL, 0, 0, VNODE_UPDATE_PARENT); | |
55e303ae | 518 | } |
91447636 | 519 | |
55e303ae A |
520 | return result; |
521 | } | |
522 | ||
523 | ||
524 | /* | |
525 | * Look up the parent directory of a given vnode. | |
526 | */ | |
527 | static int | |
91447636 | 528 | lookup_parent(vnode_t child_vp, vnode_t *parent_vpp, int is_authorized, vfs_context_t context) |
55e303ae | 529 | { |
91447636 A |
530 | struct componentname cn; |
531 | vnode_t new_vp; | |
55e303ae A |
532 | int error; |
533 | ||
91447636 | 534 | *parent_vpp = NULLVP; |
55e303ae | 535 | |
91447636 A |
536 | if (is_authorized == 0) { |
537 | error = vnode_authorize(child_vp, NULL, KAUTH_VNODE_SEARCH, context); | |
538 | if (error != 0) { | |
539 | return (error); | |
540 | } | |
541 | } | |
542 | new_vp = child_vp->v_parent; | |
55e303ae | 543 | |
91447636 A |
544 | if (new_vp != NULLVP) { |
545 | if ( (error = vnode_getwithref(new_vp)) == 0 ) | |
546 | *parent_vpp = new_vp; | |
547 | return (error); | |
548 | } | |
549 | bzero(&cn, sizeof(cn)); | |
550 | cn.cn_nameiop = LOOKUP; | |
551 | cn.cn_context = context; | |
552 | cn.cn_pnbuf = CAST_DOWN(caddr_t, &gDotDot); | |
553 | cn.cn_pnlen = strlen(cn.cn_pnbuf); | |
554 | cn.cn_nameptr = cn.cn_pnbuf; | |
555 | cn.cn_namelen = cn.cn_pnlen; | |
556 | cn.cn_flags = (FOLLOW | LOCKLEAF | ISLASTCN | ISDOTDOT); | |
557 | ||
558 | error = VNOP_LOOKUP(child_vp, &new_vp, &cn, context); | |
559 | if (error != 0) { | |
560 | return(error); | |
561 | } | |
562 | if (new_vp == child_vp) { | |
563 | vnode_put(new_vp); | |
564 | return ELOOP; | |
565 | } | |
566 | if (child_vp->v_parent == NULLVP) { | |
567 | vnode_update_identity(child_vp, new_vp, NULL, 0, 0, VNODE_UPDATE_PARENT); | |
568 | } | |
569 | *parent_vpp = new_vp; | |
570 | return 0; | |
571 | } | |
55e303ae A |
572 | |
573 | ||
574 | /* | |
575 | * verify_fullpathaccess(ret_vnode); | |
576 | */ | |
577 | ||
578 | static int | |
91447636 A |
579 | verify_fullpathaccess(struct vnode *targetvp, vfs_context_t context) |
580 | { | |
55e303ae A |
581 | struct vnode *vp, *parent_vp; |
582 | struct mount *mp = targetvp->v_mount; | |
91447636 | 583 | struct proc *p = vfs_context_proc(context); |
55e303ae | 584 | int result; |
91447636 | 585 | int dp_authorized; |
55e303ae | 586 | struct filedesc *fdp = p->p_fd; /* pointer to file descriptor state */ |
55e303ae | 587 | |
55e303ae | 588 | vp = targetvp; |
91447636 | 589 | dp_authorized = 0; |
55e303ae | 590 | |
91447636 A |
591 | /* get the parent directory. */ |
592 | if ((vp->v_flag & VROOT) == 0 && vp != fdp->fd_cdir && vp != fdp->fd_rdir) { | |
593 | if (vp->v_parent == NULLVP || (vp->v_flag & VISHARDLINK) || (vnode_getwithref(vp->v_parent) != 0)) { | |
594 | if (vp->v_type == VDIR) { | |
595 | result = lookup_parent(vp, &parent_vp, dp_authorized, context); | |
596 | ||
597 | /* | |
598 | * If the lookup fails with EACCES and the vp is a directory, | |
599 | * we should try again but bypass authorization check. Without this | |
600 | * workaround directories that you can navigate to but not traverse will | |
601 | * disappear when clicked in the Finder. | |
602 | */ | |
603 | if (result == EACCES && (vp->v_flag & VROOT) == 0) { | |
604 | dp_authorized = 1; /* bypass auth check */ | |
605 | if (lookup_parent(vp, &parent_vp, dp_authorized, context) == 0) { | |
606 | result = 0; | |
607 | } | |
608 | dp_authorized = 0; /* force us to authorize */ | |
55e303ae | 609 | } |
91447636 A |
610 | vp = parent_vp; |
611 | } | |
612 | else { | |
613 | /* | |
614 | * this is not a directory so we must get parent object ID | |
615 | */ | |
616 | result = get_parentvp(&vp, mp, context); | |
617 | parent_vp = vp; | |
618 | } | |
619 | if (result != 0) | |
620 | goto err_exit; | |
621 | } | |
622 | else { | |
623 | /* | |
624 | * we where able to get a reference on v_parent | |
625 | */ | |
626 | parent_vp = vp = vp->v_parent; | |
627 | } | |
628 | } | |
55e303ae | 629 | |
91447636 A |
630 | /* |
631 | * Keep going up until either the process's root or the process's working | |
632 | * directory is hit, either one of which are potential valid starting points | |
633 | * for a full pathname | |
634 | */ | |
635 | while (vp != NULLVP) { | |
55e303ae | 636 | |
91447636 A |
637 | result = reverse_lookup(vp, &parent_vp, fdp, context, &dp_authorized); |
638 | if (result == 0) { | |
639 | /* | |
640 | * we're done and we have access | |
641 | */ | |
642 | break; | |
55e303ae | 643 | } |
91447636 A |
644 | if (vp != parent_vp) { |
645 | /* | |
646 | * we where able to walk up the parent chain so now we don't need | |
647 | * vp any longer | |
648 | */ | |
649 | vnode_put(vp); | |
650 | vp = parent_vp; | |
651 | } | |
652 | /* | |
653 | * we have a referenced vp at this point... if dp_authorized == 1, than | |
654 | * it's been authorized for search, but v_parent was NULL... | |
655 | * if dp_authorized == 0, than we need to do the authorization check | |
656 | * before looking up the parent | |
657 | */ | |
658 | if ((vp->v_flag & VROOT) != 0 || | |
659 | vp == fdp->fd_cdir || vp == fdp->fd_rdir) { | |
660 | /* | |
661 | * we're already at the termination point, which implies that | |
662 | * the authorization check in the cache failed (otherwise we | |
663 | * would have returned 'done' from "reverse_lookup"... so, | |
664 | * do the authorization and bail | |
665 | */ | |
666 | result = vnode_authorize(vp, NULL, KAUTH_VNODE_SEARCH, context); | |
667 | goto lookup_exit; | |
668 | } | |
669 | result = lookup_parent(vp, &parent_vp, dp_authorized, context); | |
670 | if (result != 0) { | |
671 | goto lookup_exit; | |
672 | } | |
673 | if (vp != parent_vp) { | |
674 | /* | |
675 | * got the parent so now we don't need vp any longer | |
676 | */ | |
677 | vnode_put(vp); | |
678 | vp = parent_vp; | |
679 | } | |
680 | } /* while loop */ | |
681 | ||
682 | /* | |
683 | * Success: the caller has complete access to the initial vnode | |
684 | */ | |
685 | result = 0; | |
686 | ||
687 | lookup_exit: | |
688 | if (vp != NULLVP && vp != targetvp) { | |
689 | vnode_put(vp); | |
690 | } | |
55e303ae A |
691 | |
692 | err_exit: | |
55e303ae A |
693 | return result; |
694 | }; | |
695 | ||
696 | ||
1c79356b A |
697 | /* |
698 | * get_fsvnode - internal routine to create a vnode for a file system. Called with mount pointer, | |
699 | * id of filesystem to lookup and pointer to vnode pointer to fill in | |
700 | */ | |
701 | static int | |
91447636 | 702 | get_fsvnode(struct mount *our_mount, int id, vnode_t *ret_vnode) |
1c79356b | 703 | { |
1c79356b | 704 | struct mount *cur_mount; |
91447636 | 705 | fsid_t cur_fsid; |
1c79356b A |
706 | struct vnode *cur_vnode; |
707 | struct volfs_vndata *cur_privdata; | |
708 | int retval; | |
91447636 A |
709 | struct vnode_fsparam vfsp; |
710 | int vid = 0; | |
1c79356b A |
711 | |
712 | /* | |
713 | * OK, first look up the matching mount on the list of mounted file systems | |
714 | */ | |
91447636 A |
715 | /* the following will return the mount point with vfs_busy held */ |
716 | cur_mount = mount_lookupby_volfsid(id, 1); | |
1c79356b A |
717 | |
718 | if (cur_mount == NULL) { | |
719 | /* | |
720 | * No mounted file system by the specified ID currently exists in the system. | |
721 | * | |
722 | * XXX We could deal with a vnode that is still hanging about for an FS that | |
723 | * does not exists or has been unmounted now, or count on the update below | |
724 | * to happen later... | |
725 | */ | |
726 | *ret_vnode = NULL; | |
727 | return ENOENT; | |
728 | }; | |
729 | ||
91447636 A |
730 | cur_fsid = cur_mount->mnt_vfsstat.f_fsid; |
731 | ||
1c79356b A |
732 | /* |
733 | * Now search the list attached to the mount structure to | |
734 | * see if this vnode is already floating around | |
735 | */ | |
736 | search_vnodelist: | |
91447636 A |
737 | mount_lock(our_mount); |
738 | TAILQ_FOREACH(cur_vnode, &our_mount->mnt_vnodelist, v_mntvnodes) { | |
1c79356b | 739 | cur_privdata = (struct volfs_vndata *) cur_vnode->v_data; |
91447636 | 740 | if (cur_privdata->nodeID == (unsigned int)id) |
1c79356b A |
741 | { |
742 | if (cur_privdata->fs_mount != cur_mount) { | |
1c79356b | 743 | cur_privdata->fs_mount = cur_mount; |
91447636 | 744 | cur_privdata->fs_fsid = cur_fsid; |
1c79356b A |
745 | }; |
746 | break; | |
747 | } | |
91447636 A |
748 | } |
749 | mount_unlock(our_mount); | |
1c79356b | 750 | |
1c79356b | 751 | if (cur_vnode) { |
91447636 A |
752 | vid = vnode_vid(cur_vnode); |
753 | ||
754 | /* | |
755 | * use vnode_getwithvid since it will wait for a vnode currently being | |
756 | * terminated... if it returns an error, cur_vnode will not be what we | |
757 | * think it is, try again | |
758 | */ | |
759 | if (vnode_getwithvid(cur_vnode, vid) != 0) { | |
1c79356b A |
760 | goto search_vnodelist; |
761 | }; | |
762 | } | |
763 | else | |
764 | { | |
765 | MALLOC(cur_privdata, struct volfs_vndata *, | |
766 | sizeof(struct volfs_vndata), M_VOLFSNODE, M_WAITOK); | |
91447636 | 767 | |
1c79356b A |
768 | cur_privdata->vnode_type = VOLFS_FSNODE; |
769 | cur_privdata->nodeID = id; | |
770 | ||
771 | cur_privdata->fs_mount = cur_mount; | |
91447636 A |
772 | cur_privdata->fs_fsid = cur_fsid; |
773 | ||
774 | vfsp.vnfs_mp = our_mount; | |
775 | vfsp.vnfs_vtype = VDIR; | |
776 | vfsp.vnfs_str = "volfs"; | |
777 | vfsp.vnfs_dvp = 0; | |
778 | vfsp.vnfs_fsnode = cur_privdata; | |
779 | vfsp.vnfs_cnp = 0; | |
780 | vfsp.vnfs_vops = volfs_vnodeop_p; | |
781 | vfsp.vnfs_rdev = 0; | |
782 | vfsp.vnfs_filesize = 0; | |
783 | vfsp.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE; | |
784 | vfsp.vnfs_marksystem = 0; | |
785 | vfsp.vnfs_markroot = 0; | |
786 | ||
787 | retval = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &cur_vnode); | |
788 | if (retval != 0) { | |
789 | FREE(cur_privdata, M_VOLFSNODE); | |
790 | goto out; | |
791 | }; | |
792 | cur_vnode->v_tag = VT_VOLFS; | |
793 | ||
1c79356b A |
794 | } |
795 | ||
796 | *ret_vnode = cur_vnode; | |
91447636 A |
797 | retval = 0; |
798 | out: | |
799 | vfs_unbusy(cur_mount); | |
800 | return (retval); | |
1c79356b A |
801 | } |
802 | ||
803 | ||
804 | ||
805 | /* | |
806 | * get_filevnode - returns the vnode for the given id within a filesystem. The parent vnode | |
807 | * is a filesystem, id is the 32-bit id of the file/directory and ret_vnode is a pointer | |
808 | * to a vnode pointer | |
809 | */ | |
810 | static int | |
91447636 | 811 | get_filevnode(struct mount *parent_fs, u_int id, vnode_t *ret_vnode, vfs_context_t context) |
1c79356b A |
812 | { |
813 | int retval; | |
814 | ||
55e303ae | 815 | again: |
1c79356b A |
816 | /* |
817 | * Special case 2 to mean the root of a file system | |
818 | */ | |
819 | if (id == 2) | |
91447636 | 820 | retval = VFS_ROOT(parent_fs, ret_vnode, context); |
1c79356b | 821 | else |
91447636 | 822 | retval = VFS_VGET(parent_fs, (ino64_t)id, ret_vnode, context); |
55e303ae A |
823 | if (retval) goto error; |
824 | ||
91447636 | 825 | retval = verify_fullpathaccess(*ret_vnode, context); |
55e303ae A |
826 | if (retval) { |
827 | /* An error was encountered verifying that the caller has, | |
828 | in fact, got access all the way from "/" or their working | |
829 | directory to the specified item... | |
830 | */ | |
91447636 | 831 | vnode_put(*ret_vnode); |
55e303ae A |
832 | *ret_vnode = NULL; |
833 | /* vnode was recycled during access verification. */ | |
834 | if (retval == EAGAIN) { | |
835 | goto again; | |
836 | } | |
837 | }; | |
1c79356b | 838 | |
55e303ae | 839 | error: |
1c79356b A |
840 | return (retval); |
841 | } | |
842 | ||
843 | ||
91447636 A |
844 | static int |
845 | volfs_lookup(struct vnop_lookup_args *ap) | |
1c79356b | 846 | { |
91447636 A |
847 | struct volfs_vndata *priv_data; |
848 | char *nameptr; | |
849 | long namelen; | |
850 | struct mount *parent_fs; | |
851 | vnode_t vp; | |
852 | int isdot_or_dotdot = 0; | |
853 | int ret_err = ENOENT; | |
854 | char firstchar; | |
855 | int ret_val; | |
1c79356b | 856 | |
55e303ae A |
857 | #if 0 |
858 | KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 8)) | DBG_FUNC_START, | |
859 | (unsigned int)ap->a_dvp, (unsigned int)ap->a_cnp, (unsigned int)p, 0, 0); | |
860 | #endif | |
1c79356b | 861 | priv_data = ap->a_dvp->v_data; |
91447636 | 862 | nameptr = ap->a_cnp->cn_nameptr; |
1c79356b | 863 | namelen = ap->a_cnp->cn_namelen; |
91447636 | 864 | firstchar = nameptr[0]; |
1c79356b | 865 | |
91447636 A |
866 | /* First check for "." and ".." */ |
867 | if (firstchar == '.') { | |
868 | if (namelen == 1) { | |
1c79356b | 869 | /* "." requested */ |
55e303ae | 870 | isdot_or_dotdot = 1; |
91447636 A |
871 | *ap->a_vpp = ap->a_dvp; |
872 | vnode_get(*ap->a_vpp); | |
873 | ret_err = 0; | |
874 | } else if (nameptr[1] == '.' && namelen == 2) { | |
1c79356b | 875 | /* ".." requested */ |
55e303ae | 876 | isdot_or_dotdot = 1; |
91447636 | 877 | ret_err = VFS_ROOT(ap->a_dvp->v_mount, ap->a_vpp, ap->a_context); |
1c79356b | 878 | } |
91447636 | 879 | } else if (firstchar == '@') { /* '@' is alias for system root */ |
1c79356b | 880 | if ((namelen == 1) && (priv_data->vnode_type != VOLFS_ROOT)) { |
91447636 A |
881 | /* the following returns with iteration count on mount point */ |
882 | parent_fs = mount_list_lookupby_fsid(&priv_data->fs_fsid, 0, 1); | |
883 | if (parent_fs) { | |
884 | ret_val = vfs_busy(parent_fs, LK_NOWAIT); | |
885 | mount_iterdrop(parent_fs); | |
886 | if (ret_val !=0) { | |
887 | *ap->a_vpp = NULL; | |
888 | ret_err = ENOENT; | |
889 | } else { | |
890 | ret_err = VFS_ROOT(parent_fs, ap->a_vpp, ap->a_context); | |
891 | vfs_unbusy(parent_fs); | |
892 | } | |
893 | } else { | |
894 | *ap->a_vpp = NULL; | |
895 | ret_err = ENOENT; | |
896 | } | |
897 | } else { | |
898 | *ap->a_vpp = NULL; | |
899 | ret_err = ENOENT; | |
900 | } | |
901 | } else if (namelen <= 10 && firstchar > '0' && firstchar <= '9') { | |
1c79356b A |
902 | char *check_ptr; |
903 | u_long id; | |
904 | ||
91447636 | 905 | id = strtoul(nameptr, &check_ptr, 10); |
1c79356b | 906 | |
91447636 | 907 | /* |
1c79356b A |
908 | * strtol will leave us at the first non-numeric character. |
909 | * we've checked to make sure the component name does | |
910 | * begin with a numeric so check_ptr must wind up on | |
911 | * the terminating null or there was other junk following the | |
912 | * number | |
913 | */ | |
91447636 A |
914 | if ((check_ptr - nameptr) == namelen) { |
915 | if (priv_data->vnode_type == VOLFS_ROOT) { | |
916 | /* | |
917 | * OPTIMIZATION | |
918 | * | |
919 | * Obtain the mountpoint and call VFS_VGET in | |
920 | * one step (ie without creating a vnode for | |
921 | * the mountpoint). | |
922 | */ | |
923 | if (check_ptr[0] == '/' && | |
924 | check_ptr[1] > '0' && check_ptr[1] <= '9') { | |
925 | struct mount *mp; | |
926 | struct vnode *vp; | |
927 | u_long id2; | |
928 | char *endptr; | |
929 | ||
930 | /* this call will return mount point with vfs_busy held */ | |
931 | mp = mount_lookupby_volfsid(id, 1); | |
932 | if (mp == NULL) { | |
933 | *ap->a_vpp = NULL; | |
934 | return ENOENT; | |
935 | } | |
936 | id2 = strtoul(&check_ptr[1], &endptr, 10); | |
937 | if ((endptr[0] == '/' || endptr[0] == '\0') && | |
938 | get_filevnode(mp, id2, &vp, ap->a_context) == 0) { | |
939 | ap->a_cnp->cn_consume = endptr - check_ptr; | |
940 | *ap->a_vpp = vp; | |
941 | vfs_unbusy(mp); | |
942 | return (0); | |
943 | } | |
944 | vfs_unbusy(mp); | |
945 | } | |
946 | /* Fall through to default behavior... */ | |
947 | ||
1c79356b | 948 | ret_err = get_fsvnode(ap->a_dvp->v_mount, id, ap->a_vpp); |
1c79356b | 949 | |
91447636 A |
950 | } else { |
951 | parent_fs = mount_list_lookupby_fsid(&priv_data->fs_fsid, 0, 1); | |
952 | if (parent_fs) { | |
953 | ret_val = vfs_busy(parent_fs, LK_NOWAIT); | |
954 | mount_iterdrop(parent_fs); | |
955 | if (ret_val !=0) { | |
956 | *ap->a_vpp = NULL; | |
957 | ret_err = ENOENT; | |
958 | } else { | |
959 | ret_err = get_filevnode(parent_fs, id, ap->a_vpp, ap->a_context); | |
960 | vfs_unbusy(parent_fs); | |
961 | } | |
962 | } else { | |
963 | *ap->a_vpp = NULL; | |
964 | ret_err = ENOENT; | |
965 | } | |
966 | } | |
55e303ae | 967 | } |
1c79356b | 968 | } |
91447636 | 969 | vp = *ap->a_vpp; |
1c79356b | 970 | |
91447636 A |
971 | if ( ret_err == 0 && !isdot_or_dotdot && (vp != NULLVP) && (vp->v_parent == NULLVP)) |
972 | vnode_update_identity(vp, ap->a_dvp, NULL, 0, 0, VNODE_UPDATE_PARENT); | |
1c79356b | 973 | |
55e303ae A |
974 | #if 0 |
975 | KERNEL_DEBUG((FSDBG_CODE(DBG_FSVN, 8)) | DBG_FUNC_START, | |
976 | (unsigned int)ap->a_dvp, (unsigned int)ap->a_cnp, (unsigned int)p, ret_err, 0); | |
977 | #endif | |
91447636 | 978 | return (ret_err); |
1c79356b A |
979 | } |
980 |