forked from mirrors/linux
		
	Btrfs: add snapshot/subvolume destroy ioctl
This patch adds snapshot/subvolume destroy ioctl. A subvolume that isn't being used and doesn't contains links to other subvolumes can be destroyed. Signed-off-by: Yan Zheng <zheng.yan@oracle.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
		
							parent
							
								
									4df27c4d5c
								
							
						
					
					
						commit
						76dda93c6a
					
				
					 12 changed files with 610 additions and 238 deletions
				
			
		| 
						 | 
					@ -839,9 +839,7 @@ struct btrfs_fs_info {
 | 
				
			||||||
	struct mutex transaction_kthread_mutex;
 | 
						struct mutex transaction_kthread_mutex;
 | 
				
			||||||
	struct mutex cleaner_mutex;
 | 
						struct mutex cleaner_mutex;
 | 
				
			||||||
	struct mutex chunk_mutex;
 | 
						struct mutex chunk_mutex;
 | 
				
			||||||
	struct mutex drop_mutex;
 | 
					 | 
				
			||||||
	struct mutex volume_mutex;
 | 
						struct mutex volume_mutex;
 | 
				
			||||||
	struct mutex tree_reloc_mutex;
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * this protects the ordered operations list only while we are
 | 
						 * this protects the ordered operations list only while we are
 | 
				
			||||||
	 * processing all of the entries on it.  This way we make
 | 
						 * processing all of the entries on it.  This way we make
 | 
				
			||||||
| 
						 | 
					@ -852,6 +850,10 @@ struct btrfs_fs_info {
 | 
				
			||||||
	struct mutex ordered_operations_mutex;
 | 
						struct mutex ordered_operations_mutex;
 | 
				
			||||||
	struct rw_semaphore extent_commit_sem;
 | 
						struct rw_semaphore extent_commit_sem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct rw_semaphore subvol_sem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct srcu_struct subvol_srcu;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct list_head trans_list;
 | 
						struct list_head trans_list;
 | 
				
			||||||
	struct list_head hashers;
 | 
						struct list_head hashers;
 | 
				
			||||||
	struct list_head dead_roots;
 | 
						struct list_head dead_roots;
 | 
				
			||||||
| 
						 | 
					@ -2142,6 +2144,7 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid, struct
 | 
				
			||||||
int btrfs_search_root(struct btrfs_root *root, u64 search_start,
 | 
					int btrfs_search_root(struct btrfs_root *root, u64 search_start,
 | 
				
			||||||
		      u64 *found_objectid);
 | 
							      u64 *found_objectid);
 | 
				
			||||||
int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
 | 
					int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid);
 | 
				
			||||||
 | 
					int btrfs_find_orphan_roots(struct btrfs_root *tree_root);
 | 
				
			||||||
int btrfs_set_root_node(struct btrfs_root_item *item,
 | 
					int btrfs_set_root_node(struct btrfs_root_item *item,
 | 
				
			||||||
			struct extent_buffer *node);
 | 
								struct extent_buffer *node);
 | 
				
			||||||
/* dir-item.c */
 | 
					/* dir-item.c */
 | 
				
			||||||
| 
						 | 
					@ -2273,7 +2276,7 @@ int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end);
 | 
				
			||||||
int btrfs_writepages(struct address_space *mapping,
 | 
					int btrfs_writepages(struct address_space *mapping,
 | 
				
			||||||
		     struct writeback_control *wbc);
 | 
							     struct writeback_control *wbc);
 | 
				
			||||||
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
 | 
					int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
 | 
				
			||||||
			     struct btrfs_root *new_root, struct dentry *dentry,
 | 
								     struct btrfs_root *new_root,
 | 
				
			||||||
			     u64 new_dirid, u64 alloc_hint);
 | 
								     u64 new_dirid, u64 alloc_hint);
 | 
				
			||||||
int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
 | 
					int btrfs_merge_bio_hook(struct page *page, unsigned long offset,
 | 
				
			||||||
			 size_t size, struct bio *bio, unsigned long bio_flags);
 | 
								 size_t size, struct bio *bio, unsigned long bio_flags);
 | 
				
			||||||
| 
						 | 
					@ -2289,6 +2292,7 @@ int btrfs_write_inode(struct inode *inode, int wait);
 | 
				
			||||||
void btrfs_dirty_inode(struct inode *inode);
 | 
					void btrfs_dirty_inode(struct inode *inode);
 | 
				
			||||||
struct inode *btrfs_alloc_inode(struct super_block *sb);
 | 
					struct inode *btrfs_alloc_inode(struct super_block *sb);
 | 
				
			||||||
void btrfs_destroy_inode(struct inode *inode);
 | 
					void btrfs_destroy_inode(struct inode *inode);
 | 
				
			||||||
 | 
					void btrfs_drop_inode(struct inode *inode);
 | 
				
			||||||
int btrfs_init_cachep(void);
 | 
					int btrfs_init_cachep(void);
 | 
				
			||||||
void btrfs_destroy_cachep(void);
 | 
					void btrfs_destroy_cachep(void);
 | 
				
			||||||
long btrfs_ioctl_trans_end(struct file *file);
 | 
					long btrfs_ioctl_trans_end(struct file *file);
 | 
				
			||||||
| 
						 | 
					@ -2306,6 +2310,8 @@ int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode);
 | 
				
			||||||
int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode);
 | 
					int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode);
 | 
				
			||||||
void btrfs_orphan_cleanup(struct btrfs_root *root);
 | 
					void btrfs_orphan_cleanup(struct btrfs_root *root);
 | 
				
			||||||
int btrfs_cont_expand(struct inode *inode, loff_t size);
 | 
					int btrfs_cont_expand(struct inode *inode, loff_t size);
 | 
				
			||||||
 | 
					int btrfs_invalidate_inodes(struct btrfs_root *root);
 | 
				
			||||||
 | 
					extern struct dentry_operations btrfs_dentry_operations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* ioctl.c */
 | 
					/* ioctl.c */
 | 
				
			||||||
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 | 
					long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1378,8 +1378,10 @@ static int setup_bdi(struct btrfs_fs_info *info, struct backing_dev_info *bdi)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = bdi_register(bdi, NULL, "btrfs-%d",
 | 
						err = bdi_register(bdi, NULL, "btrfs-%d",
 | 
				
			||||||
				atomic_inc_return(&btrfs_bdi_num));
 | 
									atomic_inc_return(&btrfs_bdi_num));
 | 
				
			||||||
	if (err)
 | 
						if (err) {
 | 
				
			||||||
 | 
							bdi_destroy(bdi);
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bdi->ra_pages	= default_backing_dev_info.ra_pages;
 | 
						bdi->ra_pages	= default_backing_dev_info.ra_pages;
 | 
				
			||||||
	bdi->unplug_io_fn	= btrfs_unplug_io_fn;
 | 
						bdi->unplug_io_fn	= btrfs_unplug_io_fn;
 | 
				
			||||||
| 
						 | 
					@ -1469,9 +1471,12 @@ static int cleaner_kthread(void *arg)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
 | 
							vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
 | 
				
			||||||
		mutex_lock(&root->fs_info->cleaner_mutex);
 | 
					
 | 
				
			||||||
		btrfs_clean_old_snapshots(root);
 | 
							if (!(root->fs_info->sb->s_flags & MS_RDONLY) &&
 | 
				
			||||||
		mutex_unlock(&root->fs_info->cleaner_mutex);
 | 
							    mutex_trylock(&root->fs_info->cleaner_mutex)) {
 | 
				
			||||||
 | 
								btrfs_clean_old_snapshots(root);
 | 
				
			||||||
 | 
								mutex_unlock(&root->fs_info->cleaner_mutex);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (freezing(current)) {
 | 
							if (freezing(current)) {
 | 
				
			||||||
			refrigerator();
 | 
								refrigerator();
 | 
				
			||||||
| 
						 | 
					@ -1576,7 +1581,26 @@ struct btrfs_root *open_ctree(struct super_block *sb,
 | 
				
			||||||
		err = -ENOMEM;
 | 
							err = -ENOMEM;
 | 
				
			||||||
		goto fail;
 | 
							goto fail;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_NOFS);
 | 
					
 | 
				
			||||||
 | 
						ret = init_srcu_struct(&fs_info->subvol_srcu);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							err = ret;
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = setup_bdi(fs_info, &fs_info->bdi);
 | 
				
			||||||
 | 
						if (ret) {
 | 
				
			||||||
 | 
							err = ret;
 | 
				
			||||||
 | 
							goto fail_srcu;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs_info->btree_inode = new_inode(sb);
 | 
				
			||||||
 | 
						if (!fs_info->btree_inode) {
 | 
				
			||||||
 | 
							err = -ENOMEM;
 | 
				
			||||||
 | 
							goto fail_bdi;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_ATOMIC);
 | 
				
			||||||
	INIT_LIST_HEAD(&fs_info->trans_list);
 | 
						INIT_LIST_HEAD(&fs_info->trans_list);
 | 
				
			||||||
	INIT_LIST_HEAD(&fs_info->dead_roots);
 | 
						INIT_LIST_HEAD(&fs_info->dead_roots);
 | 
				
			||||||
	INIT_LIST_HEAD(&fs_info->hashers);
 | 
						INIT_LIST_HEAD(&fs_info->hashers);
 | 
				
			||||||
| 
						 | 
					@ -1586,6 +1610,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
 | 
				
			||||||
	spin_lock_init(&fs_info->delalloc_lock);
 | 
						spin_lock_init(&fs_info->delalloc_lock);
 | 
				
			||||||
	spin_lock_init(&fs_info->new_trans_lock);
 | 
						spin_lock_init(&fs_info->new_trans_lock);
 | 
				
			||||||
	spin_lock_init(&fs_info->ref_cache_lock);
 | 
						spin_lock_init(&fs_info->ref_cache_lock);
 | 
				
			||||||
 | 
						spin_lock_init(&fs_info->fs_roots_radix_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	init_completion(&fs_info->kobj_unregister);
 | 
						init_completion(&fs_info->kobj_unregister);
 | 
				
			||||||
	fs_info->tree_root = tree_root;
 | 
						fs_info->tree_root = tree_root;
 | 
				
			||||||
| 
						 | 
					@ -1604,11 +1629,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
 | 
				
			||||||
	fs_info->sb = sb;
 | 
						fs_info->sb = sb;
 | 
				
			||||||
	fs_info->max_extent = (u64)-1;
 | 
						fs_info->max_extent = (u64)-1;
 | 
				
			||||||
	fs_info->max_inline = 8192 * 1024;
 | 
						fs_info->max_inline = 8192 * 1024;
 | 
				
			||||||
	if (setup_bdi(fs_info, &fs_info->bdi))
 | 
					 | 
				
			||||||
		goto fail_bdi;
 | 
					 | 
				
			||||||
	fs_info->btree_inode = new_inode(sb);
 | 
					 | 
				
			||||||
	fs_info->btree_inode->i_ino = 1;
 | 
					 | 
				
			||||||
	fs_info->btree_inode->i_nlink = 1;
 | 
					 | 
				
			||||||
	fs_info->metadata_ratio = 8;
 | 
						fs_info->metadata_ratio = 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fs_info->thread_pool_size = min_t(unsigned long,
 | 
						fs_info->thread_pool_size = min_t(unsigned long,
 | 
				
			||||||
| 
						 | 
					@ -1620,6 +1640,8 @@ struct btrfs_root *open_ctree(struct super_block *sb,
 | 
				
			||||||
	sb->s_blocksize = 4096;
 | 
						sb->s_blocksize = 4096;
 | 
				
			||||||
	sb->s_blocksize_bits = blksize_bits(4096);
 | 
						sb->s_blocksize_bits = blksize_bits(4096);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs_info->btree_inode->i_ino = BTRFS_BTREE_INODE_OBJECTID;
 | 
				
			||||||
 | 
						fs_info->btree_inode->i_nlink = 1;
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * we set the i_size on the btree inode to the max possible int.
 | 
						 * we set the i_size on the btree inode to the max possible int.
 | 
				
			||||||
	 * the real end of the address space is determined by all of
 | 
						 * the real end of the address space is determined by all of
 | 
				
			||||||
| 
						 | 
					@ -1638,6 +1660,11 @@ struct btrfs_root *open_ctree(struct super_block *sb,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BTRFS_I(fs_info->btree_inode)->io_tree.ops = &btree_extent_io_ops;
 | 
						BTRFS_I(fs_info->btree_inode)->io_tree.ops = &btree_extent_io_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BTRFS_I(fs_info->btree_inode)->root = tree_root;
 | 
				
			||||||
 | 
						memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
 | 
				
			||||||
 | 
						       sizeof(struct btrfs_key));
 | 
				
			||||||
 | 
						BTRFS_I(fs_info->btree_inode)->dummy_inode = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock_init(&fs_info->block_group_cache_lock);
 | 
						spin_lock_init(&fs_info->block_group_cache_lock);
 | 
				
			||||||
	fs_info->block_group_cache_tree.rb_node = NULL;
 | 
						fs_info->block_group_cache_tree.rb_node = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1648,21 +1675,16 @@ struct btrfs_root *open_ctree(struct super_block *sb,
 | 
				
			||||||
	fs_info->pinned_extents = &fs_info->freed_extents[0];
 | 
						fs_info->pinned_extents = &fs_info->freed_extents[0];
 | 
				
			||||||
	fs_info->do_barriers = 1;
 | 
						fs_info->do_barriers = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BTRFS_I(fs_info->btree_inode)->root = tree_root;
 | 
					 | 
				
			||||||
	memset(&BTRFS_I(fs_info->btree_inode)->location, 0,
 | 
					 | 
				
			||||||
	       sizeof(struct btrfs_key));
 | 
					 | 
				
			||||||
	insert_inode_hash(fs_info->btree_inode);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_init(&fs_info->trans_mutex);
 | 
						mutex_init(&fs_info->trans_mutex);
 | 
				
			||||||
	mutex_init(&fs_info->ordered_operations_mutex);
 | 
						mutex_init(&fs_info->ordered_operations_mutex);
 | 
				
			||||||
	mutex_init(&fs_info->tree_log_mutex);
 | 
						mutex_init(&fs_info->tree_log_mutex);
 | 
				
			||||||
	mutex_init(&fs_info->drop_mutex);
 | 
					 | 
				
			||||||
	mutex_init(&fs_info->chunk_mutex);
 | 
						mutex_init(&fs_info->chunk_mutex);
 | 
				
			||||||
	mutex_init(&fs_info->transaction_kthread_mutex);
 | 
						mutex_init(&fs_info->transaction_kthread_mutex);
 | 
				
			||||||
	mutex_init(&fs_info->cleaner_mutex);
 | 
						mutex_init(&fs_info->cleaner_mutex);
 | 
				
			||||||
	mutex_init(&fs_info->volume_mutex);
 | 
						mutex_init(&fs_info->volume_mutex);
 | 
				
			||||||
	mutex_init(&fs_info->tree_reloc_mutex);
 | 
					 | 
				
			||||||
	init_rwsem(&fs_info->extent_commit_sem);
 | 
						init_rwsem(&fs_info->extent_commit_sem);
 | 
				
			||||||
 | 
						init_rwsem(&fs_info->subvol_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	btrfs_init_free_cluster(&fs_info->meta_alloc_cluster);
 | 
						btrfs_init_free_cluster(&fs_info->meta_alloc_cluster);
 | 
				
			||||||
	btrfs_init_free_cluster(&fs_info->data_alloc_cluster);
 | 
						btrfs_init_free_cluster(&fs_info->data_alloc_cluster);
 | 
				
			||||||
| 
						 | 
					@ -1941,6 +1963,9 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = btrfs_find_orphan_roots(tree_root);
 | 
				
			||||||
 | 
						BUG_ON(ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!(sb->s_flags & MS_RDONLY)) {
 | 
						if (!(sb->s_flags & MS_RDONLY)) {
 | 
				
			||||||
		ret = btrfs_recover_relocation(tree_root);
 | 
							ret = btrfs_recover_relocation(tree_root);
 | 
				
			||||||
		BUG_ON(ret);
 | 
							BUG_ON(ret);
 | 
				
			||||||
| 
						 | 
					@ -2000,6 +2025,8 @@ printk("thread pool is %d\n", fs_info->thread_pool_size);
 | 
				
			||||||
	btrfs_mapping_tree_free(&fs_info->mapping_tree);
 | 
						btrfs_mapping_tree_free(&fs_info->mapping_tree);
 | 
				
			||||||
fail_bdi:
 | 
					fail_bdi:
 | 
				
			||||||
	bdi_destroy(&fs_info->bdi);
 | 
						bdi_destroy(&fs_info->bdi);
 | 
				
			||||||
 | 
					fail_srcu:
 | 
				
			||||||
 | 
						cleanup_srcu_struct(&fs_info->subvol_srcu);
 | 
				
			||||||
fail:
 | 
					fail:
 | 
				
			||||||
	kfree(extent_root);
 | 
						kfree(extent_root);
 | 
				
			||||||
	kfree(tree_root);
 | 
						kfree(tree_root);
 | 
				
			||||||
| 
						 | 
					@ -2263,6 +2290,10 @@ int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root)
 | 
				
			||||||
	radix_tree_delete(&fs_info->fs_roots_radix,
 | 
						radix_tree_delete(&fs_info->fs_roots_radix,
 | 
				
			||||||
			  (unsigned long)root->root_key.objectid);
 | 
								  (unsigned long)root->root_key.objectid);
 | 
				
			||||||
	spin_unlock(&fs_info->fs_roots_radix_lock);
 | 
						spin_unlock(&fs_info->fs_roots_radix_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (btrfs_root_refs(&root->root_item) == 0)
 | 
				
			||||||
 | 
							synchronize_srcu(&fs_info->subvol_srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free_fs_root(root);
 | 
						free_fs_root(root);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2286,6 +2317,20 @@ static int del_fs_roots(struct btrfs_fs_info *fs_info)
 | 
				
			||||||
	struct btrfs_root *gang[8];
 | 
						struct btrfs_root *gang[8];
 | 
				
			||||||
	int i;
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (!list_empty(&fs_info->dead_roots)) {
 | 
				
			||||||
 | 
							gang[0] = list_entry(fs_info->dead_roots.next,
 | 
				
			||||||
 | 
									     struct btrfs_root, root_list);
 | 
				
			||||||
 | 
							list_del(&gang[0]->root_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (gang[0]->in_radix) {
 | 
				
			||||||
 | 
								btrfs_free_fs_root(fs_info, gang[0]);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								free_extent_buffer(gang[0]->node);
 | 
				
			||||||
 | 
								free_extent_buffer(gang[0]->commit_root);
 | 
				
			||||||
 | 
								kfree(gang[0]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
		ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix,
 | 
							ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix,
 | 
				
			||||||
					     (void **)gang, 0,
 | 
										     (void **)gang, 0,
 | 
				
			||||||
| 
						 | 
					@ -2315,9 +2360,6 @@ int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info)
 | 
				
			||||||
		root_objectid = gang[ret - 1]->root_key.objectid + 1;
 | 
							root_objectid = gang[ret - 1]->root_key.objectid + 1;
 | 
				
			||||||
		for (i = 0; i < ret; i++) {
 | 
							for (i = 0; i < ret; i++) {
 | 
				
			||||||
			root_objectid = gang[i]->root_key.objectid;
 | 
								root_objectid = gang[i]->root_key.objectid;
 | 
				
			||||||
			ret = btrfs_find_dead_roots(fs_info->tree_root,
 | 
					 | 
				
			||||||
						    root_objectid);
 | 
					 | 
				
			||||||
			BUG_ON(ret);
 | 
					 | 
				
			||||||
			btrfs_orphan_cleanup(gang[i]);
 | 
								btrfs_orphan_cleanup(gang[i]);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		root_objectid++;
 | 
							root_objectid++;
 | 
				
			||||||
| 
						 | 
					@ -2405,6 +2447,7 @@ int close_ctree(struct btrfs_root *root)
 | 
				
			||||||
	btrfs_mapping_tree_free(&fs_info->mapping_tree);
 | 
						btrfs_mapping_tree_free(&fs_info->mapping_tree);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bdi_destroy(&fs_info->bdi);
 | 
						bdi_destroy(&fs_info->bdi);
 | 
				
			||||||
 | 
						cleanup_srcu_struct(&fs_info->subvol_srcu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(fs_info->extent_root);
 | 
						kfree(fs_info->extent_root);
 | 
				
			||||||
	kfree(fs_info->tree_root);
 | 
						kfree(fs_info->tree_root);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,7 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
 | 
				
			||||||
	len  = BTRFS_FID_SIZE_NON_CONNECTABLE;
 | 
						len  = BTRFS_FID_SIZE_NON_CONNECTABLE;
 | 
				
			||||||
	type = FILEID_BTRFS_WITHOUT_PARENT;
 | 
						type = FILEID_BTRFS_WITHOUT_PARENT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fid->objectid = BTRFS_I(inode)->location.objectid;
 | 
						fid->objectid = inode->i_ino;
 | 
				
			||||||
	fid->root_objectid = BTRFS_I(inode)->root->objectid;
 | 
						fid->root_objectid = BTRFS_I(inode)->root->objectid;
 | 
				
			||||||
	fid->gen = inode->i_generation;
 | 
						fid->gen = inode->i_generation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -60,34 +60,61 @@ static int btrfs_encode_fh(struct dentry *dentry, u32 *fh, int *max_len,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
 | 
					static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid,
 | 
				
			||||||
				       u64 root_objectid, u32 generation)
 | 
									       u64 root_objectid, u32 generation,
 | 
				
			||||||
 | 
									       int check_generation)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct btrfs_fs_info *fs_info = btrfs_sb(sb)->fs_info;
 | 
				
			||||||
	struct btrfs_root *root;
 | 
						struct btrfs_root *root;
 | 
				
			||||||
 | 
						struct dentry *dentry;
 | 
				
			||||||
	struct inode *inode;
 | 
						struct inode *inode;
 | 
				
			||||||
	struct btrfs_key key;
 | 
						struct btrfs_key key;
 | 
				
			||||||
 | 
						int index;
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (objectid < BTRFS_FIRST_FREE_OBJECTID)
 | 
				
			||||||
 | 
							return ERR_PTR(-ESTALE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key.objectid = root_objectid;
 | 
						key.objectid = root_objectid;
 | 
				
			||||||
	btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
 | 
						btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY);
 | 
				
			||||||
	key.offset = (u64)-1;
 | 
						key.offset = (u64)-1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	root = btrfs_read_fs_root_no_name(btrfs_sb(sb)->fs_info, &key);
 | 
						index = srcu_read_lock(&fs_info->subvol_srcu);
 | 
				
			||||||
	if (IS_ERR(root))
 | 
					
 | 
				
			||||||
		return ERR_CAST(root);
 | 
						root = btrfs_read_fs_root_no_name(fs_info, &key);
 | 
				
			||||||
 | 
						if (IS_ERR(root)) {
 | 
				
			||||||
 | 
							err = PTR_ERR(root);
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (btrfs_root_refs(&root->root_item) == 0) {
 | 
				
			||||||
 | 
							err = -ENOENT;
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key.objectid = objectid;
 | 
						key.objectid = objectid;
 | 
				
			||||||
	btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
 | 
						btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
 | 
				
			||||||
	key.offset = 0;
 | 
						key.offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inode = btrfs_iget(sb, &key, root);
 | 
						inode = btrfs_iget(sb, &key, root);
 | 
				
			||||||
	if (IS_ERR(inode))
 | 
						if (IS_ERR(inode)) {
 | 
				
			||||||
		return (void *)inode;
 | 
							err = PTR_ERR(inode);
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (generation != inode->i_generation) {
 | 
						srcu_read_unlock(&fs_info->subvol_srcu, index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (check_generation && generation != inode->i_generation) {
 | 
				
			||||||
		iput(inode);
 | 
							iput(inode);
 | 
				
			||||||
		return ERR_PTR(-ESTALE);
 | 
							return ERR_PTR(-ESTALE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return d_obtain_alias(inode);
 | 
						dentry = d_obtain_alias(inode);
 | 
				
			||||||
 | 
						if (!IS_ERR(dentry))
 | 
				
			||||||
 | 
							dentry->d_op = &btrfs_dentry_operations;
 | 
				
			||||||
 | 
						return dentry;
 | 
				
			||||||
 | 
					fail:
 | 
				
			||||||
 | 
						srcu_read_unlock(&fs_info->subvol_srcu, index);
 | 
				
			||||||
 | 
						return ERR_PTR(err);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh,
 | 
					static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh,
 | 
				
			||||||
| 
						 | 
					@ -111,7 +138,7 @@ static struct dentry *btrfs_fh_to_parent(struct super_block *sb, struct fid *fh,
 | 
				
			||||||
	objectid = fid->parent_objectid;
 | 
						objectid = fid->parent_objectid;
 | 
				
			||||||
	generation = fid->parent_gen;
 | 
						generation = fid->parent_gen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return btrfs_get_dentry(sb, objectid, root_objectid, generation);
 | 
						return btrfs_get_dentry(sb, objectid, root_objectid, generation, 1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
 | 
					static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
 | 
				
			||||||
| 
						 | 
					@ -133,66 +160,76 @@ static struct dentry *btrfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
 | 
				
			||||||
	root_objectid = fid->root_objectid;
 | 
						root_objectid = fid->root_objectid;
 | 
				
			||||||
	generation = fid->gen;
 | 
						generation = fid->gen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return btrfs_get_dentry(sb, objectid, root_objectid, generation);
 | 
						return btrfs_get_dentry(sb, objectid, root_objectid, generation, 1);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct dentry *btrfs_get_parent(struct dentry *child)
 | 
					static struct dentry *btrfs_get_parent(struct dentry *child)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *dir = child->d_inode;
 | 
						struct inode *dir = child->d_inode;
 | 
				
			||||||
 | 
						static struct dentry *dentry;
 | 
				
			||||||
	struct btrfs_root *root = BTRFS_I(dir)->root;
 | 
						struct btrfs_root *root = BTRFS_I(dir)->root;
 | 
				
			||||||
	struct btrfs_key key;
 | 
					 | 
				
			||||||
	struct btrfs_path *path;
 | 
						struct btrfs_path *path;
 | 
				
			||||||
	struct extent_buffer *leaf;
 | 
						struct extent_buffer *leaf;
 | 
				
			||||||
	int slot;
 | 
						struct btrfs_root_ref *ref;
 | 
				
			||||||
	u64 objectid;
 | 
						struct btrfs_key key;
 | 
				
			||||||
 | 
						struct btrfs_key found_key;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	path = btrfs_alloc_path();
 | 
						path = btrfs_alloc_path();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key.objectid = dir->i_ino;
 | 
						if (dir->i_ino == BTRFS_FIRST_FREE_OBJECTID) {
 | 
				
			||||||
	btrfs_set_key_type(&key, BTRFS_INODE_REF_KEY);
 | 
							key.objectid = root->root_key.objectid;
 | 
				
			||||||
	key.offset = (u64)-1;
 | 
							key.type = BTRFS_ROOT_BACKREF_KEY;
 | 
				
			||||||
 | 
							key.offset = (u64)-1;
 | 
				
			||||||
 | 
							root = root->fs_info->tree_root;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							key.objectid = dir->i_ino;
 | 
				
			||||||
 | 
							key.type = BTRFS_INODE_REF_KEY;
 | 
				
			||||||
 | 
							key.offset = (u64)-1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
 | 
						ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0)
 | 
				
			||||||
		/* Error */
 | 
							goto fail;
 | 
				
			||||||
		btrfs_free_path(path);
 | 
					
 | 
				
			||||||
		return ERR_PTR(ret);
 | 
						BUG_ON(ret == 0);
 | 
				
			||||||
	}
 | 
						if (path->slots[0] == 0) {
 | 
				
			||||||
	leaf = path->nodes[0];
 | 
							ret = -ENOENT;
 | 
				
			||||||
	slot = path->slots[0];
 | 
							goto fail;
 | 
				
			||||||
	if (ret) {
 | 
					 | 
				
			||||||
		/* btrfs_search_slot() returns the slot where we'd want to
 | 
					 | 
				
			||||||
		   insert a backref for parent inode #0xFFFFFFFFFFFFFFFF.
 | 
					 | 
				
			||||||
		   The _real_ backref, telling us what the parent inode
 | 
					 | 
				
			||||||
		   _actually_ is, will be in the slot _before_ the one
 | 
					 | 
				
			||||||
		   that btrfs_search_slot() returns. */
 | 
					 | 
				
			||||||
		if (!slot) {
 | 
					 | 
				
			||||||
			/* Unless there is _no_ key in the tree before... */
 | 
					 | 
				
			||||||
			btrfs_free_path(path);
 | 
					 | 
				
			||||||
			return ERR_PTR(-EIO);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		slot--;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	btrfs_item_key_to_cpu(leaf, &key, slot);
 | 
						path->slots[0]--;
 | 
				
			||||||
 | 
						leaf = path->nodes[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
 | 
				
			||||||
 | 
						if (found_key.objectid != key.objectid || found_key.type != key.type) {
 | 
				
			||||||
 | 
							ret = -ENOENT;
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (found_key.type == BTRFS_ROOT_BACKREF_KEY) {
 | 
				
			||||||
 | 
							ref = btrfs_item_ptr(leaf, path->slots[0],
 | 
				
			||||||
 | 
									     struct btrfs_root_ref);
 | 
				
			||||||
 | 
							key.objectid = btrfs_root_ref_dirid(leaf, ref);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							key.objectid = found_key.offset;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	btrfs_free_path(path);
 | 
						btrfs_free_path(path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (key.objectid != dir->i_ino || key.type != BTRFS_INODE_REF_KEY)
 | 
						if (found_key.type == BTRFS_ROOT_BACKREF_KEY) {
 | 
				
			||||||
		return ERR_PTR(-EINVAL);
 | 
							return btrfs_get_dentry(root->fs_info->sb, key.objectid,
 | 
				
			||||||
 | 
										found_key.offset, 0, 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	objectid = key.offset;
 | 
						key.type = BTRFS_INODE_ITEM_KEY;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* If we are already at the root of a subvol, return the real root */
 | 
					 | 
				
			||||||
	if (objectid == dir->i_ino)
 | 
					 | 
				
			||||||
		return dget(dir->i_sb->s_root);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Build a new key for the inode item */
 | 
					 | 
				
			||||||
	key.objectid = objectid;
 | 
					 | 
				
			||||||
	btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
 | 
					 | 
				
			||||||
	key.offset = 0;
 | 
						key.offset = 0;
 | 
				
			||||||
 | 
						dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root));
 | 
				
			||||||
	return d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root));
 | 
						if (!IS_ERR(dentry))
 | 
				
			||||||
 | 
							dentry->d_op = &btrfs_dentry_operations;
 | 
				
			||||||
 | 
						return dentry;
 | 
				
			||||||
 | 
					fail:
 | 
				
			||||||
 | 
						btrfs_free_path(path);
 | 
				
			||||||
 | 
						return ERR_PTR(ret);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const struct export_operations btrfs_export_ops = {
 | 
					const struct export_operations btrfs_export_ops = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5463,9 +5463,24 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref)
 | 
				
			||||||
	ret = btrfs_del_root(trans, tree_root, &root->root_key);
 | 
						ret = btrfs_del_root(trans, tree_root, &root->root_key);
 | 
				
			||||||
	BUG_ON(ret);
 | 
						BUG_ON(ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free_extent_buffer(root->node);
 | 
						if (root->root_key.objectid != BTRFS_TREE_RELOC_OBJECTID) {
 | 
				
			||||||
	free_extent_buffer(root->commit_root);
 | 
							ret = btrfs_find_last_root(tree_root, root->root_key.objectid,
 | 
				
			||||||
	kfree(root);
 | 
										   NULL, NULL);
 | 
				
			||||||
 | 
							BUG_ON(ret < 0);
 | 
				
			||||||
 | 
							if (ret > 0) {
 | 
				
			||||||
 | 
								ret = btrfs_del_orphan_item(trans, tree_root,
 | 
				
			||||||
 | 
											    root->root_key.objectid);
 | 
				
			||||||
 | 
								BUG_ON(ret);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (root->in_radix) {
 | 
				
			||||||
 | 
							btrfs_free_fs_root(tree_root->fs_info, root);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							free_extent_buffer(root->node);
 | 
				
			||||||
 | 
							free_extent_buffer(root->commit_root);
 | 
				
			||||||
 | 
							kfree(root);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	btrfs_end_transaction(trans, tree_root);
 | 
						btrfs_end_transaction(trans, tree_root);
 | 
				
			||||||
	kfree(wc);
 | 
						kfree(wc);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										134
									
								
								fs/btrfs/inode.c
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								fs/btrfs/inode.c
									
									
									
									
									
								
							| 
						 | 
					@ -3089,6 +3089,11 @@ void btrfs_delete_inode(struct inode *inode)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	btrfs_wait_ordered_range(inode, 0, (u64)-1);
 | 
						btrfs_wait_ordered_range(inode, 0, (u64)-1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (inode->i_nlink > 0) {
 | 
				
			||||||
 | 
							BUG_ON(btrfs_root_refs(&root->root_item) != 0);
 | 
				
			||||||
 | 
							goto no_delete;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	btrfs_i_size_write(inode, 0);
 | 
						btrfs_i_size_write(inode, 0);
 | 
				
			||||||
	trans = btrfs_join_transaction(root, 1);
 | 
						trans = btrfs_join_transaction(root, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3225,11 +3230,13 @@ static void inode_tree_add(struct inode *inode)
 | 
				
			||||||
	struct btrfs_inode *entry;
 | 
						struct btrfs_inode *entry;
 | 
				
			||||||
	struct rb_node **p;
 | 
						struct rb_node **p;
 | 
				
			||||||
	struct rb_node *parent;
 | 
						struct rb_node *parent;
 | 
				
			||||||
 | 
					 | 
				
			||||||
again:
 | 
					again:
 | 
				
			||||||
	p = &root->inode_tree.rb_node;
 | 
						p = &root->inode_tree.rb_node;
 | 
				
			||||||
	parent = NULL;
 | 
						parent = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (hlist_unhashed(&inode->i_hash))
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock(&root->inode_lock);
 | 
						spin_lock(&root->inode_lock);
 | 
				
			||||||
	while (*p) {
 | 
						while (*p) {
 | 
				
			||||||
		parent = *p;
 | 
							parent = *p;
 | 
				
			||||||
| 
						 | 
					@ -3256,13 +3263,87 @@ static void inode_tree_add(struct inode *inode)
 | 
				
			||||||
static void inode_tree_del(struct inode *inode)
 | 
					static void inode_tree_del(struct inode *inode)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct btrfs_root *root = BTRFS_I(inode)->root;
 | 
						struct btrfs_root *root = BTRFS_I(inode)->root;
 | 
				
			||||||
 | 
						int empty = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock(&root->inode_lock);
 | 
						spin_lock(&root->inode_lock);
 | 
				
			||||||
	if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
 | 
						if (!RB_EMPTY_NODE(&BTRFS_I(inode)->rb_node)) {
 | 
				
			||||||
		rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
 | 
							rb_erase(&BTRFS_I(inode)->rb_node, &root->inode_tree);
 | 
				
			||||||
		RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
 | 
							RB_CLEAR_NODE(&BTRFS_I(inode)->rb_node);
 | 
				
			||||||
 | 
							empty = RB_EMPTY_ROOT(&root->inode_tree);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	spin_unlock(&root->inode_lock);
 | 
						spin_unlock(&root->inode_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (empty && btrfs_root_refs(&root->root_item) == 0) {
 | 
				
			||||||
 | 
							synchronize_srcu(&root->fs_info->subvol_srcu);
 | 
				
			||||||
 | 
							spin_lock(&root->inode_lock);
 | 
				
			||||||
 | 
							empty = RB_EMPTY_ROOT(&root->inode_tree);
 | 
				
			||||||
 | 
							spin_unlock(&root->inode_lock);
 | 
				
			||||||
 | 
							if (empty)
 | 
				
			||||||
 | 
								btrfs_add_dead_root(root);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int btrfs_invalidate_inodes(struct btrfs_root *root)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct rb_node *node;
 | 
				
			||||||
 | 
						struct rb_node *prev;
 | 
				
			||||||
 | 
						struct btrfs_inode *entry;
 | 
				
			||||||
 | 
						struct inode *inode;
 | 
				
			||||||
 | 
						u64 objectid = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						WARN_ON(btrfs_root_refs(&root->root_item) != 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock(&root->inode_lock);
 | 
				
			||||||
 | 
					again:
 | 
				
			||||||
 | 
						node = root->inode_tree.rb_node;
 | 
				
			||||||
 | 
						prev = NULL;
 | 
				
			||||||
 | 
						while (node) {
 | 
				
			||||||
 | 
							prev = node;
 | 
				
			||||||
 | 
							entry = rb_entry(node, struct btrfs_inode, rb_node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (objectid < entry->vfs_inode.i_ino)
 | 
				
			||||||
 | 
								node = node->rb_left;
 | 
				
			||||||
 | 
							else if (objectid > entry->vfs_inode.i_ino)
 | 
				
			||||||
 | 
								node = node->rb_right;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!node) {
 | 
				
			||||||
 | 
							while (prev) {
 | 
				
			||||||
 | 
								entry = rb_entry(prev, struct btrfs_inode, rb_node);
 | 
				
			||||||
 | 
								if (objectid <= entry->vfs_inode.i_ino) {
 | 
				
			||||||
 | 
									node = prev;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								prev = rb_next(prev);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						while (node) {
 | 
				
			||||||
 | 
							entry = rb_entry(node, struct btrfs_inode, rb_node);
 | 
				
			||||||
 | 
							objectid = entry->vfs_inode.i_ino + 1;
 | 
				
			||||||
 | 
							inode = igrab(&entry->vfs_inode);
 | 
				
			||||||
 | 
							if (inode) {
 | 
				
			||||||
 | 
								spin_unlock(&root->inode_lock);
 | 
				
			||||||
 | 
								if (atomic_read(&inode->i_count) > 1)
 | 
				
			||||||
 | 
									d_prune_aliases(inode);
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * btrfs_drop_inode will remove it from
 | 
				
			||||||
 | 
								 * the inode cache when its usage count
 | 
				
			||||||
 | 
								 * hits zero.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								iput(inode);
 | 
				
			||||||
 | 
								cond_resched();
 | 
				
			||||||
 | 
								spin_lock(&root->inode_lock);
 | 
				
			||||||
 | 
								goto again;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (cond_resched_lock(&root->inode_lock))
 | 
				
			||||||
 | 
								goto again;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							node = rb_next(node);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						spin_unlock(&root->inode_lock);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static noinline void init_btrfs_i(struct inode *inode)
 | 
					static noinline void init_btrfs_i(struct inode *inode)
 | 
				
			||||||
| 
						 | 
					@ -3379,8 +3460,11 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 | 
				
			||||||
	struct btrfs_root *root = BTRFS_I(dir)->root;
 | 
						struct btrfs_root *root = BTRFS_I(dir)->root;
 | 
				
			||||||
	struct btrfs_root *sub_root = root;
 | 
						struct btrfs_root *sub_root = root;
 | 
				
			||||||
	struct btrfs_key location;
 | 
						struct btrfs_key location;
 | 
				
			||||||
 | 
						int index;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dentry->d_op = &btrfs_dentry_operations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dentry->d_name.len > BTRFS_NAME_LEN)
 | 
						if (dentry->d_name.len > BTRFS_NAME_LEN)
 | 
				
			||||||
		return ERR_PTR(-ENAMETOOLONG);
 | 
							return ERR_PTR(-ENAMETOOLONG);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3399,6 +3483,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON(location.type != BTRFS_ROOT_ITEM_KEY);
 | 
						BUG_ON(location.type != BTRFS_ROOT_ITEM_KEY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						index = srcu_read_lock(&root->fs_info->subvol_srcu);
 | 
				
			||||||
	ret = fixup_tree_root_location(root, dir, dentry,
 | 
						ret = fixup_tree_root_location(root, dir, dentry,
 | 
				
			||||||
				       &location, &sub_root);
 | 
									       &location, &sub_root);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0) {
 | 
				
			||||||
| 
						 | 
					@ -3409,9 +3494,24 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		inode = btrfs_iget(dir->i_sb, &location, sub_root);
 | 
							inode = btrfs_iget(dir->i_sb, &location, sub_root);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						srcu_read_unlock(&root->fs_info->subvol_srcu, index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return inode;
 | 
						return inode;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int btrfs_dentry_delete(struct dentry *dentry)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct btrfs_root *root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dentry->d_inode)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						root = BTRFS_I(dentry->d_inode)->root;
 | 
				
			||||||
 | 
						if (btrfs_root_refs(&root->root_item) == 0)
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
 | 
					static struct dentry *btrfs_lookup(struct inode *dir, struct dentry *dentry,
 | 
				
			||||||
				   struct nameidata *nd)
 | 
									   struct nameidata *nd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -4773,11 +4873,11 @@ static void btrfs_truncate(struct inode *inode)
 | 
				
			||||||
 * create a new subvolume directory/inode (helper for the ioctl).
 | 
					 * create a new subvolume directory/inode (helper for the ioctl).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
 | 
					int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
 | 
				
			||||||
			     struct btrfs_root *new_root, struct dentry *dentry,
 | 
								     struct btrfs_root *new_root,
 | 
				
			||||||
			     u64 new_dirid, u64 alloc_hint)
 | 
								     u64 new_dirid, u64 alloc_hint)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *inode;
 | 
						struct inode *inode;
 | 
				
			||||||
	int error;
 | 
						int err;
 | 
				
			||||||
	u64 index = 0;
 | 
						u64 index = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inode = btrfs_new_inode(trans, new_root, NULL, "..", 2, new_dirid,
 | 
						inode = btrfs_new_inode(trans, new_root, NULL, "..", 2, new_dirid,
 | 
				
			||||||
| 
						 | 
					@ -4790,11 +4890,10 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
 | 
				
			||||||
	inode->i_nlink = 1;
 | 
						inode->i_nlink = 1;
 | 
				
			||||||
	btrfs_i_size_write(inode, 0);
 | 
						btrfs_i_size_write(inode, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	error = btrfs_update_inode(trans, new_root, inode);
 | 
						err = btrfs_update_inode(trans, new_root, inode);
 | 
				
			||||||
	if (error)
 | 
						BUG_ON(err);
 | 
				
			||||||
		return error;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	d_instantiate(dentry, inode);
 | 
						iput(inode);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4872,6 +4971,16 @@ void btrfs_destroy_inode(struct inode *inode)
 | 
				
			||||||
	kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
 | 
						kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void btrfs_drop_inode(struct inode *inode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct btrfs_root *root = BTRFS_I(inode)->root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (inode->i_nlink > 0 && btrfs_root_refs(&root->root_item) == 0)
 | 
				
			||||||
 | 
							generic_delete_inode(inode);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							generic_drop_inode(inode);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void init_once(void *foo)
 | 
					static void init_once(void *foo)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct btrfs_inode *ei = (struct btrfs_inode *) foo;
 | 
						struct btrfs_inode *ei = (struct btrfs_inode *) foo;
 | 
				
			||||||
| 
						 | 
					@ -4973,6 +5082,10 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 | 
				
			||||||
	    old_inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT)
 | 
						    old_inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT)
 | 
				
			||||||
		filemap_flush(old_inode->i_mapping);
 | 
							filemap_flush(old_inode->i_mapping);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* close the racy window with snapshot create/destroy ioctl */
 | 
				
			||||||
 | 
						if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
 | 
				
			||||||
 | 
							down_read(&root->fs_info->subvol_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trans = btrfs_start_transaction(root, 1);
 | 
						trans = btrfs_start_transaction(root, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dest != root)
 | 
						if (dest != root)
 | 
				
			||||||
| 
						 | 
					@ -5062,6 +5175,8 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	btrfs_end_transaction_throttle(trans, root);
 | 
						btrfs_end_transaction_throttle(trans, root);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (old_inode->i_ino == BTRFS_FIRST_FREE_OBJECTID)
 | 
				
			||||||
 | 
							up_read(&root->fs_info->subvol_sem);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5420,6 +5535,7 @@ static struct inode_operations btrfs_dir_ro_inode_operations = {
 | 
				
			||||||
	.lookup		= btrfs_lookup,
 | 
						.lookup		= btrfs_lookup,
 | 
				
			||||||
	.permission	= btrfs_permission,
 | 
						.permission	= btrfs_permission,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct file_operations btrfs_dir_file_operations = {
 | 
					static struct file_operations btrfs_dir_file_operations = {
 | 
				
			||||||
	.llseek		= generic_file_llseek,
 | 
						.llseek		= generic_file_llseek,
 | 
				
			||||||
	.read		= generic_read_dir,
 | 
						.read		= generic_read_dir,
 | 
				
			||||||
| 
						 | 
					@ -5506,3 +5622,7 @@ static struct inode_operations btrfs_symlink_inode_operations = {
 | 
				
			||||||
	.listxattr	= btrfs_listxattr,
 | 
						.listxattr	= btrfs_listxattr,
 | 
				
			||||||
	.removexattr	= btrfs_removexattr,
 | 
						.removexattr	= btrfs_removexattr,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct dentry_operations btrfs_dentry_operations = {
 | 
				
			||||||
 | 
						.d_delete	= btrfs_dentry_delete,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										320
									
								
								fs/btrfs/ioctl.c
									
									
									
									
									
								
							
							
						
						
									
										320
									
								
								fs/btrfs/ioctl.c
									
									
									
									
									
								
							| 
						 | 
					@ -230,8 +230,8 @@ static noinline int create_subvol(struct btrfs_root *root,
 | 
				
			||||||
	struct btrfs_root_item root_item;
 | 
						struct btrfs_root_item root_item;
 | 
				
			||||||
	struct btrfs_inode_item *inode_item;
 | 
						struct btrfs_inode_item *inode_item;
 | 
				
			||||||
	struct extent_buffer *leaf;
 | 
						struct extent_buffer *leaf;
 | 
				
			||||||
	struct btrfs_root *new_root = root;
 | 
						struct btrfs_root *new_root;
 | 
				
			||||||
	struct inode *dir;
 | 
						struct inode *dir = dentry->d_parent->d_inode;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
	u64 objectid;
 | 
						u64 objectid;
 | 
				
			||||||
| 
						 | 
					@ -241,7 +241,7 @@ static noinline int create_subvol(struct btrfs_root *root,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = btrfs_check_metadata_free_space(root);
 | 
						ret = btrfs_check_metadata_free_space(root);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		goto fail_commit;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trans = btrfs_start_transaction(root, 1);
 | 
						trans = btrfs_start_transaction(root, 1);
 | 
				
			||||||
	BUG_ON(!trans);
 | 
						BUG_ON(!trans);
 | 
				
			||||||
| 
						 | 
					@ -304,11 +304,17 @@ static noinline int create_subvol(struct btrfs_root *root,
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
		goto fail;
 | 
							goto fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						key.offset = (u64)-1;
 | 
				
			||||||
 | 
						new_root = btrfs_read_fs_root_no_name(root->fs_info, &key);
 | 
				
			||||||
 | 
						BUG_ON(IS_ERR(new_root));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						btrfs_record_root_in_trans(trans, new_root);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = btrfs_create_subvol_root(trans, new_root, new_dirid,
 | 
				
			||||||
 | 
									       BTRFS_I(dir)->block_group);
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * insert the directory item
 | 
						 * insert the directory item
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	key.offset = (u64)-1;
 | 
					 | 
				
			||||||
	dir = dentry->d_parent->d_inode;
 | 
					 | 
				
			||||||
	ret = btrfs_set_inode_index(dir, &index);
 | 
						ret = btrfs_set_inode_index(dir, &index);
 | 
				
			||||||
	BUG_ON(ret);
 | 
						BUG_ON(ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -325,30 +331,15 @@ static noinline int create_subvol(struct btrfs_root *root,
 | 
				
			||||||
	ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
 | 
						ret = btrfs_add_root_ref(trans, root->fs_info->tree_root,
 | 
				
			||||||
				 objectid, root->root_key.objectid,
 | 
									 objectid, root->root_key.objectid,
 | 
				
			||||||
				 dir->i_ino, index, name, namelen);
 | 
									 dir->i_ino, index, name, namelen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON(ret);
 | 
						BUG_ON(ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = btrfs_commit_transaction(trans, root);
 | 
						d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry));
 | 
				
			||||||
	if (ret)
 | 
					 | 
				
			||||||
		goto fail_commit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	new_root = btrfs_read_fs_root_no_name(root->fs_info, &key);
 | 
					 | 
				
			||||||
	BUG_ON(!new_root);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	trans = btrfs_start_transaction(new_root, 1);
 | 
					 | 
				
			||||||
	BUG_ON(!trans);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ret = btrfs_create_subvol_root(trans, new_root, dentry, new_dirid,
 | 
					 | 
				
			||||||
				       BTRFS_I(dir)->block_group);
 | 
					 | 
				
			||||||
	if (ret)
 | 
					 | 
				
			||||||
		goto fail;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fail:
 | 
					fail:
 | 
				
			||||||
	nr = trans->blocks_used;
 | 
						nr = trans->blocks_used;
 | 
				
			||||||
	err = btrfs_commit_transaction(trans, new_root);
 | 
						err = btrfs_commit_transaction(trans, root);
 | 
				
			||||||
	if (err && !ret)
 | 
						if (err && !ret)
 | 
				
			||||||
		ret = err;
 | 
							ret = err;
 | 
				
			||||||
fail_commit:
 | 
					 | 
				
			||||||
	btrfs_btree_balance_dirty(root, nr);
 | 
					 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -409,14 +400,15 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
 | 
				
			||||||
 * sys_mkdirat and vfs_mkdir, but we only do a single component lookup
 | 
					 * sys_mkdirat and vfs_mkdir, but we only do a single component lookup
 | 
				
			||||||
 * inside this filesystem so it's quite a bit simpler.
 | 
					 * inside this filesystem so it's quite a bit simpler.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static noinline int btrfs_mksubvol(struct path *parent, char *name,
 | 
					static noinline int btrfs_mksubvol(struct path *parent,
 | 
				
			||||||
				   int mode, int namelen,
 | 
									   char *name, int namelen,
 | 
				
			||||||
				   struct btrfs_root *snap_src)
 | 
									   struct btrfs_root *snap_src)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct inode *dir  = parent->dentry->d_inode;
 | 
				
			||||||
	struct dentry *dentry;
 | 
						struct dentry *dentry;
 | 
				
			||||||
	int error;
 | 
						int error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock_nested(&parent->dentry->d_inode->i_mutex, I_MUTEX_PARENT);
 | 
						mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dentry = lookup_one_len(name, parent->dentry, namelen);
 | 
						dentry = lookup_one_len(name, parent->dentry, namelen);
 | 
				
			||||||
	error = PTR_ERR(dentry);
 | 
						error = PTR_ERR(dentry);
 | 
				
			||||||
| 
						 | 
					@ -427,99 +419,39 @@ static noinline int btrfs_mksubvol(struct path *parent, char *name,
 | 
				
			||||||
	if (dentry->d_inode)
 | 
						if (dentry->d_inode)
 | 
				
			||||||
		goto out_dput;
 | 
							goto out_dput;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!IS_POSIXACL(parent->dentry->d_inode))
 | 
					 | 
				
			||||||
		mode &= ~current_umask();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	error = mnt_want_write(parent->mnt);
 | 
						error = mnt_want_write(parent->mnt);
 | 
				
			||||||
	if (error)
 | 
						if (error)
 | 
				
			||||||
		goto out_dput;
 | 
							goto out_dput;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	error = btrfs_may_create(parent->dentry->d_inode, dentry);
 | 
						error = btrfs_may_create(dir, dentry);
 | 
				
			||||||
	if (error)
 | 
						if (error)
 | 
				
			||||||
		goto out_drop_write;
 | 
							goto out_drop_write;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						down_read(&BTRFS_I(dir)->root->fs_info->subvol_sem);
 | 
				
			||||||
	 * Actually perform the low-level subvolume creation after all
 | 
					
 | 
				
			||||||
	 * this VFS fuzz.
 | 
						if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0)
 | 
				
			||||||
	 *
 | 
							goto out_up_read;
 | 
				
			||||||
	 * Eventually we want to pass in an inode under which we create this
 | 
					
 | 
				
			||||||
	 * subvolume, but for now all are under the filesystem root.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Also we should pass on the mode eventually to allow creating new
 | 
					 | 
				
			||||||
	 * subvolume with specific mode bits.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (snap_src) {
 | 
						if (snap_src) {
 | 
				
			||||||
		struct dentry *dir = dentry->d_parent;
 | 
							error = create_snapshot(snap_src, dentry,
 | 
				
			||||||
		struct dentry *test = dir->d_parent;
 | 
										name, namelen);
 | 
				
			||||||
		struct btrfs_path *path = btrfs_alloc_path();
 | 
					 | 
				
			||||||
		int ret;
 | 
					 | 
				
			||||||
		u64 test_oid;
 | 
					 | 
				
			||||||
		u64 parent_oid = BTRFS_I(dir->d_inode)->root->root_key.objectid;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		test_oid = snap_src->root_key.objectid;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ret = btrfs_find_root_ref(snap_src->fs_info->tree_root,
 | 
					 | 
				
			||||||
					  path, parent_oid, test_oid);
 | 
					 | 
				
			||||||
		if (ret == 0)
 | 
					 | 
				
			||||||
			goto create;
 | 
					 | 
				
			||||||
		btrfs_release_path(snap_src->fs_info->tree_root, path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* we need to make sure we aren't creating a directory loop
 | 
					 | 
				
			||||||
		 * by taking a snapshot of something that has our current
 | 
					 | 
				
			||||||
		 * subvol in its directory tree.  So, this loops through
 | 
					 | 
				
			||||||
		 * the dentries and checks the forward refs for each subvolume
 | 
					 | 
				
			||||||
		 * to see if is references the subvolume where we are
 | 
					 | 
				
			||||||
		 * placing this new snapshot.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		while (1) {
 | 
					 | 
				
			||||||
			if (!test ||
 | 
					 | 
				
			||||||
			    dir == snap_src->fs_info->sb->s_root ||
 | 
					 | 
				
			||||||
			    test == snap_src->fs_info->sb->s_root ||
 | 
					 | 
				
			||||||
			    test->d_inode->i_sb != snap_src->fs_info->sb) {
 | 
					 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (S_ISLNK(test->d_inode->i_mode)) {
 | 
					 | 
				
			||||||
				printk(KERN_INFO "Btrfs symlink in snapshot "
 | 
					 | 
				
			||||||
				       "path, failed\n");
 | 
					 | 
				
			||||||
				error = -EMLINK;
 | 
					 | 
				
			||||||
				btrfs_free_path(path);
 | 
					 | 
				
			||||||
				goto out_drop_write;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			test_oid =
 | 
					 | 
				
			||||||
				BTRFS_I(test->d_inode)->root->root_key.objectid;
 | 
					 | 
				
			||||||
			ret = btrfs_find_root_ref(snap_src->fs_info->tree_root,
 | 
					 | 
				
			||||||
				  path, test_oid, parent_oid);
 | 
					 | 
				
			||||||
			if (ret == 0) {
 | 
					 | 
				
			||||||
				printk(KERN_INFO "Btrfs snapshot creation "
 | 
					 | 
				
			||||||
				       "failed, looping\n");
 | 
					 | 
				
			||||||
				error = -EMLINK;
 | 
					 | 
				
			||||||
				btrfs_free_path(path);
 | 
					 | 
				
			||||||
				goto out_drop_write;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			btrfs_release_path(snap_src->fs_info->tree_root, path);
 | 
					 | 
				
			||||||
			test = test->d_parent;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
create:
 | 
					 | 
				
			||||||
		btrfs_free_path(path);
 | 
					 | 
				
			||||||
		error = create_snapshot(snap_src, dentry, name, namelen);
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		error = create_subvol(BTRFS_I(parent->dentry->d_inode)->root,
 | 
							error = create_subvol(BTRFS_I(dir)->root, dentry,
 | 
				
			||||||
				      dentry, name, namelen);
 | 
									      name, namelen);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (error)
 | 
						if (!error)
 | 
				
			||||||
		goto out_drop_write;
 | 
							fsnotify_mkdir(dir, dentry);
 | 
				
			||||||
 | 
					out_up_read:
 | 
				
			||||||
	fsnotify_mkdir(parent->dentry->d_inode, dentry);
 | 
						up_read(&BTRFS_I(dir)->root->fs_info->subvol_sem);
 | 
				
			||||||
out_drop_write:
 | 
					out_drop_write:
 | 
				
			||||||
	mnt_drop_write(parent->mnt);
 | 
						mnt_drop_write(parent->mnt);
 | 
				
			||||||
out_dput:
 | 
					out_dput:
 | 
				
			||||||
	dput(dentry);
 | 
						dput(dentry);
 | 
				
			||||||
out_unlock:
 | 
					out_unlock:
 | 
				
			||||||
	mutex_unlock(&parent->dentry->d_inode->i_mutex);
 | 
						mutex_unlock(&dir->i_mutex);
 | 
				
			||||||
	return error;
 | 
						return error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
static int btrfs_defrag_file(struct file *file)
 | 
					static int btrfs_defrag_file(struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *inode = fdentry(file)->d_inode;
 | 
						struct inode *inode = fdentry(file)->d_inode;
 | 
				
			||||||
| 
						 | 
					@ -597,7 +529,8 @@ static int btrfs_defrag_file(struct file *file)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int btrfs_ioctl_resize(struct btrfs_root *root, void __user *arg)
 | 
					static noinline int btrfs_ioctl_resize(struct btrfs_root *root,
 | 
				
			||||||
 | 
										void __user *arg)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u64 new_size;
 | 
						u64 new_size;
 | 
				
			||||||
	u64 old_size;
 | 
						u64 old_size;
 | 
				
			||||||
| 
						 | 
					@ -706,10 +639,7 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
 | 
						struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;
 | 
				
			||||||
	struct btrfs_ioctl_vol_args *vol_args;
 | 
						struct btrfs_ioctl_vol_args *vol_args;
 | 
				
			||||||
	struct btrfs_dir_item *di;
 | 
					 | 
				
			||||||
	struct btrfs_path *path;
 | 
					 | 
				
			||||||
	struct file *src_file;
 | 
						struct file *src_file;
 | 
				
			||||||
	u64 root_dirid;
 | 
					 | 
				
			||||||
	int namelen;
 | 
						int namelen;
 | 
				
			||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -727,32 +657,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	path = btrfs_alloc_path();
 | 
					 | 
				
			||||||
	if (!path) {
 | 
					 | 
				
			||||||
		ret = -ENOMEM;
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	root_dirid = root->fs_info->sb->s_root->d_inode->i_ino,
 | 
					 | 
				
			||||||
	di = btrfs_lookup_dir_item(NULL, root->fs_info->tree_root,
 | 
					 | 
				
			||||||
			    path, root_dirid,
 | 
					 | 
				
			||||||
			    vol_args->name, namelen, 0);
 | 
					 | 
				
			||||||
	btrfs_free_path(path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (di && !IS_ERR(di)) {
 | 
					 | 
				
			||||||
		ret = -EEXIST;
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (IS_ERR(di)) {
 | 
					 | 
				
			||||||
		ret = PTR_ERR(di);
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (subvol) {
 | 
						if (subvol) {
 | 
				
			||||||
		ret = btrfs_mksubvol(&file->f_path, vol_args->name,
 | 
							ret = btrfs_mksubvol(&file->f_path, vol_args->name, namelen,
 | 
				
			||||||
				     file->f_path.dentry->d_inode->i_mode,
 | 
									     NULL);
 | 
				
			||||||
				     namelen, NULL);
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		struct inode *src_inode;
 | 
							struct inode *src_inode;
 | 
				
			||||||
		src_file = fget(vol_args->fd);
 | 
							src_file = fget(vol_args->fd);
 | 
				
			||||||
| 
						 | 
					@ -769,17 +676,156 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
 | 
				
			||||||
			fput(src_file);
 | 
								fput(src_file);
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ret = btrfs_mksubvol(&file->f_path, vol_args->name,
 | 
							ret = btrfs_mksubvol(&file->f_path, vol_args->name, namelen,
 | 
				
			||||||
			     file->f_path.dentry->d_inode->i_mode,
 | 
									     BTRFS_I(src_inode)->root);
 | 
				
			||||||
			     namelen, BTRFS_I(src_inode)->root);
 | 
					 | 
				
			||||||
		fput(src_file);
 | 
							fput(src_file);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	kfree(vol_args);
 | 
						kfree(vol_args);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * helper to check if the subvolume references other subvolumes
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static noinline int may_destroy_subvol(struct btrfs_root *root)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct btrfs_path *path;
 | 
				
			||||||
 | 
						struct btrfs_key key;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						path = btrfs_alloc_path();
 | 
				
			||||||
 | 
						if (!path)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						key.objectid = root->root_key.objectid;
 | 
				
			||||||
 | 
						key.type = BTRFS_ROOT_REF_KEY;
 | 
				
			||||||
 | 
						key.offset = (u64)-1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = btrfs_search_slot(NULL, root->fs_info->tree_root,
 | 
				
			||||||
 | 
									&key, path, 0, 0);
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						BUG_ON(ret == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = 0;
 | 
				
			||||||
 | 
						if (path->slots[0] > 0) {
 | 
				
			||||||
 | 
							path->slots[0]--;
 | 
				
			||||||
 | 
							btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
 | 
				
			||||||
 | 
							if (key.objectid == root->root_key.objectid &&
 | 
				
			||||||
 | 
							    key.type == BTRFS_ROOT_REF_KEY)
 | 
				
			||||||
 | 
								ret = -ENOTEMPTY;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						btrfs_free_path(path);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static noinline int btrfs_ioctl_snap_destroy(struct file *file,
 | 
				
			||||||
 | 
										     void __user *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct dentry *parent = fdentry(file);
 | 
				
			||||||
 | 
						struct dentry *dentry;
 | 
				
			||||||
 | 
						struct inode *dir = parent->d_inode;
 | 
				
			||||||
 | 
						struct inode *inode;
 | 
				
			||||||
 | 
						struct btrfs_root *root = BTRFS_I(dir)->root;
 | 
				
			||||||
 | 
						struct btrfs_root *dest = NULL;
 | 
				
			||||||
 | 
						struct btrfs_ioctl_vol_args *vol_args;
 | 
				
			||||||
 | 
						struct btrfs_trans_handle *trans;
 | 
				
			||||||
 | 
						int namelen;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!capable(CAP_SYS_ADMIN))
 | 
				
			||||||
 | 
							return -EPERM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vol_args = memdup_user(arg, sizeof(*vol_args));
 | 
				
			||||||
 | 
						if (IS_ERR(vol_args))
 | 
				
			||||||
 | 
							return PTR_ERR(vol_args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
 | 
				
			||||||
 | 
						namelen = strlen(vol_args->name);
 | 
				
			||||||
 | 
						if (strchr(vol_args->name, '/') ||
 | 
				
			||||||
 | 
						    strncmp(vol_args->name, "..", namelen) == 0) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = mnt_want_write(file->f_path.mnt);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
 | 
				
			||||||
 | 
						dentry = lookup_one_len(vol_args->name, parent, namelen);
 | 
				
			||||||
 | 
						if (IS_ERR(dentry)) {
 | 
				
			||||||
 | 
							err = PTR_ERR(dentry);
 | 
				
			||||||
 | 
							goto out_unlock_dir;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dentry->d_inode) {
 | 
				
			||||||
 | 
							err = -ENOENT;
 | 
				
			||||||
 | 
							goto out_dput;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inode = dentry->d_inode;
 | 
				
			||||||
 | 
						if (inode->i_ino != BTRFS_FIRST_FREE_OBJECTID) {
 | 
				
			||||||
 | 
							err = -EINVAL;
 | 
				
			||||||
 | 
							goto out_dput;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dest = BTRFS_I(inode)->root;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&inode->i_mutex);
 | 
				
			||||||
 | 
						err = d_invalidate(dentry);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto out_unlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						down_write(&root->fs_info->subvol_sem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = may_destroy_subvol(dest);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto out_up_write;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trans = btrfs_start_transaction(root, 1);
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					out_unlock:
 | 
				
			||||||
 | 
						mutex_unlock(&inode->i_mutex);
 | 
				
			||||||
 | 
						if (!err) {
 | 
				
			||||||
 | 
							btrfs_invalidate_inodes(dest);
 | 
				
			||||||
 | 
							d_delete(dentry);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					out_dput:
 | 
				
			||||||
 | 
						dput(dentry);
 | 
				
			||||||
 | 
					out_unlock_dir:
 | 
				
			||||||
 | 
						mutex_unlock(&dir->i_mutex);
 | 
				
			||||||
 | 
						mnt_drop_write(file->f_path.mnt);
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						kfree(vol_args);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int btrfs_ioctl_defrag(struct file *file)
 | 
					static int btrfs_ioctl_defrag(struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *inode = fdentry(file)->d_inode;
 | 
						struct inode *inode = fdentry(file)->d_inode;
 | 
				
			||||||
| 
						 | 
					@ -853,8 +899,8 @@ static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
 | 
					static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
 | 
				
			||||||
		u64 off, u64 olen, u64 destoff)
 | 
									       u64 off, u64 olen, u64 destoff)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inode *inode = fdentry(file)->d_inode;
 | 
						struct inode *inode = fdentry(file)->d_inode;
 | 
				
			||||||
	struct btrfs_root *root = BTRFS_I(inode)->root;
 | 
						struct btrfs_root *root = BTRFS_I(inode)->root;
 | 
				
			||||||
| 
						 | 
					@ -1246,6 +1292,8 @@ long btrfs_ioctl(struct file *file, unsigned int
 | 
				
			||||||
		return btrfs_ioctl_snap_create(file, argp, 0);
 | 
							return btrfs_ioctl_snap_create(file, argp, 0);
 | 
				
			||||||
	case BTRFS_IOC_SUBVOL_CREATE:
 | 
						case BTRFS_IOC_SUBVOL_CREATE:
 | 
				
			||||||
		return btrfs_ioctl_snap_create(file, argp, 1);
 | 
							return btrfs_ioctl_snap_create(file, argp, 1);
 | 
				
			||||||
 | 
						case BTRFS_IOC_SNAP_DESTROY:
 | 
				
			||||||
 | 
							return btrfs_ioctl_snap_destroy(file, argp);
 | 
				
			||||||
	case BTRFS_IOC_DEFRAG:
 | 
						case BTRFS_IOC_DEFRAG:
 | 
				
			||||||
		return btrfs_ioctl_defrag(file);
 | 
							return btrfs_ioctl_defrag(file);
 | 
				
			||||||
	case BTRFS_IOC_RESIZE:
 | 
						case BTRFS_IOC_RESIZE:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,5 +65,6 @@ struct btrfs_ioctl_clone_range_args {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
 | 
					#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
 | 
				
			||||||
				   struct btrfs_ioctl_vol_args)
 | 
									   struct btrfs_ioctl_vol_args)
 | 
				
			||||||
 | 
					#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
 | 
				
			||||||
 | 
									struct btrfs_ioctl_vol_args)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3203,6 +3203,7 @@ static int check_extent_flags(u64 flags)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
 | 
					static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rb_root blocks = RB_ROOT;
 | 
						struct rb_root blocks = RB_ROOT;
 | 
				
			||||||
| 
						 | 
					@ -3220,6 +3221,9 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc)
 | 
				
			||||||
	if (!path)
 | 
						if (!path)
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc->extents_found = 0;
 | 
				
			||||||
 | 
						rc->extents_skipped = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc->search_start = rc->block_group->key.objectid;
 | 
						rc->search_start = rc->block_group->key.objectid;
 | 
				
			||||||
	clear_extent_bits(&rc->processed_blocks, 0, (u64)-1, EXTENT_DIRTY,
 | 
						clear_extent_bits(&rc->processed_blocks, 0, (u64)-1, EXTENT_DIRTY,
 | 
				
			||||||
			  GFP_NOFS);
 | 
								  GFP_NOFS);
 | 
				
			||||||
| 
						 | 
					@ -3475,14 +3479,15 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
 | 
				
			||||||
	btrfs_wait_ordered_extents(fs_info->tree_root, 0);
 | 
						btrfs_wait_ordered_extents(fs_info->tree_root, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
		mutex_lock(&fs_info->cleaner_mutex);
 | 
					 | 
				
			||||||
		btrfs_clean_old_snapshots(fs_info->tree_root);
 | 
					 | 
				
			||||||
		mutex_unlock(&fs_info->cleaner_mutex);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		rc->extents_found = 0;
 | 
							rc->extents_found = 0;
 | 
				
			||||||
		rc->extents_skipped = 0;
 | 
							rc->extents_skipped = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mutex_lock(&fs_info->cleaner_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							btrfs_clean_old_snapshots(fs_info->tree_root);
 | 
				
			||||||
		ret = relocate_block_group(rc);
 | 
							ret = relocate_block_group(rc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mutex_unlock(&fs_info->cleaner_mutex);
 | 
				
			||||||
		if (ret < 0) {
 | 
							if (ret < 0) {
 | 
				
			||||||
			err = ret;
 | 
								err = ret;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
| 
						 | 
					@ -3530,6 +3535,26 @@ int btrfs_relocate_block_group(struct btrfs_root *extent_root, u64 group_start)
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static noinline_for_stack int mark_garbage_root(struct btrfs_root *root)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct btrfs_trans_handle *trans;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trans = btrfs_start_transaction(root->fs_info->tree_root, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset(&root->root_item.drop_progress, 0,
 | 
				
			||||||
 | 
							sizeof(root->root_item.drop_progress));
 | 
				
			||||||
 | 
						root->root_item.drop_level = 0;
 | 
				
			||||||
 | 
						btrfs_set_root_refs(&root->root_item, 0);
 | 
				
			||||||
 | 
						ret = btrfs_update_root(trans, root->fs_info->tree_root,
 | 
				
			||||||
 | 
									&root->root_key, &root->root_item);
 | 
				
			||||||
 | 
						BUG_ON(ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret = btrfs_end_transaction(trans, root->fs_info->tree_root);
 | 
				
			||||||
 | 
						BUG_ON(ret);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * recover relocation interrupted by system crash.
 | 
					 * recover relocation interrupted by system crash.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -3589,8 +3614,12 @@ int btrfs_recover_relocation(struct btrfs_root *root)
 | 
				
			||||||
			fs_root = read_fs_root(root->fs_info,
 | 
								fs_root = read_fs_root(root->fs_info,
 | 
				
			||||||
					       reloc_root->root_key.offset);
 | 
										       reloc_root->root_key.offset);
 | 
				
			||||||
			if (IS_ERR(fs_root)) {
 | 
								if (IS_ERR(fs_root)) {
 | 
				
			||||||
				err = PTR_ERR(fs_root);
 | 
									ret = PTR_ERR(fs_root);
 | 
				
			||||||
				goto out;
 | 
									if (ret != -ENOENT) {
 | 
				
			||||||
 | 
										err = ret;
 | 
				
			||||||
 | 
										goto out;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									mark_garbage_root(reloc_root);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,17 +94,23 @@ int btrfs_find_last_root(struct btrfs_root *root, u64 objectid,
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BUG_ON(ret == 0);
 | 
						BUG_ON(ret == 0);
 | 
				
			||||||
	l = path->nodes[0];
 | 
						if (path->slots[0] == 0) {
 | 
				
			||||||
	BUG_ON(path->slots[0] == 0);
 | 
					 | 
				
			||||||
	slot = path->slots[0] - 1;
 | 
					 | 
				
			||||||
	btrfs_item_key_to_cpu(l, &found_key, slot);
 | 
					 | 
				
			||||||
	if (found_key.objectid != objectid) {
 | 
					 | 
				
			||||||
		ret = 1;
 | 
							ret = 1;
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot),
 | 
						l = path->nodes[0];
 | 
				
			||||||
			   sizeof(*item));
 | 
						slot = path->slots[0] - 1;
 | 
				
			||||||
	memcpy(key, &found_key, sizeof(found_key));
 | 
						btrfs_item_key_to_cpu(l, &found_key, slot);
 | 
				
			||||||
 | 
						if (found_key.objectid != objectid ||
 | 
				
			||||||
 | 
						    found_key.type != BTRFS_ROOT_ITEM_KEY) {
 | 
				
			||||||
 | 
							ret = 1;
 | 
				
			||||||
 | 
							goto out;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (item)
 | 
				
			||||||
 | 
							read_extent_buffer(l, item, btrfs_item_ptr_offset(l, slot),
 | 
				
			||||||
 | 
									   sizeof(*item));
 | 
				
			||||||
 | 
						if (key)
 | 
				
			||||||
 | 
							memcpy(key, &found_key, sizeof(found_key));
 | 
				
			||||||
	ret = 0;
 | 
						ret = 0;
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	btrfs_free_path(path);
 | 
						btrfs_free_path(path);
 | 
				
			||||||
| 
						 | 
					@ -249,6 +255,59 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int btrfs_find_orphan_roots(struct btrfs_root *tree_root)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct extent_buffer *leaf;
 | 
				
			||||||
 | 
						struct btrfs_path *path;
 | 
				
			||||||
 | 
						struct btrfs_key key;
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						path = btrfs_alloc_path();
 | 
				
			||||||
 | 
						if (!path)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						key.objectid = BTRFS_ORPHAN_OBJECTID;
 | 
				
			||||||
 | 
						key.type = BTRFS_ORPHAN_ITEM_KEY;
 | 
				
			||||||
 | 
						key.offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (1) {
 | 
				
			||||||
 | 
							ret = btrfs_search_slot(NULL, tree_root, &key, path, 0, 0);
 | 
				
			||||||
 | 
							if (ret < 0) {
 | 
				
			||||||
 | 
								err = ret;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							leaf = path->nodes[0];
 | 
				
			||||||
 | 
							if (path->slots[0] >= btrfs_header_nritems(leaf)) {
 | 
				
			||||||
 | 
								ret = btrfs_next_leaf(tree_root, path);
 | 
				
			||||||
 | 
								if (ret < 0)
 | 
				
			||||||
 | 
									err = ret;
 | 
				
			||||||
 | 
								if (ret != 0)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								leaf = path->nodes[0];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
 | 
				
			||||||
 | 
							btrfs_release_path(tree_root, path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (key.objectid != BTRFS_ORPHAN_OBJECTID ||
 | 
				
			||||||
 | 
							    key.type != BTRFS_ORPHAN_ITEM_KEY)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ret = btrfs_find_dead_roots(tree_root, key.offset);
 | 
				
			||||||
 | 
							if (ret) {
 | 
				
			||||||
 | 
								err = ret;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							key.offset++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						btrfs_free_path(path);
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* drop the root item for 'key' from 'root' */
 | 
					/* drop the root item for 'key' from 'root' */
 | 
				
			||||||
int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 | 
					int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root,
 | 
				
			||||||
		   struct btrfs_key *key)
 | 
							   struct btrfs_key *key)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -676,6 +676,7 @@ static int btrfs_unfreeze(struct super_block *sb)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct super_operations btrfs_super_ops = {
 | 
					static struct super_operations btrfs_super_ops = {
 | 
				
			||||||
 | 
						.drop_inode	= btrfs_drop_inode,
 | 
				
			||||||
	.delete_inode	= btrfs_delete_inode,
 | 
						.delete_inode	= btrfs_delete_inode,
 | 
				
			||||||
	.put_super	= btrfs_put_super,
 | 
						.put_super	= btrfs_put_super,
 | 
				
			||||||
	.sync_fs	= btrfs_sync_fs,
 | 
						.sync_fs	= btrfs_sync_fs,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,7 +104,6 @@ static noinline int record_root_in_trans(struct btrfs_trans_handle *trans,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (root->ref_cows && root->last_trans < trans->transid) {
 | 
						if (root->ref_cows && root->last_trans < trans->transid) {
 | 
				
			||||||
		WARN_ON(root == root->fs_info->extent_root);
 | 
							WARN_ON(root == root->fs_info->extent_root);
 | 
				
			||||||
		WARN_ON(root->root_item.refs == 0);
 | 
					 | 
				
			||||||
		WARN_ON(root->commit_root != root->node);
 | 
							WARN_ON(root->commit_root != root->node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		radix_tree_tag_set(&root->fs_info->fs_roots_radix,
 | 
							radix_tree_tag_set(&root->fs_info->fs_roots_radix,
 | 
				
			||||||
| 
						 | 
					@ -1078,8 +1077,13 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (!list_empty(&list)) {
 | 
						while (!list_empty(&list)) {
 | 
				
			||||||
		root = list_entry(list.next, struct btrfs_root, root_list);
 | 
							root = list_entry(list.next, struct btrfs_root, root_list);
 | 
				
			||||||
		list_del_init(&root->root_list);
 | 
							list_del(&root->root_list);
 | 
				
			||||||
		btrfs_drop_snapshot(root, 0);
 | 
					
 | 
				
			||||||
 | 
							if (btrfs_header_backref_rev(root->node) <
 | 
				
			||||||
 | 
							    BTRFS_MIXED_BACKREF_REV)
 | 
				
			||||||
 | 
								btrfs_drop_snapshot(root, 0);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								btrfs_drop_snapshot(root, 1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2841,7 +2841,7 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,
 | 
				
			||||||
		if (!parent || !parent->d_inode || sb != parent->d_inode->i_sb)
 | 
							if (!parent || !parent->d_inode || sb != parent->d_inode->i_sb)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (parent == sb->s_root)
 | 
							if (IS_ROOT(parent))
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		parent = parent->d_parent;
 | 
							parent = parent->d_parent;
 | 
				
			||||||
| 
						 | 
					@ -2880,6 +2880,12 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
 | 
				
			||||||
		goto end_no_trans;
 | 
							goto end_no_trans;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (root != BTRFS_I(inode)->root ||
 | 
				
			||||||
 | 
						    btrfs_root_refs(&root->root_item) == 0) {
 | 
				
			||||||
 | 
							ret = 1;
 | 
				
			||||||
 | 
							goto end_no_trans;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = check_parent_dirs_for_sync(trans, inode, parent,
 | 
						ret = check_parent_dirs_for_sync(trans, inode, parent,
 | 
				
			||||||
					 sb, last_committed);
 | 
										 sb, last_committed);
 | 
				
			||||||
	if (ret)
 | 
						if (ret)
 | 
				
			||||||
| 
						 | 
					@ -2907,12 +2913,15 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		inode = parent->d_inode;
 | 
							inode = parent->d_inode;
 | 
				
			||||||
 | 
							if (root != BTRFS_I(inode)->root)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (BTRFS_I(inode)->generation >
 | 
							if (BTRFS_I(inode)->generation >
 | 
				
			||||||
		    root->fs_info->last_trans_committed) {
 | 
							    root->fs_info->last_trans_committed) {
 | 
				
			||||||
			ret = btrfs_log_inode(trans, root, inode, inode_only);
 | 
								ret = btrfs_log_inode(trans, root, inode, inode_only);
 | 
				
			||||||
			BUG_ON(ret);
 | 
								BUG_ON(ret);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (parent == sb->s_root)
 | 
							if (IS_ROOT(parent))
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		parent = parent->d_parent;
 | 
							parent = parent->d_parent;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue