chiark / gitweb /
path-util: some updates to path_make_relative()
[elogind.git] / src / basic / path-util.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2010-2012 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27
28 /* When we include libgen.h because we need dirname() we immediately
29  * undefine basename() since libgen.h defines it as a macro to the
30  * POSIX version which is really broken. We prefer GNU basename(). */
31 #include <libgen.h>
32 #undef basename
33
34 #include "alloc-util.h"
35 #include "extract-word.h"
36 #include "fs-util.h"
37 #include "log.h"
38 #include "macro.h"
39 #include "missing.h"
40 #include "path-util.h"
41 #include "stat-util.h"
42 #include "string-util.h"
43 #include "strv.h"
44 #include "time-util.h"
45
46 bool path_is_absolute(const char *p) {
47         return p[0] == '/';
48 }
49
50 bool is_path(const char *p) {
51         return !!strchr(p, '/');
52 }
53
54 #if 0 /// UNNEEDED by elogind
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);
86 }
87 #endif // 0
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 negative_errno();
106
107                 c = strjoin(cwd, "/", p);
108         }
109         if (!c)
110                 return -ENOMEM;
111
112         *ret = c;
113         return 0;
114 }
115
116 #if 0 /// UNNEEDED by elogind
117 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
118         char *r, *p;
119         unsigned n_parents;
120
121         assert(from_dir);
122         assert(to_path);
123         assert(_r);
124
125         /* Strips the common part, and adds ".." elements as necessary. */
126
127         if (!path_is_absolute(from_dir))
128                 return -EINVAL;
129
130         if (!path_is_absolute(to_path))
131                 return -EINVAL;
132
133         /* Skip the common part. */
134         for (;;) {
135                 size_t a, 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                         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                 size_t w;
178
179                 from_dir += strspn(from_dir, "/");
180
181                 if (!*from_dir)
182                         break;
183
184                 w = strcspn(from_dir, "/");
185
186                 /* If this includes ".." we can't do a simple series of "..", refuse */
187                 if (w == 2 && from_dir[0] == '.' && from_dir[1] == '.')
188                         return -EINVAL;
189
190                 /* Count number of elements, except if they are "." */
191                 if (w != 1 || from_dir[0] != '.')
192                         n_parents++;
193
194                 from_dir += w;
195         }
196
197         r = new(char, n_parents * 3 + strlen(to_path) + 1);
198         if (!r)
199                 return -ENOMEM;
200
201         for (p = r; n_parents > 0; n_parents--)
202                 p = mempcpy(p, "../", 3);
203
204         strcpy(p, to_path);
205         path_kill_slashes(r);
206
207         *_r = r;
208         return 0;
209 }
210
211 int path_strv_make_absolute_cwd(char **l) {
212         char **s;
213         int r;
214
215         /* Goes through every item in the string list and makes it
216          * absolute. This works in place and won't rollback any
217          * changes on failure. */
218
219         STRV_FOREACH(s, l) {
220                 char *t;
221
222                 r = path_make_absolute_cwd(*s, &t);
223                 if (r < 0)
224                         return r;
225
226                 free(*s);
227                 *s = t;
228         }
229
230         return 0;
231 }
232 #endif // 0
233
234 char **path_strv_resolve(char **l, const char *root) {
235         char **s;
236         unsigned k = 0;
237         bool enomem = false;
238         int r;
239
240         if (strv_isempty(l))
241                 return l;
242
243         /* Goes through every item in the string list and canonicalize
244          * the path. This works in place and won't rollback any
245          * changes on failure. */
246
247         STRV_FOREACH(s, l) {
248                 _cleanup_free_ char *orig = NULL;
249                 char *t, *u;
250
251                 if (!path_is_absolute(*s)) {
252                         free(*s);
253                         continue;
254                 }
255
256                 if (root) {
257                         orig = *s;
258                         t = prefix_root(root, orig);
259                         if (!t) {
260                                 enomem = true;
261                                 continue;
262                         }
263                 } else
264                         t = *s;
265
266                 r = chase_symlinks(t, root, 0, &u);
267                 if (r == -ENOENT) {
268                         if (root) {
269                                 u = orig;
270                                 orig = NULL;
271                                 free(t);
272                         } else
273                                 u = t;
274                 } else if (r < 0) {
275                         free(t);
276
277                         if (r == -ENOMEM)
278                                 enomem = true;
279
280                         continue;
281                 } else if (root) {
282                         char *x;
283
284                         free(t);
285                         x = path_startswith(u, root);
286                         if (x) {
287                                 /* restore the slash if it was lost */
288                                 if (!startswith(x, "/"))
289                                         *(--x) = '/';
290
291                                 t = strdup(x);
292                                 free(u);
293                                 if (!t) {
294                                         enomem = true;
295                                         continue;
296                                 }
297                                 u = t;
298                         } else {
299                                 /* canonicalized path goes outside of
300                                  * prefix, keep the original path instead */
301                                 free_and_replace(u, orig);
302                         }
303                 } else
304                         free(t);
305
306                 l[k++] = u;
307         }
308
309         l[k] = NULL;
310
311         if (enomem)
312                 return NULL;
313
314         return l;
315 }
316
317 char **path_strv_resolve_uniq(char **l, const char *root) {
318
319         if (strv_isempty(l))
320                 return l;
321
322         if (!path_strv_resolve(l, root))
323                 return NULL;
324
325         return strv_uniq(l);
326 }
327
328 char *path_kill_slashes(char *path) {
329         char *f, *t;
330         bool slash = false;
331
332         /* Removes redundant inner and trailing slashes. Modifies the
333          * passed string in-place.
334          *
335          * ///foo///bar/ becomes /foo/bar
336          */
337
338         for (f = path, t = path; *f; f++) {
339
340                 if (*f == '/') {
341                         slash = true;
342                         continue;
343                 }
344
345                 if (slash) {
346                         slash = false;
347                         *(t++) = '/';
348                 }
349
350                 *(t++) = *f;
351         }
352
353         /* Special rule, if we are talking of the root directory, a
354         trailing slash is good */
355
356         if (t == path && slash)
357                 *(t++) = '/';
358
359         *t = 0;
360         return path;
361 }
362
363 char* path_startswith(const char *path, const char *prefix) {
364         assert(path);
365         assert(prefix);
366
367         /* Returns a pointer to the start of the first component after the parts matched by
368          * the prefix, iff
369          * - both paths are absolute or both paths are relative,
370          * and
371          * - each component in prefix in turn matches a component in path at the same position.
372          * An empty string will be returned when the prefix and path are equivalent.
373          *
374          * Returns NULL otherwise.
375          */
376
377         if ((path[0] == '/') != (prefix[0] == '/'))
378                 return NULL;
379
380         for (;;) {
381                 size_t a, b;
382
383                 path += strspn(path, "/");
384                 prefix += strspn(prefix, "/");
385
386                 if (*prefix == 0)
387                         return (char*) path;
388
389                 if (*path == 0)
390                         return NULL;
391
392                 a = strcspn(path, "/");
393                 b = strcspn(prefix, "/");
394
395                 if (a != b)
396                         return NULL;
397
398                 if (memcmp(path, prefix, a) != 0)
399                         return NULL;
400
401                 path += a;
402                 prefix += b;
403         }
404 }
405
406 int path_compare(const char *a, const char *b) {
407         int d;
408
409         assert(a);
410         assert(b);
411
412         /* A relative path and an abolute path must not compare as equal.
413          * Which one is sorted before the other does not really matter.
414          * Here a relative path is ordered before an absolute path. */
415         d = (a[0] == '/') - (b[0] == '/');
416         if (d != 0)
417                 return d;
418
419         for (;;) {
420                 size_t j, k;
421
422                 a += strspn(a, "/");
423                 b += strspn(b, "/");
424
425                 if (*a == 0 && *b == 0)
426                         return 0;
427
428                 /* Order prefixes first: "/foo" before "/foo/bar" */
429                 if (*a == 0)
430                         return -1;
431                 if (*b == 0)
432                         return 1;
433
434                 j = strcspn(a, "/");
435                 k = strcspn(b, "/");
436
437                 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
438                 d = memcmp(a, b, MIN(j, k));
439                 if (d != 0)
440                         return (d > 0) - (d < 0); /* sign of d */
441
442                 /* Sort "/foo/a" before "/foo/aaa" */
443                 d = (j > k) - (j < k);  /* sign of (j - k) */
444                 if (d != 0)
445                         return d;
446
447                 a += j;
448                 b += k;
449         }
450 }
451
452 bool path_equal(const char *a, const char *b) {
453         return path_compare(a, b) == 0;
454 }
455
456 bool path_equal_or_files_same(const char *a, const char *b, int flags) {
457         return path_equal(a, b) || files_same(a, b, flags) > 0;
458 }
459
460 char* path_join(const char *root, const char *path, const char *rest) {
461         assert(path);
462
463         if (!isempty(root))
464                 return strjoin(root, endswith(root, "/") ? "" : "/",
465                                path[0] == '/' ? path+1 : path,
466                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
467                                rest && rest[0] == '/' ? rest+1 : rest);
468         else
469                 return strjoin(path,
470                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
471                                rest && rest[0] == '/' ? rest+1 : rest);
472 }
473
474 int find_binary(const char *name, char **ret) {
475         int last_error, r;
476         const char *p;
477
478         assert(name);
479
480         if (is_path(name)) {
481                 if (access(name, X_OK) < 0)
482                         return -errno;
483
484                 if (ret) {
485                         r = path_make_absolute_cwd(name, ret);
486                         if (r < 0)
487                                 return r;
488                 }
489
490                 return 0;
491         }
492
493         /**
494          * Plain getenv, not secure_getenv, because we want
495          * to actually allow the user to pick the binary.
496          */
497         p = getenv("PATH");
498         if (!p)
499                 p = DEFAULT_PATH;
500
501         last_error = -ENOENT;
502
503         for (;;) {
504                 _cleanup_free_ char *j = NULL, *element = NULL;
505
506                 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
507                 if (r < 0)
508                         return r;
509                 if (r == 0)
510                         break;
511
512                 if (!path_is_absolute(element))
513                         continue;
514
515                 j = strjoin(element, "/", name);
516                 if (!j)
517                         return -ENOMEM;
518
519                 if (access(j, X_OK) >= 0) {
520                         /* Found it! */
521
522                         if (ret) {
523                                 *ret = path_kill_slashes(j);
524                                 j = NULL;
525                         }
526
527                         return 0;
528                 }
529
530                 last_error = -errno;
531         }
532
533         return last_error;
534 }
535
536 #if 0 /// UNNEEDED by elogind
537 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
538         bool changed = false;
539         const char* const* i;
540
541         assert(timestamp);
542
543         if (paths == NULL)
544                 return false;
545
546         STRV_FOREACH(i, paths) {
547                 struct stat stats;
548                 usec_t u;
549
550                 if (stat(*i, &stats) < 0)
551                         continue;
552
553                 u = timespec_load(&stats.st_mtim);
554
555                 /* first check */
556                 if (*timestamp >= u)
557                         continue;
558
559                 log_debug("timestamp of '%s' changed", *i);
560
561                 /* update timestamp */
562                 if (update) {
563                         *timestamp = u;
564                         changed = true;
565                 } else
566                         return true;
567         }
568
569         return changed;
570 }
571
572 static int binary_is_good(const char *binary) {
573         _cleanup_free_ char *p = NULL, *d = NULL;
574         int r;
575
576         r = find_binary(binary, &p);
577         if (r == -ENOENT)
578                 return 0;
579         if (r < 0)
580                 return r;
581
582         /* An fsck that is linked to /bin/true is a non-existent
583          * fsck */
584
585         r = readlink_malloc(p, &d);
586         if (r == -EINVAL) /* not a symlink */
587                 return 1;
588         if (r < 0)
589                 return r;
590
591         return !PATH_IN_SET(d, "true"
592                                "/bin/true",
593                                "/usr/bin/true",
594                                "/dev/null");
595 }
596
597 int fsck_exists(const char *fstype) {
598         const char *checker;
599
600         assert(fstype);
601
602         if (streq(fstype, "auto"))
603                 return -EINVAL;
604
605         checker = strjoina("fsck.", fstype);
606         return binary_is_good(checker);
607 }
608
609 int mkfs_exists(const char *fstype) {
610         const char *mkfs;
611
612         assert(fstype);
613
614         if (streq(fstype, "auto"))
615                 return -EINVAL;
616
617         mkfs = strjoina("mkfs.", fstype);
618         return binary_is_good(mkfs);
619 }
620 #endif // 0
621
622 char *prefix_root(const char *root, const char *path) {
623         char *n, *p;
624         size_t l;
625
626         /* If root is passed, prefixes path with it. Otherwise returns
627          * it as is. */
628
629         assert(path);
630
631         /* First, drop duplicate prefixing slashes from the path */
632         while (path[0] == '/' && path[1] == '/')
633                 path++;
634
635         if (isempty(root) || path_equal(root, "/"))
636                 return strdup(path);
637
638         l = strlen(root) + 1 + strlen(path) + 1;
639
640         n = new(char, l);
641         if (!n)
642                 return NULL;
643
644         p = stpcpy(n, root);
645
646         while (p > n && p[-1] == '/')
647                 p--;
648
649         if (path[0] != '/')
650                 *(p++) = '/';
651
652         strcpy(p, path);
653         return n;
654 }
655
656 #if 0 /// UNNEEDED by elogind
657 int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
658         char *p;
659         int r;
660
661         /*
662          * This function is intended to be used in command line
663          * parsers, to handle paths that are passed in. It makes the
664          * path absolute, and reduces it to NULL if omitted or
665          * root (the latter optionally).
666          *
667          * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
668          * SUCCESS! Hence, do not pass in uninitialized pointers.
669          */
670
671         if (isempty(path)) {
672                 *arg = mfree(*arg);
673                 return 0;
674         }
675
676         r = path_make_absolute_cwd(path, &p);
677         if (r < 0)
678                 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
679
680         path_kill_slashes(p);
681         if (suppress_root && path_equal(p, "/"))
682                 p = mfree(p);
683
684         free(*arg);
685         *arg = p;
686         return 0;
687 }
688 #endif // 0
689
690 char* dirname_malloc(const char *path) {
691         char *d, *dir, *dir2;
692
693         assert(path);
694
695         d = strdup(path);
696         if (!d)
697                 return NULL;
698
699         dir = dirname(d);
700         assert(dir);
701
702         if (dir == d)
703                 return d;
704
705         dir2 = strdup(dir);
706         free(d);
707
708         return dir2;
709 }
710
711 bool filename_is_valid(const char *p) {
712         const char *e;
713
714         if (isempty(p))
715                 return false;
716
717         if (dot_or_dot_dot(p))
718                 return false;
719
720         e = strchrnul(p, '/');
721         if (*e != 0)
722                 return false;
723
724         if (e - p > FILENAME_MAX)
725                 return false;
726
727         return true;
728 }
729
730 bool path_is_safe(const char *p) {
731
732         if (isempty(p))
733                 return false;
734
735         if (dot_or_dot_dot(p))
736                 return false;
737
738         if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
739                 return false;
740
741         if (strlen(p)+1 > PATH_MAX)
742                 return false;
743
744         /* The following two checks are not really dangerous, but hey, they still are confusing */
745         if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
746                 return false;
747
748         if (strstr(p, "//"))
749                 return false;
750
751         return true;
752 }
753
754 char *file_in_same_dir(const char *path, const char *filename) {
755         char *e, *ret;
756         size_t k;
757
758         assert(path);
759         assert(filename);
760
761         /* This removes the last component of path and appends
762          * filename, unless the latter is absolute anyway or the
763          * former isn't */
764
765         if (path_is_absolute(filename))
766                 return strdup(filename);
767
768         e = strrchr(path, '/');
769         if (!e)
770                 return strdup(filename);
771
772         k = strlen(filename);
773         ret = new(char, (e + 1 - path) + k + 1);
774         if (!ret)
775                 return NULL;
776
777         memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
778         return ret;
779 }
780
781 bool hidden_or_backup_file(const char *filename) {
782         const char *p;
783
784         assert(filename);
785
786         if (filename[0] == '.' ||
787             streq(filename, "lost+found") ||
788             streq(filename, "aquota.user") ||
789             streq(filename, "aquota.group") ||
790             endswith(filename, "~"))
791                 return true;
792
793         p = strrchr(filename, '.');
794         if (!p)
795                 return false;
796
797         /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
798          * with always new suffixes and that everybody else should just adjust to that, then it really should be on
799          * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
800          * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
801          * string. Specifically: there's now:
802          *
803          *    The generic suffixes "~" and ".bak" for backup files
804          *    The generic prefix "." for hidden files
805          *
806          * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
807          * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
808          */
809
810         return STR_IN_SET(p + 1,
811                           "rpmnew",
812                           "rpmsave",
813                           "rpmorig",
814                           "dpkg-old",
815                           "dpkg-new",
816                           "dpkg-tmp",
817                           "dpkg-dist",
818                           "dpkg-bak",
819                           "dpkg-backup",
820                           "dpkg-remove",
821                           "ucf-new",
822                           "ucf-old",
823                           "ucf-dist",
824                           "swp",
825                           "bak",
826                           "old",
827                           "new");
828 }
829
830 #if 0 /// UNNEEDED by elogind
831 bool is_device_path(const char *path) {
832
833         /* Returns true on paths that refer to a device, either in
834          * sysfs or in /dev */
835
836         return path_startswith(path, "/dev/") ||
837                path_startswith(path, "/sys/");
838 }
839
840 bool is_deviceallow_pattern(const char *path) {
841         return path_startswith(path, "/dev/") ||
842                startswith(path, "block-") ||
843                startswith(path, "char-");
844 }
845
846 int systemd_installation_has_version(const char *root, unsigned minimal_version) {
847         const char *pattern;
848         int r;
849
850         /* Try to guess if systemd installation is later than the specified version. This
851          * is hacky and likely to yield false negatives, particularly if the installation
852          * is non-standard. False positives should be relatively rare.
853          */
854
855         NULSTR_FOREACH(pattern,
856                        /* /lib works for systems without usr-merge, and for systems with a sane
857                         * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
858                         * for Gentoo which does a merge without making /lib a symlink.
859                         */
860                        "lib/systemd/libsystemd-shared-*.so\0"
861                        "usr/lib/systemd/libsystemd-shared-*.so\0") {
862
863                 _cleanup_strv_free_ char **names = NULL;
864                 _cleanup_free_ char *path = NULL;
865                 char *c, **name;
866
867                 path = prefix_root(root, pattern);
868                 if (!path)
869                         return -ENOMEM;
870
871                 r = glob_extend(&names, path);
872                 if (r == -ENOENT)
873                         continue;
874                 if (r < 0)
875                         return r;
876
877                 assert_se((c = endswith(path, "*.so")));
878                 *c = '\0'; /* truncate the glob part */
879
880                 STRV_FOREACH(name, names) {
881                         /* This is most likely to run only once, hence let's not optimize anything. */
882                         char *t, *t2;
883                         unsigned version;
884
885                         t = startswith(*name, path);
886                         if (!t)
887                                 continue;
888
889                         t2 = endswith(t, ".so");
890                         if (!t2)
891                                 continue;
892
893                         t2[0] = '\0'; /* truncate the suffix */
894
895                         r = safe_atou(t, &version);
896                         if (r < 0) {
897                                 log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
898                                 continue;
899                         }
900
901                         log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
902                                   *name, version,
903                                   version >= minimal_version ? "OK" : "too old");
904                         if (version >= minimal_version)
905                                 return true;
906                 }
907         }
908
909         return false;
910 }
911 #endif // 0
912
913 bool dot_or_dot_dot(const char *path) {
914         if (!path)
915                 return false;
916         if (path[0] != '.')
917                 return false;
918         if (path[1] == 0)
919                 return true;
920         if (path[1] != '.')
921                 return false;
922
923         return path[2] == 0;
924 }