/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1998-1999 Apple Computer, Inc. All Rights Reserved. * * Modification History: * * 02-Feb-2000 Clark Warner Added copyfile to table * 17-Aug-1999 Pat Dirks New today. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "synthfs.h" #define RWSUPPORT 0 #if RWSUPPORT #error NOT PORTED FOR UBC /* when porting to UBC, do not just replace * vnode_uncache by ubc_uncache - there's more * to it than that! */ #include #endif /* external routines defined in vfs_cache.c */ extern void cache_purge (struct vnode *vp); extern int cache_lookup (struct vnode *dvp, struct vnode **vpp, struct componentname *cnp); extern void cache_enter (struct vnode *dvp, struct vnode *vpp, struct componentname *cnp); //extern void vnode_uncache(struct vnode *); extern int groupmember(gid_t gid, struct ucred* cred); #define VOPFUNC int (*)(void *) /* Global vfs data structures for synthfs. */ int (**synthfs_vnodeop_p) (void *); struct vnodeopv_entry_desc synthfs_vnodeop_entries[] = { {&vop_default_desc, (VOPFUNC)vn_default_error}, {&vop_strategy_desc, (VOPFUNC)err_strategy}, /* strategy - not supported */ {&vop_bwrite_desc, (VOPFUNC)err_bwrite}, /* bwrite - not supported */ {&vop_lookup_desc, (VOPFUNC)synthfs_cached_lookup}, /* cached lookup */ {&vop_create_desc, (VOPFUNC)synthfs_create}, /* create - DEBUGGER */ {&vop_whiteout_desc, (VOPFUNC)err_whiteout}, /* whiteout - not supported */ {&vop_mknod_desc, (VOPFUNC)err_mknod}, /* mknod - not supported */ {&vop_mkcomplex_desc, (VOPFUNC)err_mkcomplex}, /* mkcomplex - not supported */ {&vop_open_desc, (VOPFUNC)synthfs_open}, /* open - DEBUGGER */ {&vop_close_desc, (VOPFUNC)nop_close}, /* close - NOP */ {&vop_access_desc, (VOPFUNC)synthfs_access}, /* access */ {&vop_getattr_desc, (VOPFUNC)synthfs_getattr}, /* getattr */ {&vop_setattr_desc, (VOPFUNC)synthfs_setattr}, /* setattr */ {&vop_getattrlist_desc, (VOPFUNC)err_getattrlist}, /* getattrlist - not supported */ {&vop_setattrlist_desc, (VOPFUNC)err_setattrlist}, /* setattrlist - not supported */ {&vop_read_desc, (VOPFUNC)err_read}, /* read - not supported */ {&vop_write_desc, (VOPFUNC)err_write}, /* write - not supported */ {&vop_lease_desc, (VOPFUNC)err_lease}, /* lease - not supported */ {&vop_ioctl_desc, (VOPFUNC)err_ioctl}, /* ioctl - not supported */ {&vop_select_desc, (VOPFUNC)synthfs_select}, /* select */ {&vop_exchange_desc, (VOPFUNC)err_exchange}, /* exchange - not supported */ {&vop_revoke_desc, (VOPFUNC)nop_revoke}, /* revoke - NOP */ {&vop_mmap_desc, (VOPFUNC)synthfs_mmap}, /* mmap - DEBUGGER */ {&vop_fsync_desc, (VOPFUNC)nop_fsync}, /* fsync - NOP */ {&vop_seek_desc, (VOPFUNC)nop_seek}, /* seek - NOP */ {&vop_remove_desc, (VOPFUNC)synthfs_remove}, /* remove */ {&vop_link_desc, (VOPFUNC)err_link}, /* link - not supported */ {&vop_rename_desc, (VOPFUNC)synthfs_rename}, /* rename */ {&vop_mkdir_desc, (VOPFUNC)synthfs_mkdir}, /* mkdir */ {&vop_rmdir_desc, (VOPFUNC)synthfs_rmdir}, /* rmdir */ {&vop_symlink_desc, (VOPFUNC)synthfs_symlink}, /* symlink */ {&vop_readdir_desc, (VOPFUNC)synthfs_readdir}, /* readdir */ {&vop_readdirattr_desc, (VOPFUNC)err_readdirattr}, /* readdirattr - not supported */ {&vop_readlink_desc, (VOPFUNC)synthfs_readlink}, /* readlink */ {&vop_abortop_desc, (VOPFUNC)nop_abortop}, /* abortop - NOP */ {&vop_inactive_desc, (VOPFUNC)synthfs_inactive}, /* inactive */ {&vop_reclaim_desc, (VOPFUNC)synthfs_reclaim}, /* reclaim */ {&vop_lock_desc, (VOPFUNC)synthfs_lock}, /* lock */ {&vop_unlock_desc, (VOPFUNC)synthfs_unlock}, /* unlock */ {&vop_bmap_desc, (VOPFUNC)err_bmap}, /* bmap - not supported */ {&vop_print_desc, (VOPFUNC)err_print}, /* print - not supported */ {&vop_islocked_desc, (VOPFUNC)synthfs_islocked}, /* islocked */ {&vop_pathconf_desc, (VOPFUNC)synthfs_pathconf}, /* pathconf */ {&vop_advlock_desc, (VOPFUNC)err_advlock}, /* advlock - not supported */ {&vop_blkatoff_desc, (VOPFUNC)err_blkatoff}, /* blkatoff - not supported */ {&vop_valloc_desc, (VOPFUNC)err_valloc}, /* valloc - not supported */ {&vop_reallocblks_desc, (VOPFUNC)err_reallocblks}, /* reallocblks - not supported */ {&vop_vfree_desc, (VOPFUNC)err_vfree}, /* vfree - not supported */ {&vop_truncate_desc, (VOPFUNC)err_truncate}, /* truncate - not supported */ {&vop_allocate_desc, (VOPFUNC)err_allocate}, /* allocate - not supported */ {&vop_update_desc, (VOPFUNC)synthfs_update}, /* update */ {&vop_pgrd_desc, (VOPFUNC)err_pgrd}, /* pgrd - not supported */ {&vop_pgwr_desc, (VOPFUNC)err_pgwr}, /* pgwr - not supported */ {&vop_pagein_desc, (VOPFUNC)err_pagein}, /* pagein - not supported */ {&vop_pageout_desc, (VOPFUNC)err_pageout}, /* pageout - not supported */ {&vop_devblocksize_desc, (VOPFUNC)err_devblocksize}, /* devblocksize - not supported */ {&vop_searchfs_desc, (VOPFUNC)err_searchfs}, /* searchfs - not supported */ {&vop_copyfile_desc, (VOPFUNC)err_copyfile}, /* copyfile - not supported */ { &vop_blktooff_desc, (VOPFUNC)err_blktooff }, /* blktooff not supported */ { &vop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk not supported */ { &vop_cmap_desc, (VOPFUNC)err_cmap }, /* cmap not supported */ {(struct vnodeop_desc *) NULL, (int (*) ()) NULL} }; /* * Oh what a tangled web we weave. This structure will be used by * bsd/vfs/vfs_conf.c to actually do the initialization of synthfs_vnodeop_p */ struct vnodeopv_desc synthfs_vnodeop_opv_desc = {&synthfs_vnodeop_p, synthfs_vnodeop_entries}; /* * Create a regular file #% create dvp L U U #% create vpp - L - # vop_create { IN WILLRELE struct vnode *dvp; OUT struct vnode **vpp; IN struct componentname *cnp; IN struct vattr *vap; We are responsible for freeing the namei buffer, it is done in hfs_makenode(), unless there is a previous error. */ int synthfs_create(ap) struct vop_create_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { #if DEBUG struct vnode *dvp = ap->a_dvp; char debugmsg[255]; sprintf(debugmsg, "synthfs_create: attempt to create '%s' in '%s' ?!", ap->a_cnp->cn_nameptr, VTOS(dvp)->s_name); Debugger(debugmsg); #endif return EOPNOTSUPP; } /* * Open called. #% open vp L L L # vop_open { IN struct vnode *vp; IN int mode; IN struct ucred *cred; IN struct proc *p; */ int synthfs_open(ap) struct vop_open_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; if (vp->v_type == VDIR) { return 0; } else { #if DEBUG struct synthfsnode *sp = VTOS(vp); char debugmsg[255]; sprintf(debugmsg, "synthfs_open: attempt to open '/%s' ?!", sp->s_name); Debugger(debugmsg); #endif }; return 0; } /* * Mmap a file * * NB Currently unsupported. # XXX - not used # vop_mmap { IN struct vnode *vp; IN int fflags; IN struct ucred *cred; IN struct proc *p; */ /* ARGSUSED */ int synthfs_mmap(ap) struct vop_mmap_args /* { struct vnode *a_vp; int a_fflags; struct ucred *a_cred; struct proc *a_p; } */ *ap; { #if DEBUG struct vnode *vp = ap->a_vp; char debugmsg[255]; sprintf(debugmsg, "synthfs_mmap: attempt to map '/%s' ?!", VTOS(vp)->s_name); Debugger(debugmsg); #endif return EINVAL; } /* #% access vp L L L # vop_access { IN struct vnode *vp; IN int mode; IN struct ucred *cred; IN struct proc *p; */ int synthfs_access(ap) struct vop_access_args /* { struct vnode *a_vp; int a_mode; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; mode_t mode = ap->a_mode; struct ucred *cred = ap->a_cred; struct synthfsnode *sp = VTOS(vp); register gid_t *gp; mode_t mask; int retval = 0; int i; /* * Disallow write attempts on read-only file systems; * unless the file is a socket, fifo, or a block or * character device resident on the file system. */ if (mode & VWRITE) { switch (vp->v_type) { case VDIR: case VLNK: case VREG: if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) return (EROFS); break; default: break; } } /* If immutable bit set, nobody gets to write it. */ if ((mode & VWRITE) && (sp->s_flags & IMMUTABLE)) return (EPERM); /* Otherwise, user id 0 always gets access. */ if (ap->a_cred->cr_uid == 0) { retval = 0; goto Exit; }; mask = 0; /* Otherwise, check the owner. */ if (cred->cr_uid == sp->s_uid) { if (mode & VEXEC) mask |= S_IXUSR; if (mode & VREAD) mask |= S_IRUSR; if (mode & VWRITE) mask |= S_IWUSR; retval = ((sp->s_mode & mask) == mask ? 0 : EACCES); goto Exit; } /* Otherwise, check the groups. */ for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) if (sp->s_gid == *gp) { if (mode & VEXEC) mask |= S_IXGRP; if (mode & VREAD) mask |= S_IRGRP; if (mode & VWRITE) mask |= S_IWGRP; retval = ((sp->s_mode & mask) == mask ? 0 : EACCES); goto Exit; } /* Otherwise, check everyone else. */ if (mode & VEXEC) mask |= S_IXOTH; if (mode & VREAD) mask |= S_IROTH; if (mode & VWRITE) mask |= S_IWOTH; retval = ((sp->s_mode & mask) == mask ? 0 : EACCES); Exit: return (retval); } /* #% getattr vp = = = # vop_getattr { IN struct vnode *vp; IN struct vattr *vap; IN struct ucred *cred; IN struct proc *p; */ int synthfs_getattr(ap) struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct vattr *vap = ap->a_vap; struct synthfsnode *sp = VTOS(vp); struct synthfs_mntdata *smp = VTOSFS(vp); vap->va_type = vp->v_type; vap->va_mode = sp->s_mode; vap->va_nlink = sp->s_linkcount; vap->va_uid = sp->s_uid; vap->va_gid = sp->s_gid; vap->va_fsid = VTOVFS(vp)->mnt_stat.f_fsid.val[0]; vap->va_fileid = sp->s_nodeid; switch (vp->v_type) { case VDIR: vap->va_size = (sp->s_u.d.d_entrycount + 2) * sizeof(struct dirent); break; case VREG: vap->va_size = sp->s_u.f.f_size; break; case VLNK: vap->va_size = sp->s_u.s.s_length; break; default: vap->va_size = 0; }; vap->va_blocksize = 512; vap->va_atime.tv_sec = sp->s_accesstime.tv_sec; vap->va_atime.tv_nsec = sp->s_accesstime.tv_usec * 1000; vap->va_mtime.tv_sec = sp->s_modificationtime.tv_sec; vap->va_mtime.tv_nsec = sp->s_modificationtime.tv_usec * 1000; vap->va_ctime.tv_sec = sp->s_changetime.tv_sec; vap->va_ctime.tv_nsec = sp->s_changetime.tv_usec * 1000; vap->va_gen = sp->s_generation; vap->va_flags = sp->s_flags; vap->va_rdev = sp->s_rdev; vap->va_bytes = vap->va_blocksize * ((vap->va_size + vap->va_blocksize - 1) / vap->va_blocksize); vap->va_filerev = 0; vap->va_vaflags = 0; return (0); } /* * Change the mode on a file or directory. * vnode vp must be locked on entry. */ int synthfs_chmod(struct vnode *vp, int mode, struct ucred *cred, struct proc *p) { struct synthfsnode *sp = VTOS(vp); int result; if ((cred->cr_uid != sp->s_uid) && (result = suser(cred, &p->p_acflag))) return result; if (cred->cr_uid) { if (vp->v_type != VDIR && (mode & S_ISTXT)) return EFTYPE; if (!groupmember(sp->s_gid, cred) && (mode & S_ISGID)) return (EPERM); } sp->s_mode &= ~ALLPERMS; sp->s_mode |= (mode & ALLPERMS); sp->s_nodeflags |= IN_CHANGE; #if RWSUPPORT if ((vp->v_flag & VTEXT) && (sp->s_mode & S_ISTXT) == 0) (void) vnode_uncache(vp); #endif return 0; } /* * Change the flags on a file or directory. * vnode vp must be locked on entry. */ int synthfs_chflags(struct vnode *vp, u_long flags, struct ucred *cred, struct proc *p) { struct synthfsnode *sp = VTOS(vp); int result; if (cred->cr_uid != sp->s_uid && (result = suser(cred, &p->p_acflag))) return result; if (cred->cr_uid == 0) { if ((sp->s_flags & (SF_IMMUTABLE | SF_APPEND)) && securelevel > 0) { return EPERM; }; sp->s_flags = flags; } else { if (sp->s_flags & (SF_IMMUTABLE | SF_APPEND) || (flags & UF_SETTABLE) != flags) { return EPERM; }; sp->s_flags &= SF_SETTABLE; sp->s_flags |= (flags & UF_SETTABLE); } sp->s_nodeflags |= IN_CHANGE; return 0; } /* * Perform chown operation on vnode vp; * vnode vp must be locked on entry. */ int synthfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p) { struct synthfsnode *sp = VTOS(vp); uid_t ouid; gid_t ogid; int result = 0; if (uid == (uid_t)VNOVAL) uid = sp->s_uid; if (gid == (gid_t)VNOVAL) gid = sp->s_gid; /* * If we don't own the file, are trying to change the owner * of the file, or are not a member of the target group, * the caller must be superuser or the call fails. */ if ((cred->cr_uid != sp->s_uid || uid != sp->s_uid || (gid != sp->s_gid && !groupmember((gid_t)gid, cred))) && (result = suser(cred, &p->p_acflag))) return result; ogid = sp->s_gid; ouid = sp->s_uid; sp->s_gid = gid; sp->s_uid = uid; if (ouid != uid || ogid != gid) sp->s_nodeflags |= IN_CHANGE; if (ouid != uid && cred->cr_uid != 0) sp->s_mode &= ~S_ISUID; if (ogid != gid && cred->cr_uid != 0) sp->s_mode &= ~S_ISGID; return 0; } /* * Set attribute vnode op. called from several syscalls #% setattr vp L L L # vop_setattr { IN struct vnode *vp; IN struct vattr *vap; IN struct ucred *cred; IN struct proc *p; */ int synthfs_setattr(ap) struct vop_setattr_args /* { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct synthfsnode *sp = VTOS(vp); struct vattr *vap = ap->a_vap; struct ucred *cred = ap->a_cred; struct proc *p = ap->a_p; struct timeval atimeval, mtimeval; int result; /* * Check for unsettable attributes. */ if (((vap->va_type != VNON) && (vap->va_type != vp->v_type)) || (vap->va_nlink != VNOVAL) || (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { result = EINVAL; goto Err_Exit; } if (vap->va_flags != VNOVAL) { if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) { result = EROFS; goto Err_Exit; }; if ((result = synthfs_chflags(vp, vap->va_flags, cred, p))) { goto Err_Exit; }; if (vap->va_flags & (IMMUTABLE | APPEND)) { result = 0; goto Err_Exit; }; } if (sp->s_flags & (IMMUTABLE | APPEND)) { result = EPERM; goto Err_Exit; }; /* * Go through the fields and update iff not VNOVAL. */ if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) { if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) { result = EROFS; goto Err_Exit; }; if ((result = synthfs_chown(vp, vap->va_uid, vap->va_gid, cred, p))) { goto Err_Exit; }; } if (vap->va_size != VNOVAL) { /* * Disallow write attempts on read-only file systems; * unless the file is a socket, fifo, or a block or * character device resident on the file system. */ switch (vp->v_type) { case VDIR: result = EISDIR; goto Err_Exit; case VLNK: case VREG: if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) { result = EROFS; goto Err_Exit; }; break; default: break; } #if RWSUPPORT if ((result = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p))) { goto Err_Exit; }; #else result = EINVAL; goto Err_Exit; #endif } sp = VTOS(vp); if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) { result = EROFS; goto Err_Exit; }; if (cred->cr_uid != sp->s_uid && (result = suser(cred, &p->p_acflag)) && ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || (result = VOP_ACCESS(vp, VWRITE, cred, p)))) { goto Err_Exit; }; if (vap->va_atime.tv_sec != VNOVAL) sp->s_nodeflags |= IN_ACCESS; if (vap->va_mtime.tv_sec != VNOVAL) sp->s_nodeflags |= IN_CHANGE | IN_UPDATE; atimeval.tv_sec = vap->va_atime.tv_sec; atimeval.tv_usec = vap->va_atime.tv_nsec / 1000; mtimeval.tv_sec = vap->va_mtime.tv_sec; mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000; if ((result = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))) { goto Err_Exit; }; } result = 0; if (vap->va_mode != (mode_t)VNOVAL) { if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) { result = EROFS; goto Err_Exit; }; result = synthfs_chmod(vp, (int)vap->va_mode, cred, p); }; Err_Exit: ; DBG_VOP(("synthfs_setattr: returning %d...\n", result)); return (result); } /* #% rename sourcePar_vp U U U #% rename source_vp U U U #% rename targetPar_vp L U U #% rename target_vp X U U # vop_rename { IN WILLRELE struct vnode *sourcePar_vp; IN WILLRELE struct vnode *source_vp; IN struct componentname *source_cnp; IN WILLRELE struct vnode *targetPar_vp; IN WILLRELE struct vnode *target_vp; IN struct componentname *target_cnp; */ /* * On entry: * source's parent directory is unlocked * source file or directory is unlocked * destination's parent directory is locked * destination file or directory is locked if it exists * * On exit: * all denodes should be released * */ int synthfs_rename(ap) struct vop_rename_args /* { struct vnode *a_fdvp; struct vnode *a_fvp; struct componentname *a_fcnp; struct vnode *a_tdvp; struct vnode *a_tvp; struct componentname *a_tcnp; } */ *ap; { struct vnode *target_vp = ap->a_tvp; struct vnode *targetPar_vp = ap->a_tdvp; struct vnode *source_vp = ap->a_fvp; struct vnode *sourcePar_vp = ap->a_fdvp; struct componentname *target_cnp = ap->a_tcnp; struct componentname *source_cnp = ap->a_fcnp; struct proc *p = source_cnp->cn_proc; struct synthfsnode *target_sp, *targetPar_sp, *source_sp, *sourcePar_sp; u_short doingdirectory = 0, oldparent = 0, newparent = 0; int retval = 0; struct timeval tv; #if SYNTHFS_DIAGNOSTIC if ((target_cnp->cn_flags & HASBUF) == 0 || (source_cnp->cn_flags & HASBUF) == 0) panic("synthfs_rename: no name"); #endif DBG_ASSERT((ap->a_fdvp->v_type == VDIR) && (ap->a_tdvp->v_type == VDIR)); target_sp = targetPar_sp = source_sp = sourcePar_sp = NULL; /* * Check for cross-device rename. */ if ((source_vp->v_mount != targetPar_vp->v_mount) || (target_vp && (source_vp->v_mount != target_vp->v_mount))) { retval = EXDEV; goto abortit; } /* * Check for access permissions */ if (target_vp && ((VTOS(target_vp)->s_pflags & (IMMUTABLE | APPEND)) || (VTOS(targetPar_vp)->s_pflags & APPEND))) { retval = EPERM; goto abortit; } if ((retval = vn_lock(source_vp, LK_EXCLUSIVE, p))) goto abortit; sourcePar_sp = VTOS(sourcePar_vp); source_sp = VTOS(source_vp); oldparent = sourcePar_sp->s_nodeid; if ((source_sp->s_pflags & (IMMUTABLE | APPEND)) || (sourcePar_sp->s_pflags & APPEND)) { VOP_UNLOCK(source_vp, 0, p); retval = EPERM; goto abortit; } /* * Be sure we are not renaming ".", "..", or an alias of ".". This * leads to a crippled directory tree. It's pretty tough to do a * "ls" or "pwd" with the "." directory entry missing, and "cd .." * doesn't work if the ".." entry is missing. */ if (source_sp->s_type == SYNTHFS_DIRECTORY) { if ((source_cnp->cn_namelen == 1 && source_cnp->cn_nameptr[0] == '.') || sourcePar_sp == source_sp || (source_cnp->cn_flags & ISDOTDOT) || (source_sp->s_nodeflags & IN_RENAME)) { VOP_UNLOCK(source_vp, 0, p); retval = EINVAL; goto abortit; } source_sp->s_nodeflags |= IN_RENAME; doingdirectory = TRUE; } /* Transit between abort and bad */ targetPar_sp = VTOS(targetPar_vp); target_sp = target_vp ? VTOS(target_vp) : NULL; newparent = targetPar_sp->s_nodeid; retval = VOP_ACCESS(source_vp, VWRITE, target_cnp->cn_cred, target_cnp->cn_proc); if (doingdirectory && (newparent != oldparent)) { if (retval) /* write access check above */ goto bad; } /* * If the destination exists, then be sure its type (file or dir) * matches that of the source. And, if it is a directory make sure * it is empty. Then delete the destination. */ if (target_vp) { /* * If the parent directory is "sticky", then the user must * own the parent directory, or the destination of the rename, * otherwise the destination may not be changed (except by * root). This implements append-only directories. */ if ((targetPar_sp->s_mode & S_ISTXT) && target_cnp->cn_cred->cr_uid != 0 && target_cnp->cn_cred->cr_uid != targetPar_sp->s_uid && target_sp->s_uid != target_cnp->cn_cred->cr_uid) { retval = EPERM; goto bad; } /* * VOP_REMOVE will vput targetPar_vp so we better bump * its ref count and relockit, always set target_vp to * NULL afterwards to indicate that were done with it. */ VREF(targetPar_vp); #if RWSUPPORT if (target_vp->v_type == VREG) { (void) vnode_uncache(target_vp); }; #endif cache_purge(target_vp); target_cnp->cn_flags &= ~SAVENAME; retval = VOP_REMOVE(targetPar_vp, target_vp, target_cnp); (void) vn_lock(targetPar_vp, LK_EXCLUSIVE | LK_RETRY, p); target_vp = NULL; target_sp = NULL; if (retval) goto bad; }; if (newparent != oldparent) vn_lock(sourcePar_vp, LK_EXCLUSIVE | LK_RETRY, p); /* remove the existing entry from the namei cache: */ if (source_vp->v_type == VREG) cache_purge(source_vp); retval = synthfs_move_rename_entry( source_vp, targetPar_vp, target_cnp->cn_nameptr); if (newparent != oldparent) VOP_UNLOCK(sourcePar_vp, 0, p); if (retval) goto bad; source_sp->s_nodeflags &= ~IN_RENAME; /* * Timestamp both parent directories. * Note that if this is a rename within the same directory, * (where targetPar_hp == sourcePar_hp) * the code below is still safe and correct. */ targetPar_sp->s_nodeflags |= IN_UPDATE; sourcePar_sp->s_nodeflags |= IN_UPDATE; tv = time; SYNTHFSTIMES(targetPar_sp, &tv, &tv); SYNTHFSTIMES(sourcePar_sp, &tv, &tv); vput(targetPar_vp); vrele(sourcePar_vp); vput(source_vp); return (retval); bad:; if (retval && doingdirectory) source_sp->s_nodeflags &= ~IN_RENAME; if (targetPar_vp == target_vp) vrele(targetPar_vp); else vput(targetPar_vp); if (target_vp) vput(target_vp); vrele(sourcePar_vp); if (VOP_ISLOCKED(source_vp)) vput(source_vp); else vrele(source_vp); return (retval); abortit:; VOP_ABORTOP(targetPar_vp, target_cnp); /* XXX, why not in NFS? */ if (targetPar_vp == target_vp) vrele(targetPar_vp); else vput(targetPar_vp); if (target_vp) vput(target_vp); VOP_ABORTOP(sourcePar_vp, source_cnp); /* XXX, why not in NFS? */ vrele(sourcePar_vp); vrele(source_vp); return (retval); } /* * Mkdir system call #% mkdir dvp L U U #% mkdir vpp - L - # vop_mkdir { IN WILLRELE struct vnode *dvp; OUT struct vnode **vpp; IN struct componentname *cnp; IN struct vattr *vap; We are responsible for freeing the namei buffer, it is done in synthfs_makenode(), unless there is a previous error. */ int synthfs_mkdir(ap) struct vop_mkdir_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; } */ *ap; { int retval; struct vnode *dvp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode); struct vnode *vp = NULL; *ap->a_vpp = NULL; retval = synthfs_new_directory(VTOVFS(dvp), dvp, cnp->cn_nameptr, VTOSFS(dvp)->synthfs_nextid++, mode, ap->a_cnp->cn_proc, &vp); if (retval) goto Error_Exit; retval = VOP_SETATTR(vp, ap->a_vap, cnp->cn_cred, cnp->cn_proc); if (retval != 0) goto Error_Exit; *ap->a_vpp = vp; Error_Exit:; if (retval != 0) { if (vp) synthfs_remove_directory(vp); VOP_ABORTOP(dvp, cnp); } vput(dvp); return retval; } /* #% remove dvp L U U #% remove vp L U U # vop_remove { IN WILLRELE struct vnode *dvp; IN WILLRELE struct vnode *vp; IN struct componentname *cnp; */ int synthfs_remove(ap) struct vop_remove_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; struct synthfsnode *sp = VTOS(vp); struct timeval tv; int retval = 0; if ((sp->s_flags & (IMMUTABLE | APPEND)) || (VTOS(dvp)->s_flags & APPEND)) { retval = EPERM; goto out; }; /* This is sort of silly right now but someday it may make sense... */ if (sp->s_nodeflags & IN_MODIFIED) { tv = time; VOP_UPDATE(vp, &tv, &tv, 0); }; /* remove the entry from the namei cache: */ cache_purge(vp); /* remove entry from tree and reclaim any resources consumed: */ switch (sp->s_type) { case SYNTHFS_DIRECTORY: synthfs_remove_directory(vp); break; case SYNTHFS_SYMLINK: synthfs_remove_symlink(vp); break; case SYNTHFS_FILE: /* Fall through to default case */ default: synthfs_remove_entry(vp); }; out: if (! retval) VTOS(dvp)->s_nodeflags |= IN_CHANGE | IN_UPDATE; if (dvp == vp) { vrele(vp); } else { vput(vp); }; vput(dvp); return (retval); } /* #% rmdir dvp L U U #% rmdir vp L U U # vop_rmdir { IN WILLRELE struct vnode *dvp; IN WILLRELE struct vnode *vp; IN struct componentname *cnp; */ int synthfs_rmdir(ap) struct vop_rmdir_args /* { struct vnode *a_dvp; struct vnode *a_vp; struct componentname *a_cnp; } */ *ap; { DBG_VOP(("synthfs_rmdir called\n")); return synthfs_remove((struct vop_remove_args *)ap); } /* * synthfs_select - just say OK. Only possible op is readdir * * Locking policy: ignore */ int synthfs_select(ap) struct vop_select_args /* { struct vnode *a_vp; int a_which; int a_fflags; struct ucred *a_cred; void *a_wql; struct proc *a_p; } */ *ap; { DBG_VOP(("synthfs_select called\n")); return (1); } /* # #% symlink dvp L U U #% symlink vpp - U - # # XXX - note that the return vnode has already been vrele'ed # by the filesystem layer. To use it you must use vget, # possibly with a further namei. # vop_symlink { IN WILLRELE struct vnode *dvp; OUT WILLRELE struct vnode **vpp; IN struct componentname *cnp; IN struct vattr *vap; IN char *target; We are responsible for freeing the namei buffer, it is done in synthfs_makenode(), unless there is a previous error. */ int synthfs_symlink(ap) struct vop_symlink_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; struct vattr *a_vap; char *a_target; } */ *ap; { struct vnode *dvp = ap->a_dvp; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; int retval; *vpp = NULL; retval = synthfs_new_symlink(VTOVFS(dvp), dvp, cnp->cn_nameptr, VTOSFS(dvp)->synthfs_nextid++, ap->a_target, ap->a_cnp->cn_proc, vpp); if (retval) goto Error_Exit; VOP_UNLOCK(*vpp, 0, cnp->cn_proc); Error_Exit:; if (retval != 0) { VOP_ABORTOP(dvp, cnp); } vput(dvp); return (retval); } /* # #% readlink vp L L L # vop_readlink { IN struct vnode *vp; INOUT struct uio *uio; IN struct ucred *cred; */ int synthfs_readlink(ap) struct vop_readlink_args /* { struct vnode *a_vp; struct uio *a_uio; struct ucred *a_cred; } */ *ap; { struct vnode *vp = ap->a_vp; struct synthfsnode *sp = VTOS(vp); struct uio *uio = ap->a_uio; int retval; unsigned long count; if (ap->a_uio->uio_offset > sp->s_u.s.s_length) { return 0; }; if (uio->uio_offset + uio->uio_resid <= sp->s_u.s.s_length) { count = uio->uio_resid; } else { count = sp->s_u.s.s_length - uio->uio_offset; }; retval = uiomove((void *)((unsigned char *)sp->s_u.s.s_symlinktarget + uio->uio_offset), count, uio); return (retval); } /* #% readdir vp L L L # vop_readdir { IN struct vnode *vp; INOUT struct uio *uio; IN struct ucred *cred; INOUT int *eofflag; OUT int *ncookies; INOUT u_long **cookies; */ int synthfs_readdir(ap) struct vop_readdir_args /* { struct vnode *vp; struct uio *uio; struct ucred *cred; int *eofflag; int *ncookies; u_long **cookies; } */ *ap; { struct synthfsnode *sp = VTOS(ap->a_vp); register struct uio *uio = ap->a_uio; off_t diroffset; /* Offset into simulated directory file */ struct synthfsnode *entry; DBG_VOP(("\tuio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid)); /* We assume it's all one big buffer... */ if (uio->uio_iovcnt > 1) { DBG_VOP(("\tuio->uio_iovcnt = %d?\n", uio->uio_iovcnt)); return EINVAL; }; /* NFS cookies are not supported: */ if ((ap->a_cookies != NULL) || (ap->a_ncookies != NULL)) { return EINVAL; }; diroffset = 0; /* * We must synthesize . and .. */ DBG_VOP(("\tstarting ... uio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid)); if (uio->uio_offset == diroffset) { DBG_VOP(("\tAdding .\n")); diroffset += synthfs_adddirentry(sp->s_nodeid, DT_DIR, ".", uio); DBG_VOP(("\t after adding ., uio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid)); } if ((uio->uio_resid > 0) && (diroffset > uio->uio_offset)) { /* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */ return EINVAL; }; if (uio->uio_offset == diroffset) { DBG_VOP(("\tAdding ..\n")); if (sp->s_parent != NULL) { diroffset += synthfs_adddirentry(sp->s_parent->s_nodeid, DT_DIR, "..", uio); } else { diroffset += synthfs_adddirentry(sp->s_nodeid, DT_DIR, "..", uio); } DBG_VOP(("\t after adding .., uio_offset = %d, uio_resid = %d\n", (int) uio->uio_offset, uio->uio_resid)); } if ((uio->uio_resid > 0) && (diroffset > uio->uio_offset)) { /* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */ return EINVAL; }; /* OK, so much for the fakes. Now for the "real thing": */ TAILQ_FOREACH(entry, &sp->s_u.d.d_subnodes, s_sibling) { if (diroffset == uio->uio_offset) { /* Return this entry */ diroffset += synthfs_adddirentry(entry->s_nodeid, VTTOIF(STOV(entry)->v_type), entry->s_name, uio); }; if ((uio->uio_resid > 0) && (diroffset > uio->uio_offset)) { /* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */ return EINVAL; }; }; if (ap->a_eofflag) *ap->a_eofflag = (entry == NULL); /* If we ran all the way through the list, there is no more */ return 0; } /* #% lookup dvp L ? ? #% lookup vpp - L - */ int synthfs_cached_lookup(ap) struct vop_cachedlookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { struct vnode *dp = ap->a_dvp; struct componentname *cnp = ap->a_cnp; u_long nameiop = cnp->cn_nameiop; u_long flags = cnp->cn_flags; boolean_t lockparent = (flags & LOCKPARENT); struct proc *p = cnp->cn_proc; struct ucred *cred = cnp->cn_cred; struct vnode *target_vp = NULL; u_int32_t target_vnode_id; /* Capability ID of the target vnode for .. unlock/relock handling check */ struct vnode **vpp = ap->a_vpp; int result = 0; DBG_VOP(("synthfs_cached_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen)); if (flags & LOCKPARENT) DBG_VOP(("\tLOCKPARENT is set\n")); if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n")); *vpp = NULL; if (dp->v_type != VDIR) { result = ENOTDIR; goto Err_Exit; }; if ((flags & ISLASTCN) && (VTOVFS(dp)->mnt_flag & MNT_RDONLY) && ((nameiop == DELETE) || (nameiop == RENAME))) { result = EROFS; goto Err_Exit; }; result = VOP_ACCESS(dp, VEXEC, cred, cnp->cn_proc); if (result != 0) goto Err_Exit; /* * Look up an entry in the namei cache */ result = cache_lookup(dp, vpp, cnp); if (result == 0) { /* There was no entry in the cache for this parent vnode/name pair: do the full-blown pathname lookup */ return synthfs_lookup(ap); }; if (result == ENOENT) return result; /* An entry matching the parent vnode/name was found in the cache: */ target_vp = *vpp; target_vnode_id = target_vp->v_id; if (target_vp == dp) { /* lookup on "." */ VREF(target_vp); result = 0; } else if (flags & ISDOTDOT) { /* * Carefully now: trying to step from child to parent; * must release lock on child before trying to lock parent * vnode. */ VOP_UNLOCK(dp, 0, p); result = vget(target_vp, LK_EXCLUSIVE, p); if ((result == 0) && lockparent && (flags & ISLASTCN)) { result = vn_lock(dp, LK_EXCLUSIVE, p); } } else { result = vget(target_vp, LK_EXCLUSIVE, p); if (!lockparent || (result != 0) || !(flags & ISLASTCN)) { VOP_UNLOCK(dp, 0, p); }; }; /* Check to make sure the target vnode ID didn't change while we tried to lock it: */ if (result == 0) { if (target_vnode_id == target_vp->v_id) { return 0; /* THIS IS THE NORMAL EXIT PATH */ }; /* The vnode ID didn't match anymore: we've got another vnode! */ vput(target_vp); /* Unlock the parent vnode in the cases where it should've been left locked: */ if (lockparent && (dp != target_vp) && (flags & ISLASTCN)) { VOP_UNLOCK(dp, 0, p); }; }; /* One last try for a successful lookup through the complete lookup path: */ result = vn_lock(dp, LK_EXCLUSIVE, p); if (result == 0) { return synthfs_lookup(ap); }; Err_Exit:; return result; } int synthfs_lookup(ap) struct vop_cachedlookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { struct vnode *dp = ap->a_dvp; struct synthfsnode *dsp = VTOS(dp); struct componentname *cnp = ap->a_cnp; u_long nameiop = cnp->cn_nameiop; // char *nameptr = cnp->cn_nameptr; u_long flags = cnp->cn_flags; long namelen = cnp->cn_namelen; struct proc *p = cnp->cn_proc; struct ucred *cred = cnp->cn_cred; struct synthfsnode *entry; struct vnode *target_vp = NULL; int result = 0; boolean_t found = FALSE; boolean_t isDot = FALSE; boolean_t isDotDot = FALSE; struct vnode *starting_parent = dp; DBG_VOP(("synthfs_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen)); if (flags & LOCKPARENT) DBG_VOP(("\tLOCKPARENT is set\n")); if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n")); *ap->a_vpp = NULL; if (dp->v_type != VDIR) { result = ENOTDIR; goto Err_Exit; }; if ((flags & ISLASTCN) && (VTOVFS(dp)->mnt_flag & MNT_RDONLY) && ((nameiop == DELETE) || (nameiop == RENAME))) { result = EROFS; goto Err_Exit; }; result = VOP_ACCESS(dp, VEXEC, cred, cnp->cn_proc); if (result != 0) goto Err_Exit; /* first check for "." and ".." */ if (cnp->cn_nameptr[0] == '.') { if (namelen == 1) { /* "." requested */ isDot = TRUE; found = TRUE; target_vp = dp; VREF(target_vp); result = 0; goto Std_Exit; } else if ((namelen == 2) && (cnp->cn_nameptr[1] == '.')) { /* ".." requested */ isDotDot = TRUE; found = TRUE; if ((dsp->s_parent != NULL) && (dsp->s_parent != VTOS(dp))) { target_vp = STOV(dsp->s_parent); /* * Special case for ".." to prevent deadlock: * always release the parent vnode BEFORE trying to acquire * ITS parent. This avoids deadlocking with another lookup * starting from the target_vp trying to vget() this directory. */ VOP_UNLOCK(dp, 0, p); result = vget(target_vp, LK_EXCLUSIVE | LK_RETRY, p); if (result != 0) { vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p); goto Err_Exit; } if ((flags & LOCKPARENT) && (flags & ISLASTCN)) { result = vn_lock(dp, LK_EXCLUSIVE, p); // vput(target_vp); /* XXX WHY WAS THIS HERE? */ } } else { target_vp = dp; /* dp is alread locked and ref'ed */ result = 0; } goto Std_Exit; } } /* finally, just look for entries by name (making sure the entry's length matches the cnp's namelen... */ TAILQ_FOREACH(entry, &dsp->s_u.d.d_subnodes, s_sibling) { if ((bcmp(cnp->cn_nameptr, entry->s_name, (unsigned)namelen) == 0) && (*(entry->s_name + namelen) == (char)0)) { found = TRUE; target_vp = STOV(entry); result = vget(target_vp, LK_EXCLUSIVE | LK_RETRY, p); /* vget is not really needed because refcount is always > 0... */ if (result != 0) { vrele(target_vp); goto Err_Exit; }; /* The specified entry was found and successfully acquired: */ goto Std_Exit; }; }; found = FALSE; Std_Exit:; if (found) { if ((nameiop == DELETE) && (flags & ISLASTCN)) { /* * Deleting entries requires write access: */ result = VOP_ACCESS(dp, VWRITE, cred, p); if (result != 0) goto Err_Exit; /* * If the parent directory is "sticky" then the user must own * the directory, or the file in it, in order to be allowed to * delete it (unless the user is root). This implements * append-only directories */ if ((dsp->s_mode & S_ISVTX) && (cred->cr_uid != 0) && (cred->cr_uid != dsp->s_uid) && (target_vp != NULL) && (target_vp->v_type != VLNK) && (VTOS(target_vp)->s_uid != cred->cr_uid)) { vput(target_vp); result = EPERM; goto Err_Exit; }; }; if ((nameiop == RENAME) && (flags & WANTPARENT) && (flags * ISLASTCN)) { result = VOP_ACCESS(dp, VWRITE, cred, p); if (result != 0) goto Err_Exit; if (isDot) { vrele(target_vp); result = EISDIR; goto Err_Exit; }; }; } else { /* The specified entry wasn't found: */ result = ENOENT; if ((flags & ISLASTCN) && ((nameiop == CREATE) || (nameiop == RENAME) || ((nameiop == DELETE) && (flags & DOWHITEOUT) && (flags & ISWHITEOUT)))) { /* Write access is required to create entries in the directory: */ result = VOP_ACCESS(dp, VWRITE, cred, p); if (result != 0) goto Err_Exit; cnp->cn_flags |= SAVENAME; result = EJUSTRETURN; } }; /* XXX PPD Should we do something special in case LOCKLEAF isn't set? */ if (found && !isDot && !isDotDot && (!(flags & LOCKPARENT) || !(flags & ISLASTCN))) { VOP_UNLOCK(dp, 0, p); }; *ap->a_vpp = target_vp; Err_Exit:; DBG_VOP(("synthfs_lookup: result = %d.\n", result)); if (found) { if (target_vp) { if (VOP_ISLOCKED(target_vp)) { DBG_VOP(("synthfs_lookup: target_vp = 0x%08X (locked).\n", (u_long)target_vp)); } else { DBG_VOP(("synthfs_lookup: target_vp = 0x%08X (NOT locked?).\n", (u_long)target_vp)); }; } else { DBG_VOP(("synthfs_lookup: found = true but target_vp = NULL?\n")); }; } else { DBG_VOP(("synthf_lookup: target not found.\n")); }; if (VOP_ISLOCKED(starting_parent)) { DBG_VOP(("synthfs_lookup: dp = %08X; starting_parent = 0x%08X (LOCKED).\n", (u_long)dp, (u_long)starting_parent)); } else { DBG_VOP(("synthfs_lookup: dp = %08X; starting_parent = 0x%08X (UNLOCKED).\n", (u_long)dp, (u_long)starting_parent)); }; return result; } /* #% pathconf vp L L L # vop_pathconf { IN struct vnode *vp; IN int name; OUT register_t *retval; */ int synthfs_pathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { DBG_VOP(("synthfs_pathconf called\n")); switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_NAME_MAX: *ap->a_retval = NAME_MAX; return (0); case _PC_PATH_MAX: *ap->a_retval = PATH_MAX; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_NO_TRUNC: *ap->a_retval = 1; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Update the access, modified, and node change times as specified by the * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is * used to specify that the node needs to be updated but that the times have * already been set. The access and modified times are taken from the second * and third parameters; the node change time is always taken from the current * time. If waitfor is set, then wait for the disk write of the node to * complete. */ /* #% update vp L L L IN struct vnode *vp; IN struct timeval *access; IN struct timeval *modify; IN int waitfor; */ int synthfs_update(ap) struct vop_update_args /* { struct vnode *a_vp; struct timeval *a_access; struct timeval *a_modify; int a_waitfor; } */ *ap; { struct vnode *vp = ap->a_vp; struct synthfsnode *sp = VTOS(vp); DBG_ASSERT(sp != NULL); DBG_ASSERT(*((int*)&vp->v_interlock) == 0); if (((sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) != 0) && !(VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY)) { if (sp->s_nodeflags & IN_ACCESS) sp->s_accesstime = *ap->a_access; if (sp->s_nodeflags & IN_UPDATE) sp->s_modificationtime = *ap->a_modify; if (sp->s_nodeflags & IN_CHANGE) sp->s_changetime = time; }; /* After the updates are finished, clear the flags */ sp->s_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE); // DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0); return 0; } /******************************************************************************************* Utility/housekeeping vnode operations: ******************************************************************************************/ /* #% lock vp U L U # vop_lock { IN struct vnode *vp; IN int flags; IN struct proc *p; */ int synthfs_lock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { return lockmgr(&VTOS(ap->a_vp)->s_lock, ap->a_flags, &ap->a_vp->v_interlock, ap->a_p); } /* * Unlock an synthfsnode. #% unlock vp L U L # vop_unlock { IN struct vnode *vp; IN int flags; IN struct proc *p; */ int synthfs_unlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { return lockmgr(&VTOS(ap->a_vp)->s_lock, ap->a_flags | LK_RELEASE, &ap->a_vp->v_interlock, ap->a_p); } /* * Check for a locked synthfsnode. #% islocked vp = = = # vop_islocked { IN struct vnode *vp; */ int synthfs_islocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; } */ *ap; { return lockstatus(&VTOS(ap->a_vp)->s_lock); } /* # #% inactive vp L U U # vop_inactive { IN struct vnode *vp; IN struct proc *p; */ int synthfs_inactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct proc *p = ap->a_p; struct synthfsnode *sp = VTOS(vp); struct timeval tv; if (vp->v_usecount != 0) DBG_VOP(("synthfs_inactive: bad usecount = %d\n", vp->v_usecount )); /* * Ignore nodes related to stale file handles. */ if (vp->v_type == VNON) goto out; /* This is sort of silly but might make sense in the future: */ if (sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) { tv = time; VOP_UPDATE(vp, &tv, &tv, 0); } out: VOP_UNLOCK(vp, 0, p); /* * If we are done with the inode, reclaim it * so that it can be reused immediately. */ if (vp->v_type == VNON) { vrecycle(vp, (struct slock *)0, p); }; return 0; } /* * synthfs_reclaim - Reclaim a vnode so that it can be used for other purposes. * * Locking policy: ignored */ int synthfs_reclaim(ap) struct vop_reclaim_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; struct synthfsnode *sp = VTOS(vp); void *name = sp->s_name; sp->s_name = NULL; FREE(name, M_TEMP); vp->v_data = NULL; FREE((void *)sp, M_SYNTHFS); return (0); }