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