diff --git a/src/basic/uid-range.c b/src/basic/uid-range.c index 630bf42f92..8463599276 100644 --- a/src/basic/uid-range.c +++ b/src/basic/uid-range.c @@ -15,43 +15,27 @@ #include "uid-range.h" #include "user-util.h" -static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) { - assert(range); +UidRange *uid_range_free(UidRange *range) { + if (!range) + return NULL; - return range->start <= start + nr && - range->start + range->nr >= start; + free(range->entries); + return mfree(range); } -static void uid_range_coalesce(UidRange **p, size_t *n) { - assert(p); - assert(n); +static bool uid_range_entry_intersect(const UidRangeEntry *a, const UidRangeEntry *b) { + assert(a); + assert(b); - for (size_t i = 0; i < *n; i++) { - for (size_t j = i + 1; j < *n; j++) { - UidRange *x = (*p)+i, *y = (*p)+j; - - if (uid_range_intersect(x, y->start, y->nr)) { - uid_t begin, end; - - begin = MIN(x->start, y->start); - end = MAX(x->start + x->nr, y->start + y->nr); - - x->start = begin; - x->nr = end - begin; - - if (*n > j+1) - memmove(y, y+1, sizeof(UidRange) * (*n - j -1)); - - (*n)--; - j--; - } - } - } + return a->start <= b->start + b->nr && a->start + a->nr >= b->start; } -static int uid_range_compare(const UidRange *a, const UidRange *b) { +static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry *b) { int r; + assert(a); + assert(b); + r = CMP(a->start, b->start); if (r != 0) return r; @@ -59,12 +43,44 @@ static int uid_range_compare(const UidRange *a, const UidRange *b) { return CMP(a->nr, b->nr); } -int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr) { - bool found = false; - UidRange *x; +static void uid_range_coalesce(UidRange *range) { + assert(range); - assert(p); - assert(n); + if (range->n_entries <= 0) + return; + + typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare); + + for (size_t i = 0; i < range->n_entries; i++) { + UidRangeEntry *x = range->entries + i; + + for (size_t j = i + 1; j < range->n_entries; j++) { + UidRangeEntry *y = range->entries + j; + uid_t begin, end; + + if (!uid_range_entry_intersect(x, y)) + break; + + begin = MIN(x->start, y->start); + end = MAX(x->start + x->nr, y->start + y->nr); + + x->start = begin; + x->nr = end - begin; + + if (range->n_entries > j + 1) + memmove(y, y + 1, sizeof(UidRangeEntry) * (range->n_entries - j - 1)); + + range->n_entries--; + j--; + } + } +} + +int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce) { + _cleanup_(uid_range_freep) UidRange *range_new = NULL; + UidRange *p; + + assert(range); if (nr <= 0) return 0; @@ -72,84 +88,51 @@ int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr) { if (start > UINT32_MAX - nr) /* overflow check */ return -ERANGE; - for (size_t i = 0; i < *n; i++) { - x = (*p) + i; - if (uid_range_intersect(x, start, nr)) { - found = true; - break; - } - } - - if (found) { - uid_t begin, end; - - begin = MIN(x->start, start); - end = MAX(x->start + x->nr, start + nr); - - x->start = begin; - x->nr = end - begin; - } else { - UidRange *t; - - t = reallocarray(*p, *n + 1, sizeof(UidRange)); - if (!t) + if (*range) + p = *range; + else { + range_new = new0(UidRange, 1); + if (!range_new) return -ENOMEM; - *p = t; - x = t + ((*n) ++); - - x->start = start; - x->nr = nr; + p = range_new; } - typesafe_qsort(*p, *n, uid_range_compare); - uid_range_coalesce(p, n); + if (!GREEDY_REALLOC(p->entries, p->n_entries + 1)) + return -ENOMEM; - return *n; + p->entries[p->n_entries++] = (UidRangeEntry) { + .start = start, + .nr = nr, + }; + + if (coalesce) + uid_range_coalesce(p); + + TAKE_PTR(range_new); + *range = p; + + return 0; } -int uid_range_add_str(UidRange **p, size_t *n, const char *s) { - uid_t start, nr; - const char *t; +int uid_range_add_str(UidRange **range, const char *s) { + uid_t start, end; int r; - assert(p); - assert(n); + assert(range); assert(s); - t = strchr(s, '-'); - if (t) { - char *b; - uid_t end; + r = parse_uid_range(s, &start, &end); + if (r < 0) + return r; - b = strndupa_safe(s, t - s); - r = parse_uid(b, &start); - if (r < 0) - return r; - - r = parse_uid(t+1, &end); - if (r < 0) - return r; - - if (end < start) - return -EINVAL; - - nr = end - start + 1; - } else { - r = parse_uid(s, &start); - if (r < 0) - return r; - - nr = 1; - } - - return uid_range_add(p, n, start, nr); + return uid_range_add_internal(range, start, end - start + 1, /* coalesce = */ true); } -int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid) { +int uid_range_next_lower(const UidRange *range, uid_t *uid) { uid_t closest = UID_INVALID, candidate; - assert(p); + assert(range); assert(uid); if (*uid == 0) @@ -157,11 +140,11 @@ int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid) { candidate = *uid - 1; - for (size_t i = 0; i < n; i++) { + for (size_t i = 0; i < range->n_entries; i++) { uid_t begin, end; - begin = p[i].start; - end = p[i].start + p[i].nr - 1; + begin = range->entries[i].start; + end = range->entries[i].start + range->entries[i].nr - 1; if (candidate >= begin && candidate <= end) { *uid = candidate; @@ -179,23 +162,26 @@ int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid) { return 1; } -bool uid_range_covers(const UidRange *p, size_t n, uid_t start, uid_t nr) { - assert(p || n == 0); - +bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) { if (nr == 0) /* empty range? always covered... */ return true; if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */ return false; - for (size_t i = 0; i < n; i++) - if (start >= p[i].start && start + nr <= p[i].start + p[i].nr) + if (!range) + return false; + + for (size_t i = 0; i < range->n_entries; i++) + if (start >= range->entries[i].start && + start + nr <= range->entries[i].start + range->entries[i].nr) return true; return false; } -int uid_range_load_userns(UidRange **p, size_t *n, const char *path) { +int uid_range_load_userns(UidRange **ret, const char *path) { + _cleanup_(uid_range_freep) UidRange *range = NULL; _cleanup_fclose_ FILE *f = NULL; int r; @@ -205,6 +191,8 @@ int uid_range_load_userns(UidRange **p, size_t *n, const char *path) { * * To simplify things this will modify the passed array in case of later failure. */ + assert(ret); + if (!path) path = "/proc/self/uid_map"; @@ -218,6 +206,10 @@ int uid_range_load_userns(UidRange **p, size_t *n, const char *path) { return r; } + range = new0(UidRange, 1); + if (!range) + return -ENOMEM; + for (;;) { uid_t uid_base, uid_shift, uid_range; int k; @@ -228,13 +220,18 @@ int uid_range_load_userns(UidRange **p, size_t *n, const char *path) { if (ferror(f)) return errno_or_else(EIO); - return 0; + break; } if (k != 3) return -EBADMSG; - r = uid_range_add(p, n, uid_base, uid_range); + r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false); if (r < 0) return r; } + + uid_range_coalesce(range); + + *ret = TAKE_PTR(range); + return 0; } diff --git a/src/basic/uid-range.h b/src/basic/uid-range.h index 7259c9e371..461a511737 100644 --- a/src/basic/uid-range.h +++ b/src/basic/uid-range.h @@ -4,18 +4,31 @@ #include #include -typedef struct UidRange { +#include "macro.h" + +typedef struct UidRangeEntry { uid_t start, nr; +} UidRangeEntry; + +typedef struct UidRange { + UidRangeEntry *entries; + size_t n_entries; } UidRange; -int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr); -int uid_range_add_str(UidRange **p, size_t *n, const char *s); +UidRange *uid_range_free(UidRange *range); +DEFINE_TRIVIAL_CLEANUP_FUNC(UidRange*, uid_range_free); -int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid); -bool uid_range_covers(const UidRange *p, size_t n, uid_t start, uid_t nr); +int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce); +static inline int uid_range_add(UidRange **range, uid_t start, uid_t nr) { + return uid_range_add_internal(range, start, nr, true); +} +int uid_range_add_str(UidRange **range, const char *s); -static inline bool uid_range_contains(const UidRange *p, size_t n, uid_t uid) { - return uid_range_covers(p, n, uid, 1); +int uid_range_next_lower(const UidRange *range, uid_t *uid); + +bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr); +static inline bool uid_range_contains(const UidRange *range, uid_t uid) { + return uid_range_covers(range, uid, 1); } -int uid_range_load_userns(UidRange **p, size_t *n, const char *path); +int uid_range_load_userns(UidRange **ret, const char *path); diff --git a/src/core/manager.c b/src/core/manager.c index cc89ae3f9c..90e126840a 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -4457,8 +4457,7 @@ int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t re } static int short_uid_range(const char *path) { - _cleanup_free_ UidRange *p = NULL; - size_t n = 0; + _cleanup_(uid_range_freep) UidRange *p = NULL; int r; assert(path); @@ -4466,13 +4465,14 @@ static int short_uid_range(const char *path) { /* Taint systemd if we the UID range assigned to this environment doesn't at least cover 0…65534, * i.e. from root to nobody. */ - r = uid_range_load_userns(&p, &n, path); - if (ERRNO_IS_NOT_SUPPORTED(r)) - return false; - if (r < 0) + r = uid_range_load_userns(&p, path); + if (r < 0) { + if (ERRNO_IS_NOT_SUPPORTED(r)) + return false; return log_debug_errno(r, "Failed to load %s: %m", path); + } - return !uid_range_covers(p, n, 0, 65535); + return !uid_range_covers(p, 0, 65535); } char* manager_taint_string(const Manager *m) { diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c index 072c6b94d3..48f425396a 100644 --- a/src/libsystemd/sd-device/device-monitor.c +++ b/src/libsystemd/sd-device/device-monitor.c @@ -48,7 +48,6 @@ struct sd_device_monitor { bool bound; UidRange *mapped_userns_uid_range; - size_t n_uid_range; Hashmap *subsystem_filter; Set *tag_filter; @@ -174,7 +173,6 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group, .bound = fd >= 0, .snl.nl.nl_family = AF_NETLINK, .snl.nl.nl_groups = group, - .n_uid_range = SIZE_MAX, }; if (fd >= 0) { @@ -376,7 +374,7 @@ static sd_device_monitor *device_monitor_free(sd_device_monitor *m) { (void) sd_device_monitor_detach_event(m); - free(m->mapped_userns_uid_range); + uid_range_free(m->mapped_userns_uid_range); free(m->description); hashmap_free(m->subsystem_filter); set_free(m->tag_filter); @@ -468,15 +466,14 @@ static bool check_sender_uid(sd_device_monitor *m, uid_t uid) { if (uid == getuid() || uid == geteuid()) return true; - if (m->n_uid_range == SIZE_MAX) { - m->n_uid_range = 0; - r = uid_range_load_userns(&m->mapped_userns_uid_range, &m->n_uid_range, NULL); + if (!m->mapped_userns_uid_range) { + r = uid_range_load_userns(&m->mapped_userns_uid_range, NULL); if (r < 0) log_monitor_errno(m, r, "Failed to load UID ranges mapped to the current user namespace, ignoring: %m"); } /* Trust messages come from outside of the current user namespace. */ - if (m->n_uid_range != SIZE_MAX && !uid_range_contains(m->mapped_userns_uid_range, m->n_uid_range, uid)) + if (!uid_range_contains(m->mapped_userns_uid_range, uid)) return true; /* Otherwise, refuse messages. */ diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 491e4a0ea2..6f66f087cb 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -107,7 +107,6 @@ static Set *database_users = NULL, *database_groups = NULL; static uid_t search_uid = UID_INVALID; static UidRange *uid_range = NULL; -static size_t n_uid_range = 0; static UGIDAllocationRange login_defs = {}; static bool login_defs_need_warning = false; @@ -123,7 +122,7 @@ STATIC_DESTRUCTOR_REGISTER(database_users, set_free_freep); STATIC_DESTRUCTOR_REGISTER(database_by_gid, hashmap_freep); STATIC_DESTRUCTOR_REGISTER(database_by_groupname, hashmap_freep); STATIC_DESTRUCTOR_REGISTER(database_groups, set_free_freep); -STATIC_DESTRUCTOR_REGISTER(uid_range, freep); +STATIC_DESTRUCTOR_REGISTER(uid_range, uid_range_freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); @@ -1113,7 +1112,7 @@ static int add_user(Item *i) { if (read_id_from_file(i, &c, NULL) > 0) { - if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) + if (c <= 0 || !uid_range_contains(uid_range, c)) log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name); else { r = uid_is_ok(c, i->name, true); @@ -1144,7 +1143,7 @@ static int add_user(Item *i) { maybe_emit_login_defs_warning(); for (;;) { - r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); + r = uid_range_next_lower(uid_range, &search_uid); if (r < 0) return log_error_errno(r, "No free user ID available for %s.", i->name); @@ -1297,7 +1296,7 @@ static int add_group(Item *i) { if (read_id_from_file(i, NULL, &c) > 0) { - if (c <= 0 || !uid_range_contains(uid_range, n_uid_range, c)) + if (c <= 0 || !uid_range_contains(uid_range, c)) log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name); else { r = gid_is_ok(c, true); @@ -1318,7 +1317,7 @@ static int add_group(Item *i) { for (;;) { /* We look for new GIDs in the UID pool! */ - r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); + r = uid_range_next_lower(uid_range, &search_uid); if (r < 0) return log_error_errno(r, "No free group ID available for %s.", i->name); @@ -1668,7 +1667,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { action[0], description ? "GECOS" : home ? "home directory" : "login shell"); - r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id); + r = uid_range_add_str(&uid_range, resolved_id); if (r < 0) return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EINVAL), "Invalid UID range %s.", resolved_id); @@ -2169,7 +2168,7 @@ static int run(int argc, char *argv[]) { uid_t begin = login_defs.system_alloc_uid_min, end = MIN3((uid_t) SYSTEM_UID_MAX, login_defs.system_uid_max, login_defs.system_gid_max); if (begin < end) { - r = uid_range_add(&uid_range, &n_uid_range, begin, end - begin + 1); + r = uid_range_add(&uid_range, begin, end - begin + 1); if (r < 0) return log_oom(); } diff --git a/src/test/test-uid-range.c b/src/test/test-uid-range.c index be8530bdd8..ce8b8e4bca 100644 --- a/src/test/test-uid-range.c +++ b/src/test/test-uid-range.c @@ -15,105 +15,104 @@ #include "virt.h" TEST(uid_range) { - _cleanup_free_ UidRange *p = NULL; - size_t n = 0; + _cleanup_(uid_range_freep) UidRange *p = NULL; uid_t search; - assert_se(uid_range_covers(p, n, 0, 0)); - assert_se(!uid_range_covers(p, n, 0, 1)); - assert_se(!uid_range_covers(p, n, 100, UINT32_MAX)); + assert_se(uid_range_covers(p, 0, 0)); + assert_se(!uid_range_covers(p, 0, 1)); + assert_se(!uid_range_covers(p, 100, UINT32_MAX)); - assert_se(uid_range_add_str(&p, &n, "500-999") >= 0); - assert_se(n == 1); - assert_se(p[0].start == 500); - assert_se(p[0].nr == 500); + assert_se(uid_range_add_str(&p, "500-999") >= 0); + assert_se(p); + assert_se(p->n_entries == 1); + assert_se(p->entries[0].start == 500); + assert_se(p->entries[0].nr == 500); - assert_se(!uid_range_contains(p, n, 499)); - assert_se(uid_range_contains(p, n, 500)); - assert_se(uid_range_contains(p, n, 999)); - assert_se(!uid_range_contains(p, n, 1000)); + assert_se(!uid_range_contains(p, 499)); + assert_se(uid_range_contains(p, 500)); + assert_se(uid_range_contains(p, 999)); + assert_se(!uid_range_contains(p, 1000)); - assert_se(!uid_range_covers(p, n, 100, 150)); - assert_se(!uid_range_covers(p, n, 400, 200)); - assert_se(!uid_range_covers(p, n, 499, 1)); - assert_se(uid_range_covers(p, n, 500, 1)); - assert_se(uid_range_covers(p, n, 501, 10)); - assert_se(uid_range_covers(p, n, 999, 1)); - assert_se(!uid_range_covers(p, n, 999, 2)); - assert_se(!uid_range_covers(p, n, 1000, 1)); - assert_se(!uid_range_covers(p, n, 1000, 100)); - assert_se(!uid_range_covers(p, n, 1001, 100)); + assert_se(!uid_range_covers(p, 100, 150)); + assert_se(!uid_range_covers(p, 400, 200)); + assert_se(!uid_range_covers(p, 499, 1)); + assert_se(uid_range_covers(p, 500, 1)); + assert_se(uid_range_covers(p, 501, 10)); + assert_se(uid_range_covers(p, 999, 1)); + assert_se(!uid_range_covers(p, 999, 2)); + assert_se(!uid_range_covers(p, 1000, 1)); + assert_se(!uid_range_covers(p, 1000, 100)); + assert_se(!uid_range_covers(p, 1001, 100)); search = UID_INVALID; - assert_se(uid_range_next_lower(p, n, &search)); + assert_se(uid_range_next_lower(p, &search)); assert_se(search == 999); - assert_se(uid_range_next_lower(p, n, &search)); + assert_se(uid_range_next_lower(p, &search)); assert_se(search == 998); search = 501; - assert_se(uid_range_next_lower(p, n, &search)); + assert_se(uid_range_next_lower(p, &search)); assert_se(search == 500); - assert_se(uid_range_next_lower(p, n, &search) == -EBUSY); + assert_se(uid_range_next_lower(p, &search) == -EBUSY); - assert_se(uid_range_add_str(&p, &n, "1000") >= 0); - assert_se(n == 1); - assert_se(p[0].start == 500); - assert_se(p[0].nr == 501); + assert_se(uid_range_add_str(&p, "1000") >= 0); + assert_se(p->n_entries == 1); + assert_se(p->entries[0].start == 500); + assert_se(p->entries[0].nr == 501); - assert_se(uid_range_add_str(&p, &n, "30-40") >= 0); - assert_se(n == 2); - assert_se(p[0].start == 30); - assert_se(p[0].nr == 11); - assert_se(p[1].start == 500); - assert_se(p[1].nr == 501); + assert_se(uid_range_add_str(&p, "30-40") >= 0); + assert_se(p->n_entries == 2); + assert_se(p->entries[0].start == 30); + assert_se(p->entries[0].nr == 11); + assert_se(p->entries[1].start == 500); + assert_se(p->entries[1].nr == 501); - assert_se(uid_range_add_str(&p, &n, "60-70") >= 0); - assert_se(n == 3); - assert_se(p[0].start == 30); - assert_se(p[0].nr == 11); - assert_se(p[1].start == 60); - assert_se(p[1].nr == 11); - assert_se(p[2].start == 500); - assert_se(p[2].nr == 501); + assert_se(uid_range_add_str(&p, "60-70") >= 0); + assert_se(p->n_entries == 3); + assert_se(p->entries[0].start == 30); + assert_se(p->entries[0].nr == 11); + assert_se(p->entries[1].start == 60); + assert_se(p->entries[1].nr == 11); + assert_se(p->entries[2].start == 500); + assert_se(p->entries[2].nr == 501); - assert_se(uid_range_add_str(&p, &n, "20-2000") >= 0); - assert_se(n == 1); - assert_se(p[0].start == 20); - assert_se(p[0].nr == 1981); + assert_se(uid_range_add_str(&p, "20-2000") >= 0); + assert_se(p->n_entries == 1); + assert_se(p->entries[0].start == 20); + assert_se(p->entries[0].nr == 1981); - assert_se(uid_range_add_str(&p, &n, "2002") >= 0); - assert_se(n == 2); - assert_se(p[0].start == 20); - assert_se(p[0].nr == 1981); - assert_se(p[1].start == 2002); - assert_se(p[1].nr == 1); + assert_se(uid_range_add_str(&p, "2002") >= 0); + assert_se(p->n_entries == 2); + assert_se(p->entries[0].start == 20); + assert_se(p->entries[0].nr == 1981); + assert_se(p->entries[1].start == 2002); + assert_se(p->entries[1].nr == 1); - assert_se(uid_range_add_str(&p, &n, "2001") >= 0); - assert_se(n == 1); - assert_se(p[0].start == 20); - assert_se(p[0].nr == 1983); + assert_se(uid_range_add_str(&p, "2001") >= 0); + assert_se(p->n_entries == 1); + assert_se(p->entries[0].start == 20); + assert_se(p->entries[0].nr == 1983); } TEST(load_userns) { + _cleanup_(uid_range_freep) UidRange *p = NULL; _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)) + r = uid_range_load_userns(&p, NULL); + if (r < 0 && ERRNO_IS_NOT_SUPPORTED(r)) return; assert_se(r >= 0); - assert_se(uid_range_contains(p, n, getuid())); + assert_se(uid_range_contains(p, 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(p->n_entries == 1); + assert_se(p->entries[0].start == 0); + assert_se(p->entries[0].nr == UINT32_MAX); - assert_se(uid_range_covers(p, n, 0, UINT32_MAX)); + assert_se(uid_range_covers(p, 0, UINT32_MAX)); } assert_se(fopen_temporary(NULL, &f, &fn) >= 0); @@ -121,19 +120,57 @@ TEST(load_userns) { "100 0 20\n", f); assert_se(fflush_and_check(f) >= 0); - p = mfree(p); - n = 0; + p = uid_range_free(p); - assert_se(uid_range_load_userns(&p, &n, fn) >= 0); + assert_se(uid_range_load_userns(&p, 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, 0)); + assert_se(uid_range_contains(p, 19)); + assert_se(!uid_range_contains(p, 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)); + assert_se(!uid_range_contains(p, 99)); + assert_se(uid_range_contains(p, 100)); + assert_se(uid_range_contains(p, 119)); + assert_se(!uid_range_contains(p, 120)); +} + +TEST(uid_range_coalesce) { + _cleanup_(uid_range_freep) UidRange *p = NULL; + + for (size_t i = 0; i < 10; i++) { + assert_se(uid_range_add_internal(&p, i * 10, 10, /* coalesce = */ false) >= 0); + assert_se(uid_range_add_internal(&p, i * 10 + 5, 10, /* coalesce = */ false) >= 0); + } + + assert_se(uid_range_add_internal(&p, 100, 1, /* coalesce = */ true) >= 0); + assert_se(p->n_entries == 1); + assert_se(p->entries[0].start == 0); + assert_se(p->entries[0].nr == 105); + + p = uid_range_free(p); + + for (size_t i = 0; i < 10; i++) { + assert_se(uid_range_add_internal(&p, (10 - i) * 10, 10, /* coalesce = */ false) >= 0); + assert_se(uid_range_add_internal(&p, (10 - i) * 10 + 5, 10, /* coalesce = */ false) >= 0); + } + + assert_se(uid_range_add_internal(&p, 100, 1, /* coalesce = */ true) >= 0); + assert_se(p->n_entries == 1); + assert_se(p->entries[0].start == 10); + assert_se(p->entries[0].nr == 105); + + p = uid_range_free(p); + + for (size_t i = 0; i < 10; i++) { + assert_se(uid_range_add_internal(&p, i * 10, 10, /* coalesce = */ false) >= 0); + assert_se(uid_range_add_internal(&p, i * 10 + 5, 10, /* coalesce = */ false) >= 0); + assert_se(uid_range_add_internal(&p, (10 - i) * 10, 10, /* coalesce = */ false) >= 0); + assert_se(uid_range_add_internal(&p, (10 - i) * 10 + 5, 10, /* coalesce = */ false) >= 0); + } + assert_se(uid_range_add_internal(&p, 100, 1, /* coalesce = */ true) >= 0); + assert_se(p->n_entries == 1); + assert_se(p->entries[0].start == 0); + assert_se(p->entries[0].nr == 115); } DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c index 1930fb916b..5e9bce3bae 100644 --- a/src/userdb/userdbctl.c +++ b/src/userdb/userdbctl.c @@ -168,20 +168,15 @@ static const struct { }, }; -static int table_add_uid_boundaries( - Table *table, - const UidRange *p, - size_t n) { +static int table_add_uid_boundaries(Table *table, const UidRange *p) { int r; assert(table); - assert(p || n == 0); for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) { _cleanup_free_ char *name = NULL, *comment = NULL; - if (n > 0 && - !uid_range_covers(p, n, uid_range_table[i].first, uid_range_table[i].last - uid_range_table[i].first + 1)) + if (!uid_range_covers(p, uid_range_table[i].first, uid_range_table[i].last - uid_range_table[i].first + 1)) continue; name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN), @@ -277,9 +272,9 @@ static int add_unavailable_uid(Table *table, uid_t start, uid_t end) { return table_log_add_error(r); free(name); - name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN), + name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_UP), " end unavailable users ", - special_glyph(SPECIAL_GLYPH_ARROW_DOWN)); + special_glyph(SPECIAL_GLYPH_ARROW_UP)); if (!name) return log_oom(); @@ -306,31 +301,31 @@ static int add_unavailable_uid(Table *table, uid_t start, uid_t end) { static int table_add_uid_map( Table *table, const UidRange *p, - size_t n, int (*add_unavailable)(Table *t, uid_t start, uid_t end)) { uid_t focus = 0; int n_added = 0, r; assert(table); - assert(p || n == 0); assert(add_unavailable); - for (size_t i = 0; i < n; i++) { - if (focus < p[i].start) { - r = add_unavailable(table, focus, p[i].start-1); + for (size_t i = 0; p && i < p->n_entries; i++) { + UidRangeEntry *x = p->entries + i; + + if (focus < x->start) { + r = add_unavailable(table, focus, x->start-1); if (r < 0) return r; n_added += r; } - if (p[i].start > UINT32_MAX - p[i].nr) { /* overflow check */ + if (x->start > UINT32_MAX - x->nr) { /* overflow check */ focus = UINT32_MAX; break; } - focus = p[i].start + p[i].nr; + focus = x->start + x->nr; } if (focus < UINT32_MAX-1) { @@ -429,19 +424,18 @@ static int display_user(int argc, char *argv[], void *userdata) { } if (table) { - _cleanup_free_ UidRange *uid_range = NULL; + _cleanup_(uid_range_freep) UidRange *uid_range = NULL; int boundary_lines, uid_map_lines; - size_t n_uid_range = 0; - r = uid_range_load_userns(&uid_range, &n_uid_range, "/proc/self/uid_map"); + r = uid_range_load_userns(&uid_range, "/proc/self/uid_map"); if (r < 0) log_debug_errno(r, "Failed to load /proc/self/uid_map, ignoring: %m"); - boundary_lines = table_add_uid_boundaries(table, uid_range, n_uid_range); + boundary_lines = table_add_uid_boundaries(table, uid_range); if (boundary_lines < 0) return boundary_lines; - uid_map_lines = table_add_uid_map(table, uid_range, n_uid_range, add_unavailable_uid); + uid_map_lines = table_add_uid_map(table, uid_range, add_unavailable_uid); if (uid_map_lines < 0) return uid_map_lines; @@ -531,20 +525,15 @@ static int show_group(GroupRecord *gr, Table *table) { return 0; } -static int table_add_gid_boundaries( - Table *table, - const UidRange *p, - size_t n) { +static int table_add_gid_boundaries(Table *table, const UidRange *p) { int r; assert(table); - assert(p || n == 0); for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) { _cleanup_free_ char *name = NULL, *comment = NULL; - if (n > 0 && - !uid_range_covers(p, n, uid_range_table[i].first, uid_range_table[i].last)) + if (!uid_range_covers(p, uid_range_table[i].first, uid_range_table[i].last)) continue; name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN), @@ -631,9 +620,9 @@ static int add_unavailable_gid(Table *table, uid_t start, uid_t end) { return table_log_add_error(r); free(name); - name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN), + name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_UP), " end unavailable groups ", - special_glyph(SPECIAL_GLYPH_ARROW_DOWN)); + special_glyph(SPECIAL_GLYPH_ARROW_UP)); if (!name) return log_oom(); @@ -738,19 +727,18 @@ static int display_group(int argc, char *argv[], void *userdata) { } if (table) { - _cleanup_free_ UidRange *gid_range = NULL; + _cleanup_(uid_range_freep) UidRange *gid_range = NULL; int boundary_lines, gid_map_lines; - size_t n_gid_range = 0; - r = uid_range_load_userns(&gid_range, &n_gid_range, "/proc/self/gid_map"); + r = uid_range_load_userns(&gid_range, "/proc/self/gid_map"); if (r < 0) log_debug_errno(r, "Failed to load /proc/self/gid_map, ignoring: %m"); - boundary_lines = table_add_gid_boundaries(table, gid_range, n_gid_range); + boundary_lines = table_add_gid_boundaries(table, gid_range); if (boundary_lines < 0) return boundary_lines; - gid_map_lines = table_add_uid_map(table, gid_range, n_gid_range, add_unavailable_gid); + gid_map_lines = table_add_uid_map(table, gid_range, add_unavailable_gid); if (gid_map_lines < 0) return gid_map_lines;