From c55ac716034795c8aa93e408e1efdad53075a73c Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 19 Feb 2014 13:14:39 -0600 Subject: [PATCH] Nodes in the pseudo-filesystem can now be renamed or moved within the pseduo-filesystem --- ChangeLog | 7 +- fs/Makefile | 4 +- fs/fs_inode.c | 7 +- fs/fs_inodeaddref.c | 2 +- fs/fs_inodebasename.c | 96 ++++++++++++++++++++++++++ fs/fs_inoderemove.c | 134 ++++++++++++++++++++--------------- fs/fs_inodereserve.c | 2 +- fs/fs_internal.h | 25 ++++++- fs/fs_rename.c | 157 +++++++++++++++++++++++++++++++----------- include/nuttx/fs/fs.h | 4 +- 10 files changed, 323 insertions(+), 115 deletions(-) create mode 100644 fs/fs_inodebasename.c diff --git a/ChangeLog b/ChangeLog index 892aae4584..de41ca5b15 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6610,10 +6610,11 @@ * fs/fs_opendir.c, fs_readdir.c, et al: Modified so that errors will not be reported if you attempt to list a empty pseudo-directory (2014-2-19). - * fs/fs_rmdir.c: rmdir can now be used to remove empty directories in + * fs/fs_rmdir.c: 'rmdir' can now be used to remove empty directories in the pseudo-filesystem such as might be left after umounting a file system (2014-2-19). - * fs/fs_mkdir.c: mkdir can now be used to create empty directories in + * fs/fs_mkdir.c: 'mkdir' can now be used to create empty directories in the pseudo-filesystem (2014-2-19). * drivers/lcd/mio283qt9a.c: Bug fix from Toby Duckwork (2014-2-19). - + * fs/fs_rename.c: 'rename' can now be used to rename nodes in the + pseudo-filesystem (2014-2-19). diff --git a/fs/Makefile b/fs/Makefile index 828535cfe3..615d655fb5 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -68,8 +68,8 @@ CSRCS += fs_rewinddir.c fs_rmdir.c fs_seekdir.c fs_stat.c fs_statfs.c CSRCS += fs_select.c fs_write.c CSRCS += fs_files.c fs_foreachinode.c fs_inode.c fs_inodeaddref.c -CSRCS += fs_inodefind.c fs_inoderelease.c fs_inoderemove.c -CSRCS += fs_inodereserve.c +CSRCS += fs_inodebasename.c fs_inodefind.c fs_inoderelease.c +CSRCS += fs_inoderemove.c fs_inodereserve.c CSRCS += fs_registerdriver.c fs_unregisterdriver.c CSRCS += fs_registerblockdriver.c fs_unregisterblockdriver.c diff --git a/fs/fs_inode.c b/fs/fs_inode.c index afb88532cd..913e58a5df 100644 --- a/fs/fs_inode.c +++ b/fs/fs_inode.c @@ -408,15 +408,15 @@ void inode_free(FAR struct inode *node) * Name: inode_nextname * * Description: - * Given a path with node names separated by '/', return the next node - * name. + * Given a path with node names separated by '/', return the next path + * segment name. * ****************************************************************************/ FAR const char *inode_nextname(FAR const char *name) { /* Search for the '/' delimiter or the NUL terminator at the end of the - * string. + * path segment. */ while (*name && *name != '/') @@ -435,4 +435,3 @@ FAR const char *inode_nextname(FAR const char *name) return name; } - diff --git a/fs/fs_inodeaddref.c b/fs/fs_inodeaddref.c index 32d4426d26..ebe6b48881 100644 --- a/fs/fs_inodeaddref.c +++ b/fs/fs_inodeaddref.c @@ -1,5 +1,5 @@ /**************************************************************************** - * fs_inodeaddref.c + * fs/fs_inodeaddref.c * * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. * Author: Gregory Nutt diff --git a/fs/fs_inodebasename.c b/fs/fs_inodebasename.c new file mode 100644 index 0000000000..76d8a8e201 --- /dev/null +++ b/fs/fs_inodebasename.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * fs/fs_basename.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "fs_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Public Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: inode_nextname + * + * Description: + * Given a path with node names separated by '/', return the next node + * name. + * + ****************************************************************************/ + +FAR const char *inode_basename(FAR const char *name) +{ + FAR const char *basename = NULL; + + for (;;) + { + /* Get the name for the next path segment */ + + name = inode_nextname(name); + + /* When the final segment is terminated by the NUL character, then + * previous name that we saved is the basename. + */ + + if (*name == '\0') + { + return basename; + } + } + + /* We won't get here */ + + return NULL; +} diff --git a/fs/fs_inoderemove.c b/fs/fs_inoderemove.c index c349b17595..8258b11510 100644 --- a/fs/fs_inoderemove.c +++ b/fs/fs_inoderemove.c @@ -62,84 +62,104 @@ * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: inode_unlink - ****************************************************************************/ - -static void inode_unlink(struct inode *node, - struct inode *peer, - struct inode *parent) -{ - /* If peer is non-null, then remove the node from the right of - * of that peer node. - */ - - if (peer) - { - peer->i_peer = node->i_peer; - } - - /* If parent is non-null, then remove the node from head of - * of the list of children. - */ - - else if (parent) - { - parent->i_child = node->i_peer; - } - - /* Otherwise, we must be removing the root inode. */ - - else - { - root_inode = node->i_peer; - } - - node->i_peer = NULL; -} - /**************************************************************************** * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: inode_unlink + * + * Description: + * Given a path, remove a the node from the in-memory, inode tree that the + * path refers to. This is normally done in preparation to removing or + * moving an inode. + * + * Assumptions/Limitations: + * The caller must hold the inode semaphore + * + ****************************************************************************/ + +FAR struct inode *inode_unlink(FAR const char *path) +{ + const char *name = path; + FAR struct inode *node; + FAR struct inode *peer; + FAR struct inode *parent; + + /* Verify parameters. Ignore null paths and relative paths */ + + if (!path || *path == '\0' || path[0] != '/') + { + return NULL; + } + + /* Find the node to unlink */ + + node = inode_search(&name, &peer, &parent, (const char **)NULL); + if (node) + { + /* If peer is non-null, then remove the node from the right of + * of that peer node. + */ + + if (peer) + { + peer->i_peer = node->i_peer; + } + + /* If parent is non-null, then remove the node from head of + * of the list of children. + */ + + else if (parent) + { + parent->i_child = node->i_peer; + } + + /* Otherwise, we must be removing the root inode. */ + + else + { + root_inode = node->i_peer; + } + + node->i_peer = NULL; + } + + return node; +} + /**************************************************************************** * Name: inode_remove * * Description: - * Remove a node from the in-memory, inode tree + * Given a path, remove a the node from the in-memory, inode tree that the + * path refers to and free all resources related to the inode. If the + * inode is in-use, then it will be unlinked, but will not be freed until + * the last reference to the inode is released. * - * NOTE: Caller must hold the inode semaphore + * Assumptions/Limitations: + * The caller must hold the inode semaphore * ****************************************************************************/ int inode_remove(FAR const char *path) { - const char *name = path; FAR struct inode *node; - FAR struct inode *left; - FAR struct inode *parent; - if (!*path || path[0] != '/') - { - return -EINVAL; - } + /* Find the inode and unlink it from the in-memory inode tree */ - /* Find the node to delete */ - - node = inode_search(&name, &left, &parent, (const char **)NULL); + node = inode_unlink(path); if (node) { - /* Found it, now remove it from the tree */ - - inode_unlink(node, left, parent); - - /* We cannot delete it if there reference to the inode */ + /* Found it! But we cannot delete the inode if there are references + * to it + */ if (node->i_crefs) { - /* In that case, we will mark it deleted, when the FS - * releases the inode, we will then, finally delete - * the subtree. + /* In that case, we will mark it deleted, when the filesystem + * releases the inode, we will then, finally delete the subtree */ node->i_flags |= FSNODEFLAG_DELETED; @@ -155,7 +175,7 @@ int inode_remove(FAR const char *path) } } - /* The node does not exist or it has references */ + /* The node does not exist */ return -ENOENT; } diff --git a/fs/fs_inodereserve.c b/fs/fs_inodereserve.c index 73e0fff281..ad3a8107ef 100644 --- a/fs/fs_inodereserve.c +++ b/fs/fs_inodereserve.c @@ -165,7 +165,7 @@ static void inode_insert(FAR struct inode *node, int inode_reserve(FAR const char *path, FAR struct inode **inode) { - const char *name = path; + FAR const char *name = path; FAR struct inode *left; FAR struct inode *parent; diff --git a/fs/fs_internal.h b/fs/fs_internal.h index 648f027041..5dd45bc233 100644 --- a/fs/fs_internal.h +++ b/fs/fs_internal.h @@ -1,7 +1,7 @@ /**************************************************************************** * fs/fs_internal.h * - * Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2012, 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -195,13 +195,32 @@ const char *inode_nextname(FAR const char *name); int inode_reserve(FAR const char *path, FAR struct inode **inode); /* fs_inoderemove.c *********************************************************/ +/**************************************************************************** + * Name: inode_unlink + * + * Description: + * Given a path, remove a the node from the in-memory, inode tree that the + * path refers to. This is normally done in preparation to removing or + * moving an inode. + * + * Assumptions/Limitations: + * The caller must hold the inode semaphore + * + ****************************************************************************/ + +FAR struct inode *inode_unlink(FAR const char *path); + /**************************************************************************** * Name: inode_remove * * Description: - * Remove a node from the in-memory, inode tree + * Given a path, remove a the node from the in-memory, inode tree that the + * path refers to and free all resources related to the inode. If the + * inode is in-use, then it will be unlinked, but will not be freed until + * the last reference to the inode is released. * - * NOTE: Caller must hold the inode semaphore + * Assumptions/Limitations: + * The caller must hold the inode semaphore * ****************************************************************************/ diff --git a/fs/fs_rename.c b/fs/fs_rename.c index 6f2d1e4328..474a7492ee 100644 --- a/fs/fs_rename.c +++ b/fs/fs_rename.c @@ -1,7 +1,7 @@ /**************************************************************************** * fs/fs_rename.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -75,78 +75,151 @@ int rename(FAR const char *oldpath, FAR const char *newpath) { FAR struct inode *oldinode; - FAR struct inode *newinode; const char *oldrelpath = NULL; +#ifndef CONFIG_DISABLE_MOUNTPOINT + FAR struct inode *newinode; const char *newrelpath = NULL; +#endif + int errcode; int ret; + /* Ignore paths that are interpreted as the root directory which has no name + * and cannot be moved + */ + + if (!oldpath || *oldpath == '\0' || oldpath[0] != '/' || + !newpath || *newpath == '\0' || newpath[0] != '/') + { + return -EINVAL; + } + /* Get an inode that includes the oldpath */ oldinode = inode_find(oldpath, &oldrelpath); if (!oldinode) { - /* There is no mountpoint that includes in this path */ + /* There is no inode that includes in this path */ - ret = ENOENT; + errcode = ENOENT; goto errout; } +#ifndef CONFIG_DISABLE_MOUNTPOINT /* Verify that the old inode is a valid mountpoint. */ - if (!INODE_IS_MOUNTPT(oldinode) || !oldinode->u.i_mops) + if (INODE_IS_MOUNTPT(oldinode) && oldinode->u.i_mops) { - ret = ENXIO; - goto errout_with_oldinode; - } + /* Get an inode for the new relpath -- it should like on the same + * mountpoint + */ - /* Get an inode for the new relpath -- it should like on the same - * mountpoint - */ - - newinode = inode_find(newpath, &newrelpath); - if (!newinode) - { - /* There is no mountpoint that includes in this path */ - - ret = ENOENT; - goto errout_with_oldinode; - } - - /* Verify that the two paths lie on the same mountpoint inode */ - - if (oldinode != newinode) - { - ret = EXDEV; - goto errout_with_newinode; - } - - /* 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, newrelpath); - if (ret < 0) + newinode = inode_find(newpath, &newrelpath); + if (!newinode) { - ret = -ret; + /* There is no mountpoint that includes in this path */ + + errcode = ENOENT; + goto errout_with_oldinode; + } + + /* Verify that the two paths lie on the same mountpoint inode */ + + if (oldinode != newinode) + { + errcode = EXDEV; goto errout_with_newinode; } + + /* 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, newrelpath); + if (ret < 0) + { + errcode = -ret; + goto errout_with_newinode; + } + } + else + { + errcode = ENOSYS; + goto errout_with_newinode; + } + + /* Successfully renamed */ + + inode_release(newinode); } else - { - ret = ENOSYS; - goto errout_with_newinode; +#endif + { + /* Create a new, empty inode at the destination location */ + + inode_semtake(); + ret = inode_reserve(newpath, &newinode); + if (ret < 0) + { + /* It is an error if a node at newpath already exists in the tree + * OR if we fail to allocate memory for the new inode (and possibly + * any new intermediate path segments). + */ + + inode_semgive(); + errcode = EEXIST; + goto errout_with_oldinode; + } + + /* Copy the inode state from the old inode to the newly allocated inode */ + + newinode->i_child = oldinode->i_child; /* Link to lower level inode */ + newinode->i_flags = oldinode->i_flags; /* Flags for inode */ + newinode->u.i_ops = oldinode->u.i_ops; /* Inode operations */ +#ifdef CONFIG_FILE_MODE + newinode->i_mode = oldinode->i_mode; /* Access mode flags */ +#endif + newinode->i_private = oldinode->i_private; /* Per inode driver private data */ + + /* We now have two copies of the inode. One with a reference count of + * zero (the new one), and one that may have multiple references + * including one by this logic (the old one) + * + * Remove the old inode. Because we hold a reference count on the + * inode, it will not be deleted now. It will be deleted when all of + * the references to to the inode have been released (perhaps when + * inode_release() is called below). inode_remove() should return + * -EBUSY to indicate that the inode was not deleted now. + */ + + ret = inode_remove(oldpath); + if (ret < 0 && ret != -EBUSY) + { + /* Remove the new node we just recreated */ + + (void)inode_remove(newpath); + inode_semgive(); + + errcode = -ret; + goto errout_with_oldinode; + } + + /* Remove all of the children from the unlinked inode */ + + oldinode->i_child = NULL; + inode_semgive(); } /* Successfully renamed */ inode_release(oldinode); - inode_release(newinode); return OK; +#ifndef CONFIG_DISABLE_MOUNTPOINT errout_with_newinode: inode_release(newinode); +#endif errout_with_oldinode: inode_release(oldinode); errout: diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index f73b0b0cfc..9b6502634b 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -208,8 +208,8 @@ union inode_ops_u struct inode { - FAR struct inode *i_peer; /* Pointer to same level inode */ - FAR struct inode *i_child; /* Pointer to lower level inode */ + FAR struct inode *i_peer; /* Link to same level inode */ + FAR struct inode *i_child; /* Link to lower level inode */ int16_t i_crefs; /* References to inode */ uint16_t i_flags; /* Flags for inode */ union inode_ops_u u; /* Inode operations */