Hi all,

The patch below builds on the disk UID code to implement disk names. Disk
names must match [a-zA-Z0-9_]{1,10} and are stored encoded as 6 bits per
character into the disklabel UID field. With this patch, you can use disk
names in /etc/fstab:

        bootdisk.a / ffs rw 1 1

And with every other program that uses the disk mapper or opendev(3).

The name can be set through the disklabel 'i' command:

        > i
        The disklabel UID is currently 9b3cf8a2ddef0000 ('bootdisk')
        duid: [] foo
        > l
        ...
        duid: ab3cc00000000000 ('foo')

When parsing a UID/name, the decision to parse as a UID or as a name is made
based on the length of the string: 16-character strings are parsed as UIDs,
whereas anything <= 10 is parsed as a name.

When displaying a UID/name, it is displayed both as a UID and as a name, as it
is unfortunately not possible to detect whether it was entered as a UID or a
name. This results in garbage names for randomly-generated UIDs:

        > l
        ...
        duid: c7b55168cd92a85f ('mwKGPBrHf4')

The pronouncibility of such names may leave something to be desired. :)

It's possible to change the encoding to 5 bits per character to gain an
additional 2 characters for the name, but this reduces the available character
set to 31 characters, not enough to store a full alphanumeric character set.
Changing over to 8 bits per character would reduce the maximum name length to
8 characters and make it necessary to guard against nefarious names that
scramble the terminal. So all-in-all I figured 6 bits is probably optimal.

Anyway, have fun with it.

Regards,
Wouter

PS, to mlarkin@ & armani@: I've been out of it all for a while, I'll get back
to kvirt once I've got everything here up and running again.


Index: subr_disk.c
===================================================================
RCS file: /cvs/src/sys/kern/subr_disk.c,v
retrieving revision 1.114
diff -u -r1.114 subr_disk.c
--- subr_disk.c 24 Nov 2010 15:31:34 -0000      1.114
+++ subr_disk.c 22 Jun 2011 16:40:27 -0000
@@ -1352,49 +1352,96 @@
        u_char uid[8];
        char c, part;
        int i;
+       size_t len;

        /*
-        * Attempt to map a request for a disklabel UID to the correct device.
-        * We should be supplied with a disklabel UID which has the following
-        * format:
+        * Attempt to map a request for a disk name or disklabel UID to the
+        * correct device. A disk name is a string up to 10 characters long,
+        * and can consist of alphabetical and numeric characters and the
+        * '_' character.
         *
-        * [disklabel uid] . [partition]
+        * If the DM_OPENPART flag is not set, the name or UID must be
+        * followed by the partition:
         *
-        * Alternatively, if the DM_OPENPART flag is set the disklabel UID can
-        * based passed on its own.
+        * [name or uid] . [partition]
         */

+       /* Verify that the device name is properly formed. */
        if (strchr(path, '/') != NULL)
                return -1;
-
-       /* Verify that the device name is properly formed. */
-       if (!((strlen(path) == 16 && (flags & DM_OPENPART)) ||
-           (strlen(path) == 18 && path[16] == '.')))
+       len = strlen(path);
+       if (len > 18)
                return -1;
+       if ((len <= 2 || path[len - 2] != '.') && !(flags & DM_OPENPART))
+               return -1;
+       if (len > 2 && path[len - 2] == '.')
+               len -= 2;

        /* Get partition. */
        if (flags & DM_OPENPART)
                part = 'a' + RAW_PART;
        else
-               part = path[17];
+               part = path[len + 1];

        if (part < 'a' || part >= 'a' + MAXPARTITIONS)
                return -1;

-       /* Derive label UID. */
-       bzero(uid, sizeof(uid));
-       for (i = 0; i < 16; i++) {
-               c = path[i];
-               if (c >= '0' && c <= '9')
-                       c -= '0';
-               else if (c >= 'a' && c <= 'f')
-                       c -= ('a' - 10);
-                else
-                       return -1;
-
-               uid[i / 2] <<= 4;
-               uid[i / 2] |= c & 0xf;
-       }
+       /* Decode UID or name. */
+       if (len == 16) {
+               for (i = 0; i < 16; i++) {
+                       c = path[i];
+                       if (c >= '0' && c <= '9')
+                               c -= '0';
+                       else if (c >= 'a' && c <= 'f')
+                               c -= ('a' - 10);
+                       else
+                               return -1;
+
+                       uid[i / 2] <<= 4;
+                       uid[i / 2] |= c & 0xf;
+               }
+       } else if (len <= 10) {
+               u_char *p = uid;
+               int v = 0;
+
+               /* This is basically base64: read in 4 chars, shift out 3. */
+               bzero(uid, sizeof(uid));
+               for (i = 0; i < len; i++) {
+                       c = path[i];
+                       if (c >= '0' && c <= '9')
+                               c -= '0' - 1;
+                       else if (c >= 'A' && c <= 'A')
+                               c -= 'A' - 11;
+                       else if (c >= 'a' && c <= 'z')
+                               c -= 'a' - 37;
+                       else if (c == '_')
+                               c = 63;
+                       else
+                               return -1;
+
+                       v = (v << 6) + c;
+                       if (i % 4 == 3) {
+                               *(p++) = v >> 16;
+                               *(p++) = v >> 8;
+                               *(p++) = v;
+                               v = 0;
+                       }
+               }
+
+               /* When we reach the end of the name we can have at most two
+                * bytes left to output, so just write them. This should be
+                * safe, since the loop above only writes up to uid[5]. */
+               if (v != 0) {
+                       v <<= 6 * (4 - (i % 4));
+                       *(p++) = v >> 16;
+                       *(p++) = v >> 8;
+
+                       /* Flush last byte if stopping prematurely. */
+                       if (i < 10)
+                               *(p++) = v;
+               }
+       } else
+               return -1;

        mdk = NULL;
        TAILQ_FOREACH(dk, &disklist, dk_link) {
Index: disklabel.c
===================================================================
RCS file: /cvs/src/sbin/disklabel/disklabel.c,v
retrieving revision 1.175
diff -u -r1.175 disklabel.c
--- disklabel.c 13 Dec 2010 01:01:41 -0000      1.175
+++ disklabel.c 22 Jun 2011 16:40:40 -0000
@@ -783,9 +783,9 @@
            lp->d_typename);
        fprintf(f, "label: %.*s\n", (int)sizeof(lp->d_packname),
            lp->d_packname);
-       fprintf(f, "duid: %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\n",
-            lp->d_uid[0], lp->d_uid[1], lp->d_uid[2], lp->d_uid[3],
-            lp->d_uid[4], lp->d_uid[5], lp->d_uid[6], lp->d_uid[7]);
+       fprintf(f, "duid: ");
+       duid_print(f, lp);
+       fprintf(f, "\n");
        fprintf(f, "flags:");
        if (lp->d_flags & D_BADSECT)
                fprintf(f, " badsect");
@@ -990,26 +990,107 @@
        u_char duid[8];
        char c;
        int i;
+       size_t len;
+
+       len = strlen(s);
+       if (len == 16) {
+               for (i = 0; i < 16; i++) {
+                       c = s[i];
+                       if (c >= '0' && c <= '9')
+                               c -= '0';
+                       else if (c >= 'a' && c <= 'f')
+                               c -= ('a' - 10);
+                       else if (c >= 'A' && c <= 'F')
+                               c -= ('A' - 10);
+                       else
+                               return -1;
+                       duid[i / 2] <<= 4;
+                       duid[i / 2] |= c & 0xf;
+               }
+       } else if (len <= 10) {
+               u_char *p = duid;
+               int v = 0;
+
+               /* This is basically base64: read in 4 chars, shift out 3. */
+               bzero(duid, sizeof(duid));
+               for (i = 0; i < len; i++) {
+                       c = s[i];
+                       if (c >= '0' && c <= '9')
+                               c -= '0' - 1;
+                       else if (c >= 'A' && c <= 'A')
+                               c -= 'A' - 11;
+                       else if (c >= 'a' && c <= 'z')
+                               c -= 'a' - 37;
+                       else if (c == '_')
+                               c = 63;
+                       else
+                               return -1;
+
+                       v = (v << 6) + c;
+                       if (i % 4 == 3) {
+                               *(p++) = v >> 16;
+                               *(p++) = v >> 8;
+                               *(p++) = v;
+                               v = 0;
+                       }
+               }

-       if (strlen(s) != 16)
+               /* When we reach the end of the name we can have at most two
+                * bytes left to output, so just write them. This should be
+                * safe, since the loop above only writes up to uid[5]. */
+               if (v != 0) {
+                       v <<= 6 * (4 - (i % 4));
+                       *(p++) = v >> 16;
+                       *(p++) = v >> 8;
+
+                       /* Flush last byte if stopping prematurely. */
+                       if (i < 10)
+                               *(p++) = v;
+               }
+       } else
                return -1;

-       for (i = 0; i < 16; i++) {
-               c = s[i];
-               if (c >= '0' && c <= '9')
-                       c -= '0';
-               else if (c >= 'a' && c <= 'f')
-                       c -= ('a' - 10);
-               else if (c >= 'A' && c <= 'F')
-                       c -= ('A' - 10);
+       memcpy(lp->d_uid, &duid, sizeof(lp->d_uid));
+       return 0;
+}
+
+void
+duid_print(FILE *f, struct disklabel *lp)
+{
+       char name[11];
+       int i, v;
+       u_char c, *p;
+
+       p = lp->d_uid;
+       bzero(name, sizeof(name));
+       for (i = 0; i < 10; i++) {
+               if (i % 4 == 0) {
+                       v  = ((int) *(p++) << 16);
+                       v += ((int) *(p++) <<  8);
+                       if (i < 8)
+                               v += *(p++);
+               }
+
+               c = (v >> 6 * 3) & 0x3f;
+               v <<= 6;
+               if (c == 63)
+                       c = '_';
+               else if (c >= 37)
+                       c += 'a' - 37;
+               else if (c >= 11)
+                       c += 'A' - 11;
+               else if (c >= 1)
+                       c += '0' - 1;
                else
-                       return -1;
-               duid[i / 2] <<= 4;
-               duid[i / 2] |= c & 0xf;
+                       break;
+
+               name[i] = c;
        }

-       memcpy(lp->d_uid, &duid, sizeof(lp->d_uid));
-       return 0;
+       fprintf(f, "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx ('%s')",
+           lp->d_uid[0], lp->d_uid[1], lp->d_uid[2], lp->d_uid[3],
+           lp->d_uid[4], lp->d_uid[5], lp->d_uid[6], lp->d_uid[7],
+           name);
 }

 /*
Index: editor.c
===================================================================
RCS file: /cvs/src/sbin/disklabel/editor.c,v
retrieving revision 1.248
diff -u -r1.248 editor.c
--- editor.c    19 Feb 2011 21:18:59 -0000      1.248
+++ editor.c    22 Jun 2011 16:40:41 -0000
@@ -1619,14 +1619,14 @@
        char *s;
        int i;

-       printf("The disklabel UID is currently: "
-           "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\n",
-            lp->d_uid[0], lp->d_uid[1], lp->d_uid[2], lp->d_uid[3],
-            lp->d_uid[4], lp->d_uid[5], lp->d_uid[6], lp->d_uid[7]);
+       printf("The disklabel UID is currently: ");
+       duid_print(stdout, lp);
+       printf("\n");

        do {
-               s = getstring("duid", "The disklabel UID, given as a 16 "
-                   "character hexadecimal string.", NULL);
+               s = getstring("duid", "The disklabel UID, given as a 10 "
+                   "character name or 16 character hexadecimal string.",
+                   NULL);
                if (s == NULL || strlen(s) == 0) {
                        fputs("Command aborted\n", stderr);
                        return;
Index: extern.h
===================================================================
RCS file: /cvs/src/sbin/disklabel/extern.h,v
retrieving revision 1.21
diff -u -r1.21 extern.h
--- extern.h    24 Nov 2010 14:15:31 -0000      1.21
+++ extern.h    22 Jun 2011 16:40:41 -0000
@@ -26,6 +26,7 @@
 void   display(FILE *, struct disklabel *, char, int);
 void   display_partition(FILE *, struct disklabel *, int, char);
 int    duid_parse(struct disklabel *, char *);
+void   duid_print(FILE *, struct disklabel *);

 void   readlabel(int);
 struct disklabel *makebootarea(char *, struct disklabel *, int);
Index: duid.c
===================================================================
RCS file: /cvs/src/lib/libutil/duid.c,v
retrieving revision 1.1
diff -u -r1.1 duid.c
--- duid.c      15 Nov 2010 15:07:40 -0000      1.1
+++ duid.c      22 Jun 2011 16:40:48 -0000
@@ -23,18 +23,34 @@
 {
        char c;
        int i;
+       size_t len;

        /* Basic format check. */
-       if (!((strlen(duid) == 16 && (dflags & OPENDEV_PART)) ||
-           (strlen(duid) == 18 && duid[16] == '.')))
+       len = strlen(duid);
+       if (len > 18 ||
+           ((len <= 2 || duid[(int) len - 2] != '.') && !(dflags & 
OPENDEV_PART)))
                return 0;
+       if (len > 2 && duid[(int) len - 2] == '.')
+               len -= 2;

        /* Check UID. */
-       for (i = 0; i < 16; i++) {
-               c = duid[i];
-               if ((c < '0' || c > '9') && (c < 'a' || c > 'f'))
-                       return 0;
-       }
+       if (len == 16) {
+               for (i = 0; i < 16; i++) {
+                       c = duid[i];
+                       if ((c < '0' || c > '9') && (c < 'a' || c > 'f'))
+                               return 0;
+               }
+       } else if (len <= 10) {
+               for (i = 0; i < (int) len; i++) {
+                       c = duid[i];
+                       if ((c < '0' || c > '9') &&
+                           (c < 'A' || c > 'Z') &&
+                           (c < 'a' || c > 'z') &&
+                           (c != '_'))
+                               return 0;
+               }
+       } else
+               return 0;

        return 1;
 }
Index: isduid.3
===================================================================
RCS file: /cvs/src/lib/libutil/isduid.3,v
retrieving revision 1.1
diff -u -r1.1 isduid.3
--- isduid.3    17 Nov 2010 10:10:31 -0000      1.1
+++ isduid.3    22 Jun 2011 16:40:48 -0000
@@ -19,7 +19,7 @@
 .Os
 .Sh NAME
 .Nm isduid
-.Nd disklabel UID test
+.Nd disk name or disklabel UID test
 .Sh SYNOPSIS
 .Fd #include <util.h>
 .Ft int
@@ -29,7 +29,7 @@
 .Fn isduid
 function tests the string
 .Fa duid
-to see if it is a valid
+to see if it is a valid disk name or
 .Xr disklabel 8
 UID.
 The
@@ -37,11 +37,17 @@
 are specified using the same flags as used by
 .Xr opendev 3 .
 .Pp
-If the OPENDEV_PART flag is included in
+When
+.Fa duid
+is intended as a disk name, it can be up to 10 characters long and must
+consist only of alphabetical and numeric characters and the character
+.Sq \. .
+When intended as a disklabel UID,
+.Fa duid
+must consist of a 16-character hexadecimal string.
+If the OPENDEV_PART flag is not included in
 .Fa dflags
-the disklabel UID must consist of a 16-character hexadecimal string.
-Otherwise the disklabel UID must consist of a 16-character hexidecimal
string
-followed by a
+the disk name or disklabel UID must be followed by a
 .Sq \&.
 and a partition letter.
 .Sh RETURN VALUES

Reply via email to