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