msdos: update inode during rename

rename now actively changes the inode of the file being renamed.
This change has been done to fix an issue when the file being renamed is already opened before the rename, in particular failing to update the inode number (and the two lookup tables) could cause at least two problems:
1) A new file created in the same source folder of the file being renamed may get the same fat file descriptor of the renamed file
2) An open to the renamed file opens a new fat file descriptor, thus putting out of sync the operations on the file (two distinct fat file descriptor working on the same clusters and entries)

Previously the inode was in any case changed once the file was closed.

Another possible solution would have been to separate the inode number from the lookup table, but this solution would have required much more memory because it needed to keep track of all files seen in the file system up to the last operation and not only the opened ones.

Closes #5044
This commit is contained in:
Loris Nardo
2024-06-16 07:38:22 +00:00
committed by Kinsey Moore
parent 1fe3d396d3
commit a558c96c64
6 changed files with 138 additions and 3 deletions

View File

@@ -147,6 +147,88 @@ fat_file_open(
return RC_OK;
}
/* fat_file_get_new_inode_for --
* Get a new inode for the fat file descriptor that is being
* moved to the new directory position.
* Firstly, it release the old inode of the fat file descriptor
* taking also care to remove the mapping from the "vhash" table
* Then it obtain a new inode corresponging to the new directory position
* inserting the new mapping into the "vhash" table.
* The function takes the responsability to update the inode number and
* the directory position stored into the fat file descriptor.
*
* PARAMETERS:
* fs_info - FS info
* new_dir_pos - the new directory position for the fat file descriptor
* fat_fd - the fat file descriptor for which the inode has to be changed
*
* RETURNS:
* RC_OK or an error
*/
int
fat_file_get_new_inode_for(
fat_fs_info_t *fs_info,
fat_dir_pos_t *new_dir_pos,
fat_file_fd_t *fat_fd
)
{
fat_file_fd_t *lfat_fd = NULL;
uint32_t old_key = 0;
uint32_t new_key = 0;
uint32_t old_inode = 0;
uint32_t new_inode = 0;
int rc = RC_OK;
/* construct the old key to later perform the due lookups */
old_key = fat_construct_key(fs_info, &fat_fd->dir_pos.sname);
/* construct the new key to later perform the due lookups */
new_key = fat_construct_key(fs_info, &new_dir_pos->sname);
/*
* access "valid" hash table with the new key,
* it is expected that no file can have such key, otherwise
* it means that the caller is renaming a file to an already
* existing destination.
*/
rc = _hash_search(fs_info, fs_info->vhash, new_key, 0, &lfat_fd);
assert(rc != RC_OK);
/*
* Remove from the valid table the old mapping of the inode to the
* fat file descriptor
*/
old_inode = fat_fd->ino;
_hash_delete(fs_info->vhash, old_key, old_inode, fat_fd);
/* Assign the new directory position to the fat file descriptor */
fat_fd->dir_pos = *new_dir_pos;
/*
* The old inode is no more required,
* Free it in case it has been allocated by fat_get_unique_ino
*/
if (fat_ino_is_unique(fs_info, old_inode))
fat_free_unique_ino(fs_info, old_inode);
/*
* Check if the new key can be used as an inode or a unique
* inode should be obtained because the new directory position
* may be a still opened but removed file
*/
rc = _hash_search(fs_info, fs_info->rhash, new_key, new_key, &lfat_fd);
if (rc != RC_OK) new_inode = new_key;
else new_inode = fat_get_unique_ino(fs_info);
/*
* Finally, insert the new inode in the hash table lookup and
* update the inode of the fat file descriptor.
*/
_hash_insert(fs_info->vhash, new_key, new_inode, fat_fd);
fat_fd->ino = new_inode;
return RC_OK;
}
/* fat_file_reopen --
* Increment by 1 number of links

View File

@@ -255,6 +255,11 @@ int
fat_file_update(fat_fs_info_t *fs_info,
fat_file_fd_t *fat_fd);
int
fat_file_get_new_inode_for(fat_fs_info_t *fs_info,
fat_dir_pos_t *new_dir_pos,
fat_file_fd_t *fat_fd);
#ifdef __cplusplus
}
#endif

View File

@@ -370,7 +370,7 @@ int msdos_creat_node(const rtems_filesystem_location_info_t *parent_loc,
const char *name,
int name_len,
mode_t mode,
const fat_file_fd_t *link_fd);
fat_file_fd_t *link_fd);
/* Misc prototypes */

View File

@@ -64,7 +64,7 @@ msdos_creat_node(const rtems_filesystem_location_info_t *parent_loc,
const char *name,
int name_len,
mode_t mode,
const fat_file_fd_t *link_fd)
fat_file_fd_t *link_fd)
{
int rc = RC_OK;
ssize_t ret = 0;
@@ -174,6 +174,17 @@ msdos_creat_node(const rtems_filesystem_location_info_t *parent_loc,
if ( rc != RC_OK )
return rc;
/*
* if it is performing a rename, update also the inode to prevent issues
* in case the fat file descriptor is opened not only for the rename operation.
*/
if ( type == FAT_HARD_LINK )
{
rc = fat_file_get_new_inode_for(&fs_info->fat, &dir_pos, link_fd);
if ( rc != RC_OK )
return rc;
}
/*
* if we create a new file we are done, if directory there are more steps
* to do

View File

@@ -46,6 +46,7 @@ msdos_rename(
{
int rc = RC_OK;
fat_file_fd_t *old_fat_fd = old_loc->node_access;
fat_dir_pos_t old_pos = old_fat_fd->dir_pos;
/*
* create new directory entry as "hard link", copying relevant info from
@@ -63,7 +64,7 @@ msdos_rename(
* mark file removed
*/
rc = msdos_set_first_char4file_name(old_loc->mt_entry,
&old_fat_fd->dir_pos,
&old_pos,
MSDOS_THIS_DIR_ENTRY_EMPTY);
return rc;

View File

@@ -255,6 +255,41 @@ static void rename_file_twice_test (void)
EXPECT_EQUAL (0, unlink, name03);
}
static void rename_opened_file_test (void)
{
const char *name01 = "name01.txt";
const char *name02 = "name02.txt";
const char *message = "Test file content";
mode_t mode;
int fd1;
int fd2;
int result;
char buffer[16];
puts ("\nRename opened file and open a new file\n");
mode = S_IRWXU | S_IRWXG | S_IRWXO;
fd1 = open (name01, O_RDWR | O_CREAT, mode);
rtems_test_assert (fd1 >= 0);
result = write (fd1, message, strlen (message));
rtems_test_assert (result == strlen (message));
EXPECT_EQUAL (0, rename, name01, name02);
fd2 = open (name01, O_RDWR | O_CREAT, mode);
rtems_test_assert (fd1 >= 0);
EXPECT_EQUAL (0, read, fd2, buffer, sizeof (buffer));
result = close (fd1);
rtems_test_assert (result == 0);
result = close (fd2);
rtems_test_assert (result == 0);
EXPECT_EQUAL (0, unlink, name01);
EXPECT_EQUAL (0, unlink, name02);
}
static void same_file_test (void)
{
int fd;
@@ -1271,6 +1306,7 @@ void test (void)
rename_file_twice_test ();
same_file_test ();
directory_test ();
rename_opened_file_test ();
arg_test ();
arg_format_test ();
write_permission_test ();