]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/uid-range.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
8 #include "errno-util.h"
10 #include "format-util.h"
12 #include "path-util.h"
13 #include "sort-util.h"
14 #include "stat-util.h"
15 #include "uid-range.h"
16 #include "user-util.h"
18 UidRange
*uid_range_free(UidRange
*range
) {
26 static bool uid_range_entry_intersect(const UidRangeEntry
*a
, const UidRangeEntry
*b
) {
30 return a
->start
<= b
->start
+ b
->nr
&& a
->start
+ a
->nr
>= b
->start
;
33 static int uid_range_entry_compare(const UidRangeEntry
*a
, const UidRangeEntry
*b
) {
39 r
= CMP(a
->start
, b
->start
);
43 return CMP(a
->nr
, b
->nr
);
46 static void uid_range_coalesce(UidRange
*range
) {
49 if (range
->n_entries
<= 0)
52 typesafe_qsort(range
->entries
, range
->n_entries
, uid_range_entry_compare
);
54 for (size_t i
= 0; i
< range
->n_entries
; i
++) {
55 UidRangeEntry
*x
= range
->entries
+ i
;
57 for (size_t j
= i
+ 1; j
< range
->n_entries
; j
++) {
58 UidRangeEntry
*y
= range
->entries
+ j
;
61 if (!uid_range_entry_intersect(x
, y
))
64 begin
= MIN(x
->start
, y
->start
);
65 end
= MAX(x
->start
+ x
->nr
, y
->start
+ y
->nr
);
70 if (range
->n_entries
> j
+ 1)
71 memmove(y
, y
+ 1, sizeof(UidRangeEntry
) * (range
->n_entries
- j
- 1));
79 int uid_range_add_internal(UidRange
**range
, uid_t start
, uid_t nr
, bool coalesce
) {
80 _cleanup_(uid_range_freep
) UidRange
*range_new
= NULL
;
88 if (start
> UINT32_MAX
- nr
) /* overflow check */
94 range_new
= new0(UidRange
, 1);
101 if (!GREEDY_REALLOC(p
->entries
, p
->n_entries
+ 1))
104 p
->entries
[p
->n_entries
++] = (UidRangeEntry
) {
110 uid_range_coalesce(p
);
118 int uid_range_add_str(UidRange
**range
, const char *s
) {
125 r
= parse_uid_range(s
, &start
, &end
);
129 return uid_range_add_internal(range
, start
, end
- start
+ 1, /* coalesce = */ true);
132 int uid_range_next_lower(const UidRange
*range
, uid_t
*uid
) {
133 uid_t closest
= UID_INVALID
, candidate
;
141 candidate
= *uid
- 1;
143 for (size_t i
= 0; i
< range
->n_entries
; i
++) {
146 begin
= range
->entries
[i
].start
;
147 end
= range
->entries
[i
].start
+ range
->entries
[i
].nr
- 1;
149 if (candidate
>= begin
&& candidate
<= end
) {
158 if (closest
== UID_INVALID
)
165 bool uid_range_covers(const UidRange
*range
, uid_t start
, uid_t nr
) {
166 if (nr
== 0) /* empty range? always covered... */
169 if (start
> UINT32_MAX
- nr
) /* range overflows? definitely not covered... */
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
)
183 int uid_range_load_userns(UidRange
**ret
, const char *path
) {
184 _cleanup_(uid_range_freep
) UidRange
*range
= NULL
;
185 _cleanup_fclose_
FILE *f
= NULL
;
188 /* If 'path' is NULL loads the UID range of the userns namespace we run. Otherwise load the data from
189 * the specified file (which can be either uid_map or gid_map, in case caller needs to deal with GID
192 * To simplify things this will modify the passed array in case of later failure. */
197 path
= "/proc/self/uid_map";
199 f
= fopen(path
, "re");
203 if (r
== -ENOENT
&& path_startswith(path
, "/proc/"))
204 return proc_mounted() > 0 ? -EOPNOTSUPP
: -ENOSYS
;
209 range
= new0(UidRange
, 1);
214 uid_t uid_base
, uid_shift
, uid_range
;
218 k
= fscanf(f
, UID_FMT
" " UID_FMT
" " UID_FMT
"\n", &uid_base
, &uid_shift
, &uid_range
);
221 return errno_or_else(EIO
);
228 r
= uid_range_add_internal(&range
, uid_base
, uid_range
, /* coalesce = */ false);
233 uid_range_coalesce(range
);
235 *ret
= TAKE_PTR(range
);