]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/synthfs/synthfs_vnops.c
f6f9d782c2600b911e8861e186ecd0912b7a430b
[apple/xnu.git] / bsd / miscfs / synthfs / synthfs_vnops.c
1 /*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Copyright (c) 1998-1999 Apple Computer, Inc. All Rights Reserved.
27 *
28 * Modification History:
29 *
30 * 02-Feb-2000 Clark Warner Added copyfile to table
31 * 17-Aug-1999 Pat Dirks New today.
32 */
33
34 #include <mach/mach_types.h>
35 #include <mach/machine/boolean.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/file.h>
40 #include <sys/stat.h>
41 #include <sys/buf.h>
42 #include <sys/proc.h>
43 #include <sys/conf.h>
44 #include <sys/mount.h>
45 #include <sys/vnode.h>
46 #include <sys/malloc.h>
47 #include <sys/dirent.h>
48 #include <sys/namei.h>
49 #include <sys/attr.h>
50
51 #include <sys/vm.h>
52 #include <sys/errno.h>
53 #include <vfs/vfs_support.h>
54
55 #include "synthfs.h"
56
57 #define RWSUPPORT 0
58
59 #if RWSUPPORT
60 #error NOT PORTED FOR UBC
61 /* when porting to UBC, do not just replace
62 * vnode_uncache by ubc_uncache - there's more
63 * to it than that!
64 */
65 #include <sys/ubc.h>
66 #endif
67
68 extern int groupmember(gid_t gid, struct ucred* cred);
69
70 #define VOPFUNC int (*)(void *)
71
72 /* Global vfs data structures for synthfs. */
73 int (**synthfs_vnodeop_p) (void *);
74 struct vnodeopv_entry_desc synthfs_vnodeop_entries[] = {
75 {&vop_default_desc, (VOPFUNC)vn_default_error},
76 {&vop_strategy_desc, (VOPFUNC)err_strategy}, /* strategy - not supported */
77 {&vop_bwrite_desc, (VOPFUNC)err_bwrite}, /* bwrite - not supported */
78 {&vop_lookup_desc, (VOPFUNC)synthfs_cached_lookup}, /* cached lookup */
79 {&vop_create_desc, (VOPFUNC)synthfs_create}, /* create - DEBUGGER */
80 {&vop_whiteout_desc, (VOPFUNC)err_whiteout}, /* whiteout - not supported */
81 {&vop_mknod_desc, (VOPFUNC)err_mknod}, /* mknod - not supported */
82 {&vop_mkcomplex_desc, (VOPFUNC)err_mkcomplex}, /* mkcomplex - not supported */
83 {&vop_open_desc, (VOPFUNC)synthfs_open}, /* open - DEBUGGER */
84 {&vop_close_desc, (VOPFUNC)nop_close}, /* close - NOP */
85 {&vop_access_desc, (VOPFUNC)synthfs_access}, /* access */
86 {&vop_getattr_desc, (VOPFUNC)synthfs_getattr}, /* getattr */
87 {&vop_setattr_desc, (VOPFUNC)synthfs_setattr}, /* setattr */
88 {&vop_getattrlist_desc, (VOPFUNC)err_getattrlist}, /* getattrlist - not supported */
89 {&vop_setattrlist_desc, (VOPFUNC)err_setattrlist}, /* setattrlist - not supported */
90 {&vop_read_desc, (VOPFUNC)err_read}, /* read - not supported */
91 {&vop_write_desc, (VOPFUNC)err_write}, /* write - not supported */
92 {&vop_lease_desc, (VOPFUNC)err_lease}, /* lease - not supported */
93 {&vop_ioctl_desc, (VOPFUNC)err_ioctl}, /* ioctl - not supported */
94 {&vop_select_desc, (VOPFUNC)synthfs_select}, /* select */
95 {&vop_exchange_desc, (VOPFUNC)err_exchange}, /* exchange - not supported */
96 {&vop_revoke_desc, (VOPFUNC)nop_revoke}, /* revoke - NOP */
97 {&vop_mmap_desc, (VOPFUNC)synthfs_mmap}, /* mmap - DEBUGGER */
98 {&vop_fsync_desc, (VOPFUNC)nop_fsync}, /* fsync - NOP */
99 {&vop_seek_desc, (VOPFUNC)nop_seek}, /* seek - NOP */
100 {&vop_remove_desc, (VOPFUNC)synthfs_remove}, /* remove */
101 {&vop_link_desc, (VOPFUNC)err_link}, /* link - not supported */
102 {&vop_rename_desc, (VOPFUNC)synthfs_rename}, /* rename */
103 {&vop_mkdir_desc, (VOPFUNC)synthfs_mkdir}, /* mkdir */
104 {&vop_rmdir_desc, (VOPFUNC)synthfs_rmdir}, /* rmdir */
105 {&vop_symlink_desc, (VOPFUNC)synthfs_symlink}, /* symlink */
106 {&vop_readdir_desc, (VOPFUNC)synthfs_readdir}, /* readdir */
107 {&vop_readdirattr_desc, (VOPFUNC)err_readdirattr}, /* readdirattr - not supported */
108 {&vop_readlink_desc, (VOPFUNC)synthfs_readlink}, /* readlink */
109 {&vop_abortop_desc, (VOPFUNC)nop_abortop}, /* abortop - NOP */
110 {&vop_inactive_desc, (VOPFUNC)synthfs_inactive}, /* inactive */
111 {&vop_reclaim_desc, (VOPFUNC)synthfs_reclaim}, /* reclaim */
112 {&vop_lock_desc, (VOPFUNC)synthfs_lock}, /* lock */
113 {&vop_unlock_desc, (VOPFUNC)synthfs_unlock}, /* unlock */
114 {&vop_bmap_desc, (VOPFUNC)err_bmap}, /* bmap - not supported */
115 {&vop_print_desc, (VOPFUNC)err_print}, /* print - not supported */
116 {&vop_islocked_desc, (VOPFUNC)synthfs_islocked}, /* islocked */
117 {&vop_pathconf_desc, (VOPFUNC)synthfs_pathconf}, /* pathconf */
118 {&vop_advlock_desc, (VOPFUNC)err_advlock}, /* advlock - not supported */
119 {&vop_blkatoff_desc, (VOPFUNC)err_blkatoff}, /* blkatoff - not supported */
120 {&vop_valloc_desc, (VOPFUNC)err_valloc}, /* valloc - not supported */
121 {&vop_reallocblks_desc, (VOPFUNC)err_reallocblks}, /* reallocblks - not supported */
122 {&vop_vfree_desc, (VOPFUNC)err_vfree}, /* vfree - not supported */
123 {&vop_truncate_desc, (VOPFUNC)err_truncate}, /* truncate - not supported */
124 {&vop_allocate_desc, (VOPFUNC)err_allocate}, /* allocate - not supported */
125 {&vop_update_desc, (VOPFUNC)synthfs_update}, /* update */
126 {&vop_pgrd_desc, (VOPFUNC)err_pgrd}, /* pgrd - not supported */
127 {&vop_pgwr_desc, (VOPFUNC)err_pgwr}, /* pgwr - not supported */
128 {&vop_pagein_desc, (VOPFUNC)err_pagein}, /* pagein - not supported */
129 {&vop_pageout_desc, (VOPFUNC)err_pageout}, /* pageout - not supported */
130 {&vop_devblocksize_desc, (VOPFUNC)err_devblocksize}, /* devblocksize - not supported */
131 {&vop_searchfs_desc, (VOPFUNC)err_searchfs}, /* searchfs - not supported */
132 {&vop_copyfile_desc, (VOPFUNC)err_copyfile}, /* copyfile - not supported */
133 { &vop_blktooff_desc, (VOPFUNC)err_blktooff }, /* blktooff not supported */
134 { &vop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk not supported */
135 { &vop_cmap_desc, (VOPFUNC)err_cmap }, /* cmap not supported */
136 {(struct vnodeop_desc *) NULL, (int (*) ()) NULL}
137 };
138
139 /*
140 * Oh what a tangled web we weave. This structure will be used by
141 * bsd/vfs/vfs_conf.c to actually do the initialization of synthfs_vnodeop_p
142 */
143 struct vnodeopv_desc synthfs_vnodeop_opv_desc =
144 {&synthfs_vnodeop_p, synthfs_vnodeop_entries};
145
146
147
148 /*
149 * Create a regular file
150 #% create dvp L U U
151 #% create vpp - L -
152 #
153 vop_create {
154 IN WILLRELE struct vnode *dvp;
155 OUT struct vnode **vpp;
156 IN struct componentname *cnp;
157 IN struct vattr *vap;
158
159 We are responsible for freeing the namei buffer, it is done in hfs_makenode(), unless there is
160 a previous error.
161
162 */
163
164 int
165 synthfs_create(ap)
166 struct vop_create_args /* {
167 struct vnode *a_dvp;
168 struct vnode **a_vpp;
169 struct componentname *a_cnp;
170 struct vattr *a_vap;
171 } */ *ap;
172 {
173 #if DEBUG
174 struct vnode *dvp = ap->a_dvp;
175 char debugmsg[255];
176
177 sprintf(debugmsg, "synthfs_create: attempt to create '%s' in '%s' ?!", ap->a_cnp->cn_nameptr, VTOS(dvp)->s_name);
178 Debugger(debugmsg);
179 #endif
180
181 return err_create(ap);
182 }
183
184
185
186 /*
187 * Open called.
188 #% open vp L L L
189 #
190 vop_open {
191 IN struct vnode *vp;
192 IN int mode;
193 IN struct ucred *cred;
194 IN struct proc *p;
195 */
196
197 int
198 synthfs_open(ap)
199 struct vop_open_args /* {
200 struct vnode *a_vp;
201 int a_mode;
202 struct ucred *a_cred;
203 struct proc *a_p;
204 } */ *ap;
205 {
206 struct vnode *vp = ap->a_vp;
207
208 if (vp->v_type == VDIR) {
209 return 0;
210 } else {
211 #if DEBUG
212 struct synthfsnode *sp = VTOS(vp);
213 char debugmsg[255];
214
215 sprintf(debugmsg, "synthfs_open: attempt to open '/%s' ?!", sp->s_name);
216 Debugger(debugmsg);
217 #endif
218 };
219
220 return 0;
221 }
222
223
224
225 /*
226 * Mmap a file
227 *
228 * NB Currently unsupported.
229 # XXX - not used
230 #
231 vop_mmap {
232 IN struct vnode *vp;
233 IN int fflags;
234 IN struct ucred *cred;
235 IN struct proc *p;
236
237 */
238
239 /* ARGSUSED */
240
241 int
242 synthfs_mmap(ap)
243 struct vop_mmap_args /* {
244 struct vnode *a_vp;
245 int a_fflags;
246 struct ucred *a_cred;
247 struct proc *a_p;
248 } */ *ap;
249 {
250 #if DEBUG
251 struct vnode *vp = ap->a_vp;
252 char debugmsg[255];
253
254 sprintf(debugmsg, "synthfs_mmap: attempt to map '/%s' ?!", VTOS(vp)->s_name);
255 Debugger(debugmsg);
256 #endif
257
258 return EINVAL;
259 }
260
261
262
263 /*
264 #% access vp L L L
265 #
266 vop_access {
267 IN struct vnode *vp;
268 IN int mode;
269 IN struct ucred *cred;
270 IN struct proc *p;
271
272 */
273
274 int
275 synthfs_access(ap)
276 struct vop_access_args /* {
277 struct vnode *a_vp;
278 int a_mode;
279 struct ucred *a_cred;
280 struct proc *a_p;
281 } */ *ap;
282 {
283 struct vnode *vp = ap->a_vp;
284 mode_t mode = ap->a_mode;
285 struct ucred *cred = ap->a_cred;
286 struct synthfsnode *sp = VTOS(vp);
287 register gid_t *gp;
288 mode_t mask;
289 int retval = 0;
290 int i;
291
292 /*
293 * Disallow write attempts on read-only file systems;
294 * unless the file is a socket, fifo, or a block or
295 * character device resident on the file system.
296 */
297 if (mode & VWRITE) {
298 switch (vp->v_type) {
299 case VDIR:
300 case VLNK:
301 case VREG:
302 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
303 return (EROFS);
304 break;
305 default:
306 break;
307 }
308 }
309
310 /* If immutable bit set, nobody gets to write it. */
311 if ((mode & VWRITE) && (sp->s_flags & IMMUTABLE))
312 return (EPERM);
313
314 /* Otherwise, user id 0 always gets access. */
315 if (ap->a_cred->cr_uid == 0) {
316 retval = 0;
317 goto Exit;
318 };
319
320 mask = 0;
321
322 /* Otherwise, check the owner. */
323 if (cred->cr_uid == sp->s_uid) {
324 if (mode & VEXEC)
325 mask |= S_IXUSR;
326 if (mode & VREAD)
327 mask |= S_IRUSR;
328 if (mode & VWRITE)
329 mask |= S_IWUSR;
330 retval = ((sp->s_mode & mask) == mask ? 0 : EACCES);
331 goto Exit;
332 }
333
334 /* Otherwise, check the groups. */
335 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
336 if (sp->s_gid == *gp) {
337 if (mode & VEXEC)
338 mask |= S_IXGRP;
339 if (mode & VREAD)
340 mask |= S_IRGRP;
341 if (mode & VWRITE)
342 mask |= S_IWGRP;
343 retval = ((sp->s_mode & mask) == mask ? 0 : EACCES);
344 goto Exit;
345 }
346
347 /* Otherwise, check everyone else. */
348 if (mode & VEXEC)
349 mask |= S_IXOTH;
350 if (mode & VREAD)
351 mask |= S_IROTH;
352 if (mode & VWRITE)
353 mask |= S_IWOTH;
354 retval = ((sp->s_mode & mask) == mask ? 0 : EACCES);
355
356 Exit:
357 return (retval);
358 }
359
360 /*
361 #% getattr vp = = =
362 #
363 vop_getattr {
364 IN struct vnode *vp;
365 IN struct vattr *vap;
366 IN struct ucred *cred;
367 IN struct proc *p;
368
369 */
370 int
371 synthfs_getattr(ap)
372 struct vop_getattr_args /* {
373 struct vnode *a_vp;
374 struct vattr *a_vap;
375 struct ucred *a_cred;
376 struct proc *a_p;
377 } */ *ap;
378 {
379 struct vnode *vp = ap->a_vp;
380 struct vattr *vap = ap->a_vap;
381 struct synthfsnode *sp = VTOS(vp);
382 struct synthfs_mntdata *smp = VTOSFS(vp);
383
384 vap->va_type = vp->v_type;
385 vap->va_mode = sp->s_mode;
386 vap->va_nlink = sp->s_linkcount;
387 vap->va_uid = sp->s_uid;
388 vap->va_gid = sp->s_gid;
389 vap->va_fsid = VTOVFS(vp)->mnt_stat.f_fsid.val[0];
390 vap->va_fileid = sp->s_nodeid;
391 switch (vp->v_type) {
392 case VDIR:
393 vap->va_size = (sp->s_u.d.d_entrycount + 2) * sizeof(struct dirent);
394 break;
395
396 case VREG:
397 vap->va_size = sp->s_u.f.f_size;
398 break;
399
400 case VLNK:
401 vap->va_size = sp->s_u.s.s_length;
402 break;
403
404 default:
405 vap->va_size = 0;
406 };
407 vap->va_blocksize = 512;
408 vap->va_atime.tv_sec = sp->s_accesstime.tv_sec;
409 vap->va_atime.tv_nsec = sp->s_accesstime.tv_usec * 1000;
410 vap->va_mtime.tv_sec = sp->s_modificationtime.tv_sec;
411 vap->va_mtime.tv_nsec = sp->s_modificationtime.tv_usec * 1000;
412 vap->va_ctime.tv_sec = sp->s_changetime.tv_sec;
413 vap->va_ctime.tv_nsec = sp->s_changetime.tv_usec * 1000;
414 vap->va_gen = sp->s_generation;
415 vap->va_flags = sp->s_flags;
416 vap->va_rdev = sp->s_rdev;
417 vap->va_bytes = vap->va_blocksize * ((vap->va_size + vap->va_blocksize - 1) / vap->va_blocksize);
418 vap->va_filerev = 0;
419 vap->va_vaflags = 0;
420
421 return (0);
422 }
423
424
425
426 /*
427 * Change the mode on a file or directory.
428 * vnode vp must be locked on entry.
429 */
430 int synthfs_chmod(struct vnode *vp, int mode, struct ucred *cred, struct proc *p)
431 {
432 struct synthfsnode *sp = VTOS(vp);
433 int result;
434
435 if ((cred->cr_uid != sp->s_uid) &&
436 (result = suser(cred, &p->p_acflag)))
437 return result;
438 if (cred->cr_uid) {
439 if (vp->v_type != VDIR && (mode & S_ISTXT))
440 return EFTYPE;
441 if (!groupmember(sp->s_gid, cred) && (mode & S_ISGID))
442 return (EPERM);
443 }
444 sp->s_mode &= ~ALLPERMS;
445 sp->s_mode |= (mode & ALLPERMS);
446 sp->s_nodeflags |= IN_CHANGE;
447 #if RWSUPPORT
448 if ((vp->v_flag & VTEXT) && (sp->s_mode & S_ISTXT) == 0) (void) vnode_uncache(vp);
449 #endif
450
451 return 0;
452 }
453
454
455
456 /*
457 * Change the flags on a file or directory.
458 * vnode vp must be locked on entry.
459 */
460 int synthfs_chflags(struct vnode *vp, u_long flags, struct ucred *cred, struct proc *p)
461 {
462 struct synthfsnode *sp = VTOS(vp);
463 int result;
464
465 if (cred->cr_uid != sp->s_uid &&
466 (result = suser(cred, &p->p_acflag)))
467 return result;
468
469 if (cred->cr_uid == 0) {
470 if ((sp->s_flags & (SF_IMMUTABLE | SF_APPEND)) &&
471 securelevel > 0) {
472 return EPERM;
473 };
474 sp->s_flags = flags;
475 } else {
476 if (sp->s_flags & (SF_IMMUTABLE | SF_APPEND) ||
477 (flags & UF_SETTABLE) != flags) {
478 return EPERM;
479 };
480 sp->s_flags &= SF_SETTABLE;
481 sp->s_flags |= (flags & UF_SETTABLE);
482 }
483 sp->s_nodeflags |= IN_CHANGE;
484
485 return 0;
486 }
487
488
489
490 /*
491 * Perform chown operation on vnode vp;
492 * vnode vp must be locked on entry.
493 */
494 int synthfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p)
495 {
496 struct synthfsnode *sp = VTOS(vp);
497 uid_t ouid;
498 gid_t ogid;
499 int result = 0;
500
501 if (uid == (uid_t)VNOVAL) uid = sp->s_uid;
502 if (gid == (gid_t)VNOVAL) gid = sp->s_gid;
503
504 /*
505 * If we don't own the file, are trying to change the owner
506 * of the file, or are not a member of the target group,
507 * the caller must be superuser or the call fails.
508 */
509 if ((cred->cr_uid != sp->s_uid || uid != sp->s_uid ||
510 (gid != sp->s_gid && !groupmember((gid_t)gid, cred))) &&
511 (result = suser(cred, &p->p_acflag)))
512 return result;
513
514 ogid = sp->s_gid;
515 ouid = sp->s_uid;
516
517 sp->s_gid = gid;
518 sp->s_uid = uid;
519
520 if (ouid != uid || ogid != gid) sp->s_nodeflags |= IN_CHANGE;
521 if (ouid != uid && cred->cr_uid != 0) sp->s_mode &= ~S_ISUID;
522 if (ogid != gid && cred->cr_uid != 0) sp->s_mode &= ~S_ISGID;
523
524 return 0;
525 }
526
527
528
529 /*
530 * Set attribute vnode op. called from several syscalls
531 #% setattr vp L L L
532 #
533 vop_setattr {
534 IN struct vnode *vp;
535 IN struct vattr *vap;
536 IN struct ucred *cred;
537 IN struct proc *p;
538
539 */
540
541 int
542 synthfs_setattr(ap)
543 struct vop_setattr_args /* {
544 struct vnode *a_vp;
545 struct vattr *a_vap;
546 struct ucred *a_cred;
547 struct proc *a_p;
548 } */ *ap;
549 {
550 struct vnode *vp = ap->a_vp;
551 struct synthfsnode *sp = VTOS(vp);
552 struct vattr *vap = ap->a_vap;
553 struct ucred *cred = ap->a_cred;
554 struct proc *p = ap->a_p;
555 struct timeval atimeval, mtimeval;
556 int result;
557
558 /*
559 * Check for unsettable attributes.
560 */
561 if (((vap->va_type != VNON) && (vap->va_type != vp->v_type)) ||
562 (vap->va_nlink != VNOVAL) ||
563 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
564 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
565 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
566 result = EINVAL;
567 goto Err_Exit;
568 }
569
570 if (vap->va_flags != VNOVAL) {
571 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
572 result = EROFS;
573 goto Err_Exit;
574 };
575 if ((result = synthfs_chflags(vp, vap->va_flags, cred, p))) {
576 goto Err_Exit;
577 };
578 if (vap->va_flags & (IMMUTABLE | APPEND)) {
579 result = 0;
580 goto Err_Exit;
581 };
582 }
583
584 if (sp->s_flags & (IMMUTABLE | APPEND)) {
585 result = EPERM;
586 goto Err_Exit;
587 };
588
589 /*
590 * Go through the fields and update iff not VNOVAL.
591 */
592 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
593 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
594 result = EROFS;
595 goto Err_Exit;
596 };
597 if ((result = synthfs_chown(vp, vap->va_uid, vap->va_gid, cred, p))) {
598 goto Err_Exit;
599 };
600 }
601 if (vap->va_size != VNOVAL) {
602 /*
603 * Disallow write attempts on read-only file systems;
604 * unless the file is a socket, fifo, or a block or
605 * character device resident on the file system.
606 */
607 switch (vp->v_type) {
608 case VDIR:
609 result = EISDIR;
610 goto Err_Exit;
611 case VLNK:
612 case VREG:
613 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
614 result = EROFS;
615 goto Err_Exit;
616 };
617 break;
618 default:
619 break;
620 }
621 #if RWSUPPORT
622 if ((result = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p))) {
623 goto Err_Exit;
624 };
625 #else
626 result = EINVAL;
627 goto Err_Exit;
628 #endif
629 }
630
631 sp = VTOS(vp);
632 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
633 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
634 result = EROFS;
635 goto Err_Exit;
636 };
637 if (cred->cr_uid != sp->s_uid &&
638 (result = suser(cred, &p->p_acflag)) &&
639 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
640 (result = VOP_ACCESS(vp, VWRITE, cred, p)))) {
641 goto Err_Exit;
642 };
643 if (vap->va_atime.tv_sec != VNOVAL)
644 sp->s_nodeflags |= IN_ACCESS;
645 if (vap->va_mtime.tv_sec != VNOVAL)
646 sp->s_nodeflags |= IN_CHANGE | IN_UPDATE;
647 atimeval.tv_sec = vap->va_atime.tv_sec;
648 atimeval.tv_usec = vap->va_atime.tv_nsec / 1000;
649 mtimeval.tv_sec = vap->va_mtime.tv_sec;
650 mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000;
651 if ((result = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))) {
652 goto Err_Exit;
653 };
654 }
655
656 result = 0;
657 if (vap->va_mode != (mode_t)VNOVAL) {
658 if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
659 result = EROFS;
660 goto Err_Exit;
661 };
662 result = synthfs_chmod(vp, (int)vap->va_mode, cred, p);
663 };
664
665 Err_Exit: ;
666
667 DBG_VOP(("synthfs_setattr: returning %d...\n", result));
668
669 return (result);
670 }
671
672
673
674 /*
675
676 #% rename sourcePar_vp U U U
677 #% rename source_vp U U U
678 #% rename targetPar_vp L U U
679 #% rename target_vp X U U
680 #
681 vop_rename {
682 IN WILLRELE struct vnode *sourcePar_vp;
683 IN WILLRELE struct vnode *source_vp;
684 IN struct componentname *source_cnp;
685 IN WILLRELE struct vnode *targetPar_vp;
686 IN WILLRELE struct vnode *target_vp;
687 IN struct componentname *target_cnp;
688
689
690 */
691
692 /*
693 * On entry:
694 * source's parent directory is unlocked
695 * source file or directory is unlocked
696 * destination's parent directory is locked
697 * destination file or directory is locked if it exists
698 *
699 * On exit:
700 * all denodes should be released
701 *
702 */
703
704 int
705 synthfs_rename(ap)
706 struct vop_rename_args /* {
707 struct vnode *a_fdvp;
708 struct vnode *a_fvp;
709 struct componentname *a_fcnp;
710 struct vnode *a_tdvp;
711 struct vnode *a_tvp;
712 struct componentname *a_tcnp;
713 } */ *ap;
714 {
715 struct vnode *target_vp = ap->a_tvp;
716 struct vnode *targetPar_vp = ap->a_tdvp;
717 struct vnode *source_vp = ap->a_fvp;
718 struct vnode *sourcePar_vp = ap->a_fdvp;
719 struct componentname *target_cnp = ap->a_tcnp;
720 struct componentname *source_cnp = ap->a_fcnp;
721 struct proc *p = source_cnp->cn_proc;
722 struct synthfsnode *target_sp, *targetPar_sp, *source_sp, *sourcePar_sp;
723 u_short doingdirectory = 0, oldparent = 0, newparent = 0;
724 int retval = 0;
725 struct timeval tv;
726
727 #if SYNTHFS_DIAGNOSTIC
728 if ((target_cnp->cn_flags & HASBUF) == 0 ||
729 (source_cnp->cn_flags & HASBUF) == 0)
730 panic("synthfs_rename: no name");
731 #endif
732
733 DBG_ASSERT((ap->a_fdvp->v_type == VDIR) && (ap->a_tdvp->v_type == VDIR));
734 target_sp = targetPar_sp = source_sp = sourcePar_sp = NULL;
735
736 /*
737 * Check for cross-device rename.
738 */
739 if ((source_vp->v_mount != targetPar_vp->v_mount) ||
740 (target_vp && (source_vp->v_mount != target_vp->v_mount))) {
741 retval = EXDEV;
742 goto abortit;
743 }
744
745 /*
746 * Check for access permissions
747 */
748 if (target_vp && ((VTOS(target_vp)->s_pflags & (IMMUTABLE | APPEND)) ||
749 (VTOS(targetPar_vp)->s_pflags & APPEND))) {
750 retval = EPERM;
751 goto abortit;
752 }
753
754 if ((retval = vn_lock(source_vp, LK_EXCLUSIVE, p)))
755 goto abortit;
756
757 sourcePar_sp = VTOS(sourcePar_vp);
758 source_sp = VTOS(source_vp);
759 oldparent = sourcePar_sp->s_nodeid;
760 if ((source_sp->s_pflags & (IMMUTABLE | APPEND)) || (sourcePar_sp->s_pflags & APPEND)) {
761 VOP_UNLOCK(source_vp, 0, p);
762 retval = EPERM;
763 goto abortit;
764 }
765
766 /*
767 * Be sure we are not renaming ".", "..", or an alias of ".". This
768 * leads to a crippled directory tree. It's pretty tough to do a
769 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
770 * doesn't work if the ".." entry is missing.
771 */
772 if (source_sp->s_type == SYNTHFS_DIRECTORY) {
773 if ((source_cnp->cn_namelen == 1 && source_cnp->cn_nameptr[0] == '.')
774 || sourcePar_sp == source_sp
775 || (source_cnp->cn_flags & ISDOTDOT)
776 || (source_sp->s_nodeflags & IN_RENAME)) {
777 VOP_UNLOCK(source_vp, 0, p);
778 retval = EINVAL;
779 goto abortit;
780 }
781 source_sp->s_nodeflags |= IN_RENAME;
782 doingdirectory = TRUE;
783 }
784
785 /* Transit between abort and bad */
786
787 targetPar_sp = VTOS(targetPar_vp);
788 target_sp = target_vp ? VTOS(target_vp) : NULL;
789 newparent = targetPar_sp->s_nodeid;
790
791 retval = VOP_ACCESS(source_vp, VWRITE, target_cnp->cn_cred, target_cnp->cn_proc);
792 if (doingdirectory && (newparent != oldparent)) {
793 if (retval) /* write access check above */
794 goto bad;
795 }
796
797 /*
798 * If the destination exists, then be sure its type (file or dir)
799 * matches that of the source. And, if it is a directory make sure
800 * it is empty. Then delete the destination.
801 */
802 if (target_vp) {
803 /*
804 * If the parent directory is "sticky", then the user must
805 * own the parent directory, or the destination of the rename,
806 * otherwise the destination may not be changed (except by
807 * root). This implements append-only directories.
808 */
809 if ((targetPar_sp->s_mode & S_ISTXT) && target_cnp->cn_cred->cr_uid != 0 &&
810 target_cnp->cn_cred->cr_uid != targetPar_sp->s_uid &&
811 target_sp->s_uid != target_cnp->cn_cred->cr_uid) {
812 retval = EPERM;
813 goto bad;
814 }
815
816 /*
817 * VOP_REMOVE will vput targetPar_vp so we better bump
818 * its ref count and relockit, always set target_vp to
819 * NULL afterwards to indicate that were done with it.
820 */
821 VREF(targetPar_vp);
822 #if RWSUPPORT
823 if (target_vp->v_type == VREG) {
824 (void) vnode_uncache(target_vp);
825 };
826 #endif
827 cache_purge(target_vp);
828
829 target_cnp->cn_flags &= ~SAVENAME;
830 retval = VOP_REMOVE(targetPar_vp, target_vp, target_cnp);
831 (void) vn_lock(targetPar_vp, LK_EXCLUSIVE | LK_RETRY, p);
832
833 target_vp = NULL;
834 target_sp = NULL;
835
836 if (retval) goto bad;
837 };
838
839
840 if (newparent != oldparent)
841 vn_lock(sourcePar_vp, LK_EXCLUSIVE | LK_RETRY, p);
842
843 /* remove the existing entry from the namei cache: */
844 if (source_vp->v_type == VREG) cache_purge(source_vp);
845
846 retval = synthfs_move_rename_entry( source_vp, targetPar_vp, target_cnp->cn_nameptr);
847
848 if (newparent != oldparent)
849 VOP_UNLOCK(sourcePar_vp, 0, p);
850
851 if (retval) goto bad;
852
853 source_sp->s_nodeflags &= ~IN_RENAME;
854
855 /*
856 * Timestamp both parent directories.
857 * Note that if this is a rename within the same directory,
858 * (where targetPar_hp == sourcePar_hp)
859 * the code below is still safe and correct.
860 */
861 targetPar_sp->s_nodeflags |= IN_UPDATE;
862 sourcePar_sp->s_nodeflags |= IN_UPDATE;
863 tv = time;
864 SYNTHFSTIMES(targetPar_sp, &tv, &tv);
865 SYNTHFSTIMES(sourcePar_sp, &tv, &tv);
866
867 vput(targetPar_vp);
868 vrele(sourcePar_vp);
869 vput(source_vp);
870
871 return (retval);
872
873 bad:;
874 if (retval && doingdirectory)
875 source_sp->s_nodeflags &= ~IN_RENAME;
876
877 if (targetPar_vp == target_vp)
878 vrele(targetPar_vp);
879 else
880 vput(targetPar_vp);
881
882 if (target_vp)
883 vput(target_vp);
884
885 vrele(sourcePar_vp);
886
887 if (VOP_ISLOCKED(source_vp))
888 vput(source_vp);
889 else
890 vrele(source_vp);
891
892 return (retval);
893
894 abortit:;
895
896 VOP_ABORTOP(targetPar_vp, target_cnp); /* XXX, why not in NFS? */
897
898 if (targetPar_vp == target_vp)
899 vrele(targetPar_vp);
900 else
901 vput(targetPar_vp);
902
903 if (target_vp)
904 vput(target_vp);
905
906 VOP_ABORTOP(sourcePar_vp, source_cnp); /* XXX, why not in NFS? */
907
908 vrele(sourcePar_vp);
909 vrele(source_vp);
910
911 return (retval);
912 }
913
914
915
916 /*
917 * Mkdir system call
918
919 #% mkdir dvp L U U
920 #% mkdir vpp - L -
921 #
922 vop_mkdir {
923 IN WILLRELE struct vnode *dvp;
924 OUT struct vnode **vpp;
925 IN struct componentname *cnp;
926 IN struct vattr *vap;
927
928 We are responsible for freeing the namei buffer, it is done in synthfs_makenode(), unless there is
929 a previous error.
930
931 */
932
933 int
934 synthfs_mkdir(ap)
935 struct vop_mkdir_args /* {
936 struct vnode *a_dvp;
937 struct vnode **a_vpp;
938 struct componentname *a_cnp;
939 struct vattr *a_vap;
940 } */ *ap;
941 {
942 int retval;
943 struct vnode *dvp = ap->a_dvp;
944 struct componentname *cnp = ap->a_cnp;
945 int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
946 struct vnode *vp = NULL;
947
948 *ap->a_vpp = NULL;
949
950 retval = synthfs_new_directory(VTOVFS(dvp), dvp, cnp->cn_nameptr, VTOSFS(dvp)->synthfs_nextid++, mode, ap->a_cnp->cn_proc, &vp);
951 if (retval) goto Error_Exit;
952
953 retval = VOP_SETATTR(vp, ap->a_vap, cnp->cn_cred, cnp->cn_proc);
954 if (retval != 0) goto Error_Exit;
955
956 *ap->a_vpp = vp;
957
958 Error_Exit:;
959 if (retval != 0) {
960 if (vp) synthfs_remove_directory(vp);
961 VOP_ABORTOP(dvp, cnp);
962 }
963 vput(dvp);
964
965 return retval;
966 }
967
968
969
970 /*
971
972 #% remove dvp L U U
973 #% remove vp L U U
974 #
975 vop_remove {
976 IN WILLRELE struct vnode *dvp;
977 IN WILLRELE struct vnode *vp;
978 IN struct componentname *cnp;
979
980 */
981
982 int
983 synthfs_remove(ap)
984 struct vop_remove_args /* {
985 struct vnode *a_dvp;
986 struct vnode *a_vp;
987 struct componentname *a_cnp;
988 } */ *ap;
989 {
990 struct vnode *vp = ap->a_vp;
991 struct vnode *dvp = ap->a_dvp;
992 struct synthfsnode *sp = VTOS(vp);
993 struct timeval tv;
994 int retval = 0;
995
996 if ((sp->s_flags & (IMMUTABLE | APPEND)) ||
997 (VTOS(dvp)->s_flags & APPEND)) {
998 retval = EPERM;
999 goto out;
1000 };
1001
1002 /* This is sort of silly right now but someday it may make sense... */
1003 if (sp->s_nodeflags & IN_MODIFIED) {
1004 tv = time;
1005 VOP_UPDATE(vp, &tv, &tv, 0);
1006 };
1007
1008 /* remove the entry from the namei cache: */
1009 cache_purge(vp);
1010
1011 /* remove entry from tree and reclaim any resources consumed: */
1012 switch (sp->s_type) {
1013 case SYNTHFS_DIRECTORY:
1014 synthfs_remove_directory(vp);
1015 break;
1016
1017
1018 case SYNTHFS_SYMLINK:
1019 synthfs_remove_symlink(vp);
1020 break;
1021
1022 case SYNTHFS_FILE:
1023 /* Fall through to default case */
1024
1025 default:
1026 synthfs_remove_entry(vp);
1027 };
1028
1029 out:
1030
1031 if (! retval)
1032 VTOS(dvp)->s_nodeflags |= IN_CHANGE | IN_UPDATE;
1033
1034 if (dvp == vp) {
1035 vrele(vp);
1036 } else {
1037 vput(vp);
1038 };
1039
1040 vput(dvp);
1041 return (retval);
1042 }
1043
1044
1045
1046 /*
1047 #% rmdir dvp L U U
1048 #% rmdir vp L U U
1049 #
1050 vop_rmdir {
1051 IN WILLRELE struct vnode *dvp;
1052 IN WILLRELE struct vnode *vp;
1053 IN struct componentname *cnp;
1054
1055 */
1056
1057 int
1058 synthfs_rmdir(ap)
1059 struct vop_rmdir_args /* {
1060 struct vnode *a_dvp;
1061 struct vnode *a_vp;
1062 struct componentname *a_cnp;
1063 } */ *ap;
1064 {
1065 DBG_VOP(("synthfs_rmdir called\n"));
1066 return synthfs_remove((struct vop_remove_args *)ap);
1067 }
1068
1069
1070
1071 /*
1072 * synthfs_select - just say OK. Only possible op is readdir
1073 *
1074 * Locking policy: ignore
1075 */
1076 int
1077 synthfs_select(ap)
1078 struct vop_select_args /* {
1079 struct vnode *a_vp;
1080 int a_which;
1081 int a_fflags;
1082 struct ucred *a_cred;
1083 void *a_wql;
1084 struct proc *a_p;
1085 } */ *ap;
1086 {
1087 DBG_VOP(("synthfs_select called\n"));
1088
1089 return (1);
1090 }
1091
1092 /*
1093 #
1094 #% symlink dvp L U U
1095 #% symlink vpp - U -
1096 #
1097 # XXX - note that the return vnode has already been vrele'ed
1098 # by the filesystem layer. To use it you must use vget,
1099 # possibly with a further namei.
1100 #
1101 vop_symlink {
1102 IN WILLRELE struct vnode *dvp;
1103 OUT WILLRELE struct vnode **vpp;
1104 IN struct componentname *cnp;
1105 IN struct vattr *vap;
1106 IN char *target;
1107
1108 We are responsible for freeing the namei buffer, it is done in synthfs_makenode(), unless there is
1109 a previous error.
1110
1111
1112 */
1113
1114 int
1115 synthfs_symlink(ap)
1116 struct vop_symlink_args /* {
1117 struct vnode *a_dvp;
1118 struct vnode **a_vpp;
1119 struct componentname *a_cnp;
1120 struct vattr *a_vap;
1121 char *a_target;
1122 } */ *ap;
1123 {
1124 struct vnode *dvp = ap->a_dvp;
1125 struct vnode **vpp = ap->a_vpp;
1126 struct componentname *cnp = ap->a_cnp;
1127 int retval;
1128
1129 *vpp = NULL;
1130
1131 retval = synthfs_new_symlink(VTOVFS(dvp), dvp, cnp->cn_nameptr, VTOSFS(dvp)->synthfs_nextid++, ap->a_target, ap->a_cnp->cn_proc, vpp);
1132 if (retval) goto Error_Exit;
1133
1134 VOP_UNLOCK(*vpp, 0, cnp->cn_proc);
1135
1136 Error_Exit:;
1137
1138 if (retval != 0) {
1139 VOP_ABORTOP(dvp, cnp);
1140 }
1141 vput(dvp);
1142
1143 return (retval);
1144 }
1145
1146
1147
1148 /*
1149 #
1150 #% readlink vp L L L
1151 #
1152 vop_readlink {
1153 IN struct vnode *vp;
1154 INOUT struct uio *uio;
1155 IN struct ucred *cred;
1156 */
1157
1158 int
1159 synthfs_readlink(ap)
1160 struct vop_readlink_args /* {
1161 struct vnode *a_vp;
1162 struct uio *a_uio;
1163 struct ucred *a_cred;
1164 } */ *ap;
1165 {
1166 struct vnode *vp = ap->a_vp;
1167 struct synthfsnode *sp = VTOS(vp);
1168 struct uio *uio = ap->a_uio;
1169 int retval;
1170 unsigned long count;
1171
1172 if (ap->a_uio->uio_offset > sp->s_u.s.s_length) {
1173 return 0;
1174 };
1175
1176 if (uio->uio_offset + uio->uio_resid <= sp->s_u.s.s_length) {
1177 count = uio->uio_resid;
1178 } else {
1179 count = sp->s_u.s.s_length - uio->uio_offset;
1180 };
1181 retval = uiomove((void *)((unsigned char *)sp->s_u.s.s_symlinktarget + uio->uio_offset), count, uio);
1182 return (retval);
1183
1184 }
1185
1186
1187
1188
1189
1190
1191 /*
1192 #% readdir vp L L L
1193 #
1194 vop_readdir {
1195 IN struct vnode *vp;
1196 INOUT struct uio *uio;
1197 IN struct ucred *cred;
1198 INOUT int *eofflag;
1199 OUT int *ncookies;
1200 INOUT u_long **cookies;
1201 */
1202
1203
1204 int
1205 synthfs_readdir(ap)
1206 struct vop_readdir_args /* {
1207 struct vnode *vp;
1208 struct uio *uio;
1209 struct ucred *cred;
1210 int *eofflag;
1211 int *ncookies;
1212 u_long **cookies;
1213 } */ *ap;
1214 {
1215 struct synthfsnode *sp = VTOS(ap->a_vp);
1216 register struct uio *uio = ap->a_uio;
1217 off_t diroffset; /* Offset into simulated directory file */
1218 struct synthfsnode *entry;
1219
1220 DBG_VOP(("\tuio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid));
1221
1222 /* We assume it's all one big buffer... */
1223 if (uio->uio_iovcnt > 1) {
1224 DBG_VOP(("\tuio->uio_iovcnt = %d?\n", uio->uio_iovcnt));
1225 return EINVAL;
1226 };
1227
1228 /*
1229 NFS cookies are not supported:
1230 */
1231 if ((ap->a_cookies != NULL) || (ap->a_ncookies != NULL)) {
1232 return EINVAL;
1233 };
1234
1235 diroffset = 0;
1236
1237 /*
1238 * We must synthesize . and ..
1239 */
1240 DBG_VOP(("\tstarting ... uio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid));
1241 if (uio->uio_offset == diroffset)
1242 {
1243 DBG_VOP(("\tAdding .\n"));
1244 diroffset += synthfs_adddirentry(sp->s_nodeid, DT_DIR, ".", uio);
1245 DBG_VOP(("\t after adding ., uio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid));
1246 }
1247 if ((uio->uio_resid > 0) && (diroffset > uio->uio_offset)) {
1248 /* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */
1249 return EINVAL;
1250 };
1251
1252 if (uio->uio_offset == diroffset)
1253 {
1254 DBG_VOP(("\tAdding ..\n"));
1255 if (sp->s_parent != NULL) {
1256 diroffset += synthfs_adddirentry(sp->s_parent->s_nodeid, DT_DIR, "..", uio);
1257 } else {
1258 diroffset += synthfs_adddirentry(sp->s_nodeid, DT_DIR, "..", uio);
1259 }
1260 DBG_VOP(("\t after adding .., uio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid));
1261 }
1262 if ((uio->uio_resid > 0) && (diroffset > uio->uio_offset)) {
1263 /* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */
1264 return EINVAL;
1265 };
1266
1267 /* OK, so much for the fakes. Now for the "real thing": */
1268 TAILQ_FOREACH(entry, &sp->s_u.d.d_subnodes, s_sibling) {
1269 if (diroffset == uio->uio_offset) {
1270 /* Return this entry */
1271 diroffset += synthfs_adddirentry(entry->s_nodeid, VTTOIF(STOV(entry)->v_type), entry->s_name, uio);
1272 };
1273 if ((uio->uio_resid > 0) && (diroffset > uio->uio_offset)) {
1274 /* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */
1275 return EINVAL;
1276 };
1277 };
1278
1279 if (ap->a_eofflag)
1280 *ap->a_eofflag = (entry == NULL); /* If we ran all the way through the list, there is no more */
1281
1282 return 0;
1283 }
1284
1285
1286
1287 /*
1288
1289 #% lookup dvp L ? ?
1290 #% lookup vpp - L -
1291
1292 */
1293
1294 int
1295 synthfs_cached_lookup(ap)
1296 struct vop_cachedlookup_args /* {
1297 struct vnode *a_dvp;
1298 struct vnode **a_vpp;
1299 struct componentname *a_cnp;
1300 } */ *ap;
1301 {
1302 struct vnode *dp = ap->a_dvp;
1303 struct componentname *cnp = ap->a_cnp;
1304 u_long nameiop = cnp->cn_nameiop;
1305 u_long flags = cnp->cn_flags;
1306 boolean_t lockparent = (flags & LOCKPARENT);
1307 struct proc *p = cnp->cn_proc;
1308 struct ucred *cred = cnp->cn_cred;
1309 struct vnode *target_vp = NULL;
1310 u_int32_t target_vnode_id; /* Capability ID of the target vnode for .. unlock/relock handling check */
1311 struct vnode **vpp = ap->a_vpp;
1312 int result = 0;
1313
1314 DBG_VOP(("synthfs_cached_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen));
1315 if (flags & LOCKPARENT) DBG_VOP(("\tLOCKPARENT is set\n"));
1316 if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n"));
1317
1318 *vpp = NULL;
1319
1320 if (dp->v_type != VDIR) {
1321 result = ENOTDIR;
1322 goto Err_Exit;
1323 };
1324
1325 if ((flags & ISLASTCN) &&
1326 (VTOVFS(dp)->mnt_flag & MNT_RDONLY) &&
1327 ((nameiop == DELETE) || (nameiop == RENAME))) {
1328 result = EROFS;
1329 goto Err_Exit;
1330 };
1331
1332 result = VOP_ACCESS(dp, VEXEC, cred, cnp->cn_proc);
1333 if (result != 0) goto Err_Exit;
1334
1335 /*
1336 * Look up an entry in the namei cache
1337 */
1338
1339 result = cache_lookup(dp, vpp, cnp);
1340 if (result == 0) {
1341 /* There was no entry in the cache for this parent vnode/name pair:
1342 do the full-blown pathname lookup
1343 */
1344 return synthfs_lookup(ap);
1345 };
1346 if (result == ENOENT) return result;
1347
1348 /* An entry matching the parent vnode/name was found in the cache: */
1349
1350
1351 target_vp = *vpp;
1352 target_vnode_id = target_vp->v_id;
1353 if (target_vp == dp) {
1354 /* lookup on "." */
1355 VREF(target_vp);
1356 result = 0;
1357 } else if (flags & ISDOTDOT) {
1358 /*
1359 * Carefully now: trying to step from child to parent;
1360 * must release lock on child before trying to lock parent
1361 * vnode.
1362 */
1363 VOP_UNLOCK(dp, 0, p);
1364 result = vget(target_vp, LK_EXCLUSIVE, p);
1365 if ((result == 0) && lockparent && (flags & ISLASTCN)) {
1366 result = vn_lock(dp, LK_EXCLUSIVE, p);
1367 }
1368 } else {
1369 result = vget(target_vp, LK_EXCLUSIVE, p);
1370 if (!lockparent || (result != 0) || !(flags & ISLASTCN)) {
1371 VOP_UNLOCK(dp, 0, p);
1372 };
1373 };
1374
1375 /*
1376 Check to make sure the target vnode ID didn't change while we
1377 tried to lock it:
1378 */
1379 if (result == 0) {
1380 if (target_vnode_id == target_vp->v_id) {
1381 return 0; /* THIS IS THE NORMAL EXIT PATH */
1382 };
1383
1384 /* The vnode ID didn't match anymore: we've got another vnode! */
1385 vput(target_vp);
1386 /* Unlock the parent vnode in the cases where it should've been left locked: */
1387 if (lockparent && (dp != target_vp) && (flags & ISLASTCN)) {
1388 VOP_UNLOCK(dp, 0, p);
1389 };
1390 };
1391
1392 /* One last try for a successful lookup through the complete lookup path: */
1393 result = vn_lock(dp, LK_EXCLUSIVE, p);
1394 if (result == 0) {
1395 return synthfs_lookup(ap);
1396 };
1397
1398 Err_Exit:;
1399 return result;
1400 }
1401
1402
1403
1404 int
1405 synthfs_lookup(ap)
1406 struct vop_cachedlookup_args /* {
1407 struct vnode *a_dvp;
1408 struct vnode **a_vpp;
1409 struct componentname *a_cnp;
1410 } */ *ap;
1411 {
1412 struct vnode *dp = ap->a_dvp;
1413 struct synthfsnode *dsp = VTOS(dp);
1414 struct componentname *cnp = ap->a_cnp;
1415 u_long nameiop = cnp->cn_nameiop;
1416 // char *nameptr = cnp->cn_nameptr;
1417 u_long flags = cnp->cn_flags;
1418 long namelen = cnp->cn_namelen;
1419 struct proc *p = cnp->cn_proc;
1420 struct ucred *cred = cnp->cn_cred;
1421 struct synthfsnode *entry;
1422 struct vnode *target_vp = NULL;
1423 int result = 0;
1424 boolean_t found = FALSE;
1425 boolean_t isDot = FALSE;
1426 boolean_t isDotDot = FALSE;
1427 struct vnode *starting_parent = dp;
1428
1429 DBG_VOP(("synthfs_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen));
1430 if (flags & LOCKPARENT) DBG_VOP(("\tLOCKPARENT is set\n"));
1431 if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n"));
1432
1433 *ap->a_vpp = NULL;
1434
1435 if (dp->v_type != VDIR) {
1436 result = ENOTDIR;
1437 goto Err_Exit;
1438 };
1439
1440 if ((flags & ISLASTCN) &&
1441 (VTOVFS(dp)->mnt_flag & MNT_RDONLY) &&
1442 ((nameiop == DELETE) || (nameiop == RENAME))) {
1443 result = EROFS;
1444 goto Err_Exit;
1445 };
1446
1447 result = VOP_ACCESS(dp, VEXEC, cred, cnp->cn_proc);
1448 if (result != 0) goto Err_Exit;
1449
1450 /* first check for "." and ".." */
1451 if (cnp->cn_nameptr[0] == '.') {
1452 if (namelen == 1) {
1453 /*
1454 "." requested
1455 */
1456 isDot = TRUE;
1457 found = TRUE;
1458
1459 target_vp = dp;
1460 VREF(target_vp);
1461
1462 result = 0;
1463
1464 goto Std_Exit;
1465 } else if ((namelen == 2) && (cnp->cn_nameptr[1] == '.')) {
1466 /*
1467 ".." requested
1468 */
1469 isDotDot = TRUE;
1470 found = TRUE;
1471
1472 if ((dsp->s_parent != NULL) && (dsp->s_parent != VTOS(dp))) {
1473 target_vp = STOV(dsp->s_parent);
1474 /*
1475 * Special case for ".." to prevent deadlock:
1476 * always release the parent vnode BEFORE trying to acquire
1477 * ITS parent. This avoids deadlocking with another lookup
1478 * starting from the target_vp trying to vget() this directory.
1479 */
1480 VOP_UNLOCK(dp, 0, p);
1481 result = vget(target_vp, LK_EXCLUSIVE | LK_RETRY, p);
1482 if (result != 0) {
1483 vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
1484 goto Err_Exit;
1485 }
1486 if ((flags & LOCKPARENT) && (flags & ISLASTCN)) {
1487 result = vn_lock(dp, LK_EXCLUSIVE, p);
1488 // vput(target_vp); /* XXX WHY WAS THIS HERE? */
1489 }
1490 } else {
1491 target_vp = dp;
1492 /* dp is alread locked and ref'ed */
1493 result = 0;
1494 }
1495
1496 goto Std_Exit;
1497 }
1498 }
1499
1500 /* finally, just look for entries by name (making sure the entry's length
1501 matches the cnp's namelen... */
1502 TAILQ_FOREACH(entry, &dsp->s_u.d.d_subnodes, s_sibling) {
1503 if ((bcmp(cnp->cn_nameptr, entry->s_name, (unsigned)namelen) == 0) &&
1504 (*(entry->s_name + namelen) == (char)0)) {
1505 found = TRUE;
1506 target_vp = STOV(entry);
1507 result = vget(target_vp, LK_EXCLUSIVE | LK_RETRY, p); /* vget is not really needed because refcount is always > 0... */
1508 if (result != 0) {
1509 vrele(target_vp);
1510 goto Err_Exit;
1511 };
1512
1513 /* The specified entry was found and successfully acquired: */
1514 goto Std_Exit;
1515 };
1516 };
1517
1518 found = FALSE;
1519
1520 Std_Exit:;
1521 if (found) {
1522 if ((nameiop == DELETE) && (flags & ISLASTCN)) {
1523 /*
1524 * Deleting entries requires write access:
1525 */
1526 result = VOP_ACCESS(dp, VWRITE, cred, p);
1527 if (result != 0) goto Err_Exit;
1528
1529 /*
1530 * If the parent directory is "sticky" then the user must own
1531 * the directory, or the file in it, in order to be allowed to
1532 * delete it (unless the user is root). This implements
1533 * append-only directories
1534 */
1535 if ((dsp->s_mode & S_ISVTX) &&
1536 (cred->cr_uid != 0) &&
1537 (cred->cr_uid != dsp->s_uid) &&
1538 (target_vp != NULL) &&
1539 (target_vp->v_type != VLNK) &&
1540 (VTOS(target_vp)->s_uid != cred->cr_uid)) {
1541 vput(target_vp);
1542 result = EPERM;
1543 goto Err_Exit;
1544 };
1545 };
1546
1547 if ((nameiop == RENAME) && (flags & WANTPARENT) && (flags * ISLASTCN)) {
1548 result = VOP_ACCESS(dp, VWRITE, cred, p);
1549 if (result != 0) goto Err_Exit;
1550
1551 if (isDot) {
1552 vrele(target_vp);
1553 result = EISDIR;
1554 goto Err_Exit;
1555 };
1556 };
1557 } else {
1558 /* The specified entry wasn't found: */
1559 result = ENOENT;
1560
1561 if ((flags & ISLASTCN) &&
1562 ((nameiop == CREATE) ||
1563 (nameiop == RENAME) ||
1564 ((nameiop == DELETE) && (flags & DOWHITEOUT) && (flags & ISWHITEOUT)))) {
1565 /* Write access is required to create entries in the directory: */
1566 result = VOP_ACCESS(dp, VWRITE, cred, p);
1567 if (result != 0) goto Err_Exit;
1568
1569 cnp->cn_flags |= SAVENAME;
1570
1571 result = EJUSTRETURN;
1572 }
1573 };
1574
1575 /* XXX PPD Should we do something special in case LOCKLEAF isn't set? */
1576 if (found && !isDot && !isDotDot && (!(flags & LOCKPARENT) || !(flags & ISLASTCN))) {
1577 VOP_UNLOCK(dp, 0, p);
1578 };
1579
1580 *ap->a_vpp = target_vp;
1581
1582 Err_Exit:;
1583 DBG_VOP(("synthfs_lookup: result = %d.\n", result));
1584 if (found) {
1585 if (target_vp) {
1586 if (VOP_ISLOCKED(target_vp)) {
1587 DBG_VOP(("synthfs_lookup: target_vp = 0x%08X (locked).\n", (u_long)target_vp));
1588 } else {
1589 DBG_VOP(("synthfs_lookup: target_vp = 0x%08X (NOT locked?).\n", (u_long)target_vp));
1590 };
1591 } else {
1592 DBG_VOP(("synthfs_lookup: found = true but target_vp = NULL?\n"));
1593 };
1594 } else {
1595 DBG_VOP(("synthf_lookup: target not found.\n"));
1596 };
1597 if (VOP_ISLOCKED(starting_parent)) {
1598 DBG_VOP(("synthfs_lookup: dp = %08X; starting_parent = 0x%08X (LOCKED).\n", (u_long)dp, (u_long)starting_parent));
1599 } else {
1600 DBG_VOP(("synthfs_lookup: dp = %08X; starting_parent = 0x%08X (UNLOCKED).\n", (u_long)dp, (u_long)starting_parent));
1601 };
1602
1603 return result;
1604 }
1605
1606
1607
1608 /*
1609
1610 #% pathconf vp L L L
1611 #
1612 vop_pathconf {
1613 IN struct vnode *vp;
1614 IN int name;
1615 OUT register_t *retval;
1616 */
1617 int
1618 synthfs_pathconf(ap)
1619 struct vop_pathconf_args /* {
1620 struct vnode *a_vp;
1621 int a_name;
1622 int *a_retval;
1623 } */ *ap;
1624 {
1625 DBG_VOP(("synthfs_pathconf called\n"));
1626
1627 switch (ap->a_name)
1628 {
1629 case _PC_LINK_MAX:
1630 *ap->a_retval = LINK_MAX;
1631 return (0);
1632 case _PC_NAME_MAX:
1633 *ap->a_retval = NAME_MAX;
1634 return (0);
1635 case _PC_PATH_MAX:
1636 *ap->a_retval = PATH_MAX;
1637 return (0);
1638 case _PC_PIPE_BUF:
1639 *ap->a_retval = PIPE_BUF;
1640 return (0);
1641 case _PC_CHOWN_RESTRICTED:
1642 *ap->a_retval = 1;
1643 return (0);
1644 case _PC_NO_TRUNC:
1645 *ap->a_retval = 1;
1646 return (0);
1647 default:
1648 return (EINVAL);
1649 }
1650 /* NOTREACHED */
1651 }
1652
1653
1654 /*
1655 * Update the access, modified, and node change times as specified by the
1656 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
1657 * used to specify that the node needs to be updated but that the times have
1658 * already been set. The access and modified times are taken from the second
1659 * and third parameters; the node change time is always taken from the current
1660 * time. If waitfor is set, then wait for the disk write of the node to
1661 * complete.
1662 */
1663 /*
1664 #% update vp L L L
1665 IN struct vnode *vp;
1666 IN struct timeval *access;
1667 IN struct timeval *modify;
1668 IN int waitfor;
1669 */
1670
1671 int
1672 synthfs_update(ap)
1673 struct vop_update_args /* {
1674 struct vnode *a_vp;
1675 struct timeval *a_access;
1676 struct timeval *a_modify;
1677 int a_waitfor;
1678 } */ *ap;
1679 {
1680 struct vnode *vp = ap->a_vp;
1681 struct synthfsnode *sp = VTOS(vp);
1682
1683 DBG_ASSERT(sp != NULL);
1684 DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
1685
1686 if (((sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) != 0) &&
1687 !(VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY)) {
1688 if (sp->s_nodeflags & IN_ACCESS) sp->s_accesstime = *ap->a_access;
1689 if (sp->s_nodeflags & IN_UPDATE) sp->s_modificationtime = *ap->a_modify;
1690 if (sp->s_nodeflags & IN_CHANGE) sp->s_changetime = time;
1691 };
1692
1693 /* After the updates are finished, clear the flags */
1694 sp->s_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
1695
1696 // DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0);
1697 return 0;
1698 }
1699
1700
1701
1702 /*******************************************************************************************
1703
1704 Utility/housekeeping vnode operations:
1705
1706 ******************************************************************************************/
1707
1708
1709 /*
1710 #% lock vp U L U
1711 #
1712 vop_lock {
1713 IN struct vnode *vp;
1714 IN int flags;
1715 IN struct proc *p;
1716 */
1717
1718 int
1719 synthfs_lock(ap)
1720 struct vop_lock_args /* {
1721 struct vnode *a_vp;
1722 int a_flags;
1723 struct proc *a_p;
1724 } */ *ap;
1725 {
1726 return lockmgr(&VTOS(ap->a_vp)->s_lock, ap->a_flags, &ap->a_vp->v_interlock, ap->a_p);
1727 }
1728
1729 /*
1730 * Unlock an synthfsnode.
1731 #% unlock vp L U L
1732 #
1733 vop_unlock {
1734 IN struct vnode *vp;
1735 IN int flags;
1736 IN struct proc *p;
1737
1738 */
1739 int
1740 synthfs_unlock(ap)
1741 struct vop_unlock_args /* {
1742 struct vnode *a_vp;
1743 int a_flags;
1744 struct proc *a_p;
1745 } */ *ap;
1746 {
1747 return lockmgr(&VTOS(ap->a_vp)->s_lock, ap->a_flags | LK_RELEASE, &ap->a_vp->v_interlock, ap->a_p);
1748 }
1749
1750 /*
1751 * Check for a locked synthfsnode.
1752 #% islocked vp = = =
1753 #
1754 vop_islocked {
1755 IN struct vnode *vp;
1756
1757 */
1758 int
1759 synthfs_islocked(ap)
1760 struct vop_islocked_args /* {
1761 struct vnode *a_vp;
1762 } */ *ap;
1763 {
1764 return lockstatus(&VTOS(ap->a_vp)->s_lock);
1765 }
1766
1767
1768
1769 /*
1770 #
1771 #% inactive vp L U U
1772 #
1773 vop_inactive {
1774 IN struct vnode *vp;
1775 IN struct proc *p;
1776
1777 */
1778
1779 int
1780 synthfs_inactive(ap)
1781 struct vop_inactive_args /* {
1782 struct vnode *a_vp;
1783 struct proc *a_p;
1784 } */ *ap;
1785 {
1786 struct vnode *vp = ap->a_vp;
1787 struct proc *p = ap->a_p;
1788 struct synthfsnode *sp = VTOS(vp);
1789 struct timeval tv;
1790
1791 if (vp->v_usecount != 0)
1792 DBG_VOP(("synthfs_inactive: bad usecount = %d\n", vp->v_usecount ));
1793
1794 /*
1795 * Ignore nodes related to stale file handles.
1796 */
1797 if (vp->v_type == VNON)
1798 goto out;
1799
1800 /* This is sort of silly but might make sense in the future: */
1801 if (sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
1802 tv = time;
1803 VOP_UPDATE(vp, &tv, &tv, 0);
1804 }
1805
1806 out:
1807 VOP_UNLOCK(vp, 0, p);
1808 /*
1809 * If we are done with the inode, reclaim it
1810 * so that it can be reused immediately.
1811 */
1812 if (vp->v_type == VNON) {
1813 vrecycle(vp, (struct slock *)0, p);
1814 };
1815
1816 return 0;
1817 }
1818
1819
1820
1821 /*
1822 * synthfs_reclaim - Reclaim a vnode so that it can be used for other purposes.
1823 *
1824 * Locking policy: ignored
1825 */
1826 int
1827 synthfs_reclaim(ap)
1828 struct vop_reclaim_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap;
1829 {
1830 struct vnode *vp = ap->a_vp;
1831 struct synthfsnode *sp = VTOS(vp);
1832 void *name = sp->s_name;
1833
1834 sp->s_name = NULL;
1835 FREE(name, M_TEMP);
1836
1837 vp->v_data = NULL;
1838 FREE((void *)sp, M_SYNTHFS);
1839
1840 return (0);
1841 }