*/
/*
- * 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
# include "config.h"
#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#ifdef HAVE_SYS_SYSCTL_H
+# include <sys/types.h>
+# include <sys/sysctl.h>
+#endif
+#ifdef HAVE_SYS_SYSCALL_H
+# include <sys/syscall.h>
+#endif
+#include <unistd.h>
#include "wimlib.h"
#include "wimlib/assert.h"
+#include "wimlib/error.h"
#include "wimlib/timestamp.h"
#include "wimlib/util.h"
#include "wimlib/xml.h"
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;
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;
}
wimlib_aligned_free(void *ptr)
{
if (ptr)
- FREE((char *)ptr - *((size_t *)ptr - 1));
+ FREE(((void **)ptr)[-1]);
}
void *
}
#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__ */