diff --git a/docs/SystemEdls.md b/docs/SystemEdls.md
index 58a6599078..203ce91da0 100644
--- a/docs/SystemEdls.md
+++ b/docs/SystemEdls.md
@@ -83,6 +83,7 @@ oe_syscall_link_ocall | link | - |
oe_syscall_unlink_ocall | unlink | - |
oe_syscall_rename_ocall | rename | - |
oe_syscall_truncate_ocall | truncate | - |
+oe_syscall_ftruncate_ocall | ftruncate | - |
oe_syscall_mkdir_ocall | mkdir | - |
oe_syscall_rmdir_ocall | rmdir | - |
oe_syscall_fcntl_ocall | fcntl | - |
diff --git a/docs/UsingTheIOSubsystem.md b/docs/UsingTheIOSubsystem.md
index 5b40f3fb77..7b3b207505 100644
--- a/docs/UsingTheIOSubsystem.md
+++ b/docs/UsingTheIOSubsystem.md
@@ -382,6 +382,7 @@ functions.
| dup2 | none |
| fdatasync | none |
| fsync | none |
+| ftruncate | none |
| getcwd | none |
| getdomainname | none |
| getegid | none |
@@ -401,6 +402,7 @@ functions.
| read | none |
| rmdir | none |
| sleep | none |
+| truncate | none |
| unlink | none |
| write | none |
| |
|
diff --git a/host/linux/syscall.c b/host/linux/syscall.c
index f0eb835174..9056f92d3d 100644
--- a/host/linux/syscall.c
+++ b/host/linux/syscall.c
@@ -389,6 +389,13 @@ int oe_syscall_truncate_ocall(const char* path, oe_off_t length)
return truncate(path, length);
}
+int oe_syscall_ftruncate_ocall(oe_host_fd_t fd, oe_off_t length)
+{
+ errno = 0;
+
+ return ftruncate((int)fd, length);
+}
+
int oe_syscall_mkdir_ocall(const char* pathname, oe_mode_t mode)
{
errno = 0;
diff --git a/host/windows/syscall.c b/host/windows/syscall.c
index d7a39db13d..f5e5c3b33f 100644
--- a/host/windows/syscall.c
+++ b/host/windows/syscall.c
@@ -2032,6 +2032,41 @@ int oe_syscall_truncate_ocall(const char* pathname, oe_off_t length)
return ret;
}
+int oe_syscall_ftruncate_ocall(oe_host_fd_t fd, oe_off_t length)
+{
+ const HANDLE h = (HANDLE)fd;
+ LARGE_INTEGER new_offset = {0};
+ LARGE_INTEGER old_offset = {0};
+
+ if (!SetFilePointerEx(h, new_offset, &old_offset, FILE_CURRENT))
+ {
+ _set_errno(_winerr_to_errno(GetLastError()));
+ return -1;
+ }
+
+ new_offset.QuadPart = length;
+ if (!SetFilePointerEx(h, new_offset, NULL, FILE_BEGIN))
+ {
+ _set_errno(_winerr_to_errno(GetLastError()));
+ return -1;
+ }
+
+ if (!SetEndOfFile(h))
+ {
+ _set_errno(_winerr_to_errno(GetLastError()));
+ SetFilePointerEx(h, old_offset, NULL, FILE_BEGIN);
+ return -1;
+ }
+
+ if (!SetFilePointerEx(h, old_offset, NULL, FILE_BEGIN))
+ {
+ _set_errno(_winerr_to_errno(GetLastError()));
+ return -1;
+ }
+
+ return 0;
+}
+
int oe_syscall_mkdir_ocall(const char* pathname, oe_mode_t mode)
{
int ret = -1;
diff --git a/include/openenclave/edl/fcntl.edl b/include/openenclave/edl/fcntl.edl
index 8a6f37389a..5a28f71afa 100644
--- a/include/openenclave/edl/fcntl.edl
+++ b/include/openenclave/edl/fcntl.edl
@@ -177,6 +177,11 @@ enclave
oe_off_t length)
propagate_errno;
+ int oe_syscall_ftruncate_ocall(
+ oe_host_fd_t fd,
+ oe_off_t length)
+ propagate_errno;
+
int oe_syscall_mkdir_ocall(
[in, string] const char* pathname,
oe_mode_t mode)
diff --git a/include/openenclave/internal/syscall/declarations.h b/include/openenclave/internal/syscall/declarations.h
index f5e871e64e..20ba057f34 100644
--- a/include/openenclave/internal/syscall/declarations.h
+++ b/include/openenclave/internal/syscall/declarations.h
@@ -175,6 +175,7 @@ OE_DECLARE_SYSCALL2(SYS_flock);
OE_DECLARE_SYSCALL2(SYS_fstat);
OE_DECLARE_SYSCALL4(SYS_fstatat);
OE_DECLARE_SYSCALL1_M(SYS_fsync);
+OE_DECLARE_SYSCALL2(SYS_ftruncate);
// SYS_futex is needed for compiling musl/src/internal/pthread_impl.h
// It doesn't have to be implemented.
// It is called with 3 or 4 arguments.
diff --git a/include/openenclave/internal/syscall/fd.h b/include/openenclave/internal/syscall/fd.h
index 02be71b95c..faee04c49e 100644
--- a/include/openenclave/internal/syscall/fd.h
+++ b/include/openenclave/internal/syscall/fd.h
@@ -66,6 +66,8 @@ typedef struct _oe_file_ops
int (*fstat)(oe_fd_t* file, struct oe_stat_t* buf);
+ int (*ftruncate)(oe_fd_t* file, oe_off_t length);
+
int (*fsync)(oe_fd_t* file);
int (*fdatasync)(oe_fd_t* file);
} oe_file_ops_t;
diff --git a/include/openenclave/internal/syscall/unistd.h b/include/openenclave/internal/syscall/unistd.h
index 4bf41c3527..e541e8aa04 100644
--- a/include/openenclave/internal/syscall/unistd.h
+++ b/include/openenclave/internal/syscall/unistd.h
@@ -52,6 +52,8 @@ int oe_truncate(const char* path, oe_off_t length);
int oe_truncate_d(uint64_t devid, const char* path, oe_off_t length);
+int oe_ftruncate(int fd, oe_off_t length);
+
#endif /* !defined(WIN32) */
int oe_link(const char* oldpath, const char* newpath);
diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index 6405f5686a..c4d4b63dd1 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -782,6 +782,7 @@ add_enclave_library(
${MUSLSRC}/unistd/close.c
${MUSLSRC}/unistd/fdatasync.c
${MUSLSRC}/unistd/fsync.c
+ ${MUSLSRC}/unistd/ftruncate.c
${MUSLSRC}/unistd/getgid.c
${MUSLSRC}/unistd/getegid.c
${MUSLSRC}/unistd/getuid.c
diff --git a/syscall/consolefs.c b/syscall/consolefs.c
index 2da7c52454..1c1f7b1c29 100644
--- a/syscall/consolefs.c
+++ b/syscall/consolefs.c
@@ -490,6 +490,15 @@ static int _consolefs_fstat(oe_fd_t* file, struct oe_stat_t* buf)
return -1;
}
+static int _consolefs_ftruncate(oe_fd_t* file, oe_off_t length)
+{
+ OE_UNUSED(file);
+ OE_UNUSED(length);
+ OE_RAISE_ERRNO(OE_EINVAL);
+done:
+ return -1;
+}
+
static int _consolefs_fsync(oe_fd_t* file)
{
OE_UNUSED(file);
@@ -513,6 +522,7 @@ static oe_file_ops_t _ops = {
.pwrite = _consolefs_pwrite,
.getdents64 = _consolefs_getdents64,
.fstat = _consolefs_fstat,
+ .ftruncate = _consolefs_ftruncate,
.fsync = _consolefs_fsync,
.fdatasync = _consolefs_fsync,
};
diff --git a/syscall/devices/hostfs/hostfs.c b/syscall/devices/hostfs/hostfs.c
index c03c55b5b2..66a94d7f69 100644
--- a/syscall/devices/hostfs/hostfs.c
+++ b/syscall/devices/hostfs/hostfs.c
@@ -1359,6 +1359,24 @@ static int _hostfs_truncate(
return ret;
}
+static int _hostfs_ftruncate(oe_fd_t* desc, oe_off_t length)
+{
+ int ret = -1;
+ const file_t* const file = _cast_file(desc);
+ int retval = -1;
+
+ if (!file)
+ OE_RAISE_ERRNO(OE_EINVAL);
+
+ if (oe_syscall_ftruncate_ocall(&retval, file->host_fd, length) != OE_OK)
+ OE_RAISE_ERRNO(OE_EINVAL);
+
+ ret = retval;
+
+done:
+ return ret;
+}
+
static int _hostfs_mkdir(
oe_device_t* device,
const char* pathname,
@@ -1441,6 +1459,7 @@ static oe_file_ops_t _file_ops =
.pwrite = _hostfs_pwrite,
.getdents64 = _hostfs_getdents64,
.fstat = _hostfs_fstat,
+ .ftruncate = _hostfs_ftruncate,
.fsync = _hostfs_fsync,
.fdatasync = _hostfs_fdatasync,
};
diff --git a/syscall/fdtable.c b/syscall/fdtable.c
index c38aa762c3..a23559d859 100644
--- a/syscall/fdtable.c
+++ b/syscall/fdtable.c
@@ -170,6 +170,7 @@ static void _assert_fd(oe_fd_t* desc)
oe_assert(desc->ops.file.pwrite);
oe_assert(desc->ops.file.getdents64);
oe_assert(desc->ops.file.fstat);
+ oe_assert(desc->ops.file.ftruncate);
oe_assert(desc->ops.file.fsync);
oe_assert(desc->ops.file.fdatasync);
break;
diff --git a/syscall/syscall.c b/syscall/syscall.c
index db7fd5ce3f..e81bcfa61d 100644
--- a/syscall/syscall.c
+++ b/syscall/syscall.c
@@ -316,6 +316,14 @@ OE_WEAK OE_DEFINE_SYSCALL1_M(SYS_fsync)
return oe_fsync(fd);
}
+OE_WEAK OE_DEFINE_SYSCALL2(SYS_ftruncate)
+{
+ oe_errno = 0;
+ const int fd = (int)arg1;
+ const ssize_t length = (ssize_t)arg2;
+ return oe_ftruncate(fd, length);
+}
+
OE_WEAK OE_DEFINE_SYSCALL3_M(SYS_futex)
{
OE_UNUSED(arg1);
@@ -1107,6 +1115,7 @@ static long _syscall(
OE_SYSCALL_DISPATCH(SYS_flock, arg1, arg2);
OE_SYSCALL_DISPATCH(SYS_fstat, arg1, arg2);
OE_SYSCALL_DISPATCH(SYS_fsync, arg1);
+ OE_SYSCALL_DISPATCH(SYS_ftruncate, arg1, arg2);
OE_SYSCALL_DISPATCH(SYS_getcwd, arg1, arg2);
OE_SYSCALL_DISPATCH(SYS_getdents64, arg1, arg2, arg3);
OE_SYSCALL_DISPATCH(SYS_getegid);
diff --git a/syscall/unistd.c b/syscall/unistd.c
index 67af74764b..943528b517 100644
--- a/syscall/unistd.c
+++ b/syscall/unistd.c
@@ -545,6 +545,20 @@ int oe_truncate_d(uint64_t devid, const char* path, oe_off_t length)
return ret;
}
+int oe_ftruncate(int fd, oe_off_t length)
+{
+ int ret = -1;
+ oe_fd_t* file;
+
+ if (!(file = oe_fdtable_get(fd, OE_FD_TYPE_FILE)))
+ OE_RAISE_ERRNO(oe_errno);
+
+ ret = file->ops.file.ftruncate(file, length);
+
+done:
+ return ret;
+}
+
oe_off_t oe_lseek(int fd, oe_off_t offset, int whence)
{
oe_off_t ret = -1;
diff --git a/tests/syscall/fs/enc/enc.cpp b/tests/syscall/fs/enc/enc.cpp
index 2161003a02..59ca3455bc 100644
--- a/tests/syscall/fs/enc/enc.cpp
+++ b/tests/syscall/fs/enc/enc.cpp
@@ -397,7 +397,7 @@ static void test_truncate_file(FILE_SYSTEM& fs, const char* tmp_dir)
mkpath(path, tmp_dir, "alphabet");
- /* Remove the file. */
+ /* Truncate the file. */
OE_TEST(fs.truncate(path, 5) == 0);
/* Stat the file. */
@@ -405,6 +405,27 @@ static void test_truncate_file(FILE_SYSTEM& fs, const char* tmp_dir)
OE_TEST(buf.st_size == 5);
}
+template
+static void test_ftruncate_file(FILE_SYSTEM& fs, const char* tmp_dir)
+{
+ char path[OE_PAGE_SIZE];
+ typename FILE_SYSTEM::stat_type buf;
+
+ printf("--- %s()\n", __FUNCTION__);
+
+ mkpath(path, tmp_dir, "alphabet");
+
+ /* Truncate the file. */
+ const auto file = fs.open(path, O_WRONLY, 0);
+ OE_TEST(file);
+ OE_TEST(fs.ftruncate(file, 4) == 0);
+ OE_TEST(fs.close(file) == 0);
+
+ /* Stat the file. */
+ OE_TEST(fs.stat(path, &buf) == 0);
+ OE_TEST(buf.st_size == 4);
+}
+
template
static void test_unlink_file(FILE_SYSTEM& fs, const char* tmp_dir)
{
@@ -444,6 +465,7 @@ void test_common(FILE_SYSTEM& fs, const char* tmp_dir)
test_rename_file(fs, tmp_dir);
test_readdir(fs, tmp_dir);
test_truncate_file(fs, tmp_dir);
+ test_ftruncate_file(fs, tmp_dir);
test_unlink_file(fs, tmp_dir);
test_invalid_path(fs);
cleanup(fs, tmp_dir);
diff --git a/tests/syscall/fs/enc/file_system.h b/tests/syscall/fs/enc/file_system.h
index 76f4608a06..317cdcdd36 100644
--- a/tests/syscall/fs/enc/file_system.h
+++ b/tests/syscall/fs/enc/file_system.h
@@ -146,6 +146,11 @@ class oe_fd_file_system
return oe_truncate(path, length);
}
+ int ftruncate(file_handle file, off_t length)
+ {
+ return oe_ftruncate(file, length);
+ }
+
private:
};
@@ -306,6 +311,11 @@ class fd_file_system
return ::truncate(path, length);
}
+ int ftruncate(file_handle file, off_t length)
+ {
+ return ::ftruncate(file, length);
+ }
+
private:
};
@@ -584,6 +594,11 @@ class stream_file_system
return ::truncate(path, length);
}
+ int ftruncate(file_handle file, off_t length)
+ {
+ return ::ftruncate(fileno(file), length);
+ }
+
private:
};