]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
uid-range: optimize to load uid_map file
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 16 Sep 2022 01:40:14 +0000 (10:40 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 16 Sep 2022 11:52:36 +0000 (20:52 +0900)
If uid_map contains many lines, then the previous logic takes O(n^2 log n),
This makes O(n log n).

src/basic/uid-range.c
src/basic/uid-range.h

index 73dd4b28921cd8cd8115e999ec2c356544d30c6b..8e68e0464c5c7711018ce33b3066ff6d46eb8e02 100644 (file)
@@ -69,10 +69,7 @@ static void uid_range_coalesce(UidRange **p, size_t *n) {
         }
 }
 
-int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr) {
-        bool found = false;
-        UidRange *x;
-
+int uid_range_add_internal(UidRange **p, size_t *n, uid_t start, uid_t nr, bool coalesce) {
         assert(p);
         assert(n);
 
@@ -82,37 +79,16 @@ 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);
+        if (!GREEDY_REALLOC(*p, *n + 1))
+                return -ENOMEM;
 
-                x->start = begin;
-                x->nr = end - begin;
-        } else {
-                UidRange *t;
+        (*p)[(*n)++] = (UidRange) {
+                .start = start,
+                .nr = nr,
+        };
 
-                t = reallocarray(*p, *n + 1, sizeof(UidRange));
-                if (!t)
-                        return -ENOMEM;
-
-                *p = t;
-                x = t + ((*n) ++);
-
-                x->start = start;
-                x->nr = nr;
-        }
-
-        uid_range_coalesce(p, n);
+        if (coalesce)
+                uid_range_coalesce(p, n);
 
         return *n;
 }
@@ -182,7 +158,9 @@ bool uid_range_covers(const UidRange *p, size_t n, uid_t start, uid_t nr) {
 }
 
 int uid_range_load_userns(UidRange **p, size_t *n, const char *path) {
+        _cleanup_free_ UidRange *q = NULL;
         _cleanup_fclose_ FILE *f = NULL;
+        size_t m = 0;
         int r;
 
         /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from
@@ -191,6 +169,9 @@ 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(p);
+        assert(n);
+
         if (!path)
                 path = "/proc/self/uid_map";
 
@@ -214,13 +195,20 @@ 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(&q, &m, uid_base, uid_range, /* coalesce = */ false);
                 if (r < 0)
                         return r;
         }
+
+        uid_range_coalesce(&q, &m);
+
+        *p = TAKE_PTR(q);
+        *n = m;
+
+        return 0;
 }
index 7259c9e371bf104fdde16391b37d61bf4da109e7..7e238409d11fb5ad9a96cf12c8dd179a945816b3 100644 (file)
@@ -8,7 +8,10 @@ typedef struct UidRange {
         uid_t start, nr;
 } UidRange;
 
-int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr);
+int uid_range_add_internal(UidRange **p, size_t *n, uid_t start, uid_t nr, bool coalesce);
+static inline int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr) {
+        return uid_range_add_internal(p, n, start, nr, true);
+}
 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);