if (!t)
return -ENOMEM;
- *fs = path_kill_slashes(t);
+ *fs = path_simplify(t, false);
return 0;
}
if (r < 0)
return r;
- path_kill_slashes(*fs);
+ path_simplify(*fs, false);
return 0;
}
if (!t)
return -ENOMEM;
- *path = path_kill_slashes(t);
+ *path = path_simplify(t, false);
}
if (controller)
return -EINVAL;
}
- path_kill_slashes(u);
+ path_simplify(u, false);
}
if (controller)
if (!t)
return -ENOMEM;
- *result = path_kill_slashes(t);
+ *result = path_simplify(t, false);
return 0;
}
strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
- *ret = path_kill_slashes(t);
+ *ret = path_simplify(t, false);
return 0;
}
*x = 0;
- *ret = path_kill_slashes(t);
+ *ret = path_simplify(t, false);
return 0;
}
*x = 0;
- *ret = path_kill_slashes(t);
+ *ret = path_simplify(t, false);
return 0;
}
if (!cleaned)
return -ENOMEM;
- path_kill_slashes(cleaned);
+ path_simplify(cleaned, false);
done = set_new(&path_hash_ops);
if (!done)
if (!r)
return -ENOMEM;
- path_kill_slashes(r);
+ path_simplify(r, false);
*_r = r;
return 0;
p = mempcpy(p, "../", 3);
strcpy(p, to_path);
- path_kill_slashes(r);
+ path_simplify(r, false);
*_r = r;
return 0;
if (r < 0)
return r;
- path_kill_slashes(t);
+ path_simplify(t, false);
free_and_replace(*s, t);
}
return strv_uniq(l);
}
-char *path_kill_slashes(char *path) {
+char *path_simplify(char *path, bool kill_dots) {
char *f, *t;
- bool slash = false;
+ bool slash = false, ignore_slash = false, absolute;
- /* Removes redundant inner and trailing slashes. Modifies the
- * passed string in-place.
+ assert(path);
+
+ /* Removes redundant inner and trailing slashes. Also removes unnecessary dots
+ * if kill_dots is true. Modifies the passed string in-place.
*
- * ///foo///bar/ becomes /foo/bar
+ * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false)
+ * ///foo//./bar/. becomes /foo/bar (if kill_dots is true)
+ * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false)
+ * .//./foo//./bar/. becomes foo/bar (if kill_dots is true)
*/
- for (f = path, t = path; *f; f++) {
+ absolute = path_is_absolute(path);
+
+ f = path;
+ if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/')) {
+ ignore_slash = true;
+ f++;
+ }
+
+ for (t = path; *f; f++) {
if (*f == '/') {
slash = true;
}
if (slash) {
+ if (kill_dots && *f == '.' && IN_SET(f[1], 0, '/'))
+ continue;
+
slash = false;
- *(t++) = '/';
+ if (ignore_slash)
+ ignore_slash = false;
+ else
+ *(t++) = '/';
}
*(t++) = *f;
}
- /* Special rule, if we are talking of the root directory, a
- trailing slash is good */
-
- if (t == path && slash)
+ /* Special rule, if we are talking of the root directory, a trailing slash is good */
+ if (absolute && t == path)
*(t++) = '/';
*t = 0;
/* Found it! */
if (ret) {
- *ret = path_kill_slashes(j);
+ *ret = path_simplify(j, false);
j = NULL;
}
if (r < 0)
return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path);
- path_kill_slashes(p);
+ path_simplify(p, false);
if (suppress_root && empty_or_root(p))
p = mfree(p);
- free(*arg);
- *arg = p;
+ free_and_replace(*arg, p);
return 0;
}
int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
-char* path_kill_slashes(char *path);
char* path_startswith(const char *path, const char *prefix) _pure_;
int path_compare(const char *a, const char *b) _pure_;
bool path_equal(const char *a, const char *b) _pure_;
bool path_equal_or_files_same(const char *a, const char *b, int flags);
char* path_join(const char *root, const char *path, const char *rest);
+char* path_simplify(char *path, bool kill_dots);
static inline bool path_equal_ptr(const char *a, const char *b) {
return !!a == !!b && (!a || path_equal(a, b));
* the tree, to root. Also returns "" (and not "/"!) for the root
* directory. Excludes the specified directory itself */
#define PATH_FOREACH_PREFIX(prefix, path) \
- for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+ for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */
#define PATH_FOREACH_PREFIX_MORE(prefix, path) \
- for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
+ for (char *_slash = ({ path_simplify(strcpy(prefix, path), false); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/'))
char *prefix_root(const char *root, const char *path);
if (!p)
return -ENOMEM;
- path_kill_slashes(p);
+ path_simplify(p, false);
if (empty_or_root(p))
s = strdup("-");
goto finish;
}
- path_kill_slashes(j);
+ path_simplify(j, false);
path = j;
} else
path = root;
if (!p)
return -ENOMEM;
- path_kill_slashes(p);
+ path_simplify(p, false);
r = refresh_one(controller, p, a, b, iteration, depth + 1, &child);
if (r < 0)
if (r < 0)
return r;
- path_kill_slashes(a->where);
+ path_simplify(a->where, false);
return 1;
}
return -EINVAL;
pp = strjoina("/", pp, suffix_path);
- path_kill_slashes(pp);
+ path_simplify(pp, false);
r = sd_bus_call_method(u->manager->system_bus,
"org.freedesktop.systemd1",
c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
- path_kill_slashes(c->path);
+ path_simplify(c->path, false);
exec_command_append_list(exec_command, c);
}
if (!path_is_absolute(i + offset))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
- path_kill_slashes(i + offset);
+ path_simplify(i + offset, false);
}
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (!k)
return -ENOMEM;
+ path_simplify(k, false);
+
s = new0(PathSpec, 1);
if (!s)
return -ENOMEM;
s->unit = u;
- s->path = path_kill_slashes(k);
- k = NULL;
+ s->path = TAKE_PTR(k);
s->type = t;
s->inotify_fd = -1;
if (p->type != SOCKET_SOCKET) {
p->path = strdup(a);
- path_kill_slashes(p->path);
+ path_simplify(p->path, false);
} else if (streq(t, "Netlink")) {
r = socket_address_parse_netlink(&p->address, a);
return 0;
}
- path_kill_slashes(k);
+ path_simplify(k, false);
r = strv_push(x, k);
if (r < 0)
return 0;
}
- path_kill_slashes(p->path);
+ path_simplify(p->path, false);
} else if (streq(lvalue, "ListenNetlink")) {
_cleanup_free_ char *k = NULL;
n[nlen] = NULL;
}
- path_kill_slashes(path);
+ path_simplify(path, false);
while (!isempty(p)) {
_cleanup_free_ char *word = NULL, *resolved = NULL;
return 0;
}
- path_kill_slashes(k);
+ path_simplify(k, false);
if (!path_is_absolute(k)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
return missing_ok ? 0 : -ENOEXEC;
}
- path_kill_slashes(k);
+ path_simplify(k, false);
if (!utf8_is_valid(k)) {
log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
continue;
}
- path_kill_slashes(resolved);
+ path_simplify(resolved, false);
joined = strjoin(ignore_enoent ? "-" : "",
shall_prefix ? "+" : "",
continue;
}
- path_kill_slashes(resolved);
+ path_simplify(resolved, false);
r = temporary_filesystem_add(&c->temporary_filesystems, &c->n_temporary_filesystems, path, w);
if (r == -ENOMEM)
return 0;
}
- path_kill_slashes(s);
+ path_simplify(s, false);
/* Optionally, the destination is specified. */
if (p && p[-1] == ':') {
return 0;
}
- d = path_kill_slashes(dresolved);
+ d = path_simplify(dresolved, false);
/* Optionally, there's also a short option string specified */
if (p && p[-1] == ':') {
if (c++ >= FOLLOW_MAX)
return -ELOOP;
- path_kill_slashes(*filename);
+ path_simplify(*filename, false);
/* Add the file name we are currently looking at to
* the names of this unit, but only if it is a valid
assert(path);
strcpy(p, path);
- path_kill_slashes(p);
+ path_simplify(p, false);
return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p);
}
return r;
}
- path_kill_slashes(m->where);
+ path_simplify(m->where, false);
if (!u->description) {
r = unit_set_description(u, m->where);
(void) sd_event_source_set_description(s->event_source, "path");
- /* This function assumes the path was passed through path_kill_slashes()! */
+ /* This function assumes the path was passed through path_simplify()! */
assert(!strstr(s->path, "//"));
for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
return -ENOMEM;
}
- path_kill_slashes(ps->path);
+ path_simplify(ps->path, false);
/* PATH_CHANGED would not be enough. There are daemons (sendmail) that
* keep their PID file open all the time. */
if (!ep)
return -ENOMEM;
- path_kill_slashes(ep);
+ path_simplify(ep, false);
r = usbffs_address_create(ep);
if (r < 0)
return -ENOMEM;
}
- path_kill_slashes(s->what);
+ path_simplify(s->what, false);
if (!UNIT(s)->description) {
r = unit_set_description(u, s->what);
if (!p)
return -ENOMEM;
- path = path_kill_slashes(p);
+ path = path_simplify(p, false);
if (!path_is_normalized(path))
return -EPERM;
int i;
for (i = optind; i < argc; i++) {
- path_kill_slashes(argv[i]);
+ path_simplify(argv[i], false);
k = process_suffix_chop(argv[i]);
if (k < 0)
return log_oom();
if (is_path(where)) {
- path_kill_slashes(where);
+ path_simplify(where, false);
/* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
* mount units, but causes problems since it historically worked to have symlinks in e.g.
return log_oom();
free_and_replace(syspath, new_syspath);
- path_kill_slashes(syspath);
+ path_simplify(syspath, false);
}
if (path_startswith(syspath, "/sys/devices/")) {
if (!arg_mount_what)
return log_oom();
- path_kill_slashes(arg_mount_what);
+ path_simplify(arg_mount_what, false);
if (!path_is_absolute(arg_mount_what)) {
log_error("Only absolute path is supported: %s", arg_mount_what);
if (!arg_mount_where)
return log_oom();
- path_kill_slashes(arg_mount_where);
+ path_simplify(arg_mount_where, false);
if (!path_is_absolute(arg_mount_where)) {
log_error("Only absolute path is supported: %s", arg_mount_where);
if (!p)
return log_oom();
- path_kill_slashes(p);
+ path_simplify(p, false);
r = stop_mounts(bus, p);
if (r < 0)
if (!p)
return -ENOMEM;
- path_kill_slashes(p);
+ path_simplify(p, false);
if (source) {
s = strdup(source);
if (!s)
return -ENOMEM;
- path_kill_slashes(s);
+ path_simplify(s, false);
}
c[(*n_changes)++] = (PortableChange) {
if (!n)
return log_oom();
- path_kill_slashes(n);
+ path_simplify(n, false);
finalize:
free(*s);
if (!p || (source && !s))
return -ENOMEM;
- path_kill_slashes(p);
+ path_simplify(p, false);
if (s)
- path_kill_slashes(s);
+ path_simplify(s, false);
c[*n_changes] = (UnitFileChange) { type, p, s };
p = s = NULL;
if (!n)
return -ENOMEM;
- path_kill_slashes(n);
+ path_simplify(n, false);
r = set_consume(*remove_symlinks_to, n);
if (r == -EEXIST)
p = path_make_absolute(de->d_name, path);
if (!p)
return -ENOMEM;
- path_kill_slashes(p);
+ path_simplify(p, false);
q = readlink_malloc(p, &dest);
if (q == -ENOENT)
if (!i->path)
return -ENOMEM;
- path_kill_slashes(i->path);
+ path_simplify(i->path, false);
*ret = TAKE_PTR(i);
if (resolved_id) {
if (path_is_absolute(resolved_id)) {
i->uid_path = TAKE_PTR(resolved_id);
-
- path_kill_slashes(i->uid_path);
+ path_simplify(i->uid_path, false);
} else {
_cleanup_free_ char *uid = NULL, *gid = NULL;
if (split_pair(resolved_id, ":", &uid, &gid) == 0) {
if (resolved_id) {
if (path_is_absolute(resolved_id)) {
i->gid_path = TAKE_PTR(resolved_id);
-
- path_kill_slashes(i->gid_path);
+ path_simplify(i->gid_path, false);
} else {
r = parse_gid(resolved_id, &i->gid);
if (r < 0)
assert_se(path_equal(b, a) == !result); \
}
+static void test_path_simplify(const char *in, const char *out, const char *out_dot) {
+ char *p;
+
+ p = strdupa(in);
+ assert_se(streq(path_simplify(p, false), out));
+
+ p = strdupa(in);
+ assert_se(streq(path_simplify(p, true), out_dot));
+}
+
static void test_path(void) {
_cleanup_close_ int fd = -1;
assert_se(fd >= 0);
assert_se(fd_is_mount_point(fd, "/", 0) > 0);
- {
- char p1[] = "aaa/bbb////ccc";
- char p2[] = "//aaa/.////ccc";
- char p3[] = "/./";
-
- assert_se(path_equal(path_kill_slashes(p1), "aaa/bbb/ccc"));
- assert_se(path_equal(path_kill_slashes(p2), "/aaa/./ccc"));
- assert_se(path_equal(path_kill_slashes(p3), "/./"));
- }
+ test_path_simplify("aaa/bbb////ccc", "aaa/bbb/ccc", "aaa/bbb/ccc");
+ test_path_simplify("//aaa/.////ccc", "/aaa/./ccc", "/aaa/ccc");
+ test_path_simplify("///", "/", "/");
+ test_path_simplify("///.//", "/.", "/");
+ test_path_simplify("///.//.///", "/./.", "/");
+ test_path_simplify("////.././///../.", "/.././../.", "/../..");
+ test_path_simplify(".", ".", "");
+ test_path_simplify("./", ".", "");
+ test_path_simplify(".///.//./.", "./././.", "");
+ test_path_simplify(".///.//././/", "./././.", "");
+ test_path_simplify("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/.",
+ "/./aaa/././.bbb/../c./d.dd/..eeee/.",
+ "/aaa/.bbb/../c./d.dd/..eeee");
+ test_path_simplify("//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..",
+ "/./aaa/././.bbb/../c./d.dd/..eeee/..",
+ "/aaa/.bbb/../c./d.dd/..eeee/..");
+ test_path_simplify(".//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..",
+ "././aaa/././.bbb/../c./d.dd/..eeee/..",
+ "aaa/.bbb/../c./d.dd/..eeee/..");
+ test_path_simplify("..//./aaa///.//./.bbb/..///c.//d.dd///..eeee/..",
+ ".././aaa/././.bbb/../c./d.dd/..eeee/..",
+ "../aaa/.bbb/../c./d.dd/..eeee/..");
assert_se(PATH_IN_SET("/bin", "/", "/bin", "/foo"));
assert_se(PATH_IN_SET("/bin", "/bin"));
goto fail;
}
- path_kill_slashes(s);
+ path_simplify(s, false);
r = set_consume(unix_sockets, s);
if (r < 0 && r != -EEXIST) {
return -EBADMSG;
}
- path_kill_slashes(i.argument);
+ path_simplify(i.argument, false);
break;
case CREATE_CHAR_DEVICE:
return -EBADMSG;
}
- path_kill_slashes(i.path);
+ path_simplify(i.path, false);
if (!should_include_path(i.path))
return 0;