• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13867348974

14 Mar 2025 10:27PM UTC coverage: 71.757% (-0.1%) from 71.9%
13867348974

push

github

yuwata
journal-remote: added custom headers support

54 of 67 new or added lines in 3 files covered. (80.6%)

1306 existing lines in 39 files now uncovered.

295405 of 411675 relevant lines covered (71.76%)

715677.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.95
/src/basic/uid-range.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <stdlib.h>
5
#include <string.h>
6

7
#include "alloc-util.h"
8
#include "errno-util.h"
9
#include "fd-util.h"
10
#include "format-util.h"
11
#include "macro.h"
12
#include "namespace-util.h"
13
#include "path-util.h"
14
#include "process-util.h"
15
#include "sort-util.h"
16
#include "stat-util.h"
17
#include "uid-range.h"
18
#include "user-util.h"
19

20
UIDRange *uid_range_free(UIDRange *range) {
3,749✔
21
        if (!range)
3,749✔
22
                return NULL;
23

24
        free(range->entries);
455✔
25
        return mfree(range);
455✔
26
}
27

28
static bool uid_range_entry_intersect(const UIDRangeEntry *a, const UIDRangeEntry *b) {
94✔
29
        assert(a);
94✔
30
        assert(b);
94✔
31

32
        return a->start <= b->start + b->nr && a->start + a->nr >= b->start;
94✔
33
}
34

35
static int uid_range_entry_compare(const UIDRangeEntry *a, const UIDRangeEntry *b) {
282✔
36
        int r;
282✔
37

38
        assert(a);
282✔
39
        assert(b);
282✔
40

41
        r = CMP(a->start, b->start);
282✔
42
        if (r != 0)
138✔
43
                return r;
262✔
44

45
        return CMP(a->nr, b->nr);
20✔
46
}
47

48
static void uid_range_coalesce(UIDRange *range) {
463✔
49
        assert(range);
463✔
50

51
        if (range->n_entries <= 0)
463✔
52
                return;
53

54
        typesafe_qsort(range->entries, range->n_entries, uid_range_entry_compare);
463✔
55

56
        for (size_t i = 0; i < range->n_entries; i++) {
932✔
57
                UIDRangeEntry *x = range->entries + i;
469✔
58

59
                for (size_t j = i + 1; j < range->n_entries; j++) {
557✔
60
                        UIDRangeEntry *y = range->entries + j;
94✔
61
                        uid_t begin, end;
94✔
62

63
                        if (!uid_range_entry_intersect(x, y))
94✔
64
                                break;
65

66
                        begin = MIN(x->start, y->start);
88✔
67
                        end = MAX(x->start + x->nr, y->start + y->nr);
88✔
68

69
                        x->start = begin;
88✔
70
                        x->nr = end - begin;
88✔
71

72
                        if (range->n_entries > j + 1)
88✔
73
                                memmove(y, y + 1, sizeof(UIDRangeEntry) * (range->n_entries - j - 1));
81✔
74

75
                        range->n_entries--;
88✔
76
                        j--;
88✔
77
                }
78
        }
79
}
80

81
int uid_range_add_internal(UIDRange **range, uid_t start, uid_t nr, bool coalesce) {
544✔
82
        _cleanup_(uid_range_freep) UIDRange *range_new = NULL;
544✔
83
        UIDRange *p;
544✔
84

85
        assert(range);
544✔
86

87
        if (nr <= 0)
544✔
88
                return 0;
89

90
        if (start > UINT32_MAX - nr) /* overflow check */
544✔
91
                return -ERANGE;
92

93
        if (*range)
544✔
94
                p = *range;
95
        else {
96
                range_new = new0(UIDRange, 1);
218✔
97
                if (!range_new)
218✔
98
                        return -ENOMEM;
99

100
                p = range_new;
101
        }
102

103
        if (!GREEDY_REALLOC(p->entries, p->n_entries + 1))
544✔
104
                return -ENOMEM;
105

106
        p->entries[p->n_entries++] = (UIDRangeEntry) {
544✔
107
                .start = start,
108
                .nr = nr,
109
        };
110

111
        if (coalesce)
544✔
112
                uid_range_coalesce(p);
226✔
113

114
        TAKE_PTR(range_new);
544✔
115
        *range = p;
544✔
116

117
        return 0;
544✔
118
}
119

120
int uid_range_add_str(UIDRange **range, const char *s) {
10✔
121
        uid_t start, end;
10✔
122
        int r;
10✔
123

124
        assert(range);
10✔
125
        assert(s);
10✔
126

127
        r = parse_uid_range(s, &start, &end);
10✔
128
        if (r < 0)
10✔
129
                return r;
10✔
130

131
        return uid_range_add_internal(range, start, end - start + 1, /* coalesce = */ true);
10✔
132
}
133

134
int uid_range_next_lower(const UIDRange *range, uid_t *uid) {
183✔
135
        uid_t closest = UID_INVALID, candidate;
183✔
136

137
        assert(range);
183✔
138
        assert(uid);
183✔
139

140
        if (*uid == 0)
183✔
141
                return -EBUSY;
142

143
        candidate = *uid - 1;
183✔
144

145
        for (size_t i = 0; i < range->n_entries; i++) {
229✔
146
                uid_t begin, end;
183✔
147

148
                begin = range->entries[i].start;
183✔
149
                end = range->entries[i].start + range->entries[i].nr - 1;
183✔
150

151
                if (candidate >= begin && candidate <= end) {
183✔
152
                        *uid = candidate;
137✔
153
                        return 1;
137✔
154
                }
155

156
                if (end < candidate)
46✔
157
                        closest = end;
45✔
158
        }
159

160
        if (closest == UID_INVALID)
46✔
161
                return -EBUSY;
162

163
        *uid = closest;
45✔
164
        return 1;
45✔
165
}
166

167
bool uid_range_covers(const UIDRange *range, uid_t start, uid_t nr) {
309✔
168
        if (nr == 0) /* empty range? always covered... */
309✔
169
                return true;
170

171
        if (start > UINT32_MAX - nr) /* range overflows? definitely not covered... */
308✔
172
                return false;
173

174
        if (!range)
305✔
175
                return false;
176

177
        FOREACH_ARRAY(i, range->entries, range->n_entries)
321✔
178
                if (start >= i->start &&
309✔
179
                    start + nr <= i->start + i->nr)
303✔
180
                        return true;
181

182
        return false;
183
}
184

185
int uid_map_read_one(FILE *f, uid_t *ret_base, uid_t *ret_shift, uid_t *ret_range) {
629✔
186
        uid_t uid_base, uid_shift, uid_range;
629✔
187
        int r;
629✔
188

189
        assert(f);
629✔
190

191
        errno = 0;
629✔
192
        r = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
629✔
193
        if (r == EOF)
629✔
194
                return errno_or_else(ENOMSG);
272✔
195
        assert(r >= 0);
357✔
196
        if (r != 3)
357✔
197
                return -EBADMSG;
198
        if (uid_range <= 0)
357✔
199
                return -EBADMSG;
200

201
        if (ret_base)
357✔
202
                *ret_base = uid_base;
357✔
203
        if (ret_shift)
357✔
204
                *ret_shift = uid_shift;
357✔
205
        if (ret_range)
357✔
206
                *ret_range = uid_range;
315✔
207

208
        return 0;
209
}
210

211
int uid_range_load_userns(const char *path, UIDRangeUsernsMode mode, UIDRange **ret) {
237✔
212
        _cleanup_(uid_range_freep) UIDRange *range = NULL;
×
213
        _cleanup_fclose_ FILE *f = NULL;
237✔
214
        int r;
237✔
215

216
        /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from
217
         * the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID
218
         * maps).
219
         *
220
         * To simplify things this will modify the passed array in case of later failure. */
221

222
        assert(mode >= 0);
237✔
223
        assert(mode < _UID_RANGE_USERNS_MODE_MAX);
237✔
224
        assert(ret);
237✔
225

226
        if (!path)
237✔
227
                path = IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "/proc/self/uid_map" : "/proc/self/gid_map";
236✔
228

229
        f = fopen(path, "re");
237✔
230
        if (!f) {
237✔
231
                r = -errno;
×
232

233
                if (r == -ENOENT && path_startswith(path, "/proc/"))
×
234
                        return proc_mounted() > 0 ? -EOPNOTSUPP : -ENOSYS;
×
235

236
                return r;
237
        }
238

239
        range = new0(UIDRange, 1);
237✔
240
        if (!range)
237✔
241
                return -ENOMEM;
242

243
        for (;;) {
238✔
244
                uid_t uid_base, uid_shift, uid_range;
475✔
245

246
                r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
475✔
247
                if (r == -ENOMSG)
475✔
248
                        break;
249
                if (r < 0)
238✔
250
                        return r;
×
251

252
                r = uid_range_add_internal(
238✔
253
                                &range,
254
                                IN_SET(mode, UID_RANGE_USERNS_INSIDE, GID_RANGE_USERNS_INSIDE) ? uid_base : uid_shift,
238✔
255
                                uid_range,
256
                                /* coalesce = */ false);
257
                if (r < 0)
238✔
258
                        return r;
259
        }
260

261
        uid_range_coalesce(range);
237✔
262

263
        *ret = TAKE_PTR(range);
237✔
264
        return 0;
237✔
265
}
266

UNCOV
267
int uid_range_load_userns_by_fd(int userns_fd, UIDRangeUsernsMode mode, UIDRange **ret) {
×
UNCOV
268
        _cleanup_(sigkill_waitp) pid_t pid = 0;
×
UNCOV
269
        int r;
×
270

UNCOV
271
        assert(userns_fd >= 0);
×
UNCOV
272
        assert(mode >= 0);
×
UNCOV
273
        assert(mode < _UID_RANGE_USERNS_MODE_MAX);
×
UNCOV
274
        assert(ret);
×
275

UNCOV
276
        r = userns_enter_and_pin(userns_fd, &pid);
×
UNCOV
277
        if (r < 0)
×
278
                return r;
279

UNCOV
280
        const char *p = procfs_file_alloca(
×
281
                        pid,
282
                        IN_SET(mode, UID_RANGE_USERNS_INSIDE, UID_RANGE_USERNS_OUTSIDE) ? "uid_map" : "gid_map");
283

UNCOV
284
        return uid_range_load_userns(p, mode, ret);
×
285
}
286

UNCOV
287
bool uid_range_overlaps(const UIDRange *range, uid_t start, uid_t nr) {
×
288

UNCOV
289
        if (!range)
×
290
                return false;
291

292
        /* Avoid overflow */
UNCOV
293
        if (start > UINT32_MAX - nr)
×
294
                nr = UINT32_MAX - start;
×
295

UNCOV
296
        if (nr == 0)
×
297
                return false;
298

UNCOV
299
        FOREACH_ARRAY(entry, range->entries, range->n_entries)
×
UNCOV
300
                if (start < entry->start + entry->nr &&
×
UNCOV
301
                    start + nr >= entry->start)
×
302
                        return true;
303

304
        return false;
305
}
306

307
bool uid_range_equal(const UIDRange *a, const UIDRange *b) {
4✔
308
        if (a == b)
4✔
309
                return true;
310

311
        if (!a || !b)
4✔
312
                return false;
313

314
        if (a->n_entries != b->n_entries)
3✔
315
                return false;
316

317
        for (size_t i = 0; i < a->n_entries; i++) {
5✔
318
                if (a->entries[i].start != b->entries[i].start)
3✔
319
                        return false;
320
                if (a->entries[i].nr != b->entries[i].nr)
3✔
321
                        return false;
322
        }
323

324
        return true;
325
}
326

327
int uid_map_search_root(pid_t pid, UIDRangeUsernsMode mode, uid_t *ret) {
45✔
328
        int r;
45✔
329

330
        assert(pid_is_valid(pid));
45✔
331
        assert(IN_SET(mode, UID_RANGE_USERNS_OUTSIDE, GID_RANGE_USERNS_OUTSIDE));
45✔
332

333
        const char *p = procfs_file_alloca(pid, mode == UID_RANGE_USERNS_OUTSIDE ? "uid_map" : "gid_map");
66✔
334
        _cleanup_fclose_ FILE *f = fopen(p, "re");
90✔
335
        if (!f) {
45✔
336
                if (errno != ENOENT)
×
337
                        return -errno;
×
338

339
                r = proc_mounted();
×
340
                if (r < 0)
×
341
                        return -ENOENT; /* original error, if we can't determine /proc/ state */
342

343
                return r ? -ENOPKG : -ENOSYS;
×
344
        }
345

346
        for (;;) {
×
347
                uid_t uid_base = UID_INVALID, uid_shift = UID_INVALID;
45✔
348

349
                r = uid_map_read_one(f, &uid_base, &uid_shift, /* ret_uid_range= */ NULL);
45✔
350
                if (r < 0)
45✔
351
                        return r;
45✔
352

353
                if (uid_base == 0) {
42✔
354
                        if (ret)
42✔
355
                                *ret = uid_shift;
42✔
356
                        return 0;
42✔
357
                }
358
        }
359
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc