Union FS. Add logic to omit duplicates in file system 2 when doing directory listing
This commit is contained in:
parent
05e6d9409d
commit
597493221e
@ -45,6 +45,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
@ -133,6 +134,8 @@ static int unionfs_trystatdir(FAR struct inode *inode,
|
|||||||
FAR const char *relpath, FAR const char *prefix);
|
FAR const char *relpath, FAR const char *prefix);
|
||||||
static int unionfs_trystatfile(FAR struct inode *inode,
|
static int unionfs_trystatfile(FAR struct inode *inode,
|
||||||
FAR const char *relpath, FAR const char *prefix);
|
FAR const char *relpath, FAR const char *prefix);
|
||||||
|
static FAR char *unionfs_relpath(FAR const char *path,
|
||||||
|
FAR const char *name);
|
||||||
|
|
||||||
static void unionfs_unhooked(FAR struct unionfs_inode_s *ui);
|
static void unionfs_unhooked(FAR struct unionfs_inode_s *ui);
|
||||||
static void unionfs_destroy(FAR struct unionfs_inode_s *ui);
|
static void unionfs_destroy(FAR struct unionfs_inode_s *ui);
|
||||||
@ -560,6 +563,52 @@ static int unionfs_tryrmdir(FAR struct inode *inode, FAR const char *relpath,
|
|||||||
return ops->rmdir(inode, trypath);
|
return ops->rmdir(inode, trypath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: unionfs_relpath
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static FAR char *unionfs_relpath(FAR const char *path, FAR const char *name)
|
||||||
|
{
|
||||||
|
FAR char *relpath;
|
||||||
|
int pathlen;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Check if there is a valid, non-zero-legnth path */
|
||||||
|
|
||||||
|
if (path && (pathlen = strlen(path)) > 0)
|
||||||
|
{
|
||||||
|
/* Yes.. extend the file name by prepending the path */
|
||||||
|
|
||||||
|
if (path[pathlen-1] == '/')
|
||||||
|
{
|
||||||
|
ret = asprintf(&relpath, "%s%s", path, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = asprintf(&relpath, "%s/%s", path, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle errors */
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return relpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* There is no path... just duplicate the name (so that kmm_free()
|
||||||
|
* will work later).
|
||||||
|
*/
|
||||||
|
|
||||||
|
return strdup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: unionfs_unhooked
|
* Name: unionfs_unhooked
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -1123,13 +1172,26 @@ static int unionfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
|
|||||||
DEBUGASSERT(dir);
|
DEBUGASSERT(dir);
|
||||||
fu = &dir->u.unionfs;
|
fu = &dir->u.unionfs;
|
||||||
|
|
||||||
|
/* Clone the path. We will need this when we traverse file system 2 to
|
||||||
|
* omit duplicates on file system 1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (relpath && strlen(relpath) > 0)
|
||||||
|
{
|
||||||
|
fu->fu_relpath = strdup(relpath);
|
||||||
|
if (!fu->fu_relpath)
|
||||||
|
{
|
||||||
|
goto errout_with_semaphore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate another dirent structure for the lower file system */
|
/* Allocate another dirent structure for the lower file system */
|
||||||
|
|
||||||
lowerdir = (FAR struct fs_dirent_s *)kmm_zalloc(sizeof(struct fs_dirent_s));
|
lowerdir = (FAR struct fs_dirent_s *)kmm_zalloc(sizeof(struct fs_dirent_s));
|
||||||
if (lowerdir == NULL)
|
if (lowerdir == NULL)
|
||||||
{
|
{
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto errout_with_semaphore;
|
goto errout_with_relpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check file system 2 first. */
|
/* Check file system 2 first. */
|
||||||
@ -1186,7 +1248,7 @@ static int unionfs_opendir(FAR struct inode *mountpt, FAR const char *relpath,
|
|||||||
{
|
{
|
||||||
/* Neither file system was opened! */
|
/* Neither file system was opened! */
|
||||||
|
|
||||||
goto errout_with_semaphore;
|
goto errout_with_relpath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1208,6 +1270,12 @@ errout_with_fs2open:
|
|||||||
|
|
||||||
kmm_free(fu->fu_lower[1]);
|
kmm_free(fu->fu_lower[1]);
|
||||||
|
|
||||||
|
errout_with_relpath:
|
||||||
|
if (fu->fu_relpath != NULL)
|
||||||
|
{
|
||||||
|
kmm_free(fu->fu_relpath);
|
||||||
|
}
|
||||||
|
|
||||||
errout_with_semaphore:
|
errout_with_semaphore:
|
||||||
unionfs_semgive(ui);
|
unionfs_semgive(ui);
|
||||||
return ret;
|
return ret;
|
||||||
@ -1267,7 +1335,15 @@ static int unionfs_closedir(FAR struct inode *mountpt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Free any allocated path */
|
||||||
|
|
||||||
|
if (fu->fu_relpath != NULL)
|
||||||
|
{
|
||||||
|
kmm_free(fu->fu_relpath);
|
||||||
|
}
|
||||||
|
|
||||||
fu->fu_ndx = 0;
|
fu->fu_ndx = 0;
|
||||||
|
fu->fu_relpath = NULL;
|
||||||
fu->fu_lower[0] = NULL;
|
fu->fu_lower[0] = NULL;
|
||||||
fu->fu_lower[1] = NULL;
|
fu->fu_lower[1] = NULL;
|
||||||
|
|
||||||
@ -1296,8 +1372,12 @@ static int unionfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
|
|||||||
{
|
{
|
||||||
FAR struct unionfs_inode_s *ui;
|
FAR struct unionfs_inode_s *ui;
|
||||||
FAR struct unionfs_mountpt_s *um;
|
FAR struct unionfs_mountpt_s *um;
|
||||||
|
FAR struct unionfs_mountpt_s *um0;
|
||||||
FAR const struct mountpt_operations *ops;
|
FAR const struct mountpt_operations *ops;
|
||||||
FAR struct fs_unionfsdir_s *fu;
|
FAR struct fs_unionfsdir_s *fu;
|
||||||
|
FAR char *relpath;
|
||||||
|
struct stat buf;
|
||||||
|
bool duplicate;
|
||||||
int ret = -ENOSYS;
|
int ret = -ENOSYS;
|
||||||
|
|
||||||
/* Recover the union file system data from the struct inode instance */
|
/* Recover the union file system data from the struct inode instance */
|
||||||
@ -1320,12 +1400,18 @@ static int unionfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
|
|||||||
|
|
||||||
if (ops->readdir)
|
if (ops->readdir)
|
||||||
{
|
{
|
||||||
|
/* Loop if we discard duplicate directory entries in filey system 2 */
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* Read the directory entry */
|
||||||
|
|
||||||
ret = ops->readdir(um->um_node, fu->fu_lower[fu->fu_ndx]);
|
ret = ops->readdir(um->um_node, fu->fu_lower[fu->fu_ndx]);
|
||||||
|
|
||||||
/* Did the read operation fail because we reached the end of the
|
/* Did the read operation fail because we reached the end of the
|
||||||
* directory? In that case, the error would be -ENOENT. If we hit
|
* directory? In that case, the error would be -ENOENT. If we
|
||||||
* the end-of-directory on file system, we need to seamlessly move
|
* hit the end-of-directory on file system, we need to seamlessly
|
||||||
* to the second file system (if there is one).
|
* move to the second file system (if there is one).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (ret == -ENOENT && fu->fu_ndx == 0 && fu->fu_lower[1] != NULL)
|
if (ret == -ENOENT && fu->fu_ndx == 0 && fu->fu_lower[1] != NULL)
|
||||||
@ -1335,7 +1421,8 @@ static int unionfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
|
|||||||
fu->fu_ndx = 1;
|
fu->fu_ndx = 1;
|
||||||
um = &ui->ui_fs[1];
|
um = &ui->ui_fs[1];
|
||||||
|
|
||||||
DEBUGASSERT(um != NULL && um->um_node != NULL && um->um_node->u.i_mops != NULL);
|
DEBUGASSERT(um != NULL && um->um_node != NULL &&
|
||||||
|
um->um_node->u.i_mops != NULL);
|
||||||
ops = um->um_node->u.i_mops;
|
ops = um->um_node->u.i_mops;
|
||||||
|
|
||||||
/* Make sure that the second file system directory enumeration
|
/* Make sure that the second file system directory enumeration
|
||||||
@ -1352,7 +1439,50 @@ static int unionfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir)
|
|||||||
ret = ops->readdir(um->um_node, fu->fu_lower[1]);
|
ret = ops->readdir(um->um_node, fu->fu_lower[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the return information into the diret structure that the
|
/* Did we successfully read a directory from file system 2? If
|
||||||
|
* so, we need to omit an duplicates that should be occluded by
|
||||||
|
* the matching file on file system 1 (if we are enumerating
|
||||||
|
* file system 1).
|
||||||
|
*/
|
||||||
|
|
||||||
|
duplicate = false;
|
||||||
|
if (ret >= 0 && fu->fu_ndx == 1 && fu->fu_lower[0] != NULL)
|
||||||
|
{
|
||||||
|
/* Get the relative path to the same file on file system 1.
|
||||||
|
* NOTE: the on any failures we just assume that the filep
|
||||||
|
* is not a duplicate.
|
||||||
|
*/
|
||||||
|
|
||||||
|
relpath = unionfs_relpath(fu->fu_relpath,
|
||||||
|
fu->fu_lower[1]->fd_dir.d_name);
|
||||||
|
if (relpath)
|
||||||
|
{
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
/* Check if anything exists at this path on file system 1 */
|
||||||
|
|
||||||
|
um0 = &ui->ui_fs[0];
|
||||||
|
tmp = unionfs_trystat(um0->um_node, relpath,
|
||||||
|
um0->um_prefix, &buf);
|
||||||
|
if (tmp >= 0)
|
||||||
|
{
|
||||||
|
/* There is something there!
|
||||||
|
* REVISIT: We could allow files and directories to
|
||||||
|
* have duplicat names.
|
||||||
|
*/
|
||||||
|
|
||||||
|
duplicate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the allocated relpath */
|
||||||
|
|
||||||
|
kmm_free(relpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (duplicate);
|
||||||
|
|
||||||
|
/* Copy the return information into the dirent structure that the
|
||||||
* application will see.
|
* application will see.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -157,6 +157,7 @@ struct fs_dirent_s; /* Forward reference */
|
|||||||
struct fs_unionfsdir_s
|
struct fs_unionfsdir_s
|
||||||
{
|
{
|
||||||
uint8_t fu_ndx; /* Index of file system being enumerated */
|
uint8_t fu_ndx; /* Index of file system being enumerated */
|
||||||
|
FAR char *fu_relpath; /* Path being enumerated */
|
||||||
FAR struct fs_dirent_s *fu_lower[2]; /* dirent struct used by contained file system */
|
FAR struct fs_dirent_s *fu_lower[2]; /* dirent struct used by contained file system */
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user