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