]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/path-util.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / basic / path-util.c
CommitLineData
9eb977db
KS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010-2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
9eb977db 22#include <errno.h>
9eb977db 23#include <fcntl.h>
07630cea
LP
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
9eb977db 27#include <sys/statvfs.h>
07630cea 28#include <unistd.h>
9eb977db 29
5f311f8c
LP
30/* When we include libgen.h because we need dirname() we immediately
31 * undefine basename() since libgen.h defines it as a macro to the
32 * POSIX version which is really broken. We prefer GNU basename(). */
33#include <libgen.h>
34#undef basename
35
b5efdb8a 36#include "alloc-util.h"
3ffd4af2 37#include "fd-util.h"
07630cea 38#include "fileio.h"
f4f15635 39#include "fs-util.h"
9eb977db 40#include "log.h"
07630cea
LP
41#include "macro.h"
42#include "missing.h"
6bedfcbb 43#include "parse-util.h"
3ffd4af2 44#include "path-util.h"
8fcde012 45#include "stat-util.h"
07630cea 46#include "string-util.h"
9eb977db 47#include "strv.h"
07630cea 48#include "util.h"
9eb977db
KS
49
50bool path_is_absolute(const char *p) {
51 return p[0] == '/';
52}
53
54bool is_path(const char *p) {
55 return !!strchr(p, '/');
56}
57
0f474365 58int path_split_and_make_absolute(const char *p, char ***ret) {
9eb977db 59 char **l;
0f474365
LP
60 int r;
61
9eb977db 62 assert(p);
0f474365 63 assert(ret);
9eb977db 64
116cc028
ZJS
65 l = strv_split(p, ":");
66 if (!l)
ce9d6bcf 67 return -ENOMEM;
9eb977db 68
0f474365
LP
69 r = path_strv_make_absolute_cwd(l);
70 if (r < 0) {
9eb977db 71 strv_free(l);
0f474365 72 return r;
9eb977db
KS
73 }
74
0f474365
LP
75 *ret = l;
76 return r;
9eb977db
KS
77}
78
79char *path_make_absolute(const char *p, const char *prefix) {
80 assert(p);
81
82 /* Makes every item in the list an absolute path by prepending
83 * the prefix, if specified and necessary */
84
85 if (path_is_absolute(p) || !prefix)
86 return strdup(p);
87
b7def684 88 return strjoin(prefix, "/", p, NULL);
9eb977db
KS
89}
90
0f474365
LP
91int path_make_absolute_cwd(const char *p, char **ret) {
92 char *c;
9eb977db
KS
93
94 assert(p);
0f474365 95 assert(ret);
9eb977db
KS
96
97 /* Similar to path_make_absolute(), but prefixes with the
98 * current working directory. */
99
100 if (path_is_absolute(p))
0f474365
LP
101 c = strdup(p);
102 else {
103 _cleanup_free_ char *cwd = NULL;
9eb977db 104
0f474365
LP
105 cwd = get_current_dir_name();
106 if (!cwd)
107 return -errno;
108
109 c = strjoin(cwd, "/", p, NULL);
110 }
111 if (!c)
112 return -ENOMEM;
9eb977db 113
0f474365
LP
114 *ret = c;
115 return 0;
9eb977db
KS
116}
117
7cb9c51c
TK
118int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
119 char *r, *p;
120 unsigned n_parents;
7cb9c51c
TK
121
122 assert(from_dir);
123 assert(to_path);
124 assert(_r);
125
126 /* Strips the common part, and adds ".." elements as necessary. */
127
128 if (!path_is_absolute(from_dir))
129 return -EINVAL;
130
131 if (!path_is_absolute(to_path))
132 return -EINVAL;
133
134 /* Skip the common part. */
135 for (;;) {
136 size_t a;
137 size_t b;
138
139 from_dir += strspn(from_dir, "/");
140 to_path += strspn(to_path, "/");
141
142 if (!*from_dir) {
143 if (!*to_path)
144 /* from_dir equals to_path. */
145 r = strdup(".");
146 else
147 /* from_dir is a parent directory of to_path. */
148 r = strdup(to_path);
149
150 if (!r)
151 return -ENOMEM;
152
5216f599
TK
153 path_kill_slashes(r);
154
7cb9c51c
TK
155 *_r = r;
156 return 0;
157 }
158
159 if (!*to_path)
160 break;
161
162 a = strcspn(from_dir, "/");
163 b = strcspn(to_path, "/");
164
165 if (a != b)
166 break;
167
168 if (memcmp(from_dir, to_path, a) != 0)
169 break;
170
171 from_dir += a;
172 to_path += b;
173 }
174
175 /* If we're here, then "from_dir" has one or more elements that need to
176 * be replaced with "..". */
177
178 /* Count the number of necessary ".." elements. */
179 for (n_parents = 0;;) {
180 from_dir += strspn(from_dir, "/");
181
182 if (!*from_dir)
183 break;
184
185 from_dir += strcspn(from_dir, "/");
186 n_parents++;
187 }
188
5216f599 189 r = malloc(n_parents * 3 + strlen(to_path) + 1);
7cb9c51c
TK
190 if (!r)
191 return -ENOMEM;
192
193 for (p = r; n_parents > 0; n_parents--, p += 3)
194 memcpy(p, "../", 3);
195
5216f599
TK
196 strcpy(p, to_path);
197 path_kill_slashes(r);
7cb9c51c
TK
198
199 *_r = r;
200 return 0;
201}
202
0f474365 203int path_strv_make_absolute_cwd(char **l) {
9eb977db 204 char **s;
0f474365 205 int r;
9eb977db
KS
206
207 /* Goes through every item in the string list and makes it
208 * absolute. This works in place and won't rollback any
209 * changes on failure. */
210
211 STRV_FOREACH(s, l) {
212 char *t;
213
0f474365
LP
214 r = path_make_absolute_cwd(*s, &t);
215 if (r < 0)
216 return r;
9eb977db
KS
217
218 free(*s);
219 *s = t;
220 }
221
0f474365 222 return 0;
9eb977db
KS
223}
224
7d8da2c9 225char **path_strv_resolve(char **l, const char *prefix) {
9eb977db
KS
226 char **s;
227 unsigned k = 0;
228 bool enomem = false;
229
230 if (strv_isempty(l))
231 return l;
232
233 /* Goes through every item in the string list and canonicalize
234 * the path. This works in place and won't rollback any
235 * changes on failure. */
236
237 STRV_FOREACH(s, l) {
238 char *t, *u;
12ed81d9 239 _cleanup_free_ char *orig = NULL;
9eb977db 240
12ed81d9
ZJS
241 if (!path_is_absolute(*s)) {
242 free(*s);
9eb977db 243 continue;
12ed81d9 244 }
112cfb18
MM
245
246 if (prefix) {
12ed81d9
ZJS
247 orig = *s;
248 t = strappend(prefix, orig);
112cfb18
MM
249 if (!t) {
250 enomem = true;
251 continue;
252 }
12ed81d9 253 } else
112cfb18 254 t = *s;
9eb977db
KS
255
256 errno = 0;
257 u = canonicalize_file_name(t);
9eb977db 258 if (!u) {
12ed81d9
ZJS
259 if (errno == ENOENT) {
260 if (prefix) {
261 u = orig;
262 orig = NULL;
263 free(t);
264 } else
265 u = t;
266 } else {
874310b7 267 free(t);
112cfb18 268 if (errno == ENOMEM || errno == 0)
874310b7
ZJS
269 enomem = true;
270
271 continue;
272 }
12ed81d9
ZJS
273 } else if (prefix) {
274 char *x;
275
276 free(t);
277 x = path_startswith(u, prefix);
278 if (x) {
279 /* restore the slash if it was lost */
280 if (!startswith(x, "/"))
281 *(--x) = '/';
282
283 t = strdup(x);
284 free(u);
285 if (!t) {
286 enomem = true;
287 continue;
288 }
289 u = t;
290 } else {
291 /* canonicalized path goes outside of
292 * prefix, keep the original path instead */
3542eac7 293 free(u);
12ed81d9
ZJS
294 u = orig;
295 orig = NULL;
296 }
91a6489d
LP
297 } else
298 free(t);
9eb977db
KS
299
300 l[k++] = u;
301 }
302
303 l[k] = NULL;
304
305 if (enomem)
306 return NULL;
307
308 return l;
309}
310
7d8da2c9 311char **path_strv_resolve_uniq(char **l, const char *prefix) {
112cfb18 312
fabe5c0e
LP
313 if (strv_isempty(l))
314 return l;
315
7d8da2c9 316 if (!path_strv_resolve(l, prefix))
fabe5c0e
LP
317 return NULL;
318
319 return strv_uniq(l);
320}
321
9eb977db
KS
322char *path_kill_slashes(char *path) {
323 char *f, *t;
324 bool slash = false;
325
326 /* Removes redundant inner and trailing slashes. Modifies the
327 * passed string in-place.
328 *
329 * ///foo///bar/ becomes /foo/bar
330 */
331
332 for (f = path, t = path; *f; f++) {
333
334 if (*f == '/') {
335 slash = true;
336 continue;
337 }
338
339 if (slash) {
340 slash = false;
341 *(t++) = '/';
342 }
343
344 *(t++) = *f;
345 }
346
347 /* Special rule, if we are talking of the root directory, a
348 trailing slash is good */
349
350 if (t == path && slash)
351 *(t++) = '/';
352
353 *t = 0;
354 return path;
355}
356
424a19f8 357char* path_startswith(const char *path, const char *prefix) {
9eb977db
KS
358 assert(path);
359 assert(prefix);
360
361 if ((path[0] == '/') != (prefix[0] == '/'))
424a19f8 362 return NULL;
9eb977db
KS
363
364 for (;;) {
365 size_t a, b;
366
367 path += strspn(path, "/");
368 prefix += strspn(prefix, "/");
369
370 if (*prefix == 0)
424a19f8 371 return (char*) path;
9eb977db
KS
372
373 if (*path == 0)
424a19f8 374 return NULL;
9eb977db
KS
375
376 a = strcspn(path, "/");
377 b = strcspn(prefix, "/");
378
379 if (a != b)
424a19f8 380 return NULL;
9eb977db
KS
381
382 if (memcmp(path, prefix, a) != 0)
424a19f8 383 return NULL;
9eb977db
KS
384
385 path += a;
386 prefix += b;
387 }
388}
389
2230852b
MS
390int path_compare(const char *a, const char *b) {
391 int d;
392
9eb977db
KS
393 assert(a);
394 assert(b);
395
2230852b
MS
396 /* A relative path and an abolute path must not compare as equal.
397 * Which one is sorted before the other does not really matter.
398 * Here a relative path is ordered before an absolute path. */
399 d = (a[0] == '/') - (b[0] == '/');
373cd63a 400 if (d != 0)
2230852b 401 return d;
9eb977db
KS
402
403 for (;;) {
404 size_t j, k;
405
406 a += strspn(a, "/");
407 b += strspn(b, "/");
408
409 if (*a == 0 && *b == 0)
2230852b 410 return 0;
9eb977db 411
2230852b
MS
412 /* Order prefixes first: "/foo" before "/foo/bar" */
413 if (*a == 0)
414 return -1;
415 if (*b == 0)
416 return 1;
9eb977db
KS
417
418 j = strcspn(a, "/");
419 k = strcspn(b, "/");
420
2230852b
MS
421 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
422 d = memcmp(a, b, MIN(j, k));
373cd63a 423 if (d != 0)
2230852b 424 return (d > 0) - (d < 0); /* sign of d */
9eb977db 425
2230852b
MS
426 /* Sort "/foo/a" before "/foo/aaa" */
427 d = (j > k) - (j < k); /* sign of (j - k) */
373cd63a 428 if (d != 0)
2230852b 429 return d;
9eb977db
KS
430
431 a += j;
432 b += k;
433 }
434}
435
2230852b
MS
436bool path_equal(const char *a, const char *b) {
437 return path_compare(a, b) == 0;
438}
439
c78e47a6
MS
440bool path_equal_or_files_same(const char *a, const char *b) {
441 return path_equal(a, b) || files_same(a, b) > 0;
442}
443
0c6ea3a4
ZJS
444char* path_join(const char *root, const char *path, const char *rest) {
445 assert(path);
446
447 if (!isempty(root))
bc854dc7 448 return strjoin(root, endswith(root, "/") ? "" : "/",
0c6ea3a4 449 path[0] == '/' ? path+1 : path,
bc854dc7 450 rest ? (endswith(path, "/") ? "" : "/") : NULL,
0c6ea3a4
ZJS
451 rest && rest[0] == '/' ? rest+1 : rest,
452 NULL);
453 else
454 return strjoin(path,
bc854dc7 455 rest ? (endswith(path, "/") ? "" : "/") : NULL,
0c6ea3a4
ZJS
456 rest && rest[0] == '/' ? rest+1 : rest,
457 NULL);
458}
459
85eca92e
LP
460int find_binary(const char *name, char **ret) {
461 int last_error, r;
462 const char *p;
463
c9d954b2 464 assert(name);
4087cb9e 465
571d0134 466 if (is_path(name)) {
85eca92e 467 if (access(name, X_OK) < 0)
b972115c
ZJS
468 return -errno;
469
85eca92e 470 if (ret) {
0f474365
LP
471 r = path_make_absolute_cwd(name, ret);
472 if (r < 0)
473 return r;
eb66db55 474 }
c9d954b2 475
c9d954b2 476 return 0;
85eca92e 477 }
c9d954b2 478
85eca92e
LP
479 /**
480 * Plain getenv, not secure_getenv, because we want
481 * to actually allow the user to pick the binary.
482 */
483 p = getenv("PATH");
484 if (!p)
485 p = DEFAULT_PATH;
486
487 last_error = -ENOENT;
488
489 for (;;) {
490 _cleanup_free_ char *j = NULL, *element = NULL;
491
492 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
493 if (r < 0)
494 return r;
495 if (r == 0)
496 break;
497
0f474365
LP
498 if (!path_is_absolute(element))
499 continue;
500
85eca92e
LP
501 j = strjoin(element, "/", name, NULL);
502 if (!j)
503 return -ENOMEM;
504
505 if (access(j, X_OK) >= 0) {
506 /* Found it! */
c9d954b2 507
85eca92e
LP
508 if (ret) {
509 *ret = path_kill_slashes(j);
510 j = NULL;
eb66db55 511 }
c9d954b2
ZJS
512
513 return 0;
514 }
515
85eca92e 516 last_error = -errno;
c9d954b2 517 }
85eca92e
LP
518
519 return last_error;
c9d954b2 520}
8e184852 521
2ad8416d 522bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
8e184852 523 bool changed = false;
2ad8416d 524 const char* const* i;
8e184852 525
97f2d76d
TG
526 assert(timestamp);
527
8e184852 528 if (paths == NULL)
4087cb9e 529 return false;
8e184852 530
4087cb9e 531 STRV_FOREACH(i, paths) {
8e184852 532 struct stat stats;
4087cb9e 533 usec_t u;
8e184852 534
4087cb9e 535 if (stat(*i, &stats) < 0)
8e184852
TG
536 continue;
537
4087cb9e
LP
538 u = timespec_load(&stats.st_mtim);
539
97f2d76d 540 /* first check */
4087cb9e 541 if (*timestamp >= u)
8e184852
TG
542 continue;
543
9f6445e3 544 log_debug("timestamp of '%s' changed", *i);
8e184852
TG
545
546 /* update timestamp */
4087cb9e
LP
547 if (update) {
548 *timestamp = u;
549 changed = true;
550 } else
551 return true;
8e184852 552 }
4087cb9e 553
8e184852
TG
554 return changed;
555}
eb66db55 556
5bcd08db 557static int binary_is_good(const char *binary) {
571d0134 558 _cleanup_free_ char *p = NULL, *d = NULL;
571d0134 559 int r;
eb66db55 560
85eca92e
LP
561 r = find_binary(binary, &p);
562 if (r == -ENOENT)
563 return 0;
571d0134
LP
564 if (r < 0)
565 return r;
566
061df014 567 /* An fsck that is linked to /bin/true is a non-existent
571d0134
LP
568 * fsck */
569
570 r = readlink_malloc(p, &d);
85eca92e
LP
571 if (r == -EINVAL) /* not a symlink */
572 return 1;
573 if (r < 0)
574 return r;
571d0134 575
85eca92e
LP
576 return !path_equal(d, "true") &&
577 !path_equal(d, "/bin/true") &&
578 !path_equal(d, "/usr/bin/true") &&
579 !path_equal(d, "/dev/null");
eb66db55 580}
1d13f648 581
5bcd08db
LP
582int fsck_exists(const char *fstype) {
583 const char *checker;
584
85eca92e 585 assert(fstype);
5bcd08db 586
85eca92e
LP
587 if (streq(fstype, "auto"))
588 return -EINVAL;
589
590 checker = strjoina("fsck.", fstype);
5bcd08db
LP
591 return binary_is_good(checker);
592}
593
594int mkfs_exists(const char *fstype) {
595 const char *mkfs;
596
85eca92e 597 assert(fstype);
5bcd08db 598
85eca92e
LP
599 if (streq(fstype, "auto"))
600 return -EINVAL;
601
602 mkfs = strjoina("mkfs.", fstype);
5bcd08db
LP
603 return binary_is_good(mkfs);
604}
605
1d13f648
LP
606char *prefix_root(const char *root, const char *path) {
607 char *n, *p;
608 size_t l;
609
610 /* If root is passed, prefixes path with it. Otherwise returns
611 * it as is. */
612
613 assert(path);
614
615 /* First, drop duplicate prefixing slashes from the path */
616 while (path[0] == '/' && path[1] == '/')
617 path++;
618
619 if (isempty(root) || path_equal(root, "/"))
620 return strdup(path);
621
622 l = strlen(root) + 1 + strlen(path) + 1;
623
624 n = new(char, l);
625 if (!n)
626 return NULL;
627
628 p = stpcpy(n, root);
629
630 while (p > n && p[-1] == '/')
631 p--;
632
633 if (path[0] != '/')
634 *(p++) = '/';
635
636 strcpy(p, path);
637 return n;
638}
0f03c2a4
LP
639
640int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
641 char *p;
642 int r;
643
644 /*
645 * This function is intended to be used in command line
646 * parsers, to handle paths that are passed in. It makes the
647 * path absolute, and reduces it to NULL if omitted or
648 * root (the latter optionally).
649 *
650 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
651 * SUCCESS! Hence, do not pass in uninitialized pointers.
652 */
653
654 if (isempty(path)) {
655 *arg = mfree(*arg);
656 return 0;
657 }
658
659 r = path_make_absolute_cwd(path, &p);
660 if (r < 0)
661 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
662
663 path_kill_slashes(p);
664 if (suppress_root && path_equal(p, "/"))
665 p = mfree(p);
666
667 free(*arg);
668 *arg = p;
669 return 0;
670}
5f311f8c
LP
671
672char* dirname_malloc(const char *path) {
673 char *d, *dir, *dir2;
674
675 assert(path);
676
677 d = strdup(path);
678 if (!d)
679 return NULL;
680
681 dir = dirname(d);
682 assert(dir);
683
684 if (dir == d)
685 return d;
686
687 dir2 = strdup(dir);
688 free(d);
689
690 return dir2;
691}
bb15fafe
LP
692
693bool filename_is_valid(const char *p) {
694 const char *e;
695
696 if (isempty(p))
697 return false;
698
699 if (streq(p, "."))
700 return false;
701
702 if (streq(p, ".."))
703 return false;
704
705 e = strchrnul(p, '/');
706 if (*e != 0)
707 return false;
708
709 if (e - p > FILENAME_MAX)
710 return false;
711
712 return true;
713}
714
715bool path_is_safe(const char *p) {
716
717 if (isempty(p))
718 return false;
719
720 if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
721 return false;
722
723 if (strlen(p)+1 > PATH_MAX)
724 return false;
725
726 /* The following two checks are not really dangerous, but hey, they still are confusing */
727 if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
728 return false;
729
730 if (strstr(p, "//"))
731 return false;
732
733 return true;
734}
a0956174
LP
735
736char *file_in_same_dir(const char *path, const char *filename) {
737 char *e, *ret;
738 size_t k;
739
740 assert(path);
741 assert(filename);
742
743 /* This removes the last component of path and appends
744 * filename, unless the latter is absolute anyway or the
745 * former isn't */
746
747 if (path_is_absolute(filename))
748 return strdup(filename);
749
750 e = strrchr(path, '/');
751 if (!e)
752 return strdup(filename);
753
754 k = strlen(filename);
755 ret = new(char, (e + 1 - path) + k + 1);
756 if (!ret)
757 return NULL;
758
759 memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
760 return ret;
761}
762
763bool hidden_file_allow_backup(const char *filename) {
764 assert(filename);
765
766 return
767 filename[0] == '.' ||
768 streq(filename, "lost+found") ||
769 streq(filename, "aquota.user") ||
770 streq(filename, "aquota.group") ||
771 endswith(filename, ".rpmnew") ||
772 endswith(filename, ".rpmsave") ||
773 endswith(filename, ".rpmorig") ||
774 endswith(filename, ".dpkg-old") ||
775 endswith(filename, ".dpkg-new") ||
776 endswith(filename, ".dpkg-tmp") ||
777 endswith(filename, ".dpkg-dist") ||
778 endswith(filename, ".dpkg-bak") ||
779 endswith(filename, ".dpkg-backup") ||
780 endswith(filename, ".dpkg-remove") ||
781 endswith(filename, ".swp");
782}
783
784bool hidden_file(const char *filename) {
785 assert(filename);
786
787 if (endswith(filename, "~"))
788 return true;
789
790 return hidden_file_allow_backup(filename);
791}
792
793bool is_device_path(const char *path) {
794
795 /* Returns true on paths that refer to a device, either in
796 * sysfs or in /dev */
797
798 return
799 path_startswith(path, "/dev/") ||
800 path_startswith(path, "/sys/");
801}