Author: avg
Date: Thu Oct  3 11:08:45 2019
New Revision: 353037
URL: https://svnweb.freebsd.org/changeset/base/353037

Log:
  ZFS: add bookmark renaming
  
  The feature is implemented as an extension of the existing
  ZFS_IOC_RENAME ioctl.  Both the userland and the DSL interfaces support
  renaming only a single bookmark at a time.  As of now, there is no ZCP
  interface to the new functionality.  I am going to add it once the DSL
  interface passes a test of time.
  
  This change picks up support for zfs_ioc_namecheck_t::ENTITY_NAME that
  was added to ZoL as part of Redacted Send/Receive feature by Paul
  Dagnelie <p...@delphix.com>.  This is needed to allow a bookmark name in
  zc_name.
  
  Discussed with:       mahrens
  Reviewed by:  bcr (man page)
  Sponsored by: CyberSecure
  Differential Revision: https://reviews.freebsd.org/D21795

Modified:
  head/cddl/contrib/opensolaris/cmd/zfs/zfs.8
  head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
  head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c

Modified: head/cddl/contrib/opensolaris/cmd/zfs/zfs.8
==============================================================================
--- head/cddl/contrib/opensolaris/cmd/zfs/zfs.8 Thu Oct  3 10:46:09 2019        
(r353036)
+++ head/cddl/contrib/opensolaris/cmd/zfs/zfs.8 Thu Oct  3 11:08:45 2019        
(r353037)
@@ -105,6 +105,9 @@
 .Ar snapshot snapshot
 .Nm
 .Cm rename
+.Ar bookmark bookmark
+.Nm
+.Cm rename
 .Fl u
 .Op Fl p
 .Ar filesystem filesystem
@@ -2092,6 +2095,16 @@ flag.
 .Pp
 Recursively rename the snapshots of all descendent datasets. Snapshots are the
 only dataset that can be renamed recursively.
+.It Xo
+.Nm
+.Cm rename
+.Ar bookmark bookmark
+.Xc
+.Pp
+Renames the given bookmark.
+Bookmarks can only be renamed within the parent file system or volume.
+When renaming a bookmark, the parent file system or volume of the bookmark
+does not need to be specified as part of the second argument.
 .It Xo
 .Nm
 .Cm list

Modified: head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
==============================================================================
--- head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c    Thu Oct  3 10:46:09 
2019        (r353036)
+++ head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c    Thu Oct  3 11:08:45 
2019        (r353037)
@@ -284,6 +284,7 @@ get_usage(zfs_help_t idx)
                    "<filesystem|volume|snapshot>\n"
                    "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
                    "\trename -r <snapshot> <snapshot>\n"
+                   "\trename <bookmark> <bookmark>\n"
                    "\trename -u [-p] <filesystem> <filesystem>"));
        case HELP_ROLLBACK:
                return (gettext("\trollback [-rRf] <snapshot>\n"));
@@ -3254,6 +3255,7 @@ zfs_do_list(int argc, char **argv)
  * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
  * zfs rename [-f] -p <fs | vol> <fs | vol>
  * zfs rename -r <snap> <snap>
+ * zfs rename <bmark> <bmark>
  * zfs rename -u [-p] <fs> <fs>
  *
  * Renames the given dataset to another of the same type.
@@ -3270,6 +3272,7 @@ zfs_do_rename(int argc, char **argv)
        int ret = 0;
        int types;
        boolean_t parents = B_FALSE;
+       boolean_t bookmarks = B_FALSE;
        char *snapshot = NULL;
 
        /* check options */
@@ -3320,7 +3323,7 @@ zfs_do_rename(int argc, char **argv)
                usage(B_FALSE);
        }
 
-       if (flags.recurse && strchr(argv[0], '@') == 0) {
+       if (flags.recurse && strchr(argv[0], '@') == NULL) {
                (void) fprintf(stderr, gettext("source dataset for recursive "
                    "rename must be a snapshot\n"));
                usage(B_FALSE);
@@ -3332,10 +3335,22 @@ zfs_do_rename(int argc, char **argv)
                usage(B_FALSE);
        }
 
+       if (strchr(argv[0], '#') != NULL)
+               bookmarks = B_TRUE;
+
+       if (bookmarks && (flags.nounmount || flags.recurse ||
+           flags.forceunmount || parents)) {
+               (void) fprintf(stderr, gettext("options are not supported "
+                   "for renaming bookmarks\n"));
+               usage(B_FALSE);
+       }
+
        if (flags.nounmount)
                types = ZFS_TYPE_FILESYSTEM;
        else if (parents)
                types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
+       else if (bookmarks)
+               types = ZFS_TYPE_BOOKMARK;
        else
                types = ZFS_TYPE_DATASET;
 

Modified: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
==============================================================================
--- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c    Thu Oct 
 3 10:46:09 2019        (r353036)
+++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c    Thu Oct 
 3 11:08:45 2019        (r353037)
@@ -4291,17 +4291,18 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
        /*
         * Make sure the target name is valid
         */
-       if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
-               if ((strchr(target, '@') == NULL) ||
-                   *target == '@') {
+       if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
+           zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
+               const char sep = zhp->zfs_type == ZFS_TYPE_SNAPSHOT ? '@' : '#';
+
+               if ((strchr(target, sep) == NULL) || *target == sep) {
                        /*
                         * Snapshot target name is abbreviated,
                         * reconstruct full dataset name
                         */
-                       (void) strlcpy(parent, zhp->zfs_name,
-                           sizeof (parent));
-                       delim = strchr(parent, '@');
-                       if (strchr(target, '@') == NULL)
+                       (void) strlcpy(parent, zhp->zfs_name, sizeof (parent));
+                       delim = strchr(parent, sep);
+                       if (strchr(target, sep) == NULL)
                                *(++delim) = '\0';
                        else
                                *delim = '\0';
@@ -4311,12 +4312,13 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
                        /*
                         * Make sure we're renaming within the same dataset.
                         */
-                       delim = strchr(target, '@');
+                       delim = strchr(target, sep);
                        if (strncmp(zhp->zfs_name, target, delim - target)
-                           != 0 || zhp->zfs_name[delim - target] != '@') {
+                           != 0 || zhp->zfs_name[delim - target] != sep) {
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "snapshots must be part of same "
-                                   "dataset"));
+                                   "%s must be part of same dataset"),
+                                   zhp->zfs_type == ZFS_TYPE_SNAPSHOT ?
+                                   "snapshots" : "bookmarks");
                                return (zfs_error(hdl, EZFS_CROSSTARGET,
                                    errbuf));
                        }
@@ -4379,7 +4381,6 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
                flags.nounmount = B_TRUE;
        }
        if (flags.recurse) {
-
                parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
                if (parentname == NULL) {
                        ret = -1;
@@ -4392,7 +4393,8 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
                        ret = -1;
                        goto error;
                }
-       } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) {
+       } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT &&
+           zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
                if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
                    flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0,
                    flags.forceunmount ? MS_FORCE : 0)) == NULL) {
@@ -4437,6 +4439,8 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
                            "a child dataset already has a snapshot "
                            "with the new name"));
                        (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
+               } else if (errno == EINVAL) {
+                       (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
                } else {
                        (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
                }

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c  Thu Oct 
 3 10:46:09 2019        (r353036)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c  Thu Oct 
 3 11:08:45 2019        (r353037)
@@ -459,3 +459,108 @@ dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *error
        fnvlist_free(dbda.dbda_success);
        return (rv);
 }
+
+typedef struct dsl_bookmark_rename_arg {
+       const char *dbra_fsname;
+       const char *dbra_oldname;
+       const char *dbra_newname;
+} dsl_bookmark_rename_arg_t;
+
+static int
+dsl_bookmark_rename_check(void *arg, dmu_tx_t *tx)
+{
+       dsl_bookmark_rename_arg_t *dbra = arg;
+       dsl_pool_t *dp = dmu_tx_pool(tx);
+       dsl_dataset_t *ds;
+       zfs_bookmark_phys_t bmark_phys;
+       int error;
+
+       if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
+               return (SET_ERROR(ENOTSUP));
+
+       /* Check validity and the full length of the new bookmark name. */
+       if (zfs_component_namecheck(dbra->dbra_newname, NULL, NULL))
+               return (SET_ERROR(EINVAL));
+       if (strlen(dbra->dbra_fsname) + strlen(dbra->dbra_newname) + 1 >=
+           ZFS_MAX_DATASET_NAME_LEN)
+               return (SET_ERROR(ENAMETOOLONG));
+
+       error = dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds);
+       if (error != 0)
+               return (error);
+       if (ds->ds_is_snapshot) {
+               dsl_dataset_rele(ds, FTAG);
+               return (SET_ERROR(EINVAL));
+       }
+       error = dsl_dataset_bmark_lookup(ds, dbra->dbra_oldname, &bmark_phys);
+       if (error != 0) {
+               dsl_dataset_rele(ds, FTAG);
+               return (error);
+       }
+
+       error = dsl_dataset_bmark_lookup(ds, dbra->dbra_newname, &bmark_phys);
+       dsl_dataset_rele(ds, FTAG);
+       if (error == 0)
+               return (SET_ERROR(EEXIST));
+       if (error != ESRCH)
+               return (error);
+       return (0);
+}
+
+static void
+dsl_bookmark_rename_sync(void *arg, dmu_tx_t *tx)
+{
+       zfs_bookmark_phys_t bmark_phys;
+       dsl_bookmark_rename_arg_t *dbra = arg;
+       dsl_pool_t *dp = dmu_tx_pool(tx);
+       objset_t *mos;
+       dsl_dataset_t *ds;
+       uint64_t bmark_zapobj;
+       uint64_t int_size, num_ints;
+       matchtype_t mt = 0;
+       int error;
+
+       ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
+       VERIFY0(dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds));
+
+       mos = ds->ds_dir->dd_pool->dp_meta_objset;
+       bmark_zapobj = ds->ds_bookmarks;
+
+       if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
+               mt = MT_NORMALIZE;
+
+       VERIFY0(zap_length(mos, bmark_zapobj, dbra->dbra_oldname,
+           &int_size, &num_ints));
+       ASSERT3U(int_size, ==, sizeof (uint64_t));
+       VERIFY0(zap_lookup_norm(mos, bmark_zapobj, dbra->dbra_oldname, int_size,
+           num_ints, &bmark_phys, mt, NULL, 0, NULL));
+       VERIFY0(zap_remove_norm(mos, bmark_zapobj, dbra->dbra_oldname, mt, tx));
+
+       VERIFY0(zap_add(mos, bmark_zapobj, dbra->dbra_newname, int_size,
+           num_ints, &bmark_phys, tx));
+
+       spa_history_log_internal_ds(ds, "rename bookmark", tx,
+           "#%s -> #%s creation_txg=%llu",
+           dbra->dbra_oldname, dbra->dbra_newname,
+           (longlong_t)bmark_phys.zbm_creation_txg);
+
+       dsl_dataset_rele(ds, FTAG);
+}
+
+/*
+ * The bookmarks must all be in the same pool.
+ */
+int
+dsl_bookmark_rename(const char *fsname, const char *oldbmark,
+    const char *newbmark)
+{
+       dsl_bookmark_rename_arg_t dbra;
+
+       dbra.dbra_fsname = fsname;
+       dbra.dbra_oldname = oldbmark;
+       dbra.dbra_newname = newbmark;
+
+       return (dsl_sync_task(fsname, dsl_bookmark_rename_check,
+           dsl_bookmark_rename_sync, &dbra, 1, ZFS_SPACE_CHECK_NORMAL));
+}
+

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h      
Thu Oct  3 10:46:09 2019        (r353036)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h      
Thu Oct  3 11:08:45 2019        (r353037)
@@ -41,6 +41,7 @@ int dsl_bookmark_create(nvlist_t *, nvlist_t *);
 int dsl_get_bookmarks(const char *, nvlist_t *, nvlist_t *);
 int dsl_get_bookmarks_impl(dsl_dataset_t *, nvlist_t *, nvlist_t *);
 int dsl_bookmark_destroy(nvlist_t *, nvlist_t *);
+int dsl_bookmark_rename(const char *fs, const char *from, const char *to);
 int dsl_bookmark_lookup(struct dsl_pool *, const char *,
     struct dsl_dataset *, zfs_bookmark_phys_t *);
 

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Thu Oct 
 3 10:46:09 2019        (r353036)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Thu Oct 
 3 11:08:45 2019        (r353037)
@@ -224,7 +224,8 @@ typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t
 typedef enum {
        NO_NAME,
        POOL_NAME,
-       DATASET_NAME
+       DATASET_NAME,
+       ENTITY_NAME
 } zfs_ioc_namecheck_t;
 
 typedef enum {
@@ -922,8 +923,21 @@ static int
 zfs_secpolicy_rename(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 {
        char *at = NULL;
+       char *pound;
        int error;
 
+       if ((pound = strchr(zc->zc_name, '#')) != NULL) {
+               *pound = '\0';
+               error = zfs_secpolicy_write_perms(zc->zc_name,
+                   ZFS_DELEG_PERM_RENAME, cr);
+               if (error == 0) {
+                       error = zfs_secpolicy_write_perms(zc->zc_name,
+                           ZFS_DELEG_PERM_BOOKMARK, cr);
+               }
+               *pound = '#';
+               return (error);
+       }
+
        if ((zc->zc_cookie & 1) != 0) {
                /*
                 * This is recursive rename, so the starting snapshot might
@@ -4020,8 +4034,8 @@ recursive_unmount(const char *fsname, void *arg)
 
 /*
  * inputs:
- * zc_name     old name of dataset
- * zc_value    new name of dataset
+ * zc_name     old name of dataset or bookmark
+ * zc_value    new name of dataset or bookmark
  * zc_cookie   recursive flag (only valid for snapshots)
  *
  * outputs:    none
@@ -4032,7 +4046,7 @@ zfs_ioc_rename(zfs_cmd_t *zc)
        objset_t *os;
        dmu_objset_type_t ost;
        boolean_t recursive = zc->zc_cookie & 1;
-       char *at;
+       char *pos, *pos2;
        boolean_t allow_mounted = B_TRUE;
        int err;
 
@@ -4040,9 +4054,34 @@ zfs_ioc_rename(zfs_cmd_t *zc)
        allow_mounted = (zc->zc_cookie & 2) != 0;
 #endif
 
-       /* "zfs rename" from and to ...%recv datasets should both fail */
        zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
        zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
+
+       pos = strchr(zc->zc_name, '#');
+       if (pos != NULL) {
+               /* Bookmarks must be in same fs. */
+               pos2 = strchr(zc->zc_value, '#');
+               if (pos2 == NULL)
+                       return (SET_ERROR(EINVAL));
+
+               /* Recursive flag is not supported yet. */
+               if (recursive)
+                       return (SET_ERROR(ENOTSUP));
+
+               *pos = '\0';
+               *pos2 = '\0';
+               if (strcmp(zc->zc_name, zc->zc_value) == 0) {
+                       err = dsl_bookmark_rename(zc->zc_name,
+                           pos + 1, pos2 + 1);
+               } else {
+                       err = SET_ERROR(EXDEV);
+               }
+               *pos = '#';
+               *pos2 = '#';
+               return (err);
+       }
+
+       /* "zfs rename" from and to ...%recv datasets should both fail */
        if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 ||
            dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
            strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%'))
@@ -4054,28 +4093,30 @@ zfs_ioc_rename(zfs_cmd_t *zc)
        ost = dmu_objset_type(os);
        dmu_objset_rele(os, FTAG);
 
-       at = strchr(zc->zc_name, '@');
-       if (at != NULL) {
-               /* snaps must be in same fs */
-               int error;
-
-               if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1))
-                       return (SET_ERROR(EXDEV));
-               *at = '\0';
-               if (ost == DMU_OST_ZFS && !allow_mounted) {
-                       error = dmu_objset_find(zc->zc_name,
-                           recursive_unmount, at + 1,
-                           recursive ? DS_FIND_CHILDREN : 0);
-                       if (error != 0) {
-                               *at = '@';
-                               return (error);
+       pos = strchr(zc->zc_name, '@');
+       if (pos != NULL) {
+               /* Snapshots must be in same fs. */
+               pos2 = strchr(zc->zc_value, '@');
+               if (pos2 == NULL)
+                       return (SET_ERROR(EINVAL));
+               *pos = '\0';
+               *pos2 = '\0';
+               if (strcmp(zc->zc_name, zc->zc_value) != 0) {
+                       err = SET_ERROR(EXDEV);
+               } else {
+                       if (ost == DMU_OST_ZFS && !allow_mounted) {
+                               err = dmu_objset_find(zc->zc_name,
+                                   recursive_unmount, pos + 1,
+                                   recursive ? DS_FIND_CHILDREN : 0);
                        }
+                       if (err == 0) {
+                               err = dsl_dataset_rename_snapshot(zc->zc_name,
+                                   pos + 1, pos2 + 1, recursive);
+                       }
                }
-               error = dsl_dataset_rename_snapshot(zc->zc_name,
-                   at + 1, strchr(zc->zc_value, '@') + 1, recursive);
-               *at = '@';
-
-               return (error);
+               *pos = '@';
+               *pos2 = '@';
+               return (err);
        } else {
 #ifdef illumos
                if (ost == DMU_OST_ZVOL)
@@ -6352,8 +6393,6 @@ zfs_ioctl_init(void)
            zfs_secpolicy_none);
        zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy,
            zfs_secpolicy_destroy);
-       zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename,
-           zfs_secpolicy_rename);
        zfs_ioctl_register_dataset_modify(ZFS_IOC_RECV, zfs_ioc_recv,
            zfs_secpolicy_recv);
        zfs_ioctl_register_dataset_modify(ZFS_IOC_PROMOTE, zfs_ioc_promote,
@@ -6363,6 +6402,14 @@ zfs_ioctl_init(void)
        zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl,
            zfs_secpolicy_set_fsacl);
 
+       /*
+        * Not using zfs_ioctl_register_dataset_modify as DATASET_NAME check
+        * won't allow a bookmark name.
+        */
+       zfs_ioctl_register_legacy(ZFS_IOC_RENAME, zfs_ioc_rename,
+           zfs_secpolicy_rename, ENTITY_NAME, B_TRUE,
+           POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY);
+
        zfs_ioctl_register_dataset_nolog(ZFS_IOC_SHARE, zfs_ioc_share,
            zfs_secpolicy_share, POOL_CHECK_NONE);
        zfs_ioctl_register_dataset_nolog(ZFS_IOC_SMB_ACL, zfs_ioc_smb_acl,
@@ -6392,7 +6439,8 @@ pool_status_check(const char *name, zfs_ioc_namecheck_
        spa_t *spa;
        int error;
 
-       ASSERT(type == POOL_NAME || type == DATASET_NAME);
+       ASSERT(type == POOL_NAME || type == DATASET_NAME ||
+           type == ENTITY_NAME);
 
        if (check & POOL_CHECK_NONE)
                return (0);
@@ -6723,6 +6771,15 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t ar
                else
                        error = pool_status_check(zc->zc_name,
                            vec->zvec_namecheck, vec->zvec_pool_check);
+               break;
+
+       case ENTITY_NAME:
+               if (entity_namecheck(zc->zc_name, NULL, NULL) != 0) {
+                       error = SET_ERROR(EINVAL);
+               } else {
+                       error = pool_status_check(zc->zc_name,
+                           vec->zvec_namecheck, vec->zvec_pool_check);
+               }
                break;
 
        case NO_NAME:
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to