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