* _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
This commit is contained in:
Ingo Weinhold
2009-08-14 18:31:35 +00:00
parent 0573d397e1
commit 7435db9ff0
2 changed files with 51 additions and 58 deletions
+44 -19
View File
@@ -31,6 +31,9 @@
#endif #endif
static const size_t kMaxReadDirBufferSize = 64 * 1024;
static struct file_descriptor* get_fd_locked(struct io_context* context, static struct file_descriptor* get_fd_locked(struct io_context* context,
int fd); int fd);
static struct file_descriptor* remove_fd(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 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) uint32 maxCount)
{ {
struct file_descriptor* descriptor; TRACE(("user_read_dir(fd = %d, userBuffer = %p, bufferSize = %ld, count = "
ssize_t retval; "%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; return B_BAD_ADDRESS;
TRACE(("user_read_dir(fd = %d, buffer = %p, bufferSize = %ld, count = " // get I/O context and FD
"%lu)\n", fd, buffer, bufferSize, maxCount)); io_context* ioContext = get_current_io_context(false);
FDGetter fdGetter;
struct io_context* ioContext = get_current_io_context(false); struct file_descriptor* descriptor = fdGetter.SetTo(ioContext, fd, false);
descriptor = get_fd(ioContext, fd);
if (descriptor == NULL) if (descriptor == NULL)
return B_FILE_ERROR; return B_FILE_ERROR;
if (descriptor->ops->fd_read_dir) { if (descriptor->ops->fd_read_dir == NULL)
uint32 count = maxCount; return B_UNSUPPORTED;
retval = descriptor->ops->fd_read_dir(ioContext, descriptor, buffer,
bufferSize, &count);
if (retval >= 0)
retval = count;
} else
retval = EOPNOTSUPP;
put_fd(descriptor); // restrict buffer size and allocate a heap buffer
return retval; 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;
} }
+7 -39
View File
@@ -5550,34 +5550,9 @@ dir_read(struct io_context* ioContext, struct file_descriptor* descriptor,
static status_t static status_t
fix_dirent(struct vnode* parent, struct dirent* userEntry, fix_dirent(struct vnode* parent, struct dirent* entry,
struct io_context* ioContext, uint32* _length) 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 // set d_pdev and d_pino
entry->d_pdev = parent->device; entry->d_pdev = parent->device;
entry->d_pino = parent->id; 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; 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 // we need to adjust the read dirents
uint32 count = *_count; uint32 count = *_count;
if (count > 0) { for (uint32 i = 0; i < count; i++) {
for (uint32 i = 0; i < count; i++) { error = fix_dirent(vnode, buffer, ioContext);
uint32 length; if (error != B_OK)
error = fix_dirent(vnode, buffer, ioContext, &length); return error;
if (error != B_OK)
return error;
buffer = (struct dirent*)((uint8*)buffer + length); buffer = (struct dirent*)((uint8*)buffer + buffer->d_reclen);
}
} }
return error; return error;