]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-util.c
path-util: if parent can do name_to_handle() but relevant dir not, it's a mount point
[thirdparty/systemd.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
37 bool path_is_absolute(const char *p) {
38 return p[0] == '/';
39 }
40
41 bool is_path(const char *p) {
42 return !!strchr(p, '/');
43 }
44
45 int path_get_parent(const char *path, char **_r) {
46 const char *e, *a = NULL, *b = NULL, *p;
47 char *r;
48 bool slash = false;
49
50 assert(path);
51 assert(_r);
52
53 if (!*path)
54 return -EINVAL;
55
56 for (e = path; *e; e++) {
57
58 if (!slash && *e == '/') {
59 a = b;
60 b = e;
61 slash = true;
62 } else if (slash && *e != '/')
63 slash = false;
64 }
65
66 if (*(e-1) == '/')
67 p = a;
68 else
69 p = b;
70
71 if (!p)
72 return -EINVAL;
73
74 if (p == path)
75 r = strdup("/");
76 else
77 r = strndup(path, p-path);
78
79 if (!r)
80 return -ENOMEM;
81
82 *_r = r;
83 return 0;
84 }
85
86 char **path_split_and_make_absolute(const char *p) {
87 char **l;
88 assert(p);
89
90 l = strv_split(p, ":");
91 if (!l)
92 return NULL;
93
94 if (!path_strv_make_absolute_cwd(l)) {
95 strv_free(l);
96 return NULL;
97 }
98
99 return l;
100 }
101
102 char *path_make_absolute(const char *p, const char *prefix) {
103 assert(p);
104
105 /* Makes every item in the list an absolute path by prepending
106 * the prefix, if specified and necessary */
107
108 if (path_is_absolute(p) || !prefix)
109 return strdup(p);
110
111 return strjoin(prefix, "/", p, NULL);
112 }
113
114 char *path_make_absolute_cwd(const char *p) {
115 _cleanup_free_ char *cwd = NULL;
116
117 assert(p);
118
119 /* Similar to path_make_absolute(), but prefixes with the
120 * current working directory. */
121
122 if (path_is_absolute(p))
123 return strdup(p);
124
125 cwd = get_current_dir_name();
126 if (!cwd)
127 return NULL;
128
129 return strjoin(cwd, "/", p, NULL);
130 }
131
132 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
133 char *r, *p;
134 unsigned n_parents;
135
136 assert(from_dir);
137 assert(to_path);
138 assert(_r);
139
140 /* Strips the common part, and adds ".." elements as necessary. */
141
142 if (!path_is_absolute(from_dir))
143 return -EINVAL;
144
145 if (!path_is_absolute(to_path))
146 return -EINVAL;
147
148 /* Skip the common part. */
149 for (;;) {
150 size_t a;
151 size_t b;
152
153 from_dir += strspn(from_dir, "/");
154 to_path += strspn(to_path, "/");
155
156 if (!*from_dir) {
157 if (!*to_path)
158 /* from_dir equals to_path. */
159 r = strdup(".");
160 else
161 /* from_dir is a parent directory of to_path. */
162 r = strdup(to_path);
163
164 if (!r)
165 return -ENOMEM;
166
167 path_kill_slashes(r);
168
169 *_r = r;
170 return 0;
171 }
172
173 if (!*to_path)
174 break;
175
176 a = strcspn(from_dir, "/");
177 b = strcspn(to_path, "/");
178
179 if (a != b)
180 break;
181
182 if (memcmp(from_dir, to_path, a) != 0)
183 break;
184
185 from_dir += a;
186 to_path += b;
187 }
188
189 /* If we're here, then "from_dir" has one or more elements that need to
190 * be replaced with "..". */
191
192 /* Count the number of necessary ".." elements. */
193 for (n_parents = 0;;) {
194 from_dir += strspn(from_dir, "/");
195
196 if (!*from_dir)
197 break;
198
199 from_dir += strcspn(from_dir, "/");
200 n_parents++;
201 }
202
203 r = malloc(n_parents * 3 + strlen(to_path) + 1);
204 if (!r)
205 return -ENOMEM;
206
207 for (p = r; n_parents > 0; n_parents--, p += 3)
208 memcpy(p, "../", 3);
209
210 strcpy(p, to_path);
211 path_kill_slashes(r);
212
213 *_r = r;
214 return 0;
215 }
216
217 char **path_strv_make_absolute_cwd(char **l) {
218 char **s;
219
220 /* Goes through every item in the string list and makes it
221 * absolute. This works in place and won't rollback any
222 * changes on failure. */
223
224 STRV_FOREACH(s, l) {
225 char *t;
226
227 t = path_make_absolute_cwd(*s);
228 if (!t)
229 return NULL;
230
231 free(*s);
232 *s = t;
233 }
234
235 return l;
236 }
237
238 char **path_strv_resolve(char **l, const char *prefix) {
239 char **s;
240 unsigned k = 0;
241 bool enomem = false;
242
243 if (strv_isempty(l))
244 return l;
245
246 /* Goes through every item in the string list and canonicalize
247 * the path. This works in place and won't rollback any
248 * changes on failure. */
249
250 STRV_FOREACH(s, l) {
251 char *t, *u;
252 _cleanup_free_ char *orig = NULL;
253
254 if (!path_is_absolute(*s)) {
255 free(*s);
256 continue;
257 }
258
259 if (prefix) {
260 orig = *s;
261 t = strappend(prefix, orig);
262 if (!t) {
263 enomem = true;
264 continue;
265 }
266 } else
267 t = *s;
268
269 errno = 0;
270 u = canonicalize_file_name(t);
271 if (!u) {
272 if (errno == ENOENT) {
273 if (prefix) {
274 u = orig;
275 orig = NULL;
276 free(t);
277 } else
278 u = t;
279 } else {
280 free(t);
281 if (errno == ENOMEM || errno == 0)
282 enomem = true;
283
284 continue;
285 }
286 } else if (prefix) {
287 char *x;
288
289 free(t);
290 x = path_startswith(u, prefix);
291 if (x) {
292 /* restore the slash if it was lost */
293 if (!startswith(x, "/"))
294 *(--x) = '/';
295
296 t = strdup(x);
297 free(u);
298 if (!t) {
299 enomem = true;
300 continue;
301 }
302 u = t;
303 } else {
304 /* canonicalized path goes outside of
305 * prefix, keep the original path instead */
306 free(u);
307 u = orig;
308 orig = NULL;
309 }
310 } else
311 free(t);
312
313 l[k++] = u;
314 }
315
316 l[k] = NULL;
317
318 if (enomem)
319 return NULL;
320
321 return l;
322 }
323
324 char **path_strv_resolve_uniq(char **l, const char *prefix) {
325
326 if (strv_isempty(l))
327 return l;
328
329 if (!path_strv_resolve(l, prefix))
330 return NULL;
331
332 return strv_uniq(l);
333 }
334
335 char *path_kill_slashes(char *path) {
336 char *f, *t;
337 bool slash = false;
338
339 /* Removes redundant inner and trailing slashes. Modifies the
340 * passed string in-place.
341 *
342 * ///foo///bar/ becomes /foo/bar
343 */
344
345 for (f = path, t = path; *f; f++) {
346
347 if (*f == '/') {
348 slash = true;
349 continue;
350 }
351
352 if (slash) {
353 slash = false;
354 *(t++) = '/';
355 }
356
357 *(t++) = *f;
358 }
359
360 /* Special rule, if we are talking of the root directory, a
361 trailing slash is good */
362
363 if (t == path && slash)
364 *(t++) = '/';
365
366 *t = 0;
367 return path;
368 }
369
370 char* path_startswith(const char *path, const char *prefix) {
371 assert(path);
372 assert(prefix);
373
374 if ((path[0] == '/') != (prefix[0] == '/'))
375 return NULL;
376
377 for (;;) {
378 size_t a, b;
379
380 path += strspn(path, "/");
381 prefix += strspn(prefix, "/");
382
383 if (*prefix == 0)
384 return (char*) path;
385
386 if (*path == 0)
387 return NULL;
388
389 a = strcspn(path, "/");
390 b = strcspn(prefix, "/");
391
392 if (a != b)
393 return NULL;
394
395 if (memcmp(path, prefix, a) != 0)
396 return NULL;
397
398 path += a;
399 prefix += b;
400 }
401 }
402
403 int path_compare(const char *a, const char *b) {
404 int d;
405
406 assert(a);
407 assert(b);
408
409 /* A relative path and an abolute path must not compare as equal.
410 * Which one is sorted before the other does not really matter.
411 * Here a relative path is ordered before an absolute path. */
412 d = (a[0] == '/') - (b[0] == '/');
413 if (d)
414 return d;
415
416 for (;;) {
417 size_t j, k;
418
419 a += strspn(a, "/");
420 b += strspn(b, "/");
421
422 if (*a == 0 && *b == 0)
423 return 0;
424
425 /* Order prefixes first: "/foo" before "/foo/bar" */
426 if (*a == 0)
427 return -1;
428 if (*b == 0)
429 return 1;
430
431 j = strcspn(a, "/");
432 k = strcspn(b, "/");
433
434 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
435 d = memcmp(a, b, MIN(j, k));
436 if (d)
437 return (d > 0) - (d < 0); /* sign of d */
438
439 /* Sort "/foo/a" before "/foo/aaa" */
440 d = (j > k) - (j < k); /* sign of (j - k) */
441 if (d)
442 return d;
443
444 a += j;
445 b += k;
446 }
447 }
448
449 bool path_equal(const char *a, const char *b) {
450 return path_compare(a, b) == 0;
451 }
452
453 bool path_equal_or_files_same(const char *a, const char *b) {
454 return path_equal(a, b) || files_same(a, b) > 0;
455 }
456
457 char* path_join(const char *root, const char *path, const char *rest) {
458 assert(path);
459
460 if (!isempty(root))
461 return strjoin(root, endswith(root, "/") ? "" : "/",
462 path[0] == '/' ? path+1 : path,
463 rest ? (endswith(path, "/") ? "" : "/") : NULL,
464 rest && rest[0] == '/' ? rest+1 : rest,
465 NULL);
466 else
467 return strjoin(path,
468 rest ? (endswith(path, "/") ? "" : "/") : NULL,
469 rest && rest[0] == '/' ? rest+1 : rest,
470 NULL);
471 }
472
473 int fd_is_mount_point(int fd) {
474 union file_handle_union h = FILE_HANDLE_INIT;
475 int mount_id = -1, mount_id_parent = -1;
476 bool nosupp = false;
477 struct stat a, b;
478 int r;
479
480 /* We are not actually interested in the file handles, but
481 * name_to_handle_at() also passes us the mount ID, hence use
482 * it but throw the handle away */
483
484 r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH);
485 if (r < 0) {
486 if (errno == ENOSYS)
487 /* This kernel does not support name_to_handle_at()
488 * fall back to the traditional stat() logic. */
489 goto fallback;
490 else if (errno == EOPNOTSUPP)
491 /* This kernel or file system does not support
492 * name_to_handle_at(), hence let's see if the
493 * upper fs supports it (in which case it is a
494 * mount point), otherwise fallback to the
495 * traditional stat() logic */
496 nosupp = true;
497 else if (errno == ENOENT)
498 return 0;
499 else
500 return -errno;
501 }
502
503 h.handle.handle_bytes = MAX_HANDLE_SZ;
504 r = name_to_handle_at(fd, "..", &h.handle, &mount_id_parent, 0);
505 if (r < 0) {
506 if (errno == EOPNOTSUPP) {
507 if (nosupp)
508 /* Neither parent nor child do name_to_handle_at()?
509 We have no choice but to fall back. */
510 goto fallback;
511 else
512 /* The parent can't do name_to_handle_at() but the
513 * directory we are interested in can?
514 * If so, it must be a mount point. */
515 return 1;
516 } else
517 return -errno;
518 } else if (nosupp)
519 /* The parent can do name_to_handle_at() but the
520 * directory we are interested in can't? If so, it
521 * must be a mount point. */
522 return 1;
523 else
524 return mount_id != mount_id_parent;
525
526 fallback:
527 r = fstatat(fd, "", &a, AT_EMPTY_PATH);
528 if (r < 0) {
529 if (errno == ENOENT)
530 return 0;
531
532 return -errno;
533 }
534
535 r = fstatat(fd, "..", &b, 0);
536 if (r < 0)
537 return -errno;
538
539 return a.st_dev != b.st_dev;
540 }
541
542 int path_is_mount_point(const char *t, bool allow_symlink) {
543 _cleanup_close_ int fd = -1;
544 assert(t);
545
546 if (path_equal(t, "/"))
547 return 1;
548
549 fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
550 if (fd < 0) {
551 if (errno == ENOENT)
552 return 0;
553
554 return -errno;
555 }
556
557 return fd_is_mount_point(fd);
558 }
559
560 int path_is_read_only_fs(const char *path) {
561 struct statvfs st;
562
563 assert(path);
564
565 if (statvfs(path, &st) < 0)
566 return -errno;
567
568 if (st.f_flag & ST_RDONLY)
569 return true;
570
571 /* On NFS, statvfs() might not reflect whether we can actually
572 * write to the remote share. Let's try again with
573 * access(W_OK) which is more reliable, at least sometimes. */
574 if (access(path, W_OK) < 0 && errno == EROFS)
575 return true;
576
577 return false;
578 }
579
580 int path_is_os_tree(const char *path) {
581 char *p;
582 int r;
583
584 /* We use /usr/lib/os-release as flag file if something is an OS */
585 p = strjoina(path, "/usr/lib/os-release");
586 r = access(p, F_OK);
587
588 if (r >= 0)
589 return 1;
590
591 /* Also check for the old location in /etc, just in case. */
592 p = strjoina(path, "/etc/os-release");
593 r = access(p, F_OK);
594
595 return r >= 0;
596 }
597
598 int find_binary(const char *name, bool local, char **filename) {
599 assert(name);
600
601 if (is_path(name)) {
602 if (local && access(name, X_OK) < 0)
603 return -errno;
604
605 if (filename) {
606 char *p;
607
608 p = path_make_absolute_cwd(name);
609 if (!p)
610 return -ENOMEM;
611
612 *filename = p;
613 }
614
615 return 0;
616 } else {
617 const char *path;
618 const char *word, *state;
619 size_t l;
620
621 /**
622 * Plain getenv, not secure_getenv, because we want
623 * to actually allow the user to pick the binary.
624 */
625 path = getenv("PATH");
626 if (!path)
627 path = DEFAULT_PATH;
628
629 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
630 _cleanup_free_ char *p = NULL;
631
632 if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
633 return -ENOMEM;
634
635 if (access(p, X_OK) < 0)
636 continue;
637
638 if (filename) {
639 *filename = path_kill_slashes(p);
640 p = NULL;
641 }
642
643 return 0;
644 }
645
646 return -ENOENT;
647 }
648 }
649
650 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
651 bool changed = false;
652 const char* const* i;
653
654 assert(timestamp);
655
656 if (paths == NULL)
657 return false;
658
659 STRV_FOREACH(i, paths) {
660 struct stat stats;
661 usec_t u;
662
663 if (stat(*i, &stats) < 0)
664 continue;
665
666 u = timespec_load(&stats.st_mtim);
667
668 /* first check */
669 if (*timestamp >= u)
670 continue;
671
672 log_debug("timestamp of '%s' changed", *i);
673
674 /* update timestamp */
675 if (update) {
676 *timestamp = u;
677 changed = true;
678 } else
679 return true;
680 }
681
682 return changed;
683 }
684
685 int fsck_exists(const char *fstype) {
686 _cleanup_free_ char *p = NULL, *d = NULL;
687 const char *checker;
688 int r;
689
690 checker = strjoina("fsck.", fstype);
691
692 r = find_binary(checker, true, &p);
693 if (r < 0)
694 return r;
695
696 /* An fsck that is linked to /bin/true is a non-existent
697 * fsck */
698
699 r = readlink_malloc(p, &d);
700 if (r >= 0 &&
701 (path_equal(d, "/bin/true") ||
702 path_equal(d, "/usr/bin/true") ||
703 path_equal(d, "/dev/null")))
704 return -ENOENT;
705
706 return 0;
707 }