From patchwork Mon Oct 11 18:08:19 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Goffredo Baroncelli X-Patchwork-Id: 245981 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o9BI4iZj001061 for ; Mon, 11 Oct 2010 18:04:44 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755697Ab0JKSEk (ORCPT ); Mon, 11 Oct 2010 14:04:40 -0400 Received: from smtp207.alice.it ([82.57.200.103]:52639 "EHLO smtp207.alice.it" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755684Ab0JKSEk (ORCPT ); Mon, 11 Oct 2010 14:04:40 -0400 Received: from venice.localnet (87.7.234.5) by smtp207.alice.it (8.5.124.08) id 4C9E16C90123FB4F; Mon, 11 Oct 2010 20:04:33 +0200 To: linux-btrfs@vger.kernel.org Subject: [PATCH V2] Removing a subvolume by an ordinary user From: Goffredo Baroncelli Reply-To: kreijack@libero.it Cc: Chris Mason Date: Mon, 11 Oct 2010 20:08:19 +0200 MIME-Version: 1.0 Message-Id: <201010112008.31736.kreijack@libero.it> Sender: linux-btrfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-btrfs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Mon, 11 Oct 2010 18:04:44 +0000 (UTC) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f08427c..0df2fb0 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2944,6 +2944,84 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, return 0; } +static noinline int btrfs_snap_destroy(struct inode *dir, + struct dentry *dentry) +{ + + struct inode *inode; + struct btrfs_root *root = BTRFS_I(dir)->root; + struct btrfs_root *dest = NULL; + struct btrfs_trans_handle *trans; + int ret; + int err = 0; + + if (IS_ERR(dentry)) { + err = PTR_ERR(dentry); + goto out; + } + + if (!dentry->d_inode) { + err = -ENOENT; + goto out; + } + + inode = dentry->d_inode; + if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) { + err = -EINVAL; + goto out; + } + + dest = BTRFS_I(inode)->root; + + down_write(&root->fs_info->subvol_sem); + + /* remove this check because the directory is empty. + * err = may_destroy_subvol(dest); + * if (err) + * goto out_up_write; + */ + + trans = btrfs_start_transaction(root, 0); + if (IS_ERR(trans)) { + err = PTR_ERR(trans); + goto out_up_write; + } + trans->block_rsv = &root->fs_info->global_block_rsv; + + ret = btrfs_unlink_subvol(trans, root, dir, + dest->root_key.objectid, + dentry->d_name.name, + dentry->d_name.len); + BUG_ON(ret); + + btrfs_record_root_in_trans(trans, dest); + + memset(&dest->root_item.drop_progress, 0, + sizeof(dest->root_item.drop_progress)); + dest->root_item.drop_level = 0; + btrfs_set_root_refs(&dest->root_item, 0); + + if (!xchg(&dest->orphan_item_inserted, 1)) { + ret = btrfs_insert_orphan_item(trans, + root->fs_info->tree_root, + dest->root_key.objectid); + BUG_ON(ret); + } + + ret = btrfs_commit_transaction(trans, root); + BUG_ON(ret); + inode->i_flags |= S_DEAD; +out_up_write: + up_write(&root->fs_info->subvol_sem); + if (!err) { + shrink_dcache_sb(root->fs_info->sb); + btrfs_invalidate_inodes(dest); + /*d_delete(dentry);*/ + } +out: + return err; +} + static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) { struct inode *inode = dentry->d_inode; @@ -2952,10 +3030,12 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) struct btrfs_trans_handle *trans; unsigned long nr = 0; - if (inode->i_size > BTRFS_EMPTY_DIR_SIZE || - inode->i_ino == BTRFS_FIRST_FREE_OBJECTID) + if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) return -ENOTEMPTY; + if (inode->i_ino == BTRFS_FIRST_FREE_OBJECTID) + return btrfs_snap_destroy(dir, dentry); + trans = __unlink_start_trans(dir, dentry); if (IS_ERR(trans)) return PTR_ERR(trans);