]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/uid-range.c
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
[thirdparty/systemd.git] / src / basic / 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 17
8dcc66ce
YW
18UidRange *uid_range_free(UidRange *range) {
19 if (!range)
20 return NULL;
21
22 free(range->entries);
23 return mfree(range);
24}
25
26static bool uid_range_entry_intersect(const UidRangeEntry *a, const UidRangeEntry *b) {
6cc6cd92
YW
27 assert(a);
28 assert(b);
8530dc44 29
6cc6cd92 30 return a->start <= b->start + b->nr && a->start + a->nr >= b->start;
8530dc44
LP
31}
32
8dcc66ce 33static int uid_range_entry_compare(const UidRangeEntry *a, const UidRangeEntry *b) {
34eeba47
YW
34 int r;
35
36 assert(a);
37 assert(b);
38
39 r = CMP(a->start, b->start);
40 if (r != 0)
41 return r;
42
43 return CMP(a->nr, b->nr);
44}
45
8dcc66ce
YW
46static void uid_range_coalesce(UidRange *range) {
47 assert(range);
8530dc44 48
8dcc66ce 49 if (range->n_entries <= 0)
34eeba47
YW
50 return;
51
8dcc66ce 52 typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare);
34eeba47 53
8dcc66ce
YW
54 for (size_t i = 0; i < range->n_entries; i++) {
55 UidRangeEntry *x = range->entries + i;
b5739767 56
8dcc66ce
YW
57 for (size_t j = i + 1; j < range->n_entries; j++) {
58 UidRangeEntry *y = range->entries + j;
b5739767 59 uid_t begin, end;
8530dc44 60
8dcc66ce 61 if (!uid_range_entry_intersect(x, y))
b5739767 62 break;
8530dc44 63
b5739767
YW
64 begin = MIN(x->start, y->start);
65 end = MAX(x->start + x->nr, y->start + y->nr);
8530dc44 66
b5739767
YW
67 x->start = begin;
68 x->nr = end - begin;
8530dc44 69
8dcc66ce
YW
70 if (range->n_entries > j + 1)
71 memmove(y, y + 1, sizeof(UidRangeEntry) * (range->n_entries - j - 1));
8530dc44 72
8dcc66ce 73 range->n_entries--;
b5739767 74 j--;
8530dc44
LP
75 }
76 }
8530dc44
LP
77}
78
8dcc66ce
YW
79int uid_range_add_internal(UidRange **range, uid_t start, uid_t nr, bool coalesce) {
80 _cleanup_(uid_range_freep) UidRange *range_new = NULL;
81 UidRange *p;
82
83 assert(range);
8530dc44
LP
84
85 if (nr <= 0)
86 return 0;
87
0a5c6a57
LP
88 if (start > UINT32_MAX - nr) /* overflow check */
89 return -ERANGE;
90
8dcc66ce
YW
91 if (*range)
92 p = *range;
93 else {
94 range_new = new0(UidRange, 1);
95 if (!range_new)
96 return -ENOMEM;
97
98 p = range_new;
99 }
100
101 if (!GREEDY_REALLOC(p->entries, p->n_entries + 1))
f6c13f9f 102 return -ENOMEM;
8530dc44 103
8dcc66ce 104 p->entries[p->n_entries++] = (UidRangeEntry) {
f6c13f9f
YW
105 .start = start,
106 .nr = nr,
107 };
8530dc44 108
f6c13f9f 109 if (coalesce)
8dcc66ce
YW
110 uid_range_coalesce(p);
111
112 TAKE_PTR(range_new);
113 *range = p;
8530dc44 114
8dcc66ce 115 return 0;
8530dc44
LP
116}
117
8dcc66ce 118int uid_range_add_str(UidRange **range, const char *s) {
7cf337c5 119 uid_t start, end;
8530dc44
LP
120 int r;
121
8dcc66ce 122 assert(range);
8530dc44
LP
123 assert(s);
124
7cf337c5
YW
125 r = parse_uid_range(s, &start, &end);
126 if (r < 0)
127 return r;
8530dc44 128
8dcc66ce 129 return uid_range_add_internal(range, start, end - start + 1, /* coalesce = */ true);
8530dc44
LP
130}
131
8dcc66ce 132int uid_range_next_lower(const UidRange *range, uid_t *uid) {
fed1e721 133 uid_t closest = UID_INVALID, candidate;
8530dc44 134
8dcc66ce 135 assert(range);
8530dc44
LP
136 assert(uid);
137
0a5c6a57
LP
138 if (*uid == 0)
139 return -EBUSY;
140
8530dc44
LP
141 candidate = *uid - 1;
142
8dcc66ce 143 for (size_t i = 0; i < range->n_entries; i++) {
8530dc44
LP
144 uid_t begin, end;
145
8dcc66ce
YW
146 begin = range->entries[i].start;
147 end = range->entries[i].start + range->entries[i].nr - 1;
8530dc44
LP
148
149 if (candidate >= begin && candidate <= end) {
150 *uid = candidate;
151 return 1;
152 }
153
154 if (end < candidate)
155 closest = end;
156 }
157
fed1e721 158 if (closest == UID_INVALID)
8530dc44
LP
159 return -EBUSY;
160
161 *uid = closest;
162 return 1;
163}
164
8dcc66ce 165bool uid_range_covers(const UidRange *range, uid_t start, uid_t nr) {
55656049
LP
166 if (nr == 0) /* empty range? always covered... */
167 return true;
168
169 if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */
170 return false;
8530dc44 171
8dcc66ce
YW
172 if (!range)
173 return false;
174
175 for (size_t i = 0; i < range->n_entries; i++)
176 if (start >= range->entries[i].start &&
177 start + nr <= range->entries[i].start + range->entries[i].nr)
8530dc44
LP
178 return true;
179
180 return false;
181}
5674aa7a 182
7312c422
MY
183int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) {
184 uid_t uid_base, uid_shift, uid_range;
185 int r;
186
187 assert(f);
188 assert(ret_base);
189 assert(ret_shift);
190 assert(ret_range);
191
192 errno = 0;
193 r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
194 if (r == EOF)
195 return errno_or_else(ENOMSG);
196 assert(r >= 0);
197 if (r != 3)
198 return -EBADMSG;
199
200 *ret_base = uid_base;
201 *ret_shift = uid_shift;
202 *ret_range = uid_range;
203
204 return 0;
205}
206
8dcc66ce
YW
207int uid_range_load_userns(UidRange **ret, const char *path) {
208 _cleanup_(uid_range_freep) UidRange *range = NULL;
5674aa7a
LP
209 _cleanup_fclose_ FILE *f = NULL;
210 int r;
211
212 /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from
213 * the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID
214 * maps).
215 *
216 * To simplify things this will modify the passed array in case of later failure. */
217
8dcc66ce 218 assert(ret);
f6c13f9f 219
5674aa7a
LP
220 if (!path)
221 path = "/proc/self/uid_map";
222
223 f = fopen(path, "re");
224 if (!f) {
225 r = -errno;
226
cdba12b3
LP
227 if (r == -ENOENT && path_startswith(path, "/proc/"))
228 return proc_mounted() > 0 ? -EOPNOTSUPP : -ENOSYS;
5674aa7a
LP
229
230 return r;
231 }
232
8dcc66ce
YW
233 range = new0(UidRange, 1);
234 if (!range)
235 return -ENOMEM;
236
5674aa7a
LP
237 for (;;) {
238 uid_t uid_base, uid_shift, uid_range;
5674aa7a 239
7312c422
MY
240 r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
241 if (r == -ENOMSG)
f6c13f9f 242 break;
7312c422
MY
243 if (r < 0)
244 return r;
5674aa7a 245
8dcc66ce 246 r = uid_range_add_internal(&range, uid_base, uid_range, /* coalesce = */ false);
5674aa7a
LP
247 if (r < 0)
248 return r;
249 }
f6c13f9f 250
8dcc66ce 251 uid_range_coalesce(range);
f6c13f9f 252
8dcc66ce 253 *ret = TAKE_PTR(range);
f6c13f9f 254 return 0;
5674aa7a 255}