Fix getdents when the next dir entry cannot fit into the output buffer
The output buffer given to getdents may not be large enough for the next directory entry. If no directory entries has been loaded into the buffer, just return EINVAL. Otherwise, return the total length of the directory entries already loaded in the buffer
This commit is contained in:
parent
6d7597c25e
commit
b610e5b8b8
@ -206,9 +206,13 @@ pub fn do_getdents64(fd: FileDesc, buf: &mut [u8]) -> Result<usize> {
|
||||
Ok(name) => name,
|
||||
};
|
||||
// TODO: get ino from dirent
|
||||
let ok = writer.try_write(0, 0, &name);
|
||||
if !ok {
|
||||
return_errno!(EINVAL, "the given buffer is too small");
|
||||
if let Err(e) = writer.try_write(0, 0, &name) {
|
||||
file_ref.seek(SeekFrom::Current(-1))?;
|
||||
if writer.written_size == 0 {
|
||||
return Err(e);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(writer.written_size)
|
||||
@ -570,11 +574,11 @@ impl<'a> DirentBufWriter<'a> {
|
||||
written_size: 0,
|
||||
}
|
||||
}
|
||||
fn try_write(&mut self, inode: u64, type_: u8, name: &str) -> bool {
|
||||
fn try_write(&mut self, inode: u64, type_: u8, name: &str) -> Result<()> {
|
||||
let len = ::core::mem::size_of::<LinuxDirent64>() + name.len() + 1;
|
||||
let len = (len + 7) / 8 * 8; // align up
|
||||
if self.rest_size < len {
|
||||
return false;
|
||||
return_errno!(EINVAL, "the given buffer is too small");
|
||||
}
|
||||
let dent = LinuxDirent64 {
|
||||
ino: inode,
|
||||
@ -591,7 +595,7 @@ impl<'a> DirentBufWriter<'a> {
|
||||
}
|
||||
self.rest_size -= len;
|
||||
self.written_size += len;
|
||||
true
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,89 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "test.h"
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
DIR* dirp = opendir("/");
|
||||
// ============================================================================
|
||||
// The test case of readdir
|
||||
// ============================================================================
|
||||
|
||||
static int test_readdir() {
|
||||
struct dirent *dp;
|
||||
DIR* dirp;
|
||||
|
||||
dirp = opendir("/");
|
||||
if (dirp == NULL) {
|
||||
printf("failed to open directory at /\n");
|
||||
return -1;
|
||||
THROW_ERROR("failed to open directory");
|
||||
}
|
||||
while (1) {
|
||||
errno = 0;
|
||||
dp = readdir(dirp);
|
||||
if (dp == NULL) {
|
||||
if (errno != 0) {
|
||||
closedir(dirp);
|
||||
THROW_ERROR("faild to call readdir");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct dirent *dp;
|
||||
while ((dp = readdir(dirp)) != NULL) {
|
||||
printf("get: %s\n", dp->d_name);
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
|
||||
printf("Read directory test successful\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_getdents_with_big_enough_buffer() {
|
||||
int fd, len;
|
||||
char buf[64];
|
||||
|
||||
fd = open("/", O_RDONLY | O_DIRECTORY);
|
||||
if (fd < 0) {
|
||||
THROW_ERROR("failed to open directory");
|
||||
}
|
||||
while (1) {
|
||||
len = getdents(fd, (struct dirent *)buf, sizeof(buf));
|
||||
if (len < 0) {
|
||||
close(fd);
|
||||
THROW_ERROR("failed to call getdents");
|
||||
} else if (len == 0) {
|
||||
// On end of directory, 0 is returned
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_getdents_with_too_small_buffer() {
|
||||
int fd, len;
|
||||
char buf[4];
|
||||
|
||||
fd = open("/", O_RDONLY | O_DIRECTORY);
|
||||
if (fd < 0) {
|
||||
THROW_ERROR("failed to open directory");
|
||||
}
|
||||
len = getdents(fd, (struct dirent *)buf, sizeof(buf));
|
||||
if (len >= 0 || errno != EINVAL) {
|
||||
close(fd);
|
||||
THROW_ERROR("failed to call getdents with small buffer");
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test suite main
|
||||
// ============================================================================
|
||||
|
||||
static test_case_t test_cases[] = {
|
||||
TEST_CASE(test_readdir),
|
||||
TEST_CASE(test_getdents_with_big_enough_buffer),
|
||||
TEST_CASE(test_getdents_with_too_small_buffer),
|
||||
};
|
||||
|
||||
int main() {
|
||||
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user