]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/uid-range.c
uid-range: error code tweak for uid_range_load_userns()
[thirdparty/systemd.git] / src / shared / uid-range.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
8530dc44 2
a8fbdf54
TA
3#include <errno.h>
4#include <stdlib.h>
5#include <string.h>
6
62d74c78 7#include "alloc-util.h"
5674aa7a
LP
8#include "errno-util.h"
9#include "fd-util.h"
10#include "format-util.h"
a8fbdf54 11#include "macro.h"
5674aa7a 12#include "path-util.h"
760877e9 13#include "sort-util.h"
5674aa7a 14#include "stat-util.h"
8530dc44 15#include "uid-range.h"
b1d4f8e1 16#include "user-util.h"
8530dc44
LP
17
18static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) {
19 assert(range);
20
21 return range->start <= start + nr &&
22 range->start + range->nr >= start;
23}
24
09bbaa41 25static void uid_range_coalesce(UidRange **p, size_t *n) {
8530dc44
LP
26 assert(p);
27 assert(n);
28
09bbaa41
LP
29 for (size_t i = 0; i < *n; i++) {
30 for (size_t j = i + 1; j < *n; j++) {
8530dc44
LP
31 UidRange *x = (*p)+i, *y = (*p)+j;
32
33 if (uid_range_intersect(x, y->start, y->nr)) {
34 uid_t begin, end;
35
36 begin = MIN(x->start, y->start);
37 end = MAX(x->start + x->nr, y->start + y->nr);
38
39 x->start = begin;
40 x->nr = end - begin;
41
42 if (*n > j+1)
43 memmove(y, y+1, sizeof(UidRange) * (*n - j -1));
44
313cefa1 45 (*n)--;
8530dc44
LP
46 j--;
47 }
48 }
49 }
8530dc44
LP
50}
51
93bab288
YW
52static int uid_range_compare(const UidRange *a, const UidRange *b) {
53 int r;
8530dc44 54
93bab288
YW
55 r = CMP(a->start, b->start);
56 if (r != 0)
57 return r;
8530dc44 58
93bab288 59 return CMP(a->nr, b->nr);
8530dc44
LP
60}
61
09bbaa41 62int uid_range_add(UidRange **p, size_t *n, uid_t start, uid_t nr) {
8530dc44
LP
63 bool found = false;
64 UidRange *x;
8530dc44
LP
65
66 assert(p);
67 assert(n);
68
69 if (nr <= 0)
70 return 0;
71
0a5c6a57
LP
72 if (start > UINT32_MAX - nr) /* overflow check */
73 return -ERANGE;
74
09bbaa41 75 for (size_t i = 0; i < *n; i++) {
8530dc44
LP
76 x = (*p) + i;
77 if (uid_range_intersect(x, start, nr)) {
78 found = true;
79 break;
80 }
81 }
82
83 if (found) {
84 uid_t begin, end;
85
86 begin = MIN(x->start, start);
87 end = MAX(x->start + x->nr, start + nr);
88
89 x->start = begin;
90 x->nr = end - begin;
91 } else {
92 UidRange *t;
93
62d74c78 94 t = reallocarray(*p, *n + 1, sizeof(UidRange));
8530dc44
LP
95 if (!t)
96 return -ENOMEM;
97
98 *p = t;
99 x = t + ((*n) ++);
100
101 x->start = start;
102 x->nr = nr;
103 }
104
93bab288 105 typesafe_qsort(*p, *n, uid_range_compare);
8530dc44
LP
106 uid_range_coalesce(p, n);
107
108 return *n;
109}
110
09bbaa41 111int uid_range_add_str(UidRange **p, size_t *n, const char *s) {
8530dc44
LP
112 uid_t start, nr;
113 const char *t;
114 int r;
115
116 assert(p);
117 assert(n);
118 assert(s);
119
120 t = strchr(s, '-');
121 if (t) {
122 char *b;
123 uid_t end;
124
2f82562b 125 b = strndupa_safe(s, t - s);
8530dc44
LP
126 r = parse_uid(b, &start);
127 if (r < 0)
128 return r;
129
130 r = parse_uid(t+1, &end);
131 if (r < 0)
132 return r;
133
134 if (end < start)
135 return -EINVAL;
136
137 nr = end - start + 1;
138 } else {
139 r = parse_uid(s, &start);
140 if (r < 0)
141 return r;
142
143 nr = 1;
144 }
145
146 return uid_range_add(p, n, start, nr);
147}
148
09bbaa41 149int uid_range_next_lower(const UidRange *p, size_t n, uid_t *uid) {
fed1e721 150 uid_t closest = UID_INVALID, candidate;
8530dc44
LP
151
152 assert(p);
153 assert(uid);
154
0a5c6a57
LP
155 if (*uid == 0)
156 return -EBUSY;
157
8530dc44
LP
158 candidate = *uid - 1;
159
09bbaa41 160 for (size_t i = 0; i < n; i++) {
8530dc44
LP
161 uid_t begin, end;
162
163 begin = p[i].start;
164 end = p[i].start + p[i].nr - 1;
165
166 if (candidate >= begin && candidate <= end) {
167 *uid = candidate;
168 return 1;
169 }
170
171 if (end < candidate)
172 closest = end;
173 }
174
fed1e721 175 if (closest == UID_INVALID)
8530dc44
LP
176 return -EBUSY;
177
178 *uid = closest;
179 return 1;
180}
181
55656049
LP
182bool uid_range_covers(const UidRange *p, size_t n, uid_t start, uid_t nr) {
183 assert(p || n == 0);
184
185 if (nr == 0) /* empty range? always covered... */
186 return true;
187
188 if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */
189 return false;
8530dc44 190
09bbaa41 191 for (size_t i = 0; i < n; i++)
55656049 192 if (start >= p[i].start && start + nr <= p[i].start + p[i].nr)
8530dc44
LP
193 return true;
194
195 return false;
196}
5674aa7a
LP
197
198int uid_range_load_userns(UidRange **p, size_t *n, const char *path) {
199 _cleanup_fclose_ FILE *f = NULL;
200 int r;
201
202 /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from
203 * the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID
204 * maps).
205 *
206 * To simplify things this will modify the passed array in case of later failure. */
207
208 if (!path)
209 path = "/proc/self/uid_map";
210
211 f = fopen(path, "re");
212 if (!f) {
213 r = -errno;
214
cdba12b3
LP
215 if (r == -ENOENT && path_startswith(path, "/proc/"))
216 return proc_mounted() > 0 ? -EOPNOTSUPP : -ENOSYS;
5674aa7a
LP
217
218 return r;
219 }
220
221 for (;;) {
222 uid_t uid_base, uid_shift, uid_range;
223 int k;
224
225 errno = 0;
226 k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
227 if (k == EOF) {
228 if (ferror(f))
229 return errno_or_else(EIO);
230
231 return 0;
232 }
233 if (k != 3)
234 return -EBADMSG;
235
236 r = uid_range_add(p, n, uid_base, uid_range);
237 if (r < 0)
238 return r;
239 }
240}