diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c index 1adf4f62c23..c6b4b9d7950 100644 --- a/src/shared/uid-range.c +++ b/src/shared/uid-range.c @@ -5,8 +5,13 @@ #include #include "alloc-util.h" +#include "errno-util.h" +#include "fd-util.h" +#include "format-util.h" #include "macro.h" +#include "path-util.h" #include "sort-util.h" +#include "stat-util.h" #include "uid-range.h" #include "user-util.h" @@ -178,3 +183,47 @@ bool uid_range_contains(const UidRange *p, size_t n, uid_t uid) { return false; } + +int uid_range_load_userns(UidRange **p, size_t *n, const char *path) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from + * the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID + * maps). + * + * To simplify things this will modify the passed array in case of later failure. */ + + if (!path) + path = "/proc/self/uid_map"; + + f = fopen(path, "re"); + if (!f) { + r = -errno; + + if (r == -ENOENT && path_startswith(path, "/proc/") && proc_mounted() > 0) + return -EOPNOTSUPP; + + return r; + } + + for (;;) { + uid_t uid_base, uid_shift, uid_range; + int k; + + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range); + if (k == EOF) { + if (ferror(f)) + return errno_or_else(EIO); + + return 0; + } + if (k != 3) + return -EBADMSG; + + r = uid_range_add(p, n, uid_base, uid_range); + if (r < 0) + return r; + } +} diff --git a/src/shared/uid-range.h b/src/shared/uid-range.h index 1e118ec42c8..d256a6eebba 100644 --- a/src/shared/uid-range.h +++ b/src/shared/uid-range.h @@ -13,3 +13,5 @@ int uid_range_add_str(UidRange **p, size_t *n, const char *s); int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid); bool uid_range_contains(const UidRange *p, size_t n, uid_t uid); + +int uid_range_load_userns(UidRange **p, size_t *n, const char *path); diff --git a/src/test/test-uid-range.c b/src/test/test-uid-range.c index 2dcc2d04d5a..2e39628cefe 100644 --- a/src/test/test-uid-range.c +++ b/src/test/test-uid-range.c @@ -3,10 +3,16 @@ #include #include "alloc-util.h" +#include "errno-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "tests.h" +#include "tmpfile-util.h" #include "uid-range.h" #include "user-util.h" #include "util.h" +#include "virt.h" TEST(uid_range) { _cleanup_free_ UidRange *p = NULL; @@ -72,4 +78,45 @@ TEST(uid_range) { assert_se(p[0].nr == 1983); } +TEST(load_userns) { + _cleanup_(unlink_and_freep) char *fn = NULL; + _cleanup_free_ UidRange *p = NULL; + _cleanup_fclose_ FILE *f = NULL; + size_t n = 0; + int r; + + r = uid_range_load_userns(&p, &n, NULL); + if (ERRNO_IS_NOT_SUPPORTED(r)) + return; + + assert_se(r >= 0); + assert_se(uid_range_contains(p, n, getuid())); + + r = running_in_userns(); + if (r == 0) { + assert_se(n == 1); + assert_se(p[0].start == 0); + assert_se(p[0].nr == UINT32_MAX); + } + + assert_se(fopen_temporary(NULL, &f, &fn) >= 0); + fputs("0 0 20\n" + "100 0 20\n", f); + assert_se(fflush_and_check(f) >= 0); + + p = mfree(p); + n = 0; + + assert_se(uid_range_load_userns(&p, &n, fn) >= 0); + + assert_se(uid_range_contains(p, n, 0)); + assert_se(uid_range_contains(p, n, 19)); + assert_se(!uid_range_contains(p, n, 20)); + + assert_se(!uid_range_contains(p, n, 99)); + assert_se(uid_range_contains(p, n, 100)); + assert_se(uid_range_contains(p, n, 119)); + assert_se(!uid_range_contains(p, n, 120)); +} + DEFINE_TEST_MAIN(LOG_DEBUG);