/* @(#)vfs_vnode.c 1.1 86/02/03 SMI */ /* NFSSRC @(#)vfs_vnode.c 2.1 86/04/15 */ #include "param.h" #include "user.h" #include "uio.h" #include "file.h" #include "pathname.h" #include "vfs.h" #include "vnode.h" /* * read or write a vnode */ int vn_rdwr(rw, vp, base, len, offset, seg, ioflag, aresid) enum uio_rw rw; struct vnode *vp; caddr_t base; int len; int offset; int seg; int ioflag; int *aresid; { struct uio auio; struct iovec aiov; int error; if ((rw == UIO_WRITE) && (vp->v_vfsp->vfs_flag & VFS_RDONLY)) { return (EROFS); } aiov.iov_base = base; aiov.iov_len = len; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = offset; auio.uio_segflg = seg; auio.uio_resid = len; error = VOP_RDWR(vp, &auio, rw, ioflag, u.u_cred); if (aresid) *aresid = auio.uio_resid; else if (auio.uio_resid) error = EIO; return (error); } /* * realease a vnode. Decrements reference count and * calls VOP_INACTIVE on last. */ void vn_rele(vp) register struct vnode *vp; { /* * sanity check */ if (vp->v_count == 0) panic("vn_rele"); if (--vp->v_count == 0) { (void)VOP_INACTIVE(vp, u.u_cred); } } /* * Open/create a vnode. * This may be callable by the kernel, the only known side effect being that * the current user uid and gid are used for permissions. */ int vn_open(pnamep, seg, filemode, createmode, vpp) char *pnamep; register int filemode; int createmode; struct vnode **vpp; { struct vnode *vp; /* ptr to file vnode */ register int mode; register int error; mode = 0; if (filemode & FREAD) mode |= VREAD; if (filemode & (FWRITE | FTRUNC)) mode |= VWRITE; if (filemode & FCREAT) { struct vattr vattr; enum vcexcl excl; /* * Wish to create a file. */ vattr_null(&vattr); vattr.va_type = VREG; vattr.va_mode = createmode; if (filemode & FTRUNC) vattr.va_size = 0; if (filemode & FEXCL) excl = EXCL; else excl = NONEXCL; filemode &= ~(FCREAT | FTRUNC | FEXCL); error = vn_create(pnamep, seg, &vattr, excl, mode, &vp); if (error) return (error); } else { /* * Wish to open a file. * Just look it up. */ error = lookupname(pnamep, seg, FOLLOW_LINK, (struct vnode **)0, &vp); if (error) return (error); /* * cannnot write directories, active texts or * read only filesystems */ if (filemode & (FWRITE | FTRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto out; } if (vp->v_vfsp->vfs_flag & VFS_RDONLY) { error = EROFS; goto out; } /* * If there's shared text associated with * the vnode, try to free it up once. * If we fail, we can't allow writing. */ if (vp->v_flag & VTEXT) { xrele(vp); if (vp->v_flag & VTEXT) { error = ETXTBSY; goto out; } } } /* * check permissions */ error = VOP_ACCESS(vp, mode, u.u_cred); if (error) goto out; /* * Sockets in filesystem name space are not supported (yet?) */ if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto out; } } /* * do opening protocol. */ error = VOP_OPEN(&vp, filemode, u.u_cred); /* * truncate if required */ if ((error == 0) && (filemode & FTRUNC)) { struct vattr vattr; filemode &= ~FTRUNC; vattr_null(&vattr); vattr.va_size = 0; error = VOP_SETATTR(vp, &vattr, u.u_cred); } out: if (error) { VN_RELE(vp); } else { *vpp = vp; } return (error); } /* * create a vnode (makenode) */ int vn_create(pnamep, seg, vap, excl, mode, vpp) char *pnamep; int seg; struct vattr *vap; enum vcexcl excl; int mode; struct vnode **vpp; { struct vnode *dvp; /* ptr to parent dir vnode */ struct pathname pn; register int error; /* * Lookup directory. * If new object is a file, call lower level to create it. * Note that it is up to the lower level to enforce exclusive * creation, if the file is already there. * This allows the lower level to do whatever * locking or protocol that is needed to prevent races. * If the new object is directory call lower level to make * the new directory, with "." and "..". */ dvp = (struct vnode *)0; *vpp = (struct vnode *)0; error = pn_get(pnamep, seg, &pn); if (error) return (error); /* * lookup will find the parent directory for the vnode. * When it is done the pn hold the name of the entry * in the directory. * If this is a non-exclusive create we also find the node itself. */ error = lookuppn(&pn, FOLLOW_LINK, &dvp, (excl == NONEXCL? vpp: (struct vnode **)0)); if (error) { pn_free(&pn); return (error); } /* * Make sure filesystem is writeable */ if (dvp->v_vfsp->vfs_flag & VFS_RDONLY) { if (*vpp) { VN_RELE(*vpp); } error = EROFS; } else if (excl == NONEXCL && *vpp != (struct vnode *)0) { /* * The file is already there. * If we are writing, and there's a shared text * associated with the vnode, try to free it up once. * If we fail, we can't allow writing. */ if ((mode & VWRITE) && ((*vpp)->v_flag & VTEXT)) { xrele(*vpp); if ((*vpp)->v_flag & VTEXT) { error = ETXTBSY; } } /* * we throw the vnode away to let VOP_CREATE truncate the * file in a non-racy manner. */ VN_RELE(*vpp); } if (error == 0) { /* * call mkdir if directory or create if other */ if (vap->va_type == VDIR) { error = VOP_MKDIR(dvp, pn.pn_path, vap, vpp, u.u_cred); } else { error = VOP_CREATE( dvp, pn.pn_path, vap, excl, mode, vpp, u.u_cred); } } pn_free(&pn); VN_RELE(dvp); return (error); } /* * close a vnode */ int vn_close(vp, flag) register struct vnode *vp; int flag; { return (VOP_CLOSE(vp, flag, u.u_cred)); } /* * Link. */ int vn_link(from_p, to_p, seg) char *from_p; char *to_p; int seg; { struct vnode *fvp; /* from vnode ptr */ struct vnode *tdvp; /* to directory vnode ptr */ struct pathname pn; register int error; fvp = tdvp = (struct vnode *)0; error = pn_get(to_p, seg, &pn); if (error) return (error); error = lookupname(from_p, seg, FOLLOW_LINK, (struct vnode **)0, &fvp); if (error) goto out; error = lookuppn(&pn, FOLLOW_LINK, &tdvp, (struct vnode **)0); if (error) goto out; /* * Make sure both source vnode and target directory vnode are * in the same vfs and that it is writeable. */ if (fvp->v_vfsp != tdvp->v_vfsp) { error = EXDEV; goto out; } if (tdvp->v_vfsp->vfs_flag & VFS_RDONLY) { error = EROFS; goto out; } /* * do the link */ error = VOP_LINK(fvp, tdvp, pn.pn_path, u.u_cred); out: pn_free(&pn); if (fvp) VN_RELE(fvp); if (tdvp) VN_RELE(tdvp); return (error); } /* * Rename. */ int vn_rename(from_p, to_p, seg) char *from_p; char *to_p; int seg; { struct vnode *fdvp; /* from directory vnode ptr */ struct vnode *fvp; /* from vnode ptr */ struct vnode *tdvp; /* to directory vnode ptr */ struct pathname fpn; /* from pathname */ struct pathname tpn; /* to pathname */ register int error; fvp = fdvp = tdvp = (struct vnode *)0; /* * get to and from pathnames */ error = pn_get(from_p, seg, &fpn); if (error) return (error); error = pn_get(to_p, seg, &tpn); if (error) { pn_free(&fpn); return (error); } /* * lookup to and from directories */ error = lookuppn(&fpn, NO_FOLLOW, &fdvp, &fvp); if (error) goto out; /* * make sure there is an entry */ if (fvp == (struct vnode *)0) { /* * If parent vnode does not allow search, return * access error. */ if (error = VOP_ACCESS(fdvp, VEXEC, u.u_cred) == 0) error = ENOENT; goto out; } error = lookuppn(&tpn, NO_FOLLOW, &tdvp, (struct vnode **)0); if (error) goto out; /* * Make sure both the from vnode and the to directory are * in the same vfs and that it is writeable. */ if (fvp->v_vfsp != tdvp->v_vfsp) { error = EXDEV; goto out; } if (tdvp->v_vfsp->vfs_flag & VFS_RDONLY) { error = EROFS; goto out; } /* * do the rename */ error = VOP_RENAME(fdvp, fpn.pn_path, tdvp, tpn.pn_path, u.u_cred); out: pn_free(&fpn); pn_free(&tpn); if (fvp) VN_RELE(fvp); if (fdvp) VN_RELE(fdvp); if (tdvp) VN_RELE(tdvp); return (error); } /* * remove a file or directory. */ int vn_remove(fnamep, seg, dirflag) char *fnamep; int seg; enum rm dirflag; { struct vnode *vp; /* entry vnode */ struct vnode *dvp; /* ptr to parent dir vnode */ struct pathname pn; /* name of entry */ enum vtype vtype; register int error; error = pn_get(fnamep, seg, &pn); if (error) return (error); vp = (struct vnode *)0; error = lookuppn(&pn, NO_FOLLOW, &dvp, &vp); if (error) { pn_free(&pn); return (error); } /* * make sure there is an entry */ if (vp == (struct vnode *)0) { /* * If parent vnode does not allow search, return * access error. */ if (error = VOP_ACCESS(dvp, VEXEC, u.u_cred) == 0) error = ENOENT; goto out; } /* * make sure filesystem is writeable */ if (vp->v_vfsp->vfs_flag & VFS_RDONLY) { error = EROFS; goto out; } /* * don't unlink the root of a mounted filesystem. */ if (vp->v_flag & VROOT) { error = EBUSY; goto out; } /* * release vnode before removing */ vtype = vp->v_type; VN_RELE(vp); vp = (struct vnode *)0; if (vtype == VDIR) { /* * if caller thought it was removing a directory, go ahead */ if (dirflag == DIRECTORY) error = VOP_RMDIR(dvp, pn.pn_path, u.u_cred); else error = EPERM; } else { /* * if caller thought it was removing a directory, barf. */ if (dirflag == FILE) error = VOP_REMOVE(dvp, pn.pn_path, u.u_cred); else error = ENOTDIR; } out: pn_free(&pn); if (vp != (struct vnode *)0) VN_RELE(vp); VN_RELE(dvp); return (error); } /* * Set vattr structure to a null value. * Boy is this machine dependent! */ void vattr_null(vap) struct vattr *vap; { register int n; register char *cp; n = sizeof(struct vattr); cp = (char *)vap; while (n--) { *cp++ = -1; } } #ifdef DEBUG prvnode(vp) register struct vnode *vp; { printf("vnode vp=0x%x ", vp); printf("flag=0x%x,count=%d,shlcnt=%d,exclcnt=%d\n", vp->v_flag,vp->v_count,vp->v_shlockc,vp->v_exlockc); printf(" vfsmnt=0x%x,vfsp=0x%x,type=%d,dev=0x%x\n", vp->v_vfsmountedhere,vp->v_vfsp,vp->v_type,vp->v_rdev); } prvattr(vap) register struct vattr *vap; { printf("vattr: vap=0x%x ", vap); printf("type=%d,mode=0%o,uid=%d,gid=%d\n", vap->va_type,vap->va_mode,vap->va_uid,vap->va_gid); printf("fsid=%d,nodeid=%d,nlink=%d,size=%d,bsize=%d\n", vap->va_fsid,vap->va_nodeid,vap->va_nlink, vap->va_size,vap->va_blocksize); printf("atime=(%d,%d),mtime=(%d,%d),ctime=(%d,%d)\n", vap->va_atime.tv_sec,vap->va_atime.tv_usec, vap->va_mtime.tv_sec,vap->va_mtime.tv_usec, vap->va_ctime.tv_sec,vap->va_ctime.tv_usec); printf("rdev=0x%x, blocks=%d\n",vap->va_rdev,vap->va_blocks); } #endif