| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "alloc-util.h" |
| #include "macro.h" |
| #include "sort-util.h" |
| #include "uid-range.h" |
| #include "user-util.h" |
| |
| static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) { |
| assert(range); |
| |
| return range->start <= start + nr && |
| range->start + range->nr >= start; |
| } |
| |
| static void uid_range_coalesce(UidRange **p, unsigned *n) { |
| assert(p); |
| assert(n); |
| |
| for (unsigned i = 0; i < *n; i++) { |
| for (unsigned 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--; |
| } |
| } |
| } |
| } |
| |
| static int uid_range_compare(const UidRange *a, const UidRange *b) { |
| int r; |
| |
| r = CMP(a->start, b->start); |
| if (r != 0) |
| return r; |
| |
| return CMP(a->nr, b->nr); |
| } |
| |
| int uid_range_add(UidRange **p, unsigned *n, uid_t start, uid_t nr) { |
| bool found = false; |
| UidRange *x; |
| |
| assert(p); |
| assert(n); |
| |
| if (nr <= 0) |
| return 0; |
| |
| for (unsigned 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) |
| return -ENOMEM; |
| |
| *p = t; |
| x = t + ((*n) ++); |
| |
| x->start = start; |
| x->nr = nr; |
| } |
| |
| typesafe_qsort(*p, *n, uid_range_compare); |
| uid_range_coalesce(p, n); |
| |
| return *n; |
| } |
| |
| int uid_range_add_str(UidRange **p, unsigned *n, const char *s) { |
| uid_t start, nr; |
| const char *t; |
| int r; |
| |
| assert(p); |
| assert(n); |
| assert(s); |
| |
| t = strchr(s, '-'); |
| if (t) { |
| char *b; |
| uid_t end; |
| |
| b = strndupa(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); |
| } |
| |
| int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) { |
| uid_t closest = UID_INVALID, candidate; |
| |
| assert(p); |
| assert(uid); |
| |
| candidate = *uid - 1; |
| |
| for (unsigned i = 0; i < n; i++) { |
| uid_t begin, end; |
| |
| begin = p[i].start; |
| end = p[i].start + p[i].nr - 1; |
| |
| if (candidate >= begin && candidate <= end) { |
| *uid = candidate; |
| return 1; |
| } |
| |
| if (end < candidate) |
| closest = end; |
| } |
| |
| if (closest == UID_INVALID) |
| return -EBUSY; |
| |
| *uid = closest; |
| return 1; |
| } |
| |
| bool uid_range_contains(const UidRange *p, unsigned n, uid_t uid) { |
| assert(p); |
| assert(uid); |
| |
| for (unsigned i = 0; i < n; i++) |
| if (uid >= p[i].start && uid < p[i].start + p[i].nr) |
| return true; |
| |
| return false; |
| } |