summaryrefslogtreecommitdiff
path: root/fs/aufs/i_op_del.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/aufs/i_op_del.c')
-rw-r--r--fs/aufs/i_op_del.c510
1 files changed, 0 insertions, 510 deletions
diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c
deleted file mode 100644
index 78cf67a52..000000000
--- a/fs/aufs/i_op_del.c
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * Copyright (C) 2005-2015 Junjiro R. Okajima
- *
- * This program, aufs is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * inode operations (del entry)
- */
-
-#include "aufs.h"
-
-/*
- * decide if a new whiteout for @dentry is necessary or not.
- * when it is necessary, prepare the parent dir for the upper branch whose
- * branch index is @bcpup for creation. the actual creation of the whiteout will
- * be done by caller.
- * return value:
- * 0: wh is unnecessary
- * plus: wh is necessary
- * minus: error
- */
-int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup)
-{
- int need_wh, err;
- aufs_bindex_t bstart;
- struct super_block *sb;
-
- sb = dentry->d_sb;
- bstart = au_dbstart(dentry);
- if (*bcpup < 0) {
- *bcpup = bstart;
- if (au_test_ro(sb, bstart, d_inode(dentry))) {
- err = AuWbrCopyup(au_sbi(sb), dentry);
- *bcpup = err;
- if (unlikely(err < 0))
- goto out;
- }
- } else
- AuDebugOn(bstart < *bcpup
- || au_test_ro(sb, *bcpup, d_inode(dentry)));
- AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart);
-
- if (*bcpup != bstart) {
- err = au_cpup_dirs(dentry, *bcpup);
- if (unlikely(err))
- goto out;
- need_wh = 1;
- } else {
- struct au_dinfo *dinfo, *tmp;
-
- need_wh = -ENOMEM;
- dinfo = au_di(dentry);
- tmp = au_di_alloc(sb, AuLsc_DI_TMP);
- if (tmp) {
- au_di_cp(tmp, dinfo);
- au_di_swap(tmp, dinfo);
- /* returns the number of positive dentries */
- need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0);
- au_di_swap(tmp, dinfo);
- au_rw_write_unlock(&tmp->di_rwsem);
- au_di_free(tmp);
- }
- }
- AuDbg("need_wh %d\n", need_wh);
- err = need_wh;
-
-out:
- return err;
-}
-
-/*
- * simple tests for the del-entry operations.
- * following the checks in vfs, plus the parent-child relationship.
- */
-int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
- struct dentry *h_parent, int isdir)
-{
- int err;
- umode_t h_mode;
- struct dentry *h_dentry, *h_latest;
- struct inode *h_inode;
-
- h_dentry = au_h_dptr(dentry, bindex);
- if (d_really_is_positive(dentry)) {
- err = -ENOENT;
- if (unlikely(d_is_negative(h_dentry)))
- goto out;
- h_inode = d_inode(h_dentry);
- if (unlikely(!h_inode->i_nlink))
- goto out;
-
- h_mode = h_inode->i_mode;
- if (!isdir) {
- err = -EISDIR;
- if (unlikely(S_ISDIR(h_mode)))
- goto out;
- } else if (unlikely(!S_ISDIR(h_mode))) {
- err = -ENOTDIR;
- goto out;
- }
- } else {
- /* rename(2) case */
- err = -EIO;
- if (unlikely(d_is_positive(h_dentry)))
- goto out;
- }
-
- err = -ENOENT;
- /* expected parent dir is locked */
- if (unlikely(h_parent != h_dentry->d_parent))
- goto out;
- err = 0;
-
- /*
- * rmdir a dir may break the consistency on some filesystem.
- * let's try heavy test.
- */
- err = -EACCES;
- if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)
- && au_test_h_perm(d_inode(h_parent),
- MAY_EXEC | MAY_WRITE)))
- goto out;
-
- h_latest = au_sio_lkup_one(&dentry->d_name, h_parent);
- err = -EIO;
- if (IS_ERR(h_latest))
- goto out;
- if (h_latest == h_dentry)
- err = 0;
- dput(h_latest);
-
-out:
- return err;
-}
-
-/*
- * decide the branch where we operate for @dentry. the branch index will be set
- * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent
- * dir for reverting.
- * when a new whiteout is necessary, create it.
- */
-static struct dentry*
-lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup,
- struct au_dtime *dt, struct au_pin *pin)
-{
- struct dentry *wh_dentry;
- struct super_block *sb;
- struct path h_path;
- int err, need_wh;
- unsigned int udba;
- aufs_bindex_t bcpup;
-
- need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup);
- wh_dentry = ERR_PTR(need_wh);
- if (unlikely(need_wh < 0))
- goto out;
-
- sb = dentry->d_sb;
- udba = au_opt_udba(sb);
- bcpup = *rbcpup;
- err = au_pin(pin, dentry, bcpup, udba,
- AuPin_DI_LOCKED | AuPin_MNT_WRITE);
- wh_dentry = ERR_PTR(err);
- if (unlikely(err))
- goto out;
-
- h_path.dentry = au_pinned_h_parent(pin);
- if (udba != AuOpt_UDBA_NONE
- && au_dbstart(dentry) == bcpup) {
- err = au_may_del(dentry, bcpup, h_path.dentry, isdir);
- wh_dentry = ERR_PTR(err);
- if (unlikely(err))
- goto out_unpin;
- }
-
- h_path.mnt = au_sbr_mnt(sb, bcpup);
- au_dtime_store(dt, au_pinned_parent(pin), &h_path);
- wh_dentry = NULL;
- if (!need_wh)
- goto out; /* success, no need to create whiteout */
-
- wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry);
- if (IS_ERR(wh_dentry))
- goto out_unpin;
-
- /* returns with the parent is locked and wh_dentry is dget-ed */
- goto out; /* success */
-
-out_unpin:
- au_unpin(pin);
-out:
- return wh_dentry;
-}
-
-/*
- * when removing a dir, rename it to a unique temporary whiteout-ed name first
- * in order to be revertible and save time for removing many child whiteouts
- * under the dir.
- * returns 1 when there are too many child whiteout and caller should remove
- * them asynchronously. returns 0 when the number of children is enough small to
- * remove now or the branch fs is a remote fs.
- * otherwise return an error.
- */
-static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex,
- struct au_nhash *whlist, struct inode *dir)
-{
- int rmdir_later, err, dirwh;
- struct dentry *h_dentry;
- struct super_block *sb;
- struct inode *inode;
-
- sb = dentry->d_sb;
- SiMustAnyLock(sb);
- h_dentry = au_h_dptr(dentry, bindex);
- err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex));
- if (unlikely(err))
- goto out;
-
- /* stop monitoring */
- inode = d_inode(dentry);
- au_hn_free(au_hi(inode, bindex));
-
- if (!au_test_fs_remote(h_dentry->d_sb)) {
- dirwh = au_sbi(sb)->si_dirwh;
- rmdir_later = (dirwh <= 1);
- if (!rmdir_later)
- rmdir_later = au_nhash_test_longer_wh(whlist, bindex,
- dirwh);
- if (rmdir_later)
- return rmdir_later;
- }
-
- err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist);
- if (unlikely(err)) {
- AuIOErr("rmdir %pd, b%d failed, %d. ignored\n",
- h_dentry, bindex, err);
- err = 0;
- }
-
-out:
- AuTraceErr(err);
- return err;
-}
-
-/*
- * final procedure for deleting a entry.
- * maintain dentry and iattr.
- */
-static void epilog(struct inode *dir, struct dentry *dentry,
- aufs_bindex_t bindex)
-{
- struct inode *inode;
-
- inode = d_inode(dentry);
- d_drop(dentry);
- inode->i_ctime = dir->i_ctime;
-
- au_dir_ts(dir, bindex);
- dir->i_version++;
-}
-
-/*
- * when an error happened, remove the created whiteout and revert everything.
- */
-static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex,
- aufs_bindex_t bwh, struct dentry *wh_dentry,
- struct dentry *dentry, struct au_dtime *dt)
-{
- int rerr;
- struct path h_path = {
- .dentry = wh_dentry,
- .mnt = au_sbr_mnt(dir->i_sb, bindex)
- };
-
- rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry);
- if (!rerr) {
- au_set_dbwh(dentry, bwh);
- au_dtime_revert(dt);
- return 0;
- }
-
- AuIOErr("%pd reverting whiteout failed(%d, %d)\n", dentry, err, rerr);
- return -EIO;
-}
-
-/* ---------------------------------------------------------------------- */
-
-int aufs_unlink(struct inode *dir, struct dentry *dentry)
-{
- int err;
- aufs_bindex_t bwh, bindex, bstart;
- struct inode *inode, *h_dir, *delegated;
- struct dentry *parent, *wh_dentry;
- /* to reuduce stack size */
- struct {
- struct au_dtime dt;
- struct au_pin pin;
- struct path h_path;
- } *a;
-
- IMustLock(dir);
-
- err = -ENOMEM;
- a = kmalloc(sizeof(*a), GFP_NOFS);
- if (unlikely(!a))
- goto out;
-
- err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN);
- if (unlikely(err))
- goto out_free;
- err = au_d_hashed_positive(dentry);
- if (unlikely(err))
- goto out_unlock;
- inode = d_inode(dentry);
- IMustLock(inode);
- err = -EISDIR;
- if (unlikely(d_is_dir(dentry)))
- goto out_unlock; /* possible? */
-
- bstart = au_dbstart(dentry);
- bwh = au_dbwh(dentry);
- bindex = -1;
- parent = dentry->d_parent; /* dir inode is locked */
- di_write_lock_parent(parent);
- wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt,
- &a->pin);
- err = PTR_ERR(wh_dentry);
- if (IS_ERR(wh_dentry))
- goto out_parent;
-
- a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart);
- a->h_path.dentry = au_h_dptr(dentry, bstart);
- dget(a->h_path.dentry);
- if (bindex == bstart) {
- h_dir = au_pinned_h_dir(&a->pin);
- delegated = NULL;
- err = vfsub_unlink(h_dir, &a->h_path, &delegated, /*force*/0);
- if (unlikely(err == -EWOULDBLOCK)) {
- pr_warn("cannot retry for NFSv4 delegation"
- " for an internal unlink\n");
- iput(delegated);
- }
- } else {
- /* dir inode is locked */
- h_dir = d_inode(wh_dentry->d_parent);
- IMustLock(h_dir);
- err = 0;
- }
-
- if (!err) {
- vfsub_drop_nlink(inode);
- epilog(dir, dentry, bindex);
-
- /* update target timestamps */
- if (bindex == bstart) {
- vfsub_update_h_iattr(&a->h_path, /*did*/NULL);
- /*ignore*/
- inode->i_ctime = d_inode(a->h_path.dentry)->i_ctime;
- } else
- /* todo: this timestamp may be reverted later */
- inode->i_ctime = h_dir->i_ctime;
- goto out_unpin; /* success */
- }
-
- /* revert */
- if (wh_dentry) {
- int rerr;
-
- rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
- &a->dt);
- if (rerr)
- err = rerr;
- }
-
-out_unpin:
- au_unpin(&a->pin);
- dput(wh_dentry);
- dput(a->h_path.dentry);
-out_parent:
- di_write_unlock(parent);
-out_unlock:
- aufs_read_unlock(dentry, AuLock_DW);
-out_free:
- kfree(a);
-out:
- return err;
-}
-
-int aufs_rmdir(struct inode *dir, struct dentry *dentry)
-{
- int err, rmdir_later;
- aufs_bindex_t bwh, bindex, bstart;
- struct inode *inode;
- struct dentry *parent, *wh_dentry, *h_dentry;
- struct au_whtmp_rmdir *args;
- /* to reuduce stack size */
- struct {
- struct au_dtime dt;
- struct au_pin pin;
- } *a;
-
- IMustLock(dir);
-
- err = -ENOMEM;
- a = kmalloc(sizeof(*a), GFP_NOFS);
- if (unlikely(!a))
- goto out;
-
- err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
- if (unlikely(err))
- goto out_free;
- err = au_alive_dir(dentry);
- if (unlikely(err))
- goto out_unlock;
- inode = d_inode(dentry);
- IMustLock(inode);
- err = -ENOTDIR;
- if (unlikely(!d_is_dir(dentry)))
- goto out_unlock; /* possible? */
-
- err = -ENOMEM;
- args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS);
- if (unlikely(!args))
- goto out_unlock;
-
- parent = dentry->d_parent; /* dir inode is locked */
- di_write_lock_parent(parent);
- err = au_test_empty(dentry, &args->whlist);
- if (unlikely(err))
- goto out_parent;
-
- bstart = au_dbstart(dentry);
- bwh = au_dbwh(dentry);
- bindex = -1;
- wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt,
- &a->pin);
- err = PTR_ERR(wh_dentry);
- if (IS_ERR(wh_dentry))
- goto out_parent;
-
- h_dentry = au_h_dptr(dentry, bstart);
- dget(h_dentry);
- rmdir_later = 0;
- if (bindex == bstart) {
- err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir);
- if (err > 0) {
- rmdir_later = err;
- err = 0;
- }
- } else {
- /* stop monitoring */
- au_hn_free(au_hi(inode, bstart));
-
- /* dir inode is locked */
- IMustLock(d_inode(wh_dentry->d_parent));
- err = 0;
- }
-
- if (!err) {
- vfsub_dead_dir(inode);
- au_set_dbdiropq(dentry, -1);
- epilog(dir, dentry, bindex);
-
- if (rmdir_later) {
- au_whtmp_kick_rmdir(dir, bstart, h_dentry, args);
- args = NULL;
- }
-
- goto out_unpin; /* success */
- }
-
- /* revert */
- AuLabel(revert);
- if (wh_dentry) {
- int rerr;
-
- rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry,
- &a->dt);
- if (rerr)
- err = rerr;
- }
-
-out_unpin:
- au_unpin(&a->pin);
- dput(wh_dentry);
- dput(h_dentry);
-out_parent:
- di_write_unlock(parent);
- if (args)
- au_whtmp_rmdir_free(args);
-out_unlock:
- aufs_read_unlock(dentry, AuLock_DW);
-out_free:
- kfree(a);
-out:
- AuTraceErr(err);
- return err;
-}