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