Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/syscall/proc-state.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,24 @@ const char *proc_resolve_sysroot_create_path(const char *path,
return NULL;
}

/* An all-slash guest path ("/", "///") names the root, which always exists
* and has no parent to check; trimming it would walk strrchr into the
* sysroot prefix and trip the containment guard.
*
* Return buf as-is.
*/
if (path[strspn(path, "/")] == '\0')
return buf;

char parent[LINUX_PATH_MAX];
str_copy_trunc(parent, buf, sizeof(parent));
/* Trailing slashes name the same directory (POSIX); drop them so the final
* separator splits off the leaf component rather than matching the trailing
* slash itself, which would leave the target as its own parent.
*/
size_t plen = strlen(parent);
while (plen > 1 && parent[plen - 1] == '/')
parent[--plen] = '\0';
char *slash = strrchr(parent, '/');
if (!slash || slash == parent)
return buf;
Expand Down
44 changes: 44 additions & 0 deletions tests/test-sysroot-create-paths.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#include <unistd.h>

Expand Down Expand Up @@ -57,6 +59,23 @@ int main(int argc, char **argv)

printf("test-sysroot-create-paths: create-path routing tests\n");

TEST("root create path stays inside sysroot");
{
const char *roots[] = {"/", "///"};
bool ok = true;
for (size_t i = 0; i < sizeof(roots) / sizeof(roots[0]); i++) {
errno = 0;
if (mkdir(roots[i], 0777) == 0 || errno != EEXIST) {
ok = false;
break;
}
}
if (ok)
PASS();
else
FAIL("mkdir root path returned wrong result");
}

TEST("/tmp create is redirected into sysroot");
{
if (write_file(guest_tmp_path, "tmp-redir\n") < 0) {
Expand All @@ -75,6 +94,31 @@ int main(int argc, char **argv)
}
}

TEST("trailing-slash mkdir resolves against the real parent");
{
/* Issue #100: a trailing slash made the parent-existence check split
* the target off itself, so the create wrongly fell back / returned
* EEXIST instead of creating the directory in the sysroot.
*/
const char *parent = "/tmp/elfuse-trailing-slash";
const char *child = "/tmp/elfuse-trailing-slash/child/";
struct stat st;
rmdir("/tmp/elfuse-trailing-slash/child");
rmdir(parent);
if (mkdir(parent, 0777) < 0 && errno != EEXIST) {
FAIL("parent mkdir failed");
} else if (mkdir(child, 0777) < 0) {
FAIL("trailing-slash mkdir failed");
} else if (stat("/tmp/elfuse-trailing-slash/child", &st) < 0 ||
!S_ISDIR(st.st_mode)) {
FAIL("trailing-slash target is not a directory");
} else {
rmdir("/tmp/elfuse-trailing-slash/child");
rmdir(parent);
PASS();
}
}

TEST("non-sysroot absolute create falls back to host");
{
if (write_file(host_fallback_path, "host-fallback\n") < 0) {
Expand Down
Loading