summaryrefslogtreecommitdiff
path: root/src/libudev
diff options
context:
space:
mode:
Diffstat (limited to 'src/libudev')
-rw-r--r--src/libudev/Makefile.am2
-rw-r--r--src/libudev/cgroup-util.c6
-rw-r--r--src/libudev/conf-files.c12
-rw-r--r--src/libudev/def.h5
-rw-r--r--src/libudev/hashmap.c172
-rw-r--r--src/libudev/hashmap.h8
-rw-r--r--src/libudev/libudev-queue-private.c13
-rw-r--r--src/libudev/libudev-util.c9
-rw-r--r--src/libudev/log.c55
-rw-r--r--src/libudev/log.h24
-rw-r--r--src/libudev/macro.h46
-rw-r--r--src/libudev/path-util.c43
-rw-r--r--src/libudev/path-util.h2
-rw-r--r--src/libudev/siphash24.c135
-rw-r--r--src/libudev/siphash24.h4
-rw-r--r--src/libudev/socket-util.h14
-rw-r--r--src/libudev/sparse-endian.h1
-rw-r--r--src/libudev/strv.c12
-rw-r--r--src/libudev/strv.h1
-rw-r--r--src/libudev/strxcpyx.c12
-rw-r--r--src/libudev/util.c105
-rw-r--r--src/libudev/util.h11
22 files changed, 524 insertions, 168 deletions
diff --git a/src/libudev/Makefile.am b/src/libudev/Makefile.am
index 286dc4b62a..84d018ae03 100644
--- a/src/libudev/Makefile.am
+++ b/src/libudev/Makefile.am
@@ -43,6 +43,7 @@ libudev_la_SOURCES =\
MurmurHash2.c \
path-util.c \
set.c \
+ siphash24.c \
strbuf.c \
strv.c \
strxcpyx.c \
@@ -66,6 +67,7 @@ noinst_HEADERS = \
MurmurHash2.h \
path-util.h \
set.h \
+ siphash24.h \
socket-util.h \
sparse-endian.h \
strbuf.h \
diff --git a/src/libudev/cgroup-util.c b/src/libudev/cgroup-util.c
index 71bd529ad2..af6ce959c1 100644
--- a/src/libudev/cgroup-util.c
+++ b/src/libudev/cgroup-util.c
@@ -198,7 +198,7 @@ static int join_path(const char *controller, const char *path, const char *suffi
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
const char *p;
- static __thread bool good = false;
+ static thread_local bool good = false;
assert(fs);
@@ -223,9 +223,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
#define CONTROLLER_VALID \
- "0123456789" \
- "abcdefghijklmnopqrstuvwxyz" \
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ DIGITS LETTERS \
"_"
bool cg_controller_is_valid(const char *p, bool allow_named) {
diff --git a/src/libudev/conf-files.c b/src/libudev/conf-files.c
index 2d413999f9..dc4f970313 100644
--- a/src/libudev/conf-files.c
+++ b/src/libudev/conf-files.c
@@ -53,13 +53,13 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
for (;;) {
struct dirent *de;
- union dirent_storage buf;
char *p;
int r;
- r = readdir_r(dir, &buf.de, &de);
- if (r != 0)
- return -r;
+ errno = 0;
+ de = readdir(dir);
+ if (!de && errno != 0)
+ return -errno;
if (!de)
break;
@@ -71,7 +71,7 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
if (!p)
return -ENOMEM;
- r = hashmap_put(h, path_get_file_name(p), p);
+ r = hashmap_put(h, basename(p), p);
if (r == -EEXIST) {
log_debug("Skipping overridden file: %s.", p);
free(p);
@@ -92,7 +92,7 @@ static int base_cmp(const void *a, const void *b) {
s1 = *(char * const *)a;
s2 = *(char * const *)b;
- return strcmp(path_get_file_name(s1), path_get_file_name(s2));
+ return strcmp(basename(s1), basename(s2));
}
static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) {
diff --git a/src/libudev/def.h b/src/libudev/def.h
index 3c44e27121..d4844f4186 100644
--- a/src/libudev/def.h
+++ b/src/libudev/def.h
@@ -24,3 +24,8 @@
#include "util.h"
#define SYSTEMD_CGROUP_CONTROLLER "name=systemd"
+
+#define DIGITS "0123456789"
+#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz"
+#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS
diff --git a/src/libudev/hashmap.c b/src/libudev/hashmap.c
index c483fbad11..23e42f7b34 100644
--- a/src/libudev/hashmap.c
+++ b/src/libudev/hashmap.c
@@ -27,8 +27,9 @@
#include "util.h"
#include "hashmap.h"
#include "macro.h"
+#include "siphash24.h"
-#define NBUCKETS 127
+#define INITIAL_N_BUCKETS 31
struct hashmap_entry {
const void *key;
@@ -42,12 +43,13 @@ struct Hashmap {
compare_func_t compare_func;
struct hashmap_entry *iterate_list_head, *iterate_list_tail;
- unsigned n_entries;
- bool from_pool;
-};
+ struct hashmap_entry ** buckets;
+ unsigned n_buckets, n_entries;
-#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
+ uint8_t hash_key[HASH_KEY_SIZE];
+ bool from_pool:1;
+};
struct pool {
struct pool *next;
@@ -61,9 +63,15 @@ static void *first_hashmap_tile = NULL;
static struct pool *first_entry_pool = NULL;
static void *first_entry_tile = NULL;
-static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) {
+static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size, unsigned at_least) {
unsigned i;
+ /* When a tile is released we add it to the list and simply
+ * place the next pointer at its offset 0. */
+
+ assert(tile_size >= sizeof(void*));
+ assert(at_least > 0);
+
if (*first_tile) {
void *r;
@@ -78,7 +86,7 @@ static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t t
struct pool *p;
n = *first_pool ? (*first_pool)->n_tiles : 0;
- n = MAX(512U, n * 2);
+ n = MAX(at_least, n * 2);
size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
n = (size - ALIGN(sizeof(struct pool))) / tile_size;
@@ -103,30 +111,49 @@ static void deallocate_tile(void **first_tile, void *p) {
*first_tile = p;
}
-unsigned string_hash_func(const void *p) {
- unsigned hash = 5381;
- const signed char *c;
-
- /* DJB's hash function */
-
- for (c = p; *c; c++)
- hash = (hash << 5) + hash + (unsigned) *c;
-
- return hash;
+unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ uint64_t u;
+ siphash24((uint8_t*) &u, p, strlen(p), hash_key);
+ return (unsigned long) u;
}
int string_compare_func(const void *a, const void *b) {
return strcmp(a, b);
}
-unsigned trivial_hash_func(const void *p) {
- return PTR_TO_UINT(p);
+unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+ uint64_t u;
+ siphash24((uint8_t*) &u, &p, sizeof(p), hash_key);
+ return (unsigned long) u;
}
int trivial_compare_func(const void *a, const void *b) {
return a < b ? -1 : (a > b ? 1 : 0);
}
+static unsigned bucket_hash(Hashmap *h, const void *p) {
+ return (unsigned) (h->hash_func(p, h->hash_key) % h->n_buckets);
+}
+
+static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) {
+ static uint8_t current[HASH_KEY_SIZE];
+ static bool current_initialized = false;
+
+ /* Returns a hash function key to use. In order to keep things
+ * fast we will not generate a new key each time we allocate a
+ * new hash table. Instead, we'll just reuse the most recently
+ * generated one, except if we never generated one or when we
+ * are rehashing an entire hash table because we reached a
+ * fill level */
+
+ if (!current_initialized || !reuse_is_ok) {
+ random_bytes(current, sizeof(current));
+ current_initialized = true;
+ }
+
+ memcpy(hash_key, current, sizeof(current));
+}
+
Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
bool b;
Hashmap *h;
@@ -134,10 +161,10 @@ Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
b = is_main_thread();
- size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
+ size = ALIGN(sizeof(Hashmap)) + INITIAL_N_BUCKETS * sizeof(struct hashmap_entry*);
if (b) {
- h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
+ h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size, 8);
if (!h)
return NULL;
@@ -152,11 +179,16 @@ Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
h->hash_func = hash_func ? hash_func : trivial_hash_func;
h->compare_func = compare_func ? compare_func : trivial_compare_func;
+ h->n_buckets = INITIAL_N_BUCKETS;
h->n_entries = 0;
h->iterate_list_head = h->iterate_list_tail = NULL;
+ h->buckets = (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap)));
+
h->from_pool = b;
+ get_hash_key(h->hash_key, true);
+
return h;
}
@@ -165,11 +197,11 @@ static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
assert(e);
/* Insert into hash table */
- e->bucket_next = BY_HASH(h)[hash];
+ e->bucket_next = h->buckets[hash];
e->bucket_previous = NULL;
- if (BY_HASH(h)[hash])
- BY_HASH(h)[hash]->bucket_previous = e;
- BY_HASH(h)[hash] = e;
+ if (h->buckets[hash])
+ h->buckets[hash]->bucket_previous = e;
+ h->buckets[hash] = e;
/* Insert into iteration list */
e->iterate_previous = h->iterate_list_tail;
@@ -209,7 +241,7 @@ static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) {
if (e->bucket_previous)
e->bucket_previous->bucket_next = e->bucket_next;
else
- BY_HASH(h)[hash] = e->bucket_next;
+ h->buckets[hash] = e->bucket_next;
assert(h->n_entries >= 1);
h->n_entries--;
@@ -221,8 +253,7 @@ static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
assert(h);
assert(e);
- hash = h->hash_func(e->key) % NBUCKETS;
-
+ hash = bucket_hash(h, e->key);
unlink_entry(h, e, hash);
if (h->from_pool)
@@ -240,6 +271,9 @@ void hashmap_free(Hashmap*h) {
hashmap_clear(h);
+ if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
+ free(h->buckets);
+
if (h->from_pool)
deallocate_tile(&first_hashmap_tile, h);
else
@@ -279,34 +313,92 @@ void hashmap_clear_free(Hashmap *h) {
static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
struct hashmap_entry *e;
assert(h);
- assert(hash < NBUCKETS);
+ assert(hash < h->n_buckets);
- for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
+ for (e = h->buckets[hash]; e; e = e->bucket_next)
if (h->compare_func(e->key, key) == 0)
return e;
return NULL;
}
+static bool resize_buckets(Hashmap *h) {
+ struct hashmap_entry **n, *i;
+ unsigned m;
+ uint8_t nkey[HASH_KEY_SIZE];
+
+ assert(h);
+
+ if (_likely_(h->n_entries*4 < h->n_buckets*3))
+ return false;
+
+ /* Increase by four */
+ m = (h->n_entries+1)*4-1;
+
+ /* If we hit OOM we simply risk packed hashmaps... */
+ n = new0(struct hashmap_entry*, m);
+ if (!n)
+ return false;
+
+ /* Let's use a different randomized hash key for the
+ * extension, so that people cannot guess what we are using
+ * here forever */
+ get_hash_key(nkey, false);
+
+ for (i = h->iterate_list_head; i; i = i->iterate_next) {
+ unsigned long old_bucket, new_bucket;
+
+ old_bucket = h->hash_func(i->key, h->hash_key) % h->n_buckets;
+
+ /* First, drop from old bucket table */
+ if (i->bucket_next)
+ i->bucket_next->bucket_previous = i->bucket_previous;
+
+ if (i->bucket_previous)
+ i->bucket_previous->bucket_next = i->bucket_next;
+ else
+ h->buckets[old_bucket] = i->bucket_next;
+
+ /* Then, add to new backet table */
+ new_bucket = h->hash_func(i->key, nkey) % m;
+
+ i->bucket_next = n[new_bucket];
+ i->bucket_previous = NULL;
+ if (n[new_bucket])
+ n[new_bucket]->bucket_previous = i;
+ n[new_bucket] = i;
+ }
+
+ if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap))))
+ free(h->buckets);
+
+ h->buckets = n;
+ h->n_buckets = m;
+
+ memcpy(h->hash_key, nkey, HASH_KEY_SIZE);
+
+ return true;
+}
+
int hashmap_put(Hashmap *h, const void *key, void *value) {
struct hashmap_entry *e;
unsigned hash;
assert(h);
- hash = h->hash_func(key) % NBUCKETS;
-
+ hash = bucket_hash(h, key);
e = hash_scan(h, hash, key);
if (e) {
-
if (e->value == value)
return 0;
-
return -EEXIST;
}
+ if (resize_buckets(h))
+ hash = bucket_hash(h, key);
+
if (h->from_pool)
- e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
+ e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry), 64U);
else
e = new(struct hashmap_entry, 1);
@@ -328,7 +420,7 @@ void* hashmap_get(Hashmap *h, const void *key) {
if (!h)
return NULL;
- hash = h->hash_func(key) % NBUCKETS;
+ hash = bucket_hash(h, key);
e = hash_scan(h, hash, key);
if (!e)
return NULL;
@@ -342,12 +434,8 @@ bool hashmap_contains(Hashmap *h, const void *key) {
if (!h)
return false;
- hash = h->hash_func(key) % NBUCKETS;
-
- if (!hash_scan(h, hash, key))
- return false;
-
- return true;
+ hash = bucket_hash(h, key);
+ return !!hash_scan(h, hash, key);
}
void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) {
diff --git a/src/libudev/hashmap.h b/src/libudev/hashmap.h
index 3b65142eca..387c32243e 100644
--- a/src/libudev/hashmap.h
+++ b/src/libudev/hashmap.h
@@ -30,6 +30,8 @@
* for all read operations. That way it is not necessary to
* instantiate an object for each Hashmap use. */
+#define HASH_KEY_SIZE 16
+
typedef struct Hashmap Hashmap;
typedef struct _IteratorStruct _IteratorStruct;
typedef _IteratorStruct* Iterator;
@@ -37,16 +39,16 @@ typedef _IteratorStruct* Iterator;
#define ITERATOR_FIRST ((Iterator) 0)
#define ITERATOR_LAST ((Iterator) -1)
-typedef unsigned (*hash_func_t)(const void *p);
+typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
typedef int (*compare_func_t)(const void *a, const void *b);
-unsigned string_hash_func(const void *p) _pure_;
+unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int string_compare_func(const void *a, const void *b) _pure_;
/* This will compare the passed pointers directly, and will not
* dereference them. This is hence not useful for strings or
* suchlike. */
-unsigned trivial_hash_func(const void *p) _const_;
+unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
int trivial_compare_func(const void *a, const void *b) _const_;
Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
diff --git a/src/libudev/libudev-queue-private.c b/src/libudev/libudev-queue-private.c
index 51a1d672be..80d7ceef2b 100644
--- a/src/libudev/libudev-queue-private.c
+++ b/src/libudev/libudev-queue-private.c
@@ -224,8 +224,8 @@ static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
if (new_queue_file == NULL)
goto error;
seqnum = udev_queue_export->seqnum_max;
- if (fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file) != sizeof(unsigned long long int))
- goto error;
+ fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
+
/* copy unfinished events only to the new file */
if (devpaths != NULL) {
for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
@@ -239,12 +239,9 @@ static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
devpath_len = err;
- if (fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file) != 1)
- goto error;
- if (fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file) != 1)
- goto error;
- if (fwrite(devpath, 1, devpath_len, new_queue_file) != devpath_len)
- goto error;
+ fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
+ fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
+ fwrite(devpath, 1, devpath_len, new_queue_file);
}
seqnum++;
}
diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c
index 2c31d5b1db..6087c8f106 100644
--- a/src/libudev/libudev-util.c
+++ b/src/libudev/libudev-util.c
@@ -87,11 +87,8 @@ uid_t util_lookup_user(struct udev *udev, const char *user)
struct passwd *pw;
uid_t uid;
size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
- char *buf;
+ char *buf = alloca(buflen);
- if (buflen == -1)
- buflen = 1024;
- buf = alloca(buflen);
if (streq(user, "root"))
return 0;
uid = strtoul(user, &endptr, 10);
@@ -114,11 +111,9 @@ gid_t util_lookup_group(struct udev *udev, const char *group)
struct group grbuf;
struct group *gr;
gid_t gid = 0;
- size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
+ size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
char *buf = NULL;
- if (buflen == -1)
- buflen = 1024;
if (streq(group, "root"))
return 0;
gid = strtoul(group, &endptr, 10);
diff --git a/src/libudev/log.c b/src/libudev/log.c
index da5ad583d9..75157083c6 100644
--- a/src/libudev/log.c
+++ b/src/libudev/log.c
@@ -115,10 +115,7 @@ void log_close_syslog(void) {
static int create_log_socket(int type) {
int fd;
- /* All output to the syslog/journal fds we do asynchronously,
- * and if the buffers are full we just drop the messages */
-
- fd = socket(AF_UNIX, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
@@ -190,6 +187,12 @@ int log_open(void) {
getpid() == 1 ||
isatty(STDERR_FILENO) <= 0) {
+ if (log_target == LOG_TARGET_AUTO)
+ if (r >= 0) {
+ log_close_syslog();
+ log_close_console();
+ return r;
+ }
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_SYSLOG) {
r = log_open_syslog();
@@ -214,8 +217,6 @@ int log_open(void) {
log_close_syslog();
- /* Get the real /dev/console if we are PID=1, hence reopen */
- log_close_console();
return log_open_console();
}
@@ -270,8 +271,25 @@ static int write_to_console(
IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
IOVEC_SET_STRING(iovec[n++], "\n");
- if (writev(console_fd, iovec, n) < 0)
- return -errno;
+ if (writev(console_fd, iovec, n) < 0) {
+
+ if (errno == EIO && getpid() == 1) {
+
+ /* If somebody tried to kick us from our
+ * console tty (via vhangup() or suchlike),
+ * try to reconnect */
+
+ log_close_console();
+ log_open_console();
+
+ if (console_fd < 0)
+ return 0;
+
+ if (writev(console_fd, iovec, n) < 0)
+ return -errno;
+ } else
+ return -errno;
+ }
return 1;
}
@@ -515,25 +533,29 @@ int log_meta(
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-_noreturn_ static void log_assert(const char *text, const char *file, int line, const char *func, const char *format) {
+static void log_assert(int level, const char *text, const char *file, int line, const char *func, const char *format) {
static char buffer[LINE_MAX];
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return;
+
snprintf(buffer, sizeof(buffer), format, text, file, line, func);
char_array_0(buffer);
log_abort_msg = buffer;
- log_dispatch(LOG_CRIT, file, line, func, NULL, NULL, buffer);
- abort();
+ log_dispatch(level, file, line, func, NULL, NULL, buffer);
}
#pragma GCC diagnostic pop
-_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func) {
- log_assert(text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.");
+noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) {
+ log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.");
+ abort();
}
-_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) {
- log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
+noreturn void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) {
+ log_assert(LOG_CRIT, text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
+ abort();
}
int log_oom_internal(const char *file, int line, const char *func) {
@@ -541,6 +563,9 @@ int log_oom_internal(const char *file, int line, const char *func) {
return -ENOMEM;
}
+int log_get_max_level(void) {
+ return log_max_level;
+}
static const char *const log_target_table[] = {
[LOG_TARGET_CONSOLE] = "console",
[LOG_TARGET_KMSG] = "kmsg",
diff --git a/src/libudev/log.h b/src/libudev/log.h
index 23ada2255c..45a4f9c2f0 100644
--- a/src/libudev/log.h
+++ b/src/libudev/log.h
@@ -44,6 +44,7 @@ typedef enum LogTarget{
void log_set_target(LogTarget target);
void log_set_max_level(int level);
+int log_get_max_level(void) _pure_;
int log_open(void);
void log_close(void);
@@ -73,25 +74,32 @@ int log_oom_internal(
int line,
const char *func);
-_noreturn_ void log_assert_failed(
+noreturn void log_assert_failed(
const char *text,
const char *file,
int line,
const char *func);
-_noreturn_ void log_assert_failed_unreachable(
+noreturn void log_assert_failed_unreachable(
const char *text,
const char *file,
int line,
const char *func);
-#define log_full(level, ...) log_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_debug(...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_info(...) log_meta(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_notice(...) log_meta(LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_warning(...) log_meta(LOG_WARNING, __FILE__, __LINE__, __func__, __VA_ARGS__)
-#define log_error(...) log_meta(LOG_ERR, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_full(level, ...) \
+do { \
+ if (log_get_max_level() >= (level)) \
+ log_meta((level), __FILE__, __LINE__, __func__, __VA_ARGS__); \
+} while (0)
+
+#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
+#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
+#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
+#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
+#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
+
+#define log_struct(level, ...) log_struct_internal(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__)
diff --git a/src/libudev/macro.h b/src/libudev/macro.h
index dddc040cec..835610caf3 100644
--- a/src/libudev/macro.h
+++ b/src/libudev/macro.h
@@ -30,7 +30,6 @@
#define _printf_(a,b) __attribute__ ((format (printf, a, b)))
#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__)))
#define _sentinel_ __attribute__ ((sentinel))
-#define _noreturn_ __attribute__((noreturn))
#define _pure_ __attribute__ ((pure))
#define _const_ __attribute__ ((const))
#define _packed_ __attribute__ ((packed))
@@ -68,7 +67,6 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
* @member: the name of the member within the struct.
*
*/
-
#define container_of(ptr, type, member) \
__extension__ ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
@@ -95,19 +93,9 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
} while (false)
#if defined(static_assert)
-#define assert_cc(expr) \
- do { \
- static_assert(expr, #expr); \
- } while (false)
+#define assert_cc(expr) static_assert(expr, #expr)
#else
-#define assert_cc(expr) \
- do { \
- switch (0) { \
- case 0: \
- case !!(expr): \
- ; \
- } \
- } while (false)
+#define assert_cc(expr) struct UNIQUE(_assert_struct_) { char x[(expr) ? 0 : -1]; };
#endif
#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
@@ -174,17 +162,39 @@ static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned n, size_t k) {
* the const magic to the type, otherwise the compiler warns about
* signed/unsigned comparison, because the magic can be 32 bit unsigned.
*/
-#define F_TYPE_CMP(a, b) (a == (typeof(a)) b)
-
+#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
/* Returns the number of chars needed to format variables of the
* specified type as a decimal string. Adds in extra space for a
* negative '-' prefix. */
-
#define DECIMAL_STR_MAX(type) \
- (1+(sizeof(type) <= 1 ? 3 : \
+ (2+(sizeof(type) <= 1 ? 3 : \
sizeof(type) <= 2 ? 5 : \
sizeof(type) <= 4 ? 10 : \
sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
+/* Define C11 thread_local attribute even on older gcc compiler
+ * version */
+#ifndef thread_local
+/*
+ * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__
+ * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769
+ */
+#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16))
+#define thread_local _Thread_local
+#else
+#define thread_local __thread
+#endif
+#endif
+
+/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc
+ * compiler versions */
+#ifndef noreturn
+#if __STDC_VERSION__ >= 201112L
+#define noreturn _Noreturn
+#else
+#define noreturn __attribute__((noreturn))
+#endif
+#endif
+
#include "log.h"
diff --git a/src/libudev/path-util.c b/src/libudev/path-util.c
index 616577088c..a4484c8de1 100644
--- a/src/libudev/path-util.c
+++ b/src/libudev/path-util.c
@@ -107,7 +107,7 @@ char *path_make_absolute(const char *p, const char *prefix) {
}
char *path_make_absolute_cwd(const char *p) {
- char *cwd, *r;
+ _cleanup_free_ char *cwd = NULL;
assert(p);
@@ -121,10 +121,7 @@ char *path_make_absolute_cwd(const char *p) {
if (!cwd)
return NULL;
- r = path_make_absolute(p, cwd);
- free(cwd);
-
- return r;
+ return path_make_absolute(p, cwd);
}
char **path_strv_canonicalize(char **l) {
@@ -152,7 +149,7 @@ char **path_strv_canonicalize(char **l) {
}
errno = 0;
- u = realpath(t, 0);
+ u = canonicalize_file_name(t);
if (!u) {
if (errno == ENOENT)
u = t;
@@ -332,33 +329,37 @@ fallback:
return a.st_dev != b.st_dev;
}
-bool paths_check_timestamp(char **paths, usec_t *paths_ts_usec, bool update)
-{
- unsigned int i;
+bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
bool changed = false;
+ const char* const* i;
+
+ assert(timestamp);
if (paths == NULL)
- goto out;
+ return false;
- for (i = 0; paths[i]; i++) {
+ STRV_FOREACH(i, paths) {
struct stat stats;
+ usec_t u;
- if (stat(paths[i], &stats) < 0)
+ if (stat(*i, &stats) < 0)
continue;
- if (paths_ts_usec[i] == timespec_load(&stats.st_mtim))
- continue;
+ u = timespec_load(&stats.st_mtim);
/* first check */
- if (paths_ts_usec[i] != 0) {
- log_debug("reload - timestamp of '%s' changed\n", paths[i]);
- changed = true;
- }
+ if (*timestamp >= u)
+ continue;
+
+ log_debug("timestamp of '%s' changed", *i);
/* update timestamp */
- if (update)
- paths_ts_usec[i] = timespec_load(&stats.st_mtim);
+ if (update) {
+ *timestamp = u;
+ changed = true;
+ } else
+ return true;
}
-out:
+
return changed;
}
diff --git a/src/libudev/path-util.h b/src/libudev/path-util.h
index 0b7577ff4f..eea7589d00 100644
--- a/src/libudev/path-util.h
+++ b/src/libudev/path-util.h
@@ -35,5 +35,5 @@ char** path_strv_canonicalize(char **l);
char** path_strv_canonicalize_uniq(char **l);
int path_is_mount_point(const char *path, bool allow_symlink);
+bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
-bool paths_check_timestamp(char **paths, usec_t *paths_ts_usec, bool update);
diff --git a/src/libudev/siphash24.c b/src/libudev/siphash24.c
new file mode 100644
index 0000000000..f68bd283a1
--- /dev/null
+++ b/src/libudev/siphash24.c
@@ -0,0 +1,135 @@
+/*
+ SipHash reference C implementation
+
+ Written in 2012 by
+ Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
+ Daniel J. Bernstein <djb@cr.yp.to>
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+ (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd)
+*/
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "siphash24.h"
+
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint8_t u8;
+
+#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
+
+#define U32TO8_LE(p, v) \
+ (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \
+ (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24);
+
+#define U64TO8_LE(p, v) \
+ U32TO8_LE((p), (u32)((v) )); \
+ U32TO8_LE((p) + 4, (u32)((v) >> 32));
+
+#define U8TO64_LE(p) \
+ (((u64)((p)[0]) ) | \
+ ((u64)((p)[1]) << 8) | \
+ ((u64)((p)[2]) << 16) | \
+ ((u64)((p)[3]) << 24) | \
+ ((u64)((p)[4]) << 32) | \
+ ((u64)((p)[5]) << 40) | \
+ ((u64)((p)[6]) << 48) | \
+ ((u64)((p)[7]) << 56))
+
+#define SIPROUND \
+ do { \
+ v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
+ v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
+ v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
+ v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
+ } while(0)
+
+/* SipHash-2-4 */
+void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16])
+{
+ /* "somepseudorandomlygeneratedbytes" */
+ u64 v0 = 0x736f6d6570736575ULL;
+ u64 v1 = 0x646f72616e646f6dULL;
+ u64 v2 = 0x6c7967656e657261ULL;
+ u64 v3 = 0x7465646279746573ULL;
+ u64 b;
+ u64 k0 = U8TO64_LE( k );
+ u64 k1 = U8TO64_LE( k + 8 );
+ u64 m;
+ const u8 *in = _in;
+ const u8 *end = in + inlen - ( inlen % sizeof( u64 ) );
+ const int left = inlen & 7;
+ b = ( ( u64 )inlen ) << 56;
+ v3 ^= k1;
+ v2 ^= k0;
+ v1 ^= k1;
+ v0 ^= k0;
+
+ for ( ; in != end; in += 8 )
+ {
+ m = U8TO64_LE( in );
+#ifdef DEBUG
+ printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+ printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+ printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+ printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+ printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m );
+#endif
+ v3 ^= m;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= m;
+ }
+
+ switch( left )
+ {
+ case 7: b |= ( ( u64 )in[ 6] ) << 48;
+
+ case 6: b |= ( ( u64 )in[ 5] ) << 40;
+
+ case 5: b |= ( ( u64 )in[ 4] ) << 32;
+
+ case 4: b |= ( ( u64 )in[ 3] ) << 24;
+
+ case 3: b |= ( ( u64 )in[ 2] ) << 16;
+
+ case 2: b |= ( ( u64 )in[ 1] ) << 8;
+
+ case 1: b |= ( ( u64 )in[ 0] ); break;
+
+ case 0: break;
+ }
+
+#ifdef DEBUG
+ printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+ printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+ printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+ printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+ printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b );
+#endif
+ v3 ^= b;
+ SIPROUND;
+ SIPROUND;
+ v0 ^= b;
+#ifdef DEBUG
+ printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 );
+ printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 );
+ printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 );
+ printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 );
+#endif
+ v2 ^= 0xff;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ SIPROUND;
+ b = v0 ^ v1 ^ v2 ^ v3;
+ U64TO8_LE( out, b );
+}
diff --git a/src/libudev/siphash24.h b/src/libudev/siphash24.h
new file mode 100644
index 0000000000..3253c179b6
--- /dev/null
+++ b/src/libudev/siphash24.h
@@ -0,0 +1,4 @@
+#include <inttypes.h>
+#include <sys/types.h>
+
+void siphash24(uint8_t out[8], const void *in, size_t inlen, const uint8_t k[16]);
diff --git a/src/libudev/socket-util.h b/src/libudev/socket-util.h
index f362757bc0..2daa3279fa 100644
--- a/src/libudev/socket-util.h
+++ b/src/libudev/socket-util.h
@@ -1,9 +1,5 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
/***
- This file is part of systemd.
+ This file is part of eudev, forked from systemd.
Copyright 2010 Lennart Poettering
@@ -21,14 +17,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sys/socket.h>
#include <netinet/in.h>
+#include <sys/un.h>
+#include <net/if.h>
+#include <asm/types.h>
#include <linux/netlink.h>
+#include <linux/if_packet.h>
union sockaddr_union {
struct sockaddr sa;
- struct sockaddr_in in4;
+ struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
struct sockaddr_nl nl;
struct sockaddr_storage storage;
+ struct sockaddr_ll ll;
};
diff --git a/src/libudev/sparse-endian.h b/src/libudev/sparse-endian.h
index 51694bebb7..eb4dbf3615 100644
--- a/src/libudev/sparse-endian.h
+++ b/src/libudev/sparse-endian.h
@@ -23,7 +23,6 @@
#include <endian.h>
#include <stdint.h>
-#include <byteswap.h>
#ifdef __CHECKER__
#define __bitwise __attribute__((bitwise))
diff --git a/src/libudev/strv.c b/src/libudev/strv.c
index 3619701e9d..cbfe2388d6 100644
--- a/src/libudev/strv.c
+++ b/src/libudev/strv.c
@@ -202,15 +202,11 @@ char **strv_remove(char **l, const char *s) {
/* Drops every occurrence of s in the string list, edits
* in-place. */
- for (f = t = l; *f; f++) {
-
- if (streq(*f, s)) {
+ for (f = t = l; *f; f++)
+ if (streq(*f, s))
free(*f);
- continue;
- }
-
- *(t++) = *f;
- }
+ else
+ *(t++) = *f;
*t = NULL;
return l;
diff --git a/src/libudev/strv.h b/src/libudev/strv.h
index 9fba94e566..12fa9f0524 100644
--- a/src/libudev/strv.h
+++ b/src/libudev/strv.h
@@ -31,6 +31,7 @@ static inline void strv_freep(char ***l) {
strv_free(*l);
}
+void strv_free(char **l);
#define _cleanup_strv_free_ _cleanup_(strv_freep)
char **strv_copy(char * const *l);
diff --git a/src/libudev/strxcpyx.c b/src/libudev/strxcpyx.c
index fbdf5ac658..6efa237d4b 100644
--- a/src/libudev/strxcpyx.c
+++ b/src/libudev/strxcpyx.c
@@ -29,8 +29,7 @@
#include <string.h>
#include "strxcpyx.h"
-size_t strpcpy(char **dest, size_t size, const char *src)
-{
+size_t strpcpy(char **dest, size_t size, const char *src) {
size_t len;
len = strlen(src);
@@ -48,8 +47,7 @@ size_t strpcpy(char **dest, size_t size, const char *src)
return size;
}
-size_t strpcpyf(char **dest, size_t size, const char *src, ...)
-{
+size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
va_list va;
int i;
@@ -67,8 +65,7 @@ size_t strpcpyf(char **dest, size_t size, const char *src, ...)
return size;
}
-size_t strpcpyl(char **dest, size_t size, const char *src, ...)
-{
+size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
va_list va;
va_start(va, src);
@@ -80,8 +77,7 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...)
return size;
}
-size_t strscpy(char *dest, size_t size, const char *src)
-{
+size_t strscpy(char *dest, size_t size, const char *src) {
char *s;
s = dest;
diff --git a/src/libudev/util.c b/src/libudev/util.c
index 98be5307f6..93f60b8ec4 100644
--- a/src/libudev/util.c
+++ b/src/libudev/util.c
@@ -1,7 +1,5 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
- This file is part of systemd.
+ This file is part of eudev, forked from systemd.
Copyright 2010 Lennart Poettering
@@ -78,7 +76,7 @@ static volatile unsigned cached_columns = 0;
static volatile unsigned cached_lines = 0;
size_t page_size(void) {
- static __thread size_t pgsz = 0;
+ static thread_local size_t pgsz = 0;
long r;
if (_likely_(pgsz > 0))
@@ -615,6 +613,50 @@ bool ignore_file(const char *filename) {
return ignore_file_allow_backup(filename);
}
+void random_bytes(void *p, size_t n) {
+ static bool srand_called = false;
+ _cleanup_close_ int fd;
+ ssize_t k;
+ uint8_t *q;
+
+ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ goto fallback;
+
+ k = loop_read(fd, p, n, true);
+ if (k < 0 || (size_t) k != n)
+ goto fallback;
+
+ return;
+
+fallback:
+
+ if (!srand_called) {
+
+#ifdef HAVE_SYS_AUXV_H
+ /* The kernel provides us with a bit of entropy in
+ * auxv, so let's try to make use of that to seed the
+ * pseudo-random generator. It's better than
+ * nothing... */
+
+ void *auxv;
+
+ auxv = (void*) getauxval(AT_RANDOM);
+ if (auxv)
+ srand(*(unsigned*) auxv);
+ else
+#endif
+ srand(time(NULL) + gettid());
+
+ srand_called = true;
+ }
+
+ /* If some idiot made /dev/urandom unavailable to us, he'll
+ * get a PRNG instead. */
+ for (q = p; q < (uint8_t*) p + n; q ++)
+ *q = rand();
+}
+
int open_terminal(const char *name, int mode) {
int fd, r;
unsigned c = 0;
@@ -665,9 +707,9 @@ int open_terminal(const char *name, int mode) {
_pure_ static int is_temporary_fs(struct statfs *s) {
assert(s);
- return
- F_TYPE_CMP(s->f_type, TMPFS_MAGIC) ||
- F_TYPE_CMP(s->f_type, RAMFS_MAGIC);
+
+ return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
+ F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
@@ -823,6 +865,55 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
return 0;
}
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
+ uint8_t *p;
+ ssize_t n = 0;
+
+ assert(fd >= 0);
+ assert(buf);
+
+ p = buf;
+
+ while (nbytes > 0) {
+ ssize_t k;
+
+ if ((k = read(fd, p, nbytes)) <= 0) {
+
+ if (k < 0 && errno == EINTR)
+ continue;
+
+ if (k < 0 && errno == EAGAIN && do_poll) {
+ struct pollfd pollfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+
+ if (poll(&pollfd, 1, -1) < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return n > 0 ? n : -errno;
+ }
+
+ /* We knowingly ignore the revents value here,
+ * and expect that any error/EOF is reported
+ * via read()/write()
+ */
+
+ continue;
+ }
+
+ return n > 0 ? n : (k < 0 ? -errno : 0);
+ }
+
+ p += k;
+ nbytes -= k;
+ n += k;
+ }
+
+ return n;
+}
+
char *strjoin(const char *x, ...) {
va_list ap;
size_t l;
diff --git a/src/libudev/util.h b/src/libudev/util.h
index 50e6a44af5..14233c9d7e 100644
--- a/src/libudev/util.h
+++ b/src/libudev/util.h
@@ -1,9 +1,5 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
/***
- This file is part of systemd.
+ This file is part of eudev, forked from systemd.
Copyright 2010 Lennart Poettering
@@ -21,6 +17,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#pragma once
+
#include <string.h>
#include <time.h>
#include <stdlib.h>
@@ -149,6 +147,8 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pu
bool ignore_file(const char *filename) _pure_;
+void random_bytes(void *p, size_t n);
+
/* For basic lookup tables with strictly enumerated entries */
#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
scope const char *name##_to_string(type i) { \
@@ -207,6 +207,7 @@ int open_terminal(const char *name, int mode);
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
bool null_or_empty(struct stat *st) _pure_;