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