diff --git a/src/libos/src/fs/file_ops/file_flags.rs b/src/libos/src/fs/file_ops/file_flags.rs index f501ee76..0449cfa7 100644 --- a/src/libos/src/fs/file_ops/file_flags.rs +++ b/src/libos/src/fs/file_ops/file_flags.rs @@ -84,6 +84,10 @@ impl CreationFlags { } self.contains(CreationFlags::O_DIRECTORY) } + + pub fn should_truncate(&self) -> bool { + self.contains(CreationFlags::O_TRUNC) + } } bitflags! { diff --git a/src/libos/src/fs/hostfs.rs b/src/libos/src/fs/hostfs.rs index f5818767..21b730f5 100644 --- a/src/libos/src/fs/hostfs.rs +++ b/src/libos/src/fs/hostfs.rs @@ -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(()) } diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index 1a914f68..1d412abe 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -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, diff --git a/src/libos/src/fs/sefs/sgx_storage.rs b/src/libos/src/fs/sefs/sgx_storage.rs index 0eeb5c02..d74aebb1 100644 --- a/src/libos/src/fs/sefs/sgx_storage.rs +++ b/src/libos/src/fs/sefs/sgx_storage.rs @@ -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<()> { diff --git a/test/fs_perms/main.c b/test/fs_perms/main.c index 1b8188d6..c7a55f9d 100644 --- a/test/fs_perms/main.c +++ b/test/fs_perms/main.c @@ -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]; diff --git a/test/hostfs/main.c b/test/hostfs/main.c index 7e4a3149..c14df71d 100644 --- a/test/hostfs/main.c +++ b/test/hostfs/main.c @@ -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), }; diff --git a/test/include/test.h b/test/include/test.h index 496b866f..c5a76767 100644 --- a/test/include/test.h +++ b/test/include/test.h @@ -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 */ diff --git a/test/include/test_fs.h b/test/include/test_fs.h index df5b3b10..491dd379 100644 --- a/test/include/test_fs.h +++ b/test/include/test_fs.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 */ diff --git a/test/mmap/main.c b/test/mmap/main.c index 81971b39..a5d47bb7 100644 --- a/test/mmap/main.c +++ b/test/mmap/main.c @@ -10,7 +10,7 @@ #include #include #include -#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; diff --git a/test/truncate/main.c b/test/truncate/main.c index b6b727ee..c0dffe2a 100644 --- a/test/truncate/main.c +++ b/test/truncate/main.c @@ -4,77 +4,232 @@ #include #include #include +#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)); +}