chiark / gitweb /
util: loop_write - accept 0-length message
[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) {
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
521         /* First we will try the name_to_handle_at() syscall, which
522          * tells us the mount id and an opaque file "handle". It is
523          * not supported everywhere though (kernel compile-time
524          * option, not all file systems are hooked up). If it works
525          * the mount id is usually good enough to tell us whether
526          * something is a mount point.
527          *
528          * If that didn't work we will try to read the mount id from
529          * /proc/self/fdinfo/<fd>. This is almost as good as
530          * name_to_handle_at(), however, does not return the the
531          * opaque file handle. The opaque file handle is pretty useful
532          * to detect the root directory, which we should always
533          * consider a mount point. Hence we use this only as
534          * fallback. Exporting the mnt_id in fdinfo is a pretty recent
535          * kernel addition.
536          *
537          * As last fallback we do traditional fstat() based st_dev
538          * comparisons. This is how things were traditionally done,
539          * but unionfs breaks breaks this since it exposes file
540          * systems with a variety of st_dev reported. Also, btrfs
541          * subvolumes have different st_dev, even though they aren't
542          * real mounts of their own. */
543
544         r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH);
545         if (r < 0) {
546                 if (errno == ENOSYS)
547                         /* This kernel does not support name_to_handle_at()
548                          * fall back to simpler logic. */
549                         goto fallback_fdinfo;
550                 else if (errno == EOPNOTSUPP)
551                         /* This kernel or file system does not support
552                          * name_to_handle_at(), hence let's see if the
553                          * upper fs supports it (in which case it is a
554                          * mount point), otherwise fallback to the
555                          * traditional stat() logic */
556                         nosupp = true;
557                 else
558                         return -errno;
559         }
560
561         r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0);
562         if (r < 0) {
563                 if (errno == EOPNOTSUPP) {
564                         if (nosupp)
565                                 /* Neither parent nor child do name_to_handle_at()?
566                                    We have no choice but to fall back. */
567                                 goto fallback_fdinfo;
568                         else
569                                 /* The parent can't do name_to_handle_at() but the
570                                  * directory we are interested in can?
571                                  * If so, it must be a mount point. */
572                                 return 1;
573                 } else
574                         return -errno;
575         }
576
577         /* The parent can do name_to_handle_at() but the
578          * directory we are interested in can't? If so, it
579          * must be a mount point. */
580         if (nosupp)
581                 return 1;
582
583         /* If the file handle for the directory we are
584          * interested in and its parent are identical, we
585          * assume this is the root directory, which is a mount
586          * point. */
587
588         if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
589             h.handle.handle_type == h_parent.handle.handle_type &&
590             memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
591                 return 1;
592
593         return mount_id != mount_id_parent;
594
595 fallback_fdinfo:
596         r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id);
597         if (r == -EOPNOTSUPP)
598                 goto fallback_fstat;
599         if (r < 0)
600                 return r;
601
602         r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent);
603         if (r < 0)
604                 return r;
605
606         if (mount_id != mount_id_parent)
607                 return 1;
608
609         /* Hmm, so, the mount ids are the same. This leaves one
610          * special case though for the root file system. For that,
611          * let's see if the parent directory has the same inode as we
612          * are interested in. Hence, let's also do fstat() checks now,
613          * too, but avoid the st_dev comparisons, since they aren't
614          * that useful on unionfs mounts. */
615         check_st_dev = false;
616
617 fallback_fstat:
618         if (fstatat(fd, "", &a, AT_EMPTY_PATH) < 0)
619                 return -errno;
620
621         if (fstatat(fd, "..", &b, 0) < 0)
622                 return -errno;
623
624         /* A directory with same device and inode as its parent? Must
625          * be the root directory */
626         if (a.st_dev == b.st_dev &&
627             a.st_ino == b.st_ino)
628                 return 1;
629
630         return check_st_dev && (a.st_dev != b.st_dev);
631 }
632
633 int path_is_mount_point(const char *t, bool allow_symlink) {
634         _cleanup_close_ int fd = -1;
635
636         assert(t);
637
638         if (path_equal(t, "/"))
639                 return 1;
640
641         fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
642         if (fd < 0)
643                 return -errno;
644
645         return fd_is_mount_point(fd);
646 }
647
648 int path_is_read_only_fs(const char *path) {
649         struct statvfs st;
650
651         assert(path);
652
653         if (statvfs(path, &st) < 0)
654                 return -errno;
655
656         if (st.f_flag & ST_RDONLY)
657                 return true;
658
659         /* On NFS, statvfs() might not reflect whether we can actually
660          * write to the remote share. Let's try again with
661          * access(W_OK) which is more reliable, at least sometimes. */
662         if (access(path, W_OK) < 0 && errno == EROFS)
663                 return true;
664
665         return false;
666 }
667
668 int path_is_os_tree(const char *path) {
669         char *p;
670         int r;
671
672         /* We use /usr/lib/os-release as flag file if something is an OS */
673         p = strjoina(path, "/usr/lib/os-release");
674         r = access(p, F_OK);
675
676         if (r >= 0)
677                 return 1;
678
679         /* Also check for the old location in /etc, just in case. */
680         p = strjoina(path, "/etc/os-release");
681         r = access(p, F_OK);
682
683         return r >= 0;
684 }
685
686 int find_binary(const char *name, bool local, char **filename) {
687         assert(name);
688
689         if (is_path(name)) {
690                 if (local && access(name, X_OK) < 0)
691                         return -errno;
692
693                 if (filename) {
694                         char *p;
695
696                         p = path_make_absolute_cwd(name);
697                         if (!p)
698                                 return -ENOMEM;
699
700                         *filename = p;
701                 }
702
703                 return 0;
704         } else {
705                 const char *path;
706                 const char *word, *state;
707                 size_t l;
708
709                 /**
710                  * Plain getenv, not secure_getenv, because we want
711                  * to actually allow the user to pick the binary.
712                  */
713                 path = getenv("PATH");
714                 if (!path)
715                         path = DEFAULT_PATH;
716
717                 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
718                         _cleanup_free_ char *p = NULL;
719
720                         if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
721                                 return -ENOMEM;
722
723                         if (access(p, X_OK) < 0)
724                                 continue;
725
726                         if (filename) {
727                                 *filename = path_kill_slashes(p);
728                                 p = NULL;
729                         }
730
731                         return 0;
732                 }
733
734                 return -ENOENT;
735         }
736 }
737
738 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
739         bool changed = false;
740         const char* const* i;
741
742         assert(timestamp);
743
744         if (paths == NULL)
745                 return false;
746
747         STRV_FOREACH(i, paths) {
748                 struct stat stats;
749                 usec_t u;
750
751                 if (stat(*i, &stats) < 0)
752                         continue;
753
754                 u = timespec_load(&stats.st_mtim);
755
756                 /* first check */
757                 if (*timestamp >= u)
758                         continue;
759
760                 log_debug("timestamp of '%s' changed", *i);
761
762                 /* update timestamp */
763                 if (update) {
764                         *timestamp = u;
765                         changed = true;
766                 } else
767                         return true;
768         }
769
770         return changed;
771 }
772
773 int fsck_exists(const char *fstype) {
774         _cleanup_free_ char *p = NULL, *d = NULL;
775         const char *checker;
776         int r;
777
778         checker = strjoina("fsck.", fstype);
779
780         r = find_binary(checker, true, &p);
781         if (r < 0)
782                 return r;
783
784         /* An fsck that is linked to /bin/true is a non-existent
785          * fsck */
786
787         r = readlink_malloc(p, &d);
788         if (r >= 0 &&
789             (path_equal(d, "/bin/true") ||
790              path_equal(d, "/usr/bin/true") ||
791              path_equal(d, "/dev/null")))
792                 return -ENOENT;
793
794         return 0;
795 }
796
797 char *prefix_root(const char *root, const char *path) {
798         char *n, *p;
799         size_t l;
800
801         /* If root is passed, prefixes path with it. Otherwise returns
802          * it as is. */
803
804         assert(path);
805
806         /* First, drop duplicate prefixing slashes from the path */
807         while (path[0] == '/' && path[1] == '/')
808                 path++;
809
810         if (isempty(root) || path_equal(root, "/"))
811                 return strdup(path);
812
813         l = strlen(root) + 1 + strlen(path) + 1;
814
815         n = new(char, l);
816         if (!n)
817                 return NULL;
818
819         p = stpcpy(n, root);
820
821         while (p > n && p[-1] == '/')
822                 p--;
823
824         if (path[0] != '/')
825                 *(p++) = '/';
826
827         strcpy(p, path);
828         return n;
829 }