]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/path-util.c
61fab0e0878a383d9992dc0a88d22bd181d72478
[thirdparty/systemd.git] / src / basic / path-util.c
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
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
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
36 #include "alloc-util.h"
37 #include "extract-word.h"
38 #include "fs-util.h"
39 #include "log.h"
40 #include "macro.h"
41 #include "missing.h"
42 #include "path-util.h"
43 #include "stat-util.h"
44 #include "string-util.h"
45 #include "strv.h"
46 #include "time-util.h"
47
48 bool path_is_absolute(const char *p) {
49 return p[0] == '/';
50 }
51
52 bool is_path(const char *p) {
53 return !!strchr(p, '/');
54 }
55
56 int path_split_and_make_absolute(const char *p, char ***ret) {
57 char **l;
58 int r;
59
60 assert(p);
61 assert(ret);
62
63 l = strv_split(p, ":");
64 if (!l)
65 return -ENOMEM;
66
67 r = path_strv_make_absolute_cwd(l);
68 if (r < 0) {
69 strv_free(l);
70 return r;
71 }
72
73 *ret = l;
74 return r;
75 }
76
77 char *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
86 return strjoin(prefix, "/", p, NULL);
87 }
88
89 int path_make_absolute_cwd(const char *p, char **ret) {
90 char *c;
91
92 assert(p);
93 assert(ret);
94
95 /* Similar to path_make_absolute(), but prefixes with the
96 * current working directory. */
97
98 if (path_is_absolute(p))
99 c = strdup(p);
100 else {
101 _cleanup_free_ char *cwd = NULL;
102
103 cwd = get_current_dir_name();
104 if (!cwd)
105 return -errno;
106
107 c = strjoin(cwd, "/", p, NULL);
108 }
109 if (!c)
110 return -ENOMEM;
111
112 *ret = c;
113 return 0;
114 }
115
116 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
117 char *r, *p;
118 unsigned n_parents;
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
151 path_kill_slashes(r);
152
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
187 r = malloc(n_parents * 3 + strlen(to_path) + 1);
188 if (!r)
189 return -ENOMEM;
190
191 for (p = r; n_parents > 0; n_parents--, p += 3)
192 memcpy(p, "../", 3);
193
194 strcpy(p, to_path);
195 path_kill_slashes(r);
196
197 *_r = r;
198 return 0;
199 }
200
201 int path_strv_make_absolute_cwd(char **l) {
202 char **s;
203 int r;
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
212 r = path_make_absolute_cwd(*s, &t);
213 if (r < 0)
214 return r;
215
216 free(*s);
217 *s = t;
218 }
219
220 return 0;
221 }
222
223 char **path_strv_resolve(char **l, const char *prefix) {
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;
237 _cleanup_free_ char *orig = NULL;
238
239 if (!path_is_absolute(*s)) {
240 free(*s);
241 continue;
242 }
243
244 if (prefix) {
245 orig = *s;
246 t = strappend(prefix, orig);
247 if (!t) {
248 enomem = true;
249 continue;
250 }
251 } else
252 t = *s;
253
254 errno = 0;
255 u = canonicalize_file_name(t);
256 if (!u) {
257 if (errno == ENOENT) {
258 if (prefix) {
259 u = orig;
260 orig = NULL;
261 free(t);
262 } else
263 u = t;
264 } else {
265 free(t);
266 if (errno == ENOMEM || errno == 0)
267 enomem = true;
268
269 continue;
270 }
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 */
291 free(u);
292 u = orig;
293 orig = NULL;
294 }
295 } else
296 free(t);
297
298 l[k++] = u;
299 }
300
301 l[k] = NULL;
302
303 if (enomem)
304 return NULL;
305
306 return l;
307 }
308
309 char **path_strv_resolve_uniq(char **l, const char *prefix) {
310
311 if (strv_isempty(l))
312 return l;
313
314 if (!path_strv_resolve(l, prefix))
315 return NULL;
316
317 return strv_uniq(l);
318 }
319
320 char *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
355 char* path_startswith(const char *path, const char *prefix) {
356 assert(path);
357 assert(prefix);
358
359 if ((path[0] == '/') != (prefix[0] == '/'))
360 return NULL;
361
362 for (;;) {
363 size_t a, b;
364
365 path += strspn(path, "/");
366 prefix += strspn(prefix, "/");
367
368 if (*prefix == 0)
369 return (char*) path;
370
371 if (*path == 0)
372 return NULL;
373
374 a = strcspn(path, "/");
375 b = strcspn(prefix, "/");
376
377 if (a != b)
378 return NULL;
379
380 if (memcmp(path, prefix, a) != 0)
381 return NULL;
382
383 path += a;
384 prefix += b;
385 }
386 }
387
388 int path_compare(const char *a, const char *b) {
389 int d;
390
391 assert(a);
392 assert(b);
393
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] == '/');
398 if (d != 0)
399 return d;
400
401 for (;;) {
402 size_t j, k;
403
404 a += strspn(a, "/");
405 b += strspn(b, "/");
406
407 if (*a == 0 && *b == 0)
408 return 0;
409
410 /* Order prefixes first: "/foo" before "/foo/bar" */
411 if (*a == 0)
412 return -1;
413 if (*b == 0)
414 return 1;
415
416 j = strcspn(a, "/");
417 k = strcspn(b, "/");
418
419 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
420 d = memcmp(a, b, MIN(j, k));
421 if (d != 0)
422 return (d > 0) - (d < 0); /* sign of d */
423
424 /* Sort "/foo/a" before "/foo/aaa" */
425 d = (j > k) - (j < k); /* sign of (j - k) */
426 if (d != 0)
427 return d;
428
429 a += j;
430 b += k;
431 }
432 }
433
434 bool path_equal(const char *a, const char *b) {
435 return path_compare(a, b) == 0;
436 }
437
438 bool path_equal_or_files_same(const char *a, const char *b) {
439 return path_equal(a, b) || files_same(a, b) > 0;
440 }
441
442 char* path_join(const char *root, const char *path, const char *rest) {
443 assert(path);
444
445 if (!isempty(root))
446 return strjoin(root, endswith(root, "/") ? "" : "/",
447 path[0] == '/' ? path+1 : path,
448 rest ? (endswith(path, "/") ? "" : "/") : NULL,
449 rest && rest[0] == '/' ? rest+1 : rest,
450 NULL);
451 else
452 return strjoin(path,
453 rest ? (endswith(path, "/") ? "" : "/") : NULL,
454 rest && rest[0] == '/' ? rest+1 : rest,
455 NULL);
456 }
457
458 int find_binary(const char *name, char **ret) {
459 int last_error, r;
460 const char *p;
461
462 assert(name);
463
464 if (is_path(name)) {
465 if (access(name, X_OK) < 0)
466 return -errno;
467
468 if (ret) {
469 r = path_make_absolute_cwd(name, ret);
470 if (r < 0)
471 return r;
472 }
473
474 return 0;
475 }
476
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
496 if (!path_is_absolute(element))
497 continue;
498
499 j = strjoin(element, "/", name, NULL);
500 if (!j)
501 return -ENOMEM;
502
503 if (access(j, X_OK) >= 0) {
504 /* Found it! */
505
506 if (ret) {
507 *ret = path_kill_slashes(j);
508 j = NULL;
509 }
510
511 return 0;
512 }
513
514 last_error = -errno;
515 }
516
517 return last_error;
518 }
519
520 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
521 bool changed = false;
522 const char* const* i;
523
524 assert(timestamp);
525
526 if (paths == NULL)
527 return false;
528
529 STRV_FOREACH(i, paths) {
530 struct stat stats;
531 usec_t u;
532
533 if (stat(*i, &stats) < 0)
534 continue;
535
536 u = timespec_load(&stats.st_mtim);
537
538 /* first check */
539 if (*timestamp >= u)
540 continue;
541
542 log_debug("timestamp of '%s' changed", *i);
543
544 /* update timestamp */
545 if (update) {
546 *timestamp = u;
547 changed = true;
548 } else
549 return true;
550 }
551
552 return changed;
553 }
554
555 static int binary_is_good(const char *binary) {
556 _cleanup_free_ char *p = NULL, *d = NULL;
557 int r;
558
559 r = find_binary(binary, &p);
560 if (r == -ENOENT)
561 return 0;
562 if (r < 0)
563 return r;
564
565 /* An fsck that is linked to /bin/true is a non-existent
566 * fsck */
567
568 r = readlink_malloc(p, &d);
569 if (r == -EINVAL) /* not a symlink */
570 return 1;
571 if (r < 0)
572 return r;
573
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");
578 }
579
580 int fsck_exists(const char *fstype) {
581 const char *checker;
582
583 assert(fstype);
584
585 if (streq(fstype, "auto"))
586 return -EINVAL;
587
588 checker = strjoina("fsck.", fstype);
589 return binary_is_good(checker);
590 }
591
592 int mkfs_exists(const char *fstype) {
593 const char *mkfs;
594
595 assert(fstype);
596
597 if (streq(fstype, "auto"))
598 return -EINVAL;
599
600 mkfs = strjoina("mkfs.", fstype);
601 return binary_is_good(mkfs);
602 }
603
604 char *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 }
637
638 int 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 }
669
670 char* 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 }
690
691 bool 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
713 bool 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 }
733
734 char *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
761 bool 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
782 bool 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
791 bool 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 }