busybox/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * gen_uuid.c --- generate a DCE-compatible uuid
   4 *
   5 * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
   6 *
   7 * %Begin-Header%
   8 * Redistribution and use in source and binary forms, with or without
   9 * modification, are permitted provided that the following conditions
  10 * are met:
  11 * 1. Redistributions of source code must retain the above copyright
  12 *    notice, and the entire permission notice in its entirety,
  13 *    including the disclaimer of warranties.
  14 * 2. Redistributions in binary form must reproduce the above copyright
  15 *    notice, this list of conditions and the following disclaimer in the
  16 *    documentation and/or other materials provided with the distribution.
  17 * 3. The name of the author may not be used to endorse or promote
  18 *    products derived from this software without specific prior
  19 *    written permission.
  20 *
  21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
  24 * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
  25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
  27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  31 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
  32 * DAMAGE.
  33 * %End-Header%
  34 */
  35
  36#include <unistd.h>
  37#include <stdlib.h>
  38#include <string.h>
  39#include <fcntl.h>
  40#include <errno.h>
  41#include <sys/types.h>
  42#include <sys/stat.h>
  43#include <sys/file.h>
  44#include <sys/time.h>
  45#ifdef HAVE_SYS_IOCTL_H
  46#include <sys/ioctl.h>
  47#endif
  48#include <sys/socket.h>
  49#ifdef HAVE_SYS_SOCKIO_H
  50#include <sys/sockio.h>
  51#endif
  52#ifdef HAVE_NET_IF_H
  53#include <net/if.h>
  54#endif
  55#ifdef HAVE_NETINET_IN_H
  56#include <netinet/in.h>
  57#endif
  58#ifdef HAVE_NET_IF_DL_H
  59#include <net/if_dl.h>
  60#endif
  61
  62#include "uuidP.h"
  63
  64#ifdef HAVE_SRANDOM
  65#define srand(x)        srandom(x)
  66#define rand()          random()
  67#endif
  68
  69static int get_random_fd(void)
  70{
  71        struct timeval  tv;
  72        static int      fd = -2;
  73        int             i;
  74
  75        if (fd == -2) {
  76                gettimeofday(&tv, 0);
  77                fd = open("/dev/urandom", O_RDONLY);
  78                if (fd == -1)
  79                        fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
  80                srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
  81        }
  82        /* Crank the random number generator a few times */
  83        gettimeofday(&tv, 0);
  84        for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
  85                rand();
  86        return fd;
  87}
  88
  89
  90/*
  91 * Generate a series of random bytes.  Use /dev/urandom if possible,
  92 * and if not, use srandom/random.
  93 */
  94static void get_random_bytes(void *buf, int nbytes)
  95{
  96        int i, n = nbytes, fd = get_random_fd();
  97        int lose_counter = 0;
  98        unsigned char *cp = (unsigned char *) buf;
  99
 100        if (fd >= 0) {
 101                while (n > 0) {
 102                        i = read(fd, cp, n);
 103                        if (i <= 0) {
 104                                if (lose_counter++ > 16)
 105                                        break;
 106                                continue;
 107                        }
 108                        n -= i;
 109                        cp += i;
 110                        lose_counter = 0;
 111                }
 112        }
 113
 114        /*
 115         * We do this all the time, but this is the only source of
 116         * randomness if /dev/random/urandom is out to lunch.
 117         */
 118        for (cp = buf, i = 0; i < nbytes; i++)
 119                *cp++ ^= (rand() >> 7) & 0xFF;
 120}
 121
 122/*
 123 * Get the ethernet hardware address, if we can find it...
 124 */
 125static int get_node_id(unsigned char *node_id)
 126{
 127#ifdef HAVE_NET_IF_H
 128        int             sd;
 129        struct ifreq    ifr, *ifrp;
 130        struct ifconf   ifc;
 131        char buf[1024];
 132        int             n, i;
 133        unsigned char   *a;
 134#ifdef HAVE_NET_IF_DL_H
 135        struct sockaddr_dl *sdlp;
 136#endif
 137
 138/*
 139 * BSD 4.4 defines the size of an ifreq to be
 140 * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
 141 * However, under earlier systems, sa_len isn't present, so the size is
 142 * just sizeof(struct ifreq)
 143 */
 144#ifdef HAVE_SA_LEN
 145#ifndef max
 146#define max(a,b) ((a) > (b) ? (a) : (b))
 147#endif
 148#define ifreq_size(i) max(sizeof(struct ifreq),\
 149     sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
 150#else
 151#define ifreq_size(i) sizeof(struct ifreq)
 152#endif /* HAVE_SA_LEN*/
 153
 154        sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
 155        if (sd < 0) {
 156                return -1;
 157        }
 158        memset(buf, 0, sizeof(buf));
 159        ifc.ifc_len = sizeof(buf);
 160        ifc.ifc_buf = buf;
 161        if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
 162                close(sd);
 163                return -1;
 164        }
 165        n = ifc.ifc_len;
 166        for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
 167                ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
 168                strncpy_IFNAMSIZ(ifr.ifr_name, ifrp->ifr_name);
 169#ifdef SIOCGIFHWADDR
 170                if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
 171                        continue;
 172                a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
 173#else
 174#ifdef SIOCGENADDR
 175                if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
 176                        continue;
 177                a = (unsigned char *) ifr.ifr_enaddr;
 178#else
 179#ifdef HAVE_NET_IF_DL_H
 180                sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
 181                if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
 182                        continue;
 183                a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
 184#else
 185                /*
 186                 * XXX we don't have a way of getting the hardware
 187                 * address
 188                 */
 189                close(sd);
 190                return 0;
 191#endif /* HAVE_NET_IF_DL_H */
 192#endif /* SIOCGENADDR */
 193#endif /* SIOCGIFHWADDR */
 194                if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
 195                        continue;
 196                if (node_id) {
 197                        memcpy(node_id, a, 6);
 198                        close(sd);
 199                        return 1;
 200                }
 201        }
 202        close(sd);
 203#endif
 204        return 0;
 205}
 206
 207/* Assume that the gettimeofday() has microsecond granularity */
 208#define MAX_ADJUSTMENT 10
 209
 210static int get_clock(uint32_t *clock_high, uint32_t *clock_low, uint16_t *ret_clock_seq)
 211{
 212        static int                      adjustment = 0;
 213        static struct timeval           last = {0, 0};
 214        static uint16_t                 clock_seq;
 215        struct timeval                  tv;
 216        unsigned long long              clock_reg;
 217
 218try_again:
 219        gettimeofday(&tv, 0);
 220        if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
 221                get_random_bytes(&clock_seq, sizeof(clock_seq));
 222                clock_seq &= 0x3FFF;
 223                last = tv;
 224                last.tv_sec--;
 225        }
 226        if ((tv.tv_sec < last.tv_sec) ||
 227            ((tv.tv_sec == last.tv_sec) &&
 228             (tv.tv_usec < last.tv_usec))) {
 229                clock_seq = (clock_seq+1) & 0x3FFF;
 230                adjustment = 0;
 231                last = tv;
 232        } else if ((tv.tv_sec == last.tv_sec) &&
 233            (tv.tv_usec == last.tv_usec)) {
 234                if (adjustment >= MAX_ADJUSTMENT)
 235                        goto try_again;
 236                adjustment++;
 237        } else {
 238                adjustment = 0;
 239                last = tv;
 240        }
 241
 242        clock_reg = tv.tv_usec*10 + adjustment;
 243        clock_reg += ((unsigned long long) tv.tv_sec)*10000000;
 244        clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
 245
 246        *clock_high = clock_reg >> 32;
 247        *clock_low = clock_reg;
 248        *ret_clock_seq = clock_seq;
 249        return 0;
 250}
 251
 252void uuid_generate_time(uuid_t out)
 253{
 254        static unsigned char node_id[6];
 255        static int has_init = 0;
 256        struct uuid uu;
 257        uint32_t        clock_mid;
 258
 259        if (!has_init) {
 260                if (get_node_id(node_id) <= 0) {
 261                        get_random_bytes(node_id, 6);
 262                        /*
 263                         * Set multicast bit, to prevent conflicts
 264                         * with IEEE 802 addresses obtained from
 265                         * network cards
 266                         */
 267                        node_id[0] |= 0x01;
 268                }
 269                has_init = 1;
 270        }
 271        get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
 272        uu.clock_seq |= 0x8000;
 273        uu.time_mid = (uint16_t) clock_mid;
 274        uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
 275        memcpy(uu.node, node_id, 6);
 276        uuid_pack(&uu, out);
 277}
 278
 279void uuid_generate_random(uuid_t out)
 280{
 281        uuid_t  buf;
 282        struct uuid uu;
 283
 284        get_random_bytes(buf, sizeof(buf));
 285        uuid_unpack(buf, &uu);
 286
 287        uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
 288        uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
 289        uuid_pack(&uu, out);
 290}
 291
 292/*
 293 * This is the generic front-end to uuid_generate_random and
 294 * uuid_generate_time.  It uses uuid_generate_random only if
 295 * /dev/urandom is available, since otherwise we won't have
 296 * high-quality randomness.
 297 */
 298void uuid_generate(uuid_t out)
 299{
 300        if (get_random_fd() >= 0)
 301                uuid_generate_random(out);
 302        else
 303                uuid_generate_time(out);
 304}
 305