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