chiark / gitweb /
8c43d060fb2ed93f1eb008f4c56cb63cfe3352a9
[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 <assert.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <sys/statvfs.h>
32
33 #include "macro.h"
34 #include "util.h"
35 #include "log.h"
36 #include "strv.h"
37 #include "path-util.h"
38 #include "missing.h"
39
40 bool path_is_absolute(const char *p) {
41         return p[0] == '/';
42 }
43
44 bool is_path(const char *p) {
45         return !!strchr(p, '/');
46 }
47
48 int path_get_parent(const char *path, char **_r) {
49         const char *e, *a = NULL, *b = NULL, *p;
50         char *r;
51         bool slash = false;
52
53         assert(path);
54         assert(_r);
55
56         if (!*path)
57                 return -EINVAL;
58
59         for (e = path; *e; e++) {
60
61                 if (!slash && *e == '/') {
62                         a = b;
63                         b = e;
64                         slash = true;
65                 } else if (slash && *e != '/')
66                         slash = false;
67         }
68
69         if (*(e-1) == '/')
70                 p = a;
71         else
72                 p = b;
73
74         if (!p)
75                 return -EINVAL;
76
77         if (p == path)
78                 r = strdup("/");
79         else
80                 r = strndup(path, p-path);
81
82         if (!r)
83                 return -ENOMEM;
84
85         *_r = r;
86         return 0;
87 }
88
89 char **path_split_and_make_absolute(const char *p) {
90         char **l;
91         assert(p);
92
93         l = strv_split(p, ":");
94         if (!l)
95                 return NULL;
96
97         if (!path_strv_make_absolute_cwd(l)) {
98                 strv_free(l);
99                 return NULL;
100         }
101
102         return l;
103 }
104
105 char *path_make_absolute(const char *p, const char *prefix) {
106         assert(p);
107
108         /* Makes every item in the list an absolute path by prepending
109          * the prefix, if specified and necessary */
110
111         if (path_is_absolute(p) || !prefix)
112                 return strdup(p);
113
114         return strjoin(prefix, "/", p, NULL);
115 }
116
117 char *path_make_absolute_cwd(const char *p) {
118         _cleanup_free_ char *cwd = NULL;
119
120         assert(p);
121
122         /* Similar to path_make_absolute(), but prefixes with the
123          * current working directory. */
124
125         if (path_is_absolute(p))
126                 return strdup(p);
127
128         cwd = get_current_dir_name();
129         if (!cwd)
130                 return NULL;
131
132         return strjoin(cwd, "/", p, NULL);
133 }
134
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
220 char **path_strv_make_absolute_cwd(char **l) {
221         char **s;
222
223         /* Goes through every item in the string list and makes it
224          * absolute. This works in place and won't rollback any
225          * changes on failure. */
226
227         STRV_FOREACH(s, l) {
228                 char *t;
229
230                 t = path_make_absolute_cwd(*s);
231                 if (!t)
232                         return NULL;
233
234                 free(*s);
235                 *s = t;
236         }
237
238         return l;
239 }
240
241 char **path_strv_resolve(char **l, const char *prefix) {
242         char **s;
243         unsigned k = 0;
244         bool enomem = false;
245
246         if (strv_isempty(l))
247                 return l;
248
249         /* Goes through every item in the string list and canonicalize
250          * the path. This works in place and won't rollback any
251          * changes on failure. */
252
253         STRV_FOREACH(s, l) {
254                 char *t, *u;
255                 _cleanup_free_ char *orig = NULL;
256
257                 if (!path_is_absolute(*s)) {
258                         free(*s);
259                         continue;
260                 }
261
262                 if (prefix) {
263                         orig = *s;
264                         t = strappend(prefix, orig);
265                         if (!t) {
266                                 enomem = true;
267                                 continue;
268                         }
269                 } else
270                         t = *s;
271
272                 errno = 0;
273                 u = canonicalize_file_name(t);
274                 if (!u) {
275                         if (errno == ENOENT) {
276                                 if (prefix) {
277                                         u = orig;
278                                         orig = NULL;
279                                         free(t);
280                                 } else
281                                         u = t;
282                         } else {
283                                 free(t);
284                                 if (errno == ENOMEM || errno == 0)
285                                         enomem = true;
286
287                                 continue;
288                         }
289                 } else if (prefix) {
290                         char *x;
291
292                         free(t);
293                         x = path_startswith(u, prefix);
294                         if (x) {
295                                 /* restore the slash if it was lost */
296                                 if (!startswith(x, "/"))
297                                         *(--x) = '/';
298
299                                 t = strdup(x);
300                                 free(u);
301                                 if (!t) {
302                                         enomem = true;
303                                         continue;
304                                 }
305                                 u = t;
306                         } else {
307                                 /* canonicalized path goes outside of
308                                  * prefix, keep the original path instead */
309                                 free(u);
310                                 u = orig;
311                                 orig = NULL;
312                         }
313                 } else
314                         free(t);
315
316                 l[k++] = u;
317         }
318
319         l[k] = NULL;
320
321         if (enomem)
322                 return NULL;
323
324         return l;
325 }
326
327 char **path_strv_resolve_uniq(char **l, const char *prefix) {
328
329         if (strv_isempty(l))
330                 return l;
331
332         if (!path_strv_resolve(l, prefix))
333                 return NULL;
334
335         return strv_uniq(l);
336 }
337
338 char *path_kill_slashes(char *path) {
339         char *f, *t;
340         bool slash = false;
341
342         /* Removes redundant inner and trailing slashes. Modifies the
343          * passed string in-place.
344          *
345          * ///foo///bar/ becomes /foo/bar
346          */
347
348         for (f = path, t = path; *f; f++) {
349
350                 if (*f == '/') {
351                         slash = true;
352                         continue;
353                 }
354
355                 if (slash) {
356                         slash = false;
357                         *(t++) = '/';
358                 }
359
360                 *(t++) = *f;
361         }
362
363         /* Special rule, if we are talking of the root directory, a
364         trailing slash is good */
365
366         if (t == path && slash)
367                 *(t++) = '/';
368
369         *t = 0;
370         return path;
371 }
372
373 char* path_startswith(const char *path, const char *prefix) {
374         assert(path);
375         assert(prefix);
376
377         if ((path[0] == '/') != (prefix[0] == '/'))
378                 return NULL;
379
380         for (;;) {
381                 size_t a, b;
382
383                 path += strspn(path, "/");
384                 prefix += strspn(prefix, "/");
385
386                 if (*prefix == 0)
387                         return (char*) path;
388
389                 if (*path == 0)
390                         return NULL;
391
392                 a = strcspn(path, "/");
393                 b = strcspn(prefix, "/");
394
395                 if (a != b)
396                         return NULL;
397
398                 if (memcmp(path, prefix, a) != 0)
399                         return NULL;
400
401                 path += a;
402                 prefix += b;
403         }
404 }
405
406 bool path_equal(const char *a, const char *b) {
407         assert(a);
408         assert(b);
409
410         if ((a[0] == '/') != (b[0] == '/'))
411                 return false;
412
413         for (;;) {
414                 size_t j, k;
415
416                 a += strspn(a, "/");
417                 b += strspn(b, "/");
418
419                 if (*a == 0 && *b == 0)
420                         return true;
421
422                 if (*a == 0 || *b == 0)
423                         return false;
424
425                 j = strcspn(a, "/");
426                 k = strcspn(b, "/");
427
428                 if (j != k)
429                         return false;
430
431                 if (memcmp(a, b, j) != 0)
432                         return false;
433
434                 a += j;
435                 b += k;
436         }
437 }
438
439 char* path_join(const char *root, const char *path, const char *rest) {
440         assert(path);
441
442         if (!isempty(root))
443                 return strjoin(root, endswith(root, "/") ? "" : "/",
444                                path[0] == '/' ? path+1 : path,
445                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
446                                rest && rest[0] == '/' ? rest+1 : rest,
447                                NULL);
448         else
449                 return strjoin(path,
450                                rest ? (endswith(path, "/") ? "" : "/") : NULL,
451                                rest && rest[0] == '/' ? rest+1 : rest,
452                                NULL);
453 }
454
455 int path_is_mount_point(const char *t, bool allow_symlink) {
456
457         union file_handle_union h = {
458                 .handle.handle_bytes = MAX_HANDLE_SZ
459         };
460
461         int mount_id, mount_id_parent;
462         _cleanup_free_ char *parent = NULL;
463         struct stat a, b;
464         int r;
465         bool nosupp = false;
466
467         /* We are not actually interested in the file handles, but
468          * name_to_handle_at() also passes us the mount ID, hence use
469          * it but throw the handle away */
470
471         if (path_equal(t, "/"))
472                 return 1;
473
474         r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
475         if (r < 0) {
476                 if (IN_SET(errno, ENOSYS, EOPNOTSUPP))
477                         /* This kernel or file system does not support
478                          * name_to_handle_at(), hence fallback to the
479                          * traditional stat() logic */
480                         nosupp = true;
481                 else if (errno == ENOENT)
482                         return 0;
483                 else
484                         return -errno;
485         }
486
487         r = path_get_parent(t, &parent);
488         if (r < 0)
489                 return r;
490
491         h.handle.handle_bytes = MAX_HANDLE_SZ;
492         r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW);
493         if (r < 0)
494                 if (errno == EOPNOTSUPP)
495                         if (nosupp)
496                                 /* Neither parent nor child do name_to_handle_at()?
497                                    We have no choice but to fall back. */
498                                 goto fallback;
499                         else
500                                 /* The parent can't do name_to_handle_at() but
501                                  * the directory we are interested in can?
502                                  * Or the other way around?
503                                  * If so, it must be a mount point. */
504                                 return 1;
505                 else
506                         return -errno;
507         else
508                 return mount_id != mount_id_parent;
509
510 fallback:
511         if (allow_symlink)
512                 r = stat(t, &a);
513         else
514                 r = lstat(t, &a);
515
516         if (r < 0) {
517                 if (errno == ENOENT)
518                         return 0;
519
520                 return -errno;
521         }
522
523         r = path_get_parent(t, &parent);
524         if (r < 0)
525                 return r;
526
527         r = stat(parent, &b);
528         if (r < 0)
529                 return -errno;
530
531         return a.st_dev != b.st_dev;
532 }
533
534 int path_is_read_only_fs(const char *path) {
535         struct statvfs st;
536
537         assert(path);
538
539         if (statvfs(path, &st) < 0)
540                 return -errno;
541
542         if (st.f_flag & ST_RDONLY)
543                 return true;
544
545         /* On NFS, statvfs() might not reflect whether we can actually
546          * write to the remote share. Let's try again with
547          * access(W_OK) which is more reliable, at least sometimes. */
548         if (access(path, W_OK) < 0 && errno == EROFS)
549                 return true;
550
551         return false;
552 }
553
554 int path_is_os_tree(const char *path) {
555         char *p;
556         int r;
557
558         /* We use /usr/lib/os-release as flag file if something is an OS */
559         p = strappenda(path, "/usr/lib/os-release");
560         r = access(p, F_OK);
561
562         if (r >= 0)
563                 return 1;
564
565         /* Also check for the old location in /etc, just in case. */
566         p = strappenda(path, "/etc/os-release");
567         r = access(p, F_OK);
568
569         return r >= 0;
570 }
571
572 int find_binary(const char *name, bool local, char **filename) {
573         assert(name);
574
575         if (is_path(name)) {
576                 if (local && access(name, X_OK) < 0)
577                         return -errno;
578
579                 if (filename) {
580                         char *p;
581
582                         p = path_make_absolute_cwd(name);
583                         if (!p)
584                                 return -ENOMEM;
585
586                         *filename = p;
587                 }
588
589                 return 0;
590         } else {
591                 const char *path;
592                 const char *word, *state;
593                 size_t l;
594
595                 /**
596                  * Plain getenv, not secure_getenv, because we want
597                  * to actually allow the user to pick the binary.
598                  */
599                 path = getenv("PATH");
600                 if (!path)
601                         path = DEFAULT_PATH;
602
603                 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
604                         _cleanup_free_ char *p = NULL;
605
606                         if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
607                                 return -ENOMEM;
608
609                         if (access(p, X_OK) < 0)
610                                 continue;
611
612                         if (filename) {
613                                 *filename = path_kill_slashes(p);
614                                 p = NULL;
615                         }
616
617                         return 0;
618                 }
619
620                 return -ENOENT;
621         }
622 }
623
624 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
625         bool changed = false;
626         const char* const* i;
627
628         assert(timestamp);
629
630         if (paths == NULL)
631                 return false;
632
633         STRV_FOREACH(i, paths) {
634                 struct stat stats;
635                 usec_t u;
636
637                 if (stat(*i, &stats) < 0)
638                         continue;
639
640                 u = timespec_load(&stats.st_mtim);
641
642                 /* first check */
643                 if (*timestamp >= u)
644                         continue;
645
646                 log_debug("timestamp of '%s' changed", *i);
647
648                 /* update timestamp */
649                 if (update) {
650                         *timestamp = u;
651                         changed = true;
652                 } else
653                         return true;
654         }
655
656         return changed;
657 }
658
659 int fsck_exists(const char *fstype) {
660         _cleanup_free_ char *p = NULL, *d = NULL;
661         const char *checker;
662         int r;
663
664         checker = strappenda("fsck.", fstype);
665
666         r = find_binary(checker, true, &p);
667         if (r < 0)
668                 return r;
669
670         /* An fsck that is linked to /bin/true is a non-existent
671          * fsck */
672
673         r = readlink_malloc(p, &d);
674         if (r >= 0 &&
675             (path_equal(d, "/bin/true") ||
676              path_equal(d, "/usr/bin/true") ||
677              path_equal(d, "/dev/null")))
678                 return -ENOENT;
679
680         return 0;
681 }