blob: 3fb88a3ca1bb73c687de45731bb871f2cb5c0d07 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
/*
* Copyright (C) 2023 Norbert Lange <nolange79@gmail.com>
*/
#include <string.h>
#include <errno.h>
#include "erofs/config.h"
#include "erofs/defs.h"
#include "liberofs_uuid.h"
#ifdef HAVE_LIBUUID
#include <uuid.h>
#else
#include <stdlib.h>
#ifdef HAVE_SYS_RANDOM_H
#include <sys/random.h>
#else
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#endif
/* Flags to be used, will be modified if kernel does not support them */
static unsigned int erofs_grnd_flag =
#ifdef GRND_INSECURE
GRND_INSECURE;
#else
0x0004;
#endif
static int s_getrandom(void *out, unsigned size, bool insecure)
{
unsigned int kflags = erofs_grnd_flag;
unsigned int flags = insecure ? kflags : 0;
for (;;)
{
ssize_t r;
int err;
#ifdef HAVE_SYS_RANDOM_H
r = getrandom(out, size, flags);
#elif defined(__NR_getrandom)
r = (ssize_t)syscall(__NR_getrandom, out, size, flags);
#else
r = -1;
errno = ENOSYS;
(void)flags;
#endif
if (r == size)
break;
err = errno;
if (err != EINTR) {
if (__erofs_unlikely(err == ENOSYS && insecure)) {
while (size) {
*(u8 *)out++ = rand() % 256;
--size;
}
err = 0;
} else if (err == EINVAL && kflags) {
// Kernel likely does not support GRND_INSECURE
erofs_grnd_flag = 0;
kflags = 0;
continue;
}
return -err;
}
}
return 0;
}
#endif
void erofs_uuid_generate(unsigned char *out)
{
#ifdef HAVE_LIBUUID
uuid_t new_uuid;
do {
uuid_generate(new_uuid);
} while (uuid_is_null(new_uuid));
#else
unsigned char new_uuid[16];
int res __maybe_unused;
res = s_getrandom(new_uuid, sizeof(new_uuid), true);
BUG_ON(res != 0);
// UID type + version bits
new_uuid[0] = (new_uuid[4 + 2] & 0x0f) | 0x40;
new_uuid[1] = (new_uuid[4 + 2 + 2] & 0x3f) | 0x80;
#endif
memcpy(out, new_uuid, sizeof(new_uuid));
}
int erofs_uuid_parse(const char *in, unsigned char *uu) {
#ifdef HAVE_LIBUUID
return uuid_parse((char *)in, uu);
#else
unsigned char new_uuid[16];
unsigned int hypens = ((1U << 3) | (1U << 5) | (1U << 7) | (1U << 9));
int i;
for (i = 0; i < sizeof(new_uuid); hypens >>= 1, i++)
{
char c[] = { in[0], in[1], '\0' };
char* endptr = c;
unsigned long val = strtoul(c, &endptr, 16);
if (endptr - c != 2)
return -EINVAL;
in += 2;
if ((hypens & 1U) != 0) {
if (*in++ != '-')
return -EINVAL;
}
new_uuid[i] = (unsigned char)val;
}
if (*in != '\0')
return -EINVAL;
memcpy(uu, new_uuid, sizeof(new_uuid));
return 0;
#endif
}