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
85 changes: 83 additions & 2 deletions common/src/fx_directory_rename.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
/* If found, the rename request is valid and the directory will be */
/* changed to the new name. Otherwise, if the directory is not found, */
/* the appropriate error code is returned to the caller. */
/* If the directory is moved under a different parent, the ".." */
/* entry inside the directory is updated to name the new parent. */
/* */
/* INPUT */
/* */
Expand All @@ -64,6 +66,7 @@
/* */
/* CALLS */
/* */
/* _fx_directory_entry_read Read entries from directory */
/* _fx_directory_entry_write Write the new directory entry */
/* _fx_directory_free_search Search for a free directory */
/* entry */
Expand All @@ -90,6 +93,7 @@ FX_DIR_ENTRY new_dir_entry;
FX_DIR_ENTRY search_directory;
CHAR *new_name_ptr;
ULONG i;
ULONG parent_cluster;
CHAR *work_ptr;
CHAR alpha, beta;
#ifdef FX_RENAME_PATH_INHERIT
Expand Down Expand Up @@ -451,20 +455,97 @@ UINT j;
/* Now wipe out the old directory entry. */
status = _fx_directory_entry_write(media_ptr, &old_dir_entry);

/* Determine if the write was successful. */
if (status != FX_SUCCESS)
{

#ifdef FX_ENABLE_FAULT_TOLERANT
/* Check for a bad status. */
FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
#endif /* FX_ENABLE_FAULT_TOLERANT */

/* Release media protection. */
FX_UNPROTECT

/* Return the error code. */
return(status);
}

/* The renamed directory itself contains a ".." entry, which stores
the starting cluster of its parent directory. If the rename moved
the directory under a different parent, that stored cluster is
stale now: ".." inside the renamed directory would still lead to
the old parent and, once the old parent's cluster is reused, to an
unrelated place. Update it in place. */

/* Determine the starting cluster of the new parent directory.
An empty name in the search directory marks the root
directory - the name is the only root indication, the other
fields keep earlier values in that case. A ".." entry stores
its parent's starting cluster; when the parent is the root
directory, it stores cluster 0. */
if (search_directory.fx_dir_entry_name[0])
{

/* New parent is a sub-directory. */
parent_cluster = search_directory.fx_dir_entry_cluster;
}
else
{

/* New parent is the root directory. */
parent_cluster = 0;
}

/* old_dir_entry is no longer needed and still holds the starting
cluster of the renamed directory - reuse it to address that
directory as the one to read from. Ensure the cluster chain is
walked from the start. */
old_dir_entry.fx_dir_entry_last_search_cluster = 0;

/* Read the second directory entry of the renamed directory - its
".." entry, created together with the directory. new_dir_entry
is no longer needed either and serves as the destination. */
i = 1;
status = _fx_directory_entry_read(media_ptr, &old_dir_entry, &i, &new_dir_entry);

/* Update the entry only if it is the ".." entry the FAT format
places at this position - on a non-conformant media the rename
result is left as it is - and only if the stored cluster does
not match the new parent already. A rename inside one parent
ends here without an additional write. */
if ((status == FX_SUCCESS) &&
(new_dir_entry.fx_dir_entry_name[0] == '.') &&
(new_dir_entry.fx_dir_entry_name[1] == '.') &&
(new_dir_entry.fx_dir_entry_name[2] == 0) &&
(new_dir_entry.fx_dir_entry_cluster != parent_cluster))
{

/* Update the stored parent cluster. Everything else of the
entry - its position included - stays untouched: the first
two entries of a directory are a format invariant other
services rely on. */
new_dir_entry.fx_dir_entry_cluster = parent_cluster;

/* Write the updated ".." entry back. */
status = _fx_directory_entry_write(media_ptr, &new_dir_entry);
}

/* Determine if the ".." update was successful. */
if (status != FX_SUCCESS)
{

#ifdef FX_ENABLE_FAULT_TOLERANT
FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
#endif /* FX_ENABLE_FAULT_TOLERANT */

/* Release media protection. */
FX_UNPROTECT

/* Return the bad status. */
/* Return the error code. */
return(status);
}

#ifdef FX_ENABLE_FAULT_TOLERANT
/* End transaction. */
status = _fx_fault_tolerant_transaction_end(media_ptr);
#endif /* FX_ENABLE_FAULT_TOLERANT */
Expand Down
225 changes: 225 additions & 0 deletions test/regression_test/filex_directory_rename_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "tx_api.h"
#endif
#include "fx_api.h"
#include "fx_directory.h"
#include "fx_ram_driver_test.h"
#include "fx_fault_tolerant.h"
#include "fx_utility.h"
Expand All @@ -35,6 +36,7 @@
static TX_THREAD ftest_0;
#endif
static FX_MEDIA ram_disk;
static FX_FILE probe_file;
static CHAR max_name[FX_MAX_LONG_NAME_LEN + 2];
static CHAR max_newname[FX_MAX_LONG_NAME_LEN + 1];

Expand All @@ -55,6 +57,9 @@ static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE];

void filex_directory_rename_application_define(void *first_unused_memory);
static void ftest_0_entry(ULONG thread_input);
static UINT dot_dot_probe(CHAR *marker_path);
static UINT directory_cluster_read(CHAR *directory_path, ULONG *cluster_ptr);
static UINT dot_dot_cluster_read(CHAR *directory_path, ULONG *cluster_ptr);

VOID _fx_ram_driver(FX_MEDIA *media_ptr);
void test_control_return(UINT status);
Expand Down Expand Up @@ -102,13 +107,102 @@ void filex_directory_rename_application_define(void *first_unused_memory)



/* Open and close the file at marker_path - a path that contains a ".."
component - to report where ".." currently leads. */
static UINT dot_dot_probe(CHAR *marker_path)
{

UINT status;

status = fx_file_open(&ram_disk, &probe_file, marker_path, FX_OPEN_FOR_READ);
if (status == FX_SUCCESS)
{
fx_file_close(&probe_file);
}

return(status);
}


/* Return the starting cluster of the directory at directory_path. */
static UINT directory_cluster_read(CHAR *directory_path, ULONG *cluster_ptr)
{

UINT status;
FX_DIR_ENTRY dir_entry;

/* The directory's own entry carries its starting cluster. */
dir_entry.fx_dir_entry_name = ram_disk.fx_media_name_buffer + FX_MAX_LONG_NAME_LEN;
dir_entry.fx_dir_entry_short_name[0] = 0;
status = _fx_directory_search(&ram_disk, directory_path, &dir_entry, FX_NULL, FX_NULL);

/* Return the cluster only on success. */
if (status == FX_SUCCESS)
{
*cluster_ptr = dir_entry.fx_dir_entry_cluster;
}

return(status);
}


/* Read the ".." entry - entry 1 - of the directory at directory_path and
return the parent starting cluster stored in it. Verifies that the
entry actually reads back as "..". */
static UINT dot_dot_cluster_read(CHAR *directory_path, ULONG *cluster_ptr)
{

UINT status;
ULONG entry_index;
FX_DIR_ENTRY dir_entry;
FX_DIR_ENTRY dot_dot_entry;

/* Find the directory itself first. */
dir_entry.fx_dir_entry_name = ram_disk.fx_media_name_buffer + FX_MAX_LONG_NAME_LEN;
dir_entry.fx_dir_entry_short_name[0] = 0;
status = _fx_directory_search(&ram_disk, directory_path, &dir_entry, FX_NULL, FX_NULL);
if (status != FX_SUCCESS)
{
return(status);
}

/* Read its second directory entry - the ".." entry. */
dir_entry.fx_dir_entry_last_search_cluster = 0;
dot_dot_entry.fx_dir_entry_name = ram_disk.fx_media_name_buffer + FX_MAX_LONG_NAME_LEN * 2;
dot_dot_entry.fx_dir_entry_short_name[0] = 0;
entry_index = 1;
status = _fx_directory_entry_read(&ram_disk, &dir_entry, &entry_index, &dot_dot_entry);
if (status != FX_SUCCESS)
{
return(status);
}

/* Anything other than ".." at this position is a failure. */
if ((dot_dot_entry.fx_dir_entry_name[0] != '.') ||
(dot_dot_entry.fx_dir_entry_name[1] != '.') ||
(dot_dot_entry.fx_dir_entry_name[2] != 0))
{
return(FX_INVALID_STATE);
}

*cluster_ptr = dot_dot_entry.fx_dir_entry_cluster;

return(FX_SUCCESS);
}


/* Define the test threads. */

static void ftest_0_entry(ULONG thread_input)
{

UINT status;
UINT i;
ULONG parent_cluster;
ULONG dot_dot_cluster;
#ifndef FX_MEDIA_STATISTICS_DISABLE
ULONG entry_writes_before;
#endif

FX_PARAMETER_NOT_USED(thread_input);

Expand Down Expand Up @@ -296,6 +390,137 @@ static void ftest_0_entry(ULONG thread_input)
status = fx_media_close(&ram_disk);
return_if_fail( status == FX_SUCCESS);

/* Format the media again: the checks below build their own fixture
and must not disturb - or depend on - the directory layout the
error-injection steps above are calibrated against. */
status = fx_media_format(&ram_disk,
_fx_ram_driver, // Driver entry
ram_disk_memory, // RAM disk memory pointer
cache_buffer, // Media buffer pointer
CACHE_SIZE, // Media buffer size
"MY_RAM_DISK", // Volume Name
1, // Number of FATs
128, // Directory Entries
0, // Hidden sectors

#ifdef FX_ENABLE_FAULT_TOLERANT
4096 * 8, // Total sectors
256, // Sector size
8, // Sectors per cluster
#else
4096, // Total sectors
128, // Sector size
1, // Sectors per cluster
#endif

1, // Heads
1); // Sectors per track
status += fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, cache_buffer, CACHE_SIZE);
return_if_fail( status == FX_SUCCESS);

#ifdef FX_ENABLE_FAULT_TOLERANT
status = fx_fault_tolerant_enable(&ram_disk, fault_tolerant_buffer, FAULT_TOLERANT_SIZE);
return_if_fail( status == FX_SUCCESS);
#endif

/* Moving a directory to a different parent has to update the ".."
entry inside the moved directory: it stores the starting cluster
of the parent directory - cluster 0 when the parent is the root
directory. Exercise every parent transition and verify both the
path resolution through ".." and the on-media entry itself. */
status = fx_directory_create(&ram_disk, "/DOTA");
status += fx_directory_create(&ram_disk, "/DOTB");
status += fx_file_create(&ram_disk, "/MARKR.TXT");
status += fx_file_create(&ram_disk, "/DOTA/MARKA.TXT");
status += fx_file_create(&ram_disk, "/DOTB/MARKB.TXT");
status += fx_directory_create(&ram_disk, "/DOTA/SUB");
return_if_fail( status == FX_SUCCESS);

/* The probe itself has to work before anything is moved: ".." of a
new directory leads to the parent it was created in. */
status = dot_dot_probe("/DOTA/SUB/../MARKA.TXT");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_probe("/DOTA/SUB/../MARKB.TXT");
return_if_fail( status == FX_NOT_FOUND);

/* Move the directory to a different parent directory. Exactly one
directory entry write - the ".." update - is added to the two
parent entry writes. */
#ifndef FX_MEDIA_STATISTICS_DISABLE
entry_writes_before = ram_disk.fx_media_directory_entry_writes;
#endif
status = fx_directory_rename(&ram_disk, "/DOTA/SUB", "/DOTB/SUB");
return_if_fail( status == FX_SUCCESS);
#ifndef FX_MEDIA_STATISTICS_DISABLE
return_if_fail( (ram_disk.fx_media_directory_entry_writes - entry_writes_before) == 3);
#endif

/* ".." now leads to the new parent and only to the new parent. */
status = dot_dot_probe("/DOTB/SUB/../MARKB.TXT");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_probe("/DOTB/SUB/../MARKA.TXT");
return_if_fail( status == FX_NOT_FOUND);

/* The on-media ".." entry stores the new parent's starting cluster. */
status = directory_cluster_read("/DOTB", &parent_cluster);
return_if_fail( status == FX_SUCCESS);
status = dot_dot_cluster_read("/DOTB/SUB", &dot_dot_cluster);
return_if_fail( status == FX_SUCCESS);
return_if_fail( (dot_dot_cluster == parent_cluster) && (parent_cluster != 0));

/* Move the directory into the root directory: ".." stores cluster 0
there, per the FAT convention directory create itself writes. */
status = fx_directory_rename(&ram_disk, "/DOTB/SUB", "/SUB");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_probe("/SUB/../MARKR.TXT");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_probe("/SUB/../MARKB.TXT");
return_if_fail( status == FX_NOT_FOUND);
status = dot_dot_cluster_read("/SUB", &dot_dot_cluster);
return_if_fail( (status == FX_SUCCESS) && (dot_dot_cluster == 0));

/* Move the directory back out of the root directory: 0 becomes the
new parent's cluster again. */
status = fx_directory_rename(&ram_disk, "/SUB", "/DOTA/SUB");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_probe("/DOTA/SUB/../MARKA.TXT");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_probe("/DOTA/SUB/../MARKR.TXT");
return_if_fail( status == FX_NOT_FOUND);
status = directory_cluster_read("/DOTA", &parent_cluster);
return_if_fail( status == FX_SUCCESS);
status = dot_dot_cluster_read("/DOTA/SUB", &dot_dot_cluster);
return_if_fail( (status == FX_SUCCESS) && (dot_dot_cluster == parent_cluster) && (parent_cluster != 0));

/* A rename inside one parent does not touch ".." - and adds no third
directory entry write. */
status = fx_directory_create(&ram_disk, "/DOTC");
return_if_fail( status == FX_SUCCESS);
#ifndef FX_MEDIA_STATISTICS_DISABLE
entry_writes_before = ram_disk.fx_media_directory_entry_writes;
#endif
status = fx_directory_rename(&ram_disk, "/DOTC", "/DOTD");
return_if_fail( status == FX_SUCCESS);
#ifndef FX_MEDIA_STATISTICS_DISABLE
return_if_fail( (ram_disk.fx_media_directory_entry_writes - entry_writes_before) == 2);
#endif
status = dot_dot_probe("/DOTD/../MARKR.TXT");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_cluster_read("/DOTD", &dot_dot_cluster);
return_if_fail( (status == FX_SUCCESS) && (dot_dot_cluster == 0));

/* Same inside a sub-directory: ".." keeps the parent's cluster. */
status = fx_directory_rename(&ram_disk, "/DOTA/SUB", "/DOTA/SUB2");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_probe("/DOTA/SUB2/../MARKA.TXT");
return_if_fail( status == FX_SUCCESS);
status = dot_dot_cluster_read("/DOTA/SUB2", &dot_dot_cluster);
return_if_fail( (status == FX_SUCCESS) && (dot_dot_cluster == parent_cluster));

/* Close the media. */
status = fx_media_close(&ram_disk);
return_if_fail( status == FX_SUCCESS);

printf("SUCCESS!\n");
test_control_return(0);
}