It is possible to recursively destroy snapshots even if the snapshot
doesn't exist on a dataset we are starting from. For example if we have the following configuration: tank tank/foo tank/foo@snap tank/bar tank/bar@snap We can execute: # zfs destroy -t tank@snap eventhough tank@snap doesn't exit. Unfortunately it is not possible to do the same with recursive rename: # zfs rename -r tank@snap tank@pans cannot open 'tank@snap': dataset does not exist ...until now. This change allows to recursively rename snapshots even if snapshot doesn't exist on the starting dataset. Sponsored by: rsync.net MFC after: 2 weeks
This commit is contained in:
parent
c2257f93ba
commit
c622f88dd2
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=240870
@ -3081,6 +3081,7 @@ zfs_do_rename(int argc, char **argv)
|
||||
int ret = 0;
|
||||
int types;
|
||||
boolean_t parents = B_FALSE;
|
||||
char *snapshot = NULL;
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, "fpru")) != -1) {
|
||||
@ -3149,6 +3150,19 @@ zfs_do_rename(int argc, char **argv)
|
||||
else
|
||||
types = ZFS_TYPE_DATASET;
|
||||
|
||||
if (flags.recurse) {
|
||||
/*
|
||||
* When we do recursive rename we are fine when the given
|
||||
* snapshot for the given dataset doesn't exist - it can
|
||||
* still exists below.
|
||||
*/
|
||||
|
||||
snapshot = strchr(argv[0], '@');
|
||||
assert(snapshot != NULL);
|
||||
*snapshot = '\0';
|
||||
snapshot++;
|
||||
}
|
||||
|
||||
if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
|
||||
return (1);
|
||||
|
||||
@ -3159,7 +3173,7 @@ zfs_do_rename(int argc, char **argv)
|
||||
return (1);
|
||||
}
|
||||
|
||||
ret = (zfs_rename(zhp, argv[1], flags) != 0);
|
||||
ret = (zfs_rename(zhp, snapshot, argv[1], flags) != 0);
|
||||
|
||||
zfs_close(zhp);
|
||||
return (ret);
|
||||
|
@ -571,7 +571,8 @@ typedef struct renameflags {
|
||||
int forceunmount : 1;
|
||||
} renameflags_t;
|
||||
|
||||
extern int zfs_rename(zfs_handle_t *, const char *, renameflags_t flags);
|
||||
extern int zfs_rename(zfs_handle_t *, const char *, const char *,
|
||||
renameflags_t flags);
|
||||
|
||||
typedef struct sendflags {
|
||||
/* print informational messages (ie, -v was specified) */
|
||||
|
@ -611,6 +611,22 @@ zfs_open(libzfs_handle_t *hdl, const char *path, int types)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (zhp == NULL) {
|
||||
char *at = strchr(path, '@');
|
||||
|
||||
if (at != NULL)
|
||||
*at = '\0';
|
||||
errno = 0;
|
||||
if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
|
||||
(void) zfs_standard_error(hdl, errno, errbuf);
|
||||
return (NULL);
|
||||
}
|
||||
if (at != NULL)
|
||||
*at = '@';
|
||||
(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
|
||||
zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
|
||||
}
|
||||
|
||||
if (!(types & zhp->zfs_type)) {
|
||||
(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
|
||||
zfs_close(zhp);
|
||||
@ -3614,7 +3630,8 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
|
||||
* Renames the given dataset.
|
||||
*/
|
||||
int
|
||||
zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)
|
||||
zfs_rename(zfs_handle_t *zhp, const char *source, const char *target,
|
||||
renameflags_t flags)
|
||||
{
|
||||
int ret;
|
||||
zfs_cmd_t zc = { 0 };
|
||||
@ -3634,6 +3651,18 @@ zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot rename to '%s'"), target);
|
||||
|
||||
if (source != NULL) {
|
||||
/*
|
||||
* This is recursive snapshots rename, put snapshot name
|
||||
* (that might not exist) into zfs_name.
|
||||
*/
|
||||
assert(flags.recurse);
|
||||
|
||||
(void) strlcat(zhp->zfs_name, "@", sizeof(zhp->zfs_name));
|
||||
(void) strlcat(zhp->zfs_name, source, sizeof(zhp->zfs_name));
|
||||
zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the target name is valid
|
||||
*/
|
||||
|
@ -2520,6 +2520,7 @@ struct renamesnaparg {
|
||||
char failed[MAXPATHLEN];
|
||||
char *oldsnap;
|
||||
char *newsnap;
|
||||
int error;
|
||||
};
|
||||
|
||||
static int
|
||||
@ -2557,6 +2558,9 @@ dsl_snapshot_rename_one(const char *name, void *arg)
|
||||
dsl_sync_task_create(ra->dstg, dsl_dataset_snapshot_rename_check,
|
||||
dsl_dataset_snapshot_rename_sync, ds, ra->newsnap, 0);
|
||||
|
||||
/* First successful rename clears the error. */
|
||||
ra->error = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -2585,14 +2589,16 @@ dsl_recursive_rename(char *oldname, const char *newname)
|
||||
ra->oldsnap = strchr(oldname, '@') + 1;
|
||||
ra->newsnap = strchr(newname, '@') + 1;
|
||||
*ra->failed = '\0';
|
||||
ra->error = ENOENT;
|
||||
|
||||
err = dmu_objset_find(fsname, dsl_snapshot_rename_one, ra,
|
||||
DS_FIND_CHILDREN);
|
||||
kmem_free(fsname, len);
|
||||
if (err == 0)
|
||||
err = ra->error;
|
||||
|
||||
if (err == 0) {
|
||||
if (err == 0)
|
||||
err = dsl_sync_task_group_wait(ra->dstg);
|
||||
}
|
||||
|
||||
for (dst = list_head(&ra->dstg->dstg_tasks); dst;
|
||||
dst = list_next(&ra->dstg->dstg_tasks, dst)) {
|
||||
|
@ -766,7 +766,26 @@ zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
|
||||
static int
|
||||
zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr)
|
||||
{
|
||||
return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr));
|
||||
char *at = NULL;
|
||||
int error;
|
||||
|
||||
if ((zc->zc_cookie & 1) != 0) {
|
||||
/*
|
||||
* This is recursive rename, so the starting snapshot might
|
||||
* not exist. Check file system or volume permission instead.
|
||||
*/
|
||||
at = strchr(zc->zc_name, '@');
|
||||
if (at == NULL)
|
||||
return (EINVAL);
|
||||
*at = '\0';
|
||||
}
|
||||
|
||||
error = zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr);
|
||||
|
||||
if (at != NULL)
|
||||
*at = '@';
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
|
Loading…
Reference in New Issue
Block a user