]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/path-util.c
Merge pull request #1688 from phomes/typo-fix
[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 "path-util.h"
36 #include "string-util.h"
37 #include "strv.h"
38 #include "util.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 int path_split_and_make_absolute(const char *p, char ***ret) {
90 char **l;
91 int r;
92
93 assert(p);
94 assert(ret);
95
96 l = strv_split(p, ":");
97 if (!l)
98 return -ENOMEM;
99
100 r = path_strv_make_absolute_cwd(l);
101 if (r < 0) {
102 strv_free(l);
103 return r;
104 }
105
106 *ret = l;
107 return r;
108 }
109
110 char *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
119 return strjoin(prefix, "/", p, NULL);
120 }
121
122 int path_make_absolute_cwd(const char *p, char **ret) {
123 char *c;
124
125 assert(p);
126 assert(ret);
127
128 /* Similar to path_make_absolute(), but prefixes with the
129 * current working directory. */
130
131 if (path_is_absolute(p))
132 c = strdup(p);
133 else {
134 _cleanup_free_ char *cwd = NULL;
135
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;
144
145 *ret = c;
146 return 0;
147 }
148
149 int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
150 char *r, *p;
151 unsigned n_parents;
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
184 path_kill_slashes(r);
185
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
220 r = malloc(n_parents * 3 + strlen(to_path) + 1);
221 if (!r)
222 return -ENOMEM;
223
224 for (p = r; n_parents > 0; n_parents--, p += 3)
225 memcpy(p, "../", 3);
226
227 strcpy(p, to_path);
228 path_kill_slashes(r);
229
230 *_r = r;
231 return 0;
232 }
233
234 int path_strv_make_absolute_cwd(char **l) {
235 char **s;
236 int r;
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
245 r = path_make_absolute_cwd(*s, &t);
246 if (r < 0)
247 return r;
248
249 free(*s);
250 *s = t;
251 }
252
253 return 0;
254 }
255
256 char **path_strv_resolve(char **l, const char *prefix) {
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;
270 _cleanup_free_ char *orig = NULL;
271
272 if (!path_is_absolute(*s)) {
273 free(*s);
274 continue;
275 }
276
277 if (prefix) {
278 orig = *s;
279 t = strappend(prefix, orig);
280 if (!t) {
281 enomem = true;
282 continue;
283 }
284 } else
285 t = *s;
286
287 errno = 0;
288 u = canonicalize_file_name(t);
289 if (!u) {
290 if (errno == ENOENT) {
291 if (prefix) {
292 u = orig;
293 orig = NULL;
294 free(t);
295 } else
296 u = t;
297 } else {
298 free(t);
299 if (errno == ENOMEM || errno == 0)
300 enomem = true;
301
302 continue;
303 }
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 */
324 free(u);
325 u = orig;
326 orig = NULL;
327 }
328 } else
329 free(t);
330
331 l[k++] = u;
332 }
333
334 l[k] = NULL;
335
336 if (enomem)
337 return NULL;
338
339 return l;
340 }
341
342 char **path_strv_resolve_uniq(char **l, const char *prefix) {
343
344 if (strv_isempty(l))
345 return l;
346
347 if (!path_strv_resolve(l, prefix))
348 return NULL;
349
350 return strv_uniq(l);
351 }
352
353 char *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
388 char* path_startswith(const char *path, const char *prefix) {
389 assert(path);
390 assert(prefix);
391
392 if ((path[0] == '/') != (prefix[0] == '/'))
393 return NULL;
394
395 for (;;) {
396 size_t a, b;
397
398 path += strspn(path, "/");
399 prefix += strspn(prefix, "/");
400
401 if (*prefix == 0)
402 return (char*) path;
403
404 if (*path == 0)
405 return NULL;
406
407 a = strcspn(path, "/");
408 b = strcspn(prefix, "/");
409
410 if (a != b)
411 return NULL;
412
413 if (memcmp(path, prefix, a) != 0)
414 return NULL;
415
416 path += a;
417 prefix += b;
418 }
419 }
420
421 int path_compare(const char *a, const char *b) {
422 int d;
423
424 assert(a);
425 assert(b);
426
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] == '/');
431 if (d != 0)
432 return d;
433
434 for (;;) {
435 size_t j, k;
436
437 a += strspn(a, "/");
438 b += strspn(b, "/");
439
440 if (*a == 0 && *b == 0)
441 return 0;
442
443 /* Order prefixes first: "/foo" before "/foo/bar" */
444 if (*a == 0)
445 return -1;
446 if (*b == 0)
447 return 1;
448
449 j = strcspn(a, "/");
450 k = strcspn(b, "/");
451
452 /* Alphabetical sort: "/foo/aaa" before "/foo/b" */
453 d = memcmp(a, b, MIN(j, k));
454 if (d != 0)
455 return (d > 0) - (d < 0); /* sign of d */
456
457 /* Sort "/foo/a" before "/foo/aaa" */
458 d = (j > k) - (j < k); /* sign of (j - k) */
459 if (d != 0)
460 return d;
461
462 a += j;
463 b += k;
464 }
465 }
466
467 bool path_equal(const char *a, const char *b) {
468 return path_compare(a, b) == 0;
469 }
470
471 bool path_equal_or_files_same(const char *a, const char *b) {
472 return path_equal(a, b) || files_same(a, b) > 0;
473 }
474
475 char* path_join(const char *root, const char *path, const char *rest) {
476 assert(path);
477
478 if (!isempty(root))
479 return strjoin(root, endswith(root, "/") ? "" : "/",
480 path[0] == '/' ? path+1 : path,
481 rest ? (endswith(path, "/") ? "" : "/") : NULL,
482 rest && rest[0] == '/' ? rest+1 : rest,
483 NULL);
484 else
485 return strjoin(path,
486 rest ? (endswith(path, "/") ? "" : "/") : NULL,
487 rest && rest[0] == '/' ? rest+1 : rest,
488 NULL);
489 }
490
491 static 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
529 int fd_is_mount_point(int fd, const char *filename, int flags) {
530 union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
531 int mount_id = -1, mount_id_parent = -1;
532 bool nosupp = false, check_st_dev = true;
533 struct stat a, b;
534 int r;
535
536 assert(fd >= 0);
537 assert(filename);
538
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
548 * name_to_handle_at(), however, does not return the
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. */
561
562 r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
563 if (r < 0) {
564 if (errno == ENOSYS)
565 /* This kernel does not support name_to_handle_at()
566 * fall back to simpler logic. */
567 goto fallback_fdinfo;
568 else if (errno == EOPNOTSUPP)
569 /* This kernel or file system does not support
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
573 * traditional stat() logic */
574 nosupp = true;
575 else
576 return -errno;
577 }
578
579 r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
580 if (r < 0) {
581 if (errno == EOPNOTSUPP) {
582 if (nosupp)
583 /* Neither parent nor child do name_to_handle_at()?
584 We have no choice but to fall back. */
585 goto fallback_fdinfo;
586 else
587 /* The parent can't do name_to_handle_at() but the
588 * directory we are interested in can?
589 * If so, it must be a mount point. */
590 return 1;
591 } else
592 return -errno;
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)
599 return 1;
600
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;
610
611 return mount_id != mount_id_parent;
612
613 fallback_fdinfo:
614 r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
615 if (r == -EOPNOTSUPP)
616 goto fallback_fstat;
617 if (r < 0)
618 return r;
619
620 r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
621 if (r < 0)
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
635 fallback_fstat:
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)
643 return -errno;
644
645 if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
646 return -errno;
647
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
654 return check_st_dev && (a.st_dev != b.st_dev);
655 }
656
657 /* flags can be AT_SYMLINK_FOLLOW or 0 */
658 int path_is_mount_point(const char *t, int flags) {
659 _cleanup_close_ int fd = -1;
660 _cleanup_free_ char *canonical = NULL, *parent = NULL;
661 int r;
662
663 assert(t);
664
665 if (path_equal(t, "/"))
666 return 1;
667
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;
676
677 t = canonical;
678 }
679
680 r = path_get_parent(t, &parent);
681 if (r < 0)
682 return r;
683
684 fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
685 if (fd < 0)
686 return -errno;
687
688 return fd_is_mount_point(fd, basename(t), flags);
689 }
690
691 int 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
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;
709 }
710
711 int path_is_os_tree(const char *path) {
712 char *p;
713 int r;
714
715 /* We use /usr/lib/os-release as flag file if something is an OS */
716 p = strjoina(path, "/usr/lib/os-release");
717 r = access(p, F_OK);
718 if (r >= 0)
719 return 1;
720
721 /* Also check for the old location in /etc, just in case. */
722 p = strjoina(path, "/etc/os-release");
723 r = access(p, F_OK);
724
725 return r >= 0;
726 }
727
728 int find_binary(const char *name, char **ret) {
729 int last_error, r;
730 const char *p;
731
732 assert(name);
733
734 if (is_path(name)) {
735 if (access(name, X_OK) < 0)
736 return -errno;
737
738 if (ret) {
739 r = path_make_absolute_cwd(name, ret);
740 if (r < 0)
741 return r;
742 }
743
744 return 0;
745 }
746
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
766 if (!path_is_absolute(element))
767 continue;
768
769 j = strjoin(element, "/", name, NULL);
770 if (!j)
771 return -ENOMEM;
772
773 if (access(j, X_OK) >= 0) {
774 /* Found it! */
775
776 if (ret) {
777 *ret = path_kill_slashes(j);
778 j = NULL;
779 }
780
781 return 0;
782 }
783
784 last_error = -errno;
785 }
786
787 return last_error;
788 }
789
790 bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
791 bool changed = false;
792 const char* const* i;
793
794 assert(timestamp);
795
796 if (paths == NULL)
797 return false;
798
799 STRV_FOREACH(i, paths) {
800 struct stat stats;
801 usec_t u;
802
803 if (stat(*i, &stats) < 0)
804 continue;
805
806 u = timespec_load(&stats.st_mtim);
807
808 /* first check */
809 if (*timestamp >= u)
810 continue;
811
812 log_debug("timestamp of '%s' changed", *i);
813
814 /* update timestamp */
815 if (update) {
816 *timestamp = u;
817 changed = true;
818 } else
819 return true;
820 }
821
822 return changed;
823 }
824
825 static int binary_is_good(const char *binary) {
826 _cleanup_free_ char *p = NULL, *d = NULL;
827 int r;
828
829 r = find_binary(binary, &p);
830 if (r == -ENOENT)
831 return 0;
832 if (r < 0)
833 return r;
834
835 /* An fsck that is linked to /bin/true is a non-existent
836 * fsck */
837
838 r = readlink_malloc(p, &d);
839 if (r == -EINVAL) /* not a symlink */
840 return 1;
841 if (r < 0)
842 return r;
843
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");
848 }
849
850 int fsck_exists(const char *fstype) {
851 const char *checker;
852
853 assert(fstype);
854
855 if (streq(fstype, "auto"))
856 return -EINVAL;
857
858 checker = strjoina("fsck.", fstype);
859 return binary_is_good(checker);
860 }
861
862 int mkfs_exists(const char *fstype) {
863 const char *mkfs;
864
865 assert(fstype);
866
867 if (streq(fstype, "auto"))
868 return -EINVAL;
869
870 mkfs = strjoina("mkfs.", fstype);
871 return binary_is_good(mkfs);
872 }
873
874 char *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 }
907
908 int 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 }