File: | build-scan/../src/basic/path-util.c |
Warning: | line 603, column 11 Potential leak of memory pointed to by 'p' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
2 | ||||
3 | #include <errno(*__errno_location ()).h> | |||
4 | #include <limits.h> | |||
5 | #include <stdio.h> | |||
6 | #include <stdlib.h> | |||
7 | #include <string.h> | |||
8 | #include <sys/stat.h> | |||
9 | #include <unistd.h> | |||
10 | ||||
11 | /* When we include libgen.h because we need dirname() we immediately | |||
12 | * undefine basename() since libgen.h defines it as a macro to the | |||
13 | * POSIX version which is really broken. We prefer GNU basename(). */ | |||
14 | #include <libgen.h> | |||
15 | #undef basename | |||
16 | ||||
17 | #include "alloc-util.h" | |||
18 | #include "extract-word.h" | |||
19 | #include "fs-util.h" | |||
20 | #include "glob-util.h" | |||
21 | #include "log.h" | |||
22 | #include "macro.h" | |||
23 | #include "missing.h" | |||
24 | #include "parse-util.h" | |||
25 | #include "path-util.h" | |||
26 | #include "stat-util.h" | |||
27 | #include "string-util.h" | |||
28 | #include "strv.h" | |||
29 | #include "time-util.h" | |||
30 | #include "utf8.h" | |||
31 | ||||
32 | bool_Bool path_is_absolute(const char *p) { | |||
33 | return p[0] == '/'; | |||
34 | } | |||
35 | ||||
36 | bool_Bool is_path(const char *p) { | |||
37 | return !!strchr(p, '/'); | |||
38 | } | |||
39 | ||||
40 | int path_split_and_make_absolute(const char *p, char ***ret) { | |||
41 | char **l; | |||
42 | int r; | |||
43 | ||||
44 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/path-util.c", 44, __PRETTY_FUNCTION__ ); } while (0); | |||
45 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/path-util.c", 45, __PRETTY_FUNCTION__ ); } while (0); | |||
46 | ||||
47 | l = strv_split(p, ":"); | |||
48 | if (!l) | |||
49 | return -ENOMEM12; | |||
50 | ||||
51 | r = path_strv_make_absolute_cwd(l); | |||
52 | if (r < 0) { | |||
53 | strv_free(l); | |||
54 | return r; | |||
55 | } | |||
56 | ||||
57 | *ret = l; | |||
58 | return r; | |||
59 | } | |||
60 | ||||
61 | char *path_make_absolute(const char *p, const char *prefix) { | |||
62 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/path-util.c", 62, __PRETTY_FUNCTION__ ); } while (0); | |||
63 | ||||
64 | /* Makes every item in the list an absolute path by prepending | |||
65 | * the prefix, if specified and necessary */ | |||
66 | ||||
67 | if (path_is_absolute(p) || isempty(prefix)) | |||
68 | return strdup(p); | |||
69 | ||||
70 | if (endswith(prefix, "/")) | |||
71 | return strjoin(prefix, p)strjoin_real((prefix), p, ((void*)0)); | |||
72 | else | |||
73 | return strjoin(prefix, "/", p)strjoin_real((prefix), "/", p, ((void*)0)); | |||
74 | } | |||
75 | ||||
76 | int safe_getcwd(char **ret) { | |||
77 | char *cwd; | |||
78 | ||||
79 | cwd = get_current_dir_name(); | |||
80 | if (!cwd) | |||
81 | return negative_errno(); | |||
82 | ||||
83 | /* Let's make sure the directory is really absolute, to protect us from the logic behind | |||
84 | * CVE-2018-1000001 */ | |||
85 | if (cwd[0] != '/') { | |||
86 | free(cwd); | |||
87 | return -ENOMEDIUM123; | |||
88 | } | |||
89 | ||||
90 | *ret = cwd; | |||
91 | return 0; | |||
92 | } | |||
93 | ||||
94 | int path_make_absolute_cwd(const char *p, char **ret) { | |||
95 | char *c; | |||
96 | int r; | |||
97 | ||||
98 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/path-util.c", 98, __PRETTY_FUNCTION__ ); } while (0); | |||
99 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/path-util.c", 99, __PRETTY_FUNCTION__ ); } while (0); | |||
100 | ||||
101 | /* Similar to path_make_absolute(), but prefixes with the | |||
102 | * current working directory. */ | |||
103 | ||||
104 | if (path_is_absolute(p)) | |||
105 | c = strdup(p); | |||
106 | else { | |||
107 | _cleanup_free___attribute__((cleanup(freep))) char *cwd = NULL((void*)0); | |||
108 | ||||
109 | r = safe_getcwd(&cwd); | |||
110 | if (r < 0) | |||
111 | return r; | |||
112 | ||||
113 | if (endswith(cwd, "/")) | |||
114 | c = strjoin(cwd, p)strjoin_real((cwd), p, ((void*)0)); | |||
115 | else | |||
116 | c = strjoin(cwd, "/", p)strjoin_real((cwd), "/", p, ((void*)0)); | |||
117 | } | |||
118 | if (!c) | |||
119 | return -ENOMEM12; | |||
120 | ||||
121 | *ret = c; | |||
122 | return 0; | |||
123 | } | |||
124 | ||||
125 | int path_make_relative(const char *from_dir, const char *to_path, char **_r) { | |||
126 | char *f, *t, *r, *p; | |||
127 | unsigned n_parents = 0; | |||
128 | ||||
129 | assert(from_dir)do { if ((__builtin_expect(!!(!(from_dir)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("from_dir"), "../src/basic/path-util.c", 129, __PRETTY_FUNCTION__); } while (0); | |||
130 | assert(to_path)do { if ((__builtin_expect(!!(!(to_path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("to_path"), "../src/basic/path-util.c", 130 , __PRETTY_FUNCTION__); } while (0); | |||
131 | assert(_r)do { if ((__builtin_expect(!!(!(_r)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("_r"), "../src/basic/path-util.c", 131, __PRETTY_FUNCTION__ ); } while (0); | |||
132 | ||||
133 | /* Strips the common part, and adds ".." elements as necessary. */ | |||
134 | ||||
135 | if (!path_is_absolute(from_dir) || !path_is_absolute(to_path)) | |||
136 | return -EINVAL22; | |||
137 | ||||
138 | f = strdupa(from_dir)(__extension__ ({ const char *__old = (from_dir); size_t __len = strlen (__old) + 1; char *__new = (char *) __builtin_alloca (__len); (char *) memcpy (__new, __old, __len); })); | |||
139 | t = strdupa(to_path)(__extension__ ({ const char *__old = (to_path); size_t __len = strlen (__old) + 1; char *__new = (char *) __builtin_alloca (__len); (char *) memcpy (__new, __old, __len); })); | |||
140 | ||||
141 | path_simplify(f, true1); | |||
142 | path_simplify(t, true1); | |||
143 | ||||
144 | /* Skip the common part. */ | |||
145 | for (;;) { | |||
146 | size_t a, b; | |||
147 | ||||
148 | f += *f == '/'; | |||
149 | t += *t == '/'; | |||
150 | ||||
151 | if (!*f) { | |||
152 | if (!*t) | |||
153 | /* from_dir equals to_path. */ | |||
154 | r = strdup("."); | |||
155 | else | |||
156 | /* from_dir is a parent directory of to_path. */ | |||
157 | r = strdup(t); | |||
158 | if (!r) | |||
159 | return -ENOMEM12; | |||
160 | ||||
161 | *_r = r; | |||
162 | return 0; | |||
163 | } | |||
164 | ||||
165 | if (!*t) | |||
166 | break; | |||
167 | ||||
168 | a = strcspn(f, "/"); | |||
169 | b = strcspn(t, "/"); | |||
170 | ||||
171 | if (a != b || memcmp(f, t, a) != 0) | |||
172 | break; | |||
173 | ||||
174 | f += a; | |||
175 | t += b; | |||
176 | } | |||
177 | ||||
178 | /* If we're here, then "from_dir" has one or more elements that need to | |||
179 | * be replaced with "..". */ | |||
180 | ||||
181 | /* Count the number of necessary ".." elements. */ | |||
182 | for (; *f;) { | |||
183 | size_t w; | |||
184 | ||||
185 | w = strcspn(f, "/"); | |||
186 | ||||
187 | /* If this includes ".." we can't do a simple series of "..", refuse */ | |||
188 | if (w == 2 && f[0] == '.' && f[1] == '.') | |||
189 | return -EINVAL22; | |||
190 | ||||
191 | /* Count number of elements */ | |||
192 | n_parents++; | |||
193 | ||||
194 | f += w; | |||
195 | f += *f == '/'; | |||
196 | } | |||
197 | ||||
198 | r = new(char, n_parents * 3 + strlen(t) + 1)((char*) malloc_multiply(sizeof(char), (n_parents * 3 + strlen (t) + 1))); | |||
199 | if (!r) | |||
200 | return -ENOMEM12; | |||
201 | ||||
202 | for (p = r; n_parents > 0; n_parents--) | |||
203 | p = mempcpy(p, "../", 3); | |||
204 | ||||
205 | if (*t) | |||
206 | strcpy(p, t); | |||
207 | else | |||
208 | /* Remove trailing slash */ | |||
209 | *(--p) = 0; | |||
210 | ||||
211 | *_r = r; | |||
212 | return 0; | |||
213 | } | |||
214 | ||||
215 | int path_strv_make_absolute_cwd(char **l) { | |||
216 | char **s; | |||
217 | int r; | |||
218 | ||||
219 | /* Goes through every item in the string list and makes it | |||
220 | * absolute. This works in place and won't rollback any | |||
221 | * changes on failure. */ | |||
222 | ||||
223 | STRV_FOREACH(s, l)for ((s) = (l); (s) && *(s); (s)++) { | |||
224 | char *t; | |||
225 | ||||
226 | r = path_make_absolute_cwd(*s, &t); | |||
227 | if (r < 0) | |||
228 | return r; | |||
229 | ||||
230 | path_simplify(t, false0); | |||
231 | free_and_replace(*s, t)({ free(*s); (*s) = (t); (t) = ((void*)0); 0; }); | |||
232 | } | |||
233 | ||||
234 | return 0; | |||
235 | } | |||
236 | ||||
237 | char **path_strv_resolve(char **l, const char *root) { | |||
238 | char **s; | |||
239 | unsigned k = 0; | |||
240 | bool_Bool enomem = false0; | |||
241 | int r; | |||
242 | ||||
243 | if (strv_isempty(l)) | |||
244 | return l; | |||
245 | ||||
246 | /* Goes through every item in the string list and canonicalize | |||
247 | * the path. This works in place and won't rollback any | |||
248 | * changes on failure. */ | |||
249 | ||||
250 | STRV_FOREACH(s, l)for ((s) = (l); (s) && *(s); (s)++) { | |||
251 | _cleanup_free___attribute__((cleanup(freep))) char *orig = NULL((void*)0); | |||
252 | char *t, *u; | |||
253 | ||||
254 | if (!path_is_absolute(*s)) { | |||
255 | free(*s); | |||
256 | continue; | |||
257 | } | |||
258 | ||||
259 | if (root) { | |||
260 | orig = *s; | |||
261 | t = prefix_root(root, orig); | |||
262 | if (!t) { | |||
263 | enomem = true1; | |||
264 | continue; | |||
265 | } | |||
266 | } else | |||
267 | t = *s; | |||
268 | ||||
269 | r = chase_symlinks(t, root, 0, &u); | |||
270 | if (r == -ENOENT2) { | |||
271 | if (root) { | |||
272 | u = TAKE_PTR(orig)({ typeof(orig) _ptr_ = (orig); (orig) = ((void*)0); _ptr_; } ); | |||
273 | free(t); | |||
274 | } else | |||
275 | u = t; | |||
276 | } else if (r < 0) { | |||
277 | free(t); | |||
278 | ||||
279 | if (r == -ENOMEM12) | |||
280 | enomem = true1; | |||
281 | ||||
282 | continue; | |||
283 | } else if (root) { | |||
284 | char *x; | |||
285 | ||||
286 | free(t); | |||
287 | x = path_startswith(u, root); | |||
288 | if (x) { | |||
289 | /* restore the slash if it was lost */ | |||
290 | if (!startswith(x, "/")) | |||
291 | *(--x) = '/'; | |||
292 | ||||
293 | t = strdup(x); | |||
294 | free(u); | |||
295 | if (!t) { | |||
296 | enomem = true1; | |||
297 | continue; | |||
298 | } | |||
299 | u = t; | |||
300 | } else { | |||
301 | /* canonicalized path goes outside of | |||
302 | * prefix, keep the original path instead */ | |||
303 | free_and_replace(u, orig)({ free(u); (u) = (orig); (orig) = ((void*)0); 0; }); | |||
304 | } | |||
305 | } else | |||
306 | free(t); | |||
307 | ||||
308 | l[k++] = u; | |||
309 | } | |||
310 | ||||
311 | l[k] = NULL((void*)0); | |||
312 | ||||
313 | if (enomem) | |||
314 | return NULL((void*)0); | |||
315 | ||||
316 | return l; | |||
317 | } | |||
318 | ||||
319 | char **path_strv_resolve_uniq(char **l, const char *root) { | |||
320 | ||||
321 | if (strv_isempty(l)) | |||
322 | return l; | |||
323 | ||||
324 | if (!path_strv_resolve(l, root)) | |||
325 | return NULL((void*)0); | |||
326 | ||||
327 | return strv_uniq(l); | |||
328 | } | |||
329 | ||||
330 | char *path_simplify(char *path, bool_Bool kill_dots) { | |||
331 | char *f, *t; | |||
332 | bool_Bool slash = false0, ignore_slash = false0, absolute; | |||
333 | ||||
334 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 334 , __PRETTY_FUNCTION__); } while (0); | |||
335 | ||||
336 | /* Removes redundant inner and trailing slashes. Also removes unnecessary dots | |||
337 | * if kill_dots is true. Modifies the passed string in-place. | |||
338 | * | |||
339 | * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) | |||
340 | * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) | |||
341 | * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false) | |||
342 | * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) | |||
343 | */ | |||
344 | ||||
345 | absolute = path_is_absolute(path); | |||
346 | ||||
347 | f = path; | |||
348 | if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, '/'})/sizeof(int)]; switch(f[1]) { case 0: case '/': _found = 1; break; default: break; } _found; })) { | |||
349 | ignore_slash = true1; | |||
350 | f++; | |||
351 | } | |||
352 | ||||
353 | for (t = path; *f; f++) { | |||
354 | ||||
355 | if (*f == '/') { | |||
356 | slash = true1; | |||
357 | continue; | |||
358 | } | |||
359 | ||||
360 | if (slash) { | |||
361 | if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, '/'})/sizeof(int)]; switch(f[1]) { case 0: case '/': _found = 1; break; default: break; } _found; })) | |||
362 | continue; | |||
363 | ||||
364 | slash = false0; | |||
365 | if (ignore_slash) | |||
366 | ignore_slash = false0; | |||
367 | else | |||
368 | *(t++) = '/'; | |||
369 | } | |||
370 | ||||
371 | *(t++) = *f; | |||
372 | } | |||
373 | ||||
374 | /* Special rule, if we are talking of the root directory, a trailing slash is good */ | |||
375 | if (absolute && t == path) | |||
376 | *(t++) = '/'; | |||
377 | ||||
378 | *t = 0; | |||
379 | return path; | |||
380 | } | |||
381 | ||||
382 | char* path_startswith(const char *path, const char *prefix) { | |||
383 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 383 , __PRETTY_FUNCTION__); } while (0); | |||
384 | assert(prefix)do { if ((__builtin_expect(!!(!(prefix)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("prefix"), "../src/basic/path-util.c", 384 , __PRETTY_FUNCTION__); } while (0); | |||
385 | ||||
386 | /* Returns a pointer to the start of the first component after the parts matched by | |||
387 | * the prefix, iff | |||
388 | * - both paths are absolute or both paths are relative, | |||
389 | * and | |||
390 | * - each component in prefix in turn matches a component in path at the same position. | |||
391 | * An empty string will be returned when the prefix and path are equivalent. | |||
392 | * | |||
393 | * Returns NULL otherwise. | |||
394 | */ | |||
395 | ||||
396 | if ((path[0] == '/') != (prefix[0] == '/')) | |||
397 | return NULL((void*)0); | |||
398 | ||||
399 | for (;;) { | |||
400 | size_t a, b; | |||
401 | ||||
402 | path += strspn(path, "/"); | |||
403 | prefix += strspn(prefix, "/"); | |||
404 | ||||
405 | if (*prefix == 0) | |||
406 | return (char*) path; | |||
407 | ||||
408 | if (*path == 0) | |||
409 | return NULL((void*)0); | |||
410 | ||||
411 | a = strcspn(path, "/"); | |||
412 | b = strcspn(prefix, "/"); | |||
413 | ||||
414 | if (a != b) | |||
415 | return NULL((void*)0); | |||
416 | ||||
417 | if (memcmp(path, prefix, a) != 0) | |||
418 | return NULL((void*)0); | |||
419 | ||||
420 | path += a; | |||
421 | prefix += b; | |||
422 | } | |||
423 | } | |||
424 | ||||
425 | int path_compare(const char *a, const char *b) { | |||
426 | int d; | |||
427 | ||||
428 | assert(a)do { if ((__builtin_expect(!!(!(a)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("a"), "../src/basic/path-util.c", 428, __PRETTY_FUNCTION__ ); } while (0); | |||
429 | assert(b)do { if ((__builtin_expect(!!(!(b)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("b"), "../src/basic/path-util.c", 429, __PRETTY_FUNCTION__ ); } while (0); | |||
430 | ||||
431 | /* A relative path and an abolute path must not compare as equal. | |||
432 | * Which one is sorted before the other does not really matter. | |||
433 | * Here a relative path is ordered before an absolute path. */ | |||
434 | d = (a[0] == '/') - (b[0] == '/'); | |||
435 | if (d != 0) | |||
436 | return d; | |||
437 | ||||
438 | for (;;) { | |||
439 | size_t j, k; | |||
440 | ||||
441 | a += strspn(a, "/"); | |||
442 | b += strspn(b, "/"); | |||
443 | ||||
444 | if (*a == 0 && *b == 0) | |||
445 | return 0; | |||
446 | ||||
447 | /* Order prefixes first: "/foo" before "/foo/bar" */ | |||
448 | if (*a == 0) | |||
449 | return -1; | |||
450 | if (*b == 0) | |||
451 | return 1; | |||
452 | ||||
453 | j = strcspn(a, "/"); | |||
454 | k = strcspn(b, "/"); | |||
455 | ||||
456 | /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ | |||
457 | d = memcmp(a, b, MIN(j, k)__extension__ ({ const typeof((j)) __unique_prefix_A12 = ((j) ); const typeof((k)) __unique_prefix_B13 = ((k)); __unique_prefix_A12 < __unique_prefix_B13 ? __unique_prefix_A12 : __unique_prefix_B13 ; })); | |||
458 | if (d != 0) | |||
459 | return (d > 0) - (d < 0); /* sign of d */ | |||
460 | ||||
461 | /* Sort "/foo/a" before "/foo/aaa" */ | |||
462 | d = (j > k) - (j < k); /* sign of (j - k) */ | |||
463 | if (d != 0) | |||
464 | return d; | |||
465 | ||||
466 | a += j; | |||
467 | b += k; | |||
468 | } | |||
469 | } | |||
470 | ||||
471 | bool_Bool path_equal(const char *a, const char *b) { | |||
472 | return path_compare(a, b) == 0; | |||
473 | } | |||
474 | ||||
475 | bool_Bool path_equal_or_files_same(const char *a, const char *b, int flags) { | |||
476 | return path_equal(a, b) || files_same(a, b, flags) > 0; | |||
477 | } | |||
478 | ||||
479 | char* path_join(const char *root, const char *path, const char *rest) { | |||
480 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 480 , __PRETTY_FUNCTION__); } while (0); | |||
481 | ||||
482 | if (!isempty(root)) | |||
483 | return strjoin(root, endswith(root, "/") ? "" : "/",strjoin_real((root), endswith(root, "/") ? "" : "/", path[0] == '/' ? path+1 : path, rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest , ((void*)0)) | |||
484 | path[0] == '/' ? path+1 : path,strjoin_real((root), endswith(root, "/") ? "" : "/", path[0] == '/' ? path+1 : path, rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest , ((void*)0)) | |||
485 | rest ? (endswith(path, "/") ? "" : "/") : NULL,strjoin_real((root), endswith(root, "/") ? "" : "/", path[0] == '/' ? path+1 : path, rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest , ((void*)0)) | |||
486 | rest && rest[0] == '/' ? rest+1 : rest)strjoin_real((root), endswith(root, "/") ? "" : "/", path[0] == '/' ? path+1 : path, rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest , ((void*)0)); | |||
487 | else | |||
488 | return strjoin(path,strjoin_real((path), rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest, ( (void*)0)) | |||
489 | rest ? (endswith(path, "/") ? "" : "/") : NULL,strjoin_real((path), rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest, ( (void*)0)) | |||
490 | rest && rest[0] == '/' ? rest+1 : rest)strjoin_real((path), rest ? (endswith(path, "/") ? "" : "/") : ((void*)0), rest && rest[0] == '/' ? rest+1 : rest, ( (void*)0)); | |||
491 | } | |||
492 | ||||
493 | int find_binary(const char *name, char **ret) { | |||
494 | int last_error, r; | |||
495 | const char *p; | |||
496 | ||||
497 | assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name"), "../src/basic/path-util.c", 497 , __PRETTY_FUNCTION__); } while (0); | |||
498 | ||||
499 | if (is_path(name)) { | |||
500 | if (access(name, X_OK1) < 0) | |||
501 | return -errno(*__errno_location ()); | |||
502 | ||||
503 | if (ret
| |||
504 | r = path_make_absolute_cwd(name, ret); | |||
505 | if (r
| |||
506 | return r; | |||
507 | } | |||
508 | ||||
509 | return 0; | |||
510 | } | |||
511 | ||||
512 | /** | |||
513 | * Plain getenv, not secure_getenv, because we want | |||
514 | * to actually allow the user to pick the binary. | |||
515 | */ | |||
516 | p = getenv("PATH"); | |||
517 | if (!p) | |||
518 | p = DEFAULT_PATH"/usr/local/" "sbin:" "/usr/local/" "bin" ":" "/usr/" "sbin:" "/usr/" "bin"; | |||
519 | ||||
520 | last_error = -ENOENT2; | |||
521 | ||||
522 | for (;;) { | |||
523 | _cleanup_free___attribute__((cleanup(freep))) char *j = NULL((void*)0), *element = NULL((void*)0); | |||
524 | ||||
525 | r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); | |||
526 | if (r < 0) | |||
527 | return r; | |||
528 | if (r == 0) | |||
529 | break; | |||
530 | ||||
531 | if (!path_is_absolute(element)) | |||
532 | continue; | |||
533 | ||||
534 | j = strjoin(element, "/", name)strjoin_real((element), "/", name, ((void*)0)); | |||
535 | if (!j) | |||
536 | return -ENOMEM12; | |||
537 | ||||
538 | if (access(j, X_OK1) >= 0) { | |||
539 | /* Found it! */ | |||
540 | ||||
541 | if (ret) { | |||
542 | *ret = path_simplify(j, false0); | |||
543 | j = NULL((void*)0); | |||
544 | } | |||
545 | ||||
546 | return 0; | |||
547 | } | |||
548 | ||||
549 | last_error = -errno(*__errno_location ()); | |||
550 | } | |||
551 | ||||
552 | return last_error; | |||
553 | } | |||
554 | ||||
555 | bool_Bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool_Bool update) { | |||
556 | bool_Bool changed = false0; | |||
557 | const char* const* i; | |||
558 | ||||
559 | assert(timestamp)do { if ((__builtin_expect(!!(!(timestamp)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("timestamp"), "../src/basic/path-util.c" , 559, __PRETTY_FUNCTION__); } while (0); | |||
560 | ||||
561 | if (!paths) | |||
562 | return false0; | |||
563 | ||||
564 | STRV_FOREACH(i, paths)for ((i) = (paths); (i) && *(i); (i)++) { | |||
565 | struct stat stats; | |||
566 | usec_t u; | |||
567 | ||||
568 | if (stat(*i, &stats) < 0) | |||
569 | continue; | |||
570 | ||||
571 | u = timespec_load(&stats.st_mtim); | |||
572 | ||||
573 | /* first check */ | |||
574 | if (*timestamp >= u) | |||
575 | continue; | |||
576 | ||||
577 | log_debug("timestamp of '%s' changed", *i)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 577, __func__, "timestamp of '%s' changed" , *i) : -abs(_e); }); | |||
578 | ||||
579 | /* update timestamp */ | |||
580 | if (update) { | |||
581 | *timestamp = u; | |||
582 | changed = true1; | |||
583 | } else | |||
584 | return true1; | |||
585 | } | |||
586 | ||||
587 | return changed; | |||
588 | } | |||
589 | ||||
590 | static int binary_is_good(const char *binary) { | |||
591 | _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0), *d = NULL((void*)0); | |||
592 | int r; | |||
593 | ||||
594 | r = find_binary(binary, &p); | |||
595 | if (r == -ENOENT2) | |||
596 | return 0; | |||
597 | if (r
| |||
598 | return r; | |||
599 | ||||
600 | /* An fsck that is linked to /bin/true is a non-existent | |||
601 | * fsck */ | |||
602 | ||||
603 | r = readlink_malloc(p, &d); | |||
| ||||
604 | if (r == -EINVAL22) /* not a symlink */ | |||
605 | return 1; | |||
606 | if (r < 0) | |||
607 | return r; | |||
608 | ||||
609 | return !PATH_IN_SET(d, "true"({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "true" "/bin/true", "/usr/bin/true", "/dev/null", ((void *)0) }))); (s) && *(s); (s)++) if (path_equal(d, *s)) { _found = 1; break; } _found; }) | |||
610 | "/bin/true",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "true" "/bin/true", "/usr/bin/true", "/dev/null", ((void *)0) }))); (s) && *(s); (s)++) if (path_equal(d, *s)) { _found = 1; break; } _found; }) | |||
611 | "/usr/bin/true",({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "true" "/bin/true", "/usr/bin/true", "/dev/null", ((void *)0) }))); (s) && *(s); (s)++) if (path_equal(d, *s)) { _found = 1; break; } _found; }) | |||
612 | "/dev/null")({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "true" "/bin/true", "/usr/bin/true", "/dev/null", ((void *)0) }))); (s) && *(s); (s)++) if (path_equal(d, *s)) { _found = 1; break; } _found; }); | |||
613 | } | |||
614 | ||||
615 | int fsck_exists(const char *fstype) { | |||
616 | const char *checker; | |||
617 | ||||
618 | assert(fstype)do { if ((__builtin_expect(!!(!(fstype)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fstype"), "../src/basic/path-util.c", 618 , __PRETTY_FUNCTION__); } while (0); | |||
619 | ||||
620 | if (streq(fstype, "auto")(strcmp((fstype),("auto")) == 0)) | |||
621 | return -EINVAL22; | |||
622 | ||||
623 | checker = strjoina("fsck.", fstype)({ const char *_appendees_[] = { "fsck.", fstype }; char *_d_ , *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
624 | return binary_is_good(checker); | |||
625 | } | |||
626 | ||||
627 | int mkfs_exists(const char *fstype) { | |||
628 | const char *mkfs; | |||
629 | ||||
630 | assert(fstype)do { if ((__builtin_expect(!!(!(fstype)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fstype"), "../src/basic/path-util.c", 630 , __PRETTY_FUNCTION__); } while (0); | |||
| ||||
631 | ||||
632 | if (streq(fstype, "auto")(strcmp((fstype),("auto")) == 0)) | |||
633 | return -EINVAL22; | |||
634 | ||||
635 | mkfs = strjoina("mkfs.", fstype)({ const char *_appendees_[] = { "mkfs.", fstype }; char *_d_ , *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | |||
636 | return binary_is_good(mkfs); | |||
637 | } | |||
638 | ||||
639 | char *prefix_root(const char *root, const char *path) { | |||
640 | char *n, *p; | |||
641 | size_t l; | |||
642 | ||||
643 | /* If root is passed, prefixes path with it. Otherwise returns | |||
644 | * it as is. */ | |||
645 | ||||
646 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 646 , __PRETTY_FUNCTION__); } while (0); | |||
647 | ||||
648 | /* First, drop duplicate prefixing slashes from the path */ | |||
649 | while (path[0] == '/' && path[1] == '/') | |||
650 | path++; | |||
651 | ||||
652 | if (empty_or_root(root)) | |||
653 | return strdup(path); | |||
654 | ||||
655 | l = strlen(root) + 1 + strlen(path) + 1; | |||
656 | ||||
657 | n = new(char, l)((char*) malloc_multiply(sizeof(char), (l))); | |||
658 | if (!n) | |||
659 | return NULL((void*)0); | |||
660 | ||||
661 | p = stpcpy(n, root); | |||
662 | ||||
663 | while (p > n && p[-1] == '/') | |||
664 | p--; | |||
665 | ||||
666 | if (path[0] != '/') | |||
667 | *(p++) = '/'; | |||
668 | ||||
669 | strcpy(p, path); | |||
670 | return n; | |||
671 | } | |||
672 | ||||
673 | int parse_path_argument_and_warn(const char *path, bool_Bool suppress_root, char **arg) { | |||
674 | char *p; | |||
675 | int r; | |||
676 | ||||
677 | /* | |||
678 | * This function is intended to be used in command line | |||
679 | * parsers, to handle paths that are passed in. It makes the | |||
680 | * path absolute, and reduces it to NULL if omitted or | |||
681 | * root (the latter optionally). | |||
682 | * | |||
683 | * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON | |||
684 | * SUCCESS! Hence, do not pass in uninitialized pointers. | |||
685 | */ | |||
686 | ||||
687 | if (isempty(path)) { | |||
688 | *arg = mfree(*arg); | |||
689 | return 0; | |||
690 | } | |||
691 | ||||
692 | r = path_make_absolute_cwd(path, &p); | |||
693 | if (r < 0) | |||
694 | return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 694, __func__, "Failed to parse path \"%s\" and make it absolute: %m" , path) : -abs(_e); }); | |||
695 | ||||
696 | path_simplify(p, false0); | |||
697 | if (suppress_root && empty_or_root(p)) | |||
698 | p = mfree(p); | |||
699 | ||||
700 | free_and_replace(*arg, p)({ free(*arg); (*arg) = (p); (p) = ((void*)0); 0; }); | |||
701 | ||||
702 | return 0; | |||
703 | } | |||
704 | ||||
705 | char* dirname_malloc(const char *path) { | |||
706 | char *d, *dir, *dir2; | |||
707 | ||||
708 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 708 , __PRETTY_FUNCTION__); } while (0); | |||
709 | ||||
710 | d = strdup(path); | |||
711 | if (!d) | |||
712 | return NULL((void*)0); | |||
713 | ||||
714 | dir = dirname(d); | |||
715 | assert(dir)do { if ((__builtin_expect(!!(!(dir)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dir"), "../src/basic/path-util.c", 715, __PRETTY_FUNCTION__); } while (0); | |||
716 | ||||
717 | if (dir == d) | |||
718 | return d; | |||
719 | ||||
720 | dir2 = strdup(dir); | |||
721 | free(d); | |||
722 | ||||
723 | return dir2; | |||
724 | } | |||
725 | ||||
726 | const char *last_path_component(const char *path) { | |||
727 | ||||
728 | /* Finds the last component of the path, preserving the optional trailing slash that signifies a directory. | |||
729 | * | |||
730 | * a/b/c → c | |||
731 | * a/b/c/ → c/ | |||
732 | * x → x | |||
733 | * x/ → x/ | |||
734 | * /y → y | |||
735 | * /y/ → y/ | |||
736 | * / → / | |||
737 | * // → / | |||
738 | * /foo/a → a | |||
739 | * /foo/a/ → a/ | |||
740 | * | |||
741 | * Also, the empty string is mapped to itself. | |||
742 | * | |||
743 | * This is different than basename(), which returns "" when a trailing slash is present. | |||
744 | */ | |||
745 | ||||
746 | unsigned l, k; | |||
747 | ||||
748 | l = k = strlen(path); | |||
749 | if (l == 0) /* special case — an empty string */ | |||
750 | return path; | |||
751 | ||||
752 | while (k > 0 && path[k-1] == '/') | |||
753 | k--; | |||
754 | ||||
755 | if (k == 0) /* the root directory */ | |||
756 | return path + l - 1; | |||
757 | ||||
758 | while (k > 0 && path[k-1] != '/') | |||
759 | k--; | |||
760 | ||||
761 | return path + k; | |||
762 | } | |||
763 | ||||
764 | bool_Bool filename_is_valid(const char *p) { | |||
765 | const char *e; | |||
766 | ||||
767 | if (isempty(p)) | |||
768 | return false0; | |||
769 | ||||
770 | if (dot_or_dot_dot(p)) | |||
771 | return false0; | |||
772 | ||||
773 | e = strchrnul(p, '/'); | |||
774 | if (*e != 0) | |||
775 | return false0; | |||
776 | ||||
777 | if (e - p > FILENAME_MAX4096) | |||
778 | return false0; | |||
779 | ||||
780 | return true1; | |||
781 | } | |||
782 | ||||
783 | bool_Bool path_is_normalized(const char *p) { | |||
784 | ||||
785 | if (isempty(p)) | |||
786 | return false0; | |||
787 | ||||
788 | if (dot_or_dot_dot(p)) | |||
789 | return false0; | |||
790 | ||||
791 | if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) | |||
792 | return false0; | |||
793 | ||||
794 | if (strlen(p)+1 > PATH_MAX4096) | |||
795 | return false0; | |||
796 | ||||
797 | if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) | |||
798 | return false0; | |||
799 | ||||
800 | if (strstr(p, "//")) | |||
801 | return false0; | |||
802 | ||||
803 | return true1; | |||
804 | } | |||
805 | ||||
806 | char *file_in_same_dir(const char *path, const char *filename) { | |||
807 | char *e, *ret; | |||
808 | size_t k; | |||
809 | ||||
810 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 810 , __PRETTY_FUNCTION__); } while (0); | |||
811 | assert(filename)do { if ((__builtin_expect(!!(!(filename)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("filename"), "../src/basic/path-util.c", 811, __PRETTY_FUNCTION__); } while (0); | |||
812 | ||||
813 | /* This removes the last component of path and appends | |||
814 | * filename, unless the latter is absolute anyway or the | |||
815 | * former isn't */ | |||
816 | ||||
817 | if (path_is_absolute(filename)) | |||
818 | return strdup(filename); | |||
819 | ||||
820 | e = strrchr(path, '/'); | |||
821 | if (!e) | |||
822 | return strdup(filename); | |||
823 | ||||
824 | k = strlen(filename); | |||
825 | ret = new(char, (e + 1 - path) + k + 1)((char*) malloc_multiply(sizeof(char), ((e + 1 - path) + k + 1 ))); | |||
826 | if (!ret) | |||
827 | return NULL((void*)0); | |||
828 | ||||
829 | memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1); | |||
830 | return ret; | |||
831 | } | |||
832 | ||||
833 | bool_Bool hidden_or_backup_file(const char *filename) { | |||
834 | const char *p; | |||
835 | ||||
836 | assert(filename)do { if ((__builtin_expect(!!(!(filename)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("filename"), "../src/basic/path-util.c", 836, __PRETTY_FUNCTION__); } while (0); | |||
837 | ||||
838 | if (filename[0] == '.' || | |||
839 | streq(filename, "lost+found")(strcmp((filename),("lost+found")) == 0) || | |||
840 | streq(filename, "aquota.user")(strcmp((filename),("aquota.user")) == 0) || | |||
841 | streq(filename, "aquota.group")(strcmp((filename),("aquota.group")) == 0) || | |||
842 | endswith(filename, "~")) | |||
843 | return true1; | |||
844 | ||||
845 | p = strrchr(filename, '.'); | |||
846 | if (!p) | |||
847 | return false0; | |||
848 | ||||
849 | /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up | |||
850 | * with always new suffixes and that everybody else should just adjust to that, then it really should be on | |||
851 | * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt | |||
852 | * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional | |||
853 | * string. Specifically: there's now: | |||
854 | * | |||
855 | * The generic suffixes "~" and ".bak" for backup files | |||
856 | * The generic prefix "." for hidden files | |||
857 | * | |||
858 | * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" | |||
859 | * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. | |||
860 | */ | |||
861 | ||||
862 | return STR_IN_SET(p + 1,(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
863 | "rpmnew",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
864 | "rpmsave",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
865 | "rpmorig",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
866 | "dpkg-old",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
867 | "dpkg-new",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
868 | "dpkg-tmp",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
869 | "dpkg-dist",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
870 | "dpkg-bak",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
871 | "dpkg-backup",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
872 | "dpkg-remove",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
873 | "ucf-new",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
874 | "ucf-old",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
875 | "ucf-dist",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
876 | "swp",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
877 | "bak",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
878 | "old",(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))) | |||
879 | "new")(!!strv_find((((char**) ((const char*[]) { "rpmnew", "rpmsave" , "rpmorig", "dpkg-old", "dpkg-new", "dpkg-tmp", "dpkg-dist", "dpkg-bak", "dpkg-backup", "dpkg-remove", "ucf-new", "ucf-old" , "ucf-dist", "swp", "bak", "old", "new", ((void*)0) }))), (p + 1))); | |||
880 | } | |||
881 | ||||
882 | bool_Bool is_device_path(const char *path) { | |||
883 | ||||
884 | /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ | |||
885 | ||||
886 | return PATH_STARTSWITH_SET(path, "/dev/", "/sys/")({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/dev/", "/sys/", ((void*)0) }))); (s) && *(s) ; (s)++) if (path_startswith(path, *s)) { _found = 1; break; } _found; }); | |||
887 | } | |||
888 | ||||
889 | bool_Bool valid_device_node_path(const char *path) { | |||
890 | ||||
891 | /* Some superficial checks whether the specified path is a valid device node path, all without looking at the | |||
892 | * actual device node. */ | |||
893 | ||||
894 | if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")({ char **s; _Bool _found = 0; for ((s) = (((char**) ((const char *[]) { "/dev/", "/run/systemd/inaccessible/", ((void*)0) }))) ; (s) && *(s); (s)++) if (path_startswith(path, *s)) { _found = 1; break; } _found; })) | |||
895 | return false0; | |||
896 | ||||
897 | if (endswith(path, "/")) /* can't be a device node if it ends in a slash */ | |||
898 | return false0; | |||
899 | ||||
900 | return path_is_normalized(path); | |||
901 | } | |||
902 | ||||
903 | bool_Bool valid_device_allow_pattern(const char *path) { | |||
904 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/basic/path-util.c", 904 , __PRETTY_FUNCTION__); } while (0); | |||
905 | ||||
906 | /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= | |||
907 | * accept it */ | |||
908 | ||||
909 | if (startswith(path, "block-") || | |||
910 | startswith(path, "char-")) | |||
911 | return true1; | |||
912 | ||||
913 | return valid_device_node_path(path); | |||
914 | } | |||
915 | ||||
916 | int systemd_installation_has_version(const char *root, unsigned minimal_version) { | |||
917 | const char *pattern; | |||
918 | int r; | |||
919 | ||||
920 | /* Try to guess if systemd installation is later than the specified version. This | |||
921 | * is hacky and likely to yield false negatives, particularly if the installation | |||
922 | * is non-standard. False positives should be relatively rare. | |||
923 | */ | |||
924 | ||||
925 | NULSTR_FOREACH(pattern,for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
926 | /* /lib works for systems without usr-merge, and for systems with a sanefor ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
927 | * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessaryfor ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
928 | * for Gentoo which does a merge without making /lib a symlink.for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
929 | */for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
930 | "lib/systemd/libsystemd-shared-*.so\0"for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
931 | "lib64/systemd/libsystemd-shared-*.so\0"for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
932 | "usr/lib/systemd/libsystemd-shared-*.so\0"for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) | |||
933 | "usr/lib64/systemd/libsystemd-shared-*.so\0")for ((pattern) = ("lib/systemd/libsystemd-shared-*.so\0" "lib64/systemd/libsystemd-shared-*.so\0" "usr/lib/systemd/libsystemd-shared-*.so\0" "usr/lib64/systemd/libsystemd-shared-*.so\0" ); (pattern) && *(pattern); (pattern) = strchr((pattern ), 0)+1) { | |||
934 | ||||
935 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **names = NULL((void*)0); | |||
936 | _cleanup_free___attribute__((cleanup(freep))) char *path = NULL((void*)0); | |||
937 | char *c, **name; | |||
938 | ||||
939 | path = prefix_root(root, pattern); | |||
940 | if (!path) | |||
941 | return -ENOMEM12; | |||
942 | ||||
943 | r = glob_extend(&names, path); | |||
944 | if (r == -ENOENT2) | |||
945 | continue; | |||
946 | if (r < 0) | |||
947 | return r; | |||
948 | ||||
949 | assert_se(c = endswith(path, "*.so"))do { if ((__builtin_expect(!!(!(c = endswith(path, "*.so"))), 0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("c = endswith(path, \"*.so\")" ), "../src/basic/path-util.c", 949, __PRETTY_FUNCTION__); } while (0); | |||
950 | *c = '\0'; /* truncate the glob part */ | |||
951 | ||||
952 | STRV_FOREACH(name, names)for ((name) = (names); (name) && *(name); (name)++) { | |||
953 | /* This is most likely to run only once, hence let's not optimize anything. */ | |||
954 | char *t, *t2; | |||
955 | unsigned version; | |||
956 | ||||
957 | t = startswith(*name, path); | |||
958 | if (!t) | |||
959 | continue; | |||
960 | ||||
961 | t2 = endswith(t, ".so"); | |||
962 | if (!t2) | |||
963 | continue; | |||
964 | ||||
965 | t2[0] = '\0'; /* truncate the suffix */ | |||
966 | ||||
967 | r = safe_atou(t, &version); | |||
968 | if (r < 0) { | |||
969 | log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 969, __func__, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m" , *name) : -abs(_e); }); | |||
970 | continue; | |||
971 | } | |||
972 | ||||
973 | log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 975, __func__, "Found libsystemd shared at \"%s.so\", version %u (%s)." , *name, version, version >= minimal_version ? "OK" : "too old" ) : -abs(_e); }) | |||
974 | *name, version,({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 975, __func__, "Found libsystemd shared at \"%s.so\", version %u (%s)." , *name, version, version >= minimal_version ? "OK" : "too old" ) : -abs(_e); }) | |||
975 | version >= minimal_version ? "OK" : "too old")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/path-util.c", 975, __func__, "Found libsystemd shared at \"%s.so\", version %u (%s)." , *name, version, version >= minimal_version ? "OK" : "too old" ) : -abs(_e); }); | |||
976 | if (version >= minimal_version) | |||
977 | return true1; | |||
978 | } | |||
979 | } | |||
980 | ||||
981 | return false0; | |||
982 | } | |||
983 | ||||
984 | bool_Bool dot_or_dot_dot(const char *path) { | |||
985 | if (!path) | |||
986 | return false0; | |||
987 | if (path[0] != '.') | |||
988 | return false0; | |||
989 | if (path[1] == 0) | |||
990 | return true1; | |||
991 | if (path[1] != '.') | |||
992 | return false0; | |||
993 | ||||
994 | return path[2] == 0; | |||
995 | } | |||
996 | ||||
997 | bool_Bool empty_or_root(const char *root) { | |||
998 | ||||
999 | /* For operations relative to some root directory, returns true if the specified root directory is redundant, | |||
1000 | * i.e. either / or NULL or the empty string or any equivalent. */ | |||
1001 | ||||
1002 | if (!root) | |||
1003 | return true1; | |||
1004 | ||||
1005 | return root[strspn(root, "/")] == 0; | |||
1006 | } | |||
1007 | ||||
1008 | int path_simplify_and_warn( | |||
1009 | char *path, | |||
1010 | unsigned flag, | |||
1011 | const char *unit, | |||
1012 | const char *filename, | |||
1013 | unsigned line, | |||
1014 | const char *lvalue) { | |||
1015 | ||||
1016 | bool_Bool absolute, fatal = flag & PATH_CHECK_FATAL; | |||
1017 | ||||
1018 | assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE))do { if ((__builtin_expect(!!(!(!(((flag) & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) == (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE )))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)" ), "../src/basic/path-util.c", 1018, __PRETTY_FUNCTION__); } while (0); | |||
1019 | ||||
1020 | if (!utf8_is_valid(path)) { | |||
1021 | log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path)({ int _level = (3); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_invalid_utf8_internal (unit, _level, filename, line, "../src/basic/path-util.c", 1021 , __func__, path) : -22; }); | |||
1022 | return -EINVAL22; | |||
1023 | } | |||
1024 | ||||
1025 | if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { | |||
1026 | absolute = path_is_absolute(path); | |||
1027 | ||||
1028 | if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) { | |||
1029 | log_syntax(unit, LOG_ERR, filename, line, 0,({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1031, __func__ , "%s= path is not absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
1030 | "%s= path is not absolute%s: %s",({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1031, __func__ , "%s= path is not absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
1031 | lvalue, fatal ? "" : ", ignoring", path)({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1031, __func__ , "%s= path is not absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }); | |||
1032 | return -EINVAL22; | |||
1033 | } | |||
1034 | ||||
1035 | if (absolute && (flag & PATH_CHECK_RELATIVE)) { | |||
1036 | log_syntax(unit, LOG_ERR, filename, line, 0,({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1038, __func__ , "%s= path is absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
1037 | "%s= path is absolute%s: %s",({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1038, __func__ , "%s= path is absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
1038 | lvalue, fatal ? "" : ", ignoring", path)({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1038, __func__ , "%s= path is absolute%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }); | |||
1039 | return -EINVAL22; | |||
1040 | } | |||
1041 | } | |||
1042 | ||||
1043 | path_simplify(path, true1); | |||
1044 | ||||
1045 | if (!path_is_normalized(path)) { | |||
1046 | log_syntax(unit, LOG_ERR, filename, line, 0,({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1048, __func__ , "%s= path is not normalized%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
1047 | "%s= path is not normalized%s: %s",({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1048, __func__ , "%s= path is not normalized%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }) | |||
1048 | lvalue, fatal ? "" : ", ignoring", path)({ int _level = (3), _e = (0); (log_get_max_level_realm(LOG_REALM_SYSTEMD ) >= ((_level) & 0x07)) ? log_syntax_internal(unit, _level , filename, line, _e, "../src/basic/path-util.c", 1048, __func__ , "%s= path is not normalized%s: %s", lvalue, fatal ? "" : ", ignoring" , path) : -abs(_e); }); | |||
1049 | return -EINVAL22; | |||
1050 | } | |||
1051 | ||||
1052 | return 0; | |||
1053 | } |