Linux 3.5 compat, eops->encode_fh() takes inodes
[zfs.git] / lib / libzfs / libzfs_dataset.c
index 7401513..c51b99d 100644 (file)
@@ -22,7 +22,8 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2010 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
  */
 
 #include <ctype.h>
@@ -413,6 +414,9 @@ make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
                zhp->zfs_head_type = ZFS_TYPE_VOLUME;
        else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
                zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
+       else if (zhp->zfs_dmustats.dds_type == DMU_OST_OTHER)
+               return (-1); /* zpios' and other testing datasets are
+                               of this type, ignore if encountered */
        else
                abort();
 
@@ -477,6 +481,23 @@ make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
        return (zhp);
 }
 
+static zfs_handle_t *
+make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
+{
+       zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+
+       if (zhp == NULL)
+               return (NULL);
+
+       zhp->zfs_hdl = pzhp->zfs_hdl;
+       (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
+       zhp->zfs_head_type = pzhp->zfs_type;
+       zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
+       zhp->zpool_hdl = zpool_handle(zhp);
+
+       return (zhp);
+}
+
 /*
  * Opens the given snapshot, filesystem, or volume.   The 'types'
  * argument is a mask of acceptable types.  The function will print an
@@ -2249,15 +2270,19 @@ out:
  * convert the propname into parameters needed by kernel
  * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
  * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
+ * Eg: groupquota@staff -> ZFS_PROP_GROUPQUOTA, "", 1234
+ * Eg: groupused@staff -> ZFS_PROP_GROUPUSED, "", 1234
  */
 static int
 userquota_propname_decode(const char *propname, boolean_t zoned,
     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
 {
        zfs_userquota_prop_t type;
-       char *cp, *end;
-       char *numericsid = NULL;
+       char *cp;
        boolean_t isuser;
+       boolean_t isgroup;
+       struct passwd *pw;
+       struct group *gr;
 
        domain[0] = '\0';
 
@@ -2271,18 +2296,29 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
                return (EINVAL);
        *typep = type;
 
-       isuser = (type == ZFS_PROP_USERQUOTA ||
-           type == ZFS_PROP_USERUSED);
+       isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED);
+       isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED);
 
        cp = strchr(propname, '@') + 1;
 
-       if (strchr(cp, '@')) {
+       if (isuser && (pw = getpwnam(cp)) != NULL) {
+               if (zoned && getzoneid() == GLOBAL_ZONEID)
+                       return (ENOENT);
+               *ridp = pw->pw_uid;
+       } else if (isgroup && (gr = getgrnam(cp)) != NULL) {
+               if (zoned && getzoneid() == GLOBAL_ZONEID)
+                       return (ENOENT);
+               *ridp = gr->gr_gid;
+       } else if (strchr(cp, '@')) {
 #ifdef HAVE_IDMAP
                /*
                 * It's a SID name (eg "user@domain") that needs to be
                 * turned into S-1-domainID-RID.
                 */
                directory_error_t e;
+               char *numericsid = NULL;
+               char *end;
+
                if (zoned && getzoneid() == GLOBAL_ZONEID)
                        return (ENOENT);
                if (isuser) {
@@ -2299,14 +2335,6 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
                if (numericsid == NULL)
                        return (ENOENT);
                cp = numericsid;
-               /* will be further decoded below */
-#else
-               return (ENOSYS);
-#endif /* HAVE_IDMAP */
-       }
-
-       if (strncmp(cp, "S-1-", 4) == 0) {
-               /* It's a numeric SID (eg "S-1-234-567-89") */
                (void) strlcpy(domain, cp, domainlen);
                cp = strrchr(domain, '-');
                *cp = '\0';
@@ -2314,39 +2342,22 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
 
                errno = 0;
                *ridp = strtoull(cp, &end, 10);
-               if (numericsid) {
-                       free(numericsid);
-                       numericsid = NULL;
-               }
+               free(numericsid);
+
                if (errno != 0 || *end != '\0')
                        return (EINVAL);
-       } else if (!isdigit(*cp)) {
-               /*
-                * It's a user/group name (eg "user") that needs to be
-                * turned into a uid/gid
-                */
-               if (zoned && getzoneid() == GLOBAL_ZONEID)
-                       return (ENOENT);
-               if (isuser) {
-                       struct passwd *pw;
-                       pw = getpwnam(cp);
-                       if (pw == NULL)
-                               return (ENOENT);
-                       *ridp = pw->pw_uid;
-               } else {
-                       struct group *gr;
-                       gr = getgrnam(cp);
-                       if (gr == NULL)
-                               return (ENOENT);
-                       *ridp = gr->gr_gid;
-               }
+#else
+               return (ENOSYS);
+#endif /* HAVE_IDMAP */
        } else {
 #ifdef HAVE_IDMAP
                /* It's a user/group ID (eg "12345"). */
-               uid_t id = strtoul(cp, &end, 10);
+               uid_t id;
                idmap_rid_t rid;
                char *mapdomain;
+               char *end;
 
+               id = strtoul(cp, &end, 10);
                if (*end != '\0')
                        return (EINVAL);
                if (id > MAXUID) {
@@ -2364,7 +2375,6 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
 #endif /* HAVE_IDMAP */
        }
 
-       ASSERT3P(numericsid, ==, NULL);
        return (0);
 }
 
@@ -2526,7 +2536,8 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
  * Iterate over all snapshots
  */
 int
-zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
+    void *data)
 {
        zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
        zfs_handle_t *nzhp;
@@ -2535,15 +2546,19 @@ zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
        if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
                return (0);
 
+       zc.zc_simple = simple;
+
        if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
                return (-1);
        while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
            &zc)) == 0) {
 
-               if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
-                   &zc)) == NULL) {
+               if (simple)
+                       nzhp = make_dataset_simple_handle_zc(zhp, &zc);
+               else
+                       nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
+               if (nzhp == NULL)
                        continue;
-               }
 
                if ((ret = func(nzhp, data)) != 0) {
                        zcmd_free_nvlists(&zc);
@@ -2565,7 +2580,7 @@ zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
        if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
                return (ret);
 
-       return (zfs_iter_snapshots(zhp, func, data));
+       return (zfs_iter_snapshots(zhp, B_FALSE, func, data));
 }
 
 /*
@@ -3295,7 +3310,7 @@ zfs_promote(zfs_handle_t *zhp)
                return (-1);
        (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
            sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
-       ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
+       ret = zfs_iter_snapshots(pzhp, B_FALSE, promote_snap_cb, &pd);
        if (ret != 0) {
                zfs_close(pzhp);
                return (-1);
@@ -3310,7 +3325,8 @@ zfs_promote(zfs_handle_t *zhp)
        if (ret != 0) {
                int save_errno = errno;
 
-               (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
+               (void) zfs_iter_snapshots(pzhp, B_FALSE, promote_snap_done_cb,
+                   &pd);
                zfs_close(pzhp);
 
                switch (save_errno) {
@@ -3329,7 +3345,8 @@ zfs_promote(zfs_handle_t *zhp)
                        return (zfs_standard_error(hdl, save_errno, errbuf));
                }
        } else {
-               (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
+               (void) zfs_iter_snapshots(zhp, B_FALSE, promote_snap_done_cb,
+                   &pd);
        }
 
        zfs_close(pzhp);
@@ -3928,10 +3945,29 @@ int
 zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
 {
        zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 };
+       int timeout = 3000; /* in milliseconds */
+       int error = 0;
+       int i;
 
        (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 
-       if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
+       /*
+        * Due to concurrent updates by udev the device may be reported as
+        * busy.  In this case don't immediately fail.  Instead briefly delay
+        * and retry the ioctl() which is now likely to succeed.  If unable
+        * remove the link after timeout milliseconds return the failure.
+        */
+       for (i = 0; i < timeout; i++) {
+               error = ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
+               if (error && errno == EBUSY) {
+                       usleep(1000);
+                       continue;
+               } else {
+                       break;
+               }
+       }
+
+       if (error) {
                switch (errno) {
                case ENXIO:
                        /*
@@ -3944,7 +3980,7 @@ zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
                default:
                        return (zfs_standard_error_fmt(hdl, errno,
                            dgettext(TEXT_DOMAIN, "cannot remove device "
-                           "links for '%s'"), dataset));
+                           "links for '%s': %s"), dataset, strerror(errno)));
                }
        }
 
@@ -4341,7 +4377,7 @@ tryagain:
 
        (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
 
-       if (zfs_ioctl(hdl, ZFS_IOC_GET_FSACL, &zc) != 0) {
+       if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
                (void) snprintf(errbuf, sizeof (errbuf),
                    dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
                    zc.zc_name);