]> git.proxmox.com Git - mirror_zfs-debian.git/blobdiff - lib/libzfs/libzfs_dataset.c
Imported Upstream version 0.6.5.3
[mirror_zfs-debian.git] / lib / libzfs / libzfs_dataset.c
index e517e06bc36ac4ccf48db843d472f6c80bbcefb6..088ee35c4f6b2a14ce7fd83e7313a3c85d994444 100644 (file)
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
@@ -259,7 +260,7 @@ zpool_handle(zfs_handle_t *zhp)
        int len;
        zpool_handle_t *zph;
 
-       len = strcspn(zhp->zfs_name, "/@") + 1;
+       len = strcspn(zhp->zfs_name, "/@#") + 1;
        pool_name = zfs_alloc(zhp->zfs_hdl, len);
        (void) strlcpy(pool_name, zhp->zfs_name, len);
 
@@ -546,6 +547,70 @@ zfs_handle_dup(zfs_handle_t *zhp_orig)
        return (zhp);
 }
 
+boolean_t
+zfs_bookmark_exists(const char *path)
+{
+       nvlist_t *bmarks;
+       nvlist_t *props;
+       char fsname[ZFS_MAXNAMELEN];
+       char *bmark_name;
+       char *pound;
+       int err;
+       boolean_t rv;
+
+
+       (void) strlcpy(fsname, path, sizeof (fsname));
+       pound = strchr(fsname, '#');
+       if (pound == NULL)
+               return (B_FALSE);
+
+       *pound = '\0';
+       bmark_name = pound + 1;
+       props = fnvlist_alloc();
+       err = lzc_get_bookmarks(fsname, props, &bmarks);
+       nvlist_free(props);
+       if (err != 0) {
+               nvlist_free(bmarks);
+               return (B_FALSE);
+       }
+
+       rv = nvlist_exists(bmarks, bmark_name);
+       nvlist_free(bmarks);
+       return (rv);
+}
+
+zfs_handle_t *
+make_bookmark_handle(zfs_handle_t *parent, const char *path,
+    nvlist_t *bmark_props)
+{
+       zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+
+       if (zhp == NULL)
+               return (NULL);
+
+       /* Fill in the name. */
+       zhp->zfs_hdl = parent->zfs_hdl;
+       (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
+
+       /* Set the property lists. */
+       if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) {
+               free(zhp);
+               return (NULL);
+       }
+
+       /* Set the types. */
+       zhp->zfs_head_type = parent->zfs_head_type;
+       zhp->zfs_type = ZFS_TYPE_BOOKMARK;
+
+       if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) {
+               nvlist_free(zhp->zfs_props);
+               free(zhp);
+               return (NULL);
+       }
+
+       return (zhp);
+}
+
 /*
  * Opens the given snapshot, filesystem, or volume.   The 'types'
  * argument is a mask of acceptable types.  The function will print an
@@ -948,7 +1013,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        goto error;
                }
 
-               if (!zfs_prop_valid_for_type(prop, type)) {
+               if (!zfs_prop_valid_for_type(prop, type, B_FALSE)) {
                        zfs_error_aux(hdl,
                            dgettext(TEXT_DOMAIN, "'%s' does not "
                            "apply to datasets of this type"), propname);
@@ -990,21 +1055,31 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        break;
                }
 
-               case ZFS_PROP_RECORDSIZE:
                case ZFS_PROP_VOLBLOCKSIZE:
-                       /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
+               case ZFS_PROP_RECORDSIZE:
+               {
+                       int maxbs = SPA_MAXBLOCKSIZE;
+                       char buf[64];
+
+                       if (zhp != NULL) {
+                               maxbs = zpool_get_prop_int(zhp->zpool_hdl,
+                                   ZPOOL_PROP_MAXBLOCKSIZE, NULL);
+                       }
+                       /*
+                        * The value must be a power of two between
+                        * SPA_MINBLOCKSIZE and maxbs.
+                        */
                        if (intval < SPA_MINBLOCKSIZE ||
-                           intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
+                           intval > maxbs || !ISP2(intval)) {
+                               zfs_nicenum(maxbs, buf, sizeof (buf));
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "'%s' must be power of 2 from %u "
-                                   "to %uk"), propname,
-                                   (uint_t)SPA_MINBLOCKSIZE,
-                                   (uint_t)SPA_MAXBLOCKSIZE >> 10);
+                                   "'%s' must be power of 2 from 512B "
+                                   "to %s"), propname, buf);
                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                                goto error;
                        }
                        break;
-
+               }
                case ZFS_PROP_MLSLABEL:
                {
 #ifdef HAVE_MLSLABEL
@@ -1373,6 +1448,12 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
                (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
                break;
 
+       case E2BIG:
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "property value too long"));
+               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+               break;
+
        case ENOTSUP:
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                    "pool and or dataset must be upgraded to set this "
@@ -1381,7 +1462,8 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
                break;
 
        case ERANGE:
-               if (prop == ZFS_PROP_COMPRESSION) {
+               if (prop == ZFS_PROP_COMPRESSION ||
+                   prop == ZFS_PROP_RECORDSIZE) {
                        (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "property setting is not allowed on "
                            "bootable datasets"));
@@ -1610,7 +1692,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
        /*
         * Check to see if the value applies to this type
         */
-       if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
+       if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE))
                return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
 
        /*
@@ -1655,6 +1737,15 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
                 * Refresh the statistics so the new property is reflected.
                 */
                (void) get_stats(zhp);
+
+               /*
+                * Remount the filesystem to propagate the change
+                * if one of the options handled by the generic
+                * Linux namespace layer has been modified.
+                */
+               if (zfs_is_namespace_prop(prop) &&
+                   zfs_is_mounted(zhp, NULL))
+                       ret = zfs_mount(zhp, MNTOPT_REMOUNT, 0);
        }
 
 error:
@@ -1751,6 +1842,16 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
 
        *source = NULL;
 
+       /*
+        * If the property is being fetched for a snapshot, check whether
+        * the property is valid for the snapshot's head dataset type.
+        */
+       if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT &&
+               !zfs_prop_valid_for_type(prop, zhp->zfs_head_type, B_TRUE)) {
+                       *val = zfs_prop_default_numeric(prop);
+                       return (-1);
+       }
+
        switch (prop) {
        case ZFS_PROP_ATIME:
                mntopt_on = MNTOPT_ATIME;
@@ -1851,6 +1952,10 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
        case ZFS_PROP_REFQUOTA:
        case ZFS_PROP_RESERVATION:
        case ZFS_PROP_REFRESERVATION:
+       case ZFS_PROP_FILESYSTEM_LIMIT:
+       case ZFS_PROP_SNAPSHOT_LIMIT:
+       case ZFS_PROP_FILESYSTEM_COUNT:
+       case ZFS_PROP_SNAPSHOT_COUNT:
                *val = getprop_uint64(zhp, prop, source);
 
                if (*source == NULL) {
@@ -1871,12 +1976,14 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
        case ZFS_PROP_NORMALIZE:
        case ZFS_PROP_UTF8ONLY:
        case ZFS_PROP_CASE:
-               if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
-                   zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
+               if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
                        return (-1);
                (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
                if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
                        zcmd_free_nvlists(&zc);
+                       if (prop == ZFS_PROP_VERSION &&
+                           zhp->zfs_type == ZFS_TYPE_VOLUME)
+                               *val = zfs_prop_default_numeric(prop);
                        return (-1);
                }
                if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
@@ -2115,7 +2222,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
        /*
         * Check to see if this property applies to our object
         */
-       if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
+       if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE))
                return (-1);
 
        if (received && zfs_prop_readonly(prop))
@@ -2257,6 +2364,30 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                }
                break;
 
+       case ZFS_PROP_FILESYSTEM_LIMIT:
+       case ZFS_PROP_SNAPSHOT_LIMIT:
+       case ZFS_PROP_FILESYSTEM_COUNT:
+       case ZFS_PROP_SNAPSHOT_COUNT:
+
+               if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
+                       return (-1);
+
+               /*
+                * If limit is UINT64_MAX, we translate this into 'none' (unless
+                * literal is set), and indicate that it's the default value.
+                * Otherwise, we print the number nicely and indicate that it's
+                * set locally.
+                */
+               if (literal) {
+                       (void) snprintf(propbuf, proplen, "%llu",
+                           (u_longlong_t)val);
+               } else if (val == UINT64_MAX) {
+                       (void) strlcpy(propbuf, "none", proplen);
+               } else {
+                       zfs_nicenum(val, propbuf, proplen);
+               }
+               break;
+
        case ZFS_PROP_REFRATIO:
        case ZFS_PROP_COMPRESSRATIO:
                if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
@@ -2277,6 +2408,9 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                case ZFS_TYPE_SNAPSHOT:
                        str = "snapshot";
                        break;
+               case ZFS_TYPE_BOOKMARK:
+                       str = "bookmark";
+                       break;
                default:
                        abort();
                }
@@ -2436,7 +2570,7 @@ zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
        /*
         * Check to see if this property applies to our object
         */
-       if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
+       if (!zfs_prop_valid_for_type(prop, zhp->zfs_type, B_FALSE)) {
                return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
                    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
                    zfs_prop_to_name(prop)));
@@ -3079,6 +3213,8 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
        /* check for failure */
        if (ret != 0) {
                char parent[ZFS_MAXNAMELEN];
+               char buf[64];
+
                (void) parent_name(path, parent, sizeof (parent));
 
                switch (errno) {
@@ -3093,11 +3229,10 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
                        return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 
                case EDOM:
+                       zfs_nicenum(SPA_MAXBLOCKSIZE, buf, sizeof (buf));
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "volume block size must be power of 2 from "
-                           "%u to %uk"),
-                           (uint_t)SPA_MINBLOCKSIZE,
-                           (uint_t)SPA_MAXBLOCKSIZE >> 10);
+                           "512B to %s"), buf);
 
                        return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 
@@ -3134,6 +3269,19 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
 {
        zfs_cmd_t zc = {"\0"};
 
+       if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
+               nvlist_t *nv = fnvlist_alloc();
+               fnvlist_add_boolean(nv, zhp->zfs_name);
+               int error = lzc_destroy_bookmarks(nv, NULL);
+               fnvlist_free(nv);
+               if (error != 0) {
+                       return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
+                           dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
+                           zhp->zfs_name));
+               }
+               return (0);
+       }
+
        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
        if (ZFS_IS_VOLUME(zhp)) {
@@ -3517,45 +3665,44 @@ typedef struct rollback_data {
        const char      *cb_target;             /* the snapshot */
        uint64_t        cb_create;              /* creation time reference */
        boolean_t       cb_error;
-       boolean_t       cb_dependent;
        boolean_t       cb_force;
 } rollback_data_t;
 
 static int
-rollback_destroy(zfs_handle_t *zhp, void *data)
+rollback_destroy_dependent(zfs_handle_t *zhp, void *data)
 {
        rollback_data_t *cbp = data;
+       prop_changelist_t *clp;
 
-       if (!cbp->cb_dependent) {
-               if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
-                   zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
-                   zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
-                   cbp->cb_create) {
+       /* We must destroy this clone; first unmount it */
+       clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
+           cbp->cb_force ? MS_FORCE: 0);
+       if (clp == NULL || changelist_prefix(clp) != 0) {
+               cbp->cb_error = B_TRUE;
+               zfs_close(zhp);
+               return (0);
+       }
+       if (zfs_destroy(zhp, B_FALSE) != 0)
+               cbp->cb_error = B_TRUE;
+       else
+               changelist_remove(clp, zhp->zfs_name);
+       (void) changelist_postfix(clp);
+       changelist_free(clp);
 
-                       cbp->cb_dependent = B_TRUE;
-                       cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
-                           rollback_destroy, cbp);
-                       cbp->cb_dependent = B_FALSE;
+       zfs_close(zhp);
+       return (0);
+}
 
-                       cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
-               }
-       } else {
-               /* We must destroy this clone; first unmount it */
-               prop_changelist_t *clp;
+static int
+rollback_destroy(zfs_handle_t *zhp, void *data)
+{
+       rollback_data_t *cbp = data;
 
-               clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
-                   cbp->cb_force ? MS_FORCE: 0);
-               if (clp == NULL || changelist_prefix(clp) != 0) {
-                       cbp->cb_error = B_TRUE;
-                       zfs_close(zhp);
-                       return (0);
-               }
-               if (zfs_destroy(zhp, B_FALSE) != 0)
-                       cbp->cb_error = B_TRUE;
-               else
-                       changelist_remove(clp, zhp->zfs_name);
-               (void) changelist_postfix(clp);
-               changelist_free(clp);
+       if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
+               cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
+                   rollback_destroy_dependent, cbp);
+
+               cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
        }
 
        zfs_close(zhp);
@@ -3566,8 +3713,8 @@ rollback_destroy(zfs_handle_t *zhp, void *data)
  * Given a dataset, rollback to a specific snapshot, discarding any
  * data changes since then and making it the active dataset.
  *
- * Any snapshots more recent than the target are destroyed, along with
- * their dependents.
+ * Any snapshots and bookmarks more recent than the target are
+ * destroyed, along with their dependents (i.e. clones).
  */
 int
 zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
@@ -3587,7 +3734,8 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
        cb.cb_force = force;
        cb.cb_target = snap->zfs_name;
        cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
-       (void) zfs_iter_children(zhp, rollback_destroy, &cb);
+       (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb);
+       (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb);
 
        if (cb.cb_error)
                return (-1);