mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 10:54:35 +03:00
zfs_rename: support RENAME_* flags
Implement support for Linux's RENAME_* flags (for renameat2). Aside from being quite useful for userspace (providing race-free ways to exchange paths and implement mv --no-clobber), they are used by overlayfs and are thus required in order to use overlayfs-on-ZFS. In order for us to represent the new renameat2(2) flags in the ZIL, we create two new transaction types for the two flags which need transactional-level support (RENAME_EXCHANGE and RENAME_WHITEOUT). RENAME_NOREPLACE does not need any ZIL support because we know that if the operation succeeded before creating the ZIL entry, there was no file to be clobbered and thus it can be treated as a regular TX_RENAME. Reviewed-by: Ryan Moeller <ryan@iXsystems.com> Reviewed-by: Alexander Motin <mav@FreeBSD.org> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Pavel Snajdr <snajpa@snajpa.net> Signed-off-by: Aleksa Sarai <cyphar@cyphar.com> Closes #12209 Closes #14070
This commit is contained in:
committed by
Brian Behlendorf
parent
e015d6cc0b
commit
dbf6108b4d
@@ -3420,7 +3420,7 @@ out:
|
||||
|
||||
int
|
||||
zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname,
|
||||
cred_t *cr, int flags, zuserns_t *mnt_ns)
|
||||
cred_t *cr, int flags, uint64_t rflags, vattr_t *wo_vap, zuserns_t *mnt_ns)
|
||||
{
|
||||
struct componentname scn, tcn;
|
||||
vnode_t *sdvp, *tdvp;
|
||||
@@ -3428,6 +3428,9 @@ zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname,
|
||||
int error;
|
||||
svp = tvp = NULL;
|
||||
|
||||
if (rflags != 0 || wo_vap != NULL)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
sdvp = ZTOV(sdzp);
|
||||
tdvp = ZTOV(tdzp);
|
||||
error = zfs_lookup_internal(sdzp, sname, &svp, &scn, DELETE);
|
||||
|
||||
@@ -1035,7 +1035,8 @@ zfs_link_destroy(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag,
|
||||
}
|
||||
|
||||
/* The only error is !zfs_dirempty() and we checked earlier. */
|
||||
ASSERT3U(zfs_drop_nlink_locked(zp, tx, &unlinked), ==, 0);
|
||||
error = zfs_drop_nlink_locked(zp, tx, &unlinked);
|
||||
ASSERT3U(error, ==, 0);
|
||||
mutex_exit(&zp->z_lock);
|
||||
} else {
|
||||
error = zfs_dropname(dl, zp, dzp, tx, flag);
|
||||
|
||||
@@ -2655,6 +2655,8 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
|
||||
* tnm - New entry name.
|
||||
* cr - credentials of caller.
|
||||
* flags - case flags
|
||||
* rflags - RENAME_* flags
|
||||
* wa_vap - attributes for RENAME_WHITEOUT (must be a char 0:0).
|
||||
* mnt_ns - user namespace of the mount
|
||||
*
|
||||
* RETURN: 0 on success, error code on failure.
|
||||
@@ -2664,7 +2666,7 @@ zfs_rename_lock(znode_t *szp, znode_t *tdzp, znode_t *sdzp, zfs_zlock_t **zlpp)
|
||||
*/
|
||||
int
|
||||
zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm,
|
||||
cred_t *cr, int flags, zuserns_t *mnt_ns)
|
||||
cred_t *cr, int flags, uint64_t rflags, vattr_t *wo_vap, zuserns_t *mnt_ns)
|
||||
{
|
||||
znode_t *szp, *tzp;
|
||||
zfsvfs_t *zfsvfs = ZTOZSB(sdzp);
|
||||
@@ -2676,10 +2678,33 @@ zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp, char *tnm,
|
||||
int error = 0;
|
||||
int zflg = 0;
|
||||
boolean_t waited = B_FALSE;
|
||||
/* Needed for whiteout inode creation. */
|
||||
boolean_t fuid_dirtied;
|
||||
zfs_acl_ids_t acl_ids;
|
||||
boolean_t have_acl = B_FALSE;
|
||||
znode_t *wzp = NULL;
|
||||
|
||||
|
||||
if (snm == NULL || tnm == NULL)
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
if (rflags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
/* Already checked by Linux VFS, but just to make sure. */
|
||||
if (rflags & RENAME_EXCHANGE &&
|
||||
(rflags & (RENAME_NOREPLACE | RENAME_WHITEOUT)))
|
||||
return (SET_ERROR(EINVAL));
|
||||
|
||||
/*
|
||||
* Make sure we only get wo_vap iff. RENAME_WHITEOUT and that it's the
|
||||
* right kind of vattr_t for the whiteout file. These are set
|
||||
* internally by ZFS so should never be incorrect.
|
||||
*/
|
||||
VERIFY_EQUIV(rflags & RENAME_WHITEOUT, wo_vap != NULL);
|
||||
VERIFY_IMPLY(wo_vap, wo_vap->va_mode == S_IFCHR);
|
||||
VERIFY_IMPLY(wo_vap, wo_vap->va_rdev == makedevice(0, 0));
|
||||
|
||||
if ((error = zfs_enter_verify_zp(zfsvfs, sdzp, FTAG)) != 0)
|
||||
return (error);
|
||||
zilog = zfsvfs->z_log;
|
||||
@@ -2856,7 +2881,6 @@ top:
|
||||
* Note that if target and source are the same, this can be
|
||||
* done in a single check.
|
||||
*/
|
||||
|
||||
if ((error = zfs_zaccess_rename(sdzp, szp, tdzp, tzp, cr, mnt_ns)))
|
||||
goto out;
|
||||
|
||||
@@ -2873,16 +2897,22 @@ top:
|
||||
* Does target exist?
|
||||
*/
|
||||
if (tzp) {
|
||||
/*
|
||||
* Source and target must be the same type.
|
||||
*/
|
||||
boolean_t s_is_dir = S_ISDIR(ZTOI(szp)->i_mode) != 0;
|
||||
boolean_t t_is_dir = S_ISDIR(ZTOI(tzp)->i_mode) != 0;
|
||||
|
||||
if (s_is_dir != t_is_dir) {
|
||||
error = SET_ERROR(s_is_dir ? ENOTDIR : EISDIR);
|
||||
if (rflags & RENAME_NOREPLACE) {
|
||||
error = SET_ERROR(EEXIST);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Source and target must be the same type (unless exchanging).
|
||||
*/
|
||||
if (!(rflags & RENAME_EXCHANGE)) {
|
||||
boolean_t s_is_dir = S_ISDIR(ZTOI(szp)->i_mode) != 0;
|
||||
boolean_t t_is_dir = S_ISDIR(ZTOI(tzp)->i_mode) != 0;
|
||||
|
||||
if (s_is_dir != t_is_dir) {
|
||||
error = SET_ERROR(s_is_dir ? ENOTDIR : EISDIR);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* POSIX dictates that when the source and target
|
||||
* entries refer to the same file object, rename
|
||||
@@ -2892,12 +2922,43 @@ top:
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
} else if (rflags & RENAME_EXCHANGE) {
|
||||
/* Target must exist for RENAME_EXCHANGE. */
|
||||
error = SET_ERROR(ENOENT);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set up inode creation for RENAME_WHITEOUT. */
|
||||
if (rflags & RENAME_WHITEOUT) {
|
||||
/*
|
||||
* Whiteout files are not regular files or directories, so to
|
||||
* match zfs_create() we do not inherit the project id.
|
||||
*/
|
||||
uint64_t wo_projid = ZFS_DEFAULT_PROJID;
|
||||
|
||||
error = zfs_zaccess(sdzp, ACE_ADD_FILE, 0, B_FALSE, cr, mnt_ns);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (!have_acl) {
|
||||
error = zfs_acl_ids_create(sdzp, 0, wo_vap, cr, NULL,
|
||||
&acl_ids, mnt_ns);
|
||||
if (error)
|
||||
goto out;
|
||||
have_acl = B_TRUE;
|
||||
}
|
||||
|
||||
if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, wo_projid)) {
|
||||
error = SET_ERROR(EDQUOT);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
tx = dmu_tx_create(zfsvfs->z_os);
|
||||
dmu_tx_hold_sa(tx, szp->z_sa_hdl, B_FALSE);
|
||||
dmu_tx_hold_sa(tx, sdzp->z_sa_hdl, B_FALSE);
|
||||
dmu_tx_hold_zap(tx, sdzp->z_id, FALSE, snm);
|
||||
dmu_tx_hold_zap(tx, sdzp->z_id,
|
||||
(rflags & RENAME_EXCHANGE) ? TRUE : FALSE, snm);
|
||||
dmu_tx_hold_zap(tx, tdzp->z_id, TRUE, tnm);
|
||||
if (sdzp != tdzp) {
|
||||
dmu_tx_hold_sa(tx, tdzp->z_sa_hdl, B_FALSE);
|
||||
@@ -2907,7 +2968,21 @@ top:
|
||||
dmu_tx_hold_sa(tx, tzp->z_sa_hdl, B_FALSE);
|
||||
zfs_sa_upgrade_txholds(tx, tzp);
|
||||
}
|
||||
if (rflags & RENAME_WHITEOUT) {
|
||||
dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes +
|
||||
ZFS_SA_BASE_ATTR_SIZE);
|
||||
|
||||
dmu_tx_hold_zap(tx, sdzp->z_id, TRUE, snm);
|
||||
dmu_tx_hold_sa(tx, sdzp->z_sa_hdl, B_FALSE);
|
||||
if (!zfsvfs->z_use_sa &&
|
||||
acl_ids.z_aclp->z_acl_bytes > ZFS_ACE_SPACE) {
|
||||
dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
|
||||
0, acl_ids.z_aclp->z_acl_bytes);
|
||||
}
|
||||
}
|
||||
fuid_dirtied = zfsvfs->z_fuid_dirty;
|
||||
if (fuid_dirtied)
|
||||
zfs_fuid_txhold(zfsvfs, tx);
|
||||
zfs_sa_upgrade_txholds(tx, szp);
|
||||
dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL);
|
||||
error = dmu_tx_assign(tx, (waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT);
|
||||
@@ -2946,7 +3021,7 @@ top:
|
||||
|
||||
error = sa_update(szp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs),
|
||||
(void *)&szp->z_pflags, sizeof (uint64_t), tx);
|
||||
ASSERT0(error);
|
||||
VERIFY0(error);
|
||||
|
||||
error = zfs_link_destroy(sdl, szp, tx, ZRENAMING, NULL);
|
||||
if (error)
|
||||
@@ -2956,13 +3031,30 @@ top:
|
||||
* Unlink the target.
|
||||
*/
|
||||
if (tzp) {
|
||||
error = zfs_link_destroy(tdl, tzp, tx, zflg, NULL);
|
||||
int tzflg = zflg;
|
||||
|
||||
if (rflags & RENAME_EXCHANGE) {
|
||||
/* This inode will be re-linked soon. */
|
||||
tzflg |= ZRENAMING;
|
||||
|
||||
tzp->z_pflags |= ZFS_AV_MODIFIED;
|
||||
if (sdzp->z_pflags & ZFS_PROJINHERIT)
|
||||
tzp->z_pflags |= ZFS_PROJINHERIT;
|
||||
|
||||
error = sa_update(tzp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs),
|
||||
(void *)&tzp->z_pflags, sizeof (uint64_t), tx);
|
||||
ASSERT0(error);
|
||||
}
|
||||
error = zfs_link_destroy(tdl, tzp, tx, tzflg, NULL);
|
||||
if (error)
|
||||
goto commit_link_szp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new link at the target.
|
||||
* Create the new target links:
|
||||
* * We always link the target.
|
||||
* * RENAME_EXCHANGE: Link the old target to the source.
|
||||
* * RENAME_WHITEOUT: Create a whiteout inode in-place of the source.
|
||||
*/
|
||||
error = zfs_link_create(tdl, szp, tx, ZRENAMING);
|
||||
if (error) {
|
||||
@@ -2975,18 +3067,55 @@ top:
|
||||
goto commit_link_tzp;
|
||||
}
|
||||
|
||||
zfs_log_rename(zilog, tx, TX_RENAME |
|
||||
(flags & FIGNORECASE ? TX_CI : 0), sdzp,
|
||||
sdl->dl_name, tdzp, tdl->dl_name, szp);
|
||||
switch (rflags & (RENAME_EXCHANGE | RENAME_WHITEOUT)) {
|
||||
case RENAME_EXCHANGE:
|
||||
error = zfs_link_create(sdl, tzp, tx, ZRENAMING);
|
||||
/*
|
||||
* The same argument as zfs_link_create() failing for
|
||||
* szp applies here, since the source directory must
|
||||
* have had an entry we are replacing.
|
||||
*/
|
||||
ASSERT0(error);
|
||||
if (error)
|
||||
goto commit_unlink_td_szp;
|
||||
break;
|
||||
case RENAME_WHITEOUT:
|
||||
zfs_mknode(sdzp, wo_vap, tx, cr, 0, &wzp, &acl_ids);
|
||||
error = zfs_link_create(sdl, wzp, tx, ZNEW);
|
||||
if (error) {
|
||||
zfs_znode_delete(wzp, tx);
|
||||
remove_inode_hash(ZTOI(wzp));
|
||||
goto commit_unlink_td_szp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (fuid_dirtied)
|
||||
zfs_fuid_sync(zfsvfs, tx);
|
||||
|
||||
switch (rflags & (RENAME_EXCHANGE | RENAME_WHITEOUT)) {
|
||||
case RENAME_EXCHANGE:
|
||||
zfs_log_rename_exchange(zilog, tx,
|
||||
(flags & FIGNORECASE ? TX_CI : 0), sdzp, sdl->dl_name,
|
||||
tdzp, tdl->dl_name, szp);
|
||||
break;
|
||||
case RENAME_WHITEOUT:
|
||||
zfs_log_rename_whiteout(zilog, tx,
|
||||
(flags & FIGNORECASE ? TX_CI : 0), sdzp, sdl->dl_name,
|
||||
tdzp, tdl->dl_name, szp, wzp);
|
||||
break;
|
||||
default:
|
||||
ASSERT0(rflags & ~RENAME_NOREPLACE);
|
||||
zfs_log_rename(zilog, tx, (flags & FIGNORECASE ? TX_CI : 0),
|
||||
sdzp, sdl->dl_name, tdzp, tdl->dl_name, szp);
|
||||
break;
|
||||
}
|
||||
|
||||
commit:
|
||||
dmu_tx_commit(tx);
|
||||
out:
|
||||
if (zl != NULL)
|
||||
zfs_rename_unlock(&zl);
|
||||
|
||||
zfs_dirent_unlock(sdl);
|
||||
zfs_dirent_unlock(tdl);
|
||||
if (have_acl)
|
||||
zfs_acl_ids_free(&acl_ids);
|
||||
|
||||
zfs_znode_update_vfs(sdzp);
|
||||
if (sdzp == tdzp)
|
||||
@@ -2997,11 +3126,21 @@ out:
|
||||
|
||||
zfs_znode_update_vfs(szp);
|
||||
zrele(szp);
|
||||
if (wzp) {
|
||||
zfs_znode_update_vfs(wzp);
|
||||
zrele(wzp);
|
||||
}
|
||||
if (tzp) {
|
||||
zfs_znode_update_vfs(tzp);
|
||||
zrele(tzp);
|
||||
}
|
||||
|
||||
if (zl != NULL)
|
||||
zfs_rename_unlock(&zl);
|
||||
|
||||
zfs_dirent_unlock(sdl);
|
||||
zfs_dirent_unlock(tdl);
|
||||
|
||||
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
|
||||
zil_commit(zilog, 0);
|
||||
|
||||
@@ -3012,23 +3151,31 @@ out:
|
||||
* Clean-up path for broken link state.
|
||||
*
|
||||
* At this point we are in a (very) bad state, so we need to do our
|
||||
* best to correct the state. In particular, the nlink of szp is wrong
|
||||
* because we were destroying and creating links with ZRENAMING.
|
||||
* best to correct the state. In particular, all of the nlinks are
|
||||
* wrong because we were destroying and creating links with ZRENAMING.
|
||||
*
|
||||
* link_create()s are allowed to fail (though they shouldn't because we
|
||||
* only just unlinked them and are putting the entries back during
|
||||
* clean-up). But if they fail, we can just forcefully drop the nlink
|
||||
* value to (at the very least) avoid broken nlink values -- though in
|
||||
* the case of non-empty directories we will have to panic.
|
||||
* In some form, all of these operations have to resolve the state:
|
||||
*
|
||||
* * link_destroy() *must* succeed. Fortunately, this is very likely
|
||||
* since we only just created it.
|
||||
*
|
||||
* * link_create()s are allowed to fail (though they shouldn't because
|
||||
* we only just unlinked them and are putting the entries back
|
||||
* during clean-up). But if they fail, we can just forcefully drop
|
||||
* the nlink value to (at the very least) avoid broken nlink values
|
||||
* -- though in the case of non-empty directories we will have to
|
||||
* panic (otherwise we'd have a leaked directory with a broken ..).
|
||||
*/
|
||||
commit_unlink_td_szp:
|
||||
VERIFY0(zfs_link_destroy(tdl, szp, tx, ZRENAMING, NULL));
|
||||
commit_link_tzp:
|
||||
if (tzp) {
|
||||
if (zfs_link_create(tdl, tzp, tx, ZRENAMING))
|
||||
VERIFY3U(zfs_drop_nlink(tzp, tx, NULL), ==, 0);
|
||||
VERIFY0(zfs_drop_nlink(tzp, tx, NULL));
|
||||
}
|
||||
commit_link_szp:
|
||||
if (zfs_link_create(sdl, szp, tx, ZRENAMING))
|
||||
VERIFY3U(zfs_drop_nlink(szp, tx, NULL), ==, 0);
|
||||
VERIFY0(zfs_drop_nlink(szp, tx, NULL));
|
||||
goto commit;
|
||||
}
|
||||
|
||||
|
||||
@@ -422,7 +422,12 @@ zfs_inode_set_ops(zfsvfs_t *zfsvfs, struct inode *ip)
|
||||
break;
|
||||
|
||||
case S_IFDIR:
|
||||
#ifdef HAVE_RENAME2_OPERATIONS_WRAPPER
|
||||
ip->i_flags |= S_IOPS_WRAPPER;
|
||||
ip->i_op = &zpl_dir_inode_operations.ops;
|
||||
#else
|
||||
ip->i_op = &zpl_dir_inode_operations;
|
||||
#endif
|
||||
ip->i_fop = &zpl_dir_file_operations;
|
||||
ITOZ(ip)->z_zn_prefetch = B_TRUE;
|
||||
break;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/zfs_ctldir.h>
|
||||
#include <sys/zfs_vfsops.h>
|
||||
#include <sys/zfs_vnops.h>
|
||||
@@ -498,35 +499,42 @@ static int
|
||||
#ifdef HAVE_IOPS_RENAME_USERNS
|
||||
zpl_rename2(struct user_namespace *user_ns, struct inode *sdip,
|
||||
struct dentry *sdentry, struct inode *tdip, struct dentry *tdentry,
|
||||
unsigned int flags)
|
||||
unsigned int rflags)
|
||||
#else
|
||||
zpl_rename2(struct inode *sdip, struct dentry *sdentry,
|
||||
struct inode *tdip, struct dentry *tdentry, unsigned int flags)
|
||||
struct inode *tdip, struct dentry *tdentry, unsigned int rflags)
|
||||
#endif
|
||||
{
|
||||
cred_t *cr = CRED();
|
||||
vattr_t *wo_vap = NULL;
|
||||
int error;
|
||||
fstrans_cookie_t cookie;
|
||||
#ifndef HAVE_IOPS_RENAME_USERNS
|
||||
zuserns_t *user_ns = NULL;
|
||||
#endif
|
||||
|
||||
/* We don't have renameat2(2) support */
|
||||
if (flags)
|
||||
return (-EINVAL);
|
||||
|
||||
crhold(cr);
|
||||
if (rflags & RENAME_WHITEOUT) {
|
||||
wo_vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP);
|
||||
zpl_vap_init(wo_vap, sdip, S_IFCHR, cr, user_ns);
|
||||
wo_vap->va_rdev = makedevice(0, 0);
|
||||
}
|
||||
|
||||
cookie = spl_fstrans_mark();
|
||||
error = -zfs_rename(ITOZ(sdip), dname(sdentry), ITOZ(tdip),
|
||||
dname(tdentry), cr, 0, user_ns);
|
||||
dname(tdentry), cr, 0, rflags, wo_vap, user_ns);
|
||||
spl_fstrans_unmark(cookie);
|
||||
if (wo_vap)
|
||||
kmem_free(wo_vap, sizeof (vattr_t));
|
||||
crfree(cr);
|
||||
ASSERT3S(error, <=, 0);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
#if !defined(HAVE_RENAME_WANTS_FLAGS) && !defined(HAVE_IOPS_RENAME_USERNS)
|
||||
#if !defined(HAVE_IOPS_RENAME_USERNS) && \
|
||||
!defined(HAVE_RENAME_WANTS_FLAGS) && \
|
||||
!defined(HAVE_RENAME2)
|
||||
static int
|
||||
zpl_rename(struct inode *sdip, struct dentry *sdentry,
|
||||
struct inode *tdip, struct dentry *tdentry)
|
||||
@@ -745,7 +753,12 @@ const struct inode_operations zpl_inode_operations = {
|
||||
#endif /* CONFIG_FS_POSIX_ACL */
|
||||
};
|
||||
|
||||
#ifdef HAVE_RENAME2_OPERATIONS_WRAPPER
|
||||
const struct inode_operations_wrapper zpl_dir_inode_operations = {
|
||||
.ops = {
|
||||
#else
|
||||
const struct inode_operations zpl_dir_inode_operations = {
|
||||
#endif
|
||||
.create = zpl_create,
|
||||
.lookup = zpl_lookup,
|
||||
.link = zpl_link,
|
||||
@@ -754,7 +767,9 @@ const struct inode_operations zpl_dir_inode_operations = {
|
||||
.mkdir = zpl_mkdir,
|
||||
.rmdir = zpl_rmdir,
|
||||
.mknod = zpl_mknod,
|
||||
#if defined(HAVE_RENAME_WANTS_FLAGS) || defined(HAVE_IOPS_RENAME_USERNS)
|
||||
#ifdef HAVE_RENAME2
|
||||
.rename2 = zpl_rename2,
|
||||
#elif defined(HAVE_RENAME_WANTS_FLAGS) || defined(HAVE_IOPS_RENAME_USERNS)
|
||||
.rename = zpl_rename2,
|
||||
#else
|
||||
.rename = zpl_rename,
|
||||
@@ -776,6 +791,10 @@ const struct inode_operations zpl_dir_inode_operations = {
|
||||
#endif /* HAVE_SET_ACL */
|
||||
.get_acl = zpl_get_acl,
|
||||
#endif /* CONFIG_FS_POSIX_ACL */
|
||||
#ifdef HAVE_RENAME2_OPERATIONS_WRAPPER
|
||||
},
|
||||
.rename2 = zpl_rename2,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct inode_operations zpl_symlink_inode_operations = {
|
||||
|
||||
+81
-5
@@ -494,11 +494,8 @@ zfs_log_symlink(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
|
||||
zil_itx_assign(zilog, itx, tx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles TX_RENAME transactions.
|
||||
*/
|
||||
void
|
||||
zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *sdzp,
|
||||
static void
|
||||
do_zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *sdzp,
|
||||
const char *sname, znode_t *tdzp, const char *dname, znode_t *szp)
|
||||
{
|
||||
itx_t *itx;
|
||||
@@ -520,6 +517,85 @@ zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *sdzp,
|
||||
zil_itx_assign(zilog, itx, tx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles TX_RENAME transactions.
|
||||
*/
|
||||
void
|
||||
zfs_log_rename(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype, znode_t *sdzp,
|
||||
const char *sname, znode_t *tdzp, const char *dname, znode_t *szp)
|
||||
{
|
||||
txtype |= TX_RENAME;
|
||||
do_zfs_log_rename(zilog, tx, txtype, sdzp, sname, tdzp, dname, szp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles TX_RENAME_EXCHANGE transactions.
|
||||
*/
|
||||
void
|
||||
zfs_log_rename_exchange(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
|
||||
znode_t *sdzp, const char *sname, znode_t *tdzp, const char *dname,
|
||||
znode_t *szp)
|
||||
{
|
||||
txtype |= TX_RENAME_EXCHANGE;
|
||||
do_zfs_log_rename(zilog, tx, txtype, sdzp, sname, tdzp, dname, szp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles TX_RENAME_WHITEOUT transactions.
|
||||
*
|
||||
* Unfortunately we cannot reuse do_zfs_log_rename because we we need to call
|
||||
* zfs_mknode() on replay which requires stashing bits as with TX_CREATE.
|
||||
*/
|
||||
void
|
||||
zfs_log_rename_whiteout(zilog_t *zilog, dmu_tx_t *tx, uint64_t txtype,
|
||||
znode_t *sdzp, const char *sname, znode_t *tdzp, const char *dname,
|
||||
znode_t *szp, znode_t *wzp)
|
||||
{
|
||||
itx_t *itx;
|
||||
lr_rename_whiteout_t *lr;
|
||||
size_t snamesize = strlen(sname) + 1;
|
||||
size_t dnamesize = strlen(dname) + 1;
|
||||
|
||||
if (zil_replaying(zilog, tx))
|
||||
return;
|
||||
|
||||
txtype |= TX_RENAME_WHITEOUT;
|
||||
itx = zil_itx_create(txtype, sizeof (*lr) + snamesize + dnamesize);
|
||||
lr = (lr_rename_whiteout_t *)&itx->itx_lr;
|
||||
lr->lr_rename.lr_sdoid = sdzp->z_id;
|
||||
lr->lr_rename.lr_tdoid = tdzp->z_id;
|
||||
|
||||
/*
|
||||
* RENAME_WHITEOUT will create an entry at the source znode, so we need
|
||||
* to store the same data that the equivalent call to zfs_log_create()
|
||||
* would.
|
||||
*/
|
||||
lr->lr_wfoid = wzp->z_id;
|
||||
LR_FOID_SET_SLOTS(lr->lr_wfoid, wzp->z_dnodesize >> DNODE_SHIFT);
|
||||
(void) sa_lookup(wzp->z_sa_hdl, SA_ZPL_GEN(ZTOZSB(wzp)), &lr->lr_wgen,
|
||||
sizeof (uint64_t));
|
||||
(void) sa_lookup(wzp->z_sa_hdl, SA_ZPL_CRTIME(ZTOZSB(wzp)),
|
||||
lr->lr_wcrtime, sizeof (uint64_t) * 2);
|
||||
lr->lr_wmode = wzp->z_mode;
|
||||
lr->lr_wuid = (uint64_t)KUID_TO_SUID(ZTOUID(wzp));
|
||||
lr->lr_wgid = (uint64_t)KGID_TO_SGID(ZTOGID(wzp));
|
||||
|
||||
/*
|
||||
* This rdev will always be makdevice(0, 0) but because the ZIL log and
|
||||
* replay code needs to be platform independent (and there is no
|
||||
* platform independent makdev()) we need to copy the one created
|
||||
* during the rename operation.
|
||||
*/
|
||||
(void) sa_lookup(wzp->z_sa_hdl, SA_ZPL_RDEV(ZTOZSB(wzp)), &lr->lr_wrdev,
|
||||
sizeof (lr->lr_wrdev));
|
||||
|
||||
memcpy((char *)(lr + 1), sname, snamesize);
|
||||
memcpy((char *)(lr + 1) + snamesize, dname, dnamesize);
|
||||
itx->itx_oid = szp->z_id;
|
||||
|
||||
zil_itx_assign(zilog, itx, tx);
|
||||
}
|
||||
|
||||
/*
|
||||
* zfs_log_write() handles TX_WRITE transactions. The specified callback is
|
||||
* called as soon as the write is on stable storage (be it via a DMU sync or a
|
||||
|
||||
+96
-10
@@ -643,18 +643,21 @@ zfs_replay_link(void *arg1, void *arg2, boolean_t byteswap)
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
|
||||
do_zfs_replay_rename(zfsvfs_t *zfsvfs, lr_rename_t *lr, char *sname,
|
||||
char *tname, uint64_t rflags, vattr_t *wo_vap)
|
||||
{
|
||||
zfsvfs_t *zfsvfs = arg1;
|
||||
lr_rename_t *lr = arg2;
|
||||
char *sname = (char *)(lr + 1); /* sname and tname follow lr_rename_t */
|
||||
char *tname = sname + strlen(sname) + 1;
|
||||
znode_t *sdzp, *tdzp;
|
||||
int error;
|
||||
int vflg = 0;
|
||||
int error, vflg = 0;
|
||||
|
||||
if (byteswap)
|
||||
byteswap_uint64_array(lr, sizeof (*lr));
|
||||
/* Only Linux currently supports RENAME_* flags. */
|
||||
#ifdef __linux__
|
||||
VERIFY0(rflags & ~(RENAME_EXCHANGE | RENAME_WHITEOUT));
|
||||
|
||||
/* wo_vap must be non-NULL iff. we're doing RENAME_WHITEOUT */
|
||||
VERIFY_EQUIV(rflags & RENAME_WHITEOUT, wo_vap != NULL);
|
||||
#else
|
||||
VERIFY0(rflags);
|
||||
#endif
|
||||
|
||||
if ((error = zfs_zget(zfsvfs, lr->lr_sdoid, &sdzp)) != 0)
|
||||
return (error);
|
||||
@@ -667,13 +670,94 @@ zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
|
||||
if (lr->lr_common.lrc_txtype & TX_CI)
|
||||
vflg |= FIGNORECASE;
|
||||
|
||||
error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, NULL);
|
||||
error = zfs_rename(sdzp, sname, tdzp, tname, kcred, vflg, rflags,
|
||||
wo_vap, NULL);
|
||||
|
||||
zrele(tdzp);
|
||||
zrele(sdzp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_replay_rename(void *arg1, void *arg2, boolean_t byteswap)
|
||||
{
|
||||
zfsvfs_t *zfsvfs = arg1;
|
||||
lr_rename_t *lr = arg2;
|
||||
char *sname = (char *)(lr + 1); /* sname and tname follow lr_rename_t */
|
||||
char *tname = sname + strlen(sname) + 1;
|
||||
|
||||
if (byteswap)
|
||||
byteswap_uint64_array(lr, sizeof (*lr));
|
||||
|
||||
return (do_zfs_replay_rename(zfsvfs, lr, sname, tname, 0, NULL));
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_replay_rename_exchange(void *arg1, void *arg2, boolean_t byteswap)
|
||||
{
|
||||
#ifdef __linux__
|
||||
zfsvfs_t *zfsvfs = arg1;
|
||||
lr_rename_t *lr = arg2;
|
||||
char *sname = (char *)(lr + 1); /* sname and tname follow lr_rename_t */
|
||||
char *tname = sname + strlen(sname) + 1;
|
||||
|
||||
if (byteswap)
|
||||
byteswap_uint64_array(lr, sizeof (*lr));
|
||||
|
||||
return (do_zfs_replay_rename(zfsvfs, lr, sname, tname, RENAME_EXCHANGE,
|
||||
NULL));
|
||||
#else
|
||||
return (SET_ERROR(ENOTSUP));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_replay_rename_whiteout(void *arg1, void *arg2, boolean_t byteswap)
|
||||
{
|
||||
#ifdef __linux__
|
||||
zfsvfs_t *zfsvfs = arg1;
|
||||
lr_rename_whiteout_t *lr = arg2;
|
||||
int error;
|
||||
/* sname and tname follow lr_rename_whiteout_t */
|
||||
char *sname = (char *)(lr + 1);
|
||||
char *tname = sname + strlen(sname) + 1;
|
||||
/* For the whiteout file. */
|
||||
xvattr_t xva;
|
||||
uint64_t objid;
|
||||
uint64_t dnodesize;
|
||||
|
||||
if (byteswap)
|
||||
byteswap_uint64_array(lr, sizeof (*lr));
|
||||
|
||||
objid = LR_FOID_GET_OBJ(lr->lr_wfoid);
|
||||
dnodesize = LR_FOID_GET_SLOTS(lr->lr_wfoid) << DNODE_SHIFT;
|
||||
|
||||
xva_init(&xva);
|
||||
zfs_init_vattr(&xva.xva_vattr, ATTR_MODE | ATTR_UID | ATTR_GID,
|
||||
lr->lr_wmode, lr->lr_wuid, lr->lr_wgid, lr->lr_wrdev, objid);
|
||||
|
||||
/*
|
||||
* As with TX_CREATE, RENAME_WHITEOUT ends up in zfs_mknode(), which
|
||||
* assigns the object's creation time, generation number, and dnode
|
||||
* slot count. The generic zfs_rename() has no concept of these
|
||||
* attributes, so we smuggle the values inside the vattr's otherwise
|
||||
* unused va_ctime, va_nblocks, and va_fsid fields.
|
||||
*/
|
||||
ZFS_TIME_DECODE(&xva.xva_vattr.va_ctime, lr->lr_wcrtime);
|
||||
xva.xva_vattr.va_nblocks = lr->lr_wgen;
|
||||
xva.xva_vattr.va_fsid = dnodesize;
|
||||
|
||||
error = dnode_try_claim(zfsvfs->z_os, objid, dnodesize >> DNODE_SHIFT);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
return (do_zfs_replay_rename(zfsvfs, &lr->lr_rename, sname, tname,
|
||||
RENAME_WHITEOUT, &xva.xva_vattr));
|
||||
#else
|
||||
return (SET_ERROR(ENOTSUP));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_replay_write(void *arg1, void *arg2, boolean_t byteswap)
|
||||
{
|
||||
@@ -1069,4 +1153,6 @@ zil_replay_func_t *const zfs_replay_vector[TX_MAX_TYPE] = {
|
||||
zfs_replay_create_acl, /* TX_MKDIR_ACL_ATTR */
|
||||
zfs_replay_write2, /* TX_WRITE2 */
|
||||
zfs_replay_setsaxattr, /* TX_SETSAXATTR */
|
||||
zfs_replay_rename_exchange, /* TX_RENAME_EXCHANGE */
|
||||
zfs_replay_rename_whiteout, /* TX_RENAME_WHITEOUT */
|
||||
};
|
||||
|
||||
+2
-4
@@ -759,11 +759,9 @@ zil_commit_activate_saxattr_feature(zilog_t *zilog)
|
||||
uint64_t txg = 0;
|
||||
dmu_tx_t *tx = NULL;
|
||||
|
||||
if (spa_feature_is_enabled(zilog->zl_spa,
|
||||
SPA_FEATURE_ZILSAXATTR) &&
|
||||
if (spa_feature_is_enabled(zilog->zl_spa, SPA_FEATURE_ZILSAXATTR) &&
|
||||
dmu_objset_type(zilog->zl_os) != DMU_OST_ZVOL &&
|
||||
!dsl_dataset_feature_is_active(ds,
|
||||
SPA_FEATURE_ZILSAXATTR)) {
|
||||
!dsl_dataset_feature_is_active(ds, SPA_FEATURE_ZILSAXATTR)) {
|
||||
tx = dmu_tx_create(zilog->zl_os);
|
||||
VERIFY0(dmu_tx_assign(tx, TXG_WAIT));
|
||||
dsl_dataset_dirty(ds, tx);
|
||||
|
||||
@@ -514,6 +514,8 @@ zil_replay_func_t *const zvol_replay_vector[TX_MAX_TYPE] = {
|
||||
zvol_replay_err, /* TX_MKDIR_ACL_ATTR */
|
||||
zvol_replay_err, /* TX_WRITE2 */
|
||||
zvol_replay_err, /* TX_SETSAXATTR */
|
||||
zvol_replay_err, /* TX_RENAME_EXCHANGE */
|
||||
zvol_replay_err, /* TX_RENAME_WHITEOUT */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user