Retry removal of busy minors
[zfs.git] / lib / libzfs / libzfs_dataset.c
index 7401513..661f9b5 100644 (file)
@@ -413,6 +413,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();
 
@@ -2249,15 +2252,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 +2278,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 +2317,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 +2324,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 +2357,6 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
 #endif /* HAVE_IDMAP */
        }
 
-       ASSERT3P(numericsid, ==, NULL);
        return (0);
 }
 
@@ -3928,10 +3920,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:
                        /*