From 14833f3243b1bd69819e48a707001c4f55056f24 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Mon, 23 May 2022 18:35:12 -0700 Subject: [PATCH] Handle AT_EMPTY_PATH for fstatat64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is used by newer glibc when loading shared objects. AT_EMPTY_PATH (since Linux 2.6.39) If pathname is an empty string, operate on the file referred to by dirfd (which may have been ob‐ tained using the open(2) O_PATH flag). In this case, dirfd can refer to any type of file, not just a directory, and the behavior of fstatat() is similar to that of fstat(). If dirfd is AT_FDCWD, the call operates on the current working directory. This flag is Linux-specific; de‐ fine _GNU_SOURCE to obtain its definition. --- fs/mount.c | 2 +- fs/stat.c | 41 +++++++++++++++++++++++++++++++---------- kernel/fs.c | 2 +- kernel/fs.h | 3 ++- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/fs/mount.c b/fs/mount.c index 9763f346c6..dd85b9d603 100644 --- a/fs/mount.c +++ b/fs/mount.c @@ -155,7 +155,7 @@ dword_t sys_mount(addr_t source_addr, addr_t point_addr, addr_t type_addr, dword return _EINVAL; struct statbuf stat; - int err = generic_statat(AT_PWD, point_raw, &stat, true); + int err = generic_statat(AT_PWD, point_raw, &stat, 0); if (err < 0) return err; if (!S_ISDIR(stat.mode)) diff --git a/fs/stat.c b/fs/stat.c index d277bae4f5..33fd566edd 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -30,11 +30,31 @@ struct newstat64 stat_convert_newstat64(struct statbuf stat) { return newstat; } -int generic_statat(struct fd *at, const char *path_raw, struct statbuf *stat, bool follow_links) { +int generic_statat(struct fd *at, const char *path_raw, struct statbuf *stat, int flags) { + int err; + + bool empty_path = flags & AT_EMPTY_PATH_; + bool follow_links = !(flags & AT_SYMLINK_NOFOLLOW_); + char path[MAX_PATH]; - int err = path_normalize(at, path_raw, path, follow_links ? N_SYMLINK_FOLLOW : N_SYMLINK_NOFOLLOW); - if (err < 0) - return err; + if (empty_path && (strcmp(path_raw, "") == 0)) { + if (at == AT_PWD) { + at = current->fs->pwd; + } + + err = generic_getpath(at, path); + if (err < 0) + return err; + + if (!path_is_normalized(path)) { + return _ENOENT; + } + } else { + err = path_normalize(at, path_raw, path, follow_links ? N_SYMLINK_FOLLOW : N_SYMLINK_NOFOLLOW); + if (err < 0) + return err; + } + struct mount *mount = find_mount_and_trim_path(path); memset(stat, 0, sizeof(*stat)); err = mount->fs->stat(mount, path, stat); @@ -49,17 +69,18 @@ static struct fd *at_fd(fd_t f) { return f_get(f); } -static dword_t sys_stat_path(fd_t at_f, addr_t path_addr, addr_t statbuf_addr, bool follow_links) { +// The `flags` parameter accepts AT_ flags +static dword_t sys_stat_path(fd_t at_f, addr_t path_addr, addr_t statbuf_addr, int flags) { int err; char path[MAX_PATH]; if (user_read_string(path_addr, path, sizeof(path))) return _EFAULT; - STRACE("stat(at=%d, path=\"%s\", statbuf=0x%x, follow_links=%d)", at_f, path, statbuf_addr, follow_links); + STRACE("stat(at=%d, path=\"%s\", statbuf=0x%x, flags=0x%x)", at_f, path, statbuf_addr, flags); struct fd *at = at_fd(at_f); if (at == NULL) return _EBADF; struct statbuf stat = {}; - if ((err = generic_statat(at, path, &stat, follow_links)) < 0) + if ((err = generic_statat(at, path, &stat, flags)) < 0) return err; struct newstat64 newstat = stat_convert_newstat64(stat); if (user_put(statbuf_addr, newstat)) @@ -68,15 +89,15 @@ static dword_t sys_stat_path(fd_t at_f, addr_t path_addr, addr_t statbuf_addr, b } dword_t sys_stat64(addr_t path_addr, addr_t statbuf_addr) { - return sys_stat_path(AT_FDCWD_, path_addr, statbuf_addr, true); + return sys_stat_path(AT_FDCWD_, path_addr, statbuf_addr, 0); } dword_t sys_lstat64(addr_t path_addr, addr_t statbuf_addr) { - return sys_stat_path(AT_FDCWD_, path_addr, statbuf_addr, false); + return sys_stat_path(AT_FDCWD_, path_addr, statbuf_addr, AT_SYMLINK_NOFOLLOW_); } dword_t sys_fstatat64(fd_t at, addr_t path_addr, addr_t statbuf_addr, dword_t flags) { - return sys_stat_path(at, path_addr, statbuf_addr, !(flags & AT_SYMLINK_NOFOLLOW_)); + return sys_stat_path(at, path_addr, statbuf_addr, flags); } dword_t sys_fstat64(fd_t fd_no, addr_t statbuf_addr) { diff --git a/kernel/fs.c b/kernel/fs.c index df1d2aaaa2..4abedff955 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -561,7 +561,7 @@ dword_t sys_getcwd(addr_t buf_addr, dword_t size) { static struct fd *open_dir(const char *path) { struct statbuf stat; - int err = generic_statat(AT_PWD, path, &stat, true); + int err = generic_statat(AT_PWD, path, &stat, 0); if (err < 0) return ERR_PTR(err); if (!(stat.mode & S_IFDIR)) diff --git a/kernel/fs.h b/kernel/fs.h index 5a8154ccb4..0e662a8b15 100644 --- a/kernel/fs.h +++ b/kernel/fs.h @@ -44,6 +44,7 @@ struct attr { #define make_attr(_type, thing) \ ((struct attr) {.type = attr_##_type, ._type = thing}) +#define AT_EMPTY_PATH_ 0x1000 #define AT_SYMLINK_NOFOLLOW_ 0x100 struct fd *generic_open(const char *path, int flags, int mode); @@ -61,7 +62,7 @@ int generic_seek(struct fd *fd, off_t_ off, int whence, size_t size); #define AC_X 1 #define AC_F 0 int generic_accessat(struct fd *dirfd, const char *path, int mode); -int generic_statat(struct fd *at, const char *path, struct statbuf *stat, bool follow_links); +int generic_statat(struct fd *at, const char *path, struct statbuf *stat, int flags); int generic_setattrat(struct fd *at, const char *path, struct attr attr, bool follow_links); int generic_utime(struct fd *at, const char *path, struct timespec atime, struct timespec mtime, bool follow_links); ssize_t generic_readlinkat(struct fd *at, const char *path, char *buf, size_t bufsize);