X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Futil.c;h=0fe3d4e4582026b3ea9aaae6f375fdb7f8a73578;hp=242280961d634fbcee28def2272df4ef64dec741;hb=90f1e04a2a143876a4413577b25db60b5ba0fe97;hpb=3de1ec66f778edda19865482d685bc6f4e17faf7 diff --git a/src/util.c b/src/util.c index 24228096..0fe3d4e4 100644 --- a/src/util.c +++ b/src/util.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2012, 2013, 2014 Eric Biggers + * Copyright (C) 2012-2016 Eric Biggers * * This file is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free @@ -23,12 +23,24 @@ # include "config.h" #endif +#include +#include +#include #include #include #include +#ifdef HAVE_SYS_SYSCTL_H +# include +# include +#endif +#ifdef HAVE_SYS_SYSCALL_H +# include +#endif +#include #include "wimlib.h" #include "wimlib/assert.h" +#include "wimlib/error.h" #include "wimlib/timestamp.h" #include "wimlib/util.h" #include "wimlib/xml.h" @@ -75,7 +87,14 @@ void * wimlib_calloc(size_t nmemb, size_t size) { size_t total_size = nmemb * size; - void *p = MALLOC(total_size); + void *p; + + if (size != 0 && nmemb > SIZE_MAX / size) { + errno = ENOMEM; + return NULL; + } + + p = MALLOC(total_size); if (p) p = memset(p, 0, total_size); return p; @@ -84,46 +103,27 @@ wimlib_calloc(size_t nmemb, size_t size) char * wimlib_strdup(const char *str) { - size_t size; - char *p; - - size = strlen(str); - p = MALLOC(size + 1); - if (p) - p = memcpy(p, str, size + 1); - return p; + return memdup(str, strlen(str) + 1); } #ifdef __WIN32__ wchar_t * wimlib_wcsdup(const wchar_t *str) { - size_t size; - wchar_t *p; - - size = wcslen(str); - p = MALLOC((size + 1) * sizeof(wchar_t)); - if (p) - p = wmemcpy(p, str, size + 1); - return p; + return memdup(str, (wcslen(str) + 1) * sizeof(wchar_t)); } #endif void * wimlib_aligned_malloc(size_t size, size_t alignment) { - wimlib_assert(alignment != 0 && is_power_of_2(alignment) && - alignment <= 4096); - - const uintptr_t mask = alignment - 1; - char *ptr = NULL; - char *raw_ptr; - - raw_ptr = MALLOC(mask + sizeof(size_t) + size); - if (raw_ptr) { - ptr = (char *)raw_ptr + sizeof(size_t); - ptr = (void *)(((uintptr_t)ptr + mask) & ~mask); - *((size_t *)ptr - 1) = ptr - raw_ptr; + wimlib_assert(is_power_of_2(alignment)); + + void *ptr = MALLOC(sizeof(void *) + alignment - 1 + size); + if (ptr) { + void *orig_ptr = ptr; + ptr = (void *)ALIGN((uintptr_t)ptr + sizeof(void *), alignment); + ((void **)ptr)[-1] = orig_ptr; } return ptr; } @@ -132,7 +132,7 @@ void wimlib_aligned_free(void *ptr) { if (ptr) - FREE((char *)ptr - *((size_t *)ptr - 1)); + FREE(((void **)ptr)[-1]); } void * @@ -170,38 +170,145 @@ void *mempcpy(void *dst, const void *src, size_t n) } #endif -static bool seeded = false; +/************************** + * Random number generation + **************************/ -static void -seed_random(void) +#ifndef __WIN32__ +/* + * Generate @n cryptographically secure random bytes (thread-safe) + * + * This is the UNIX version. It uses the Linux getrandom() system call if + * available; otherwise, it falls back to reading from /dev/urandom. + */ +void +get_random_bytes(void *p, size_t n) { - srand(now_as_wim_timestamp()); - seeded = true; + if (n == 0) + return; +#ifdef HAVE_NR_GETRANDOM + static bool getrandom_unavailable; + + if (getrandom_unavailable) + goto try_dev_urandom; + do { + int res = syscall(__NR_getrandom, p, n, 0); + if (unlikely(res < 0)) { + if (errno == ENOSYS) { + getrandom_unavailable = true; + goto try_dev_urandom; + } + if (errno == EINTR) + continue; + ERROR_WITH_ERRNO("getrandom() failed"); + wimlib_assert(0); + res = 0; + } + p += res; + n -= res; + } while (n != 0); + return; + +try_dev_urandom: + ; +#endif /* HAVE_NR_GETRANDOM */ + int fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + ERROR_WITH_ERRNO("Unable to open /dev/urandom"); + wimlib_assert(0); + } + do { + int res = read(fd, p, min(n, INT_MAX)); + if (unlikely(res < 0)) { + if (errno == EINTR) + continue; + ERROR_WITH_ERRNO("Error reading from /dev/urandom"); + wimlib_assert(0); + res = 0; + } + p += res; + n -= res; + } while (n != 0); + close(fd); } +#endif /* !__WIN32__ */ -/* Fills @n characters pointed to by @p with random alphanumeric characters. */ +/* + * Generate @n cryptographically secure random alphanumeric characters + * (thread-safe) + * + * This is implemented on top of get_random_bytes(). For efficiency the calls + * to get_random_bytes() are batched. + */ void -randomize_char_array_with_alnum(tchar *p, size_t n) +get_random_alnum_chars(tchar *p, size_t n) { - if (!seeded) - seed_random(); - while (n--) { - int r = rand() % 62; - if (r < 26) - *p++ = r + 'a'; - else if (r < 52) - *p++ = r - 26 + 'A'; + u32 r[64]; + int r_idx = 0; + int r_end = 0; + + for (; n != 0; p++, n--) { + tchar x; + + if (r_idx >= r_end) { + r_idx = 0; + r_end = min(n, ARRAY_LEN(r)); + get_random_bytes(r, r_end * sizeof(r[0])); + } + + STATIC_ASSERT(sizeof(r[0]) == sizeof(u32)); + while (unlikely(r[r_idx] >= UINT32_MAX - (UINT32_MAX % 62))) + get_random_bytes(&r[r_idx], sizeof(r[0])); + + x = r[r_idx++] % 62; + + if (x < 26) + *p = 'a' + x; + else if (x < 52) + *p = 'A' + x - 26; else - *p++ = r - 52 + '0'; + *p = '0' + x - 52; } } -/* Fills @n bytes pointer to by @p with random numbers. */ -void -randomize_byte_array(u8 *p, size_t n) +/************************ + * System information + ************************/ + +#ifndef __WIN32__ +unsigned +get_available_cpus(void) { - if (!seeded) - seed_random(); - while (n--) - *p++ = rand(); + long n = sysconf(_SC_NPROCESSORS_ONLN); + if (n < 1 || n >= UINT_MAX) { + WARNING("Failed to determine number of processors; assuming 1."); + return 1; + } + return n; +} +#endif /* !__WIN32__ */ + +#ifndef __WIN32__ +u64 +get_available_memory(void) +{ +#if defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) + long page_size = sysconf(_SC_PAGESIZE); + long num_pages = sysconf(_SC_PHYS_PAGES); + if (page_size <= 0 || num_pages <= 0) + goto default_size; + return ((u64)page_size * (u64)num_pages); +#else + int mib[2] = {CTL_HW, HW_MEMSIZE}; + u64 memsize; + size_t len = sizeof(memsize); + if (sysctl(mib, ARRAY_LEN(mib), &memsize, &len, NULL, 0) < 0 || len != 8) + goto default_size; + return memsize; +#endif + +default_size: + WARNING("Failed to determine available memory; assuming 1 GiB"); + return (u64)1 << 30; } +#endif /* !__WIN32__ */