* _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

View File

@ -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;
}

View File

@ -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;