]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-util.c
systemctl: refuse to edit runtime dropins when they already exist in /etc
[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 <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 u = orig;
310 orig = NULL;
311 }
312 } else
313 free(t);
314
315 l[k++] = u;
316 }
317
318 l[k] = NULL;
319
320 if (enomem)
321 return NULL;
322
323 return l;
324 }
325
326 char **path_strv_resolve_uniq(char **l, const char *prefix) {
327
328 if (strv_isempty(l))
329 return l;
330
331 if (!path_strv_resolve(l, prefix))
332 return NULL;
333
334 return strv_uniq(l);
335 }
336
337 char *path_kill_slashes(char *path) {
338 char *f, *t;
339 bool slash = false;
340
341 /* Removes redundant inner and trailing slashes. Modifies the
342 * passed string in-place.
343 *
344 * ///foo///bar/ becomes /foo/bar
345 */
346
347 for (f = path, t = path; *f; f++) {
348
349 if (*f == '/') {
350 slash = true;
351 continue;
352 }
353
354 if (slash) {
355 slash = false;
356 *(t++) = '/';
357 }
358
359 *(t++) = *f;
360 }
361
362 /* Special rule, if we are talking of the root directory, a
363 trailing slash is good */
364
365 if (t == path && slash)
366 *(t++) = '/';
367
368 *t = 0;
369 return path;
370 }
371
372 char* path_startswith(const char *path, const char *prefix) {
373 assert(path);
374 assert(prefix);
375
376 if ((path[0] == '/') != (prefix[0] == '/'))
377 return NULL;
378
379 for (;;) {
380 size_t a, b;
381
382 path += strspn(path, "/");
383 prefix += strspn(prefix, "/");
384
385 if (*prefix == 0)
386 return (char*) path;
387
388 if (*path == 0)
389 return NULL;
390
391 a = strcspn(path, "/");
392 b = strcspn(prefix, "/");
393
394 if (a != b)
395 return NULL;
396
397 if (memcmp(path, prefix, a) != 0)
398 return NULL;
399
400 path += a;
401 prefix += b;
402 }
403 }
404
405 bool path_equal(const char *a, const char *b) {
406 assert(a);
407 assert(b);
408
409 if ((a[0] == '/') != (b[0] == '/'))
410 return false;
411
412 for (;;) {
413 size_t j, k;
414
415 a += strspn(a, "/");
416 b += strspn(b, "/");
417
418 if (*a == 0 && *b == 0)
419 return true;
420
421 if (*a == 0 || *b == 0)
422 return false;
423
424 j = strcspn(a, "/");
425 k = strcspn(b, "/");
426
427 if (j != k)
428 return false;
429
430 if (memcmp(a, b, j) != 0)
431 return false;
432
433 a += j;
434 b += k;
435 }
436 }
437
438 char* path_join(const char *root, const char *path, const char *rest) {
439 assert(path);
440
441 if (!isempty(root))
442 return strjoin(root, endswith(root, "/") ? "" : "/",
443 path[0] == '/' ? path+1 : path,
444 rest ? (endswith(path, "/") ? "" : "/") : NULL,
445 rest && rest[0] == '/' ? rest+1 : rest,
446 NULL);
447 else
448 return strjoin(path,
449 rest ? (endswith(path, "/") ? "" : "/") : NULL,
450 rest && rest[0] == '/' ? rest+1 : rest,
451 NULL);
452 }
453
454 int path_is_mount_point(const char *t, bool allow_symlink) {
455
456 union file_handle_union h = {
457 .handle.handle_bytes = MAX_HANDLE_SZ
458 };
459
460 int mount_id, mount_id_parent;
461 _cleanup_free_ char *parent = NULL;
462 struct stat a, b;
463 int r;
464
465 /* We are not actually interested in the file handles, but
466 * name_to_handle_at() also passes us the mount ID, hence use
467 * it but throw the handle away */
468
469 if (path_equal(t, "/"))
470 return 1;
471
472 r = name_to_handle_at(AT_FDCWD, t, &h.handle, &mount_id, allow_symlink ? AT_SYMLINK_FOLLOW : 0);
473 if (r < 0) {
474 if (IN_SET(errno, ENOSYS, EOPNOTSUPP))
475 /* This kernel or file system does not support
476 * name_to_handle_at(), hence fallback to the
477 * traditional stat() logic */
478 goto fallback;
479
480 if (errno == ENOENT)
481 return 0;
482
483 return -errno;
484 }
485
486 r = path_get_parent(t, &parent);
487 if (r < 0)
488 return r;
489
490 h.handle.handle_bytes = MAX_HANDLE_SZ;
491 r = name_to_handle_at(AT_FDCWD, parent, &h.handle, &mount_id_parent, 0);
492 if (r < 0) {
493 /* The parent can't do name_to_handle_at() but the
494 * directory we are interested in can? If so, it must
495 * be a mount point */
496 if (errno == EOPNOTSUPP)
497 return 1;
498
499 return -errno;
500 }
501
502 return mount_id != mount_id_parent;
503
504 fallback:
505 if (allow_symlink)
506 r = stat(t, &a);
507 else
508 r = lstat(t, &a);
509
510 if (r < 0) {
511 if (errno == ENOENT)
512 return 0;
513
514 return -errno;
515 }
516
517 r = path_get_parent(t, &parent);
518 if (r < 0)
519 return r;
520
521 r = lstat(parent, &b);
522 if (r < 0)
523 return -errno;
524
525 return a.st_dev != b.st_dev;
526 }
527
528 int path_is_read_only_fs(const char *path) {
529 struct statvfs st;
530
531 assert(path);
532
533 if (statvfs(path, &st) < 0)
534 return -errno;
535
536 if (st.f_flag & ST_RDONLY)
537 return true;
538
539 /* On NFS, statvfs() might not reflect whether we can actually
540 * write to the remote share. Let's try again with
541 * access(W_OK) which is more reliable, at least sometimes. */
542 if (access(path, W_OK) < 0 && errno == EROFS)
543 return true;
544
545 return false;
546 }
547
548 int path_is_os_tree(const char *path) {
549 char *p;
550 int r;
551
552 /* We use /usr/lib/os-release as flag file if something is an OS */
553 p = strappenda(path, "/usr/lib/os-release");
554 r = access(p, F_OK);
555
556 if (r >= 0)
557 return 1;
558
559 /* Also check for the old location in /etc, just in case. */
560 p = strappenda(path, "/etc/os-release");
561 r = access(p, F_OK);
562
563 return r >= 0;
564 }
565
566 int find_binary(const char *name, bool local, char **filename) {
567 assert(name);
568
569 if (is_path(name)) {
570 if (local && access(name, X_OK) < 0)
571 return -errno;
572
573 if (filename) {
574 char *p;
575
576 p = path_make_absolute_cwd(name);
577 if (!p)
578 return -ENOMEM;
579
580 *filename = p;
581 }
582
583 return 0;
584 } else {
585 const char *path;
586 const char *word, *state;
587 size_t l;
588
589 /**
590 * Plain getenv, not secure_getenv, because we want
591 * to actually allow the user to pick the binary.
592 */
593 path = getenv("PATH");
594 if (!path)
595 path = DEFAULT_PATH;
596
597 FOREACH_WORD_SEPARATOR(word, l, path, ":", state) {
598 _cleanup_free_ char *p = NULL;
599
600 if (asprintf(&p, "%.*s/%s", (int) l, word, name) < 0)
601 return -ENOMEM;
602
603 if (access(p, X_OK) < 0)
604 continue;
605
606 if (filename) {
607 *filename = path_kill_slashes(p);
608 p = NULL;
609 }
610
611 return 0;
612 }
613
614 return -ENOENT;
615 }
616 }
617
618 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
619 bool changed = false;
620 const char* const* i;
621
622 assert(timestamp);
623
624 if (paths == NULL)
625 return false;
626
627 STRV_FOREACH(i, paths) {
628 struct stat stats;
629 usec_t u;
630
631 if (stat(*i, &stats) < 0)
632 continue;
633
634 u = timespec_load(&stats.st_mtim);
635
636 /* first check */
637 if (*timestamp >= u)
638 continue;
639
640 log_debug("timestamp of '%s' changed", *i);
641
642 /* update timestamp */
643 if (update) {
644 *timestamp = u;
645 changed = true;
646 } else
647 return true;
648 }
649
650 return changed;
651 }
652
653 int fsck_exists(const char *fstype) {
654 _cleanup_free_ char *p = NULL, *d = NULL;
655 const char *checker;
656 int r;
657
658 checker = strappenda("fsck.", fstype);
659
660 r = find_binary(checker, true, &p);
661 if (r < 0)
662 return r;
663
664 /* An fsck that is linked to /bin/true is a non-existent
665 * fsck */
666
667 r = readlink_malloc(p, &d);
668 if (r >= 0 &&
669 (path_equal(d, "/bin/true") ||
670 path_equal(d, "/usr/bin/true") ||
671 path_equal(d, "/dev/null")))
672 return -ENOENT;
673
674 return 0;
675 }