chiark / gitweb /
Simplify execute_directory()
[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 = -1, mount_id_parent = -1;
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 (errno == ENOSYS)
477                         /* This kernel does not support name_to_handle_at()
478                          * fall back to the traditional stat() logic. */
479                         goto fallback;
480                 else if (errno == EOPNOTSUPP)
481                         /* This kernel or file system does not support
482                          * name_to_handle_at(), hence fallback to the
483                          * traditional stat() logic */
484                         nosupp = true;
485                 else if (errno == ENOENT)
486                         return 0;
487                 else
488                         return -errno;
489         }
490
491         r = path_get_parent(t, &parent);
492         if (r < 0)
493                 return r;
494
495         h.handle.handle_bytes = MAX_HANDLE_SZ;
496         r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, AT_SYMLINK_FOLLOW);
497         if (r < 0)
498                 if (errno == EOPNOTSUPP)
499                         if (nosupp)
500                                 /* Neither parent nor child do name_to_handle_at()?
501                                    We have no choice but to fall back. */
502                                 goto fallback;
503                         else
504                                 /* The parent can't do name_to_handle_at() but
505                                  * the directory we are interested in can?
506                                  * Or the other way around?
507                                  * If so, it must be a mount point. */
508                                 return 1;
509                 else
510                         return -errno;
511         else
512                 return mount_id != mount_id_parent;
513
514 fallback:
515         if (allow_symlink)
516                 r = stat(t, &a);
517         else
518                 r = lstat(t, &a);
519
520         if (r < 0) {
521                 if (errno == ENOENT)
522                         return 0;
523
524                 return -errno;
525         }
526
527         free(parent);
528         parent = NULL;
529
530         r = path_get_parent(t, &parent);
531         if (r < 0)
532                 return r;
533
534         r = stat(parent, &b);
535         if (r < 0)
536                 return -errno;
537
538         return a.st_dev != b.st_dev;
539 }
540
541 int path_is_read_only_fs(const char *path) {
542         struct statvfs st;
543
544         assert(path);
545
546         if (statvfs(path, &st) < 0)
547                 return -errno;
548
549         if (st.f_flag & ST_RDONLY)
550                 return true;
551
552         /* On NFS, statvfs() might not reflect whether we can actually
553          * write to the remote share. Let's try again with
554          * access(W_OK) which is more reliable, at least sometimes. */
555         if (access(path, W_OK) < 0 && errno == EROFS)
556                 return true;
557
558         return false;
559 }
560
561 int path_is_os_tree(const char *path) {
562         char *p;
563         int r;
564
565         /* We use /usr/lib/os-release as flag file if something is an OS */
566         p = strappenda(path, "/usr/lib/os-release");
567         r = access(p, F_OK);
568
569         if (r >= 0)
570                 return 1;
571
572         /* Also check for the old location in /etc, just in case. */
573         p = strappenda(path, "/etc/os-release");
574         r = access(p, F_OK);
575
576         return r >= 0;
577 }
578
579 int find_binary(const char *name, bool local, char **filename) {
580         assert(name);
581
582         if (is_path(name)) {
583                 if (local && access(name, X_OK) < 0)
584                         return -errno;
585
586                 if (filename) {
587                         char *p;
588
589                         p = path_make_absolute_cwd(name);
590                         if (!p)
591                                 return -ENOMEM;
592
593                         *filename = p;
594                 }
595
596                 return 0;
597         } else {
598                 const char *path;
599                 const char *word, *state;
600                 size_t l;
601
602                 /**
603                  * Plain getenv, not secure_getenv, because we want
604                  * to actually allow the user to pick the binary.
605                  */
606                 path = getenv("PATH");
607                 if (!path)
608                         path = DEFAULT_PATH;
609
610                 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
611                         _cleanup_free_ char *p = NULL;
612
613                         if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
614                                 return -ENOMEM;
615
616                         if (access(p, X_OK) < 0)
617                                 continue;
618
619                         if (filename) {
620                                 *filename = path_kill_slashes(p);
621                                 p = NULL;
622                         }
623
624                         return 0;
625                 }
626
627                 return -ENOENT;
628         }
629 }
630
631 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
632         bool changed = false;
633         const char* const* i;
634
635         assert(timestamp);
636
637         if (paths == NULL)
638                 return false;
639
640         STRV_FOREACH(i, paths) {
641                 struct stat stats;
642                 usec_t u;
643
644                 if (stat(*i, &stats) < 0)
645                         continue;
646
647                 u = timespec_load(&stats.st_mtim);
648
649                 /* first check */
650                 if (*timestamp >= u)
651                         continue;
652
653                 log_debug("timestamp of '%s' changed", *i);
654
655                 /* update timestamp */
656                 if (update) {
657                         *timestamp = u;
658                         changed = true;
659                 } else
660                         return true;
661         }
662
663         return changed;
664 }
665
666 int fsck_exists(const char *fstype) {
667         _cleanup_free_ char *p = NULL, *d = NULL;
668         const char *checker;
669         int r;
670
671         checker = strappenda("fsck.", fstype);
672
673         r = find_binary(checker, true, &p);
674         if (r < 0)
675                 return r;
676
677         /* An fsck that is linked to /bin/true is a non-existent
678          * fsck */
679
680         r = readlink_malloc(p, &d);
681         if (r >= 0 &&
682             (path_equal(d, "/bin/true") ||
683              path_equal(d, "/usr/bin/true") ||
684              path_equal(d, "/dev/null")))
685                 return -ENOENT;
686
687         return 0;
688 }