Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1022,19 +1022,35 @@ static int dfs_tmpfs_truncate(struct dfs_file *file, off_t offset)
superblock = d_file->sb;
RT_ASSERT(superblock != RT_NULL);

ptr = rt_realloc(d_file->data, offset);
if (ptr == RT_NULL)
if (offset == 0)
{
rt_set_errno(-ENOMEM);
return 0;
/* rt_realloc(ptr, 0) frees the block and returns RT_NULL, which cannot
* be told apart from an allocation failure. Handle the empty case
* explicitly, otherwise d_file->data is left dangling. */
if (d_file->data != RT_NULL)
{
rt_free(d_file->data);
d_file->data = RT_NULL;
}
ptr = RT_NULL;
}
else
{
ptr = rt_realloc(d_file->data, offset);
if (ptr == RT_NULL)
{
rt_set_errno(-ENOMEM);
return -ENOMEM;
}

d_file->data = ptr;
}

rt_spin_lock(&superblock->lock);
superblock->df_size = offset;
rt_spin_unlock(&superblock->lock);

/* update d_file and file size */
d_file->data = ptr;
d_file->size = offset;
file->vnode->size = d_file->size;
LOG_D("tmpfile ptr:%x, size:%d", ptr, d_file->size);
Expand Down
7 changes: 7 additions & 0 deletions components/dfs/dfs_v2/filesystems/tmpfs/utest/Kconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
menu "Tmpfs Testcase"

config RT_UTEST_TMPFS_TRUNCATE
bool "tmpfs truncate test"
default n
depends on RT_USING_POSIX_FS && RT_USING_DFS_TMPFS
help
Test zero-length truncation, subsequent I/O and allocation failures.

config RT_UTEST_TMPFS_CP
bool "tmpfs cp test"
default n
Expand Down
3 changes: 3 additions & 0 deletions components/dfs/dfs_v2/filesystems/tmpfs/utest/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ cwd = GetCurrentDir()
src = []
CPPPATH = [cwd]

if GetDepend(['RT_UTEST_TMPFS_TRUNCATE']):
src += ['tc_truncate.c']

if GetDepend(['RT_USING_SMART','RT_UTEST_TMPFS_CP']):
src += ['tmpfs.c']

Expand Down
158 changes: 158 additions & 0 deletions components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_truncate.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright (c) 2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <rtthread.h>
#include <dfs_fs.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "utest.h"

#define TEST_MOUNT "/tmpfs-truncate"
#define TEST_PATH TEST_MOUNT "/file"

static const char payload[] = "0123456789abcdef";

static int create_file(void)
{
int fd = open(TEST_PATH, O_CREAT | O_RDWR | O_TRUNC, 0600);

uassert_true(fd >= 0);
if (fd < 0)
{
return -1;
}

uassert_int_equal(write(fd, payload, sizeof(payload) - 1), sizeof(payload) - 1);
/* Closing flushes dirty pages before testing the backing buffer. */
uassert_int_equal(close(fd), 0);
fd = open(TEST_PATH, O_RDWR, 0);
uassert_true(fd >= 0);
return fd;
}

static void test_zero_length(void)
{
struct stat st;
char buffer[sizeof(payload)];
int fd = create_file();

if (fd < 0)
{
return;
}

uassert_int_equal(ftruncate(fd, 0), 0);
uassert_int_equal(fstat(fd, &st), 0);
uassert_int_equal(st.st_size, 0);
uassert_int_equal(lseek(fd, 0, SEEK_SET), 0);
uassert_int_equal(read(fd, buffer, sizeof(buffer)), 0);

/* Truncating an already empty file must also be safe. */
uassert_int_equal(ftruncate(fd, 0), 0);
uassert_int_equal(write(fd, payload, sizeof(payload) - 1), sizeof(payload) - 1);
uassert_int_equal(lseek(fd, 0, SEEK_SET), 0);
uassert_int_equal(read(fd, buffer, sizeof(payload) - 1), sizeof(payload) - 1);
uassert_int_equal(rt_memcmp(buffer, payload, sizeof(payload) - 1), 0);
uassert_int_equal(close(fd), 0);
fd = open(TEST_PATH, O_RDONLY, 0);
uassert_true(fd >= 0);
if (fd >= 0)
{
uassert_int_equal(fstat(fd, &st), 0);
uassert_int_equal(st.st_size, sizeof(payload) - 1);
uassert_int_equal(close(fd), 0);
}
uassert_int_equal(unlink(TEST_PATH), 0);
}

static void test_nonzero_length(void)
{
struct stat st;
char buffer[4];
int fd = create_file();

if (fd < 0)
{
return;
}

uassert_int_equal(ftruncate(fd, sizeof(buffer)), 0);
uassert_int_equal(fstat(fd, &st), 0);
uassert_int_equal(st.st_size, sizeof(buffer));
uassert_int_equal(lseek(fd, 0, SEEK_SET), 0);
uassert_int_equal(read(fd, buffer, sizeof(buffer)), sizeof(buffer));
uassert_int_equal(rt_memcmp(buffer, payload, sizeof(buffer)), 0);

uassert_int_equal(ftruncate(fd, 4096), 0);
uassert_int_equal(fstat(fd, &st), 0);
uassert_int_equal(st.st_size, 4096);
uassert_int_equal(lseek(fd, 0, SEEK_SET), 0);
uassert_int_equal(read(fd, buffer, sizeof(buffer)), sizeof(buffer));
uassert_int_equal(rt_memcmp(buffer, payload, sizeof(buffer)), 0);
uassert_int_equal(close(fd), 0);
uassert_int_equal(unlink(TEST_PATH), 0);
}

static void test_allocation_failure(void)
{
rt_size_t total, used, max_used;
struct stat st;
char buffer[sizeof(payload)];
int fd = create_file();

if (fd < 0)
{
return;
}

/* An existing allocation cannot grow to the size of the entire heap. */
rt_memory_info(&total, &used, &max_used);
uassert_true(total > 0);
uassert_true((off_t)total > 0);
errno = 0;
uassert_int_equal(ftruncate(fd, (off_t)total), -1);
uassert_int_equal(errno, -ENOMEM);
uassert_int_equal(fstat(fd, &st), 0);
uassert_int_equal(st.st_size, sizeof(payload) - 1);
uassert_int_equal(lseek(fd, 0, SEEK_SET), 0);
uassert_int_equal(read(fd, buffer, sizeof(payload) - 1), sizeof(payload) - 1);
uassert_int_equal(rt_memcmp(buffer, payload, sizeof(payload) - 1), 0);
uassert_int_equal(close(fd), 0);
uassert_int_equal(unlink(TEST_PATH), 0);
}

static rt_err_t test_init(void)
{
if (mkdir(TEST_MOUNT, 0700) != 0 && errno != -EEXIST)
{
return -RT_ERROR;
}

return dfs_mount(RT_NULL, TEST_MOUNT, "tmp", 0, RT_NULL) == 0 ? RT_EOK : -RT_ERROR;
}

static rt_err_t test_cleanup(void)
{
int ret = dfs_unmount(TEST_MOUNT);

rmdir(TEST_MOUNT);
return ret == 0 ? RT_EOK : -RT_ERROR;
}

static void test_truncate(void)
{
test_zero_length();
test_nonzero_length();
test_allocation_failure();
}

static void testcase(void)
{
UTEST_UNIT_RUN(test_truncate);
}
UTEST_TC_EXPORT(testcase, "components.dfs.tmpfs_truncate", test_init, test_cleanup, 30);
Loading