* _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:
parent
0573d397e1
commit
7435db9ff0
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user