| // 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 |
| } |