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