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