X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fbasic%2Futil.c;h=7e0a913f51c88bc97247e14058984ea9cc0c5f7c;hp=3945abdbef17c212206d4f7b6f8c5b8b41bcb6ad;hb=d76bb3c179b7a32b109e39aa87ff09c8f5a8c178;hpb=1cfc78c91965df340cdde100ad6cb3ed50b28927 diff --git a/src/basic/util.c b/src/basic/util.c index 3945abdbe..7e0a913f5 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -19,81 +19,82 @@ along with systemd; If not, see . ***/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +// #include +// #include +#include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +#include +// #include +#include +// #include +#include +#include +// #include +// #include +// #include +#include +// #include +// #include +// #include +#include +// #include +#include +// #include +// #include +#include +// #include +// #include +#include /* When we include libgen.h because we need dirname() we immediately * undefine basename() since libgen.h defines it as a macro to the POSIX * version which is really broken. We prefer GNU basename(). */ -#include -#undef basename +// #include +// #undef basename #ifdef HAVE_SYS_AUXV_H #include #endif -#include "config.h" -#include "macro.h" -#include "util.h" -#include "ioprio.h" -#include "missing.h" -#include "log.h" -#include "strv.h" -#include "mkdir.h" -#include "path-util.h" -#include "exit-status.h" -#include "hashmap.h" -#include "env-util.h" -#include "fileio.h" -#include "device-nodes.h" -#include "utf8.h" -#include "gunicode.h" -#include "virt.h" -#include "def.h" -#include "sparse-endian.h" -#include "formats-util.h" -#include "process-util.h" -#include "random-util.h" -#include "terminal-util.h" -#include "hostname-util.h" -#include "signal-util.h" +#include "config.h" +#include "macro.h" +#include "util.h" +// #include "ioprio.h" +// #include "missing.h" +// #include "log.h" +#include "strv.h" +#include "mkdir.h" +#include "path-util.h" +// #include "exit-status.h" +#include "hashmap.h" +#include "set.h" +// #include "env-util.h" +#include "fileio.h" +// #include "device-nodes.h" +#include "utf8.h" +#include "gunicode.h" +#include "virt.h" +// #include "def.h" +#include "sparse-endian.h" +// #include "formats-util.h" +#include "process-util.h" +#include "random-util.h" +// #include "terminal-util.h" +#include "hostname-util.h" +#include "signal-util.h" /* Put this test here for a lack of better place */ assert_cc(EAGAIN == EWOULDBLOCK); @@ -115,17 +116,23 @@ size_t page_size(void) { return pgsz; } -bool streq_ptr(const char *a, const char *b) { - - /* Like streq(), but tries to make sense of NULL pointers */ +int strcmp_ptr(const char *a, const char *b) { + /* Like strcmp(), but tries to make sense of NULL pointers */ if (a && b) - return streq(a, b); + return strcmp(a, b); - if (!a && !b) - return true; + if (!a && b) + return -1; - return false; + if (a && !b) + return 1; + + return 0; +} + +bool streq_ptr(const char *a, const char *b) { + return strcmp_ptr(a, b) == 0; } char* endswith(const char *s, const char *postfix) { @@ -367,6 +374,19 @@ int parse_pid(const char *s, pid_t* ret_pid) { return 0; } +bool uid_is_valid(uid_t uid) { + + /* Some libc APIs use UID_INVALID as special placeholder */ + if (uid == (uid_t) 0xFFFFFFFF) + return false; + + /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ + if (uid == (uid_t) 0xFFFF) + return false; + + return true; +} + int parse_uid(const char *s, uid_t* ret_uid) { unsigned long ul = 0; uid_t uid; @@ -383,13 +403,11 @@ int parse_uid(const char *s, uid_t* ret_uid) { if ((unsigned long) uid != ul) return -ERANGE; - /* Some libc APIs use UID_INVALID as special placeholder */ - if (uid == (uid_t) 0xFFFFFFFF) - return -ENXIO; - - /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ - if (uid == (uid_t) 0xFFFF) - return -ENXIO; + if (!uid_is_valid(uid)) + return -ENXIO; /* we return ENXIO instead of EINVAL + * here, to make it easy to distuingish + * invalid numeric uids invalid + * strings. */ if (ret_uid) *ret_uid = uid; @@ -707,6 +725,8 @@ int readlink_malloc(const char *p, char **ret) { return readlinkat_malloc(AT_FDCWD, p, ret); } +/// UNNEEDED by elogind +#if 0 int readlink_value(const char *p, char **ret) { _cleanup_free_ char *link = NULL; char *value; @@ -728,6 +748,7 @@ int readlink_value(const char *p, char **ret) { return 0; } +#endif // 0 int readlink_and_make_absolute(const char *p, char **r) { _cleanup_free_ char *target = NULL; @@ -749,6 +770,8 @@ int readlink_and_make_absolute(const char *p, char **r) { return 0; } +/// UNNEEDED by elogind +#if 0 int readlink_and_canonicalize(const char *p, char **r) { char *t, *s; int j; @@ -771,6 +794,7 @@ int readlink_and_canonicalize(const char *p, char **r) { return 0; } +#endif // 0 char *strstrip(char *s) { char *e; @@ -836,6 +860,8 @@ char *file_in_same_dir(const char *path, const char *filename) { return ret; } +/// UNNEEDED by elogind +#if 0 int rmdir_parents(const char *path, const char *stop) { size_t l; int r = 0; @@ -881,6 +907,7 @@ int rmdir_parents(const char *path, const char *stop) { return 0; } +#endif // 0 char hexchar(int x) { static const char table[16] = "0123456789abcdef"; @@ -919,32 +946,573 @@ char *hexmem(const void *p, size_t l) { return r; } -void *unhexmem(const char *p, size_t l) { - uint8_t *r, *z; +int unhexmem(const char *p, size_t l, void **mem, size_t *len) { + _cleanup_free_ uint8_t *r = NULL; + uint8_t *z; const char *x; + assert(mem); + assert(len); assert(p); z = r = malloc((l + 1) / 2 + 1); if (!r) - return NULL; + return -ENOMEM; for (x = p; x < p + l; x += 2) { int a, b; a = unhexchar(x[0]); - if (x+1 < p + l) + if (a < 0) + return a; + else if (x+1 < p + l) { b = unhexchar(x[1]); - else + if (b < 0) + return b; + } else b = 0; *(z++) = (uint8_t) a << 4 | (uint8_t) b; } + *z = 0; + + *mem = r; + r = NULL; + *len = (l + 1) / 2; + + return 0; +} + +/* https://tools.ietf.org/html/rfc4648#section-6 + * Notice that base32hex differs from base32 in the alphabet it uses. + * The distinction is that the base32hex representation preserves the + * order of the underlying data when compared as bytestrings, this is + * useful when representing NSEC3 hashes, as one can then verify the + * order of hashes directly from their representation. */ +char base32hexchar(int x) { + static const char table[32] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUV"; + + return table[x & 31]; +} + +int unbase32hexchar(char c) { + unsigned offset; + + if (c >= '0' && c <= '9') + return c - '0'; + + offset = '9' - '0' + 1; + + if (c >= 'A' && c <= 'V') + return c - 'A' + offset; + + return -EINVAL; +} + +char *base32hexmem(const void *p, size_t l, bool padding) { + char *r, *z; + const uint8_t *x; + size_t len; + + if (padding) + /* five input bytes makes eight output bytes, padding is added so we must round up */ + len = 8 * (l + 4) / 5; + else { + /* same, but round down as there is no padding */ + len = 8 * l / 5; + + switch (l % 5) { + case 4: + len += 7; + break; + case 3: + len += 5; + break; + case 2: + len += 4; + break; + case 1: + len += 2; + break; + } + } + + z = r = malloc(len + 1); + if (!r) + return NULL; + + for (x = p; x < (const uint8_t*) p + (l / 5) * 5; x += 5) { + /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ + x[3] == QQQQQQQQ; x[4] == WWWWWWWW */ + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ + *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ + *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ + *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ + *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ + *(z++) = base32hexchar((x[3] & 3) << 3 | x[4] >> 5); /* 000QQWWW */ + *(z++) = base32hexchar((x[4] & 31)); /* 000WWWWW */ + } + + switch (l % 5) { + case 4: + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ + *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ + *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ + *(z++) = base32hexchar((x[2] & 15) << 1 | x[3] >> 7); /* 000ZZZZQ */ + *(z++) = base32hexchar((x[3] & 127) >> 2); /* 000QQQQQ */ + *(z++) = base32hexchar((x[3] & 3) << 3); /* 000QQ000 */ + if (padding) + *(z++) = '='; + + break; + + case 3: + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ + *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ + *(z++) = base32hexchar((x[1] & 1) << 4 | x[2] >> 4); /* 000YZZZZ */ + *(z++) = base32hexchar((x[2] & 15) << 1); /* 000ZZZZ0 */ + if (padding) { + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + } + + break; + + case 2: + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2 | x[1] >> 6); /* 000XXXYY */ + *(z++) = base32hexchar((x[1] & 63) >> 1); /* 000YYYYY */ + *(z++) = base32hexchar((x[1] & 1) << 4); /* 000Y0000 */ + if (padding) { + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + } + + break; + + case 1: + *(z++) = base32hexchar(x[0] >> 3); /* 000XXXXX */ + *(z++) = base32hexchar((x[0] & 7) << 2); /* 000XXX00 */ + if (padding) { + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + *(z++) = '='; + } + + break; + } + *z = 0; return r; } +int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_len) { + _cleanup_free_ uint8_t *r = NULL; + int a, b, c, d, e, f, g, h; + uint8_t *z; + const char *x; + size_t len; + unsigned pad = 0; + + assert(p); + + /* padding ensures any base32hex input has input divisible by 8 */ + if (padding && l % 8 != 0) + return -EINVAL; + + if (padding) { + /* strip the padding */ + while (l > 0 && p[l - 1] == '=' && pad < 7) { + pad ++; + l --; + } + } + + /* a group of eight input bytes needs five output bytes, in case of + padding we need to add some extra bytes */ + len = (l / 8) * 5; + + switch (l % 8) { + case 7: + len += 4; + break; + case 5: + len += 3; + break; + case 4: + len += 2; + break; + case 2: + len += 1; + break; + case 0: + break; + default: + return -EINVAL; + } + + z = r = malloc(len + 1); + if (!r) + return -ENOMEM; + + for (x = p; x < p + (l / 8) * 8; x += 8) { + /* a == 000XXXXX; b == 000YYYYY; c == 000ZZZZZ; d == 000WWWWW + e == 000SSSSS; f == 000QQQQQ; g == 000VVVVV; h == 000RRRRR */ + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase32hexchar(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase32hexchar(x[3]); + if (d < 0) + return -EINVAL; + + e = unbase32hexchar(x[4]); + if (e < 0) + return -EINVAL; + + f = unbase32hexchar(x[5]); + if (f < 0) + return -EINVAL; + + g = unbase32hexchar(x[6]); + if (g < 0) + return -EINVAL; + + h = unbase32hexchar(x[7]); + if (h < 0) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ + *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ + *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ + *(z++) = (uint8_t) g << 5 | (uint8_t) h; /* VVVRRRRR */ + } + + switch (l % 8) { + case 7: + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase32hexchar(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase32hexchar(x[3]); + if (d < 0) + return -EINVAL; + + e = unbase32hexchar(x[4]); + if (e < 0) + return -EINVAL; + + f = unbase32hexchar(x[5]); + if (f < 0) + return -EINVAL; + + g = unbase32hexchar(x[6]); + if (g < 0) + return -EINVAL; + + /* g == 000VV000 */ + if (g & 7) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ + *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ + *(z++) = (uint8_t) e << 7 | (uint8_t) f << 2 | (uint8_t) g >> 3; /* SQQQQQVV */ + + break; + case 5: + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase32hexchar(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase32hexchar(x[3]); + if (d < 0) + return -EINVAL; + + e = unbase32hexchar(x[4]); + if (e < 0) + return -EINVAL; + + /* e == 000SSSS0 */ + if (e & 1) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ + *(z++) = (uint8_t) d << 4 | (uint8_t) e >> 1; /* WWWWSSSS */ + + break; + case 4: + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase32hexchar(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase32hexchar(x[3]); + if (d < 0) + return -EINVAL; + + /* d == 000W0000 */ + if (d & 15) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + *(z++) = (uint8_t) b << 6 | (uint8_t) c << 1 | (uint8_t) d >> 4; /* YYZZZZZW */ + + break; + case 2: + a = unbase32hexchar(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase32hexchar(x[1]); + if (b < 0) + return -EINVAL; + + /* b == 000YYY00 */ + if (b & 3) + return -EINVAL; + + *(z++) = (uint8_t) a << 3 | (uint8_t) b >> 2; /* XXXXXYYY */ + + break; + case 0: + break; + default: + return -EINVAL; + } + + *z = 0; + + *mem = r; + r = NULL; + *_len = len; + + return 0; +} + +/* https://tools.ietf.org/html/rfc4648#section-4 */ +char base64char(int x) { + static const char table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + return table[x & 63]; +} + +int unbase64char(char c) { + unsigned offset; + + if (c >= 'A' && c <= 'Z') + return c - 'A'; + + offset = 'Z' - 'A' + 1; + + if (c >= 'a' && c <= 'z') + return c - 'a' + offset; + + offset += 'z' - 'a' + 1; + + if (c >= '0' && c <= '9') + return c - '0' + offset; + + offset += '9' - '0' + 1; + + if (c == '+') + return offset; + + offset ++; + + if (c == '/') + return offset; + + return -EINVAL; +} + +char *base64mem(const void *p, size_t l) { + char *r, *z; + const uint8_t *x; + + /* three input bytes makes four output bytes, padding is added so we must round up */ + z = r = malloc(4 * (l + 2) / 3 + 1); + if (!r) + return NULL; + + for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { + /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ + *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ + *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ + *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ + } + + switch (l % 3) { + case 2: + *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ + *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ + *(z++) = '='; + + break; + case 1: + *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ + *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ + *(z++) = '='; + *(z++) = '='; + + break; + } + + *z = 0; + return r; +} + +int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) { + _cleanup_free_ uint8_t *r = NULL; + int a, b, c, d; + uint8_t *z; + const char *x; + size_t len; + + assert(p); + + /* padding ensures any base63 input has input divisible by 4 */ + if (l % 4 != 0) + return -EINVAL; + + /* strip the padding */ + if (l > 0 && p[l - 1] == '=') + l --; + if (l > 0 && p[l - 1] == '=') + l --; + + /* a group of four input bytes needs three output bytes, in case of + padding we need to add two or three extra bytes */ + len = (l / 4) * 3 + (l % 4 ? (l % 4) - 1 : 0); + + z = r = malloc(len + 1); + if (!r) + return -ENOMEM; + + for (x = p; x < p + (l / 4) * 4; x += 4) { + /* a == 00XXXXXX; b == 00YYYYYY; c == 00ZZZZZZ; d == 00WWWWWW */ + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase64char(x[2]); + if (c < 0) + return -EINVAL; + + d = unbase64char(x[3]); + if (d < 0) + return -EINVAL; + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + *(z++) = (uint8_t) c << 6 | (uint8_t) d; /* ZZWWWWWW */ + } + + switch (l % 4) { + case 3: + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + c = unbase64char(x[2]); + if (c < 0) + return -EINVAL; + + /* c == 00ZZZZ00 */ + if (c & 3) + return -EINVAL; + + *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ + *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ + + break; + case 2: + a = unbase64char(x[0]); + if (a < 0) + return -EINVAL; + + b = unbase64char(x[1]); + if (b < 0) + return -EINVAL; + + /* b == 00YY0000 */ + if (b & 15) + return -EINVAL; + + *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ + + break; + case 0: + + break; + default: + return -EINVAL; + } + + *z = 0; + + *mem = r; + r = NULL; + *_len = len; + + return 0; +} + char octchar(int x) { return '0' + (x & 7); } @@ -1301,6 +1869,8 @@ char *xescape(const char *s, const char *bad) { return r; } +/// UNNEEDED by elogind +#if 0 char *ascii_strlower(char *t) { char *p; @@ -1312,6 +1882,7 @@ char *ascii_strlower(char *t) { return t; } +#endif // 0 _pure_ static bool hidden_file_allow_backup(const char *filename) { assert(filename); @@ -1471,6 +2042,8 @@ bool chars_intersect(const char *a, const char *b) { return false; } +/// UNNEEDED by elogind +#if 0 bool fstype_is_network(const char *fstype) { static const char table[] = "afs\0" @@ -1493,6 +2066,7 @@ bool fstype_is_network(const char *fstype) { return nulstr_contains(table, fstype); } +#endif // 0 int flush_fd(int fd) { struct pollfd pollfd = { @@ -1771,6 +2345,8 @@ bool is_device_path(const char *path) { path_startswith(path, "/sys/"); } +/// UNNEEDED by elogind +#if 0 int dir_is_empty(const char *path) { _cleanup_closedir_ DIR *d; @@ -1842,6 +2418,7 @@ void rename_process(const char name[8]) { } } } +#endif // 0 char *lookup_uid(uid_t uid) { long bufsize; @@ -1870,6 +2447,8 @@ char *lookup_uid(uid_t uid) { return name; } +/// UNNEEDED by elogind +#if 0 char* getlogname_malloc(void) { uid_t uid; struct stat st; @@ -1891,6 +2470,7 @@ char *getusername_malloc(void) { return lookup_uid(getuid()); } +#endif // 0 bool is_temporary_fs(const struct statfs *s) { assert(s); @@ -1945,7 +2525,6 @@ int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { return 0; } -#endif // 0 cpu_set_t* cpu_set_malloc(unsigned *ncpus) { cpu_set_t *r; @@ -1974,6 +2553,7 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) { n *= 2; } } +#endif // 0 int files_same(const char *filea, const char *fileb) { struct stat a, b; @@ -2308,8 +2888,6 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { return endswith(de->d_name, suffix); } -/// UNNEEDED by elogind -#if 0 static int do_execute(char **directories, usec_t timeout, char *argv[]) { _cleanup_hashmap_free_free_ Hashmap *pids = NULL; _cleanup_set_free_free_ Set *seen = NULL; @@ -2449,7 +3027,6 @@ void execute_directories(const char* const* directories, usec_t timeout, char *a wait_for_terminate_and_warn(name, executor_pid, true); } -#endif // 0 bool nulstr_contains(const char*nulstr, const char *needle) { const char *i; @@ -2480,21 +3057,6 @@ char* strshorten(char *s, size_t l) { return s; } -bool machine_name_is_valid(const char *s) { - - if (!hostname_is_valid(s)) - return false; - - /* Machine names should be useful hostnames, but also be - * useful in unit names, hence we enforce a stricter length - * limitation. */ - - if (strlen(s) > 64) - return false; - - return true; -} - int pipe_eof(int fd) { struct pollfd pollfd = { .fd = fd, @@ -2554,8 +3116,9 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { f = fdopen(fd, "we"); if (!f) { - unlink(t); + unlink_noerrno(t); free(t); + safe_close(fd); return -errno; } @@ -2565,6 +3128,8 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { return 0; } +/// UNNEEDED by elogind +#if 0 int symlink_atomic(const char *from, const char *to) { _cleanup_free_ char *t = NULL; int r; @@ -2587,8 +3152,6 @@ int symlink_atomic(const char *from, const char *to) { return 0; } -/// UNNEEDED by elogind -#if 0 int symlink_idempotent(const char *from, const char *to) { _cleanup_free_ char *p = NULL; int r; @@ -2865,7 +3428,6 @@ int in_group(const char *name) { return in_gid(gid); } -#endif // 0 int glob_exists(const char *path) { _cleanup_globfree_ glob_t g = {}; @@ -2886,8 +3448,6 @@ int glob_exists(const char *path) { return errno ? -errno : -EIO; } -/// UNNEEDED by elogind -#if 0 int glob_extend(char ***strv, const char *path) { _cleanup_globfree_ glob_t g = {}; int k; @@ -3056,6 +3616,8 @@ bool is_main_thread(void) { return cached > 0; } +/// UNNEEDED by elogind +#if 0 int block_get_whole_disk(dev_t d, dev_t *ret) { char *p, *s; int r; @@ -3124,6 +3686,7 @@ static const char *const ioprio_class_table[] = { }; DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ioprio_class, int, INT_MAX); +#endif // 0 static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", @@ -3245,7 +3808,7 @@ int prot_from_flags(int flags) { return -EINVAL; } } -#endif // 0 + char *format_bytes(char *buf, size_t l, off_t t) { unsigned i; @@ -3284,6 +3847,7 @@ finish: return buf; } +#endif // 0 void* memdup(const void *p, size_t l) { void *r; @@ -3435,6 +3999,8 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa _exit(EXIT_FAILURE); } +/// UNNEEDED by elogind +#if 0 int setrlimit_closest(int resource, const struct rlimit *rlim) { struct rlimit highest, fixed; @@ -3459,8 +4025,6 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) { return 0; } -/// UNNEEDED by elogind -#if 0 bool http_etag_is_valid(const char *etag) { if (isempty(etag)) return false; @@ -3585,6 +4149,8 @@ int get_home_dir(char **_h) { return 0; } +/// UNNEEDED by elogind +#if 0 int get_shell(char **_s) { struct passwd *p; const char *e; @@ -3631,6 +4197,7 @@ int get_shell(char **_s) { *_s = s; return 0; } +#endif // 0 bool filename_is_valid(const char *p) { @@ -3662,7 +4229,7 @@ bool string_is_safe(const char *p) { if (*t > 0 && *t < ' ') return false; - if (strchr("\\\"\'\0x7f", *t)) + if (strchr("\\\"\'\x7f", *t)) return false; } @@ -3778,7 +4345,7 @@ bool is_locale_utf8(void) { /* Check result, but ignore the result if C was set * explicitly. */ cached_answer = - streq(set, "C") && + STR_IN_SET(set, "C", "POSIX") && !getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"); @@ -3816,6 +4383,8 @@ const char *draw_special_char(DrawSpecialChar ch) { return draw_table[!is_locale_utf8()][ch]; } +/// UNNEEDED by elogind +#if 0 char *strreplace(const char *text, const char *old_string, const char *new_string) { const char *f; char *t, *r; @@ -4024,6 +4593,7 @@ int on_ac_power(void) { return found_online || !found_offline; } +#endif // 0 static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) { char **i; @@ -4339,7 +4909,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) { _cleanup_free_ char *word = NULL; char *value = NULL; - r = unquote_first_word(&p, &word, UNQUOTE_RELAX); + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) return r; if (r == 0) @@ -4362,8 +4932,6 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) { return 0; } -/// UNNEEDED by elogind -#if 0 int get_proc_cmdline_key(const char *key, char **value) { _cleanup_free_ char *line = NULL, *ret = NULL; bool found = false; @@ -4381,7 +4949,7 @@ int get_proc_cmdline_key(const char *key, char **value) { _cleanup_free_ char *word = NULL; const char *e; - r = unquote_first_word(&p, &word, UNQUOTE_RELAX); + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) return r; if (r == 0) @@ -4416,7 +4984,6 @@ int get_proc_cmdline_key(const char *key, char **value) { return found; } -#endif // 0 int container_get_leader(const char *machine, pid_t *pid) { _cleanup_free_ char *s = NULL, *class = NULL; @@ -4427,6 +4994,9 @@ int container_get_leader(const char *machine, pid_t *pid) { assert(machine); assert(pid); + if (!machine_name_is_valid(machine)) + return -EINVAL; + p = strjoina("/run/systemd/machines/", machine); r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); if (r == -ENOENT) @@ -4449,8 +5019,8 @@ int container_get_leader(const char *machine, pid_t *pid) { return 0; } -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1; +int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; int rfd = -1; assert(pid >= 0); @@ -4482,6 +5052,15 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int * return -errno; } + if (userns_fd) { + const char *userns; + + userns = procfs_file_alloca(pid, "ns/user"); + usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (usernsfd < 0 && errno != ENOENT) + return -errno; + } + if (root_fd) { const char *root; @@ -4500,15 +5079,33 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int * if (netns_fd) *netns_fd = netnsfd; + if (userns_fd) + *userns_fd = usernsfd; + if (root_fd) *root_fd = rfd; - pidnsfd = mntnsfd = netnsfd = -1; + pidnsfd = mntnsfd = netnsfd = usernsfd = -1; return 0; } -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) { +int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { + if (userns_fd >= 0) { + /* Can't setns to your own userns, since then you could + * escalate from non-root to root in your own namespace, so + * check if namespaces equal before attempting to enter. */ + _cleanup_free_ char *userns_fd_path = NULL; + int r; + if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) + return -ENOMEM; + + r = files_same(userns_fd_path, "/proc/self/ns/user"); + if (r < 0) + return r; + if (r) + userns_fd = -1; + } if (pidns_fd >= 0) if (setns(pidns_fd, CLONE_NEWPID) < 0) @@ -4522,6 +5119,10 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) { if (setns(netns_fd, CLONE_NEWNET) < 0) return -errno; + if (userns_fd >= 0) + if (setns(userns_fd, CLONE_NEWUSER) < 0) + return -errno; + if (root_fd >= 0) { if (fchdir(root_fd) < 0) return -errno; @@ -4616,6 +5217,8 @@ int mkostemp_safe(char *pattern, int flags) { return fd; } +/// UNNEEDED by elogind +#if 0 int open_tmpfile(const char *path, int flags) { char *p; int fd; @@ -4639,6 +5242,7 @@ int open_tmpfile(const char *path, int flags) { unlink(p); return fd; } +#endif // 0 int fd_warn_permissions(const char *path, int fd) { struct stat st; @@ -4685,7 +5289,6 @@ unsigned long personality_from_string(const char *p) { return PERSONALITY_INVALID; } -#endif // 0 const char* personality_to_string(unsigned long p) { @@ -4705,6 +5308,7 @@ const char* personality_to_string(unsigned long p) { return NULL; } +#endif // 0 uint64_t physical_memory(void) { long mem; @@ -4762,7 +5366,6 @@ void hexdump(FILE *f, const void *p, size_t s) { s -= 16; } } -#endif // 0 int update_reboot_param_file(const char *param) { int r = 0; @@ -5009,6 +5612,7 @@ int bind_remount_recursive(const char *prefix, bool ro) { } } } +#endif // 0 int fflush_and_check(FILE *f) { assert(f); @@ -5172,7 +5776,6 @@ int take_password_lock(const char *root) { return fd; } -#endif // 0 int is_symlink(const char *path) { struct stat info; @@ -5182,6 +5785,7 @@ int is_symlink(const char *path) { return !!S_ISLNK(info.st_mode); } +#endif // 0 int is_dir(const char* path, bool follow) { struct stat st; @@ -5209,7 +5813,7 @@ int is_device_node(const char *path) { } #endif // 0 -int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) { +int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { _cleanup_free_ char *s = NULL; size_t allocated = 0, sz = 0; int r; @@ -5222,13 +5826,19 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) { SINGLE_QUOTE_ESCAPE, DOUBLE_QUOTE, DOUBLE_QUOTE_ESCAPE, - SPACE, + SEPARATOR, } state = START; assert(p); - assert(*p); assert(ret); + if (!separators) + separators = WHITESPACE; + + /* Bail early if called after last value or with no input */ + if (!*p) + goto finish_force_terminate; + /* Parses the first word of a string, and returns it in * *ret. Removes all quotes in the process. When parsing fails * (because of an uneven number of quotes or similar), leaves @@ -5240,26 +5850,45 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) { switch (state) { case START: + if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) + if (!GREEDY_REALLOC(s, allocated, sz+1)) + return -ENOMEM; + if (c == 0) - goto finish; - else if (strchr(WHITESPACE, c)) + goto finish_force_terminate; + else if (strchr(separators, c)) { + if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { + (*p) ++; + goto finish_force_next; + } break; + } + + /* We found a non-blank character, so we will always + * want to return a string (even if it is empty), + * allocate it here. */ + if (!GREEDY_REALLOC(s, allocated, sz+1)) + return -ENOMEM; state = VALUE; /* fallthrough */ case VALUE: if (c == 0) - goto finish; - else if (c == '\'') + goto finish_force_terminate; + else if (c == '\'' && (flags & EXTRACT_QUOTES)) state = SINGLE_QUOTE; else if (c == '\\') state = VALUE_ESCAPE; - else if (c == '\"') + else if (c == '\"' && (flags & EXTRACT_QUOTES)) state = DOUBLE_QUOTE; - else if (strchr(WHITESPACE, c)) - state = SPACE; - else { + else if (strchr(separators, c)) { + if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { + (*p) ++; + goto finish_force_next; + } + state = SEPARATOR; + } else { if (!GREEDY_REALLOC(s, allocated, sz+2)) return -ENOMEM; @@ -5270,8 +5899,8 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) { case SINGLE_QUOTE: if (c == 0) { - if (flags & UNQUOTE_RELAX) - goto finish; + if (flags & EXTRACT_RELAX) + goto finish_force_terminate; return -EINVAL; } else if (c == '\'') state = VALUE; @@ -5309,29 +5938,29 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) { return -ENOMEM; if (c == 0) { - if ((flags & UNQUOTE_CUNESCAPE_RELAX) && - (state == VALUE_ESCAPE || flags & UNQUOTE_RELAX)) { + if ((flags & EXTRACT_CUNESCAPE_RELAX) && + (state == VALUE_ESCAPE || flags & EXTRACT_RELAX)) { /* If we find an unquoted trailing backslash and we're in - * UNQUOTE_CUNESCAPE_RELAX mode, keep it verbatim in the + * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the * output. * - * Unbalanced quotes will only be allowed in UNQUOTE_RELAX - * mode, UNQUOTE_CUNESCAP_RELAX mode does not allow them. + * Unbalanced quotes will only be allowed in EXTRACT_RELAX + * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them. */ s[sz++] = '\\'; - goto finish; + goto finish_force_terminate; } - if (flags & UNQUOTE_RELAX) - goto finish; + if (flags & EXTRACT_RELAX) + goto finish_force_terminate; return -EINVAL; } - if (flags & UNQUOTE_CUNESCAPE) { + if (flags & EXTRACT_CUNESCAPE) { uint32_t u; r = cunescape_one(*p, (size_t) -1, &c, &u); if (r < 0) { - if (flags & UNQUOTE_CUNESCAPE_RELAX) { + if (flags & EXTRACT_CUNESCAPE_RELAX) { s[sz++] = '\\'; s[sz++] = c; goto end_escape; @@ -5354,24 +5983,27 @@ end_escape: VALUE; break; - case SPACE: + case SEPARATOR: if (c == 0) + goto finish_force_terminate; + if (!strchr(separators, c)) goto finish; - if (!strchr(WHITESPACE, c)) - goto finish; - break; } (*p) ++; } +finish_force_terminate: + *p = NULL; finish: if (!s) { + *p = NULL; *ret = NULL; return 0; } +finish_force_next: s[sz] = 0; *ret = s; s = NULL; @@ -5379,26 +6011,29 @@ finish: return 1; } -int unquote_first_word_and_warn( +/// UNNEEDED by elogind +#if 0 +int extract_first_word_and_warn( const char **p, char **ret, - UnquoteFlags flags, + const char *separators, + ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue) { /* Try to unquote it, if it fails, warn about it and try again but this - * time using UNQUOTE_CUNESCAPE_RELAX to keep the backslashes verbatim + * time using EXTRACT_CUNESCAPE_RELAX to keep the backslashes verbatim * in invalid escape sequences. */ const char *save; int r; save = *p; - r = unquote_first_word(p, ret, flags); - if (r < 0 && !(flags&UNQUOTE_CUNESCAPE_RELAX)) { - /* Retry it with UNQUOTE_CUNESCAPE_RELAX. */ + r = extract_first_word(p, ret, separators, flags); + if (r < 0 && !(flags&EXTRACT_CUNESCAPE_RELAX)) { + /* Retry it with EXTRACT_CUNESCAPE_RELAX. */ *p = save; - r = unquote_first_word(p, ret, flags|UNQUOTE_CUNESCAPE_RELAX); + r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX); if (r < 0) log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue); @@ -5409,9 +6044,7 @@ int unquote_first_word_and_warn( return r; } -/// UNNEEDED by elogind -#if 0 -int unquote_many_words(const char **p, UnquoteFlags flags, ...) { +int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) { va_list ap; char **l; int n = 0, i, c, r; @@ -5437,7 +6070,7 @@ int unquote_many_words(const char **p, UnquoteFlags flags, ...) { l = newa0(char*, n); for (c = 0; c < n; c++) { - r = unquote_first_word(p, &l[c], flags); + r = extract_first_word(p, &l[c], separators, flags); if (r < 0) { int j; @@ -5492,6 +6125,8 @@ int free_and_strdup(char **p, const char *s) { return 1; } +/// UNNEEDED by elogind +#if 0 int ptsname_malloc(int fd, char **ret) { size_t l = 100; @@ -5519,10 +6154,8 @@ int ptsname_malloc(int fd, char **ret) { } } -/// UNNEEDED by elogind -#if 0 int openpt_in_namespace(pid_t pid, int flags) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1; + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; _cleanup_close_pair_ int pair[2] = { -1, -1 }; union { struct cmsghdr cmsghdr; @@ -5539,7 +6172,7 @@ int openpt_in_namespace(pid_t pid, int flags) { assert(pid > 0); - r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd); + r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); if (r < 0) return r; @@ -5555,7 +6188,7 @@ int openpt_in_namespace(pid_t pid, int flags) { pair[0] = safe_close(pair[0]); - r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd); + r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd); if (r < 0) _exit(EXIT_FAILURE); @@ -5563,6 +6196,9 @@ int openpt_in_namespace(pid_t pid, int flags) { if (master < 0) _exit(EXIT_FAILURE); + if (unlockpt(master) < 0) + _exit(EXIT_FAILURE); + cmsg = CMSG_FIRSTHDR(&mh); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; @@ -5657,6 +6293,8 @@ int fd_getcrtime(int fd, usec_t *usec) { return parse_crtime(le, usec); } +/// UNNEEDED by elogind +#if 0 int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) { le64_t le; ssize_t n; @@ -5670,8 +6308,6 @@ int fd_getcrtime_at(int dirfd, const char *name, usec_t *usec, int flags) { return parse_crtime(le, usec); } -/// UNNEEDED by elogind -#if 0 int path_getcrtime(const char *p, usec_t *usec) { le64_t le; ssize_t n; @@ -5687,7 +6323,6 @@ int path_getcrtime(const char *p, usec_t *usec) { return parse_crtime(le, usec); } -#endif // 0 int fd_setcrtime(int fd, usec_t usec) { le64_t le; @@ -5768,6 +6403,7 @@ int same_fd(int a, int b) { return fa == fb; } +#endif // 0 int chattr_fd(int fd, unsigned value, unsigned mask) { unsigned old_attr, new_attr; @@ -5803,6 +6439,8 @@ int chattr_fd(int fd, unsigned value, unsigned mask) { return 1; } +/// UNNEEDED by elogind +#if 0 int chattr_path(const char *p, unsigned value, unsigned mask) { _cleanup_close_ int fd = -1; @@ -5817,6 +6455,7 @@ int chattr_path(const char *p, unsigned value, unsigned mask) { return chattr_fd(fd, value, mask); } +#endif // 0 int read_attr_fd(int fd, unsigned *ret) { struct stat st; @@ -5835,6 +6474,8 @@ int read_attr_fd(int fd, unsigned *ret) { return 0; } +/// UNNEEDED by elogind +#if 0 int read_attr_path(const char *p, unsigned *ret) { _cleanup_close_ int fd = -1; @@ -5848,8 +6489,6 @@ int read_attr_path(const char *p, unsigned *ret) { return read_attr_fd(fd, ret); } -/// UNNEEDED by elogind -#if 0 static size_t nul_length(const uint8_t *p, size_t sz) { size_t n = 0; @@ -5981,6 +6620,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k return -1; } +/// UNNEEDED by elogind +#if 0 void cmsg_close_all(struct msghdr *mh) { struct cmsghdr *cmsg; @@ -5999,10 +6640,9 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char if (ret >= 0) return 0; - /* Even though renameat2() exists since Linux 3.15, btrfs added - * support for it later. If it is not implemented, fallback to another - * method. */ - if (errno != EINVAL) + /* renameat2() exists since Linux 3.15, btrfs added support for it later. + * If it is not implemented, fallback to another method. */ + if (!IN_SET(errno, EINVAL, ENOSYS)) return -errno; /* The link()/unlink() fallback does not work on directories. But @@ -6032,6 +6672,32 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char return 0; } +static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) { + assert(bad); + + for (; *s; s++) { + if (*s == '\\' || strchr(bad, *s)) + *(t++) = '\\'; + + *(t++) = *s; + } + + return t; +} + +char *shell_escape(const char *s, const char *bad) { + char *r, *t; + + r = new(char, strlen(s)*2+1); + if (!r) + return NULL; + + t = strcpy_backslash_escaped(r, s, bad); + *t = 0; + + return r; +} + char *shell_maybe_quote(const char *s) { const char *p; char *r, *t; @@ -6058,19 +6724,14 @@ char *shell_maybe_quote(const char *s) { *(t++) = '"'; t = mempcpy(t, s, p - s); - for (; *p; p++) { - - if (strchr(SHELL_NEED_ESCAPE, *p)) - *(t++) = '\\'; - - *(t++) = *p; - } + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE); *(t++)= '"'; *t = 0; return r; } +#endif // 0 int parse_mode(const char *s, mode_t *ret) { char *x; @@ -6093,6 +6754,8 @@ int parse_mode(const char *s, mode_t *ret) { return 0; } +/// UNNEEDED by elogind +#if 0 int mount_move_root(const char *path) { assert(path); @@ -6110,6 +6773,7 @@ int mount_move_root(const char *path) { return 0; } +#endif // 0 int reset_uid_gid(void) { @@ -6124,3 +6788,73 @@ int reset_uid_gid(void) { return 0; } + +int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) { + char *v; + size_t l; + ssize_t n; + + assert(path); + assert(name); + assert(value); + + for (l = 100; ; l = (size_t) n + 1) { + v = new0(char, l); + if (!v) + return -ENOMEM; + + if (allow_symlink) + n = lgetxattr(path, name, v, l); + else + n = getxattr(path, name, v, l); + + if (n >= 0 && (size_t) n < l) { + *value = v; + return n; + } + + free(v); + + if (n < 0 && errno != ERANGE) + return -errno; + + if (allow_symlink) + n = lgetxattr(path, name, NULL, 0); + else + n = getxattr(path, name, NULL, 0); + if (n < 0) + return -errno; + } +} + +int fgetxattr_malloc(int fd, const char *name, char **value) { + char *v; + size_t l; + ssize_t n; + + assert(fd >= 0); + assert(name); + assert(value); + + for (l = 100; ; l = (size_t) n + 1) { + v = new0(char, l); + if (!v) + return -ENOMEM; + + n = fgetxattr(fd, name, v, l); + + if (n >= 0 && (size_t) n < l) { + *value = v; + return n; + } + + free(v); + + if (n < 0 && errno != ERANGE) + return -errno; + + n = fgetxattr(fd, name, NULL, 0); + if (n < 0) + return -errno; + } +}