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