X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/a322f06aaa4cc0adbbfd19deded37e9ff06a6ab8..e3b1aef51b44c1dabf00fbf91603d9a4cc7c56a9:/usr/src/sys/ufs/ffs/ufs_vnops.c diff --git a/usr/src/sys/ufs/ffs/ufs_vnops.c b/usr/src/sys/ufs/ffs/ufs_vnops.c index aa6e310cfe..b6fdda7c41 100644 --- a/usr/src/sys/ufs/ffs/ufs_vnops.c +++ b/usr/src/sys/ufs/ffs/ufs_vnops.c @@ -4,7 +4,7 @@ * * %sccs.include.redist.c% * - * @(#)ufs_vnops.c 7.94 (Berkeley) %G% + * @(#)ufs_vnops.c 7.118 (Berkeley) %G% */ #include @@ -19,12 +19,13 @@ #include #include #include -#include -#include #include +#include #include +#include + #include #include #include @@ -32,14 +33,10 @@ #include #include -int ufs_chmod __P((struct vnode *, int, struct ucred *, struct proc *)); -int ufs_chown +static int ufs_chmod __P((struct vnode *, int, struct ucred *, struct proc *)); +static int ufs_chown __P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *)); -#ifdef _NOQUAD -#define SETHIGH(q, h) (q).val[_QUAD_HIGHWORD] = (h) -#define SETLOW(q, l) (q).val[_QUAD_LOWWORD] = (l) -#else /* QUAD */ union _qcvt { quad_t qcvt; long val[2]; @@ -56,14 +53,18 @@ union _qcvt { tmp.val[_QUAD_LOWWORD] = (l); \ (q) = tmp.qcvt; \ } -#endif /* QUAD */ /* * Create a regular file */ int ufs_create(ap) - struct vop_create_args *ap; + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; { int error; @@ -80,7 +81,12 @@ ufs_create(ap) /* ARGSUSED */ int ufs_mknod(ap) - struct vop_mknod_args *ap; + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; { register struct vattr *vap = ap->a_vap; register struct vnode **vpp = ap->a_vpp; @@ -120,9 +126,20 @@ ufs_mknod(ap) /* ARGSUSED */ int ufs_open(ap) - struct vop_open_args *ap; + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { + /* + * Files marked append-only must be opened for appending. + */ + if ((VTOI(ap->a_vp)->i_flags & APPEND) && + (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) + return (EPERM); return (0); } @@ -134,7 +151,12 @@ ufs_open(ap) /* ARGSUSED */ int ufs_close(ap) - struct vop_close_args *ap; + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); @@ -151,9 +173,13 @@ ufs_close(ap) */ int ufs_access(ap) - struct vop_access_args *ap; + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { - USES_VOP_ISLOCKED; register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); register struct ucred *cred = ap->a_cred; @@ -176,6 +202,8 @@ ufs_access(ap) } } #endif /* QUOTA */ + if ((mode & VWRITE) && (ip->i_flags & IMMUTABLE)) + return (EPERM); /* * If you're the super-user, you always get access. */ @@ -196,15 +224,18 @@ ufs_access(ap) found: ; } - if ((ip->i_mode & mode) != 0) - return (0); - return (EACCES); + return ((ip->i_mode & mode) == mode ? 0 : EACCES); } /* ARGSUSED */ int ufs_getattr(ap) - struct vop_getattr_args *ap; + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); @@ -221,12 +252,7 @@ ufs_getattr(ap) vap->va_uid = ip->i_uid; vap->va_gid = ip->i_gid; vap->va_rdev = (dev_t)ip->i_rdev; -#ifdef tahoe - vap->va_size = ip->i_size; - vap->va_size_rsv = 0; -#else - vap->va_qsize = ip->i_din.di_qsize; -#endif + vap->va_size = ip->i_din.di_size; vap->va_atime = ip->i_atime; vap->va_mtime = ip->i_mtime; vap->va_ctime = ip->i_ctime; @@ -240,9 +266,6 @@ ufs_getattr(ap) else vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; vap->va_bytes = dbtob(ip->i_blocks); -#ifdef _NOQUAD - vap->va_bytes_rsv = 0; -#endif vap->va_type = vp->v_type; vap->va_filerev = ip->i_modrev; return (0); @@ -253,15 +276,19 @@ ufs_getattr(ap) */ int ufs_setattr(ap) - struct vop_setattr_args *ap; + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { - USES_VOP_TRUNCATE; - USES_VOP_UPDATE; register struct vattr *vap = ap->a_vap; register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); register struct ucred *cred = ap->a_cred; register struct proc *p = ap->a_p; + struct timeval atimeval, mtimeval; int error; /* @@ -273,6 +300,27 @@ ufs_setattr(ap) ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { return (EINVAL); } + if (vap->va_flags != VNOVAL) { + if (cred->cr_uid != ip->i_uid && + (error = suser(cred, &p->p_acflag))) + return (error); + if (cred->cr_uid == 0) { + if ((ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) && + securelevel > 0) + return (EPERM); + ip->i_flags = vap->va_flags; + } else { + if (ip->i_flags & (SF_IMMUTABLE | SF_APPEND)) + return (EPERM); + ip->i_flags &= 0xffff0000; + ip->i_flags |= (vap->va_flags & 0xffff); + } + ip->i_flag |= ICHG; + if (vap->va_flags & (IMMUTABLE | APPEND)) + return (0); + } + if (ip->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); /* * Go through the fields and update iff not VNOVAL. */ @@ -282,37 +330,30 @@ ufs_setattr(ap) if (vap->va_size != VNOVAL) { if (vp->v_type == VDIR) return (EISDIR); - if (error = VOP_TRUNCATE(vp, vap->va_size, 0, cred)) + if (error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p)) return (error); } ip = VTOI(vp); - if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { + if (vap->va_atime.ts_sec != VNOVAL || vap->va_mtime.ts_sec != VNOVAL) { if (cred->cr_uid != ip->i_uid && - (error = suser(cred, &p->p_acflag))) + (error = suser(cred, &p->p_acflag)) && + ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || + (error = VOP_ACCESS(vp, VWRITE, cred, p)))) return (error); - if (vap->va_atime.tv_sec != VNOVAL) + if (vap->va_atime.ts_sec != VNOVAL) ip->i_flag |= IACC; - if (vap->va_mtime.tv_sec != VNOVAL) - ip->i_flag |= IUPD; - ip->i_flag |= ICHG; - if (error = VOP_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 1)) + if (vap->va_mtime.ts_sec != VNOVAL) + ip->i_flag |= IUPD | ICHG; + atimeval.tv_sec = vap->va_atime.ts_sec; + atimeval.tv_usec = vap->va_atime.ts_nsec / 1000; + mtimeval.tv_sec = vap->va_mtime.ts_sec; + mtimeval.tv_usec = vap->va_mtime.ts_nsec / 1000; + if (error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1)) return (error); } error = 0; if (vap->va_mode != (mode_t)VNOVAL) error = ufs_chmod(vp, (int)vap->va_mode, cred, p); - if (vap->va_flags != VNOVAL) { - if (cred->cr_uid != ip->i_uid && - (error = suser(cred, &p->p_acflag))) - return (error); - if (cred->cr_uid == 0) { - ip->i_flags = vap->va_flags; - } else { - ip->i_flags &= 0xffff0000; - ip->i_flags |= (vap->va_flags & 0xffff); - } - ip->i_flag |= ICHG; - } return (error); } @@ -457,7 +498,14 @@ good: /* ARGSUSED */ int ufs_ioctl(ap) - struct vop_ioctl_args *ap; + struct vop_ioctl_args /* { + struct vnode *a_vp; + int a_command; + caddr_t a_data; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { return (ENOTTY); @@ -466,7 +514,13 @@ ufs_ioctl(ap) /* ARGSUSED */ int ufs_select(ap) - struct vop_select_args *ap; + struct vop_select_args /* { + struct vnode *a_vp; + int a_which; + int a_fflags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { /* @@ -483,7 +537,12 @@ ufs_select(ap) /* ARGSUSED */ int ufs_mmap(ap) - struct vop_mmap_args *ap; + struct vop_mmap_args /* { + struct vnode *a_vp; + int a_fflags; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { return (EINVAL); @@ -497,7 +556,12 @@ ufs_mmap(ap) /* ARGSUSED */ int ufs_seek(ap) - struct vop_seek_args *ap; + struct vop_seek_args /* { + struct vnode *a_vp; + off_t a_oldoff; + off_t a_newoff; + struct ucred *a_cred; + } */ *ap; { return (0); @@ -510,23 +574,33 @@ ufs_seek(ap) */ int ufs_remove(ap) - struct vop_remove_args *ap; + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; { - register struct inode *ip, *dp; + register struct inode *ip; + register struct vnode *vp = ap->a_vp; + register struct vnode *dvp = ap->a_dvp; int error; - ip = VTOI(ap->a_vp); - dp = VTOI(ap->a_dvp); - error = ufs_dirremove(ap->a_dvp, ap->a_cnp); - if (!error) { + ip = VTOI(vp); + if ((ip->i_flags & (IMMUTABLE | APPEND)) || + (VTOI(dvp)->i_flags & APPEND)) { + error = EPERM; + goto out; + } + if ((error = ufs_dirremove(dvp, ap->a_cnp)) == 0) { ip->i_nlink--; ip->i_flag |= ICHG; } - if (dp == ip) - vrele(ITOV(ip)); +out: + if (dvp == vp) + vrele(vp); else - ufs_iput(ip); - ufs_iput(dp); + vput(vp); + vput(dvp); return (error); } @@ -535,49 +609,59 @@ ufs_remove(ap) */ int ufs_link(ap) - struct vop_link_args *ap; + struct vop_link_args /* { + struct vnode *a_vp; + struct vnode *a_tdvp; + struct componentname *a_cnp; + } */ *ap; { - USES_VOP_UPDATE; - USES_VOP_ABORTOP; register struct vnode *vp = ap->a_vp; register struct vnode *tdvp = ap->a_tdvp; register struct componentname *cnp = ap->a_cnp; register struct inode *ip; + struct timeval tv; int error; - if (vp->v_mount != tdvp->v_mount) { - VOP_ABORTOP(vp, cnp); - if (tdvp == vp) - vrele(vp); - else - vput(vp); - return (EXDEV); - } - #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) panic("ufs_link: no name"); #endif + if (vp->v_mount != tdvp->v_mount) { + VOP_ABORTOP(vp, cnp); + error = EXDEV; + goto out2; + } + if (vp != tdvp && (error = VOP_LOCK(tdvp))) { + VOP_ABORTOP(vp, cnp); + goto out2; + } ip = VTOI(tdvp); if ((nlink_t)ip->i_nlink >= LINK_MAX) { - free(cnp->cn_pnbuf, M_NAMEI); - return (EMLINK); + VOP_ABORTOP(vp, cnp); + error = EMLINK; + goto out1; + } + if (ip->i_flags & (IMMUTABLE | APPEND)) { + VOP_ABORTOP(vp, cnp); + error = EPERM; + goto out1; } - if (vp != tdvp) - ILOCK(ip); ip->i_nlink++; ip->i_flag |= ICHG; - error = VOP_UPDATE(tdvp, &time, &time, 1); + tv = time; + error = VOP_UPDATE(tdvp, &tv, &tv, 1); if (!error) error = ufs_direnter(ip, vp, cnp); - if (vp != tdvp) - IUNLOCK(ip); - FREE(cnp->cn_pnbuf, M_NAMEI); - vput(vp); if (error) { ip->i_nlink--; ip->i_flag |= ICHG; } + FREE(cnp->cn_pnbuf, M_NAMEI); +out1: + if (vp != tdvp) + VOP_UNLOCK(tdvp); +out2: + vput(vp); return (error); } @@ -592,9 +676,6 @@ relookup(dvp, vpp, cnp) struct vnode *dvp, **vpp; struct componentname *cnp; { - USES_VOP_LOCK; - USES_VOP_LOOKUP; - USES_VOP_UNLOCK; register struct vnode *dp = 0; /* the directory we are searching */ struct vnode *tdp; /* saved dp */ struct mount *mp; /* mount table entry */ @@ -766,14 +847,15 @@ bad: */ int ufs_rename(ap) - struct vop_rename_args *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; { - USES_VOP_ABORTOP; - USES_VOP_ACCESS; - USES_VOP_LOCK; - USES_VOP_TRUNCATE; - USES_VOP_UNLOCK; - USES_VOP_UPDATE; struct vnode *tvp = ap->a_tvp; register struct vnode *tdvp = ap->a_tdvp; struct vnode *fvp = ap->a_fvp; @@ -782,13 +864,24 @@ ufs_rename(ap) register struct componentname *fcnp = ap->a_fcnp; register struct inode *ip, *xp, *dp; struct dirtemplate dirbuf; + struct timeval tv; int doingdirectory = 0, oldparent = 0, newparent = 0; int error = 0; int fdvpneedsrele = 1, tdvpneedsrele = 1; + u_char namlen; - /* Check for cross-device rename */ +#ifdef DIAGNOSTIC + if ((tcnp->cn_flags & HASBUF) == 0 || + (fcnp->cn_flags & HASBUF) == 0) + panic("ufs_rename: no name"); +#endif + /* + * Check for cross-device rename. + */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; +abortit: VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */ if (tdvp == tvp) vrele(tdvp); @@ -799,48 +892,54 @@ ufs_rename(ap) VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ vrele(fdvp); vrele(fvp); - return (EXDEV); + return (error); } -#ifdef DIAGNOSTIC - if ((tcnp->cn_flags & HASBUF) == 0 || - (fcnp->cn_flags & HASBUF) == 0) - panic("ufs_rename: no name"); -#endif - dp = VTOI(fdvp); - ip = VTOI(fvp); /* * Check if just deleting a link name. */ + if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) || + (VTOI(tdvp)->i_flags & APPEND))) { + error = EPERM; + goto abortit; + } if (fvp == tvp) { - VOP_ABORTOP(tdvp, tcnp); + if (fvp->v_type == VDIR) { + error = EINVAL; + goto abortit; + } + VOP_ABORTOP(fdvp, fcnp); + vrele(fdvp); + vrele(fvp); vput(tdvp); vput(tvp); - vrele(fdvp); - if ((ip->i_mode&IFMT) == IFDIR) { - VOP_ABORTOP(fdvp, fcnp); - vrele(fvp); - return (EINVAL); - } - doingdirectory = 0; - goto unlinkit; + tcnp->cn_flags &= ~MODMASK; + tcnp->cn_flags |= LOCKPARENT | LOCKLEAF; + if ((tcnp->cn_flags & SAVESTART) == 0) + panic("ufs_rename: lost from startdir"); + tcnp->cn_nameiop = DELETE; + (void) relookup(tdvp, &tvp, tcnp); + return (VOP_REMOVE(tdvp, tvp, tcnp)); } - ILOCK(ip); - if ((ip->i_mode&IFMT) == IFDIR) { + if (error = VOP_LOCK(fvp)) + goto abortit; + dp = VTOI(fdvp); + ip = VTOI(fvp); + if ((ip->i_flags & (IMMUTABLE | APPEND)) || (dp->i_flags & APPEND)) { + VOP_UNLOCK(fvp); + error = EPERM; + goto abortit; + } + if ((ip->i_mode & IFMT) == IFDIR) { /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') || dp == ip || (fcnp->cn_flags&ISDOTDOT) || (ip->i_flag & IRENAME)) { - VOP_ABORTOP(tdvp, tcnp); - vput(tdvp); - if (tvp) - vput(tvp); - VOP_ABORTOP(fdvp, fcnp); - vrele(fdvp); - vput(fvp); - return (EINVAL); + VOP_UNLOCK(fvp); + error = EINVAL; + goto abortit; } ip->i_flag |= IRENAME; oldparent = dp->i_number; @@ -848,6 +947,15 @@ ufs_rename(ap) } vrele(fdvp); + /* + * When the target exists, both the directory + * and target vnodes are returned locked. + */ + dp = VTOI(tdvp); + xp = NULL; + if (tvp) + xp = VTOI(tvp); + /* * 1) Bump link count while we're moving stuff * around. If we crash somewhere before @@ -856,17 +964,12 @@ ufs_rename(ap) */ ip->i_nlink++; ip->i_flag |= ICHG; - error = VOP_UPDATE(fvp, &time, &time, 1); - IUNLOCK(ip); + tv = time; + if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) { + VOP_UNLOCK(fvp); + goto bad; + } - /* - * When the target exists, both the directory - * and target vnodes are returned locked. - */ - dp = VTOI(tdvp); - xp = NULL; - if (tvp) - xp = VTOI(tvp); /* * If ".." must be changed (ie the directory gets a new * parent) then the source directory must not be in the @@ -877,16 +980,15 @@ ufs_rename(ap) * to namei, as the parent directory is unlocked by the * call to checkpath(). */ + error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); + VOP_UNLOCK(fvp); if (oldparent != dp->i_number) newparent = dp->i_number; if (doingdirectory && newparent) { - VOP_LOCK(fvp); - error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); - VOP_UNLOCK(fvp); - if (error) + if (error) /* write access check above */ goto bad; if (xp != NULL) - ufs_iput(xp); + vput(tvp); if (error = ufs_checkpath(ip, dp, tcnp->cn_cred)) goto out; if ((tcnp->cn_flags & SAVESTART) == 0) @@ -921,18 +1023,18 @@ ufs_rename(ap) } dp->i_nlink++; dp->i_flag |= ICHG; - if (error = VOP_UPDATE(ITOV(dp), &time, &time, 1)) + if (error = VOP_UPDATE(tdvp, &tv, &tv, 1)) goto bad; } if (error = ufs_direnter(ip, tdvp, tcnp)) { if (doingdirectory && newparent) { dp->i_nlink--; dp->i_flag |= ICHG; - (void)VOP_UPDATE(ITOV(dp), &time, &time, 1); + (void)VOP_UPDATE(tdvp, &tv, &tv, 1); } goto bad; } - ufs_iput(dp); + vput(tdvp); } else { if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) panic("rename: EXDEV"); @@ -968,7 +1070,7 @@ ufs_rename(ap) error = ENOTDIR; goto bad; } - cache_purge(ITOV(dp)); + cache_purge(tdvp); } else if (doingdirectory) { error = EISDIR; goto bad; @@ -985,7 +1087,7 @@ ufs_rename(ap) dp->i_nlink--; dp->i_flag |= ICHG; } - ufs_iput(dp); + vput(tdvp); /* * Adjust the link count of the target to * reflect the dirrewrite above. If this is @@ -1000,18 +1102,17 @@ ufs_rename(ap) if (doingdirectory) { if (--xp->i_nlink != 0) panic("rename: linked directory"); - error = VOP_TRUNCATE(ITOV(xp), (off_t)0, IO_SYNC, - tcnp->cn_cred); + error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC, + tcnp->cn_cred, tcnp->cn_proc); } xp->i_flag |= ICHG; - ufs_iput(xp); + vput(tvp); xp = NULL; } /* * 3) Unlink the source. */ -unlinkit: fcnp->cn_flags &= ~MODMASK; fcnp->cn_flags |= LOCKPARENT | LOCKLEAF; if ((fcnp->cn_flags & SAVESTART) == 0) @@ -1027,7 +1128,7 @@ unlinkit: */ if (doingdirectory) panic("rename: lost dir entry"); - vrele(ITOV(ip)); + vrele(ap->a_fvp); return (0); } /* @@ -1053,26 +1154,34 @@ unlinkit: if (doingdirectory && newparent) { dp->i_nlink--; dp->i_flag |= ICHG; - error = vn_rdwr(UIO_READ, ITOV(xp), (caddr_t)&dirbuf, + error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, tcnp->cn_cred, (int *)0, (struct proc *)0); if (error == 0) { - if (dirbuf.dotdot_namlen != 2 || +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (fvp->v_mount->mnt_maxsymlinklen <= 0) + namlen = dirbuf.dotdot_type; + else + namlen = dirbuf.dotdot_namlen; +# else + namlen = dirbuf.dotdot_namlen; +# endif + if (namlen != 2 || dirbuf.dotdot_name[0] != '.' || dirbuf.dotdot_name[1] != '.') { ufs_dirbad(xp, (doff_t)12, "rename: mangled dir"); } else { dirbuf.dotdot_ino = newparent; - (void) vn_rdwr(UIO_WRITE, ITOV(xp), + (void) vn_rdwr(UIO_WRITE, fvp, (caddr_t)&dirbuf, sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_SYNC, tcnp->cn_cred, (int *)0, (struct proc *)0); - cache_purge(ITOV(dp)); + cache_purge(fdvp); } } } @@ -1084,10 +1193,10 @@ unlinkit: xp->i_flag &= ~IRENAME; } if (dp) - vput(ITOV(dp)); + vput(fdvp); if (xp) - vput(ITOV(xp)); - vrele(ITOV(ip)); + vput(fvp); + vrele(ap->a_fvp); return (error); bad: @@ -1095,9 +1204,12 @@ bad: vput(ITOV(xp)); vput(ITOV(dp)); out: - ip->i_nlink--; - ip->i_flag |= ICHG; - vrele(ITOV(ip)); + if (VOP_LOCK(fvp) == 0) { + ip->i_nlink--; + ip->i_flag |= ICHG; + vput(fvp); + } else + vrele(fvp); return (error); } @@ -1105,6 +1217,10 @@ out: * A virgin directory (no blushing please). */ static struct dirtemplate mastertemplate = { + 0, 12, DT_DIR, 1, ".", + 0, DIRBLKSIZ - 12, DT_DIR, 2, ".." +}; +static struct odirtemplate omastertemplate = { 0, 12, 1, ".", 0, DIRBLKSIZ - 12, 2, ".." }; @@ -1114,20 +1230,21 @@ static struct dirtemplate mastertemplate = { */ int ufs_mkdir(ap) - struct vop_mkdir_args *ap; + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; { - USES_VOP_TRUNCATE; - USES_VOP_UPDATE; - USES_VOP_VALLOC; - USES_VOP_VFREE; register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct inode *ip, *dp; struct vnode *tvp; - struct dirtemplate dirtemplate; - int error; - int dmode; + struct dirtemplate dirtemplate, *dtp; + struct timeval tv; + int error, dmode; #ifdef DIAGNOSTIC if ((cnp->cn_flags & HASBUF) == 0) @@ -1135,22 +1252,18 @@ ufs_mkdir(ap) #endif dp = VTOI(dvp); if ((nlink_t)dp->i_nlink >= LINK_MAX) { - free(cnp->cn_pnbuf, M_NAMEI); - ufs_iput(dp); - return (EMLINK); + error = EMLINK; + goto out; } - dmode = vap->va_mode&0777; + dmode = vap->va_mode & 0777; dmode |= IFDIR; /* - * Must simulate part of maknode here to acquire the inode, but - * not have it entered in the parent directory. The entry is made - * later after writing "." and ".." entries. + * Must simulate part of ufs_makeinode here to acquire the inode, + * but not have it entered in the parent directory. The entry is + * made later after writing "." and ".." entries. */ - if (error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp)) { - free(cnp->cn_pnbuf, M_NAMEI); - ufs_iput(dp); - return (error); - } + if (error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp)) + goto out; ip = VTOI(tvp); ip->i_uid = cnp->cn_cred->cr_uid; ip->i_gid = dp->i_gid; @@ -1159,16 +1272,17 @@ ufs_mkdir(ap) (error = chkiq(ip, 1, cnp->cn_cred, 0))) { free(cnp->cn_pnbuf, M_NAMEI); VOP_VFREE(tvp, ip->i_number, dmode); - ufs_iput(ip); - ufs_iput(dp); + vput(tvp); + vput(dvp); return (error); } #endif ip->i_flag |= IACC|IUPD|ICHG; ip->i_mode = dmode; - ITOV(ip)->v_type = VDIR; /* Rest init'd in iget() */ + tvp->v_type = VDIR; /* Rest init'd in iget() */ ip->i_nlink = 2; - error = VOP_UPDATE(ITOV(ip), &time, &time, 1); + tv = time; + error = VOP_UPDATE(tvp, &tv, &tv, 1); /* * Bump link count in parent directory @@ -1178,14 +1292,18 @@ ufs_mkdir(ap) */ dp->i_nlink++; dp->i_flag |= ICHG; - if (error = VOP_UPDATE(ITOV(dp), &time, &time, 1)) + if (error = VOP_UPDATE(dvp, &tv, &tv, 1)) goto bad; /* Initialize directory with "." and ".." from static template. */ - dirtemplate = mastertemplate; + if (dvp->v_mount->mnt_maxsymlinklen > 0) + dtp = &mastertemplate; + else + dtp = (struct dirtemplate *)&omastertemplate; + dirtemplate = *dtp; dirtemplate.dot_ino = ip->i_number; dirtemplate.dotdot_ino = dp->i_number; - error = vn_rdwr(UIO_WRITE, ITOV(ip), (caddr_t)&dirtemplate, + error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate, sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0); if (error) { @@ -1213,11 +1331,12 @@ bad: if (error) { ip->i_nlink = 0; ip->i_flag |= ICHG; - ufs_iput(ip); + vput(tvp); } else - *ap->a_vpp = ITOV(ip); + *ap->a_vpp = tvp; +out: FREE(cnp->cn_pnbuf, M_NAMEI); - ufs_iput(dp); + vput(dvp); return (error); } @@ -1226,22 +1345,26 @@ bad: */ int ufs_rmdir(ap) - struct vop_rmdir_args *ap; + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap; { - USES_VOP_TRUNCATE; + register struct vnode *vp = ap->a_vp; register struct vnode *dvp = ap->a_dvp; register struct componentname *cnp = ap->a_cnp; register struct inode *ip, *dp; int error; - ip = VTOI(ap->a_vp); + ip = VTOI(vp); dp = VTOI(dvp); /* * No rmdir "." please. */ if (dp == ip) { vrele(dvp); - ufs_iput(ip); + vput(vp); return (EINVAL); } /* @@ -1257,6 +1380,10 @@ ufs_rmdir(ap) error = ENOTEMPTY; goto out; } + if ((dp->i_flags & APPEND) || (ip->i_flags & (IMMUTABLE | APPEND))) { + error = EPERM; + goto out; + } /* * Delete reference to directory before purging * inode. If we crash in between, the directory @@ -1267,7 +1394,7 @@ ufs_rmdir(ap) dp->i_nlink--; dp->i_flag |= ICHG; cache_purge(dvp); - ufs_iput(dp); + vput(dvp); dvp = NULL; /* * Truncate inode. The only stuff left @@ -1281,12 +1408,13 @@ ufs_rmdir(ap) * worry about them later. */ ip->i_nlink -= 2; - error = VOP_TRUNCATE(ap->a_vp, (off_t)0, IO_SYNC, cnp->cn_cred); + error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, + cnp->cn_proc); cache_purge(ITOV(ip)); out: if (dvp) - ufs_iput(dp); - ufs_iput(ip); + vput(dvp); + vput(vp); return (error); } @@ -1295,18 +1423,33 @@ out: */ int ufs_symlink(ap) - struct vop_symlink_args *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; { - register struct vnode **vpp = ap->a_vpp; - int error; + register struct vnode *vp, **vpp = ap->a_vpp; + register struct inode *ip; + int len, error; if (error = ufs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, vpp, ap->a_cnp)) return (error); - error = vn_rdwr(UIO_WRITE, *vpp, ap->a_target, strlen(ap->a_target), - (off_t)0, UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, - (int *)0, (struct proc *)0); - vput(*vpp); + vp = *vpp; + len = strlen(ap->a_target); + if (len < vp->v_mount->mnt_maxsymlinklen) { + ip = VTOI(vp); + bcopy(ap->a_target, (char *)ip->i_shortlink, len); + ip->i_size = len; + ip->i_flag |= IUPD|ICHG; + } else + error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0, + UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0, + (struct proc *)0); + vput(vp); return (error); } @@ -1321,9 +1464,12 @@ ufs_symlink(ap) */ int ufs_readdir(ap) - struct vop_readdir_args *ap; + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap; { - USES_VOP_READ; register struct uio *uio = ap->a_uio; int count, lost, error; @@ -1334,12 +1480,49 @@ ufs_readdir(ap) return (EINVAL); uio->uio_resid = count; uio->uio_iov->iov_len = count; - error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); +# if (BYTE_ORDER == LITTLE_ENDIAN) + if (ap->a_vp->v_mount->mnt_maxsymlinklen > 0) { + error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); + } else { + struct dirent *dp, *edp; + struct uio auio; + struct iovec aiov; + caddr_t dirbuf; + int readcnt; + u_char tmp; + + auio = *uio; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_segflg = UIO_SYSSPACE; + aiov.iov_len = count; + MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK); + aiov.iov_base = dirbuf; + error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred); + if (error == 0) { + readcnt = count - auio.uio_resid; + edp = (struct dirent *)&dirbuf[readcnt]; + for (dp = (struct dirent *)dirbuf; dp < edp; ) { + tmp = dp->d_namlen; + dp->d_namlen = dp->d_type; + dp->d_type = tmp; + if (dp->d_reclen > 0) { + dp = (struct dirent *) + ((char *)dp + dp->d_reclen); + } else { + error = EIO; + break; + } + } + if (dp >= edp) + error = uiomove(dirbuf, readcnt, uio); + } + FREE(dirbuf, M_TEMP); + } +# else + error = VOP_READ(ap->a_vp, uio, 0, ap->a_cred); +# endif uio->uio_resid += lost; - if ((VTOI(ap->a_vp)->i_size - uio->uio_offset) <= 0) - *ap->a_eofflagp = 1; - else - *ap->a_eofflagp = 0; return (error); } @@ -1348,11 +1531,22 @@ ufs_readdir(ap) */ int ufs_readlink(ap) - struct vop_readlink_args *ap; + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + struct ucred *a_cred; + } */ *ap; { - USES_VOP_READ; + register struct vnode *vp = ap->a_vp; + register struct inode *ip = VTOI(vp); + int isize; - return (VOP_READ(ap->a_vp, ap->a_uio, 0, ap->a_cred)); + isize = ip->i_size; + if (isize < vp->v_mount->mnt_maxsymlinklen) { + uiomove((char *)ip->i_shortlink, isize, ap->a_uio); + return (0); + } + return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred)); } /* @@ -1362,7 +1556,10 @@ ufs_readlink(ap) /* ARGSUSED */ int ufs_abortop(ap) - struct vop_abortop_args *ap; + struct vop_abortop_args /* { + struct vnode *a_dvp; + struct componentname *a_cnp; + } */ *ap; { if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); @@ -1370,30 +1567,83 @@ ufs_abortop(ap) } /* - * Lock an inode. + * Lock an inode. If its already locked, set the WANT bit and sleep. */ int ufs_lock(ap) - struct vop_lock_args *ap; + struct vop_lock_args /* { + struct vnode *a_vp; + } */ *ap; { - register struct inode *ip = VTOI(ap->a_vp); + register struct vnode *vp = ap->a_vp; + register struct inode *ip; + struct proc *p = curproc; /* XXX */ - ILOCK(ip); +start: + while (vp->v_flag & VXLOCK) { + vp->v_flag |= VXWANT; + sleep((caddr_t)vp, PINOD); + } + if (vp->v_tag == VT_NON) + return (ENOENT); + ip = VTOI(vp); + if (ip->i_flag & ILOCKED) { + ip->i_flag |= IWANT; +#ifdef DIAGNOSTIC + if (p) { + if (p->p_pid == ip->i_lockholder) + panic("locking against myself"); + ip->i_lockwaiter = p->p_pid; + } else + ip->i_lockwaiter = -1; +#endif + (void) sleep((caddr_t)ip, PINOD); + goto start; + } +#ifdef DIAGNOSTIC + ip->i_lockwaiter = 0; + if (ip->i_lockholder != 0) + panic("lockholder (%d) != 0", ip->i_lockholder); + if (p && p->p_pid == 0) + printf("locking by process 0\n"); + if (p) + ip->i_lockholder = p->p_pid; + else + ip->i_lockholder = -1; +#endif + ip->i_flag |= ILOCKED; return (0); } /* - * Unlock an inode. + * Unlock an inode. If WANT bit is on, wakeup. */ +int lockcount = 90; int ufs_unlock(ap) - struct vop_unlock_args *ap; + struct vop_unlock_args /* { + struct vnode *a_vp; + } */ *ap; { register struct inode *ip = VTOI(ap->a_vp); + struct proc *p = curproc; /* XXX */ - if (!(ip->i_flag & ILOCKED)) +#ifdef DIAGNOSTIC + if ((ip->i_flag & ILOCKED) == 0) { + vprint("ufs_unlock: unlocked inode", ap->a_vp); panic("ufs_unlock NOT LOCKED"); - IUNLOCK(ip); + } + if (p && p->p_pid != ip->i_lockholder && p->p_pid > -1 && + ip->i_lockholder > -1 && lockcount++ < 100) + panic("unlocker (%d) != lock holder (%d)", + p->p_pid, ip->i_lockholder); + ip->i_lockholder = 0; +#endif + ip->i_flag &= ~ILOCKED; + if (ip->i_flag & IWANT) { + ip->i_flag &= ~IWANT; + wakeup((caddr_t)ip); + } return (0); } @@ -1402,7 +1652,9 @@ ufs_unlock(ap) */ int ufs_islocked(ap) - struct vop_islocked_args *ap; + struct vop_islocked_args /* { + struct vnode *a_vp; + } */ *ap; { if (VTOI(ap->a_vp)->i_flag & ILOCKED) @@ -1414,30 +1666,62 @@ ufs_islocked(ap) * Calculate the logical to physical mapping if not done already, * then call the device strategy routine. */ +#include +int checkblk = 1; +struct ctldebug debug10 = { "checkblk", &checkblk }; int ufs_strategy(ap) - struct vop_strategy_args *ap; + struct vop_strategy_args /* { + struct buf *a_bp; + } */ *ap; { - USES_VOP_BMAP; register struct buf *bp = ap->a_bp; register struct vnode *vp = bp->b_vp; register struct inode *ip; + daddr_t blkno; int error; ip = VTOI(vp); if (vp->v_type == VBLK || vp->v_type == VCHR) panic("ufs_strategy: spec"); - if (bp->b_blkno == bp->b_lblkno) { - if (error = - VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno)) { + if ((checkblk && (long)bp->b_lblkno >= 0 && + (bp->b_flags & B_XXX) == 0) || + bp->b_blkno == bp->b_lblkno) { + if (error = VOP_BMAP(vp, bp->b_lblkno, NULL, &blkno, NULL)) { bp->b_error = error; bp->b_flags |= B_ERROR; biodone(bp); return (error); } + if (bp->b_blkno != bp->b_lblkno && bp->b_blkno != blkno && + !((bp->b_flags & B_READ) && (long)blkno == -1)) + panic("ufs_strategy: bad blkno %d != %d", bp->b_blkno, + blkno); + /* If this is a clustered block, check sub-blocks as well */ + if (bp->b_saveaddr) { + struct buf *tbp; + struct cluster_save *b_save; + int i; + daddr_t bn; + + b_save = (struct cluster_save *)bp->b_saveaddr; + for (i = 0; i < b_save->bs_nchildren; i++) { + tbp = b_save->bs_children[i]; + if ((tbp->b_flags & B_XXX) == 0 && + !VOP_BMAP(vp, tbp->b_lblkno, NULL, + &bn, NULL) && tbp->b_blkno != bn) + panic("ufs_strategy: bad bno %d != %d", + bp->b_blkno, blkno); + } + } + bp->b_blkno = blkno; if ((long)bp->b_blkno == -1) - clrbuf(bp); + if ((bp->b_flags & B_READ) == 0) + panic("ufs_startegy: write unallocated block"); + else + clrbuf(bp); } + bp->b_flags &=~ B_XXX; if ((long)bp->b_blkno == -1) { biodone(bp); return (0); @@ -1453,7 +1737,9 @@ ufs_strategy(ap) */ int ufs_print(ap) - struct vop_print_args *ap; + struct vop_print_args /* { + struct vnode *a_vp; + } */ *ap; { register struct vnode *vp = ap->a_vp; register struct inode *ip = VTOI(vp); @@ -1479,9 +1765,13 @@ ufs_print(ap) */ int ufsspec_read(ap) - struct vop_read_args *ap; + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; { - extern int (**spec_vnodeop_p)(); /* * Set access flag. @@ -1495,9 +1785,13 @@ ufsspec_read(ap) */ int ufsspec_write(ap) - struct vop_write_args *ap; + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; { - extern int (**spec_vnodeop_p)(); /* * Set update and change flags. @@ -1513,9 +1807,13 @@ ufsspec_write(ap) */ int ufsspec_close(ap) - struct vop_close_args *ap; + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { - extern int (**spec_vnodeop_p)(); register struct inode *ip = VTOI(ap->a_vp); if (ap->a_vp->v_usecount > 1 && !(ip->i_flag & ILOCKED)) @@ -1529,7 +1827,12 @@ ufsspec_close(ap) */ int ufsfifo_read(ap) - struct vop_read_args *ap; + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; { extern int (**fifo_vnodeop_p)(); @@ -1545,7 +1848,12 @@ ufsfifo_read(ap) */ int ufsfifo_write(ap) - struct vop_write_args *ap; + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + struct ucred *a_cred; + } */ *ap; { extern int (**fifo_vnodeop_p)(); @@ -1562,7 +1870,12 @@ ufsfifo_write(ap) * Update the times on the inode then do device close. */ ufsfifo_close(ap) - struct vop_close_args *ap; + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + struct ucred *a_cred; + struct proc *a_p; + } */ *ap; { extern int (**fifo_vnodeop_p)(); register struct inode *ip = VTOI(ap->a_vp); @@ -1578,7 +1891,13 @@ ufsfifo_close(ap) */ int ufs_advlock(ap) - struct vop_advlock_args *ap; + struct vop_advlock_args /* { + struct vnode *a_vp; + caddr_t a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap; { register struct inode *ip = VTOI(ap->a_vp); register struct flock *fl = ap->a_fl; @@ -1669,9 +1988,8 @@ ufs_vinit(mntp, specops, fifoops, vpp) int (**fifoops)(); struct vnode **vpp; { - struct inode *ip, *nip; + struct inode *ip; struct vnode *vp, *nvp; - extern int (**spec_vnodeop_p)(); vp = *vpp; ip = VTOI(vp); @@ -1683,8 +2001,8 @@ ufs_vinit(mntp, specops, fifoops, vpp) /* * Discard unneeded vnode, but save its inode. */ - remque(ip); - IUNLOCK(ip); + ufs_ihashrem(ip); + VOP_UNLOCK(vp); nvp->v_data = vp->v_data; vp->v_data = NULL; vp->v_op = spec_vnodeop_p; @@ -1727,10 +2045,8 @@ ufs_makeinode(mode, dvp, vpp, cnp) struct vnode **vpp; struct componentname *cnp; { - USES_VOP_UPDATE; - USES_VOP_VALLOC; - USES_VOP_VFREE; register struct inode *ip, *pdir; + struct timeval tv; struct vnode *tvp; int error; @@ -1745,19 +2061,22 @@ ufs_makeinode(mode, dvp, vpp, cnp) if (error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp)) { free(cnp->cn_pnbuf, M_NAMEI); - ufs_iput(pdir); + vput(dvp); return (error); } ip = VTOI(tvp); - ip->i_uid = cnp->cn_cred->cr_uid; ip->i_gid = pdir->i_gid; + if ((mode & IFMT) == IFLNK) + ip->i_uid = pdir->i_uid; + else + ip->i_uid = cnp->cn_cred->cr_uid; #ifdef QUOTA if ((error = getinoquota(ip)) || (error = chkiq(ip, 1, cnp->cn_cred, 0))) { free(cnp->cn_pnbuf, M_NAMEI); VOP_VFREE(tvp, ip->i_number, mode); - ufs_iput(ip); - ufs_iput(pdir); + vput(tvp); + vput(dvp); return (error); } #endif @@ -1772,13 +2091,14 @@ ufs_makeinode(mode, dvp, vpp, cnp) /* * Make sure inode goes to disk before directory entry. */ - if (error = VOP_UPDATE(tvp, &time, &time, 1)) + tv = time; + if (error = VOP_UPDATE(tvp, &tv, &tv, 1)) goto bad; if (error = ufs_direnter(ip, dvp, cnp)) goto bad; if ((cnp->cn_flags & SAVESTART) == 0) FREE(cnp->cn_pnbuf, M_NAMEI); - ufs_iput(pdir); + vput(dvp); *vpp = tvp; return (0); @@ -1788,9 +2108,9 @@ bad: * or the directory so must deallocate the inode. */ free(cnp->cn_pnbuf, M_NAMEI); - ufs_iput(pdir); + vput(dvp); ip->i_nlink = 0; ip->i_flag |= ICHG; - ufs_iput(ip); + vput(tvp); return (error); }