chiark / gitweb /
Merge pull request #7 from elogind/dev_v228-r1
[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
465 int find_binary(const char *name, char **ret) {
466         int last_error, r;
467         const char *p;
468
469         assert(name);
470
471         if (is_path(name)) {
472                 if (access(name, X_OK) < 0)
473                         return -errno;
474
475                 if (ret) {
476                         r = path_make_absolute_cwd(name, ret);
477                         if (r < 0)
478                                 return r;
479                 }
480
481                 return 0;
482         }
483
484                 /**
485                  * Plain getenv, not secure_getenv, because we want
486                  * to actually allow the user to pick the binary.
487                  */
488         p = getenv("PATH");
489         if (!p)
490                 p = DEFAULT_PATH;
491
492         last_error = -ENOENT;
493
494         for (;;) {
495                 _cleanup_free_ char *j = NULL, *element = NULL;
496
497                 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
498                 if (r < 0)
499                         return r;
500                 if (r == 0)
501                         break;
502
503                 if (!path_is_absolute(element))
504                                 continue;
505
506                 j = strjoin(element, "/", name, NULL);
507                 if (!j)
508                         return -ENOMEM;
509
510                 if (access(j, X_OK) >= 0) {
511                         /* Found it! */
512
513                         if (ret) {
514                                 *ret = path_kill_slashes(j);
515                                 j = NULL;
516                         }
517
518                         return 0;
519                 }
520
521                 last_error = -errno;
522         }
523
524         return last_error;
525 }
526
527 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
528         bool changed = false;
529         const char* const* i;
530
531         assert(timestamp);
532
533         if (paths == NULL)
534                 return false;
535
536         STRV_FOREACH(i, paths) {
537                 struct stat stats;
538                 usec_t u;
539
540                 if (stat(*i, &stats) < 0)
541                         continue;
542
543                 u = timespec_load(&stats.st_mtim);
544
545                 /* first check */
546                 if (*timestamp >= u)
547                         continue;
548
549                 log_debug("timestamp of '%s' changed", *i);
550
551                 /* update timestamp */
552                 if (update) {
553                         *timestamp = u;
554                         changed = true;
555                 } else
556                         return true;
557         }
558
559         return changed;
560 }
561
562 static int binary_is_good(const char *binary) {
563         _cleanup_free_ char *p = NULL, *d = NULL;
564         int r;
565
566         r = find_binary(binary, &p);
567         if (r == -ENOENT)
568                 return 0;
569         if (r < 0)
570                 return r;
571
572         /* An fsck that is linked to /bin/true is a non-existent
573          * fsck */
574
575         r = readlink_malloc(p, &d);
576         if (r == -EINVAL) /* not a symlink */
577                 return 1;
578         if (r < 0)
579                 return r;
580
581         return !path_equal(d, "true") &&
582                !path_equal(d, "/bin/true") &&
583                !path_equal(d, "/usr/bin/true") &&
584                !path_equal(d, "/dev/null");
585 }
586
587 int fsck_exists(const char *fstype) {
588         const char *checker;
589
590         assert(fstype);
591
592         if (streq(fstype, "auto"))
593                 return -EINVAL;
594
595         checker = strjoina("fsck.", fstype);
596         return binary_is_good(checker);
597 }
598
599 int mkfs_exists(const char *fstype) {
600         const char *mkfs;
601
602         assert(fstype);
603
604         if (streq(fstype, "auto"))
605                 return -EINVAL;
606
607         mkfs = strjoina("mkfs.", fstype);
608         return binary_is_good(mkfs);
609 }
610
611 char *prefix_root(const char *root, const char *path) {
612         char *n, *p;
613         size_t l;
614
615         /* If root is passed, prefixes path with it. Otherwise returns
616          * it as is. */
617
618         assert(path);
619
620         /* First, drop duplicate prefixing slashes from the path */
621         while (path[0] == '/' && path[1] == '/')
622                 path++;
623
624         if (isempty(root) || path_equal(root, "/"))
625                 return strdup(path);
626
627         l = strlen(root) + 1 + strlen(path) + 1;
628
629         n = new(char, l);
630         if (!n)
631                 return NULL;
632
633         p = stpcpy(n, root);
634
635         while (p > n && p[-1] == '/')
636                 p--;
637
638         if (path[0] != '/')
639                 *(p++) = '/';
640
641         strcpy(p, path);
642         return n;
643 }
644
645 int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
646         char *p;
647         int r;
648
649         /*
650          * This function is intended to be used in command line
651          * parsers, to handle paths that are passed in. It makes the
652          * path absolute, and reduces it to NULL if omitted or
653          * root (the latter optionally).
654          *
655          * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
656          * SUCCESS! Hence, do not pass in uninitialized pointers.
657          */
658
659         if (isempty(path)) {
660                 *arg = mfree(*arg);
661                 return 0;
662         }
663
664         r = path_make_absolute_cwd(path, &p);
665         if (r < 0)
666                 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
667
668         path_kill_slashes(p);
669         if (suppress_root && path_equal(p, "/"))
670                 p = mfree(p);
671
672         free(*arg);
673         *arg = p;
674         return 0;
675 }
676 #endif // 0
677
678 char* dirname_malloc(const char *path) {
679         char *d, *dir, *dir2;
680
681         assert(path);
682
683         d = strdup(path);
684         if (!d)
685                 return NULL;
686
687         dir = dirname(d);
688         assert(dir);
689
690         if (dir == d)
691                 return d;
692
693         dir2 = strdup(dir);
694         free(d);
695
696         return dir2;
697 }
698
699 bool filename_is_valid(const char *p) {
700         const char *e;
701
702         if (isempty(p))
703                 return false;
704
705         if (streq(p, "."))
706                 return false;
707
708         if (streq(p, ".."))
709                 return false;
710
711         e = strchrnul(p, '/');
712         if (*e != 0)
713                 return false;
714
715         if (e - p > FILENAME_MAX)
716                 return false;
717
718         return true;
719 }
720
721 bool path_is_safe(const char *p) {
722
723         if (isempty(p))
724                 return false;
725
726         if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
727                 return false;
728
729         if (strlen(p)+1 > PATH_MAX)
730                 return false;
731
732         /* The following two checks are not really dangerous, but hey, they still are confusing */
733         if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
734                 return false;
735
736         if (strstr(p, "//"))
737                 return false;
738
739         return true;
740 }
741
742 char *file_in_same_dir(const char *path, const char *filename) {
743         char *e, *ret;
744         size_t k;
745
746         assert(path);
747         assert(filename);
748
749         /* This removes the last component of path and appends
750          * filename, unless the latter is absolute anyway or the
751          * former isn't */
752
753         if (path_is_absolute(filename))
754                 return strdup(filename);
755
756         e = strrchr(path, '/');
757         if (!e)
758                 return strdup(filename);
759
760         k = strlen(filename);
761         ret = new(char, (e + 1 - path) + k + 1);
762         if (!ret)
763                 return NULL;
764
765         memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
766         return ret;
767 }
768
769 bool hidden_file_allow_backup(const char *filename) {
770         assert(filename);
771
772         return
773                 filename[0] == '.' ||
774                 streq(filename, "lost+found") ||
775                 streq(filename, "aquota.user") ||
776                 streq(filename, "aquota.group") ||
777                 endswith(filename, ".rpmnew") ||
778                 endswith(filename, ".rpmsave") ||
779                 endswith(filename, ".rpmorig") ||
780                 endswith(filename, ".dpkg-old") ||
781                 endswith(filename, ".dpkg-new") ||
782                 endswith(filename, ".dpkg-tmp") ||
783                 endswith(filename, ".dpkg-dist") ||
784                 endswith(filename, ".dpkg-bak") ||
785                 endswith(filename, ".dpkg-backup") ||
786                 endswith(filename, ".dpkg-remove") ||
787                 endswith(filename, ".swp");
788 }
789
790 bool hidden_file(const char *filename) {
791         assert(filename);
792
793         if (endswith(filename, "~"))
794                 return true;
795
796         return hidden_file_allow_backup(filename);
797 }
798
799 #if 0 /// UNNEEDED by elogind
800 bool is_device_path(const char *path) {
801
802         /* Returns true on paths that refer to a device, either in
803          * sysfs or in /dev */
804
805         return
806                 path_startswith(path, "/dev/") ||
807                 path_startswith(path, "/sys/");
808 }
809 #endif // 0