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