]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/path-util.c
Merge pull request #1679 from evverx/refuse-manual-start-by-reload-or-restart
[thirdparty/systemd.git] / src / basic / path-util.c
CommitLineData
9eb977db
KS
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
9eb977db 22#include <errno.h>
9eb977db 23#include <fcntl.h>
07630cea
LP
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
9eb977db 27#include <sys/statvfs.h>
07630cea 28#include <unistd.h>
9eb977db 29
3ffd4af2 30#include "fd-util.h"
07630cea 31#include "fileio.h"
9eb977db 32#include "log.h"
07630cea
LP
33#include "macro.h"
34#include "missing.h"
3ffd4af2 35#include "path-util.h"
07630cea 36#include "string-util.h"
9eb977db 37#include "strv.h"
07630cea 38#include "util.h"
9eb977db
KS
39
40bool path_is_absolute(const char *p) {
41 return p[0] == '/';
42}
43
44bool is_path(const char *p) {
45 return !!strchr(p, '/');
46}
47
9eb977db
KS
48int 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
0f474365 89int path_split_and_make_absolute(const char *p, char ***ret) {
9eb977db 90 char **l;
0f474365
LP
91 int r;
92
9eb977db 93 assert(p);
0f474365 94 assert(ret);
9eb977db 95
116cc028
ZJS
96 l = strv_split(p, ":");
97 if (!l)
9eb977db
KS
98 return NULL;
99
0f474365
LP
100 r = path_strv_make_absolute_cwd(l);
101 if (r < 0) {
9eb977db 102 strv_free(l);
0f474365 103 return r;
9eb977db
KS
104 }
105
0f474365
LP
106 *ret = l;
107 return r;
9eb977db
KS
108}
109
110char *path_make_absolute(const char *p, const char *prefix) {
111 assert(p);
112
113 /* Makes every item in the list an absolute path by prepending
114 * the prefix, if specified and necessary */
115
116 if (path_is_absolute(p) || !prefix)
117 return strdup(p);
118
b7def684 119 return strjoin(prefix, "/", p, NULL);
9eb977db
KS
120}
121
0f474365
LP
122int path_make_absolute_cwd(const char *p, char **ret) {
123 char *c;
9eb977db
KS
124
125 assert(p);
0f474365 126 assert(ret);
9eb977db
KS
127
128 /* Similar to path_make_absolute(), but prefixes with the
129 * current working directory. */
130
131 if (path_is_absolute(p))
0f474365
LP
132 c = strdup(p);
133 else {
134 _cleanup_free_ char *cwd = NULL;
9eb977db 135
0f474365
LP
136 cwd = get_current_dir_name();
137 if (!cwd)
138 return -errno;
139
140 c = strjoin(cwd, "/", p, NULL);
141 }
142 if (!c)
143 return -ENOMEM;
9eb977db 144
0f474365
LP
145 *ret = c;
146 return 0;
9eb977db
KS
147}
148
7cb9c51c
TK
149int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
150 char *r, *p;
151 unsigned n_parents;
7cb9c51c
TK
152
153 assert(from_dir);
154 assert(to_path);
155 assert(_r);
156
157 /* Strips the common part, and adds ".." elements as necessary. */
158
159 if (!path_is_absolute(from_dir))
160 return -EINVAL;
161
162 if (!path_is_absolute(to_path))
163 return -EINVAL;
164
165 /* Skip the common part. */
166 for (;;) {
167 size_t a;
168 size_t b;
169
170 from_dir += strspn(from_dir, "/");
171 to_path += strspn(to_path, "/");
172
173 if (!*from_dir) {
174 if (!*to_path)
175 /* from_dir equals to_path. */
176 r = strdup(".");
177 else
178 /* from_dir is a parent directory of to_path. */
179 r = strdup(to_path);
180
181 if (!r)
182 return -ENOMEM;
183
5216f599
TK
184 path_kill_slashes(r);
185
7cb9c51c
TK
186 *_r = r;
187 return 0;
188 }
189
190 if (!*to_path)
191 break;
192
193 a = strcspn(from_dir, "/");
194 b = strcspn(to_path, "/");
195
196 if (a != b)
197 break;
198
199 if (memcmp(from_dir, to_path, a) != 0)
200 break;
201
202 from_dir += a;
203 to_path += b;
204 }
205
206 /* If we're here, then "from_dir" has one or more elements that need to
207 * be replaced with "..". */
208
209 /* Count the number of necessary ".." elements. */
210 for (n_parents = 0;;) {
211 from_dir += strspn(from_dir, "/");
212
213 if (!*from_dir)
214 break;
215
216 from_dir += strcspn(from_dir, "/");
217 n_parents++;
218 }
219
5216f599 220 r = malloc(n_parents * 3 + strlen(to_path) + 1);
7cb9c51c
TK
221 if (!r)
222 return -ENOMEM;
223
224 for (p = r; n_parents > 0; n_parents--, p += 3)
225 memcpy(p, "../", 3);
226
5216f599
TK
227 strcpy(p, to_path);
228 path_kill_slashes(r);
7cb9c51c
TK
229
230 *_r = r;
231 return 0;
232}
233
0f474365 234int path_strv_make_absolute_cwd(char **l) {
9eb977db 235 char **s;
0f474365 236 int r;
9eb977db
KS
237
238 /* Goes through every item in the string list and makes it
239 * absolute. This works in place and won't rollback any
240 * changes on failure. */
241
242 STRV_FOREACH(s, l) {
243 char *t;
244
0f474365
LP
245 r = path_make_absolute_cwd(*s, &t);
246 if (r < 0)
247 return r;
9eb977db
KS
248
249 free(*s);
250 *s = t;
251 }
252
0f474365 253 return 0;
9eb977db
KS
254}
255
7d8da2c9 256char **path_strv_resolve(char **l, const char *prefix) {
9eb977db
KS
257 char **s;
258 unsigned k = 0;
259 bool enomem = false;
260
261 if (strv_isempty(l))
262 return l;
263
264 /* Goes through every item in the string list and canonicalize
265 * the path. This works in place and won't rollback any
266 * changes on failure. */
267
268 STRV_FOREACH(s, l) {
269 char *t, *u;
12ed81d9 270 _cleanup_free_ char *orig = NULL;
9eb977db 271
12ed81d9
ZJS
272 if (!path_is_absolute(*s)) {
273 free(*s);
9eb977db 274 continue;
12ed81d9 275 }
112cfb18
MM
276
277 if (prefix) {
12ed81d9
ZJS
278 orig = *s;
279 t = strappend(prefix, orig);
112cfb18
MM
280 if (!t) {
281 enomem = true;
282 continue;
283 }
12ed81d9 284 } else
112cfb18 285 t = *s;
9eb977db
KS
286
287 errno = 0;
288 u = canonicalize_file_name(t);
9eb977db 289 if (!u) {
12ed81d9
ZJS
290 if (errno == ENOENT) {
291 if (prefix) {
292 u = orig;
293 orig = NULL;
294 free(t);
295 } else
296 u = t;
297 } else {
874310b7 298 free(t);
112cfb18 299 if (errno == ENOMEM || errno == 0)
874310b7
ZJS
300 enomem = true;
301
302 continue;
303 }
12ed81d9
ZJS
304 } else if (prefix) {
305 char *x;
306
307 free(t);
308 x = path_startswith(u, prefix);
309 if (x) {
310 /* restore the slash if it was lost */
311 if (!startswith(x, "/"))
312 *(--x) = '/';
313
314 t = strdup(x);
315 free(u);
316 if (!t) {
317 enomem = true;
318 continue;
319 }
320 u = t;
321 } else {
322 /* canonicalized path goes outside of
323 * prefix, keep the original path instead */
3542eac7 324 free(u);
12ed81d9
ZJS
325 u = orig;
326 orig = NULL;
327 }
91a6489d
LP
328 } else
329 free(t);
9eb977db
KS
330
331 l[k++] = u;
332 }
333
334 l[k] = NULL;
335
336 if (enomem)
337 return NULL;
338
339 return l;
340}
341
7d8da2c9 342char **path_strv_resolve_uniq(char **l, const char *prefix) {
112cfb18 343
fabe5c0e
LP
344 if (strv_isempty(l))
345 return l;
346
7d8da2c9 347 if (!path_strv_resolve(l, prefix))
fabe5c0e
LP
348 return NULL;
349
350 return strv_uniq(l);
351}
352
9eb977db
KS
353char *path_kill_slashes(char *path) {
354 char *f, *t;
355 bool slash = false;
356
357 /* Removes redundant inner and trailing slashes. Modifies the
358 * passed string in-place.
359 *
360 * ///foo///bar/ becomes /foo/bar
361 */
362
363 for (f = path, t = path; *f; f++) {
364
365 if (*f == '/') {
366 slash = true;
367 continue;
368 }
369
370 if (slash) {
371 slash = false;
372 *(t++) = '/';
373 }
374
375 *(t++) = *f;
376 }
377
378 /* Special rule, if we are talking of the root directory, a
379 trailing slash is good */
380
381 if (t == path && slash)
382 *(t++) = '/';
383
384 *t = 0;
385 return path;
386}
387
424a19f8 388char* path_startswith(const char *path, const char *prefix) {
9eb977db
KS
389 assert(path);
390 assert(prefix);
391
392 if ((path[0] == '/') != (prefix[0] == '/'))
424a19f8 393 return NULL;
9eb977db
KS
394
395 for (;;) {
396 size_t a, b;
397
398 path += strspn(path, "/");
399 prefix += strspn(prefix, "/");
400
401 if (*prefix == 0)
424a19f8 402 return (char*) path;
9eb977db
KS
403
404 if (*path == 0)
424a19f8 405 return NULL;
9eb977db
KS
406
407 a = strcspn(path, "/");
408 b = strcspn(prefix, "/");
409
410 if (a != b)
424a19f8 411 return NULL;
9eb977db
KS
412
413 if (memcmp(path, prefix, a) != 0)
424a19f8 414 return NULL;
9eb977db
KS
415
416 path += a;
417 prefix += b;
418 }
419}
420
2230852b
MS
421int path_compare(const char *a, const char *b) {
422 int d;
423
9eb977db
KS
424 assert(a);
425 assert(b);
426
2230852b
MS
427 /* A relative path and an abolute path must not compare as equal.
428 * Which one is sorted before the other does not really matter.
429 * Here a relative path is ordered before an absolute path. */
430 d = (a[0] == '/') - (b[0] == '/');
373cd63a 431 if (d != 0)
2230852b 432 return d;
9eb977db
KS
433
434 for (;;) {
435 size_t j, k;
436
437 a += strspn(a, "/");
438 b += strspn(b, "/");
439
440 if (*a == 0 && *b == 0)
2230852b 441 return 0;
9eb977db 442
2230852b
MS
443 /* Order prefixes first: "/foo" before "/foo/bar" */
444 if (*a == 0)
445 return -1;
446 if (*b == 0)
447 return 1;
9eb977db
KS
448
449 j = strcspn(a, "/");
450 k = strcspn(b, "/");
451
2230852b
MS
452 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
453 d = memcmp(a, b, MIN(j, k));
373cd63a 454 if (d != 0)
2230852b 455 return (d > 0) - (d < 0); /* sign of d */
9eb977db 456
2230852b
MS
457 /* Sort "/foo/a" before "/foo/aaa" */
458 d = (j > k) - (j < k); /* sign of (j - k) */
373cd63a 459 if (d != 0)
2230852b 460 return d;
9eb977db
KS
461
462 a += j;
463 b += k;
464 }
465}
466
2230852b
MS
467bool path_equal(const char *a, const char *b) {
468 return path_compare(a, b) == 0;
469}
470
c78e47a6
MS
471bool path_equal_or_files_same(const char *a, const char *b) {
472 return path_equal(a, b) || files_same(a, b) > 0;
473}
474
0c6ea3a4
ZJS
475char* path_join(const char *root, const char *path, const char *rest) {
476 assert(path);
477
478 if (!isempty(root))
bc854dc7 479 return strjoin(root, endswith(root, "/") ? "" : "/",
0c6ea3a4 480 path[0] == '/' ? path+1 : path,
bc854dc7 481 rest ? (endswith(path, "/") ? "" : "/") : NULL,
0c6ea3a4
ZJS
482 rest && rest[0] == '/' ? rest+1 : rest,
483 NULL);
484 else
485 return strjoin(path,
bc854dc7 486 rest ? (endswith(path, "/") ? "" : "/") : NULL,
0c6ea3a4
ZJS
487 rest && rest[0] == '/' ? rest+1 : rest,
488 NULL);
489}
490
3f72b427
LP
491static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
492 char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
493 _cleanup_free_ char *fdinfo = NULL;
494 _cleanup_close_ int subfd = -1;
495 char *p;
496 int r;
497
498 if ((flags & AT_EMPTY_PATH) && isempty(filename))
499 xsprintf(path, "/proc/self/fdinfo/%i", fd);
500 else {
501 subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
502 if (subfd < 0)
503 return -errno;
504
505 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
506 }
507
508 r = read_full_file(path, &fdinfo, NULL);
509 if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
510 return -EOPNOTSUPP;
511 if (r < 0)
512 return -errno;
513
514 p = startswith(fdinfo, "mnt_id:");
515 if (!p) {
516 p = strstr(fdinfo, "\nmnt_id:");
517 if (!p) /* The mnt_id field is a relatively new addition */
518 return -EOPNOTSUPP;
519
520 p += 8;
521 }
522
523 p += strspn(p, WHITESPACE);
524 p[strcspn(p, WHITESPACE)] = 0;
525
526 return safe_atoi(p, mnt_id);
527}
528
5d409034 529int fd_is_mount_point(int fd, const char *filename, int flags) {
05d990ef 530 union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
e40872fc 531 int mount_id = -1, mount_id_parent = -1;
84317788 532 bool nosupp = false, check_st_dev = true;
1640a0b6 533 struct stat a, b;
21749924 534 int r;
9eb977db 535
05d990ef 536 assert(fd >= 0);
5d409034 537 assert(filename);
05d990ef 538
3f72b427
LP
539 /* First we will try the name_to_handle_at() syscall, which
540 * tells us the mount id and an opaque file "handle". It is
541 * not supported everywhere though (kernel compile-time
542 * option, not all file systems are hooked up). If it works
543 * the mount id is usually good enough to tell us whether
544 * something is a mount point.
545 *
546 * If that didn't work we will try to read the mount id from
547 * /proc/self/fdinfo/<fd>. This is almost as good as
af86c440 548 * name_to_handle_at(), however, does not return the
3f72b427
LP
549 * opaque file handle. The opaque file handle is pretty useful
550 * to detect the root directory, which we should always
551 * consider a mount point. Hence we use this only as
552 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
553 * kernel addition.
554 *
555 * As last fallback we do traditional fstat() based st_dev
556 * comparisons. This is how things were traditionally done,
557 * but unionfs breaks breaks this since it exposes file
558 * systems with a variety of st_dev reported. Also, btrfs
559 * subvolumes have different st_dev, even though they aren't
560 * real mounts of their own. */
cde9cb34 561
5d409034 562 r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
9eb977db 563 if (r < 0) {
e40872fc
DR
564 if (errno == ENOSYS)
565 /* This kernel does not support name_to_handle_at()
3f72b427
LP
566 * fall back to simpler logic. */
567 goto fallback_fdinfo;
e40872fc 568 else if (errno == EOPNOTSUPP)
fa125f4e 569 /* This kernel or file system does not support
f25afeb6
LP
570 * name_to_handle_at(), hence let's see if the
571 * upper fs supports it (in which case it is a
572 * mount point), otherwise fallback to the
1640a0b6 573 * traditional stat() logic */
6feeeab0 574 nosupp = true;
6feeeab0
ZJS
575 else
576 return -errno;
9eb977db
KS
577 }
578
5d409034 579 r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
f25afeb6
LP
580 if (r < 0) {
581 if (errno == EOPNOTSUPP) {
6feeeab0
ZJS
582 if (nosupp)
583 /* Neither parent nor child do name_to_handle_at()?
584 We have no choice but to fall back. */
3f72b427 585 goto fallback_fdinfo;
6feeeab0 586 else
f25afeb6
LP
587 /* The parent can't do name_to_handle_at() but the
588 * directory we are interested in can?
6feeeab0
ZJS
589 * If so, it must be a mount point. */
590 return 1;
f25afeb6 591 } else
6feeeab0 592 return -errno;
3f72b427
LP
593 }
594
595 /* The parent can do name_to_handle_at() but the
596 * directory we are interested in can't? If so, it
597 * must be a mount point. */
598 if (nosupp)
8f06b239 599 return 1;
05d990ef 600
3f72b427
LP
601 /* If the file handle for the directory we are
602 * interested in and its parent are identical, we
603 * assume this is the root directory, which is a mount
604 * point. */
605
606 if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
607 h.handle.handle_type == h_parent.handle.handle_type &&
608 memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
609 return 1;
1640a0b6 610
3f72b427
LP
611 return mount_id != mount_id_parent;
612
613fallback_fdinfo:
5d409034 614 r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
3f72b427
LP
615 if (r == -EOPNOTSUPP)
616 goto fallback_fstat;
e792e890 617 if (r < 0)
3f72b427 618 return r;
9eb977db 619
5d409034 620 r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
1640a0b6 621 if (r < 0)
3f72b427
LP
622 return r;
623
624 if (mount_id != mount_id_parent)
625 return 1;
626
627 /* Hmm, so, the mount ids are the same. This leaves one
628 * special case though for the root file system. For that,
629 * let's see if the parent directory has the same inode as we
630 * are interested in. Hence, let's also do fstat() checks now,
631 * too, but avoid the st_dev comparisons, since they aren't
632 * that useful on unionfs mounts. */
633 check_st_dev = false;
634
635fallback_fstat:
5d409034
MP
636 /* yay for fstatat() taking a different set of flags than the other
637 * _at() above */
638 if (flags & AT_SYMLINK_FOLLOW)
639 flags &= ~AT_SYMLINK_FOLLOW;
640 else
641 flags |= AT_SYMLINK_NOFOLLOW;
642 if (fstatat(fd, filename, &a, flags) < 0)
3f72b427
LP
643 return -errno;
644
5d409034 645 if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
1640a0b6
LP
646 return -errno;
647
05d990ef
LP
648 /* A directory with same device and inode as its parent? Must
649 * be the root directory */
650 if (a.st_dev == b.st_dev &&
651 a.st_ino == b.st_ino)
652 return 1;
653
3f72b427 654 return check_st_dev && (a.st_dev != b.st_dev);
9eb977db
KS
655}
656
e26d6ce5
MP
657/* flags can be AT_SYMLINK_FOLLOW or 0 */
658int path_is_mount_point(const char *t, int flags) {
f25afeb6 659 _cleanup_close_ int fd = -1;
36908eb8 660 _cleanup_free_ char *canonical = NULL, *parent = NULL;
5d409034 661 int r;
e792e890 662
f25afeb6
LP
663 assert(t);
664
665 if (path_equal(t, "/"))
666 return 1;
667
36908eb8
MP
668 /* we need to resolve symlinks manually, we can't just rely on
669 * fd_is_mount_point() to do that for us; if we have a structure like
670 * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
671 * look at needs to be /usr, not /. */
672 if (flags & AT_SYMLINK_FOLLOW) {
673 canonical = canonicalize_file_name(t);
674 if (!canonical)
675 return -errno;
10c03e9e
LP
676
677 t = canonical;
36908eb8
MP
678 }
679
10c03e9e 680 r = path_get_parent(t, &parent);
5d409034
MP
681 if (r < 0)
682 return r;
683
684 fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
e792e890 685 if (fd < 0)
f25afeb6 686 return -errno;
f25afeb6 687
10c03e9e 688 return fd_is_mount_point(fd, basename(t), flags);
f25afeb6
LP
689}
690
9eb977db
KS
691int path_is_read_only_fs(const char *path) {
692 struct statvfs st;
693
694 assert(path);
695
696 if (statvfs(path, &st) < 0)
697 return -errno;
698
70421bdc
LP
699 if (st.f_flag & ST_RDONLY)
700 return true;
701
702 /* On NFS, statvfs() might not reflect whether we can actually
703 * write to the remote share. Let's try again with
704 * access(W_OK) which is more reliable, at least sometimes. */
705 if (access(path, W_OK) < 0 && errno == EROFS)
706 return true;
707
708 return false;
9eb977db 709}
66060897
LP
710
711int path_is_os_tree(const char *path) {
712 char *p;
713 int r;
714
5ae4d543 715 /* We use /usr/lib/os-release as flag file if something is an OS */
63c372cb 716 p = strjoina(path, "/usr/lib/os-release");
5ae4d543 717 r = access(p, F_OK);
5ae4d543
LP
718 if (r >= 0)
719 return 1;
66060897 720
5ae4d543 721 /* Also check for the old location in /etc, just in case. */
63c372cb 722 p = strjoina(path, "/etc/os-release");
66060897
LP
723 r = access(p, F_OK);
724
5ae4d543 725 return r >= 0;
66060897 726}
c9d954b2 727
85eca92e
LP
728int find_binary(const char *name, char **ret) {
729 int last_error, r;
730 const char *p;
731
c9d954b2 732 assert(name);
4087cb9e 733
571d0134 734 if (is_path(name)) {
85eca92e 735 if (access(name, X_OK) < 0)
b972115c
ZJS
736 return -errno;
737
85eca92e 738 if (ret) {
0f474365
LP
739 r = path_make_absolute_cwd(name, ret);
740 if (r < 0)
741 return r;
eb66db55 742 }
c9d954b2 743
c9d954b2 744 return 0;
85eca92e 745 }
c9d954b2 746
85eca92e
LP
747 /**
748 * Plain getenv, not secure_getenv, because we want
749 * to actually allow the user to pick the binary.
750 */
751 p = getenv("PATH");
752 if (!p)
753 p = DEFAULT_PATH;
754
755 last_error = -ENOENT;
756
757 for (;;) {
758 _cleanup_free_ char *j = NULL, *element = NULL;
759
760 r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS);
761 if (r < 0)
762 return r;
763 if (r == 0)
764 break;
765
0f474365
LP
766 if (!path_is_absolute(element))
767 continue;
768
85eca92e
LP
769 j = strjoin(element, "/", name, NULL);
770 if (!j)
771 return -ENOMEM;
772
773 if (access(j, X_OK) >= 0) {
774 /* Found it! */
c9d954b2 775
85eca92e
LP
776 if (ret) {
777 *ret = path_kill_slashes(j);
778 j = NULL;
eb66db55 779 }
c9d954b2
ZJS
780
781 return 0;
782 }
783
85eca92e 784 last_error = -errno;
c9d954b2 785 }
85eca92e
LP
786
787 return last_error;
c9d954b2 788}
8e184852 789
2ad8416d 790bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
8e184852 791 bool changed = false;
2ad8416d 792 const char* const* i;
8e184852 793
97f2d76d
TG
794 assert(timestamp);
795
8e184852 796 if (paths == NULL)
4087cb9e 797 return false;
8e184852 798
4087cb9e 799 STRV_FOREACH(i, paths) {
8e184852 800 struct stat stats;
4087cb9e 801 usec_t u;
8e184852 802
4087cb9e 803 if (stat(*i, &stats) < 0)
8e184852
TG
804 continue;
805
4087cb9e
LP
806 u = timespec_load(&stats.st_mtim);
807
97f2d76d 808 /* first check */
4087cb9e 809 if (*timestamp >= u)
8e184852
TG
810 continue;
811
9f6445e3 812 log_debug("timestamp of '%s' changed", *i);
8e184852
TG
813
814 /* update timestamp */
4087cb9e
LP
815 if (update) {
816 *timestamp = u;
817 changed = true;
818 } else
819 return true;
8e184852 820 }
4087cb9e 821
8e184852
TG
822 return changed;
823}
eb66db55 824
5bcd08db 825static int binary_is_good(const char *binary) {
571d0134 826 _cleanup_free_ char *p = NULL, *d = NULL;
571d0134 827 int r;
eb66db55 828
85eca92e
LP
829 r = find_binary(binary, &p);
830 if (r == -ENOENT)
831 return 0;
571d0134
LP
832 if (r < 0)
833 return r;
834
061df014 835 /* An fsck that is linked to /bin/true is a non-existent
571d0134
LP
836 * fsck */
837
838 r = readlink_malloc(p, &d);
85eca92e
LP
839 if (r == -EINVAL) /* not a symlink */
840 return 1;
841 if (r < 0)
842 return r;
571d0134 843
85eca92e
LP
844 return !path_equal(d, "true") &&
845 !path_equal(d, "/bin/true") &&
846 !path_equal(d, "/usr/bin/true") &&
847 !path_equal(d, "/dev/null");
eb66db55 848}
1d13f648 849
5bcd08db
LP
850int fsck_exists(const char *fstype) {
851 const char *checker;
852
85eca92e 853 assert(fstype);
5bcd08db 854
85eca92e
LP
855 if (streq(fstype, "auto"))
856 return -EINVAL;
857
858 checker = strjoina("fsck.", fstype);
5bcd08db
LP
859 return binary_is_good(checker);
860}
861
862int mkfs_exists(const char *fstype) {
863 const char *mkfs;
864
85eca92e 865 assert(fstype);
5bcd08db 866
85eca92e
LP
867 if (streq(fstype, "auto"))
868 return -EINVAL;
869
870 mkfs = strjoina("mkfs.", fstype);
5bcd08db
LP
871 return binary_is_good(mkfs);
872}
873
1d13f648
LP
874char *prefix_root(const char *root, const char *path) {
875 char *n, *p;
876 size_t l;
877
878 /* If root is passed, prefixes path with it. Otherwise returns
879 * it as is. */
880
881 assert(path);
882
883 /* First, drop duplicate prefixing slashes from the path */
884 while (path[0] == '/' && path[1] == '/')
885 path++;
886
887 if (isempty(root) || path_equal(root, "/"))
888 return strdup(path);
889
890 l = strlen(root) + 1 + strlen(path) + 1;
891
892 n = new(char, l);
893 if (!n)
894 return NULL;
895
896 p = stpcpy(n, root);
897
898 while (p > n && p[-1] == '/')
899 p--;
900
901 if (path[0] != '/')
902 *(p++) = '/';
903
904 strcpy(p, path);
905 return n;
906}
0f03c2a4
LP
907
908int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
909 char *p;
910 int r;
911
912 /*
913 * This function is intended to be used in command line
914 * parsers, to handle paths that are passed in. It makes the
915 * path absolute, and reduces it to NULL if omitted or
916 * root (the latter optionally).
917 *
918 * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON
919 * SUCCESS! Hence, do not pass in uninitialized pointers.
920 */
921
922 if (isempty(path)) {
923 *arg = mfree(*arg);
924 return 0;
925 }
926
927 r = path_make_absolute_cwd(path, &p);
928 if (r < 0)
929 return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
930
931 path_kill_slashes(p);
932 if (suppress_root && path_equal(p, "/"))
933 p = mfree(p);
934
935 free(*arg);
936 *arg = p;
937 return 0;
938}