opr: Add UUID handling functions
authorSimon Wilkinson <sxw@your-file-system.com>
Tue, 17 Jul 2012 15:50:59 +0000 (16:50 +0100)
committerDerrick Brashear <shadow@dementix.org>
Tue, 14 Aug 2012 19:53:08 +0000 (12:53 -0700)
Add a set of functions to the opr library to handle creating and
manipulating UUIDs.

The opr_uuid_t type is held as a 16 octet character string, which
comprises the UUID encoded into network byte order. This is the
primary form for manipulating UUIDs with this library, as it avoids
any nbo/hbo problems.

For applications which require raw access to the UUID components,
the opr_uuid_unpacked structure is provided, and
opr_uuid_pack/opr_uuid_unpack can be used to convert to and from
this format.

Finally, functions to print the UUID as a string, and parse a UUID
from a string, are provided. When printing, we use the standard UUID
format of 000000-0000-0000-0000-00000000. However, the afsUUID library
used to print UUIDs as 000000-0000-0000-00-00-00000000, so we also
accept this format.

Change-Id: I78ef79b7ab8ae15fb955c6495118722875c94f8d
Reviewed-on: http://gerrit.openafs.org/7977
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Derrick Brashear <shadow@dementix.org>

Makefile.in
acinclude.m4
src/opr/Makefile.in
src/opr/NTMakefile
src/opr/uuid.c [new file with mode: 0644]
src/opr/uuid.h [new file with mode: 0644]
tests/TESTS
tests/opr/Makefile.in
tests/opr/uuid-t.c [new file with mode: 0644]

index 672e53d..22e3ca0 100644 (file)
@@ -149,7 +149,7 @@ config: prelude
 procmgmt: $(DIR_roken) config
        +${COMPILE_PART1} procmgmt ${COMPILE_PART2}
 
-opr: config $(DIR_roken)
+opr: config hcrypto $(DIR_roken)
        +${COMPILE_PART1} opr ${COMPILE_PART2}
 
 util: opr $(DIR_roken) procmgmt hcrypto lwp_depinstall rx_depinstall
index 3855509..7e5c41f 100644 (file)
@@ -1858,8 +1858,14 @@ LIB_hcrypto="-lafshcrypto"
 LDFLAGS_hcrypto="-L\$(TOP_LIBDIR)"
 AC_SUBST(LIB_hcrypto)
 AC_SUBST(LDFLAGS_hcrypto)
+
+dnl Check for UUID library
+AC_CHECK_HEADERS([uuid/uuid.h])
+AC_CHECK_LIB(uuid, uuid_generate, LIBS_uuid="-luuid")
+AC_CHECK_FUNCS([uuid_generate])
 ])
 
+
 AC_DEFUN([SUMMARY], [
     # Print a configuration summary
 echo 
index 6640779..11af649 100644 (file)
@@ -2,14 +2,15 @@ srcdir=@srcdir@
 include @TOP_OBJDIR@/src/config/Makefile.config
 include @TOP_OBJDIR@/src/config/Makefile.shared
 
-objects = assert.o casestrcpy.o rbtree.o
+objects = assert.o casestrcpy.o rbtree.o uuid.o
 
 HEADERS = $(TOP_INCDIR)/afs/opr.h \
          $(TOP_INCDIR)/afs/opr_assert.h \
          $(TOP_INCDIR)/opr/jhash.h \
          $(TOP_INCDIR)/opr/queue.h \
          $(TOP_INCDIR)/opr/rbtree.h \
-         $(TOP_INCDIR)/opr/time.h
+         $(TOP_INCDIR)/opr/time.h \
+         $(TOP_INCDIR)/opr/uuid.h
 
 all: $(HEADERS) $(TOP_LIBDIR)/libopr.a
 
@@ -39,6 +40,9 @@ $(TOP_INCDIR)/opr/rbtree.h: ${srcdir}/rbtree.h
 $(TOP_INCDIR)/opr/time.h: ${srcdir}/opr_time.h
        $(INSTALL_DATA) $? $@
 
+$(TOP_INCDIR)/opr/uuid.h: ${srcdir}/uuid.h
+       $(INSTALL_DATA) $? $@
+
 clean:
        rm -f $(objects) libopr.a
 
index 0eeefda..aec589a 100644 (file)
@@ -16,7 +16,8 @@ INCFILES = \
        $(DESTDIR)\include\opr\jhash.h \
        $(DESTDIR)\include\opr\queue.h \
        $(DESTDIR)\include\opr\rbtree.h \
-       $(DESTDIR)\include\opr\time.h
+       $(DESTDIR)\include\opr\time.h \
+       $(DESTDIR)\include\opr\uuid.h
 
 $(DESTDIR)\include\opr\time.h: opr_time.h
         $(COPY) $** $@
@@ -27,6 +28,7 @@ LIBOBJS = \
        $(OUT)\assert.obj \
        $(OUT)\casestrcpy.obj \
        $(OUT)\rbtree.obj \
+       $(OUT)\uuid.obj \
        $(OUT)\AFS_component_version_number.obj
 
 $(LIBOBJS): $(INCFILES)
diff --git a/src/opr/uuid.c b/src/opr/uuid.c
new file mode 100644 (file)
index 0000000..451712d
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2012 Your File System Inc. All rights reserved.
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+#include <roken.h>
+
+#ifdef HAVE_UUID_UUID_H
+# include <uuid/uuid.h>
+#endif
+
+#ifdef AFS_NT40_ENV
+# include <rpc.h>
+#endif
+
+#include <hcrypto/rand.h>
+
+#include "uuid.h"
+#include "jhash.h"
+
+static const opr_uuid_t nilUid = {{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}};
+
+void
+opr_uuid_create(opr_uuid_t *uuid)
+{
+#if defined (AFS_NT40_ENV)
+    struct opr_uuid_unpacked raw;
+
+    UuidCreate((UUID *) &raw);
+    opr_uuid_pack(uuid, &raw);
+
+#elif !defined(KERNEL) && defined(HAVE_UUID_GENERATE)
+
+    uuid_generate(uuid->data);
+
+#else
+    RAND_bytes(uuid->data, 16);
+
+    uuid->data[6] = (uuid->data[6] & 0x0F) | 0x40; /* verison is 4 */
+    uuid->data[8] = (uuid->data[8] & 0x3F) | 0x80; /* variant is DCE */
+#endif
+}
+
+int
+opr_uuid_isNil(const opr_uuid_t *uuid)
+{
+   return opr_uuid_equal(uuid, &nilUid);
+}
+
+int
+opr_uuid_equal(const opr_uuid_t *uuid1, const opr_uuid_t *uuid2)
+{
+   return memcmp(uuid1, uuid2, sizeof(opr_uuid_t)) == 0;
+}
+
+int
+opr_uuid_hash(const opr_uuid_t *uuid)
+{
+   /* uuid->data is a (unsigned char *), so there's every danger that this
+    * may cause an unaligned access on some platforms */
+   return opr_jhash((const afs_uint32 *)uuid->data, 4, 0);
+}
+
+#if !defined(KERNEL)
+void
+opr_uuid_toString(const opr_uuid_t *uuid, char **string)
+{
+   unsigned const char *p;
+
+   p = uuid->data;
+   asprintf(string,
+           "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
+           "%02x%02x%02x%02x%02x%02x",
+           p[0], p[1], p[2],  p[3],  p[4],  p[5],  p[6],  p[7],
+           p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+}
+
+void
+opr_uuid_freeString(char *string)
+{
+    free(string);
+}
+
+int
+opr_uuid_fromString(opr_uuid_t *uuid, const char *string)
+{
+    unsigned int i[16];
+    int items, c;
+
+    /* XXX - Traditionally, AFS has printed UUIDs as
+     * 00000000-0000-00-00-00000000. We should perhaps also accept
+     * that format here
+     */
+    items = sscanf(string,
+                  "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
+                  "%02x%02x%02x%02x%02x%02x",
+                  &i[0],  &i[1],  &i[2],  &i[3], &i[4],  &i[5],
+                  &i[6],  &i[7],  &i[8],  &i[9], &i[10], &i[11],
+                  &i[12], &i[13], &i[14], &i[15]);
+    if (items !=16) {
+       /* Originally, AFS's printed UUIDs would take the form
+        * 00000000-0000-0000-00-00-00000000. Also handle this. */
+       items = sscanf(string,
+                  "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x-"
+                  "%02x%02x%02x%02x%02x%02x",
+                  &i[0],  &i[1],  &i[2],  &i[3], &i[4],  &i[5],
+                  &i[6],  &i[7],  &i[8],  &i[9], &i[10], &i[11],
+                  &i[12], &i[13], &i[14], &i[15]);
+    }
+    if (items !=16)
+       return EINVAL;
+
+    for (c=0; c<16; c++)
+       uuid->data[c] = i[c];
+
+    return 0;
+}
+
+void
+opr_uuid_pack(opr_uuid_t *uuid, const struct opr_uuid_unpacked *raw)
+{
+    afs_uint32 intval;
+    unsigned short shortval;
+
+    intval = htonl(raw->time_low);
+    memcpy(&uuid->data[0], &intval, sizeof(uint32_t));
+
+    shortval = htons(raw->time_mid);
+    memcpy(&uuid->data[4], &shortval, sizeof(uint16_t));
+
+    shortval = htons(raw->time_hi_and_version);
+    memcpy(&uuid->data[6], &shortval, sizeof(uint16_t));
+
+    uuid->data[8] = raw->clock_seq_hi_and_reserved;
+    uuid->data[9] = raw->clock_seq_low;
+
+    memcpy(&uuid->data[10], &raw->node, 6);
+}
+
+void
+opr_uuid_unpack(const opr_uuid_t *uuid, struct opr_uuid_unpacked *raw)
+{
+    afs_uint32 intval;
+    unsigned short shortval;
+
+    memcpy(&intval, &uuid->data[0], sizeof(uint32_t));
+    raw->time_low = ntohl(intval);
+
+    memcpy(&shortval, &uuid->data[4], sizeof(uint16_t));
+    raw->time_mid = ntohs(shortval);
+
+    memcpy(&shortval, &uuid->data[6], sizeof(uint16_t));
+    raw->time_hi_and_version = ntohs(shortval);
+
+    raw->clock_seq_hi_and_reserved = uuid->data[8];
+    raw->clock_seq_low = uuid->data[9];
+
+    memcpy(&raw->node, &uuid->data[10], 6);
+}
+
+#endif
diff --git a/src/opr/uuid.h b/src/opr/uuid.h
new file mode 100644 (file)
index 0000000..c69711b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012 Your File System Inc. All rights reserved.
+ */
+
+#ifndef OPENAFS_OPR_UUID_H
+#define OPENAFS_OPR_UUID_H 1
+
+struct opr_uuid {
+    unsigned char data[16];
+};
+
+struct opr_uuid_unpacked {
+    afs_uint32 time_low;
+    unsigned short time_mid;
+    unsigned short time_hi_and_version;
+    char clock_seq_hi_and_reserved;
+    char clock_seq_low;
+    char node[6];
+};
+
+typedef struct opr_uuid opr_uuid_t;
+typedef opr_uuid_t opr_uuid; /* For XDR */
+
+extern void opr_uuid_create(opr_uuid_t *uuid);
+extern int opr_uuid_isNil(const opr_uuid_t *uuid);
+extern int opr_uuid_equal(const opr_uuid_t *uuid1, const opr_uuid_t *uuid2);
+extern int opr_uuid_hash(const opr_uuid_t *uuid);
+
+#if !defined(KERNEL)
+extern void opr_uuid_toString(const opr_uuid_t *uuid, char **string);
+extern void opr_uuid_freeString(char *string);
+extern int opr_uuid_fromString(opr_uuid_t *uuid, const char *string);
+#endif
+
+extern void opr_uuid_pack(opr_uuid_t *uuid, const struct opr_uuid_unpacked *raw);
+extern void opr_uuid_unpack(const opr_uuid_t *uuid, struct opr_uuid_unpacked *raw);
+
+#endif
index 4ed9809..59c4fb3 100644 (file)
@@ -9,6 +9,7 @@ opr/jhash
 opr/queues
 opr/rbtree
 opr/time
+opr/uuid
 ptserver/pt_util
 ptserver/pts-man
 rx/event
index dccdb58..e276dc4 100644 (file)
@@ -7,7 +7,7 @@ MODULE_CFLAGS = -I$(srcdir)/../..
 
 LIBS=../tap/libtap.a $(abs_top_builddir)/lib/libopr.a
 
-tests = jhash-t queues-t rbtree-t time-t
+tests = jhash-t queues-t rbtree-t time-t uuid-t
 
 all check test tests: $(tests)
 
@@ -23,5 +23,8 @@ jhash-t: jhash-t.o
 time-t: time-t.o
        $(AFS_LDRULE) time-t.o ../tap/libtap.a $(XLIBS)
 
+uuid-t: uuid-t.o
+       $(AFS_LDRULE) uuid-t.o ../tap/libtap.a $(LIBS) $(LIB_hcrypto) $(XLIBS)
+
 clean distclean:
        $(RM) -f $(tests) *.o core
diff --git a/tests/opr/uuid-t.c b/tests/opr/uuid-t.c
new file mode 100644 (file)
index 0000000..e66721e
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012 Your File System Inc. All rights reserved.
+ */
+
+#include <afsconfig.h>
+#include <afs/param.h>
+
+#include <roken.h>
+
+#include <opr/uuid.h>
+
+#include <tests/tap/basic.h>
+
+int
+main(int argc, char **argv)
+{
+    opr_uuid_t uuidA = {{0x4F, 0x44, 0x94, 0x47, 0x76, 0xBA, 0x47, 0x2C,
+                         0x97, 0x1A, 0x86, 0x6B, 0xC0, 0x10, 0x1A, 0x4B}};
+    opr_uuid_t uuidB = {{0x5D, 0x2A, 0x39, 0x36, 0x94, 0xB2, 0x48, 0x90,
+                         0xA8, 0xD2, 0x7F, 0xBC, 0x1B, 0x29, 0xDA, 0x9B}};
+    opr_uuid_t uuidC;
+    char *str;
+    int version;
+    struct opr_uuid_unpacked raw;
+
+    memset(&uuidC, 0, sizeof(opr_uuid_t));
+
+    ok(opr_uuid_isNil(&uuidC), "opr_uuid_isNil(nilUuid) works");
+    ok(!opr_uuid_isNil(&uuidA), "opr_uuid_isNil(uuid) works");
+
+    ok(opr_uuid_equal(&uuidA, &uuidA), "opr_uuid_equal(A, A) works");
+    ok(!opr_uuid_equal(&uuidA, &uuidB), "opr_uuid_equal(A, B) works");
+    ok(!opr_uuid_equal(&uuidA, &uuidC), "opr_uuid_equal(A, nilUid) works");
+
+    is_int(1187447773, opr_uuid_hash(&uuidA), "opr_uuid_hash(A) works");
+    is_int(1251907497, opr_uuid_hash(&uuidB), "opr_uuid_hash(B) works");
+
+    opr_uuid_toString(&uuidA, &str);
+    is_string("4f449447-76ba-472c-971a-866bc0101a4b", str,
+             "opr_uuid_toString(uuidA) works");
+    opr_uuid_freeString(str);
+
+    is_int(0, opr_uuid_fromString(&uuidC, "4F449447-76BA-472C-971A-866BC0101A4B"),
+          "opr_uuid_fromString works with conventional UUID");
+    ok(opr_uuid_equal(&uuidA, &uuidC), " ... and UUID is correct");
+
+    memset(&uuidC, 0, sizeof(opr_uuid_t));
+    is_int(0, opr_uuid_fromString(&uuidC, "4F449447-76BA-472C-97-1A-866BC0101A4B"),
+          "opr_uuid_fromString works with AFS style UUID");
+    ok(opr_uuid_equal(&uuidA, &uuidC), " ... and UUID is correct");
+
+    memset(&uuidC, 0, sizeof(opr_uuid_t));
+    opr_uuid_create(&uuidC);
+    ok(!opr_uuid_isNil(&uuidC), "opr_uuid_create makes non-nil UUID");
+    is_int(0x80, uuidC.data[8] & 0xB0, "variant is DCE as expected");
+    version = (uuidC.data[6] & 0xF0) >> 4;
+    ok(version >=1 && version <=5, "version %d is in expected range", version);
+
+    opr_uuid_unpack(&uuidB, &raw);
+    opr_uuid_pack(&uuidC, &raw);
+    ok(opr_uuid_equal(&uuidB, &uuidC),
+       "opr_uuid_pack(opr_uuid_unpack()) works as expected");
+
+    return 0;
+}