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