diff --git a/src/kernel/core/fs/fd.c b/src/kernel/core/fs/fd.c index bb0d604450..a1219efa90 100644 --- a/src/kernel/core/fs/fd.c +++ b/src/kernel/core/fs/fd.c @@ -448,12 +448,15 @@ user_rewind_dir(int fd) } -int -user_read_stat(int fd, struct stat *userStat) +status_t +_user_read_stat(int fd, struct stat *userStat, size_t statSize) { struct file_descriptor *descriptor; status_t status; + if (statSize > sizeof(struct stat)) + return B_BAD_VALUE; + /* This is a user_function, so abort if we have a kernel address */ CHECK_USER_ADDR(userStat) @@ -461,7 +464,7 @@ user_read_stat(int fd, struct stat *userStat) if (descriptor == NULL) return B_FILE_ERROR; - TRACE(("user_read_stat(descriptor = %p)\n", descriptor)); + TRACE(("_user_read_stat(descriptor = %p)\n", descriptor)); if (descriptor->ops->fd_read_stat) { // we're using the stat buffer on the stack to not have to @@ -470,7 +473,7 @@ user_read_stat(int fd, struct stat *userStat) status = descriptor->ops->fd_read_stat(descriptor, &stat); if (status >= 0) - status = user_memcpy(userStat, &stat, sizeof(stat)); + status = user_memcpy(userStat, &stat, statSize); } else status = EOPNOTSUPP; @@ -479,25 +482,31 @@ user_read_stat(int fd, struct stat *userStat) } -int -user_write_stat(int fd, const struct stat *userStat, int statMask) +status_t +_user_write_stat(int fd, const struct stat *userStat, size_t statSize, int statMask) { struct file_descriptor *descriptor; status_t status; + if (statSize > sizeof(struct stat)) + return B_BAD_VALUE; + CHECK_USER_ADDR(userStat) descriptor = get_fd(get_current_io_context(false), fd); if (descriptor == NULL) return B_FILE_ERROR; - TRACE(("user_write_stat(descriptor = %p)\n", descriptor)); + TRACE(("_user_write_stat(descriptor = %p)\n", descriptor)); if (descriptor->ops->fd_write_stat) { // we're using the stat buffer on the stack to not have to // lock the given stat buffer in memory struct stat stat; - status = user_memcpy(&stat, userStat, sizeof(stat)); + if (sizeof(struct stat) != statSize) + memset((uint8 *)&stat + statSize, 0, sizeof(struct stat) - statSize); + + status = user_memcpy(&stat, userStat, statSize); if (status == B_OK) status = descriptor->ops->fd_write_stat(descriptor, &stat, statMask); } else @@ -678,21 +687,35 @@ sys_rewind_dir(int fd) } -int -sys_read_stat(int fd, struct stat *stat) +status_t +_kern_read_stat(int fd, struct stat *stat, size_t statSize) { struct file_descriptor *descriptor; status_t status; + if (statSize > sizeof(struct stat)) + return B_BAD_VALUE; + descriptor = get_fd(get_current_io_context(true), fd); if (descriptor == NULL) return B_FILE_ERROR; - TRACE(("sys_read_stat(descriptor = %p)\n", descriptor)); + TRACE(("_kern_read_stat(descriptor = %p)\n", descriptor)); - if (descriptor->ops->fd_read_stat) + if (descriptor->ops->fd_read_stat) { + // this supports different stat extensions + struct stat completeStat; + struct stat *originalStat = NULL; + + if (statSize < sizeof(struct stat)) { + originalStat = stat; + stat = &completeStat; + } status = descriptor->ops->fd_read_stat(descriptor, stat); - else + + if (originalStat != NULL) + memcpy(originalStat, stat, statSize); + } else status = EOPNOTSUPP; put_fd(descriptor); @@ -700,21 +723,32 @@ sys_read_stat(int fd, struct stat *stat) } -int -sys_write_stat(int fd, const struct stat *stat, int statMask) +status_t +_kern_write_stat(int fd, const struct stat *stat, size_t statSize, int statMask) { struct file_descriptor *descriptor; status_t status; + if (statSize > sizeof(struct stat)) + return B_BAD_VALUE; + descriptor = get_fd(get_current_io_context(true), fd); if (descriptor == NULL) return B_FILE_ERROR; - TRACE(("sys_write_stat(descriptor = %p)\n", descriptor)); + TRACE(("_kern_write_stat(descriptor = %p)\n", descriptor)); - if (descriptor->ops->fd_write_stat) + if (descriptor->ops->fd_write_stat) { + // this supports different stat extensions + struct stat completeStat; + + if (statSize < sizeof(struct stat)) { + memset((uint8 *)&completeStat + statSize, 0, sizeof(struct stat) - statSize); + memcpy(&completeStat, stat, statSize); + stat = &completeStat; + } status = descriptor->ops->fd_write_stat(descriptor, stat, statMask); - else + } else status = EOPNOTSUPP; put_fd(descriptor); diff --git a/src/kernel/core/fs/vfs.cpp b/src/kernel/core/fs/vfs.cpp index 78d72f971d..4e55b23876 100755 --- a/src/kernel/core/fs/vfs.cpp +++ b/src/kernel/core/fs/vfs.cpp @@ -3731,22 +3731,52 @@ sys_access(const char *path, int mode) } -int -sys_read_path_stat(const char *path, bool traverseLeafLink, struct stat *stat) +status_t +_kern_read_path_stat(const char *path, bool traverseLeafLink, struct stat *stat, size_t statSize) { + struct stat completeStat; + struct stat *originalStat = NULL; char pathBuffer[SYS_MAX_PATH_LEN + 1]; + status_t status; + + if (statSize > sizeof(struct stat)) + return B_BAD_VALUE; + strlcpy(pathBuffer, path, SYS_MAX_PATH_LEN - 1); - return common_path_read_stat(pathBuffer, traverseLeafLink, stat, true); + // this supports different stat extensions + if (statSize < sizeof(struct stat)) { + originalStat = stat; + stat = &completeStat; + } + + status = common_path_read_stat(pathBuffer, traverseLeafLink, stat, true); + if (status == B_OK && originalStat != NULL) + memcpy(originalStat, stat, statSize); + + return status; } -int -sys_write_path_stat(const char *path, bool traverseLeafLink, const struct stat *stat, int statMask) +status_t +_kern_write_path_stat(const char *path, bool traverseLeafLink, const struct stat *stat, size_t statSize, int statMask) { + struct stat completeStat; char pathBuffer[SYS_MAX_PATH_LEN + 1]; + + if (statSize > sizeof(struct stat)) + return B_BAD_VALUE; + strlcpy(pathBuffer, path, SYS_MAX_PATH_LEN - 1); + // this supports different stat extensions + + if (statSize < sizeof(struct stat)) { + memset((uint8 *)&completeStat + statSize, 0, sizeof(struct stat) - statSize); + memcpy(&completeStat, stat, statSize); + stat = &completeStat; + } + return common_path_write_stat(pathBuffer, traverseLeafLink, stat, statMask, true); } @@ -4281,38 +4311,50 @@ user_access(const char *userPath, int mode) } -int -user_read_path_stat(const char *userPath, bool traverseLink, struct stat *userStat) +status_t +_user_read_path_stat(const char *userPath, bool traverseLink, struct stat *userStat, + size_t statSize) { char path[SYS_MAX_PATH_LEN + 1]; struct stat stat; int status; + if (statSize > sizeof(struct stat)) + return B_BAD_VALUE; + if (!IS_USER_ADDRESS(userPath) || !IS_USER_ADDRESS(userStat) || user_strlcpy(path, userPath, SYS_MAX_PATH_LEN) < B_OK) return B_BAD_ADDRESS; status = common_path_read_stat(path, traverseLink, &stat, false); - if (status < 0) + if (status < B_OK) return status; - return user_memcpy(userStat, &stat, sizeof(struct stat)); + return user_memcpy(userStat, &stat, statSize); } -int -user_write_path_stat(const char *userPath, bool traverseLeafLink, const struct stat *userStat, int statMask) +status_t +_user_write_path_stat(const char *userPath, bool traverseLeafLink, const struct stat *userStat, + size_t statSize, int statMask) { char path[SYS_MAX_PATH_LEN + 1]; struct stat stat; + if (statSize > sizeof(struct stat)) + return B_BAD_VALUE; + if (!IS_USER_ADDRESS(userStat) || !IS_USER_ADDRESS(userPath) || user_strlcpy(path, userPath, SYS_MAX_PATH_LEN) < B_OK - || user_memcpy(&stat, userStat, sizeof(struct stat)) < B_OK) + || user_memcpy(&stat, userStat, statSize) < B_OK) return B_BAD_ADDRESS; + // clear additional stat fields + if (statSize < sizeof(struct stat)) + memset((uint8 *)&stat + statSize, 0, sizeof(struct stat) - statSize); + return common_path_write_stat(path, traverseLeafLink, &stat, statMask, false); } diff --git a/src/kernel/core/syscalls.c b/src/kernel/core/syscalls.c index 0e2f3e6e97..818372e0fd 100644 --- a/src/kernel/core/syscalls.c +++ b/src/kernel/core/syscalls.c @@ -140,16 +140,16 @@ syscall_dispatcher(unsigned long call_num, void *arg_buffer, uint64 *call_ret) *call_ret = user_rename((const char *)arg0, (const char *)arg1); break; case SYSCALL_READ_PATH_STAT: - *call_ret = user_read_path_stat((const char *)arg0, (bool)arg1, (struct stat *)arg2); + *call_ret = _user_read_path_stat((const char *)arg0, (bool)arg1, (struct stat *)arg2, (size_t)arg3); break; case SYSCALL_WRITE_PATH_STAT: - *call_ret = user_write_path_stat((const char *)arg0, (bool)arg1, (const struct stat *)arg2, (int)arg3); + *call_ret = _user_write_path_stat((const char *)arg0, (bool)arg1, (const struct stat *)arg2, (size_t)arg3, (int)arg4); break; case SYSCALL_READ_STAT: - *call_ret = user_read_stat((int)arg0, (struct stat*)arg1); + *call_ret = _user_read_stat((int)arg0, (struct stat *)arg1, (size_t)arg2); break; case SYSCALL_WRITE_STAT: - *call_ret = user_write_stat((int)arg0, (const struct stat *)arg1, (int)arg2); + *call_ret = _user_write_stat((int)arg0, (const struct stat *)arg1, (size_t)arg2, (int)arg3); break; case SYSCALL_ACCESS: *call_ret = user_access((const char *)arg0, (int)arg1);