diff --git a/cpukit/libfs/src/dosfs/fat_file.c b/cpukit/libfs/src/dosfs/fat_file.c index 3384818801..31c74abaf6 100644 --- a/cpukit/libfs/src/dosfs/fat_file.c +++ b/cpukit/libfs/src/dosfs/fat_file.c @@ -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 diff --git a/cpukit/libfs/src/dosfs/fat_file.h b/cpukit/libfs/src/dosfs/fat_file.h index 6e39b7fcb3..78e4cea8b2 100644 --- a/cpukit/libfs/src/dosfs/fat_file.h +++ b/cpukit/libfs/src/dosfs/fat_file.h @@ -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 diff --git a/cpukit/libfs/src/dosfs/msdos.h b/cpukit/libfs/src/dosfs/msdos.h index e6d650c8d5..7f0e9ab423 100644 --- a/cpukit/libfs/src/dosfs/msdos.h +++ b/cpukit/libfs/src/dosfs/msdos.h @@ -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 */ diff --git a/cpukit/libfs/src/dosfs/msdos_create.c b/cpukit/libfs/src/dosfs/msdos_create.c index f71969f787..a6b02646ae 100644 --- a/cpukit/libfs/src/dosfs/msdos_create.c +++ b/cpukit/libfs/src/dosfs/msdos_create.c @@ -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 diff --git a/cpukit/libfs/src/dosfs/msdos_rename.c b/cpukit/libfs/src/dosfs/msdos_rename.c index 375ca80362..e679b077c9 100644 --- a/cpukit/libfs/src/dosfs/msdos_rename.c +++ b/cpukit/libfs/src/dosfs/msdos_rename.c @@ -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; diff --git a/testsuites/fstests/fsrename/test.c b/testsuites/fstests/fsrename/test.c index bdb3a7a3de..3da7573768 100644 --- a/testsuites/fstests/fsrename/test.c +++ b/testsuites/fstests/fsrename/test.c @@ -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 ();