diff options
Diffstat (limited to 'sys/miscfs/devfs/devfs_vnops.c')
-rw-r--r-- | sys/miscfs/devfs/devfs_vnops.c | 249 |
1 files changed, 242 insertions, 7 deletions
diff --git a/sys/miscfs/devfs/devfs_vnops.c b/sys/miscfs/devfs/devfs_vnops.c index 2aba6c1..8d220ef 100644 --- a/sys/miscfs/devfs/devfs_vnops.c +++ b/sys/miscfs/devfs/devfs_vnops.c @@ -1,7 +1,7 @@ /* * Written by Julian Elischer (julian@DIALix.oz.au) * - * $Header: /home/ncvs/src/sys/miscfs/devfs/devfs_vnops.c,v 1.10 1995/09/06 23:15:54 julian Exp $ + * $Header: /home/ncvs/src/sys/miscfs/devfs/devfs_vnops.c,v 1.11 1995/09/07 06:01:36 julian Exp $ * * symlinks can wait 'til later. */ @@ -77,6 +77,7 @@ int devfs_lookup(struct vop_lookup_args *ap) /*proto*/ struct vnode **result_vnode = ap->a_vpp; dn_p dir_node; /* the directory we are searching */ dn_p new_node; /* the node we are searching for */ + devnm_p new_nodename; int flags = cnp->cn_flags; int op = cnp->cn_nameiop; /* LOOKUP, CREATE, RENAME, or DELETE */ int lockparent = flags & LOCKPARENT; @@ -174,12 +175,14 @@ DBPRINT(("errr, maybe not cached ")); heldchar = cnp->cn_nameptr[cnp->cn_namelen]; cnp->cn_nameptr[cnp->cn_namelen] = '\0'; - new_node = dev_findname(dir_node,cnp->cn_nameptr); + new_nodename = dev_findname(dir_node,cnp->cn_nameptr); cnp->cn_nameptr[cnp->cn_namelen] = heldchar; - if(new_node) + if(new_nodename) { + new_node = new_nodename->dnp; goto found; } + new_node = NULL; /* to be safe */ /***********************************************************************\ * Failed to find it.. (That may be good) * \***********************************************************************/ @@ -736,6 +739,7 @@ DBPRINT(("write\n")); } } +/* presently not called from devices anyhow */ int devfs_ioctl(struct vop_ioctl_args *ap) /*proto*/ /*struct vop_ioctl_args { struct vnode *a_vp; @@ -760,7 +764,7 @@ int devfs_select(struct vop_select_args *ap) /*proto*/ } */ { DBPRINT(("select\n")); - return 1; /* DOS filesystems never block? */ + return 1; /* filesystems never block? */ } int devfs_mmap(struct vop_mmap_args *ap) /*proto*/ @@ -829,6 +833,33 @@ DBPRINT(("link\n")); return 0; } +/* + * Rename system call. Seems overly complicated to me... + * rename("foo", "bar"); + * is essentially + * unlink("bar"); + * link("foo", "bar"); + * unlink("foo"); + * but ``atomically''. + * + * When the target exists, both the directory + * and target vnodes are locked. + * the source and source-parent vnodes are referenced + * + * + * Basic algorithm is: + * + * 1) Bump link count on source while we're linking it to the + * target. This also ensure the inode won't be deleted out + * from underneath us while we work (it may be truncated by + * a concurrent `trunc' or `open' for creation). + * 2) Link source to destination. If destination already exists, + * delete it first. + * 3) Unlink source reference to node if still around. If a + * directory was moved and the parent of the destination + * is different from the source, patch the ".." entry in the + * directory. + */ int devfs_rename(struct vop_rename_args *ap) /*proto*/ /*struct vop_rename_args { struct vnode *a_fdvp; @@ -839,8 +870,212 @@ int devfs_rename(struct vop_rename_args *ap) /*proto*/ struct componentname *a_tcnp; } */ { -DBPRINT(("rename\n")); - return 0; + struct vnode *tvp = ap->a_tvp; + struct vnode *tdvp = ap->a_tdvp; + struct vnode *fvp = ap->a_fvp; + struct vnode *fdvp = ap->a_fdvp; + struct componentname *tcnp = ap->a_tcnp; + struct componentname *fcnp = ap->a_fcnp; + dn_p fp, fdp, tp, tdp; + devnm_p fnp,tnp; + int doingdirectory = 0, oldparent = 0, newparent = 0; + int error = 0; + uid_t outuid = tcnp->cn_cred->cr_uid; + + /* + * First catch an arbitrary restriction for this FS + */ + if(tcnp->cn_namelen > DEVMAXNAMESIZE) { + error = ENAMETOOLONG; + goto abortit; + } + + /* + * Lock our directories and get our name pointers + * assume that the names are null terminated as they + * are the end of the path. Get pointers to all our + * devfs structures. + */ + if ( error = devfs_vntodn(tdvp,&tdp)) goto abortit; + if ( error = devfs_vntodn(fdvp,&fdp)) goto abortit; + if ( error = devfs_vntodn(fvp,&fp)) goto abortit; + fnp = dev_findname(fdp,fcnp->cn_nameptr); + if(!fnp) panic("devfs_rename: source dissapeared"); + if (tvp) { + if ( error = devfs_vntodn(tvp,&tp)) goto abortit; + tnp = dev_findname(tdp,tcnp->cn_nameptr); + if(!tnp) panic("devfs_rename: target dissapeared"); + } else { + tp = NULL; + tnp = NULL; + } + + /* + * trying to move it out of devfs? (v_tag == VT_DEVFS) + * if we move a dir across mnt points. we need to fix all + * the mountpoint pointers! XXX + * so for now keep dirs within the same mount + */ + if ( (fvp->v_tag != VT_DEVFS) + || (fvp->v_tag != tdvp->v_tag) + || (tvp && (fvp->v_tag != tvp->v_tag)) + || ((fp->type == DEV_DIR) && (fp->dvm != tdp->dvm ))) { + error = EXDEV; +abortit: + VOP_ABORTOP(tdvp, tcnp); + if (tdvp == tvp) /* eh? */ + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ + vrele(fdvp); + vrele(fvp); + return (error); + } + + /* + * Check we are doing legal things WRT the new flags + */ + if ((tp && (tp->flags & (IMMUTABLE | APPEND))) + || (fp->flags & (IMMUTABLE | APPEND)) + || (tdp->flags & APPEND) /*XXX eh?*/ + || (fdp->flags & APPEND)) { + error = EPERM; + goto abortit; + } + + /* + * Make sure that we don't try do something stupid + */ + if ((fp->type) == DEV_DIR) { + /* + * Avoid ".", "..", and aliases of "." for obvious reasons. + */ + if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') + || (fcnp->cn_flags&ISDOTDOT) + || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.') + || (tcnp->cn_flags&ISDOTDOT) + || (tdp == fp )) { + error = EINVAL; + goto abortit; + } + doingdirectory++; + } + + /* + * If ".." must be changed (ie the directory gets a new + * parent) then the source directory must not be in the + * directory heirarchy above the target, as this would + * orphan everything below the source directory. Also + * the user must have write permission in the source so + * as to be able to change "..". + */ + if (doingdirectory && (tdp != fdp)) { + dn_p tmp,ntmp; + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); + tmp = tdp; + do { + if(tmp == fp) { + /* XXX unlock stuff here probably */ + error = EINVAL; + goto out; + } + ntmp = tmp; + } while ((tmp = tmp->by.Dir.parent) != ntmp); + } + + /*********************************** + * Start actually doing things.... * + ***********************************/ + TIMEVAL_TO_TIMESPEC(&time,&(fp->atime)); + /* + * Check if just deleting a link name. + */ + if (fvp == tvp) { + if (fvp->v_type == VDIR) { + error = EINVAL; + goto abortit; + } + + /* Release destination completely. */ + VOP_ABORTOP(tdvp, tcnp); + vput(tdvp); + vput(tvp); + + /* Delete source. */ + VOP_ABORTOP(fdvp, fcnp); /*XXX*/ + vrele(fdvp); + vrele(fvp); + dev_free_name(fnp); + return 0; + } + + + /* + * 1) Bump link count while we're moving stuff + * around. If we crash somewhere before + * completing our work, too bad :) + */ + fp->links++; + /* + * If the target exists zap it (unless it's a non-empty directory) + * We could do that as well but won't + */ + if (tp) { + int ouruid = tcnp->cn_cred->cr_uid; + /* + * 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. + * XXX shoudn't this be in generic code? + */ + if ((tdp->mode & S_ISTXT) + && ouruid != 0 + && ouruid != tdp->uid + && ouruid != tp->uid ) { + error = EPERM; + goto bad; + } + /* + * Target must be empty if a directory and have no links + * to it. Also, ensure source and target are compatible + * (both directories, or both not directories). + */ + if (( doingdirectory) && (tp->links > 2)) { + printf("nlink = %d\n",tp->links); /*XXX*/ + error = ENOTEMPTY; + goto bad; + } + cache_purge(tvp); /*XXX*/ + dev_free_name(tnp); + tp = NULL; + } + dev_add_name(tcnp->cn_nameptr,tdp,fnp->as.front.realthing,fp,&tnp); + fnp->dnp = NULL; + fp->links--; /* one less link to it.. */ + dev_free_name(fnp); + fp->links--; /* we added one earlier*/ + if (tdp) + vput(tdvp); + if (tp) + vput(fvp); + vrele(ap->a_fvp); + return (error); + +bad: + if (tp) + vput(tvp); + vput(tdvp); +out: + if (VOP_LOCK(fvp) == 0) { + fp->links--; /* we added one earlier*/ + vput(fvp); + } else + vrele(fvp); + return (error); } @@ -1227,7 +1462,7 @@ void devfs_dropvnode(dn_p dnp) /*proto*/ #define devfs_seek ((int (*) __P((struct vop_seek_args *)))nullop) #define devfs_remove ((int (*) __P((struct vop_remove_args *)))devfs_enotsupp) #define devfs_link ((int (*) __P((struct vop_link_args *)))devfs_enotsupp) -#define devfs_rename ((int (*) __P((struct vop_rename_args *)))devfs_enotsupp) +/*#define devfs_rename ((int (*) __P((struct vop_rename_args *)))devfs_enotsupp)*/ #define devfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))devfs_enotsupp) #define devfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))devfs_enotsupp) #define devfs_symlink ((int (*) __P((struct vop_symlink_args *)))devfs_enotsupp) |