chiark / gitweb /
login: use normal comparison to zero for integers
[elogind.git] / src / shared / 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 <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <sys/statvfs.h>
29
30 #include "macro.h"
31 #include "util.h"
32 #include "log.h"
33 #include "strv.h"
34 #include "path-util.h"
35 #include "missing.h"
36 #include "fileio.h"
37
38 bool path_is_absolute(const char *p) {
39         return p[0] == '/';
40 }
41
42 bool is_path(const char *p) {
43         return !!strchr(p, '/');
44 }
45
46 int path_get_parent(const char *path, char **_r) {
47         const char *e, *a = NULL, *b = NULL, *p;
48         char *r;
49         bool slash = false;
50
51         assert(path);
52         assert(_r);
53
54         if (!*path)
55                 return -EINVAL;
56
57         for (e = path; *e; e++) {
58
59                 if (!slash && *e == '/') {
60                         a = b;
61                         b = e;
62                         slash = true;
63                 } else if (slash && *e != '/')
64                         slash = false;
65         }
66
67         if (*(e-1) == '/')
68                 p = a;
69         else
70                 p = b;
71
72         if (!p)
73                 return -EINVAL;
74
75         if (p == path)
76                 r = strdup("/");
77         else
78                 r = strndup(path, p-path);
79
80         if (!r)
81                 return -ENOMEM;
82
83         *_r = r;
84         return 0;
85 }
86
87 char **path_split_and_make_absolute(const char *p) {
88         char **l;
89         assert(p);
90
91         l = strv_split(p, ":");
92         if (!l)
93                 return NULL;
94
95         if (!path_strv_make_absolute_cwd(l)) {
96                 strv_free(l);
97                 return NULL;
98         }
99
100         return l;
101 }
102
103 char *path_make_absolute(const char *p, const char *prefix) {
104         assert(p);
105
106         /* Makes every item in the list an absolute path by prepending
107          * the prefix, if specified and necessary */
108
109         if (path_is_absolute(p) || !prefix)
110                 return strdup(p);
111
112         return strjoin(prefix, "/", p, NULL);
113 }
114
115 char *path_make_absolute_cwd(const char *p) {
116         _cleanup_free_ char *cwd = NULL;
117
118         assert(p);
119
120         /* Similar to path_make_absolute(), but prefixes with the
121          * current working directory. */
122
123         if (path_is_absolute(p))
124                 return strdup(p);
125
126         cwd = get_current_dir_name();
127         if (!cwd)
128                 return NULL;
129
130         return strjoin(cwd, "/", p, NULL);
131 }
132
133 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
134         char *r, *p;
135         unsigned n_parents;
136
137         assert(from_dir);
138         assert(to_path);
139         assert(_r);
140
141         /* Strips the common part, and adds ".." elements as necessary. */
142
143         if (!path_is_absolute(from_dir))
144                 return -EINVAL;
145
146         if (!path_is_absolute(to_path))
147                 return -EINVAL;
148
149         /* Skip the common part. */
150         for (;;) {
151                 size_t a;
152                 size_t b;
153
154                 from_dir += strspn(from_dir, "/");
155                 to_path += strspn(to_path, "/");
156
157                 if (!*from_dir) {
158                         if (!*to_path)
159                                 /* from_dir equals to_path. */
160                                 r = strdup(".");
161                         else
162                                 /* from_dir is a parent directory of to_path. */
163                                 r = strdup(to_path);
164
165                         if (!r)
166                                 return -ENOMEM;
167
168                         path_kill_slashes(r);
169
170                         *_r = r;
171                         return 0;
172                 }
173
174                 if (!*to_path)
175                         break;
176
177                 a = strcspn(from_dir, "/");
178                 b = strcspn(to_path, "/");
179
180                 if (a != b)
181                         break;
182
183                 if (memcmp(from_dir, to_path, a) != 0)
184                         break;
185
186                 from_dir += a;
187                 to_path += b;
188         }
189
190         /* If we're here, then "from_dir" has one or more elements that need to
191          * be replaced with "..". */
192
193         /* Count the number of necessary ".." elements. */
194         for (n_parents = 0;;) {
195                 from_dir += strspn(from_dir, "/");
196
197                 if (!*from_dir)
198                         break;
199
200                 from_dir += strcspn(from_dir, "/");
201                 n_parents++;
202         }
203
204         r = malloc(n_parents * 3 + strlen(to_path) + 1);
205         if (!r)
206                 return -ENOMEM;
207
208         for (p = r; n_parents > 0; n_parents--, p += 3)
209                 memcpy(p, "../", 3);
210
211         strcpy(p, to_path);
212         path_kill_slashes(r);
213
214         *_r = r;
215         return 0;
216 }
217
218 char **path_strv_make_absolute_cwd(char **l) {
219         char **s;
220
221         /* Goes through every item in the string list and makes it
222          * absolute. This works in place and won't rollback any
223          * changes on failure. */
224
225         STRV_FOREACH(s, l) {
226                 char *t;
227
228                 t = path_make_absolute_cwd(*s);
229                 if (!t)
230                         return NULL;
231
232                 free(*s);
233                 *s = t;
234         }
235
236         return l;
237 }
238
239 char **path_strv_resolve(char **l, const char *prefix) {
240         char **s;
241         unsigned k = 0;
242         bool enomem = false;
243
244         if (strv_isempty(l))
245                 return l;
246
247         /* Goes through every item in the string list and canonicalize
248          * the path. This works in place and won't rollback any
249          * changes on failure. */
250
251         STRV_FOREACH(s, l) {
252                 char *t, *u;
253                 _cleanup_free_ char *orig = NULL;
254
255                 if (!path_is_absolute(*s)) {
256                         free(*s);
257                         continue;
258                 }
259
260                 if (prefix) {
261                         orig = *s;
262                         t = strappend(prefix, orig);
263                         if (!t) {
264                                 enomem = true;
265                                 continue;
266                         }
267                 } else
268                         t = *s;
269
270                 errno = 0;
271                 u = canonicalize_file_name(t);
272                 if (!u) {
273                         if (errno == ENOENT) {
274                                 if (prefix) {
275                                         u = orig;
276                                         orig = NULL;
277                                         free(t);
278                                 } else
279                                         u = t;
280                         } else {
281                                 free(t);
282                                 if (errno == ENOMEM || errno == 0)
283                                         enomem = true;
284
285                                 continue;
286                         }
287                 } else if (prefix) {
288                         char *x;
289
290                         free(t);
291                         x = path_startswith(u, prefix);
292                         if (x) {
293                                 /* restore the slash if it was lost */
294                                 if (!startswith(x, "/"))
295                                         *(--x) = '/';
296
297                                 t = strdup(x);
298                                 free(u);
299                                 if (!t) {
300                                         enomem = true;
301                                         continue;
302                                 }
303                                 u = t;
304                         } else {
305                                 /* canonicalized path goes outside of
306                                  * prefix, keep the original path instead */
307                                 free(u);
308                                 u = orig;
309                                 orig = NULL;
310                         }
311                 } else
312                         free(t);
313
314                 l[k++] = u;
315         }
316
317         l[k] = NULL;
318
319         if (enomem)
320                 return NULL;
321
322         return l;
323 }
324
325 char **path_strv_resolve_uniq(char **l, const char *prefix) {
326
327         if (strv_isempty(l))
328                 return l;
329
330         if (!path_strv_resolve(l, prefix))
331                 return NULL;
332
333         return strv_uniq(l);
334 }
335
336 char *path_kill_slashes(char *path) {
337         char *f, *t;
338         bool slash = false;
339
340         /* Removes redundant inner and trailing slashes. Modifies the
341          * passed string in-place.
342          *
343          * ///foo///bar/ becomes /foo/bar
344          */
345
346         for (f = path, t = path; *f; f++) {
347
348                 if (*f == '/') {
349                         slash = true;
350                         continue;
351                 }
352
353                 if (slash) {
354                         slash = false;
355                         *(t++) = '/';
356                 }
357
358                 *(t++) = *f;
359         }
360
361         /* Special rule, if we are talking of the root directory, a
362         trailing slash is good */
363
364         if (t == path && slash)
365                 *(t++) = '/';
366
367         *t = 0;
368         return path;
369 }
370
371 char* path_startswith(const char *path, const char *prefix) {
372         assert(path);
373         assert(prefix);
374
375         if ((path[0] == '/') != (prefix[0] == '/'))
376                 return NULL;
377
378         for (;;) {
379                 size_t a, b;
380
381                 path += strspn(path, "/");
382                 prefix += strspn(prefix, "/");
383
384                 if (*prefix == 0)
385                         return (char*) path;
386
387                 if (*path == 0)
388                         return NULL;
389
390                 a = strcspn(path, "/");
391                 b = strcspn(prefix, "/");
392
393                 if (a != b)
394                         return NULL;
395
396                 if (memcmp(path, prefix, a) != 0)
397                         return NULL;
398
399                 path += a;
400                 prefix += b;
401         }
402 }
403
404 int path_compare(const char *a, const char *b) {
405         int d;
406
407         assert(a);
408         assert(b);
409
410         /* A relative path and an abolute path must not compare as equal.
411          * Which one is sorted before the other does not really matter.
412          * Here a relative path is ordered before an absolute path. */
413         d = (a[0] == '/') - (b[0] == '/');
414         if (d)
415                 return d;
416
417         for (;;) {
418                 size_t j, k;
419
420                 a += strspn(a, "/");
421                 b += strspn(b, "/");
422
423                 if (*a == 0 && *b == 0)
424                         return 0;
425
426                 /* Order prefixes first: "/foo" before "/foo/bar" */
427                 if (*a == 0)
428                         return -1;
429                 if (*b == 0)
430                         return 1;
431
432                 j = strcspn(a, "/");
433                 k = strcspn(b, "/");
434
435                 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
436                 d = memcmp(a, b, MIN(j, k));
437                 if (d)
438                         return (d > 0) - (d < 0); /* sign of d */
439
440                 /* Sort "/foo/a" before "/foo/aaa" */
441                 d = (j > k) - (j < k);  /* sign of (j - k) */
442                 if (d)
443                         return d;
444
445                 a += j;
446                 b += k;
447         }
448 }
449
450 bool path_equal(const char *a, const char *b) {
451         return path_compare(a, b) == 0;
452 }
453
454 bool path_equal_or_files_same(const char *a, const char *b) {
455         return path_equal(a, b) || files_same(a, b) > 0;
456 }
457
458 char* path_join(const char *root, const char *path, const char *rest) {
459         assert(path);
460
461         if (!isempty(root))
462                 return strjoin(root, endswith(root, "/") ? "" : "/",
463                                path[0] == '/' ? path+1 : path,
464                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
465                                rest && rest[0] == '/' ? rest+1 : rest,
466                                NULL);
467         else
468                 return strjoin(path,
469                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
470                                rest && rest[0] == '/' ? rest+1 : rest,
471                                NULL);
472 }
473
474 static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
475         char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
476         _cleanup_free_ char *fdinfo = NULL;
477         _cleanup_close_ int subfd = -1;
478         char *p;
479         int r;
480
481         if ((flags & AT_EMPTY_PATH) && isempty(filename))
482                 xsprintf(path, "/proc/self/fdinfo/%i", fd);
483         else {
484                 subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
485                 if (subfd < 0)
486                         return -errno;
487
488                 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
489         }
490
491         r = read_full_file(path, &fdinfo, NULL);
492         if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
493                 return -EOPNOTSUPP;
494         if (r < 0)
495                 return -errno;
496
497         p = startswith(fdinfo, "mnt_id:");
498         if (!p) {
499                 p = strstr(fdinfo, "\nmnt_id:");
500                 if (!p) /* The mnt_id field is a relatively new addition */
501                         return -EOPNOTSUPP;
502
503                 p += 8;
504         }
505
506         p += strspn(p, WHITESPACE);
507         p[strcspn(p, WHITESPACE)] = 0;
508
509         return safe_atoi(p, mnt_id);
510 }
511
512 int fd_is_mount_point(int fd, const char *filename, int flags) {
513         union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
514         int mount_id = -1, mount_id_parent = -1;
515         bool nosupp = false, check_st_dev = true;
516         struct stat a, b;
517         int r;
518
519         assert(fd >= 0);
520         assert(filename);
521
522         /* First we will try the name_to_handle_at() syscall, which
523          * tells us the mount id and an opaque file "handle". It is
524          * not supported everywhere though (kernel compile-time
525          * option, not all file systems are hooked up). If it works
526          * the mount id is usually good enough to tell us whether
527          * something is a mount point.
528          *
529          * If that didn't work we will try to read the mount id from
530          * /proc/self/fdinfo/<fd>. This is almost as good as
531          * name_to_handle_at(), however, does not return the the
532          * opaque file handle. The opaque file handle is pretty useful
533          * to detect the root directory, which we should always
534          * consider a mount point. Hence we use this only as
535          * fallback. Exporting the mnt_id in fdinfo is a pretty recent
536          * kernel addition.
537          *
538          * As last fallback we do traditional fstat() based st_dev
539          * comparisons. This is how things were traditionally done,
540          * but unionfs breaks breaks this since it exposes file
541          * systems with a variety of st_dev reported. Also, btrfs
542          * subvolumes have different st_dev, even though they aren't
543          * real mounts of their own. */
544
545         r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
546         if (r < 0) {
547                 if (errno == ENOSYS)
548                         /* This kernel does not support name_to_handle_at()
549                          * fall back to simpler logic. */
550                         goto fallback_fdinfo;
551                 else if (errno == EOPNOTSUPP)
552                         /* This kernel or file system does not support
553                          * name_to_handle_at(), hence let's see if the
554                          * upper fs supports it (in which case it is a
555                          * mount point), otherwise fallback to the
556                          * traditional stat() logic */
557                         nosupp = true;
558                 else
559                         return -errno;
560         }
561
562         r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
563         if (r < 0) {
564                 if (errno == EOPNOTSUPP) {
565                         if (nosupp)
566                                 /* Neither parent nor child do name_to_handle_at()?
567                                    We have no choice but to fall back. */
568                                 goto fallback_fdinfo;
569                         else
570                                 /* The parent can't do name_to_handle_at() but the
571                                  * directory we are interested in can?
572                                  * If so, it must be a mount point. */
573                                 return 1;
574                 } else
575                         return -errno;
576         }
577
578         /* The parent can do name_to_handle_at() but the
579          * directory we are interested in can't? If so, it
580          * must be a mount point. */
581         if (nosupp)
582                 return 1;
583
584         /* If the file handle for the directory we are
585          * interested in and its parent are identical, we
586          * assume this is the root directory, which is a mount
587          * point. */
588
589         if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
590             h.handle.handle_type == h_parent.handle.handle_type &&
591             memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
592                 return 1;
593
594         return mount_id != mount_id_parent;
595
596 fallback_fdinfo:
597         r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
598         if (r == -EOPNOTSUPP)
599                 goto fallback_fstat;
600         if (r < 0)
601                 return r;
602
603         r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
604         if (r < 0)
605                 return r;
606
607         if (mount_id != mount_id_parent)
608                 return 1;
609
610         /* Hmm, so, the mount ids are the same. This leaves one
611          * special case though for the root file system. For that,
612          * let's see if the parent directory has the same inode as we
613          * are interested in. Hence, let's also do fstat() checks now,
614          * too, but avoid the st_dev comparisons, since they aren't
615          * that useful on unionfs mounts. */
616         check_st_dev = false;
617
618 fallback_fstat:
619         /* yay for fstatat() taking a different set of flags than the other
620          * _at() above */
621         if (flags & AT_SYMLINK_FOLLOW)
622                 flags &= ~AT_SYMLINK_FOLLOW;
623         else
624                 flags |= AT_SYMLINK_NOFOLLOW;
625         if (fstatat(fd, filename, &a, flags) < 0)
626                 return -errno;
627
628         if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
629                 return -errno;
630
631         /* A directory with same device and inode as its parent? Must
632          * be the root directory */
633         if (a.st_dev == b.st_dev &&
634             a.st_ino == b.st_ino)
635                 return 1;
636
637         return check_st_dev && (a.st_dev != b.st_dev);
638 }
639
640 /* flags can be AT_SYMLINK_FOLLOW or 0 */
641 int path_is_mount_point(const char *t, int flags) {
642         _cleanup_close_ int fd = -1;
643         _cleanup_free_ char *canonical = NULL, *parent = NULL;
644         int r;
645
646         assert(t);
647
648         if (path_equal(t, "/"))
649                 return 1;
650
651         /* we need to resolve symlinks manually, we can't just rely on
652          * fd_is_mount_point() to do that for us; if we have a structure like
653          * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
654          * look at needs to be /usr, not /. */
655         if (flags & AT_SYMLINK_FOLLOW) {
656                 canonical = canonicalize_file_name(t);
657                 if (!canonical)
658                         return -errno;
659         }
660
661         r = path_get_parent(canonical ?: t, &parent);
662         if (r < 0)
663                 return r;
664
665         fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
666         if (fd < 0)
667                 return -errno;
668
669         return fd_is_mount_point(fd, basename(canonical ?: t), flags);
670 }
671
672 int path_is_read_only_fs(const char *path) {
673         struct statvfs st;
674
675         assert(path);
676
677         if (statvfs(path, &st) < 0)
678                 return -errno;
679
680         if (st.f_flag & ST_RDONLY)
681                 return true;
682
683         /* On NFS, statvfs() might not reflect whether we can actually
684          * write to the remote share. Let's try again with
685          * access(W_OK) which is more reliable, at least sometimes. */
686         if (access(path, W_OK) < 0 && errno == EROFS)
687                 return true;
688
689         return false;
690 }
691
692 int path_is_os_tree(const char *path) {
693         char *p;
694         int r;
695
696         /* We use /usr/lib/os-release as flag file if something is an OS */
697         p = strjoina(path, "/usr/lib/os-release");
698         r = access(p, F_OK);
699
700         if (r >= 0)
701                 return 1;
702
703         /* Also check for the old location in /etc, just in case. */
704         p = strjoina(path, "/etc/os-release");
705         r = access(p, F_OK);
706
707         return r >= 0;
708 }
709
710 int find_binary(const char *name, bool local, char **filename) {
711         assert(name);
712
713         if (is_path(name)) {
714                 if (local && access(name, X_OK) < 0)
715                         return -errno;
716
717                 if (filename) {
718                         char *p;
719
720                         p = path_make_absolute_cwd(name);
721                         if (!p)
722                                 return -ENOMEM;
723
724                         *filename = p;
725                 }
726
727                 return 0;
728         } else {
729                 const char *path;
730                 const char *word, *state;
731                 size_t l;
732
733                 /**
734                  * Plain getenv, not secure_getenv, because we want
735                  * to actually allow the user to pick the binary.
736                  */
737                 path = getenv("PATH");
738                 if (!path)
739                         path = DEFAULT_PATH;
740
741                 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
742                         _cleanup_free_ char *p = NULL;
743
744                         if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
745                                 return -ENOMEM;
746
747                         if (access(p, X_OK) < 0)
748                                 continue;
749
750                         if (filename) {
751                                 *filename = path_kill_slashes(p);
752                                 p = NULL;
753                         }
754
755                         return 0;
756                 }
757
758                 return -ENOENT;
759         }
760 }
761
762 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
763         bool changed = false;
764         const char* const* i;
765
766         assert(timestamp);
767
768         if (paths == NULL)
769                 return false;
770
771         STRV_FOREACH(i, paths) {
772                 struct stat stats;
773                 usec_t u;
774
775                 if (stat(*i, &stats) < 0)
776                         continue;
777
778                 u = timespec_load(&stats.st_mtim);
779
780                 /* first check */
781                 if (*timestamp >= u)
782                         continue;
783
784                 log_debug("timestamp of '%s' changed", *i);
785
786                 /* update timestamp */
787                 if (update) {
788                         *timestamp = u;
789                         changed = true;
790                 } else
791                         return true;
792         }
793
794         return changed;
795 }
796
797 int fsck_exists(const char *fstype) {
798         _cleanup_free_ char *p = NULL, *d = NULL;
799         const char *checker;
800         int r;
801
802         checker = strjoina("fsck.", fstype);
803
804         r = find_binary(checker, true, &p);
805         if (r < 0)
806                 return r;
807
808         /* An fsck that is linked to /bin/true is a non-existent
809          * fsck */
810
811         r = readlink_malloc(p, &d);
812         if (r >= 0 &&
813             (path_equal(d, "/bin/true") ||
814              path_equal(d, "/usr/bin/true") ||
815              path_equal(d, "/dev/null")))
816                 return -ENOENT;
817
818         return 0;
819 }
820
821 char *prefix_root(const char *root, const char *path) {
822         char *n, *p;
823         size_t l;
824
825         /* If root is passed, prefixes path with it. Otherwise returns
826          * it as is. */
827
828         assert(path);
829
830         /* First, drop duplicate prefixing slashes from the path */
831         while (path[0] == '/' && path[1] == '/')
832                 path++;
833
834         if (isempty(root) || path_equal(root, "/"))
835                 return strdup(path);
836
837         l = strlen(root) + 1 + strlen(path) + 1;
838
839         n = new(char, l);
840         if (!n)
841                 return NULL;
842
843         p = stpcpy(n, root);
844
845         while (p > n && p[-1] == '/')
846                 p--;
847
848         if (path[0] != '/')
849                 *(p++) = '/';
850
851         strcpy(p, path);
852         return n;
853 }