diff --git a/src/syscall/fs-stat.c b/src/syscall/fs-stat.c index ddcbe9e..6a7dd2e 100644 --- a/src/syscall/fs-stat.c +++ b/src/syscall/fs-stat.c @@ -367,6 +367,20 @@ static bool statfs_path_is_proc(const char *path) return !strncmp(path, "/proc", 5) && (path[5] == '\0' || path[5] == '/'); } +static void fill_proc_statfs(linux_statfs_t *lin) +{ + memset(lin, 0, sizeof(*lin)); + lin->f_type = 0x9fa0; /* PROC_SUPER_MAGIC */ + lin->f_bsize = 4096; + lin->f_blocks = 0; + lin->f_bfree = 0; + lin->f_bavail = 0; + lin->f_files = 0; + lin->f_ffree = 0; + lin->f_namelen = 255; + lin->f_frsize = 4096; +} + int64_t sys_statfs(guest_t *g, uint64_t path_gva, uint64_t buf_gva) { char path[LINUX_PATH_MAX]; @@ -379,38 +393,18 @@ int64_t sys_statfs(guest_t *g, uint64_t path_gva, uint64_t buf_gva) if (tx.fuse_path) return -LINUX_ENOSYS; - struct statfs mac_st; - - /* /proc has no host-filesystem counterpart under the sysroot: every entry - * is synthesized on open (see proc_intercept_open), so a raw statfs() on - * the guest path always misses with ENOENT even for "/proc" itself. Try - * the lightweight statfs-specific resolver first (it shortcuts nodes whose - * open path would otherwise allocate scratch state statfs never uses), - * then fall back to the same intercept open/fstatfs/close that - * sys_newfstatat uses via proc_intercept_stat. - */ if (statfs_path_is_proc(tx.intercept_path)) { - int intercepted = proc_intercept_statfs(tx.intercept_path, &mac_st); - if (intercepted == PROC_NOT_INTERCEPTED) { - int host_fd = proc_intercept_open(g, tx.intercept_path, 0, 0); - if (host_fd == PROC_NOT_INTERCEPTED) { - if (statfs(tx.host_path, &mac_st) < 0) - return linux_errno(); - } else if (host_fd < 0) { - return linux_errno(); - } else { - int rc = fstatfs(host_fd, &mac_st); - close_keep_errno(host_fd); - if (rc < 0) - return linux_errno(); - } - } else if (intercepted < 0) { - return linux_errno(); - } - } else if (statfs(tx.host_path, &mac_st) < 0) { - return linux_errno(); + linux_statfs_t lin_st; + fill_proc_statfs(&lin_st); + if (guest_write_small(g, buf_gva, &lin_st, sizeof(lin_st)) < 0) + return -LINUX_EFAULT; + return 0; } + struct statfs mac_st; + if (statfs(tx.host_path, &mac_st) < 0) + return linux_errno(); + linux_statfs_t lin_st; translate_statfs(&mac_st, &lin_st); if (guest_write_small(g, buf_gva, &lin_st, sizeof(lin_st)) < 0) @@ -421,6 +415,16 @@ int64_t sys_statfs(guest_t *g, uint64_t path_gva, uint64_t buf_gva) int64_t sys_fstatfs(guest_t *g, int fd, uint64_t buf_gva) { + fd_entry_t snap; + memset(&snap, 0, sizeof(snap)); + if (fd_snapshot(fd, &snap) && statfs_path_is_proc(snap.proc_path)) { + linux_statfs_t proc_st; + fill_proc_statfs(&proc_st); + if (guest_write_small(g, buf_gva, &proc_st, sizeof(proc_st)) < 0) + return -LINUX_EFAULT; + return 0; + } + host_fd_ref_t host_ref; if (host_fd_ref_open(fd, &host_ref) < 0) return -LINUX_EBADF; diff --git a/src/syscall/fs.c b/src/syscall/fs.c index 6213d1f..20a3e2f 100644 --- a/src/syscall/fs.c +++ b/src/syscall/fs.c @@ -127,6 +127,43 @@ static const char *proc_stateful_file_path(const char *path) return NULL; } +static bool proc_path_is_symlink(const char *path) +{ + if (!path) + return false; + + if (!strcmp(path, "/proc/self/exe") || !strcmp(path, "/proc/self/cwd") || + !strcmp(path, "/proc/self/root")) { + return true; + } + + if (!strncmp(path, "/proc/self/fd/", 14)) { + char *endp; + long n = strtol(path + 14, &endp, 10); + if (endp != path + 14 && *endp == '\0' && n >= 0) + return true; + } + + if (!strncmp(path, "/proc/self/task/", 16)) { + char *endp; + strtol(path + 16, &endp, 10); + if (endp != path + 16 && *endp == '/') { + const char *sub = endp + 1; + if (!strcmp(sub, "exe") || !strcmp(sub, "cwd") || + !strcmp(sub, "root")) { + return true; + } + if (!strncmp(sub, "fd/", 3)) { + long n = strtol(sub + 3, &endp, 10); + if (endp != sub + 3 && *endp == '\0' && n >= 0) + return true; + } + } + } + + return false; +} + /* Resolve the proc_path the fd table should record for an intercepted path. * Returns true and fills *out when a mapping exists; false otherwise so the * caller can skip the install entirely. Pure string work; safe to call before @@ -149,11 +186,32 @@ static bool resolve_virtual_path(const char *path, char *out, size_t out_size) const char *virt = proc_virtual_dir_path(path, virt_buf, sizeof(virt_buf)); if (!virt) virt = proc_stateful_file_path(path); - if (!virt) - return false; - str_copy_trunc(out, virt, out_size); - return true; + if (virt) { + str_copy_trunc(out, virt, out_size); + return true; + } + + /* If it has a valid /proc prefix, normalize it and record it. */ + if (path[5] == '\0' || path[5] == '/') { + if (strncmp(path, "/proc/", 6) == 0) { + char *endp; + long pid = strtol(path + 6, &endp, 10); + if (endp != path + 6 && pid == (long) proc_get_pid() && + (*endp == '\0' || *endp == '/')) { + snprintf(out, out_size, "/proc/self%s", endp); + if (proc_path_is_symlink(out)) + return false; + return true; + } + } + if (proc_path_is_symlink(path)) + return false; + str_copy_trunc(out, path, out_size); + return true; + } + + return false; } static const char *proc_virtual_dir_path(const char *path,