]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/path-util.c
util-lib: handle empty string in last_path_component
[thirdparty/systemd.git] / src / basic / path-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9eb977db
KS
2/***
3 This file is part of systemd.
4
5 Copyright 2010-2012 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
9eb977db 21#include <errno.h>
11c3a366 22#include <limits.h>
07630cea
LP
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
11c3a366 26#include <sys/stat.h>
07630cea 27#include <unistd.h>
9eb977db 28
5f311f8c
LP
29/* When we include libgen.h because we need dirname() we immediately
30 * undefine basename() since libgen.h defines it as a macro to the
31 * POSIX version which is really broken. We prefer GNU basename(). */
32#include <libgen.h>
33#undef basename
34
b5efdb8a 35#include "alloc-util.h"
93cc7779 36#include "extract-word.h"
f4f15635 37#include "fs-util.h"
5a46d55f 38#include "glob-util.h"
9eb977db 39#include "log.h"
07630cea
LP
40#include "macro.h"
41#include "missing.h"
5a46d55f 42#include "parse-util.h"
3ffd4af2 43#include "path-util.h"
8fcde012 44#include "stat-util.h"
07630cea 45#include "string-util.h"
9eb977db 46#include "strv.h"
93cc7779 47#include "time-util.h"
9eb977db
KS
48
49bool path_is_absolute(const char *p) {
50 return p[0] == '/';
51}
52
53bool is_path(const char *p) {
54 return !!strchr(p, '/');
55}
56
0f474365 57int path_split_and_make_absolute(const char *p, char ***ret) {
9eb977db 58 char **l;
0f474365
LP
59 int r;
60
9eb977db 61 assert(p);
0f474365 62 assert(ret);
9eb977db 63
116cc028
ZJS
64 l = strv_split(p, ":");
65 if (!l)
ce9d6bcf 66 return -ENOMEM;
9eb977db 67
0f474365
LP
68 r = path_strv_make_absolute_cwd(l);
69 if (r < 0) {
9eb977db 70 strv_free(l);
0f474365 71 return r;
9eb977db
KS
72 }
73
0f474365
LP
74 *ret = l;
75 return r;
9eb977db
KS
76}
77
78char *path_make_absolute(const char *p, const char *prefix) {
79 assert(p);
80
81 /* Makes every item in the list an absolute path by prepending
82 * the prefix, if specified and necessary */
83
84 if (path_is_absolute(p) || !prefix)
85 return strdup(p);
86
605405c6 87 return strjoin(prefix, "/", p);
9eb977db
KS
88}
89
0f474365
LP
90int path_make_absolute_cwd(const char *p, char **ret) {
91 char *c;
9eb977db
KS
92
93 assert(p);
0f474365 94 assert(ret);
9eb977db
KS
95
96 /* Similar to path_make_absolute(), but prefixes with the
97 * current working directory. */
98
99 if (path_is_absolute(p))
0f474365
LP
100 c = strdup(p);
101 else {
102 _cleanup_free_ char *cwd = NULL;
9eb977db 103
0f474365
LP
104 cwd = get_current_dir_name();
105 if (!cwd)
9c4615fb 106 return negative_errno();
0f474365 107
605405c6 108 c = strjoin(cwd, "/", p);
0f474365
LP
109 }
110 if (!c)
111 return -ENOMEM;
9eb977db 112
0f474365
LP
113 *ret = c;
114 return 0;
9eb977db
KS
115}
116
7cb9c51c
TK
117int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
118 char *r, *p;
119 unsigned n_parents;
7cb9c51c
TK
120
121 assert(from_dir);
122 assert(to_path);
123 assert(_r);
124
125 /* Strips the common part, and adds ".." elements as necessary. */
126
127 if (!path_is_absolute(from_dir))
128 return -EINVAL;
129
130 if (!path_is_absolute(to_path))
131 return -EINVAL;
132
133 /* Skip the common part. */
134 for (;;) {
2a5beb66 135 size_t a, b;
7cb9c51c
TK
136
137 from_dir += strspn(from_dir, "/");
138 to_path += strspn(to_path, "/");
139
140 if (!*from_dir) {
141 if (!*to_path)
142 /* from_dir equals to_path. */
143 r = strdup(".");
144 else
145 /* from_dir is a parent directory of to_path. */
146 r = strdup(to_path);
7cb9c51c
TK
147 if (!r)
148 return -ENOMEM;
149
5216f599
TK
150 path_kill_slashes(r);
151
7cb9c51c
TK
152 *_r = r;
153 return 0;
154 }
155
156 if (!*to_path)
157 break;
158
159 a = strcspn(from_dir, "/");
160 b = strcspn(to_path, "/");
161
162 if (a != b)
163 break;
164
165 if (memcmp(from_dir, to_path, a) != 0)
166 break;
167
168 from_dir += a;
169 to_path += b;
170 }
171
172 /* If we're here, then "from_dir" has one or more elements that need to
173 * be replaced with "..". */
174
175 /* Count the number of necessary ".." elements. */
176 for (n_parents = 0;;) {
2a5beb66
LP
177 size_t w;
178
7cb9c51c
TK
179 from_dir += strspn(from_dir, "/");
180
181 if (!*from_dir)
182 break;
183
2a5beb66
LP
184 w = strcspn(from_dir, "/");
185
186 /* If this includes ".." we can't do a simple series of "..", refuse */
187 if (w == 2 && from_dir[0] == '.' && from_dir[1] == '.')
188 return -EINVAL;
189
190 /* Count number of elements, except if they are "." */
191 if (w != 1 || from_dir[0] != '.')
192 n_parents++;
193
194 from_dir += w;
7cb9c51c
TK
195 }
196
2a5beb66 197 r = new(char, n_parents * 3 + strlen(to_path) + 1);
7cb9c51c
TK
198 if (!r)
199 return -ENOMEM;
200
2a5beb66
LP
201 for (p = r; n_parents > 0; n_parents--)
202 p = mempcpy(p, "../", 3);
7cb9c51c 203
5216f599
TK
204 strcpy(p, to_path);
205 path_kill_slashes(r);
7cb9c51c
TK
206
207 *_r = r;
208 return 0;
209}
210
0f474365 211int path_strv_make_absolute_cwd(char **l) {
9eb977db 212 char **s;
0f474365 213 int r;
9eb977db
KS
214
215 /* Goes through every item in the string list and makes it
216 * absolute. This works in place and won't rollback any
217 * changes on failure. */
218
219 STRV_FOREACH(s, l) {
220 char *t;
221
0f474365
LP
222 r = path_make_absolute_cwd(*s, &t);
223 if (r < 0)
224 return r;
9eb977db
KS
225
226 free(*s);
227 *s = t;
228 }
229
0f474365 230 return 0;
9eb977db
KS
231}
232
e1873695 233char **path_strv_resolve(char **l, const char *root) {
9eb977db
KS
234 char **s;
235 unsigned k = 0;
236 bool enomem = false;
e1873695 237 int r;
9eb977db
KS
238
239 if (strv_isempty(l))
240 return l;
241
242 /* Goes through every item in the string list and canonicalize
243 * the path. This works in place and won't rollback any
244 * changes on failure. */
245
246 STRV_FOREACH(s, l) {
12ed81d9 247 _cleanup_free_ char *orig = NULL;
e1873695 248 char *t, *u;
9eb977db 249
12ed81d9
ZJS
250 if (!path_is_absolute(*s)) {
251 free(*s);
9eb977db 252 continue;
12ed81d9 253 }
112cfb18 254
e1873695 255 if (root) {
12ed81d9 256 orig = *s;
e1873695 257 t = prefix_root(root, orig);
112cfb18
MM
258 if (!t) {
259 enomem = true;
260 continue;
261 }
12ed81d9 262 } else
112cfb18 263 t = *s;
9eb977db 264
c4f4fce7 265 r = chase_symlinks(t, root, 0, &u);
e1873695
LP
266 if (r == -ENOENT) {
267 if (root) {
268 u = orig;
269 orig = NULL;
874310b7 270 free(t);
e1873695
LP
271 } else
272 u = t;
273 } else if (r < 0) {
274 free(t);
874310b7 275
e1873695
LP
276 if (r == -ENOMEM)
277 enomem = true;
278
279 continue;
280 } else if (root) {
12ed81d9
ZJS
281 char *x;
282
283 free(t);
e1873695 284 x = path_startswith(u, root);
12ed81d9
ZJS
285 if (x) {
286 /* restore the slash if it was lost */
287 if (!startswith(x, "/"))
288 *(--x) = '/';
289
290 t = strdup(x);
291 free(u);
292 if (!t) {
293 enomem = true;
294 continue;
295 }
296 u = t;
297 } else {
298 /* canonicalized path goes outside of
299 * prefix, keep the original path instead */
3b319885 300 free_and_replace(u, orig);
12ed81d9 301 }
91a6489d
LP
302 } else
303 free(t);
9eb977db
KS
304
305 l[k++] = u;
306 }
307
308 l[k] = NULL;
309
310 if (enomem)
311 return NULL;
312
313 return l;
314}
315
e1873695 316char **path_strv_resolve_uniq(char **l, const char *root) {
112cfb18 317
fabe5c0e
LP
318 if (strv_isempty(l))
319 return l;
320
e1873695 321 if (!path_strv_resolve(l, root))
fabe5c0e
LP
322 return NULL;
323
324 return strv_uniq(l);
325}
326
9eb977db
KS
327char *path_kill_slashes(char *path) {
328 char *f, *t;
329 bool slash = false;
330
331 /* Removes redundant inner and trailing slashes. Modifies the
332 * passed string in-place.
333 *
334 * ///foo///bar/ becomes /foo/bar
335 */
336
337 for (f = path, t = path; *f; f++) {
338
339 if (*f == '/') {
340 slash = true;
341 continue;
342 }
343
344 if (slash) {
345 slash = false;
346 *(t++) = '/';
347 }
348
349 *(t++) = *f;
350 }
351
352 /* Special rule, if we are talking of the root directory, a
353 trailing slash is good */
354
355 if (t == path && slash)
356 *(t++) = '/';
357
358 *t = 0;
359 return path;
360}
361
424a19f8 362char* path_startswith(const char *path, const char *prefix) {
9eb977db
KS
363 assert(path);
364 assert(prefix);
365
0470289b
ZJS
366 /* Returns a pointer to the start of the first component after the parts matched by
367 * the prefix, iff
368 * - both paths are absolute or both paths are relative,
369 * and
370 * - each component in prefix in turn matches a component in path at the same position.
371 * An empty string will be returned when the prefix and path are equivalent.
372 *
373 * Returns NULL otherwise.
374 */
375
9eb977db 376 if ((path[0] == '/') != (prefix[0] == '/'))
424a19f8 377 return NULL;
9eb977db
KS
378
379 for (;;) {
380 size_t a, b;
381
382 path += strspn(path, "/");
383 prefix += strspn(prefix, "/");
384
385 if (*prefix == 0)
424a19f8 386 return (char*) path;
9eb977db
KS
387
388 if (*path == 0)
424a19f8 389 return NULL;
9eb977db
KS
390
391 a = strcspn(path, "/");
392 b = strcspn(prefix, "/");
393
394 if (a != b)
424a19f8 395 return NULL;
9eb977db
KS
396
397 if (memcmp(path, prefix, a) != 0)
424a19f8 398 return NULL;
9eb977db
KS
399
400 path += a;
401 prefix += b;
402 }
403}
404
2230852b
MS
405int path_compare(const char *a, const char *b) {
406 int d;
407
9eb977db
KS
408 assert(a);
409 assert(b);
410
2230852b
MS
411 /* A relative path and an abolute path must not compare as equal.
412 * Which one is sorted before the other does not really matter.
413 * Here a relative path is ordered before an absolute path. */
414 d = (a[0] == '/') - (b[0] == '/');
373cd63a 415 if (d != 0)
2230852b 416 return d;
9eb977db
KS
417
418 for (;;) {
419 size_t j, k;
420
421 a += strspn(a, "/");
422 b += strspn(b, "/");
423
424 if (*a == 0 && *b == 0)
2230852b 425 return 0;
9eb977db 426
2230852b
MS
427 /* Order prefixes first: "/foo" before "/foo/bar" */
428 if (*a == 0)
429 return -1;
430 if (*b == 0)
431 return 1;
9eb977db
KS
432
433 j = strcspn(a, "/");
434 k = strcspn(b, "/");
435
2230852b
MS
436 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
437 d = memcmp(a, b, MIN(j, k));
373cd63a 438 if (d != 0)
2230852b 439 return (d > 0) - (d < 0); /* sign of d */
9eb977db 440
2230852b
MS
441 /* Sort "/foo/a" before "/foo/aaa" */
442 d = (j > k) - (j < k); /* sign of (j - k) */
373cd63a 443 if (d != 0)
2230852b 444 return d;
9eb977db
KS
445
446 a += j;
447 b += k;
448 }
449}
450
2230852b
MS
451bool path_equal(const char *a, const char *b) {
452 return path_compare(a, b) == 0;
453}
454
e3f791a2
ZJS
455bool path_equal_or_files_same(const char *a, const char *b, int flags) {
456 return path_equal(a, b) || files_same(a, b, flags) > 0;
c78e47a6
MS
457}
458
0c6ea3a4
ZJS
459char* path_join(const char *root, const char *path, const char *rest) {
460 assert(path);
461
462 if (!isempty(root))
bc854dc7 463 return strjoin(root, endswith(root, "/") ? "" : "/",
0c6ea3a4 464 path[0] == '/' ? path+1 : path,
bc854dc7 465 rest ? (endswith(path, "/") ? "" : "/") : NULL,
605405c6 466 rest && rest[0] == '/' ? rest+1 : rest);
0c6ea3a4
ZJS
467 else
468 return strjoin(path,
bc854dc7 469 rest ? (endswith(path, "/") ? "" : "/") : NULL,
605405c6 470 rest && rest[0] == '/' ? rest+1 : rest);
0c6ea3a4
ZJS
471}
472
85eca92e
LP
473int find_binary(const char *name, char **ret) {
474 int last_error, r;
475 const char *p;
476
c9d954b2 477 assert(name);
4087cb9e 478
571d0134 479 if (is_path(name)) {
85eca92e 480 if (access(name, X_OK) < 0)
b972115c
ZJS
481 return -errno;
482
85eca92e 483 if (ret) {
0f474365
LP
484 r = path_make_absolute_cwd(name, ret);
485 if (r < 0)
486 return r;
eb66db55 487 }
c9d954b2 488
c9d954b2 489 return 0;
85eca92e 490 }
c9d954b2 491
85eca92e
LP
492 /**
493 * Plain getenv, not secure_getenv, because we want
494 * to actually allow the user to pick the binary.
495 */
496 p = getenv("PATH");
497 if (!p)
498 p = DEFAULT_PATH;
499
500 last_error = -ENOENT;
501
502 for (;;) {
503 _cleanup_free_ char *j = NULL, *element = NULL;
504
505 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
506 if (r < 0)
507 return r;
508 if (r == 0)
509 break;
510
0f474365
LP
511 if (!path_is_absolute(element))
512 continue;
513
605405c6 514 j = strjoin(element, "/", name);
85eca92e
LP
515 if (!j)
516 return -ENOMEM;
517
518 if (access(j, X_OK) >= 0) {
519 /* Found it! */
c9d954b2 520
85eca92e
LP
521 if (ret) {
522 *ret = path_kill_slashes(j);
523 j = NULL;
eb66db55 524 }
c9d954b2
ZJS
525
526 return 0;
527 }
528
85eca92e 529 last_error = -errno;
c9d954b2 530 }
85eca92e
LP
531
532 return last_error;
c9d954b2 533}
8e184852 534
2ad8416d 535bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
8e184852 536 bool changed = false;
2ad8416d 537 const char* const* i;
8e184852 538
97f2d76d
TG
539 assert(timestamp);
540
8e184852 541 if (paths == NULL)
4087cb9e 542 return false;
8e184852 543
4087cb9e 544 STRV_FOREACH(i, paths) {
8e184852 545 struct stat stats;
4087cb9e 546 usec_t u;
8e184852 547
4087cb9e 548 if (stat(*i, &stats) < 0)
8e184852
TG
549 continue;
550
4087cb9e
LP
551 u = timespec_load(&stats.st_mtim);
552
97f2d76d 553 /* first check */
4087cb9e 554 if (*timestamp >= u)
8e184852
TG
555 continue;
556
9f6445e3 557 log_debug("timestamp of '%s' changed", *i);
8e184852
TG
558
559 /* update timestamp */
4087cb9e
LP
560 if (update) {
561 *timestamp = u;
562 changed = true;
563 } else
564 return true;
8e184852 565 }
4087cb9e 566
8e184852
TG
567 return changed;
568}
eb66db55 569
5bcd08db 570static int binary_is_good(const char *binary) {
571d0134 571 _cleanup_free_ char *p = NULL, *d = NULL;
571d0134 572 int r;
eb66db55 573
85eca92e
LP
574 r = find_binary(binary, &p);
575 if (r == -ENOENT)
576 return 0;
571d0134
LP
577 if (r < 0)
578 return r;
579
061df014 580 /* An fsck that is linked to /bin/true is a non-existent
571d0134
LP
581 * fsck */
582
583 r = readlink_malloc(p, &d);
85eca92e
LP
584 if (r == -EINVAL) /* not a symlink */
585 return 1;
586 if (r < 0)
587 return r;
571d0134 588
3ae5990c
ZJS
589 return !PATH_IN_SET(d, "true"
590 "/bin/true",
591 "/usr/bin/true",
592 "/dev/null");
eb66db55 593}
1d13f648 594
5bcd08db
LP
595int fsck_exists(const char *fstype) {
596 const char *checker;
597
85eca92e 598 assert(fstype);
5bcd08db 599
85eca92e
LP
600 if (streq(fstype, "auto"))
601 return -EINVAL;
602
603 checker = strjoina("fsck.", fstype);
5bcd08db
LP
604 return binary_is_good(checker);
605}
606
607int mkfs_exists(const char *fstype) {
608 const char *mkfs;
609
85eca92e 610 assert(fstype);
5bcd08db 611
85eca92e
LP
612 if (streq(fstype, "auto"))
613 return -EINVAL;
614
615 mkfs = strjoina("mkfs.", fstype);
5bcd08db
LP
616 return binary_is_good(mkfs);
617}
618
1d13f648
LP
619char *prefix_root(const char *root, const char *path) {
620 char *n, *p;
621 size_t l;
622
623 /* If root is passed, prefixes path with it. Otherwise returns
624 * it as is. */
625
626 assert(path);
627
628 /* First, drop duplicate prefixing slashes from the path */
629 while (path[0] == '/' && path[1] == '/')
630 path++;
631
632 if (isempty(root) || path_equal(root, "/"))
633 return strdup(path);
634
635 l = strlen(root) + 1 + strlen(path) + 1;
636
637 n = new(char, l);
638 if (!n)
639 return NULL;
640
641 p = stpcpy(n, root);
642
643 while (p > n && p[-1] == '/')
644 p--;
645
646 if (path[0] != '/')
647 *(p++) = '/';
648
649 strcpy(p, path);
650 return n;
651}
0f03c2a4
LP
652
653int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
654 char *p;
655 int r;
656
657 /*
658 * This function is intended to be used in command line
659 * parsers, to handle paths that are passed in. It makes the
660 * path absolute, and reduces it to NULL if omitted or
661 * root (the latter optionally).
662 *
663 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
664 * SUCCESS! Hence, do not pass in uninitialized pointers.
665 */
666
667 if (isempty(path)) {
668 *arg = mfree(*arg);
669 return 0;
670 }
671
672 r = path_make_absolute_cwd(path, &p);
673 if (r < 0)
674 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
675
676 path_kill_slashes(p);
677 if (suppress_root && path_equal(p, "/"))
678 p = mfree(p);
679
680 free(*arg);
681 *arg = p;
682 return 0;
683}
5f311f8c
LP
684
685char* dirname_malloc(const char *path) {
686 char *d, *dir, *dir2;
687
688 assert(path);
689
690 d = strdup(path);
691 if (!d)
692 return NULL;
693
694 dir = dirname(d);
695 assert(dir);
696
697 if (dir == d)
698 return d;
699
700 dir2 = strdup(dir);
701 free(d);
702
703 return dir2;
704}
bb15fafe 705
b12d25a8
ZJS
706const char *last_path_component(const char *path) {
707 /* Finds the last component of the path, preserving the
708 * optional trailing slash that signifies a directory.
709 * a/b/c → c
710 * a/b/c/ → c/
711 * / → /
712 * // → /
713 * /foo/a → a
714 * /foo/a/ → a/
715 * This is different than basename, which returns "" when
716 * a trailing slash is present.
717 */
718
719 unsigned l, k;
720
721 l = k = strlen(path);
69f9ccf1
ZJS
722 if (l == 0) /* special case — an empty string */
723 return path;
724
b12d25a8
ZJS
725 while (k > 0 && path[k-1] == '/')
726 k--;
727
728 if (k == 0) /* the root directory */
729 return path + l - 1;
730
731 while (k > 0 && path[k-1] != '/')
732 k--;
733
734 return path + k;
735}
736
bb15fafe
LP
737bool filename_is_valid(const char *p) {
738 const char *e;
739
740 if (isempty(p))
741 return false;
742
49bfc877 743 if (dot_or_dot_dot(p))
bb15fafe
LP
744 return false;
745
746 e = strchrnul(p, '/');
747 if (*e != 0)
748 return false;
749
750 if (e - p > FILENAME_MAX)
751 return false;
752
753 return true;
754}
755
99be45a4 756bool path_is_normalized(const char *p) {
bb15fafe
LP
757
758 if (isempty(p))
759 return false;
760
49bfc877
LP
761 if (dot_or_dot_dot(p))
762 return false;
763
764 if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
bb15fafe
LP
765 return false;
766
767 if (strlen(p)+1 > PATH_MAX)
768 return false;
769
49bfc877 770 if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
bb15fafe
LP
771 return false;
772
773 if (strstr(p, "//"))
774 return false;
775
776 return true;
777}
a0956174
LP
778
779char *file_in_same_dir(const char *path, const char *filename) {
780 char *e, *ret;
781 size_t k;
782
783 assert(path);
784 assert(filename);
785
786 /* This removes the last component of path and appends
787 * filename, unless the latter is absolute anyway or the
788 * former isn't */
789
790 if (path_is_absolute(filename))
791 return strdup(filename);
792
793 e = strrchr(path, '/');
794 if (!e)
795 return strdup(filename);
796
797 k = strlen(filename);
798 ret = new(char, (e + 1 - path) + k + 1);
799 if (!ret)
800 return NULL;
801
802 memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
803 return ret;
804}
805
55cdd057
ZJS
806bool hidden_or_backup_file(const char *filename) {
807 const char *p;
a0956174 808
a0956174
LP
809 assert(filename);
810
55cdd057
ZJS
811 if (filename[0] == '.' ||
812 streq(filename, "lost+found") ||
813 streq(filename, "aquota.user") ||
814 streq(filename, "aquota.group") ||
815 endswith(filename, "~"))
a0956174
LP
816 return true;
817
55cdd057
ZJS
818 p = strrchr(filename, '.');
819 if (!p)
820 return false;
821
941060bf
LP
822 /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
823 * with always new suffixes and that everybody else should just adjust to that, then it really should be on
824 * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
825 * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
826 * string. Specifically: there's now:
827 *
828 * The generic suffixes "~" and ".bak" for backup files
829 * The generic prefix "." for hidden files
830 *
94a0ef6e
ZJS
831 * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
832 * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
941060bf
LP
833 */
834
55cdd057
ZJS
835 return STR_IN_SET(p + 1,
836 "rpmnew",
837 "rpmsave",
838 "rpmorig",
839 "dpkg-old",
840 "dpkg-new",
841 "dpkg-tmp",
842 "dpkg-dist",
843 "dpkg-bak",
844 "dpkg-backup",
845 "dpkg-remove",
846 "ucf-new",
847 "ucf-old",
848 "ucf-dist",
941060bf 849 "swp",
94a0ef6e
ZJS
850 "bak",
851 "old",
852 "new");
a0956174
LP
853}
854
855bool is_device_path(const char *path) {
856
857 /* Returns true on paths that refer to a device, either in
858 * sysfs or in /dev */
859
3ccb8862
ZJS
860 return path_startswith(path, "/dev/") ||
861 path_startswith(path, "/sys/");
862}
863
864bool is_deviceallow_pattern(const char *path) {
865 return path_startswith(path, "/dev/") ||
866 startswith(path, "block-") ||
867 startswith(path, "char-");
a0956174 868}
5a46d55f
ZJS
869
870int systemd_installation_has_version(const char *root, unsigned minimal_version) {
871 const char *pattern;
872 int r;
873
874 /* Try to guess if systemd installation is later than the specified version. This
875 * is hacky and likely to yield false negatives, particularly if the installation
876 * is non-standard. False positives should be relatively rare.
877 */
878
879 NULSTR_FOREACH(pattern,
880 /* /lib works for systems without usr-merge, and for systems with a sane
881 * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
882 * for Gentoo which does a merge without making /lib a symlink.
883 */
884 "lib/systemd/libsystemd-shared-*.so\0"
885 "usr/lib/systemd/libsystemd-shared-*.so\0") {
886
887 _cleanup_strv_free_ char **names = NULL;
888 _cleanup_free_ char *path = NULL;
889 char *c, **name;
890
891 path = prefix_root(root, pattern);
892 if (!path)
893 return -ENOMEM;
894
895 r = glob_extend(&names, path);
896 if (r == -ENOENT)
897 continue;
898 if (r < 0)
899 return r;
900
901 assert_se((c = endswith(path, "*.so")));
902 *c = '\0'; /* truncate the glob part */
903
904 STRV_FOREACH(name, names) {
905 /* This is most likely to run only once, hence let's not optimize anything. */
906 char *t, *t2;
907 unsigned version;
908
909 t = startswith(*name, path);
910 if (!t)
911 continue;
912
913 t2 = endswith(t, ".so");
914 if (!t2)
915 continue;
916
917 t2[0] = '\0'; /* truncate the suffix */
918
919 r = safe_atou(t, &version);
920 if (r < 0) {
921 log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
922 continue;
923 }
924
925 log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
926 *name, version,
927 version >= minimal_version ? "OK" : "too old");
928 if (version >= minimal_version)
929 return true;
930 }
931 }
932
933 return false;
934}
49bfc877
LP
935
936bool dot_or_dot_dot(const char *path) {
937 if (!path)
938 return false;
939 if (path[0] != '.')
940 return false;
941 if (path[1] == 0)
942 return true;
943 if (path[1] != '.')
944 return false;
945
946 return path[2] == 0;
947}