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