Unionfs: release lower resources on successful rmdir
authorErez Zadok <ezk@cs.sunysb.edu>
Sun, 25 Nov 2007 23:27:27 +0000 (18:27 -0500)
committerErez Zadok <ezk@cs.sunysb.edu>
Sun, 23 Mar 2008 03:49:03 +0000 (23:49 -0400)
This patch prevents those resources from lingering around until memory
pressure would have forced them out.  The patch also properly handles
directories that have been rmdir'ed which are still some process's cwd.

CC: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
fs/unionfs/inode.c
fs/unionfs/mmap.c
fs/unionfs/unlink.c

index a8f3590b60324c02fa329e527b90e6cca22a710b..77c7f3308a4734e6dc5434bdaaa483996d52e02c 100644 (file)
@@ -933,7 +933,8 @@ static int unionfs_permission(struct inode *inode, int mask,
                 * dentry+inode.  This should be equivalent to issuing
                 * __unionfs_d_revalidate_chain on nd.dentry here.
                 */
-               err = -ESTALE;  /* force revalidate */
+               if (is_file)    /* dirs can be unlinked but chdir'ed to */
+                       err = -ESTALE;  /* force revalidate */
                goto out;
        }
 
index 9b95c3948e24ef81966c4a06b78901bf02bd29e2..df0f45b55898426df73943b8a036f665c7e3e64b 100644 (file)
@@ -155,8 +155,11 @@ static int unionfs_writepages(struct address_space *mapping,
        struct inode *inode;
 
        inode = mapping->host;
+       if (ibstart(inode) < 0 && ibend(inode) < 0)
+               goto out;
        lower_inode = unionfs_lower_inode(inode);
-       BUG_ON(!lower_inode);
+       if (!lower_inode)
+               goto out;
 
        if (!mapping_cap_writeback_dirty(lower_inode->i_mapping))
                goto out;
index a8de6725965162c273f9461826020fe4c3992172..f65245dcb4f94264217d4b5991a5ec3e7a506558 100644 (file)
@@ -148,6 +148,7 @@ int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
        int err = 0;
        struct unionfs_dir_state *namelist = NULL;
+       int dstart, dend;
 
        unionfs_read_lock(dentry->d_sb);
        unionfs_lock_dentry(dentry);
@@ -164,28 +165,50 @@ int unionfs_rmdir(struct inode *dir, struct dentry *dentry)
                goto out;
 
        err = unionfs_rmdir_first(dir, dentry, namelist);
+       dstart = dbstart(dentry);
+       dend = dbend(dentry);
        /* create whiteout */
        if (!err) {
-               err = create_whiteout(dentry, dbstart(dentry));
+               err = create_whiteout(dentry, dstart);
        } else {
                int new_err;
 
-               if (dbstart(dentry) == 0)
+               if (dstart == 0)
                        goto out;
 
                /* exit if the error returned was NOT -EROFS */
                if (!IS_COPYUP_ERR(err))
                        goto out;
 
-               new_err = create_whiteout(dentry, dbstart(dentry) - 1);
+               new_err = create_whiteout(dentry, dstart - 1);
                if (new_err != -EEXIST)
                        err = new_err;
        }
 
 out:
-       /* call d_drop so the system "forgets" about us */
-       if (!err)
+       /*
+        * Drop references to lower dentry/inode so storage space for them
+        * can be reclaimed.  Then, call d_drop so the system "forgets"
+        * about us.
+        */
+       if (!err) {
+               struct inode *inode = dentry->d_inode;
+               BUG_ON(!inode);
+               iput(unionfs_lower_inode_idx(inode, dstart));
+               unionfs_set_lower_inode_idx(inode, dstart, NULL);
+               dput(unionfs_lower_dentry_idx(dentry, dstart));
+               unionfs_set_lower_dentry_idx(dentry, dstart, NULL);
+               /*
+                * If the last directory is unlinked, then mark istart/end
+                * as -1, (to maintain the invariant that if there are no
+                * lower objects, then branch index start and end are set to
+                * -1).
+                */
+               if (!unionfs_lower_inode_idx(inode, dstart) &&
+                   !unionfs_lower_inode_idx(inode, dend))
+                       ibstart(inode) = ibend(inode) = -1;
                d_drop(dentry);
+       }
 
        if (namelist)
                free_rdstate(namelist);