Allow leading digits in userquota/groupquota names
authorSuman Chakravartula <schakrava@gmail.com>
Sat, 19 Nov 2011 19:53:12 +0000 (11:53 -0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 22 Nov 2011 00:29:18 +0000 (16:29 -0800)
While setting/getting userquota and groupquota properties, the input
was not treated as a possible username or groupname if it had a
leading digit. While useradd in linux recommends the regexp
[a-z_][a-z0-9_-]*[$]? , it is not enforced. This causes problem for
usernames with leading digits in them. We need to be able to support
getting and setting properties for this unconventional but possible
input category

I've updated the code to validate the username or groupname directly
via the API. Also, note that I moved this validation to the beginning
before the check for SID names with @. This also supports usernames
with @ character in them which are valid. Only when input with @ is
not a valid username, it is interpreted as a potential SID name.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #428

lib/libzfs/libzfs_dataset.c

index 7401513..44fdadd 100644 (file)
@@ -2249,15 +2249,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 +2275,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 +2314,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 +2321,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 +2354,6 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
 #endif /* HAVE_IDMAP */
        }
 
-       ASSERT3P(numericsid, ==, NULL);
        return (0);
 }