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