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