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