From 7435db9ff0ca459441d8fe6f8b9eb64725de28e3 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 14 Aug 2009 18:31:35 +0000 Subject: [PATCH] * _user_read_dir(): Allocate a temporary heap buffer passed into the kernel instead of using the user buffer. This frees the VFS and FS implementations from handling user buffers. * Adjusted fix_dirent() accordingly. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32384 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/fs/fd.cpp | 63 +++++++++++++++++++++++++----------- src/system/kernel/fs/vfs.cpp | 46 ++++---------------------- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/system/kernel/fs/fd.cpp b/src/system/kernel/fs/fd.cpp index 63342cec6d..ad2ebf6279 100644 --- a/src/system/kernel/fs/fd.cpp +++ b/src/system/kernel/fs/fd.cpp @@ -31,6 +31,9 @@ #endif +static const size_t kMaxReadDirBufferSize = 64 * 1024; + + static struct file_descriptor* get_fd_locked(struct io_context* context, int fd); static struct file_descriptor* remove_fd(struct io_context* context, int fd); @@ -908,34 +911,56 @@ _user_ioctl(int fd, ulong op, void* buffer, size_t length) ssize_t -_user_read_dir(int fd, struct dirent* buffer, size_t bufferSize, +_user_read_dir(int fd, struct dirent* userBuffer, size_t bufferSize, uint32 maxCount) { - struct file_descriptor* descriptor; - ssize_t retval; + TRACE(("user_read_dir(fd = %d, userBuffer = %p, bufferSize = %ld, count = " + "%lu)\n", fd, userBuffer, bufferSize, maxCount)); - if (!IS_USER_ADDRESS(buffer)) + if (maxCount == 0) + return 0; + + if (userBuffer == NULL || !IS_USER_ADDRESS(userBuffer)) return B_BAD_ADDRESS; - TRACE(("user_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = " - "%lu)\n", fd, buffer, bufferSize, maxCount)); - - struct io_context* ioContext = get_current_io_context(false); - descriptor = get_fd(ioContext, fd); + // get I/O context and FD + io_context* ioContext = get_current_io_context(false); + FDGetter fdGetter; + struct file_descriptor* descriptor = fdGetter.SetTo(ioContext, fd, false); if (descriptor == NULL) return B_FILE_ERROR; - if (descriptor->ops->fd_read_dir) { - uint32 count = maxCount; - retval = descriptor->ops->fd_read_dir(ioContext, descriptor, buffer, - bufferSize, &count); - if (retval >= 0) - retval = count; - } else - retval = EOPNOTSUPP; + if (descriptor->ops->fd_read_dir == NULL) + return B_UNSUPPORTED; - put_fd(descriptor); - return retval; + // restrict buffer size and allocate a heap buffer + if (bufferSize > kMaxReadDirBufferSize) + bufferSize = kMaxReadDirBufferSize; + struct dirent* buffer = (struct dirent*)malloc(bufferSize); + if (buffer == NULL) + return B_NO_MEMORY; + MemoryDeleter bufferDeleter(buffer); + + // read the directory + uint32 count = maxCount; + status_t status = descriptor->ops->fd_read_dir(ioContext, descriptor, + buffer, bufferSize, &count); + if (status != B_OK) + return status; + + // copy the buffer back -- determine the total buffer size first + size_t sizeToCopy = 0; + struct dirent* entry = buffer; + for (uint32 i = 0; i < count; i++) { + size_t length = entry->d_reclen; + sizeToCopy += length; + entry = (struct dirent*)((uint8*)entry + length); + } + + if (user_memcpy(userBuffer, buffer, sizeToCopy) != B_OK) + return B_BAD_ADDRESS; + + return count; } diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 424739b52a..d660cc4bae 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -5550,34 +5550,9 @@ dir_read(struct io_context* ioContext, struct file_descriptor* descriptor, static status_t -fix_dirent(struct vnode* parent, struct dirent* userEntry, - struct io_context* ioContext, uint32* _length) +fix_dirent(struct vnode* parent, struct dirent* entry, + struct io_context* ioContext) { - char buffer[sizeof(struct dirent) + B_FILE_NAME_LENGTH]; - struct dirent* entry; - - if (IS_USER_ADDRESS(userEntry)) { - entry = (struct dirent*)buffer; - if (user_memcpy(entry, userEntry, sizeof(struct dirent) - 1) != B_OK) - return B_BAD_ADDRESS; - - ASSERT(entry->d_reclen >= sizeof(struct dirent) - && entry->d_reclen <= sizeof(buffer)); - - // This hints to a problem in the file system implementation, but - // could also be caused by a malicious user application - if (entry->d_reclen < sizeof(struct dirent) - || entry->d_reclen > sizeof(buffer)) - return B_BAD_DATA; - - if (user_memcpy(entry->d_name, userEntry->d_name, - entry->d_reclen + 1 - sizeof(struct dirent)) != B_OK) - return B_BAD_ADDRESS; - } else - entry = userEntry; - - *_length = entry->d_reclen; - // set d_pdev and d_pino entry->d_pdev = parent->device; entry->d_pino = parent->id; @@ -5619,10 +5594,6 @@ fix_dirent(struct vnode* parent, struct dirent* userEntry, } } - // copy back from userland buffer if needed - if (entry != userEntry) - return user_memcpy(userEntry, entry, sizeof(struct dirent) - 1); - return B_OK; } @@ -5641,15 +5612,12 @@ dir_read(struct io_context* ioContext, struct vnode* vnode, void* cookie, // we need to adjust the read dirents uint32 count = *_count; - if (count > 0) { - for (uint32 i = 0; i < count; i++) { - uint32 length; - error = fix_dirent(vnode, buffer, ioContext, &length); - if (error != B_OK) - return error; + for (uint32 i = 0; i < count; i++) { + error = fix_dirent(vnode, buffer, ioContext); + if (error != B_OK) + return error; - buffer = (struct dirent*)((uint8*)buffer + length); - } + buffer = (struct dirent*)((uint8*)buffer + buffer->d_reclen); } return error;