diff --git a/LibOS/shim/include/shim_table.h b/LibOS/shim/include/shim_table.h index d8dfb5a9..059adaca 100644 --- a/LibOS/shim/include/shim_table.h +++ b/LibOS/shim/include/shim_table.h @@ -410,7 +410,7 @@ int shim_do_mkdir(const char* pathname, int mode); int shim_do_rmdir(const char* pathname); int shim_do_creat(const char* path, mode_t mode); int shim_do_unlink(const char* file); -int shim_do_readlink(const char* file, char* buf, size_t bufsize); +int shim_do_readlink(const char* file, char* buf, int bufsize); int shim_do_chmod(const char* filename, mode_t mode); int shim_do_fchmod(int fd, mode_t mode); int shim_do_chown(const char* filename, uid_t user, gid_t group); @@ -476,6 +476,7 @@ int shim_do_openat(int dfd, const char* filename, int flags, int mode); int shim_do_mkdirat(int dfd, const char* pathname, int mode); int shim_do_newfstatat(int dirfd, const char* pathname, struct stat* statbuf, int flags); int shim_do_unlinkat(int dfd, const char* pathname, int flag); +int shim_do_readlinkat(int dirfd, const char* file, char* buf, int bufsize); int shim_do_renameat(int olddfd, const char* pathname, int newdfd, const char* newname); int shim_do_fchmodat(int dfd, const char* filename, mode_t mode); int shim_do_fchownat(int dfd, const char* filename, uid_t user, gid_t group, int flags); @@ -599,7 +600,7 @@ int shim_creat(const char* path, mode_t mode); int shim_link(const char* oldname, const char* newname); int shim_unlink(const char* file); int shim_symlink(const char* old, const char* new); -int shim_readlink(const char* file, char* buf, size_t bufsize); +int shim_readlink(const char* file, char* buf, int bufsize); int shim_chmod(const char* filename, mode_t mode); int shim_fchmod(int fd, mode_t mode); int shim_chown(const char* filename, uid_t user, gid_t group); diff --git a/LibOS/shim/src/shim_syscalls.c b/LibOS/shim/src/shim_syscalls.c index 68fb8a94..ad6d79f6 100644 --- a/LibOS/shim/src/shim_syscalls.c +++ b/LibOS/shim/src/shim_syscalls.c @@ -392,7 +392,7 @@ DEFINE_SHIM_SYSCALL(unlink, 1, shim_do_unlink, int, const char*, file) SHIM_SYSCALL_RETURN_ENOSYS(symlink, 2, int, const char*, old, const char*, new) /* readlink: sys/shim_stat.c */ -DEFINE_SHIM_SYSCALL(readlink, 3, shim_do_readlink, int, const char*, path, char*, buf, size_t, +DEFINE_SHIM_SYSCALL(readlink, 3, shim_do_readlink, int, const char*, path, char*, buf, int, bufsize) DEFINE_SHIM_SYSCALL(chmod, 2, shim_do_chmod, int, const char*, filename, mode_t, mode) @@ -904,7 +904,8 @@ SHIM_SYSCALL_RETURN_ENOSYS(linkat, 5, int, int, olddfd, const char*, oldname, in SHIM_SYSCALL_RETURN_ENOSYS(symlinkat, 3, int, const char*, oldname, int, newdfd, const char*, newname) -SHIM_SYSCALL_RETURN_ENOSYS(readlinkat, 4, int, int, dfd, const char*, path, char*, buf, int, bufsiz) +DEFINE_SHIM_SYSCALL(readlinkat, 4, shim_do_readlinkat, int, int, dfd, const char*, path, char*, buf, + int, bufsiz) /* fchmodat: sys/shim_fs.c */ DEFINE_SHIM_SYSCALL(fchmodat, 3, shim_do_fchmodat, int, int, dfd, const char*, filename, mode_t, diff --git a/LibOS/shim/src/sys/shim_stat.c b/LibOS/shim/src/sys/shim_stat.c index c1fcacb4..2c5ae7e0 100644 --- a/LibOS/shim/src/sys/shim_stat.c +++ b/LibOS/shim/src/sys/shim_stat.c @@ -92,22 +92,27 @@ out: return ret; } -int shim_do_readlink(const char* file, char* buf, size_t bufsize) { +int shim_do_readlinkat(int dirfd, const char* file, char* buf, int bufsize) { if (!file || test_user_string(file)) return -EFAULT; - if (!buf || !bufsize || test_user_memory(buf, bufsize, true)) - return -EFAULT; - if (bufsize <= 0) return -EINVAL; - int ret; + if (test_user_memory(buf, bufsize, true)) + return -EFAULT; + struct shim_dentry* dent = NULL; + struct shim_dentry* dir = NULL; + int ret = get_dirfd_dentry(dirfd, &dir); + if (ret < 0) { + goto out; + } + struct shim_qstr qstr = QSTR_INIT; - if ((ret = path_lookupat(NULL, file, LOOKUP_ACCESS, &dent, NULL)) < 0) - return ret; + if ((ret = path_lookupat(dir, file, LOOKUP_ACCESS, &dent, NULL)) < 0) + goto out; ret = -EINVAL; /* The correct behavior is to return -EINVAL if file is not a @@ -122,17 +127,25 @@ int shim_do_readlink(const char* file, char* buf, size_t bufsize) { if (ret < 0) goto out; - ret = -ENAMETOOLONG; - if (qstr.len >= bufsize) - goto out; + ret = bufsize; + if (qstr.len < (size_t)bufsize) + ret = qstr.len; - memcpy(buf, qstrgetstr(&qstr), qstr.len); - ret = qstr.len; + memcpy(buf, qstrgetstr(&qstr), ret); out: - put_dentry(dent); + if (dent) { + put_dentry(dent); + } + if (dir) { + put_dentry(dir); + } return ret; } +int shim_do_readlink(const char* file, char* buf, int bufsize) { + return shim_do_readlinkat(AT_FDCWD, file, buf, bufsize); +} + static int __do_statfs(struct shim_mount* fs, struct statfs* buf) { __UNUSED(fs); if (!buf || test_user_memory(buf, sizeof(*buf), true)) diff --git a/LibOS/shim/test/regression/proc_common.c b/LibOS/shim/test/regression/proc_common.c index 6bbef384..6c573259 100644 --- a/LibOS/shim/test/regression/proc_common.c +++ b/LibOS/shim/test/regression/proc_common.c @@ -1,4 +1,4 @@ -#define _XOPEN_SOURCE 700 +#define _GNU_SOURCE #include #include #include @@ -133,12 +133,23 @@ int main(int argc, char** argv) { } printf("===== Reading /proc/self/exe symlink\n"); - ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + int proc_dirfd = open("/proc", O_DIRECTORY | O_PATH | O_RDONLY); + if (proc_dirfd < 0) { + perror("open /proc"); + return 1; + } + + ssize_t len = readlinkat(proc_dirfd, "self/exe", buf, sizeof(buf) - 1); if (len < 0) { perror("readlink /proc/self/exe"); return 1; } + if (close(proc_dirfd) < 0) { + perror("close proc_dirfd"); + return 1; + } + buf[len] = '\0'; printf("symlink /proc/self/exec resolves to %s\n", buf);