Add ext4l_rename() to rename files and directories, including moves across directories. This uses the Linux ext4_rename() function. Also fix the symlink test to verify reading through symlinks works correctly, since ext4l_resolve_path follows symlinks (stat behavior). Add Python test wrappers for mkdir, ln, and rename tests. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com>
650 lines
19 KiB
C
650 lines
19 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Tests for ext4l filesystem (Linux ext4 port)
|
|
*
|
|
* Copyright 2025 Canonical Ltd
|
|
* Written by Simon Glass <simon.glass@canonical.com>
|
|
*/
|
|
|
|
#include <command.h>
|
|
#include <env.h>
|
|
#include <ext4l.h>
|
|
#include <fs.h>
|
|
#include <fs_legacy.h>
|
|
#include <linux/sizes.h>
|
|
#include <u-boot/uuid.h>
|
|
#include <test/test.h>
|
|
#include <test/ut.h>
|
|
#include <test/fs.h>
|
|
|
|
#define EXT4L_ARG_IMAGE 0 /* fs_image: path to filesystem image */
|
|
|
|
/**
|
|
* fs_test_ext4l_probe_norun() - Test probing an ext4l filesystem
|
|
*
|
|
* This test verifies that the ext4l driver can successfully probe and
|
|
* mount an ext4 filesystem image.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_probe_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_probe_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_msgs_norun() - Test ext4l_msgs env var output
|
|
*
|
|
* This test verifies that setting ext4l_msgs=y causes mount messages
|
|
* to be printed when probing an ext4 filesystem.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_msgs_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
char uuid_str[UUID_STR_LEN + 1];
|
|
u8 uuid[16];
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(env_set("ext4l_msgs", "y"));
|
|
console_record_reset_enable();
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Get the UUID and clear the env var now we have the output */
|
|
ut_assertok(ext4l_get_uuid(uuid));
|
|
uuid_bin_to_str(uuid, uuid_str, UUID_STR_FORMAT_STD);
|
|
ut_assertok(env_set("ext4l_msgs", NULL));
|
|
|
|
/*
|
|
* Check messages. The probe test runs first and doesn't unmount,
|
|
* so the journal needs recovery. The filesystem may be mounted
|
|
* multiple times during probe operations. Just verify we see the
|
|
* expected mount message at least once.
|
|
*/
|
|
ut_assert_skip_to_line("EXT4-fs (ext4l_mmc0): mounted filesystem %s r/w"
|
|
" with ordered data mode. Quota mode: disabled.",
|
|
uuid_str);
|
|
/* Skip any remaining messages */
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_msgs_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_ls_norun() - Test ext4l ls command
|
|
*
|
|
* This test verifies that the ext4l driver can list directory contents.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_ls_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
console_record_reset_enable();
|
|
ut_assertok(run_commandf("ls host 0"));
|
|
/*
|
|
* The Python test adds testfile.txt (12 bytes) to the image.
|
|
* Directory entries appear in hash order which varies between runs.
|
|
* Verify the file entry appears with correct size (12 bytes).
|
|
* Other entries like ., .., subdir, lost+found may also appear.
|
|
*/
|
|
ut_assert_skip_to_line(" 12 testfile.txt");
|
|
/* Skip any remaining entries */
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_ls_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_opendir_norun() - Test ext4l opendir/readdir/closedir
|
|
*
|
|
* Verifies that the ext4l driver can iterate through directory entries using
|
|
* the opendir/readdir/closedir interface. It checks:
|
|
* - Regular files (testfile.txt)
|
|
* - Subdirectories (subdir)
|
|
* - Symlinks (link.txt)
|
|
* - Files in subdirectories (subdir/nested.txt)
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_opendir_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
struct fs_dir_stream *dirs;
|
|
struct fs_dirent *dent;
|
|
bool found_testfile = false;
|
|
bool found_subdir = false;
|
|
bool found_symlink = false;
|
|
bool found_nested = false;
|
|
int count = 0;
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Open root directory */
|
|
ut_assertok(ext4l_opendir("/", &dirs));
|
|
ut_assertnonnull(dirs);
|
|
|
|
/* Iterate through entries */
|
|
while (!ext4l_readdir(dirs, &dent)) {
|
|
ut_assertnonnull(dent);
|
|
count++;
|
|
if (!strcmp(dent->name, "testfile.txt")) {
|
|
found_testfile = true;
|
|
ut_asserteq(FS_DT_REG, dent->type);
|
|
ut_asserteq(12, dent->size);
|
|
} else if (!strcmp(dent->name, "subdir")) {
|
|
found_subdir = true;
|
|
ut_asserteq(FS_DT_DIR, dent->type);
|
|
} else if (!strcmp(dent->name, "link.txt")) {
|
|
found_symlink = true;
|
|
ut_asserteq(FS_DT_LNK, dent->type);
|
|
}
|
|
}
|
|
|
|
ext4l_closedir(dirs);
|
|
|
|
/* Verify we found expected entries */
|
|
ut_assert(found_testfile);
|
|
ut_assert(found_subdir);
|
|
ut_assert(found_symlink);
|
|
/* At least ., .., testfile.txt, subdir, link.txt */
|
|
ut_assert(count >= 5);
|
|
|
|
/* Now test reading the subdirectory */
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
ut_assertok(ext4l_opendir("/subdir", &dirs));
|
|
ut_assertnonnull(dirs);
|
|
|
|
count = 0;
|
|
while (!ext4l_readdir(dirs, &dent)) {
|
|
ut_assertnonnull(dent);
|
|
count++;
|
|
if (!strcmp(dent->name, "nested.txt")) {
|
|
found_nested = true;
|
|
ut_asserteq(FS_DT_REG, dent->type);
|
|
ut_asserteq(12, dent->size);
|
|
}
|
|
}
|
|
|
|
ext4l_closedir(dirs);
|
|
|
|
ut_assert(found_nested);
|
|
/* At least ., .., nested.txt */
|
|
ut_assert(count >= 3);
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_opendir_norun, UTF_SCAN_FDT | UTF_CONSOLE |
|
|
UTF_MANUAL, { "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_exists_norun() - Test ext4l_exists function
|
|
*
|
|
* Verifies that ext4l_exists correctly reports file existence.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_exists_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Test existing directory */
|
|
ut_asserteq(1, ext4l_exists("/"));
|
|
|
|
/* Test non-existent paths */
|
|
ut_asserteq(0, ext4l_exists("/no/such/path"));
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_exists_norun, UTF_SCAN_FDT | UTF_CONSOLE |
|
|
UTF_MANUAL, { "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_size_norun() - Test ext4l_size function
|
|
*
|
|
* Verifies that ext4l_size correctly reports file size.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_size_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
loff_t size;
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Test root directory size - one block on a 4K block filesystem */
|
|
ut_assertok(ext4l_size("/", &size));
|
|
ut_asserteq(SZ_4K, size);
|
|
|
|
/* Test file size - testfile.txt contains "hello world\n" */
|
|
ut_assertok(ext4l_size("/testfile.txt", &size));
|
|
ut_asserteq(12, size);
|
|
|
|
/* Test non-existent path returns -ENOENT */
|
|
ut_asserteq(-ENOENT, ext4l_size("/no/such/path", &size));
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_size_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_read_norun() - Test ext4l_read function
|
|
*
|
|
* Verifies that ext4l can read file contents.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_read_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
loff_t actread;
|
|
char buf[32];
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Read the test file - contains "hello world\n" (12 bytes) */
|
|
memset(buf, '\0', sizeof(buf));
|
|
ut_assertok(ext4l_read("/testfile.txt", buf, 0, 0, &actread));
|
|
ut_asserteq(12, actread);
|
|
ut_asserteq_str("hello world\n", buf);
|
|
|
|
/* Test partial read with offset */
|
|
memset(buf, '\0', sizeof(buf));
|
|
ut_assertok(ext4l_read("/testfile.txt", buf, 6, 5, &actread));
|
|
ut_asserteq(5, actread);
|
|
ut_asserteq_str("world", buf);
|
|
|
|
/* Verify read returns error for non-existent path */
|
|
ut_asserteq(-ENOENT, ext4l_read("/no/such/file", buf, 0, 10, &actread));
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_read_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_uuid_norun() - Test ext4l_uuid function
|
|
*
|
|
* Verifies that ext4l can return the filesystem UUID.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_uuid_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
char uuid_str[UUID_STR_LEN + 1];
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Get the UUID string */
|
|
ut_assertok(ext4l_uuid(uuid_str));
|
|
|
|
/* Verify it's a valid UUID format (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) */
|
|
ut_asserteq(UUID_STR_LEN, strlen(uuid_str));
|
|
ut_asserteq('-', uuid_str[8]);
|
|
ut_asserteq('-', uuid_str[13]);
|
|
ut_asserteq('-', uuid_str[18]);
|
|
ut_asserteq('-', uuid_str[23]);
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_uuid_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_fsinfo_norun() - Test fsinfo command
|
|
*
|
|
* Verifies that the fsinfo command displays filesystem statistics.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_fsinfo_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
struct fs_statfs stats;
|
|
u64 used;
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
ut_assertok(ext4l_statfs(&stats));
|
|
used = stats.blocks - stats.bfree;
|
|
|
|
console_record_reset_enable();
|
|
ut_assertok(run_commandf("fsinfo host 0"));
|
|
|
|
/* Skip any EXT4-fs mount messages, check output format */
|
|
ut_assert_skip_to_line("Block size: %lu bytes", stats.bsize);
|
|
ut_assert_nextlinen("Total blocks: %llu (%llu bytes,",
|
|
stats.blocks, stats.blocks * stats.bsize);
|
|
ut_assert_nextlinen("Used blocks: %llu (%llu bytes,",
|
|
used, used * stats.bsize);
|
|
ut_assert_nextlinen("Free blocks: %llu (%llu bytes,",
|
|
stats.bfree, stats.bfree * stats.bsize);
|
|
ut_assert_console_end();
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_fsinfo_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_statfs_norun() - Test ext4l_statfs function
|
|
*
|
|
* Verifies that ext4l can return filesystem statistics.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_statfs_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
struct fs_statfs stats;
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Get filesystem statistics */
|
|
ut_assertok(ext4l_statfs(&stats));
|
|
|
|
/* Verify reasonable values for a 64MB filesystem */
|
|
ut_asserteq(SZ_4K, stats.bsize);
|
|
ut_assert(stats.blocks > 0);
|
|
ut_assert(stats.bfree > 0);
|
|
ut_assert(stats.bfree <= stats.blocks);
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_statfs_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_write_norun() - Test ext4l_write function
|
|
*
|
|
* Verifies that ext4l can write file contents to the filesystem.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_write_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
const char *test_data = "test write data\n";
|
|
size_t test_len = strlen(test_data);
|
|
loff_t actwrite, actread;
|
|
char read_buf[32];
|
|
loff_t size;
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Write a new file */
|
|
ut_assertok(ext4l_write("/newfile.txt", (void *)test_data, 0,
|
|
test_len, &actwrite));
|
|
ut_asserteq(test_len, actwrite);
|
|
|
|
/* Verify the file exists and has correct size */
|
|
ut_asserteq(1, ext4l_exists("/newfile.txt"));
|
|
ut_assertok(ext4l_size("/newfile.txt", &size));
|
|
ut_asserteq(test_len, size);
|
|
|
|
/* Read back and verify contents */
|
|
memset(read_buf, '\0', sizeof(read_buf));
|
|
ut_assertok(ext4l_read("/newfile.txt", read_buf, 0, 0, &actread));
|
|
ut_asserteq(test_len, actread);
|
|
ut_asserteq_str(test_data, read_buf);
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_write_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_unlink_norun() - Test ext4l_unlink function
|
|
*
|
|
* Verifies that ext4l can delete files from the filesystem.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_unlink_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
const char *test_data = "unlink test\n";
|
|
size_t test_len = strlen(test_data);
|
|
loff_t actwrite;
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Create a new file to unlink */
|
|
ut_assertok(ext4l_write("/unlinkme.txt", (void *)test_data, 0,
|
|
test_len, &actwrite));
|
|
ut_asserteq(test_len, actwrite);
|
|
|
|
/* Verify file exists (same mount) */
|
|
ut_asserteq(1, ext4l_exists("/unlinkme.txt"));
|
|
|
|
/* Unlink the file */
|
|
ut_assertok(ext4l_unlink("/unlinkme.txt"));
|
|
|
|
/* Verify file no longer exists */
|
|
ut_asserteq(0, ext4l_exists("/unlinkme.txt"));
|
|
|
|
/* Verify unlinking non-existent file returns -ENOENT */
|
|
ut_asserteq(-ENOENT, ext4l_unlink("/nonexistent"));
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_unlink_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_mkdir_norun() - Test ext4l_mkdir function
|
|
*
|
|
* Verifies that ext4l can create directories on the filesystem.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_mkdir_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
static int test_counter;
|
|
char dir_name[32];
|
|
char subdir_name[64];
|
|
int ret;
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Use unique directory names to avoid issues with test re-runs */
|
|
snprintf(dir_name, sizeof(dir_name), "/testdir%d", test_counter);
|
|
snprintf(subdir_name, sizeof(subdir_name), "%s/subdir", dir_name);
|
|
test_counter++;
|
|
|
|
/* Create a new directory */
|
|
ret = ext4l_mkdir(dir_name);
|
|
ut_assertok(ret);
|
|
|
|
/* Verify directory exists */
|
|
ut_asserteq(1, ext4l_exists(dir_name));
|
|
|
|
/* Verify creating duplicate returns -EEXIST */
|
|
ut_asserteq(-EEXIST, ext4l_mkdir(dir_name));
|
|
|
|
/* Create nested directory */
|
|
ut_assertok(ext4l_mkdir(subdir_name));
|
|
ut_asserteq(1, ext4l_exists(subdir_name));
|
|
|
|
/* Verify creating directory in non-existent parent returns -ENOENT */
|
|
ut_asserteq(-ENOENT, ext4l_mkdir("/nonexistent/dir"));
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_mkdir_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_ln_norun() - Test ext4l_ln function
|
|
*
|
|
* Verifies that ext4l can create symbolic links on the filesystem.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_ln_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
static int test_counter;
|
|
char link_name[32];
|
|
const char *target = "/testfile.txt";
|
|
loff_t size;
|
|
loff_t actread;
|
|
char buf[32];
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Use unique symlink names to avoid issues with test re-runs */
|
|
snprintf(link_name, sizeof(link_name), "/testlink%d", test_counter);
|
|
test_counter++;
|
|
|
|
/*
|
|
* Create a symbolic link. ext4l_ln follows U-Boot's ln command
|
|
* convention: ext4l_ln(target, linkname) creates linkname pointing
|
|
* to target.
|
|
*/
|
|
ut_assertok(ext4l_ln(target, link_name));
|
|
|
|
/* Verify symlink exists */
|
|
ut_asserteq(1, ext4l_exists(link_name));
|
|
|
|
/*
|
|
* Size through symlink should be target file's size (12 bytes),
|
|
* since ext4l_resolve_path follows symlinks (like stat, not lstat)
|
|
*/
|
|
ut_assertok(ext4l_size(link_name, &size));
|
|
ut_asserteq(12, size);
|
|
|
|
/* Verify we can read through the symlink */
|
|
memset(buf, '\0', sizeof(buf));
|
|
ut_assertok(ext4l_read(link_name, buf, 0, 0, &actread));
|
|
ut_asserteq(12, actread);
|
|
ut_asserteq_str("hello world\n", buf);
|
|
|
|
/* Verify creating duplicate succeeds (like ln -sf) */
|
|
ut_assertok(ext4l_ln(target, link_name));
|
|
|
|
/* Verify creating symlink in non-existent parent returns -ENOENT */
|
|
ut_asserteq(-ENOENT, ext4l_ln(target, "/nonexistent/link"));
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_ln_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|
|
|
|
/**
|
|
* fs_test_ext4l_rename_norun() - Test ext4l_rename function
|
|
*
|
|
* Verifies that ext4l can rename files and directories on the filesystem.
|
|
*
|
|
* Arguments:
|
|
* fs_image: Path to the ext4 filesystem image
|
|
*/
|
|
static int fs_test_ext4l_rename_norun(struct unit_test_state *uts)
|
|
{
|
|
const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
|
|
const char *test_data = "rename test\n";
|
|
size_t test_len = strlen(test_data);
|
|
static int test_counter;
|
|
char old_name[32], new_name[32], subdir_name[32], moved_name[64];
|
|
loff_t actwrite, size;
|
|
|
|
ut_assertnonnull(fs_image);
|
|
ut_assertok(run_commandf("host bind 0 %s", fs_image));
|
|
ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY));
|
|
|
|
/* Use unique names to avoid issues with test re-runs */
|
|
snprintf(old_name, sizeof(old_name), "/renameme%d.txt", test_counter);
|
|
snprintf(new_name, sizeof(new_name), "/renamed%d.txt", test_counter);
|
|
snprintf(subdir_name, sizeof(subdir_name), "/renamedir%d", test_counter);
|
|
snprintf(moved_name, sizeof(moved_name), "%s/moved.txt", subdir_name);
|
|
test_counter++;
|
|
|
|
/* Create a file to rename */
|
|
ut_assertok(ext4l_write(old_name, (void *)test_data, 0,
|
|
test_len, &actwrite));
|
|
ut_asserteq(test_len, actwrite);
|
|
|
|
/* Verify file exists */
|
|
ut_asserteq(1, ext4l_exists(old_name));
|
|
|
|
/* Rename the file */
|
|
ut_assertok(ext4l_rename(old_name, new_name));
|
|
|
|
/* Verify old name no longer exists, new name does */
|
|
ut_asserteq(0, ext4l_exists(old_name));
|
|
ut_asserteq(1, ext4l_exists(new_name));
|
|
|
|
/* Verify file size is preserved */
|
|
ut_assertok(ext4l_size(new_name, &size));
|
|
ut_asserteq(test_len, size);
|
|
|
|
/* Verify renaming non-existent file returns -ENOENT */
|
|
ut_asserteq(-ENOENT, ext4l_rename("/nonexistent", "/newname"));
|
|
|
|
/* Test cross-directory rename */
|
|
ut_assertok(ext4l_mkdir(subdir_name));
|
|
ut_assertok(ext4l_rename(new_name, moved_name));
|
|
ut_asserteq(0, ext4l_exists(new_name));
|
|
ut_asserteq(1, ext4l_exists(moved_name));
|
|
|
|
return 0;
|
|
}
|
|
FS_TEST_ARGS(fs_test_ext4l_rename_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
|
|
{ "fs_image", UT_ARG_STR });
|