Adapt ffs, lfs, and ext2fs to use genfs_rename.
ok dholland, rmind
This commit is contained in:
parent
aff071a220
commit
aeadee1d6d
@ -1,4 +1,4 @@
|
||||
# $NetBSD: Makefile,v 1.3 2010/02/16 20:42:46 pooka Exp $
|
||||
# $NetBSD: Makefile,v 1.4 2012/05/09 00:21:17 riastradh Exp $
|
||||
#
|
||||
|
||||
.PATH: ${.CURDIR}/../../../../ufs/ext2fs
|
||||
@ -6,8 +6,8 @@
|
||||
LIB= rumpfs_ext2fs
|
||||
|
||||
SRCS= ext2fs_alloc.c ext2fs_balloc.c ext2fs_bmap.c ext2fs_bswap.c \
|
||||
ext2fs_inode.c ext2fs_lookup.c ext2fs_readwrite.c ext2fs_subr.c \
|
||||
ext2fs_vfsops.c ext2fs_vnops.c
|
||||
ext2fs_inode.c ext2fs_lookup.c ext2fs_readwrite.c \
|
||||
ext2fs_rename.c ext2fs_subr.c ext2fs_vfsops.c ext2fs_vnops.c
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
.include <bsd.klinks.mk>
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: Makefile,v 1.13 2011/03/06 17:08:37 bouyer Exp $
|
||||
# $NetBSD: Makefile,v 1.14 2012/05/09 00:21:17 riastradh Exp $
|
||||
#
|
||||
|
||||
.PATH: ${.CURDIR}/../../../../ufs/ffs ${.CURDIR}/../../../../ufs/ufs
|
||||
@ -10,7 +10,7 @@ SRCS= ffs_alloc.c ffs_appleufs.c ffs_balloc.c ffs_bswap.c ffs_inode.c \
|
||||
ffs_wapbl.c ffs_quota2.c
|
||||
|
||||
SRCS+= ufs_bmap.c ufs_dirhash.c ufs_extattr.c ufs_ihash.c ufs_inode.c \
|
||||
ufs_lookup.c ufs_vfsops.c ufs_vnops.c ufs_wapbl.c \
|
||||
ufs_lookup.c ufs_rename.c ufs_vfsops.c ufs_vnops.c ufs_wapbl.c \
|
||||
ufs_quota.c ufs_quota2.c quota2_subr.c
|
||||
|
||||
CPPFLAGS+= -DFFS_EI -DUFS_DIRHASH -DWAPBL -DAPPLE_UFS -DUFS_EXTATTR \
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ext2fs_dir.h,v 1.18 2009/10/19 18:41:17 bouyer Exp $ */
|
||||
/* $NetBSD: ext2fs_dir.h,v 1.19 2012/05/09 00:21:18 riastradh Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
@ -67,6 +67,8 @@
|
||||
#ifndef _UFS_EXT2FS_EXT2FS_DIR_H_
|
||||
#define _UFS_EXT2FS_EXT2FS_DIR_H_
|
||||
|
||||
#include <ufs/ext2fs/ext2fs_dinode.h>
|
||||
|
||||
/*
|
||||
* Theoretically, directories can be more than 2Gb in length, however, in
|
||||
* practice this seems unlikely. So, we define the type doff_t as a 32-bit
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ext2fs_extern.h,v 1.43 2011/07/12 16:59:48 dholland Exp $ */
|
||||
/* $NetBSD: ext2fs_extern.h,v 1.44 2012/05/09 00:21:18 riastradh Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993, 1994
|
||||
@ -77,6 +77,7 @@ struct uio;
|
||||
struct vnode;
|
||||
struct mbuf;
|
||||
struct componentname;
|
||||
struct ufs_lookup_results;
|
||||
|
||||
extern struct pool ext2fs_inode_pool; /* memory pool for inodes */
|
||||
extern struct pool ext2fs_dinode_pool; /* memory pool for dinodes */
|
||||
|
1012
sys/ufs/ext2fs/ext2fs_rename.c
Normal file
1012
sys/ufs/ext2fs/ext2fs_rename.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ext2fs_vnops.c,v 1.103 2012/04/29 22:54:00 chs Exp $ */
|
||||
/* $NetBSD: ext2fs_vnops.c,v 1.104 2012/05/09 00:21:18 riastradh Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
@ -65,7 +65,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ext2fs_vnops.c,v 1.103 2012/04/29 22:54:00 chs Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: ext2fs_vnops.c,v 1.104 2012/05/09 00:21:18 riastradh Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
@ -643,426 +643,6 @@ out2:
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rename system call.
|
||||
* rename("foo", "bar");
|
||||
* is essentially
|
||||
* unlink("bar");
|
||||
* link("foo", "bar");
|
||||
* unlink("foo");
|
||||
* but ``atomically''. Can't do full commit without saving state in the
|
||||
* inode on disk which isn't feasible at this time. Best we can do is
|
||||
* always guarantee the target exists.
|
||||
*
|
||||
* 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 inode 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
|
||||
ext2fs_rename(void *v)
|
||||
{
|
||||
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 = v;
|
||||
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;
|
||||
struct inode *ip, *xp, *dp;
|
||||
struct ext2fs_dirtemplate dirbuf;
|
||||
int doingdirectory = 0, oldparent = 0, newparent = 0;
|
||||
int error = 0;
|
||||
u_char namlen;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
else
|
||||
vput(tdvp);
|
||||
if (tvp)
|
||||
vput(tvp);
|
||||
VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
|
||||
vrele(fdvp);
|
||||
vrele(fvp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if just deleting a link name.
|
||||
*/
|
||||
if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
|
||||
(VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) {
|
||||
error = EPERM;
|
||||
goto abortit;
|
||||
}
|
||||
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. */
|
||||
vrele(fvp);
|
||||
fcnp->cn_flags &= ~(MODMASK);
|
||||
fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
|
||||
fcnp->cn_nameiop = DELETE;
|
||||
vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
|
||||
if ((error = relookup(fdvp, &fvp, fcnp, 0))) {
|
||||
vput(fdvp);
|
||||
return (error);
|
||||
}
|
||||
return (VOP_REMOVE(fdvp, fvp, fcnp));
|
||||
}
|
||||
if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
|
||||
goto abortit;
|
||||
dp = VTOI(fdvp);
|
||||
ip = VTOI(fvp);
|
||||
if ((nlink_t) ip->i_e2fs_nlink >= LINK_MAX) {
|
||||
VOP_UNLOCK(fvp);
|
||||
error = EMLINK;
|
||||
goto abortit;
|
||||
}
|
||||
if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
|
||||
(dp->i_e2fs_flags & EXT2_APPEND)) {
|
||||
VOP_UNLOCK(fvp);
|
||||
error = EPERM;
|
||||
goto abortit;
|
||||
}
|
||||
if ((ip->i_e2fs_mode & IFMT) == IFDIR) {
|
||||
error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
|
||||
if (!error && tvp)
|
||||
error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred);
|
||||
if (error) {
|
||||
VOP_UNLOCK(fvp);
|
||||
error = EACCES;
|
||||
goto abortit;
|
||||
}
|
||||
/*
|
||||
* Avoid ".", "..", and aliases of "." for obvious reasons.
|
||||
*/
|
||||
if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
|
||||
dp == ip ||
|
||||
(fcnp->cn_flags & ISDOTDOT) ||
|
||||
(tcnp->cn_flags & ISDOTDOT) ||
|
||||
(ip->i_flag & IN_RENAME)) {
|
||||
VOP_UNLOCK(fvp);
|
||||
error = EINVAL;
|
||||
goto abortit;
|
||||
}
|
||||
ip->i_flag |= IN_RENAME;
|
||||
oldparent = dp->i_number;
|
||||
doingdirectory = 1;
|
||||
}
|
||||
VN_KNOTE(fdvp, NOTE_WRITE); /* XXXLUKEM/XXX: right place? */
|
||||
|
||||
/*
|
||||
* 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
|
||||
* completing our work, the link count
|
||||
* may be wrong, but correctable.
|
||||
*/
|
||||
ip->i_e2fs_nlink++;
|
||||
ip->i_flag |= IN_CHANGE;
|
||||
if ((error = ext2fs_update(fvp, NULL, NULL, UPDATE_WAIT)) != 0) {
|
||||
VOP_UNLOCK(fvp);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* If ".." must be changed (ie the directory gets a new
|
||||
* parent) then the source directory must not be in the
|
||||
* directory hierarchy 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 "..". We must repeat the call
|
||||
* to namei, as the parent directory is unlocked by the
|
||||
* call to checkpath().
|
||||
*/
|
||||
error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
|
||||
VOP_UNLOCK(fvp);
|
||||
if (oldparent != dp->i_number)
|
||||
newparent = dp->i_number;
|
||||
if (doingdirectory && newparent) {
|
||||
if (error) /* write access check above */
|
||||
goto bad;
|
||||
if (xp != NULL)
|
||||
vput(tvp);
|
||||
vref(tdvp); /* compensate for the ref checkpath loses */
|
||||
error = ext2fs_checkpath(ip, dp, tcnp->cn_cred);
|
||||
if (error != 0) {
|
||||
vrele(tdvp);
|
||||
goto out;
|
||||
}
|
||||
vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY);
|
||||
if ((error = relookup(tdvp, &tvp, tcnp, 0)) != 0) {
|
||||
vput(tdvp);
|
||||
goto out;
|
||||
}
|
||||
dp = VTOI(tdvp);
|
||||
xp = NULL;
|
||||
if (tvp)
|
||||
xp = VTOI(tvp);
|
||||
}
|
||||
/*
|
||||
* 2) If target doesn't exist, link the target
|
||||
* to the source and unlink the source.
|
||||
* Otherwise, rewrite the target directory
|
||||
* entry to reference the source inode and
|
||||
* expunge the original entry's existence.
|
||||
*/
|
||||
if (xp == NULL) {
|
||||
if (dp->i_dev != ip->i_dev)
|
||||
panic("rename: EXDEV");
|
||||
/*
|
||||
* Account for ".." in new directory.
|
||||
* When source and destination have the same
|
||||
* parent we don't fool with the link count.
|
||||
*/
|
||||
if (doingdirectory && newparent) {
|
||||
if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
|
||||
error = EMLINK;
|
||||
goto bad;
|
||||
}
|
||||
dp->i_e2fs_nlink++;
|
||||
dp->i_flag |= IN_CHANGE;
|
||||
if ((error = ext2fs_update(tdvp, NULL, NULL,
|
||||
UPDATE_WAIT)) != 0)
|
||||
goto bad;
|
||||
}
|
||||
error = ext2fs_direnter(ip, tdvp, &VTOI(tdvp)->i_crap, tcnp);
|
||||
if (error != 0) {
|
||||
if (doingdirectory && newparent) {
|
||||
dp->i_e2fs_nlink--;
|
||||
dp->i_flag |= IN_CHANGE;
|
||||
(void)ext2fs_update(tdvp, NULL, NULL,
|
||||
UPDATE_WAIT);
|
||||
}
|
||||
goto bad;
|
||||
}
|
||||
VN_KNOTE(tdvp, NOTE_WRITE);
|
||||
vput(tdvp);
|
||||
} else {
|
||||
if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
|
||||
panic("rename: EXDEV");
|
||||
/*
|
||||
* Short circuit rename(foo, foo).
|
||||
*/
|
||||
if (xp->i_number == ip->i_number)
|
||||
panic("rename: same file");
|
||||
/*
|
||||
* 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 (dp->i_e2fs_mode & S_ISTXT) {
|
||||
error = kauth_authorize_vnode(tcnp->cn_cred,
|
||||
KAUTH_VNODE_DELETE, tvp, tdvp,
|
||||
genfs_can_sticky(tcnp->cn_cred, dp->i_uid,
|
||||
xp->i_uid));
|
||||
if (error) {
|
||||
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 ((xp->i_e2fs_mode & IFMT) == IFDIR) {
|
||||
if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
|
||||
xp->i_e2fs_nlink > 2) {
|
||||
error = ENOTEMPTY;
|
||||
goto bad;
|
||||
}
|
||||
if (!doingdirectory) {
|
||||
error = ENOTDIR;
|
||||
goto bad;
|
||||
}
|
||||
cache_purge(tdvp);
|
||||
} else if (doingdirectory) {
|
||||
error = EISDIR;
|
||||
goto bad;
|
||||
}
|
||||
error = ext2fs_dirrewrite(dp, &dp->i_crap, ip, tcnp);
|
||||
if (error != 0)
|
||||
goto bad;
|
||||
/*
|
||||
* If the target directory is in the same
|
||||
* directory as the source directory,
|
||||
* decrement the link count on the parent
|
||||
* of the target directory.
|
||||
*/
|
||||
if (doingdirectory && !newparent) {
|
||||
dp->i_e2fs_nlink--;
|
||||
dp->i_flag |= IN_CHANGE;
|
||||
}
|
||||
/*
|
||||
* Adjust the link count of the target to
|
||||
* reflect the dirrewrite above. If this is
|
||||
* a directory it is empty and there are
|
||||
* no links to it, so we can squash the inode and
|
||||
* any space associated with it. We disallowed
|
||||
* renaming over top of a directory with links to
|
||||
* it above, as the remaining link would point to
|
||||
* a directory without "." or ".." entries.
|
||||
*/
|
||||
xp->i_e2fs_nlink--;
|
||||
if (doingdirectory) {
|
||||
if (--xp->i_e2fs_nlink != 0)
|
||||
panic("rename: linked directory");
|
||||
error = ext2fs_truncate(tvp, (off_t)0, IO_SYNC,
|
||||
tcnp->cn_cred);
|
||||
}
|
||||
xp->i_flag |= IN_CHANGE;
|
||||
VN_KNOTE(tdvp, NOTE_WRITE);
|
||||
vput(tdvp);
|
||||
VN_KNOTE(tvp, NOTE_DELETE);
|
||||
vput(tvp);
|
||||
xp = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3) Unlink the source.
|
||||
*/
|
||||
fcnp->cn_flags &= ~(MODMASK);
|
||||
fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
|
||||
vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
|
||||
if ((error = relookup(fdvp, &fvp, fcnp, 0))) {
|
||||
vput(fdvp);
|
||||
vrele(ap->a_fvp);
|
||||
return (error);
|
||||
}
|
||||
if (fvp != NULL) {
|
||||
xp = VTOI(fvp);
|
||||
dp = VTOI(fdvp);
|
||||
} else {
|
||||
/*
|
||||
* From name has disappeared.
|
||||
*/
|
||||
if (doingdirectory)
|
||||
panic("ext2fs_rename: lost dir entry");
|
||||
vrele(ap->a_fvp);
|
||||
return (0);
|
||||
}
|
||||
/*
|
||||
* Ensure that the directory entry still exists and has not
|
||||
* changed while the new name has been entered. If the source is
|
||||
* a file then the entry may have been unlinked or renamed. In
|
||||
* either case there is no further work to be done. If the source
|
||||
* is a directory then it cannot have been rmdir'ed; its link
|
||||
* count of three would cause a rmdir to fail with ENOTEMPTY.
|
||||
* The IRENAME flag ensures that it cannot be moved by another
|
||||
* rename.
|
||||
*/
|
||||
if (xp != ip) {
|
||||
if (doingdirectory)
|
||||
panic("ext2fs_rename: lost dir entry");
|
||||
} else {
|
||||
/*
|
||||
* If the source is a directory with a
|
||||
* new parent, the link count of the old
|
||||
* parent directory must be decremented
|
||||
* and ".." set to point to the new parent.
|
||||
*/
|
||||
if (doingdirectory && newparent) {
|
||||
KASSERT(dp != NULL);
|
||||
dp->i_e2fs_nlink--;
|
||||
dp->i_flag |= IN_CHANGE;
|
||||
error = vn_rdwr(UIO_READ, fvp, (void *)&dirbuf,
|
||||
sizeof (struct ext2fs_dirtemplate), (off_t)0,
|
||||
UIO_SYSSPACE, IO_NODELOCKED,
|
||||
tcnp->cn_cred, (size_t *)0, NULL);
|
||||
if (error == 0) {
|
||||
namlen = dirbuf.dotdot_namlen;
|
||||
if (namlen != 2 ||
|
||||
dirbuf.dotdot_name[0] != '.' ||
|
||||
dirbuf.dotdot_name[1] != '.') {
|
||||
ufs_dirbad(xp, (doff_t)12,
|
||||
"ext2fs_rename: mangled dir");
|
||||
} else {
|
||||
dirbuf.dotdot_ino = h2fs32(newparent);
|
||||
(void) vn_rdwr(UIO_WRITE, fvp,
|
||||
(void *)&dirbuf,
|
||||
sizeof (struct dirtemplate),
|
||||
(off_t)0, UIO_SYSSPACE,
|
||||
IO_NODELOCKED|IO_SYNC,
|
||||
tcnp->cn_cred, (size_t *)0,
|
||||
NULL);
|
||||
cache_purge(fdvp);
|
||||
}
|
||||
}
|
||||
}
|
||||
error = ext2fs_dirremove(fdvp, &VTOI(fdvp)->i_crap, fcnp);
|
||||
if (!error) {
|
||||
xp->i_e2fs_nlink--;
|
||||
xp->i_flag |= IN_CHANGE;
|
||||
}
|
||||
xp->i_flag &= ~IN_RENAME;
|
||||
}
|
||||
VN_KNOTE(fvp, NOTE_RENAME);
|
||||
if (dp)
|
||||
vput(fdvp);
|
||||
if (xp)
|
||||
vput(fvp);
|
||||
vrele(ap->a_fvp);
|
||||
return (error);
|
||||
|
||||
bad:
|
||||
if (xp)
|
||||
vput(ITOV(xp));
|
||||
vput(ITOV(dp));
|
||||
out:
|
||||
if (doingdirectory)
|
||||
ip->i_flag &= ~IN_RENAME;
|
||||
if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
|
||||
ip->i_e2fs_nlink--;
|
||||
ip->i_flag |= IN_CHANGE;
|
||||
vput(fvp);
|
||||
} else
|
||||
vrele(fvp);
|
||||
vrele(fdvp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mkdir system call
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: files.ufs,v 1.28 2012/04/19 15:55:40 ttoth Exp $
|
||||
# $NetBSD: files.ufs,v 1.29 2012/05/09 00:21:17 riastradh Exp $
|
||||
|
||||
deffs FFS
|
||||
deffs EXT2FS
|
||||
@ -19,6 +19,7 @@ file ufs/ext2fs/ext2fs_bswap.c ext2fs
|
||||
file ufs/ext2fs/ext2fs_inode.c ext2fs
|
||||
file ufs/ext2fs/ext2fs_lookup.c ext2fs
|
||||
file ufs/ext2fs/ext2fs_readwrite.c ext2fs
|
||||
file ufs/ext2fs/ext2fs_rename.c ext2fs
|
||||
file ufs/ext2fs/ext2fs_subr.c ext2fs
|
||||
file ufs/ext2fs/ext2fs_vfsops.c ext2fs
|
||||
file ufs/ext2fs/ext2fs_vnops.c ext2fs
|
||||
@ -83,6 +84,7 @@ file ufs/ufs/ufs_quota1.c quota & (ffs | lfs | mfs | ext2fs | chfs)
|
||||
file ufs/ufs/ufs_quota2.c quota2 & (ffs | lfs | mfs | ext2fs | chfs)
|
||||
file ufs/ufs/quota1_subr.c
|
||||
file ufs/ufs/quota2_subr.c quota2 & (ffs | lfs | mfs | ext2fs | chfs)
|
||||
file ufs/ufs/ufs_rename.c ffs | lfs | mfs | chfs
|
||||
file ufs/ufs/ufs_vfsops.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ufs/ufs_vnops.c ffs | lfs | mfs | ext2fs | chfs
|
||||
file ufs/ufs/ufs_wapbl.c ffs & wapbl
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: lfs_vnops.c,v 1.241 2012/03/13 18:41:14 elad Exp $ */
|
||||
/* $NetBSD: lfs_vnops.c,v 1.242 2012/05/09 00:21:18 riastradh Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc.
|
||||
@ -60,7 +60,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: lfs_vnops.c,v 1.241 2012/03/13 18:41:14 elad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: lfs_vnops.c,v 1.242 2012/05/09 00:21:18 riastradh Exp $");
|
||||
|
||||
#ifdef _KERNEL_OPT
|
||||
#include "opt_compat_netbsd.h"
|
||||
@ -810,872 +810,131 @@ lfs_link(void *v)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* XXX following lifted from ufs_lookup.c */
|
||||
#define FSFMT(vp) (((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0)
|
||||
static const struct genfs_rename_ops lfs_genfs_rename_ops;
|
||||
|
||||
/*
|
||||
* Check if either entry referred to by FROM_ULR is within the range
|
||||
* of entries named by TO_ULR.
|
||||
*/
|
||||
static int
|
||||
ulr_overlap(const struct ufs_lookup_results *from_ulr,
|
||||
const struct ufs_lookup_results *to_ulr)
|
||||
{
|
||||
doff_t from_start, from_prevstart;
|
||||
doff_t to_start, to_end;
|
||||
|
||||
/*
|
||||
* FROM is a DELETE result; offset points to the entry to
|
||||
* remove and subtracting count gives the previous entry.
|
||||
*/
|
||||
from_start = from_ulr->ulr_offset - from_ulr->ulr_count;
|
||||
from_prevstart = from_ulr->ulr_offset;
|
||||
|
||||
/*
|
||||
* TO is a RENAME (thus non-DELETE) result; offset points
|
||||
* to the beginning of a region to write in, and adding
|
||||
* count gives the end of the region.
|
||||
*/
|
||||
to_start = to_ulr->ulr_offset;
|
||||
to_end = to_ulr->ulr_offset + to_ulr->ulr_count;
|
||||
|
||||
if (from_prevstart >= to_start && from_prevstart < to_end) {
|
||||
return 1;
|
||||
}
|
||||
if (from_start >= to_start && from_start < to_end) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A virgin directory (no blushing please).
|
||||
*/
|
||||
static const struct dirtemplate mastertemplate = {
|
||||
0, 12, DT_DIR, 1, ".",
|
||||
0, DIRBLKSIZ - 12, DT_DIR, 2, ".."
|
||||
};
|
||||
|
||||
/*
|
||||
* Wrapper for relookup that also updates the supplemental results.
|
||||
*/
|
||||
static int
|
||||
do_relookup(struct vnode *dvp, struct ufs_lookup_results *ulr,
|
||||
struct vnode **vp, struct componentname *cnp)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = relookup(dvp, vp, cnp, 0);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
/* update the supplemental reasults */
|
||||
*ulr = VTOI(dvp)->i_crap;
|
||||
UFS_CHECK_CRAPCOUNTER(VTOI(dvp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock and relookup a sequence of two directories and two children.
|
||||
* lfs_sane_rename: The hairiest vop, with the saner API.
|
||||
*
|
||||
* Arguments:
|
||||
*
|
||||
* . fdvp (from directory vnode),
|
||||
* . fcnp (from component name),
|
||||
* . tdvp (to directory vnode),
|
||||
* . tcnp (to component name),
|
||||
* . cred (credentials structure), and
|
||||
* . posixly_correct (flag for behaviour if target & source link same file).
|
||||
*
|
||||
* fdvp and tdvp may be the same, and must be referenced and unlocked.
|
||||
*/
|
||||
static int
|
||||
lock_vnode_sequence(struct vnode *d1, struct ufs_lookup_results *ulr1,
|
||||
struct vnode **v1_ret, struct componentname *cn1,
|
||||
int v1_missing_ok,
|
||||
int overlap_error,
|
||||
struct vnode *d2, struct ufs_lookup_results *ulr2,
|
||||
struct vnode **v2_ret, struct componentname *cn2,
|
||||
int v2_missing_ok)
|
||||
lfs_sane_rename(
|
||||
struct vnode *fdvp, struct componentname *fcnp,
|
||||
struct vnode *tdvp, struct componentname *tcnp,
|
||||
kauth_cred_t cred, bool posixly_correct)
|
||||
{
|
||||
struct vnode *v1, *v2;
|
||||
int error;
|
||||
|
||||
KASSERT(d1 != d2);
|
||||
|
||||
vn_lock(d1, LK_EXCLUSIVE | LK_RETRY);
|
||||
if (VTOI(d1)->i_size == 0) {
|
||||
/* d1 has been rmdir'd */
|
||||
VOP_UNLOCK(d1);
|
||||
return ENOENT;
|
||||
}
|
||||
error = do_relookup(d1, ulr1, &v1, cn1);
|
||||
if (v1_missing_ok) {
|
||||
if (error == ENOENT) {
|
||||
/*
|
||||
* Note: currently if the name doesn't exist,
|
||||
* relookup succeeds (it intercepts the
|
||||
* EJUSTRETURN from VOP_LOOKUP) and sets tvp
|
||||
* to NULL. Therefore, we will never get
|
||||
* ENOENT and this branch is not needed.
|
||||
* However, in a saner future the EJUSTRETURN
|
||||
* garbage will go away, so let's DTRT.
|
||||
*/
|
||||
v1 = NULL;
|
||||
error = 0;
|
||||
}
|
||||
} else {
|
||||
if (error == 0 && v1 == NULL) {
|
||||
/* This is what relookup sets if v1 disappeared. */
|
||||
error = ENOENT;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
VOP_UNLOCK(d1);
|
||||
return error;
|
||||
}
|
||||
if (v1 && v1 == d2) {
|
||||
VOP_UNLOCK(d1);
|
||||
VOP_UNLOCK(v1);
|
||||
vrele(v1);
|
||||
return overlap_error;
|
||||
}
|
||||
struct ufs_lookup_results fulr, tulr;
|
||||
|
||||
/*
|
||||
* The right way to do this is to do lookups without locking
|
||||
* the results, and lock the results afterwards; then at the
|
||||
* end we can avoid trying to lock v2 if v2 == v1.
|
||||
*
|
||||
* However, for the reasons described in the fdvp == tdvp case
|
||||
* in rename below, we can't do that safely. So, in the case
|
||||
* where v1 is not a directory, unlock it and lock it again
|
||||
* afterwards. This is safe in locking order because a
|
||||
* non-directory can't be above anything else in the tree. If
|
||||
* v1 *is* a directory, that's not true, but then because d1
|
||||
* != d2, v1 != v2.
|
||||
* XXX Provisional kludge -- ufs_lookup does not reject rename
|
||||
* of . or .. (from or to), so we hack it here. This is not
|
||||
* the right place: it should be caller's responsibility to
|
||||
* reject this case.
|
||||
*/
|
||||
if (v1 && v1->v_type != VDIR) {
|
||||
VOP_UNLOCK(v1);
|
||||
}
|
||||
vn_lock(d2, LK_EXCLUSIVE | LK_RETRY);
|
||||
if (VTOI(d2)->i_size == 0) {
|
||||
/* d2 has been rmdir'd */
|
||||
VOP_UNLOCK(d2);
|
||||
if (v1 && v1->v_type == VDIR) {
|
||||
VOP_UNLOCK(v1);
|
||||
}
|
||||
VOP_UNLOCK(d1);
|
||||
if (v1) {
|
||||
vrele(v1);
|
||||
}
|
||||
return ENOENT;
|
||||
}
|
||||
error = do_relookup(d2, ulr2, &v2, cn2);
|
||||
if (v2_missing_ok) {
|
||||
if (error == ENOENT) {
|
||||
/* as above */
|
||||
v2 = NULL;
|
||||
error = 0;
|
||||
}
|
||||
} else {
|
||||
if (error == 0 && v2 == NULL) {
|
||||
/* This is what relookup sets if v2 disappeared. */
|
||||
error = ENOENT;
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
VOP_UNLOCK(d2);
|
||||
if (v1 && v1->v_type == VDIR) {
|
||||
VOP_UNLOCK(v1);
|
||||
}
|
||||
VOP_UNLOCK(d1);
|
||||
if (v1) {
|
||||
vrele(v1);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
if (v1 && v1->v_type != VDIR && v1 != v2) {
|
||||
vn_lock(v1, LK_EXCLUSIVE | LK_RETRY);
|
||||
}
|
||||
*v1_ret = v1;
|
||||
*v2_ret = v2;
|
||||
return 0;
|
||||
KASSERT(fcnp != NULL);
|
||||
KASSERT(tcnp != NULL);
|
||||
KASSERT(fcnp != tcnp);
|
||||
KASSERT(fcnp->cn_nameptr != NULL);
|
||||
KASSERT(tcnp->cn_nameptr != NULL);
|
||||
|
||||
if ((fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT)
|
||||
return EINVAL; /* XXX EISDIR? */
|
||||
if ((fcnp->cn_namelen == 1) && (fcnp->cn_nameptr[0] == '.'))
|
||||
return EINVAL;
|
||||
if ((tcnp->cn_namelen == 1) && (tcnp->cn_nameptr[0] == '.'))
|
||||
return EINVAL;
|
||||
|
||||
return genfs_sane_rename(&lfs_genfs_rename_ops,
|
||||
fdvp, fcnp, &fulr, tdvp, tcnp, &tulr,
|
||||
cred, posixly_correct);
|
||||
}
|
||||
|
||||
/*
|
||||
* lfs_rename: The hairiest vop, with the insanest API. Defer to
|
||||
* genfs_insane_rename immediately.
|
||||
*/
|
||||
int
|
||||
lfs_rename(void *v)
|
||||
{
|
||||
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 = v;
|
||||
struct vnode *tvp, *tdvp, *fvp, *fdvp;
|
||||
struct componentname *tcnp, *fcnp;
|
||||
struct inode *ip, *txp, *fxp, *tdp, *fdp;
|
||||
struct mount *mp;
|
||||
struct direct *newdir;
|
||||
int doingdirectory, error, marked;
|
||||
ino_t oldparent, newparent;
|
||||
|
||||
struct ufs_lookup_results from_ulr, to_ulr;
|
||||
struct lfs *fs = VTOI(ap->a_fvp)->i_lfs;
|
||||
return genfs_insane_rename(v, &lfs_sane_rename);
|
||||
}
|
||||
|
||||
tvp = ap->a_tvp;
|
||||
tdvp = ap->a_tdvp;
|
||||
fvp = ap->a_fvp;
|
||||
fdvp = ap->a_fdvp;
|
||||
tcnp = ap->a_tcnp;
|
||||
fcnp = ap->a_fcnp;
|
||||
doingdirectory = error = 0;
|
||||
oldparent = newparent = 0;
|
||||
marked = 0;
|
||||
/*
|
||||
* lfs_gro_rename: Actually perform the rename operation. Do a little
|
||||
* LFS bookkeeping and then defer to ufs_gro_rename.
|
||||
*/
|
||||
static int
|
||||
lfs_gro_rename(struct mount *mp, kauth_cred_t cred,
|
||||
struct vnode *fdvp, struct componentname *fcnp,
|
||||
void *fde, struct vnode *fvp,
|
||||
struct vnode *tdvp, struct componentname *tcnp,
|
||||
void *tde, struct vnode *tvp)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* save the supplemental lookup results as they currently exist */
|
||||
from_ulr = VTOI(fdvp)->i_crap;
|
||||
to_ulr = VTOI(tdvp)->i_crap;
|
||||
UFS_CHECK_CRAPCOUNTER(VTOI(fdvp));
|
||||
UFS_CHECK_CRAPCOUNTER(VTOI(tdvp));
|
||||
KASSERT(mp != NULL);
|
||||
KASSERT(fdvp != NULL);
|
||||
KASSERT(fcnp != NULL);
|
||||
KASSERT(fde != NULL);
|
||||
KASSERT(fvp != NULL);
|
||||
KASSERT(tdvp != NULL);
|
||||
KASSERT(tcnp != NULL);
|
||||
KASSERT(tde != NULL);
|
||||
KASSERT(fdvp != fvp);
|
||||
KASSERT(fdvp != tvp);
|
||||
KASSERT(tdvp != fvp);
|
||||
KASSERT(tdvp != tvp);
|
||||
KASSERT(fvp != tvp);
|
||||
KASSERT(fdvp->v_mount == mp);
|
||||
KASSERT(fvp->v_mount == mp);
|
||||
KASSERT(tdvp->v_mount == mp);
|
||||
KASSERT((tvp == NULL) || (tvp->v_mount == mp));
|
||||
KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
|
||||
KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
|
||||
KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
|
||||
KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
|
||||
|
||||
/*
|
||||
* Owing to VFS oddities we are currently called with tdvp/tvp
|
||||
* locked and not fdvp/fvp. In a sane world we'd be passed
|
||||
* tdvp and fdvp only, unlocked, and two name strings. Pretend
|
||||
* we have a sane world and unlock tdvp and tvp.
|
||||
*/
|
||||
VOP_UNLOCK(tdvp);
|
||||
if (tvp && tvp != tdvp) {
|
||||
VOP_UNLOCK(tvp);
|
||||
}
|
||||
error = SET_DIROP_REMOVE(tdvp, tvp);
|
||||
if (error != 0)
|
||||
return error;
|
||||
|
||||
/* Also pretend we have a sane world and vrele fvp/tvp. */
|
||||
vrele(fvp);
|
||||
fvp = NULL;
|
||||
if (tvp) {
|
||||
vrele(tvp);
|
||||
tvp = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for cross-device rename.
|
||||
*/
|
||||
if (fdvp->v_mount != tdvp->v_mount) {
|
||||
error = EXDEV;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reject "." and ".."
|
||||
*/
|
||||
if ((fcnp->cn_flags & ISDOTDOT) || (tcnp->cn_flags & ISDOTDOT) ||
|
||||
(fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
|
||||
(tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.')) {
|
||||
error = EINVAL;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get locks.
|
||||
*/
|
||||
|
||||
/* paranoia */
|
||||
fcnp->cn_flags |= LOCKPARENT|LOCKLEAF;
|
||||
tcnp->cn_flags |= LOCKPARENT|LOCKLEAF;
|
||||
|
||||
if (fdvp == tdvp) {
|
||||
/* One directory. Lock it and relookup both children. */
|
||||
vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY);
|
||||
|
||||
if (VTOI(fdvp)->i_size == 0) {
|
||||
/* directory has been rmdir'd */
|
||||
VOP_UNLOCK(fdvp);
|
||||
error = ENOENT;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
error = do_relookup(fdvp, &from_ulr, &fvp, fcnp);
|
||||
if (error == 0 && fvp == NULL) {
|
||||
/* relookup may produce this if fvp disappears */
|
||||
error = ENOENT;
|
||||
}
|
||||
if (error) {
|
||||
VOP_UNLOCK(fdvp);
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/*
|
||||
* The right way to do this is to look up both children
|
||||
* without locking either, and then lock both unless they
|
||||
* turn out to be the same. However, due to deep-seated
|
||||
* VFS-level issues all lookups lock the child regardless
|
||||
* of whether LOCKLEAF is set (if LOCKLEAF is not set,
|
||||
* the child is locked during lookup and then unlocked)
|
||||
* so it is not safe to look up tvp while fvp is locked.
|
||||
*
|
||||
* Unlocking fvp here temporarily is more or less safe,
|
||||
* because with the directory locked there's not much
|
||||
* that can happen to it. However, ideally it wouldn't
|
||||
* be necessary. XXX.
|
||||
*/
|
||||
VOP_UNLOCK(fvp);
|
||||
/* remember fdvp == tdvp so tdvp is locked */
|
||||
error = do_relookup(tdvp, &to_ulr, &tvp, tcnp);
|
||||
if (error && error != ENOENT) {
|
||||
VOP_UNLOCK(fdvp);
|
||||
goto abort;
|
||||
}
|
||||
if (error == ENOENT) {
|
||||
/*
|
||||
* Note: currently if the name doesn't exist,
|
||||
* relookup succeeds (it intercepts the
|
||||
* EJUSTRETURN from VOP_LOOKUP) and sets tvp
|
||||
* to NULL. Therefore, we will never get
|
||||
* ENOENT and this branch is not needed.
|
||||
* However, in a saner future the EJUSTRETURN
|
||||
* garbage will go away, so let's DTRT.
|
||||
*/
|
||||
tvp = NULL;
|
||||
}
|
||||
|
||||
/* tvp is locked; lock fvp if necessary */
|
||||
if (!tvp || tvp != fvp) {
|
||||
vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY);
|
||||
}
|
||||
} else {
|
||||
int found_fdvp;
|
||||
struct vnode *illegal_fvp;
|
||||
|
||||
/*
|
||||
* The source must not be above the destination. (If
|
||||
* it were, the rename would detach a section of the
|
||||
* tree.)
|
||||
*
|
||||
* Look up the tree from tdvp to see if we find fdvp,
|
||||
* and if so, return the immediate child of fdvp we're
|
||||
* under; that must not turn out to be the same as
|
||||
* fvp.
|
||||
*
|
||||
* The per-volume rename lock guarantees that the
|
||||
* result of this check remains true until we finish
|
||||
* looking up and locking.
|
||||
*/
|
||||
error = ufs_parentcheck(fdvp, tdvp, fcnp->cn_cred,
|
||||
&found_fdvp, &illegal_fvp);
|
||||
if (error) {
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Must lock in tree order. */
|
||||
|
||||
if (found_fdvp) {
|
||||
/* fdvp -> fvp -> tdvp -> tvp */
|
||||
error = lock_vnode_sequence(fdvp, &from_ulr,
|
||||
&fvp, fcnp, 0,
|
||||
EINVAL,
|
||||
tdvp, &to_ulr,
|
||||
&tvp, tcnp, 1);
|
||||
} else {
|
||||
/* tdvp -> tvp -> fdvp -> fvp */
|
||||
error = lock_vnode_sequence(tdvp, &to_ulr,
|
||||
&tvp, tcnp, 1,
|
||||
ENOTEMPTY,
|
||||
fdvp, &from_ulr,
|
||||
&fvp, fcnp, 0);
|
||||
}
|
||||
if (error) {
|
||||
if (illegal_fvp) {
|
||||
vrele(illegal_fvp);
|
||||
}
|
||||
goto abort;
|
||||
}
|
||||
KASSERT(fvp != NULL);
|
||||
|
||||
if (illegal_fvp && fvp == illegal_fvp) {
|
||||
vrele(illegal_fvp);
|
||||
error = EINVAL;
|
||||
goto abort_withlocks;
|
||||
}
|
||||
|
||||
if (illegal_fvp) {
|
||||
vrele(illegal_fvp);
|
||||
}
|
||||
}
|
||||
|
||||
KASSERT(fdvp && VOP_ISLOCKED(fdvp));
|
||||
KASSERT(fvp && VOP_ISLOCKED(fvp));
|
||||
KASSERT(tdvp && VOP_ISLOCKED(tdvp));
|
||||
KASSERT(tvp == NULL || VOP_ISLOCKED(tvp));
|
||||
|
||||
/* --- everything is now locked --- */
|
||||
|
||||
if (tvp && ((VTOI(tvp)->i_flags & (IMMUTABLE | APPEND)) ||
|
||||
(VTOI(tdvp)->i_flags & APPEND))) {
|
||||
error = EPERM;
|
||||
goto abort_withlocks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if just deleting a link name.
|
||||
*/
|
||||
if (fvp == tvp) {
|
||||
if (fvp->v_type == VDIR) {
|
||||
error = EINVAL;
|
||||
goto abort_withlocks;
|
||||
}
|
||||
|
||||
/* Release destination completely. Leave fdvp locked. */
|
||||
VOP_ABORTOP(tdvp, tcnp);
|
||||
if (fdvp != tdvp) {
|
||||
VOP_UNLOCK(tdvp);
|
||||
}
|
||||
VOP_UNLOCK(tvp);
|
||||
vrele(tdvp);
|
||||
vrele(tvp);
|
||||
|
||||
/* Delete source. */
|
||||
/* XXX: do we really need to relookup again? */
|
||||
|
||||
/*
|
||||
* fdvp is still locked, but we just unlocked fvp
|
||||
* (because fvp == tvp) so just decref fvp
|
||||
*/
|
||||
vrele(fvp);
|
||||
fcnp->cn_flags &= ~(MODMASK);
|
||||
fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
|
||||
fcnp->cn_nameiop = DELETE;
|
||||
if ((error = relookup(fdvp, &fvp, fcnp, 0))) {
|
||||
vput(fdvp);
|
||||
return (error);
|
||||
}
|
||||
return (VOP_REMOVE(fdvp, fvp, fcnp));
|
||||
}
|
||||
|
||||
/* The tiny bit of actual LFS code in this function */
|
||||
if ((error = SET_DIROP_REMOVE(tdvp, tvp)) != 0)
|
||||
goto abort_withlocks;
|
||||
MARK_VNODE(fdvp);
|
||||
MARK_VNODE(fvp);
|
||||
marked = 1;
|
||||
|
||||
fdp = VTOI(fdvp);
|
||||
ip = VTOI(fvp);
|
||||
if ((nlink_t) ip->i_nlink >= LINK_MAX) {
|
||||
error = EMLINK;
|
||||
goto abort_withlocks;
|
||||
}
|
||||
if ((ip->i_flags & (IMMUTABLE | APPEND)) ||
|
||||
(fdp->i_flags & APPEND)) {
|
||||
error = EPERM;
|
||||
goto abort_withlocks;
|
||||
}
|
||||
if ((ip->i_mode & IFMT) == IFDIR) {
|
||||
/*
|
||||
* Avoid ".", "..", and aliases of "." for obvious reasons.
|
||||
*/
|
||||
if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
|
||||
fdp == ip ||
|
||||
(fcnp->cn_flags & ISDOTDOT) ||
|
||||
(tcnp->cn_flags & ISDOTDOT) ||
|
||||
(ip->i_flag & IN_RENAME)) {
|
||||
error = EINVAL;
|
||||
goto abort_withlocks;
|
||||
}
|
||||
ip->i_flag |= IN_RENAME;
|
||||
doingdirectory = 1;
|
||||
}
|
||||
oldparent = fdp->i_number;
|
||||
VN_KNOTE(fdvp, NOTE_WRITE); /* XXXLUKEM/XXX: right place? */
|
||||
error = ufs_gro_rename(mp, cred,
|
||||
fdvp, fcnp, fde, fvp,
|
||||
tdvp, tcnp, tde, tvp);
|
||||
|
||||
/*
|
||||
* Both the directory
|
||||
* and target vnodes are locked.
|
||||
*/
|
||||
tdp = VTOI(tdvp);
|
||||
txp = NULL;
|
||||
if (tvp)
|
||||
txp = VTOI(tvp);
|
||||
|
||||
mp = fdvp->v_mount;
|
||||
fstrans_start(mp, FSTRANS_SHARED);
|
||||
|
||||
if (oldparent != tdp->i_number)
|
||||
newparent = tdp->i_number;
|
||||
|
||||
/*
|
||||
* If ".." must be changed (ie the directory gets a new
|
||||
* parent) the user must have write permission in the source
|
||||
* so as to be able to change "..".
|
||||
*/
|
||||
if (doingdirectory && newparent) {
|
||||
error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
KASSERT(fdvp != tvp);
|
||||
|
||||
if (newparent) {
|
||||
/* Check for the rename("foo/foo", "foo") case. */
|
||||
if (fdvp == tvp) {
|
||||
error = doingdirectory ? ENOTEMPTY : EISDIR;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
fxp = VTOI(fvp);
|
||||
fdp = VTOI(fdvp);
|
||||
|
||||
error = UFS_WAPBL_BEGIN(fdvp->v_mount);
|
||||
if (error)
|
||||
goto out2;
|
||||
|
||||
/*
|
||||
* 1) Bump link count while we're moving stuff
|
||||
* around. If we crash somewhere before
|
||||
* completing our work, the link count
|
||||
* may be wrong, but correctable.
|
||||
*/
|
||||
ip->i_nlink++;
|
||||
DIP_ASSIGN(ip, nlink, ip->i_nlink);
|
||||
ip->i_flag |= IN_CHANGE;
|
||||
if ((error = UFS_UPDATE(fvp, NULL, NULL, UPDATE_DIROP)) != 0) {
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2) If target doesn't exist, link the target
|
||||
* to the source and unlink the source.
|
||||
* Otherwise, rewrite the target directory
|
||||
* entry to reference the source inode and
|
||||
* expunge the original entry's existence.
|
||||
*/
|
||||
if (txp == NULL) {
|
||||
if (tdp->i_dev != ip->i_dev)
|
||||
panic("rename: EXDEV");
|
||||
/*
|
||||
* Account for ".." in new directory.
|
||||
* When source and destination have the same
|
||||
* parent we don't fool with the link count.
|
||||
*/
|
||||
if (doingdirectory && newparent) {
|
||||
if ((nlink_t)tdp->i_nlink >= LINK_MAX) {
|
||||
error = EMLINK;
|
||||
goto bad;
|
||||
}
|
||||
tdp->i_nlink++;
|
||||
DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
|
||||
tdp->i_flag |= IN_CHANGE;
|
||||
if ((error = UFS_UPDATE(tdvp, NULL, NULL,
|
||||
UPDATE_DIROP)) != 0) {
|
||||
tdp->i_nlink--;
|
||||
DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
|
||||
tdp->i_flag |= IN_CHANGE;
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK);
|
||||
ufs_makedirentry(ip, tcnp, newdir);
|
||||
error = ufs_direnter(tdvp, &to_ulr,
|
||||
NULL, newdir, tcnp, NULL);
|
||||
pool_cache_put(ufs_direct_cache, newdir);
|
||||
if (error != 0) {
|
||||
if (doingdirectory && newparent) {
|
||||
tdp->i_nlink--;
|
||||
DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
|
||||
tdp->i_flag |= IN_CHANGE;
|
||||
(void)UFS_UPDATE(tdvp, NULL, NULL,
|
||||
UPDATE_WAIT | UPDATE_DIROP);
|
||||
}
|
||||
goto bad;
|
||||
}
|
||||
VN_KNOTE(tdvp, NOTE_WRITE);
|
||||
} else {
|
||||
if (txp->i_dev != tdp->i_dev || txp->i_dev != ip->i_dev)
|
||||
panic("rename: EXDEV");
|
||||
/*
|
||||
* Short circuit rename(foo, foo).
|
||||
*/
|
||||
if (txp->i_number == ip->i_number)
|
||||
panic("rename: same file");
|
||||
/*
|
||||
* 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 (tdp->i_mode & S_ISTXT) {
|
||||
error = kauth_authorize_vnode(tcnp->cn_cred,
|
||||
KAUTH_VNODE_DELETE, tvp, tdvp,
|
||||
genfs_can_sticky(tcnp->cn_cred, tdp->i_uid,
|
||||
txp->i_uid));
|
||||
if (error)
|
||||
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 ((txp->i_mode & IFMT) == IFDIR) {
|
||||
if (txp->i_nlink > 2 ||
|
||||
!ufs_dirempty(txp, tdp->i_number, tcnp->cn_cred)) {
|
||||
error = ENOTEMPTY;
|
||||
goto bad;
|
||||
}
|
||||
if (!doingdirectory) {
|
||||
error = ENOTDIR;
|
||||
goto bad;
|
||||
}
|
||||
cache_purge(tdvp);
|
||||
} else if (doingdirectory) {
|
||||
error = EISDIR;
|
||||
goto bad;
|
||||
}
|
||||
if ((error = ufs_dirrewrite(tdp, to_ulr.ulr_offset,
|
||||
txp, ip->i_number,
|
||||
IFTODT(ip->i_mode), doingdirectory && newparent ?
|
||||
newparent : doingdirectory, IN_CHANGE | IN_UPDATE)) != 0)
|
||||
goto bad;
|
||||
if (doingdirectory) {
|
||||
/*
|
||||
* Truncate inode. The only stuff left in the directory
|
||||
* is "." and "..". The "." reference is inconsequential
|
||||
* since we are quashing it. We have removed the "."
|
||||
* reference and the reference in the parent directory,
|
||||
* but there may be other hard links.
|
||||
*/
|
||||
if (!newparent) {
|
||||
tdp->i_nlink--;
|
||||
DIP_ASSIGN(tdp, nlink, tdp->i_nlink);
|
||||
tdp->i_flag |= IN_CHANGE;
|
||||
UFS_WAPBL_UPDATE(tdvp, NULL, NULL, 0);
|
||||
}
|
||||
txp->i_nlink--;
|
||||
DIP_ASSIGN(txp, nlink, txp->i_nlink);
|
||||
txp->i_flag |= IN_CHANGE;
|
||||
if ((error = UFS_TRUNCATE(tvp, (off_t)0, IO_SYNC,
|
||||
tcnp->cn_cred)))
|
||||
goto bad;
|
||||
}
|
||||
VN_KNOTE(tdvp, NOTE_WRITE);
|
||||
VN_KNOTE(tvp, NOTE_DELETE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle case where the directory entry we need to remove,
|
||||
* which is/was at from_ulr.ulr_offset, or the one before it,
|
||||
* which is/was at from_ulr.ulr_offset - from_ulr.ulr_count,
|
||||
* may have been moved when the directory insertion above
|
||||
* performed compaction.
|
||||
*/
|
||||
if (tdp->i_number == fdp->i_number &&
|
||||
ulr_overlap(&from_ulr, &to_ulr)) {
|
||||
|
||||
struct buf *bp;
|
||||
struct direct *ep;
|
||||
struct ufsmount *ump = fdp->i_ump;
|
||||
doff_t curpos;
|
||||
doff_t endsearch; /* offset to end directory search */
|
||||
uint32_t prev_reclen;
|
||||
int dirblksiz = ump->um_dirblksiz;
|
||||
const int needswap = UFS_MPNEEDSWAP(ump);
|
||||
u_long bmask;
|
||||
int namlen, entryoffsetinblock;
|
||||
char *dirbuf;
|
||||
|
||||
bmask = fdvp->v_mount->mnt_stat.f_iosize - 1;
|
||||
|
||||
/*
|
||||
* The fcnp entry will be somewhere between the start of
|
||||
* compaction (to_ulr.ulr_offset) and the original location
|
||||
* (from_ulr.ulr_offset).
|
||||
*/
|
||||
curpos = to_ulr.ulr_offset;
|
||||
endsearch = from_ulr.ulr_offset + from_ulr.ulr_reclen;
|
||||
entryoffsetinblock = 0;
|
||||
|
||||
/*
|
||||
* Get the directory block containing the start of
|
||||
* compaction.
|
||||
*/
|
||||
error = ufs_blkatoff(fdvp, (off_t)to_ulr.ulr_offset, &dirbuf,
|
||||
&bp, false);
|
||||
if (error)
|
||||
goto bad;
|
||||
|
||||
/*
|
||||
* Keep existing ulr_count (length of previous record)
|
||||
* for the case where compaction did not include the
|
||||
* previous entry but started at the from-entry.
|
||||
*/
|
||||
prev_reclen = from_ulr.ulr_count;
|
||||
|
||||
while (curpos < endsearch) {
|
||||
uint32_t reclen;
|
||||
|
||||
/*
|
||||
* If necessary, get the next directory block.
|
||||
*
|
||||
* dholland 7/13/11 to the best of my understanding
|
||||
* this should never happen; compaction occurs only
|
||||
* within single blocks. I think.
|
||||
*/
|
||||
if ((curpos & bmask) == 0) {
|
||||
if (bp != NULL)
|
||||
brelse(bp, 0);
|
||||
error = ufs_blkatoff(fdvp, (off_t)curpos,
|
||||
&dirbuf, &bp, false);
|
||||
if (error)
|
||||
goto bad;
|
||||
entryoffsetinblock = 0;
|
||||
}
|
||||
|
||||
KASSERT(bp != NULL);
|
||||
ep = (struct direct *)(dirbuf + entryoffsetinblock);
|
||||
reclen = ufs_rw16(ep->d_reclen, needswap);
|
||||
|
||||
#if (BYTE_ORDER == LITTLE_ENDIAN)
|
||||
if (FSFMT(fdvp) && needswap == 0)
|
||||
namlen = ep->d_type;
|
||||
else
|
||||
namlen = ep->d_namlen;
|
||||
#else
|
||||
if (FSFMT(fdvp) && needswap != 0)
|
||||
namlen = ep->d_type;
|
||||
else
|
||||
namlen = ep->d_namlen;
|
||||
#endif
|
||||
if ((ep->d_ino != 0) &&
|
||||
(ufs_rw32(ep->d_ino, needswap) != WINO) &&
|
||||
(namlen == fcnp->cn_namelen) &&
|
||||
memcmp(ep->d_name, fcnp->cn_nameptr, namlen) == 0) {
|
||||
from_ulr.ulr_reclen = reclen;
|
||||
break;
|
||||
}
|
||||
curpos += reclen;
|
||||
entryoffsetinblock += reclen;
|
||||
prev_reclen = reclen;
|
||||
}
|
||||
|
||||
from_ulr.ulr_offset = curpos;
|
||||
from_ulr.ulr_count = prev_reclen;
|
||||
|
||||
KASSERT(curpos <= endsearch);
|
||||
|
||||
/*
|
||||
* If ulr_offset points to start of a directory block,
|
||||
* clear ulr_count so ufs_dirremove() doesn't try to
|
||||
* merge free space over a directory block boundary.
|
||||
*/
|
||||
if ((from_ulr.ulr_offset & (dirblksiz - 1)) == 0)
|
||||
from_ulr.ulr_count = 0;
|
||||
|
||||
brelse(bp, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 3) Unlink the source.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Ensure that the directory entry still exists and has not
|
||||
* changed while the new name has been entered. If the source is
|
||||
* a file then the entry may have been unlinked or renamed. In
|
||||
* either case there is no further work to be done. If the source
|
||||
* is a directory then it cannot have been rmdir'ed; The IRENAME
|
||||
* flag ensures that it cannot be moved by another rename or removed
|
||||
* by a rmdir.
|
||||
*/
|
||||
#endif
|
||||
KASSERT(fxp == ip);
|
||||
|
||||
/*
|
||||
* If the source is a directory with a new parent, the link
|
||||
* count of the old parent directory must be decremented and
|
||||
* ".." set to point to the new parent.
|
||||
*/
|
||||
if (doingdirectory && newparent) {
|
||||
KASSERT(fdp != NULL);
|
||||
ufs_dirrewrite(fxp, mastertemplate.dot_reclen,
|
||||
fdp, newparent, DT_DIR, 0, IN_CHANGE);
|
||||
cache_purge(fdvp);
|
||||
}
|
||||
error = ufs_dirremove(fdvp, &from_ulr,
|
||||
fxp, fcnp->cn_flags, 0);
|
||||
fxp->i_flag &= ~IN_RENAME;
|
||||
|
||||
VN_KNOTE(fvp, NOTE_RENAME);
|
||||
goto done;
|
||||
|
||||
out:
|
||||
goto out2;
|
||||
|
||||
/* exit routines from steps 1 & 2 */
|
||||
bad:
|
||||
if (doingdirectory)
|
||||
ip->i_flag &= ~IN_RENAME;
|
||||
ip->i_nlink--;
|
||||
DIP_ASSIGN(ip, nlink, ip->i_nlink);
|
||||
ip->i_flag |= IN_CHANGE;
|
||||
ip->i_flag &= ~IN_RENAME;
|
||||
UFS_WAPBL_UPDATE(fvp, NULL, NULL, 0);
|
||||
done:
|
||||
UFS_WAPBL_END(fdvp->v_mount);
|
||||
out2:
|
||||
/*
|
||||
* clear IN_RENAME - some exit paths happen too early to go
|
||||
* through the cleanup done in the "bad" case above, so we
|
||||
* always do this mini-cleanup here.
|
||||
*/
|
||||
ip->i_flag &= ~IN_RENAME;
|
||||
|
||||
VOP_UNLOCK(fdvp);
|
||||
if (tdvp != fdvp) {
|
||||
VOP_UNLOCK(tdvp);
|
||||
}
|
||||
VOP_UNLOCK(fvp);
|
||||
if (tvp && tvp != fvp) {
|
||||
VOP_UNLOCK(tvp);
|
||||
}
|
||||
|
||||
vrele(fdvp);
|
||||
vrele(tdvp);
|
||||
vrele(fvp);
|
||||
if (tvp) {
|
||||
vrele(tvp);
|
||||
}
|
||||
|
||||
fstrans_done(mp);
|
||||
if (marked) {
|
||||
UNMARK_VNODE(fdvp);
|
||||
UNMARK_VNODE(fvp);
|
||||
SET_ENDOP_REMOVE(fs, tdvp, tvp, "rename");
|
||||
}
|
||||
return (error);
|
||||
SET_ENDOP_REMOVE(VFSTOUFS(mp)->um_lfs, tdvp, tvp, "rename");
|
||||
|
||||
abort_withlocks:
|
||||
VOP_UNLOCK(fdvp);
|
||||
if (tdvp != fdvp) {
|
||||
VOP_UNLOCK(tdvp);
|
||||
}
|
||||
VOP_UNLOCK(fvp);
|
||||
if (tvp && tvp != fvp) {
|
||||
VOP_UNLOCK(tvp);
|
||||
}
|
||||
|
||||
abort:
|
||||
VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
|
||||
VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
|
||||
vrele(tdvp);
|
||||
if (tvp) {
|
||||
vrele(tvp);
|
||||
}
|
||||
vrele(fdvp);
|
||||
if (fvp) {
|
||||
vrele(fvp);
|
||||
}
|
||||
if (marked) {
|
||||
UNMARK_VNODE(fdvp);
|
||||
UNMARK_VNODE(fvp);
|
||||
SET_ENDOP_REMOVE(fs, tdvp, tvp, "rename");
|
||||
}
|
||||
return (error);
|
||||
return error;
|
||||
}
|
||||
|
||||
static const struct genfs_rename_ops lfs_genfs_rename_ops = {
|
||||
.gro_directory_empty_p = ufs_gro_directory_empty_p,
|
||||
.gro_rename_check_possible = ufs_gro_rename_check_possible,
|
||||
.gro_rename_check_permitted = ufs_gro_rename_check_permitted,
|
||||
.gro_remove_check_possible = ufs_gro_remove_check_possible,
|
||||
.gro_remove_check_permitted = ufs_gro_remove_check_permitted,
|
||||
.gro_rename = lfs_gro_rename,
|
||||
.gro_remove = ufs_gro_remove,
|
||||
.gro_lookup = ufs_gro_lookup,
|
||||
.gro_genealogy = ufs_gro_genealogy,
|
||||
.gro_lock_directory = ufs_gro_lock_directory,
|
||||
};
|
||||
|
||||
/* XXX hack to avoid calling ITIMES in getattr */
|
||||
int
|
||||
lfs_getattr(void *v)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ufs_extern.h,v 1.71 2012/02/01 05:34:43 dholland Exp $ */
|
||||
/* $NetBSD: ufs_extern.h,v 1.72 2012/05/09 00:21:18 riastradh Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991, 1993, 1994
|
||||
@ -139,6 +139,29 @@ int ufs_parentcheck(struct vnode *, struct vnode *, kauth_cred_t,
|
||||
int *, struct vnode **);
|
||||
int ufs_blkatoff(struct vnode *, off_t, char **, struct buf **, bool);
|
||||
|
||||
/* ufs_rename.c -- for lfs */
|
||||
bool ufs_gro_directory_empty_p(struct mount *, kauth_cred_t,
|
||||
struct vnode *, struct vnode *);
|
||||
int ufs_gro_rename_check_possible(struct mount *,
|
||||
struct vnode *, struct vnode *, struct vnode *, struct vnode *);
|
||||
int ufs_gro_rename_check_permitted(struct mount *, kauth_cred_t,
|
||||
struct vnode *, struct vnode *, struct vnode *, struct vnode *);
|
||||
int ufs_gro_remove_check_possible(struct mount *,
|
||||
struct vnode *, struct vnode *);
|
||||
int ufs_gro_remove_check_permitted(struct mount *, kauth_cred_t,
|
||||
struct vnode *, struct vnode *);
|
||||
int ufs_gro_rename(struct mount *, kauth_cred_t,
|
||||
struct vnode *, struct componentname *, void *, struct vnode *,
|
||||
struct vnode *, struct componentname *, void *, struct vnode *);
|
||||
int ufs_gro_remove(struct mount *, kauth_cred_t,
|
||||
struct vnode *, struct componentname *, void *, struct vnode *);
|
||||
int ufs_gro_lookup(struct mount *, struct vnode *,
|
||||
struct componentname *, void *, struct vnode **);
|
||||
int ufs_gro_genealogy(struct mount *, kauth_cred_t,
|
||||
struct vnode *, struct vnode *, struct vnode **);
|
||||
int ufs_gro_lock_directory(struct mount *, struct vnode *);
|
||||
|
||||
|
||||
/* ufs_quota.c */
|
||||
/*
|
||||
* Flags to chkdq() and chkiq()
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ufs_lookup.c,v 1.114 2012/05/05 04:08:53 yamt Exp $ */
|
||||
/* $NetBSD: ufs_lookup.c,v 1.115 2012/05/09 00:21:18 riastradh Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989, 1993
|
||||
@ -37,7 +37,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ufs_lookup.c,v 1.114 2012/05/05 04:08:53 yamt Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: ufs_lookup.c,v 1.115 2012/05/09 00:21:18 riastradh Exp $");
|
||||
|
||||
#ifdef _KERNEL_OPT
|
||||
#include "opt_ffs.h"
|
||||
@ -76,8 +76,6 @@ int dirchk = 1;
|
||||
int dirchk = 0;
|
||||
#endif
|
||||
|
||||
#define FSFMT(vp) (((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0)
|
||||
|
||||
/*
|
||||
* Convert a component of a pathname into a pointer to a locked inode.
|
||||
* This is a very central and rather complicated routine.
|
||||
|
1080
sys/ufs/ufs/ufs_rename.c
Normal file
1080
sys/ufs/ufs/ufs_rename.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ufsmount.h,v 1.37 2011/11/24 15:51:32 ahoka Exp $ */
|
||||
/* $NetBSD: ufsmount.h,v 1.38 2012/05/09 00:21:18 riastradh Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
@ -190,6 +190,12 @@ struct ufs_ops {
|
||||
*/
|
||||
#define MNINDIR(ump) ((ump)->um_nindir)
|
||||
#define blkptrtodb(ump, b) ((b) << (ump)->um_bptrtodb)
|
||||
|
||||
/*
|
||||
* Predicate for byte-swapping support.
|
||||
*/
|
||||
#define FSFMT(vp) (((vp)->v_mount->mnt_iflag & IMNT_DTYPE) == 0)
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* !_UFS_UFS_UFSMOUNT_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user