diff --git a/fs/mount/fs_mount.c b/fs/mount/fs_mount.c index 88641aa4858..0fec2660483 100644 --- a/fs/mount/fs_mount.c +++ b/fs/mount/fs_mount.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/mount/fs_mount.c * - * Copyright (C) 2007-2009, 2011-2013, 2015, 2017, 2018 Gregory Nutt. All + * Copyright (C) 2007-2009, 2011-2013, 2015, 2017-2019 Gregory Nutt. All * rights reserved. * Author: Gregory Nutt * @@ -68,7 +68,8 @@ /* In the canonical case, a file system is bound to a block driver. However, * some less typical cases a block driver is not required. Examples are - * pseudo file systems (like BINFS or PROCFS) and MTD file systems (like NXFFS). + * pseudo file systems (like BINFS or PROCFS) and MTD file systems (like + * NXFFS). * * These file systems all require block drivers: */ @@ -89,7 +90,7 @@ #if defined(CONFIG_FS_NXFFS) || defined(CONFIG_FS_BINFS) || \ defined(CONFIG_FS_PROCFS) || defined(CONFIG_NFS) || \ defined(CONFIG_FS_TMPFS) || defined(CONFIG_FS_USERFS) || \ - defined(CONFIG_FS_CROMFS) + defined(CONFIG_FS_CROMFS) || defined(CONFIG_FS_UNIONFS) # define NODFS_SUPPORT #endif @@ -196,6 +197,9 @@ extern const struct mountpt_operations hostfs_operations; #ifdef CONFIG_FS_CROMFS extern const struct mountpt_operations cromfs_operations; #endif +#ifdef CONFIG_FS_UNIONFS +extern const struct mountpt_operations unionfs_operations; +#endif static const struct fsmap_t g_nonbdfsmap[] = { @@ -222,6 +226,9 @@ static const struct fsmap_t g_nonbdfsmap[] = #endif #ifdef CONFIG_FS_CROMFS { "cromfs", &cromfs_operations }, +#endif +#ifdef CONFIG_FS_UNIONFS + { "unionfs", &unionfs_operations }, #endif { NULL, NULL }, }; @@ -398,12 +405,14 @@ int mount(FAR const char *source, FAR const char *target, ret = inode_reserve(target, &mountpt_inode); if (ret < 0) { - /* inode_reserve can fail for a couple of reasons, but the most likely - * one is that the inode already exists. inode_reserve may return: + /* inode_reserve can fail for a couple of reasons, but the most + * likely one is that the inode already exists. inode_reserve may + * return: * * -EINVAL - 'path' is invalid for this operation * -EEXIST - An inode already exists at 'path' - * -ENOMEM - Failed to allocate in-memory resources for the operation + * -ENOMEM - Failed to allocate in-memory resources for the + * operation */ ferr("ERROR: Failed to reserve inode for target %s\n", target); diff --git a/fs/unionfs/fs_unionfs.c b/fs/unionfs/fs_unionfs.c index 7367d696f6e..d8e5d97092d 100644 --- a/fs/unionfs/fs_unionfs.c +++ b/fs/unionfs/fs_unionfs.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/unionfs/fs_unionfs.c * - * Copyright (C) 2015, 2017-2018 Gregory Nutt. All rights reserved. + * Copyright (C) 2015, 2017-2019 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -177,6 +177,8 @@ static int unionfs_readdir(FAR struct inode *mountpt, static int unionfs_rewinddir(FAR struct inode *mountpt, FAR struct fs_dirent_s *dir); +static int unionfs_bind(FAR struct inode *blkdriver, FAR const void *data, + FAR void **handle); static int unionfs_unbind(FAR void *handle, FAR struct inode **blkdriver, unsigned int flags); static int unionfs_statfs(FAR struct inode *mountpt, @@ -199,6 +201,9 @@ static int unionfs_stat(FAR struct inode *mountpt, static int unionfs_getmount(FAR const char *path, FAR struct inode **inode); +static int unionfs_dobind(FAR const char *fspath1, + FAR const char *prefix1, FAR const char *fspath2, + FAR const char *prefix2, FAR void **handle); /**************************************************************************** * Public Data @@ -209,7 +214,7 @@ static int unionfs_getmount(FAR const char *path, * with any compiler. */ -static const struct mountpt_operations g_unionfs_mops = +const struct mountpt_operations unionfs_operations = { unionfs_open, /* open */ unionfs_close, /* close */ @@ -228,7 +233,7 @@ static const struct mountpt_operations g_unionfs_mops = unionfs_readdir, /* readdir */ unionfs_rewinddir, /* rewinddir */ - NULL, /* bind */ + unionfs_bind, /* bind */ unionfs_unbind, /* unbind */ unionfs_statfs, /* statfs */ @@ -870,7 +875,8 @@ static int unionfs_open(FAR struct file *filep, FAR const char *relpath, /* Try to open the file on file system 1 */ um = &ui->ui_fs[0]; - 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); uf->uf_file.f_oflags = filep->f_oflags; uf->uf_file.f_pos = 0; @@ -895,7 +901,8 @@ static int unionfs_open(FAR struct file *filep, FAR const char *relpath, uf->uf_file.f_inode = um->um_node; uf->uf_file.f_priv = NULL; - ret = unionfs_tryopen(&uf->uf_file, relpath, um->um_prefix, oflags, mode); + ret = unionfs_tryopen(&uf->uf_file, relpath, um->um_prefix, oflags, + mode); if (ret < 0) { goto errout_with_semaphore; @@ -948,7 +955,8 @@ static int unionfs_close(FAR struct file *filep) DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1); um = &ui->ui_fs[uf->uf_ndx]; - 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; finfo("Closing: ui_nopen=%d\n", ui->ui_nopen); @@ -1012,7 +1020,8 @@ static ssize_t unionfs_read(FAR struct file *filep, FAR char *buffer, DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1); um = &ui->ui_fs[uf->uf_ndx]; - 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; /* Perform the lower level read operation */ @@ -1060,7 +1069,8 @@ static ssize_t unionfs_write(FAR struct file *filep, FAR const char *buffer, DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1); um = &ui->ui_fs[uf->uf_ndx]; - 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; /* Perform the lower level write operation */ @@ -1107,7 +1117,8 @@ static off_t unionfs_seek(FAR struct file *filep, off_t offset, int whence) DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1); um = &ui->ui_fs[uf->uf_ndx]; - 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; /* Invoke the file seek method if available */ @@ -1183,7 +1194,8 @@ static int unionfs_ioctl(FAR struct file *filep, int cmd, unsigned long arg) DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1); um = &ui->ui_fs[uf->uf_ndx]; - 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; /* Perform the lower level ioctl operation */ @@ -1230,7 +1242,8 @@ static int unionfs_sync(FAR struct file *filep) DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1); um = &ui->ui_fs[uf->uf_ndx]; - 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; /* Perform the lower level sync operation */ @@ -1278,14 +1291,16 @@ static int unionfs_dup(FAR const struct file *oldp, FAR struct file *newp) DEBUGASSERT(oldpriv->uf_ndx == 0 || oldpriv->uf_ndx == 1); um = &ui->ui_fs[oldpriv->uf_ndx]; - 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; DEBUGASSERT(newp != NULL && newp->f_priv == NULL); /* Allocate a new container for the union FS open file */ - newpriv = (FAR struct unionfs_file_s *)kmm_malloc(sizeof(struct unionfs_file_s)); + newpriv = (FAR struct unionfs_file_s *) + kmm_malloc(sizeof(struct unionfs_file_s)); if (newpriv != NULL) { /* Clone the old file structure into the newly allocated one */ @@ -1352,7 +1367,8 @@ static int unionfs_fstat(FAR const struct file *filep, FAR struct stat *buf) DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1); um = &ui->ui_fs[uf->uf_ndx]; - 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; /* Perform the lower level write operation */ @@ -1403,7 +1419,8 @@ static int unionfs_truncate(FAR struct file *filep, off_t length) DEBUGASSERT(uf->uf_ndx == 0 || uf->uf_ndx == 1); um = &ui->ui_fs[uf->uf_ndx]; - 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; /* Perform the lower level write operation */ @@ -1485,7 +1502,8 @@ static int unionfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, /* Allocate yet another dirent structure for the lower file system 1 */ - 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) { ret = -ENOMEM; @@ -1493,8 +1511,8 @@ static int unionfs_opendir(FAR struct inode *mountpt, FAR const char *relpath, } } - /* Check if the user is stat'ing some "fake" node between the unionfs root and - * the file system 1/2 root directory. + /* Check if the user is stat'ing some "fake" node between the unionfs root + * and the file system 1/2 root directory. */ else if (unionfs_ispartprefix(relpath, ui->ui_fs[1].um_prefix)) @@ -1618,7 +1636,8 @@ static int unionfs_closedir(FAR struct inode *mountpt, { um = &ui->ui_fs[i]; - 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; /* Perform the lower level closedir operation */ @@ -1846,8 +1865,9 @@ static int unionfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) um->um_node->u.i_mops != NULL); ops = um->um_node->u.i_mops; - /* Make sure that the second file system directory enumeration - * is rewound to the beginning of the directory. + /* Make sure that the second file system directory + * enumeration is rewound to the beginning of the + * directory. */ if (ops->rewinddir != NULL) @@ -1909,7 +1929,8 @@ static int unionfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) */ dir->fd_position = fu->fu_lower[fu->fu_ndx]->fd_position; - memcpy(&dir->fd_dir, &fu->fu_lower[fu->fu_ndx]->fd_dir, sizeof(struct dirent)); + memcpy(&dir->fd_dir, &fu->fu_lower[fu->fu_ndx]->fd_dir, + sizeof(struct dirent)); } return ret; @@ -1962,7 +1983,8 @@ static int unionfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) DEBUGASSERT(fu->fu_lower[fu->fu_ndx] != NULL); um = &ui->ui_fs[fu->fu_ndx]; - 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; /* Perform the file system rewind operation */ @@ -1982,6 +2004,58 @@ static int unionfs_rewinddir(struct inode *mountpt, struct fs_dirent_s *dir) return ret; } +/**************************************************************************** + * Name: unionfs_bind + ****************************************************************************/ + +static int unionfs_bind(FAR struct inode *blkdriver, FAR const void *data, + FAR void **handle) +{ + FAR const char *fspath1 = ""; + FAR const char *prefix1 = ""; + FAR const char *fspath2 = ""; + FAR const char *prefix2 = ""; + FAR char *dup; + FAR char *tmp; + FAR char *tok; + int ret; + + /* Parse options from mount syscall */ + + dup = tmp = strdup(data); + if (!dup) + { + return -ENOMEM; + } + + while ((tok = strsep(&tmp, ","))) + { + if (tok == strstr(tok, "fspath1=")) + { + fspath1 = tok + 8; + } + else if (tok == strstr(tok, "prefix1=")) + { + prefix1 = tok + 8; + } + else if (tok == strstr(tok, "fspath2=")) + { + fspath2 = tok + 8; + } + else if (tok == strstr(tok, "prefix2=")) + { + prefix2 = tok + 8; + } + } + + /* Call unionfs_dobind to do the real work. */ + + ret = unionfs_dobind(fspath1, prefix1, fspath2, prefix2, handle); + kmm_free(dup); + + return ret; +} + /**************************************************************************** * Name: unionfs_unbind ****************************************************************************/ @@ -2062,11 +2136,13 @@ static int unionfs_statfs(FAR struct inode *mountpt, FAR struct statfs *buf) */ um1 = &ui->ui_fs[0]; - DEBUGASSERT(um1 != NULL && um1->um_node != NULL && um1->um_node->u.i_mops != NULL); + DEBUGASSERT(um1 != NULL && um1->um_node != NULL && + um1->um_node->u.i_mops != NULL); ops1 = um1->um_node->u.i_mops; um2 = &ui->ui_fs[1]; - DEBUGASSERT(um2 != NULL && um2->um_node != NULL && um2->um_node->u.i_mops != NULL); + DEBUGASSERT(um2 != NULL && um2->um_node != NULL && + um2->um_node->u.i_mops != NULL); ops2 = um2->um_node->u.i_mops; if (ops1->statfs != NULL && ops2->statfs != NULL) @@ -2177,7 +2253,8 @@ static int unionfs_unlink(FAR struct inode *mountpt, /* Recover the union file system data from the struct inode instance */ - DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && relpath != NULL); + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && + relpath != NULL); ui = (FAR struct unionfs_inode_s *)mountpt->i_private; /* Get exclusive access to the file system data structures */ @@ -2249,7 +2326,8 @@ static int unionfs_mkdir(FAR struct inode *mountpt, FAR const char *relpath, /* Recover the union file system data from the struct inode instance */ - DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && relpath != NULL); + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && + relpath != NULL); ui = (FAR struct unionfs_inode_s *)mountpt->i_private; /* Get exclusive access to the file system data structures */ @@ -2322,7 +2400,8 @@ static int unionfs_rmdir(FAR struct inode *mountpt, FAR const char *relpath) /* Recover the union file system data from the struct inode instance */ - DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && relpath != NULL); + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && + relpath != NULL); ui = (FAR struct unionfs_inode_s *)mountpt->i_private; /* Get exclusive access to the file system data structures */ @@ -2471,7 +2550,8 @@ static int unionfs_stat(FAR struct inode *mountpt, FAR const char *relpath, /* Recover the union file system data from the struct inode instance */ - DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && relpath != NULL); + DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL && + relpath != NULL); ui = (FAR struct unionfs_inode_s *)mountpt->i_private; /* Get exclusive access to the file system data structures */ @@ -2510,8 +2590,8 @@ static int unionfs_stat(FAR struct inode *mountpt, FAR const char *relpath, return OK; } - /* Special case the unionfs root directory when both file systems are offset. - * In that case, both of the above trystat calls will fail. + /* Special case the unionfs root directory when both file systems are + * offset. In that case, both of the above trystat calls will fail. */ if (ui->ui_fs[0].um_prefix != NULL && ui->ui_fs[1].um_prefix != NULL) @@ -2600,46 +2680,24 @@ errout_with_search: } /**************************************************************************** - * Public Functions + * Name: unionfs_dobind ****************************************************************************/ -/**************************************************************************** - * Name: unionfs_mount - * - * Description: - * Create and mount a union file system - * - * Input Parameters: - * fspath1 - The full path to the first file system mountpoint - * prefix1 - An optiona prefix that may be applied to make the first - * file system appear a some path below the unionfs mountpoint, - * fspath2 - The full path to the second file system mountpoint - * prefix2 - An optiona prefix that may be applied to make the first - * file system appear a some path below the unionfs mountpoint, - * mountpt - The full path to the mountpoint for the union file system - * - * Returned Value: - * Zero (OK) is returned if the union file system was correctly created and - * mounted. On any failure, a negated error value will be returned to - * indicate the nature of the failure. - * - ****************************************************************************/ - -int unionfs_mount(FAR const char *fspath1, FAR const char *prefix1, - FAR const char *fspath2, FAR const char *prefix2, - FAR const char *mountpt) +static int unionfs_dobind(FAR const char *fspath1, FAR const char *prefix1, + FAR const char *fspath2, FAR const char *prefix2, + FAR void **handle) { FAR struct unionfs_inode_s *ui; - FAR struct inode *mpinode; int ret; - DEBUGASSERT(fspath1 != NULL && fspath2 != NULL && mountpt != NULL); + DEBUGASSERT(fspath1 != NULL && fspath2 != NULL && handle != NULL); /* Allocate a structure a structure that will describe the union file * system. */ - ui = (FAR struct unionfs_inode_s *)kmm_zalloc(sizeof(struct unionfs_inode_s)); + ui = (FAR struct unionfs_inode_s *) + kmm_zalloc(sizeof(struct unionfs_inode_s)); if (!ui) { ferr("ERROR: Failed to allocated union FS state structure\n"); @@ -2688,14 +2746,80 @@ int unionfs_mount(FAR const char *fspath1, FAR const char *prefix1, } } - /* Finally, mount the union FS. We should adapt the standard mount to do + /* Unlink the contained mountpoint inodes from the pseudo file system. + * The inodes will be marked as deleted so that they will be removed when + * the reference count decrements to zero in inode_release(). Because we + * hold a reference count on the inodes, they will not be deleted until + * this logic calls inode_release() in the unionfs_destroy() function. + */ + + (void)inode_remove(fspath1); + (void)inode_remove(fspath2); + + *handle = ui; + return OK; + +errout_with_prefix1: + if (ui->ui_fs[0].um_prefix != NULL) + { + kmm_free(ui->ui_fs[0].um_prefix); + } + +errout_with_fs2: + inode_release(ui->ui_fs[1].um_node); + +errout_with_fs1: + inode_release(ui->ui_fs[0].um_node); + +errout_with_uinode: + nxsem_destroy(&ui->ui_exclsem); + kmm_free(ui); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: unionfs_mount + * + * Description: + * Create and mount a union file system + * + * Input Parameters: + * fspath1 - The full path to the first file system mountpoint + * prefix1 - An optiona prefix that may be applied to make the first + * file system appear a some path below the unionfs mountpoint, + * fspath2 - The full path to the second file system mountpoint + * prefix2 - An optiona prefix that may be applied to make the first + * file system appear a some path below the unionfs mountpoint, + * mountpt - The full path to the mountpoint for the union file system + * + * Returned Value: + * Zero (OK) is returned if the union file system was correctly created and + * mounted. On any failure, a negated error value will be returned to + * indicate the nature of the failure. + * + ****************************************************************************/ + +int unionfs_mount(FAR const char *fspath1, FAR const char *prefix1, + FAR const char *fspath2, FAR const char *prefix2, + FAR const char *mountpt) +{ + FAR struct inode *mpinode; + int ret; + + DEBUGASSERT(mountpt != NULL); + + /* Mount the union FS. We should adapt the standard mount to do * this using optional parameters. This custom mount should do the job * for now, however. */ /* Insert a dummy node -- we need to hold the inode semaphore * to do this because we will have a momentarily bad structure. - * NOTE that the inode will be created with a refernce count of zero. + * NOTE that the inode will be created with a reference count of zero. */ inode_semtake(); @@ -2718,47 +2842,28 @@ int unionfs_mount(FAR const char *fspath1, FAR const char *prefix1, INODE_SET_MOUNTPT(mpinode); - mpinode->u.i_mops = &g_unionfs_mops; + mpinode->u.i_mops = &unionfs_operations; #ifdef CONFIG_FILE_MODE mpinode->i_mode = 0755; #endif - mpinode->i_private = ui; - /* Unlink the contained mountpoint inodes from the pseudo file system. - * The inodes will be marked as deleted so that they will be removed when - * the reference count decrements to zero in inode_release(). Because we - * hold a reference count on the inodes, they will not be deleted until - * this logic calls inode_release() in the unionfs_destroy() function. - */ + /* Call unionfs_dobind to do the real work. */ + + ret = unionfs_dobind(fspath1, prefix1, fspath2, prefix2, + &mpinode->i_private); + if (ret < 0) + { + goto errout_with_mountpt; + } - (void)inode_remove(fspath1); - (void)inode_remove(fspath2); inode_semgive(); return OK; +errout_with_mountpt: + inode_release(mpinode); + errout_with_semaphore: inode_semgive(); - - if (ui->ui_fs[1].um_prefix != NULL) - { - kmm_free(ui->ui_fs[1].um_prefix); - } - -errout_with_prefix1: - if (ui->ui_fs[0].um_prefix != NULL) - { - kmm_free(ui->ui_fs[0].um_prefix); - } - -errout_with_fs2: - inode_release(ui->ui_fs[1].um_node); - -errout_with_fs1: - inode_release(ui->ui_fs[0].um_node); - -errout_with_uinode: - nxsem_destroy(&ui->ui_exclsem); - kmm_free(ui); return ret; } #endif /* !CONFIG_DISABLE_MOUNTPOINT && CONFIG_FS_UNIONFS */