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