Add O_TRUNC support in open syscall and rewrite the truncate test

This commit is contained in:
LI Qing 2021-03-04 19:21:49 +08:00 committed by Tate, Hongliang Tian
parent 001df6f309
commit eb046d4241
10 changed files with 335 additions and 124 deletions

@ -84,6 +84,10 @@ impl CreationFlags {
}
self.contains(CreationFlags::O_DIRECTORY)
}
pub fn should_truncate(&self) -> bool {
self.contains(CreationFlags::O_TRUNC)
}
}
bitflags! {

@ -128,7 +128,9 @@ impl INode for HNode {
}
fn resize(&self, len: usize) -> Result<()> {
warn!("HostFS: resize() is unimplemented");
let mut guard = self.open_file()?;
let file = guard.as_mut().unwrap();
try_std!(file.set_len(len as u64));
Ok(())
}

@ -243,6 +243,14 @@ impl INodeFile {
if access_mode.writable() && inode.metadata()?.type_ == FileType::Dir {
return_errno!(EISDIR, "Directory cannot be open to write");
}
let creation_flags = CreationFlags::from_bits_truncate(flags);
if creation_flags.should_truncate()
&& inode.metadata()?.type_ == FileType::File
&& access_mode.writable()
{
// truncate the length to 0
inode.resize(0)?;
}
let status_flags = StatusFlags::from_bits_truncate(flags);
Ok(INodeFile {
inode,

@ -246,11 +246,11 @@ impl File for LockedFile {
let file_size = file.seek(SeekFrom::End(0))? as usize;
if file_size < offset {
static ZEROS: [u8; 0x1000] = [0; 0x1000];
let mut rest_len = offset - file_size;
while rest_len != 0 {
let l = rest_len.min(0x1000);
let mut remaining_len = offset - file_size;
while remaining_len != 0 {
let l = remaining_len.min(0x1000);
let len = file.write(&ZEROS[..l])?;
rest_len -= len;
remaining_len -= len;
}
}
@ -262,8 +262,31 @@ impl File for LockedFile {
}
fn set_len(&self, len: usize) -> DevResult<()> {
// NOTE: do nothing ??
Ok(())
// The set_len() is unsupported for SgxFile, we have to
// implement it in a slow way by padding null bytes.
convert_result!({
let mut file = self.0.lock().unwrap();
let file_size = file.seek(SeekFrom::End(0))? as usize;
let mut reset_len = if len > file_size {
// Expand the file by padding null bytes
len - file_size
} else {
// Shrink the file by setting null bytes between len and file_size
file.seek(SeekFrom::Start(len as u64))?;
file_size - len
};
static ZEROS: [u8; 0x1000] = [0; 0x1000];
while reset_len != 0 {
let l = reset_len.min(0x1000);
// Probably there's not enough space on disk, let's panic here
let written_len = file.write(&ZEROS[..l]).unwrap_or_else(|e| {
error!("failed to set null bytes: {}", e);
panic!();
});
reset_len -= written_len;
}
Ok(())
})
}
fn flush(&self) -> DevResult<()> {

@ -66,7 +66,7 @@ static int do_perm_tests(
size_t num_files,
int flags, int do_write,
int *expected_results) {
flags |= O_CREAT | O_TRUNC;
flags |= O_CREAT;
for (size_t i = 0; i < num_files; i++) {
const char *filename = files[i];
int expected_result = expected_results[i];

@ -154,6 +154,21 @@ static int __test_readdir(const char *file_path) {
return 0;
}
static int __test_truncate(const char *file_path) {
off_t len = 256;
if (truncate(file_path, len) < 0) {
THROW_ERROR("failed to call truncate");
}
struct stat stat_buf;
if (stat(file_path, &stat_buf) < 0) {
THROW_ERROR("failed to stat file");
}
if (stat_buf.st_size != len) {
THROW_ERROR("failed to check the len after truncate");
}
return 0;
}
typedef int(*test_hostfs_func_t)(const char *);
static int test_hostfs_framework(test_hostfs_func_t fn) {
@ -191,6 +206,10 @@ static int test_readdir() {
return test_hostfs_framework(__test_readdir);
}
static int test_truncate() {
return test_hostfs_framework(__test_truncate);
}
static int test_mkdir_then_rmdir() {
const char *dir_path = "/host/hostfs_dir";
struct stat stat_buf;
@ -221,6 +240,7 @@ static test_case_t test_cases[] = {
TEST_CASE(test_write_fsync_read),
TEST_CASE(test_rename),
TEST_CASE(test_readdir),
TEST_CASE(test_truncate),
TEST_CASE(test_mkdir_then_rmdir),
};

@ -49,4 +49,14 @@ void close_files(int count, ...) {
va_end(ap);
}
int check_bytes_in_buf(char *buf, size_t len, int expected_byte_val) {
for (size_t bi = 0; bi < len; bi++) {
if (buf[bi] != (char)expected_byte_val) {
THROW_ERROR("check_bytes_in_buf: expect %02X, but found %02X, at offset %lu\n",
(unsigned char)expected_byte_val, (unsigned char)buf[bi], bi);
}
}
return 0;
}
#endif /* __TEST_H */

@ -56,4 +56,43 @@ int fs_check_file_content(const char *path, const char *msg) {
return 0;
}
int fill_file_with_repeated_bytes(int fd, size_t len, int byte_val) {
char buf[1024 * 4];
memset(buf, byte_val, sizeof(buf));
size_t remain_bytes = len;
while (remain_bytes > 0) {
int to_write_bytes = MIN(sizeof(buf), remain_bytes);
int written_bytes = write(fd, buf, to_write_bytes);
if (written_bytes != to_write_bytes) {
THROW_ERROR("file write failed");
}
remain_bytes -= written_bytes;
}
return 0;
}
int check_file_with_repeated_bytes(int fd, size_t len, int expected_byte_val) {
size_t remain = len;
char read_buf[512];
while (remain > 0) {
int read_nbytes = read(fd, read_buf, sizeof(read_buf));
if (read_nbytes < 0) {
// I/O error
return -1;
}
remain -= read_nbytes;
if (read_nbytes == 0 && remain > 0) {
// Not enough data in the file
return -1;
}
if (check_bytes_in_buf(read_buf, read_nbytes, expected_byte_val) < 0) {
// Incorrect data
return -1;
}
}
return 0;
}
#endif /* __TEST_FS_H */

@ -10,7 +10,7 @@
#include <string.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include "test.h"
#include "test_fs.h"
// ============================================================================
// Helper macros
@ -29,56 +29,6 @@
// Helper functions
// ============================================================================
static int fill_file_with_repeated_bytes(int fd, size_t len, int byte_val) {
char buf[PAGE_SIZE];
memset(buf, byte_val, sizeof(buf));
size_t remain_bytes = len;
while (remain_bytes > 0) {
int to_write_bytes = MIN(sizeof(buf), remain_bytes);
int written_bytes = write(fd, buf, to_write_bytes);
if (written_bytes != to_write_bytes) {
THROW_ERROR("file write failed");
}
remain_bytes -= written_bytes;
}
return 0;
}
static int check_bytes_in_buf(char *buf, size_t len, int expected_byte_val) {
for (size_t bi = 0; bi < len; bi++) {
if (buf[bi] != (char)expected_byte_val) {
printf("check_bytes_in_buf: expect %02X, but found %02X, at offset %lu\n",
(unsigned char)expected_byte_val, (unsigned char)buf[bi], bi);
return -1;
}
}
return 0;
}
static int check_file_with_repeated_bytes(int fd, size_t len, int expected_byte_val) {
size_t remain = len;
char read_buf[512];
while (remain > 0) {
int read_nbytes = read(fd, read_buf, sizeof(read_buf));
if (read_nbytes < 0) {
// I/O error
return -1;
}
remain -= read_nbytes;
if (read_nbytes == 0 && remain > 0) {
// Not enough data in the file
return -1;
}
if (check_bytes_in_buf(read_buf, read_nbytes, expected_byte_val) < 0) {
// Incorrect data
return -1;
}
}
return 0;
}
static void *get_a_stack_ptr() {
volatile int a = 0;
return (void *) &a;

@ -4,77 +4,232 @@
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "test_fs.h"
int main(int argc, const char *argv[]) {
const char *FILE_NAME = "root/test_filesystem_truncate.txt";
const int TRUNC_LEN = 256;
const int TRUNC_LEN1 = 128;
const int MODE_MASK = 0777;
int ret;
// ============================================================================
// Helper function
// ============================================================================
static int create_file(const char *file_path) {
int fd;
int flags = O_WRONLY | O_CREAT | O_TRUNC;
int mode = 00666;
int fd = open(FILE_NAME, flags, mode);
fd = open(file_path, flags, mode);
if (fd < 0) {
printf("failed to open a file for write\n");
return fd;
THROW_ERROR("failed to create a file");
}
if (access(FILE_NAME, F_OK) < 0) {
printf("cannot access the new file\n");
return -1;
}
ret = ftruncate(fd, TRUNC_LEN);
if (ret < 0) {
printf("failed to ftruncate the file\n");
return ret;
}
struct stat stat_buf;
ret = fstat(fd, &stat_buf);
if (ret < 0) {
printf("failed to fstat the file\n");
return ret;
}
int file_size = stat_buf.st_size;
if (file_size != TRUNC_LEN) {
printf("Incorrect file size %d. Expected %d\n", file_size, TRUNC_LEN);
return -1;
}
int file_mode = stat_buf.st_mode & MODE_MASK;
if (file_mode != mode) {
printf("Incorrect file mode %o. Expected %o\n", file_mode, mode);
return -1;
}
int file_type = stat_buf.st_mode & S_IFMT;
if (file_type != S_IFREG) {
printf("Incorrect file type %o. Expected %o\n", file_type, S_IFREG);
return -1;
}
close(fd);
ret = truncate(FILE_NAME, TRUNC_LEN1);
if (ret < 0) {
printf("failed to truncate the file\n");
return ret;
}
ret = stat(FILE_NAME, &stat_buf);
if (ret < 0) {
printf("failed to stat the file\n");
return ret;
}
file_size = stat_buf.st_size;
if (file_size != TRUNC_LEN1) {
printf("Incorrect file size %d. Expected %d\n", file_size, TRUNC_LEN1);
return -1;
}
printf("(f)truncate, (f)stat test successful\n");
return 0;
}
static int remove_file(const char *file_path) {
int ret;
ret = unlink(file_path);
if (ret < 0) {
THROW_ERROR("failed to unlink the created file");
}
return 0;
}
// ============================================================================
// Test cases for truncate
// ============================================================================
static int __test_truncate(const char *file_path) {
int fd;
off_t len = 128;
fd = open(file_path, O_WRONLY);
if (fd < 0) {
THROW_ERROR("failed to open a file to truncate");
}
if (ftruncate(fd, len) < 0) {
THROW_ERROR("failed to call ftruncate");
}
struct stat stat_buf;
if (fstat(fd, &stat_buf) < 0) {
THROW_ERROR("failed to stat file");
}
if (stat_buf.st_size != len) {
THROW_ERROR("failed to check the len after ftruncate");
}
close(fd);
len = 256;
if (truncate(file_path, len) < 0) {
THROW_ERROR("failed to call truncate");
}
if (stat(file_path, &stat_buf) < 0) {
THROW_ERROR("failed to stat file");
}
if (stat_buf.st_size != len) {
THROW_ERROR("failed to check the len after truncate");
}
return 0;
}
static int __test_open_truncate_existing_file(const char *file_path) {
char *write_str = "Hello World\n";
int fd;
fd = open(file_path, O_WRONLY);
if (fd < 0) {
THROW_ERROR("failed to open a file to write");
}
if (write(fd, write_str, strlen(write_str)) <= 0) {
THROW_ERROR("failed to write");
}
close(fd);
fd = open(file_path, O_RDWR | O_TRUNC);
if (fd < 0) {
THROW_ERROR("failed to open an existing file with O_TRUNC");
}
struct stat stat_buf;
if (fstat(fd, &stat_buf) < 0) {
THROW_ERROR("failed to stat file");
}
if (stat_buf.st_size != 0) {
THROW_ERROR("failed to check the len after open with O_TRUNC");
}
close(fd);
return 0;
}
static int __test_truncate_then_read(const char *file_path) {
size_t file_len = 32;
off_t small_len = 16;
off_t big_len = 48;
char read_buf[128] = { 0 };
int fd;
fd = open(file_path, O_RDWR);
if (fd < 0) {
THROW_ERROR("failed to open file");
}
// truncate to small length, then read
if (fill_file_with_repeated_bytes(fd, file_len, 0xfa) < 0) {
THROW_ERROR("");
}
if (ftruncate(fd, small_len) < 0) {
THROW_ERROR("failed to call ftruncate to small length");
}
if (lseek(fd, 0, SEEK_SET) < 0) {
THROW_ERROR("failed to call lseek");
}
if (read(fd, read_buf, sizeof(read_buf)) != small_len) {
THROW_ERROR("failed to check read with small length");
}
if (check_bytes_in_buf(read_buf, small_len, 0xfa) < 0) {
THROW_ERROR("failed to check the read buf after truncate with smaller length");
}
// truncate to big length, then check the file content between small length and big length
if (ftruncate(fd, big_len) < 0) {
THROW_ERROR("failed to call ftruncate");
}
if (lseek(fd, small_len, SEEK_SET) < 0) {
THROW_ERROR("failed to call lseek");
}
memset(read_buf, 0x00, sizeof(read_buf));
if (read(fd, read_buf, sizeof(read_buf)) != big_len - small_len) {
THROW_ERROR("failed to check read with big length");
}
if (check_bytes_in_buf(read_buf, big_len - small_len, 0x00) < 0) {
THROW_ERROR("failed to check the read buf after truncate with bigger lenghth");
}
close(fd);
return 0;
}
static int __test_truncate_then_write(const char *file_path) {
size_t file_len = 32;
off_t small_len = 16;
char write_buf[16] = { 0 };
char read_buf[16] = { 0 };
int fd;
fd = open(file_path, O_RDWR);
if (fd < 0) {
THROW_ERROR("failed to open file");
}
// truncate file to small length, then write beyond the length
if (fill_file_with_repeated_bytes(fd, file_len, 0xfa) < 0) {
THROW_ERROR("");
}
if (ftruncate(fd, small_len) < 0) {
THROW_ERROR("failed to call ftruncate to small length");
}
if (lseek(fd, file_len, SEEK_SET) < 0) {
THROW_ERROR("failed to call lseek");
}
memset(write_buf, 0xaa, sizeof(write_buf));
if (write(fd, write_buf, sizeof(write_buf)) != sizeof(write_buf)) {
THROW_ERROR("failed to write buffer");
}
// check the file content between small length and old length
if (lseek(fd, small_len, SEEK_SET) < 0) {
THROW_ERROR("failed to call lseek");
}
if (read(fd, read_buf, sizeof(read_buf)) != file_len - small_len) {
THROW_ERROR("failed to read buf");
}
if (check_bytes_in_buf(read_buf, file_len - small_len, 0x00) < 0) {
THROW_ERROR("failed to check the read buf after write beyond the length");
}
close(fd);
return 0;
}
typedef int(*test_file_func_t)(const char *);
static int test_file_framework(test_file_func_t fn) {
const char *file_path = "/root/test_filesystem_truncate.txt";
if (create_file(file_path) < 0) {
return -1;
}
if (fn(file_path) < 0) {
return -1;
}
if (remove_file(file_path) < 0) {
return -1;
}
return 0;
}
static int test_truncate() {
return test_file_framework(__test_truncate);
}
static int test_open_truncate_existing_file() {
return test_file_framework(__test_open_truncate_existing_file);
}
static int test_truncate_then_read() {
return test_file_framework(__test_truncate_then_read);
}
static int test_truncate_then_write() {
return test_file_framework(__test_truncate_then_write);
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_truncate),
TEST_CASE(test_open_truncate_existing_file),
TEST_CASE(test_truncate_then_write),
TEST_CASE(test_truncate_then_read),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}