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