Add logic to VFS rename: If target of rename exists and is a directory, then the source file should be moved 'under' the target directory. POSIX also requires that if the target is a file, then that old file must be deleted.
This commit is contained in:
parent
af5a8e73d3
commit
bd7d3a92f5
@ -2419,7 +2419,10 @@ int fat_rename(FAR struct inode *mountpt, FAR const char *oldrelpath,
|
||||
{
|
||||
if (ret == OK)
|
||||
{
|
||||
/* It is an error if the object at newrelpath already exists */
|
||||
/* It is an error if the directory entry at newrelpath already
|
||||
* exists. The necessary steps to avoid this case should have
|
||||
* been handled by higher level logic in the VFS.
|
||||
*/
|
||||
|
||||
ret = -EEXIST;
|
||||
}
|
||||
|
@ -1890,9 +1890,6 @@ int smartfs_rename(struct inode *mountpt, const char *oldrelpath,
|
||||
const char *newfilename;
|
||||
mode_t mode;
|
||||
uint16_t type;
|
||||
uint16_t sector;
|
||||
uint16_t offset;
|
||||
uint16_t entrysize;
|
||||
struct smartfs_entry_header_s *direntry;
|
||||
struct smart_read_write_s readwrite;
|
||||
|
||||
@ -1911,94 +1908,25 @@ int smartfs_rename(struct inode *mountpt, const char *oldrelpath,
|
||||
oldentry.name = NULL;
|
||||
newentry.name = NULL;
|
||||
ret = smartfs_finddirentry(fs, &oldentry, oldrelpath, &oldparentdirsector,
|
||||
&oldfilename);
|
||||
&oldfilename);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* Search for the new entry and validate it DOESN'T exist, unless we
|
||||
* are copying to a directory and keeping the same filename, such as:
|
||||
*
|
||||
* mv /mntpoint/somefile.txt /mntpoint/somedir
|
||||
*
|
||||
* in which case, we need to validate the /mntpoint/somedir/somefile.txt
|
||||
* doens't exsit.
|
||||
*/
|
||||
/* Search for the new entry and validate it DOESN'T exist. */
|
||||
|
||||
ret = smartfs_finddirentry(fs, &newentry, newrelpath, &newparentdirsector,
|
||||
&newfilename);
|
||||
&newfilename);
|
||||
if (ret == OK)
|
||||
{
|
||||
/* Test if it's a file. If it is, then it's an error */
|
||||
/* It is an error if the directory entry at newrelpath already
|
||||
* exists. The necessary steps to avoid this case should have been
|
||||
* handled by higher level logic in the VFS.
|
||||
*/
|
||||
|
||||
if ((newentry.flags & SMARTFS_DIRENT_TYPE) == SMARTFS_DIRENT_TYPE_FILE)
|
||||
{
|
||||
ret = -EEXIST;
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* Nope, it's a directory. Now search the directory for oldfilename */
|
||||
|
||||
sector = newentry.firstsector;
|
||||
while (sector != SMARTFS_ERASEDSTATE_16BIT)
|
||||
{
|
||||
/* Read the next sector of diretory entries */
|
||||
|
||||
readwrite.logsector = sector;
|
||||
readwrite.offset = 0;
|
||||
readwrite.count = fs->fs_llformat.availbytes;
|
||||
readwrite.buffer = (uint8_t *) fs->fs_rwbuffer;
|
||||
ret = FS_IOCTL(fs, BIOC_READSECT, (unsigned long) &readwrite);
|
||||
if (ret < 0)
|
||||
{
|
||||
ferr("ERROR: Error %d reading sector %d data\n",
|
||||
ret, oldentry.dsector);
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* Search for newfilename in this sector */
|
||||
|
||||
offset = sizeof(struct smartfs_chain_header_s);
|
||||
entrysize = sizeof(struct smartfs_entry_header_s) + fs->fs_llformat.namesize;
|
||||
while (offset + entrysize < fs->fs_llformat.availbytes)
|
||||
{
|
||||
/* Test the next entry */
|
||||
|
||||
direntry = (struct smartfs_entry_header_s *) &fs->fs_rwbuffer[offset];
|
||||
if (((direntry->flags & SMARTFS_DIRENT_EMPTY) ==
|
||||
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_EMPTY)) ||
|
||||
((direntry->flags & SMARTFS_DIRENT_ACTIVE) !=
|
||||
(SMARTFS_ERASEDSTATE_16BIT & SMARTFS_DIRENT_ACTIVE)))
|
||||
{
|
||||
/* This entry isn't valid, skip it */
|
||||
|
||||
offset += entrysize;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Test if this entry matches newfilename */
|
||||
|
||||
if (strcmp(newfilename, direntry->name) == 0)
|
||||
{
|
||||
/* Uh-oh, looks like the entry already exists */
|
||||
|
||||
ret = -EEXIST;
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
offset += entrysize;
|
||||
}
|
||||
|
||||
/* Chain to next sector */
|
||||
|
||||
sector = SMARTFS_NEXTSECTOR(((struct smartfs_chain_header_s *)fs->fs_rwbuffer));
|
||||
}
|
||||
|
||||
/* Okay, we will create newfilename in the newentry directory */
|
||||
|
||||
newparentdirsector = newentry.firstsector;
|
||||
newfilename = oldfilename;
|
||||
ret = -EEXIST;
|
||||
goto errout_with_semaphore;
|
||||
}
|
||||
|
||||
/* Test if the new parent directory is valid */
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <libgen.h>
|
||||
@ -95,6 +96,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode,
|
||||
* first, provided that it is not a directory.
|
||||
*/
|
||||
|
||||
next_subdir:
|
||||
SETUP_SEARCH(&newdesc, newpath, true);
|
||||
ret = inode_find(&newdesc);
|
||||
if (ret >= 0)
|
||||
@ -121,6 +123,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode,
|
||||
if (newinode->i_child != NULL)
|
||||
{
|
||||
FAR char *subdirname;
|
||||
FAR char *tmp;
|
||||
|
||||
/* Yes.. In this case, the target of the rename must be a
|
||||
* subdirectory of newinode, not the newinode itself. For
|
||||
@ -128,7 +131,16 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode,
|
||||
*/
|
||||
|
||||
subdirname = basename((FAR char *)oldpath);
|
||||
tmp = subdir;
|
||||
subdir = NULL;
|
||||
|
||||
(void)asprintf(&subdir, "%s/%s", newpath, subdirname);
|
||||
|
||||
if (tmp != NULL)
|
||||
{
|
||||
kmm_free(tmp);
|
||||
}
|
||||
|
||||
if (subdir == NULL)
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
@ -137,10 +149,12 @@ static int pseudorename(FAR const char *oldpath, FAR struct inode *oldinode,
|
||||
|
||||
newpath = subdir;
|
||||
|
||||
/* REVISIT: This can be a recursive case, another inode may
|
||||
* already exist at oldpth/subdirname. In that case, we need
|
||||
* to do this all over again.
|
||||
/* This can be a recursive case, another inode may already exist
|
||||
* at oldpth/subdirname. In that case, we need to do this all
|
||||
* over again. A nasty goto is used because I am lazy.
|
||||
*/
|
||||
|
||||
goto next_subdir;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -252,10 +266,19 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode,
|
||||
{
|
||||
struct inode_search_s newdesc;
|
||||
FAR struct inode *newinode;
|
||||
FAR const char *newrelpath;
|
||||
FAR char *subdir = NULL;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(oldinode->u.i_mops);
|
||||
|
||||
/* If the file system does not support the rename() method, then bail now. */
|
||||
|
||||
if (oldinode->u.i_mops->rename == NULL)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/* Get an inode for the new relpath -- it should lie on the same
|
||||
* mountpoint
|
||||
*/
|
||||
@ -272,7 +295,8 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode,
|
||||
|
||||
/* Get the search results */
|
||||
|
||||
newinode = newdesc.node;
|
||||
newinode = newdesc.node;
|
||||
newrelpath = newdesc.relpath;
|
||||
DEBUGASSERT(newinode != NULL);
|
||||
|
||||
/* Verify that the two paths lie on the same mountpoint inode */
|
||||
@ -283,27 +307,84 @@ static int mountptrename(FAR const char *oldpath, FAR struct inode *oldinode,
|
||||
goto errout_with_newinode;
|
||||
}
|
||||
|
||||
/* Perform the rename operation using the relative paths at the common
|
||||
/* Does a directory entry already exist at the 'rewrelpath'?
|
||||
*
|
||||
* If the directy entry at the newrelpath is a regular file, then that
|
||||
* file should remove removed first.
|
||||
*
|
||||
* If the directory entry at the target is a directory, then the source
|
||||
* file should be moved "under" the directory, i.e., if newrelpath is a
|
||||
* directory, then rename(b,a) should use move the olrelpath should be
|
||||
* moved as if rename(b,a/basename(b)) had been called.
|
||||
*/
|
||||
|
||||
if (oldinode->u.i_mops->stat != NULL)
|
||||
{
|
||||
struct stat buf;
|
||||
|
||||
next_subdir:
|
||||
ret = oldinode->u.i_mops->stat(oldinode, newrelpath, &buf);
|
||||
if (ret >= 0)
|
||||
{
|
||||
/* Something exists for this directory entry. Is it a directory? */
|
||||
|
||||
if (S_ISDIR(buf.st_mode))
|
||||
{
|
||||
FAR char *subdirname;
|
||||
FAR char *tmp;
|
||||
|
||||
/* Yes.. In this case, the target of the rename must be a
|
||||
* subdirectory of newinode, not the newinode itself. For
|
||||
* example: mv b a/ must move b to a/b.
|
||||
*/
|
||||
|
||||
subdirname = basename((FAR char *)oldrelpath);
|
||||
tmp = subdir;
|
||||
subdir = NULL;
|
||||
|
||||
(void)asprintf(&subdir, "%s/%s", newrelpath, subdirname);
|
||||
|
||||
if (tmp != NULL)
|
||||
{
|
||||
kmm_free(tmp);
|
||||
}
|
||||
|
||||
if (subdir == NULL)
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
goto errout_with_newinode;
|
||||
}
|
||||
|
||||
newrelpath = subdir;
|
||||
|
||||
/* This can be a recursive case, another inode may already
|
||||
* exist at oldpth/subdirname. In that case, we need to
|
||||
* do this all over again. A nasty goto is used because
|
||||
* I am lazy.
|
||||
*/
|
||||
|
||||
goto next_subdir;
|
||||
}
|
||||
else if (oldinode->u.i_mops->unlink)
|
||||
{
|
||||
/* No.. newrelpath must refer to a regular file. Attempt
|
||||
* to remove the file before doing the rename.
|
||||
*
|
||||
* NOTE that errors are not handled here. If we failed to
|
||||
* remove the file, then the file system 'rename' method
|
||||
* should check that.
|
||||
*/
|
||||
|
||||
(void)oldinode->u.i_mops->unlink(oldinode, newrelpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform the rename operation using the relative paths at the common
|
||||
* mountpoint.
|
||||
*/
|
||||
|
||||
if (oldinode->u.i_mops->rename)
|
||||
{
|
||||
ret = oldinode->u.i_mops->rename(oldinode, oldrelpath, newdesc.relpath);
|
||||
if (ret < 0)
|
||||
{
|
||||
goto errout_with_newinode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -ENOSYS;
|
||||
goto errout_with_newinode;
|
||||
}
|
||||
|
||||
/* Successfully renamed */
|
||||
|
||||
ret = OK;
|
||||
ret = oldinode->u.i_mops->rename(oldinode, oldrelpath, newrelpath);
|
||||
|
||||
errout_with_newinode:
|
||||
inode_release(newinode);
|
||||
@ -311,6 +392,11 @@ errout_with_newinode:
|
||||
errout_with_newsearch:
|
||||
RELEASE_SEARCH(&newdesc);
|
||||
|
||||
if (subdir != NULL)
|
||||
{
|
||||
kmm_free(subdir);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_DISABLE_MOUNTPOINT */
|
||||
|
Loading…
Reference in New Issue
Block a user