diff options
Diffstat (limited to 'fs/btrfs/inode.c')
| -rw-r--r-- | fs/btrfs/inode.c | 151 | 
1 files changed, 138 insertions, 13 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index bcc461a9695f..512c3d1da083 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -90,13 +90,14 @@ static noinline int cow_file_range(struct inode *inode,  				   unsigned long *nr_written, int unlock);  static int btrfs_init_inode_security(struct btrfs_trans_handle *trans, -				     struct inode *inode,  struct inode *dir) +				     struct inode *inode,  struct inode *dir, +				     const struct qstr *qstr)  {  	int err;  	err = btrfs_init_acl(trans, inode, dir);  	if (!err) -		err = btrfs_xattr_security_init(trans, inode, dir); +		err = btrfs_xattr_security_init(trans, inode, dir, qstr);  	return err;  } @@ -644,6 +645,7 @@ retry:  					async_extent->ram_size - 1, 0);  		em = alloc_extent_map(GFP_NOFS); +		BUG_ON(!em);  		em->start = async_extent->start;  		em->len = async_extent->ram_size;  		em->orig_start = em->start; @@ -820,6 +822,7 @@ static noinline int cow_file_range(struct inode *inode,  		BUG_ON(ret);  		em = alloc_extent_map(GFP_NOFS); +		BUG_ON(!em);  		em->start = start;  		em->orig_start = em->start;  		ram_size = ins.offset; @@ -1169,6 +1172,7 @@ out_check:  			struct extent_map_tree *em_tree;  			em_tree = &BTRFS_I(inode)->extent_tree;  			em = alloc_extent_map(GFP_NOFS); +			BUG_ON(!em);  			em->start = cur_offset;  			em->orig_start = em->start;  			em->len = num_bytes; @@ -1910,7 +1914,7 @@ static int btrfs_clean_io_failures(struct inode *inode, u64 start)  	private = 0;  	if (count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private, -			     (u64)-1, 1, EXTENT_DIRTY)) { +			     (u64)-1, 1, EXTENT_DIRTY, 0)) {  		ret = get_state_private(&BTRFS_I(inode)->io_failure_tree,  					start, &private_failure);  		if (ret == 0) { @@ -4701,7 +4705,7 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,  	if (IS_ERR(inode))  		goto out_unlock; -	err = btrfs_init_inode_security(trans, inode, dir); +	err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);  	if (err) {  		drop_inode = 1;  		goto out_unlock; @@ -4762,7 +4766,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,  	if (IS_ERR(inode))  		goto out_unlock; -	err = btrfs_init_inode_security(trans, inode, dir); +	err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);  	if (err) {  		drop_inode = 1;  		goto out_unlock; @@ -4803,9 +4807,6 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,  	int err;  	int drop_inode = 0; -	if (inode->i_nlink == 0) -		return -ENOENT; -  	/* do not allow sys_link's with other subvols of the same device */  	if (root->objectid != BTRFS_I(inode)->root->objectid)  		return -EPERM; @@ -4818,10 +4819,11 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,  		goto fail;  	/* -	 * 1 item for inode ref +	 * 2 items for inode and inode ref  	 * 2 items for dir items +	 * 1 item for parent inode  	 */ -	trans = btrfs_start_transaction(root, 3); +	trans = btrfs_start_transaction(root, 5);  	if (IS_ERR(trans)) {  		err = PTR_ERR(trans);  		goto fail; @@ -4890,7 +4892,7 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)  	drop_on_err = 1; -	err = btrfs_init_inode_security(trans, inode, dir); +	err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);  	if (err)  		goto out_fail; @@ -5277,6 +5279,128 @@ out:  	return em;  } +struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page, +					   size_t pg_offset, u64 start, u64 len, +					   int create) +{ +	struct extent_map *em; +	struct extent_map *hole_em = NULL; +	u64 range_start = start; +	u64 end; +	u64 found; +	u64 found_end; +	int err = 0; + +	em = btrfs_get_extent(inode, page, pg_offset, start, len, create); +	if (IS_ERR(em)) +		return em; +	if (em) { +		/* +		 * if our em maps to a hole, there might +		 * actually be delalloc bytes behind it +		 */ +		if (em->block_start != EXTENT_MAP_HOLE) +			return em; +		else +			hole_em = em; +	} + +	/* check to see if we've wrapped (len == -1 or similar) */ +	end = start + len; +	if (end < start) +		end = (u64)-1; +	else +		end -= 1; + +	em = NULL; + +	/* ok, we didn't find anything, lets look for delalloc */ +	found = count_range_bits(&BTRFS_I(inode)->io_tree, &range_start, +				 end, len, EXTENT_DELALLOC, 1); +	found_end = range_start + found; +	if (found_end < range_start) +		found_end = (u64)-1; + +	/* +	 * we didn't find anything useful, return +	 * the original results from get_extent() +	 */ +	if (range_start > end || found_end <= start) { +		em = hole_em; +		hole_em = NULL; +		goto out; +	} + +	/* adjust the range_start to make sure it doesn't +	 * go backwards from the start they passed in +	 */ +	range_start = max(start,range_start); +	found = found_end - range_start; + +	if (found > 0) { +		u64 hole_start = start; +		u64 hole_len = len; + +		em = alloc_extent_map(GFP_NOFS); +		if (!em) { +			err = -ENOMEM; +			goto out; +		} +		/* +		 * when btrfs_get_extent can't find anything it +		 * returns one huge hole +		 * +		 * make sure what it found really fits our range, and +		 * adjust to make sure it is based on the start from +		 * the caller +		 */ +		if (hole_em) { +			u64 calc_end = extent_map_end(hole_em); + +			if (calc_end <= start || (hole_em->start > end)) { +				free_extent_map(hole_em); +				hole_em = NULL; +			} else { +				hole_start = max(hole_em->start, start); +				hole_len = calc_end - hole_start; +			} +		} +		em->bdev = NULL; +		if (hole_em && range_start > hole_start) { +			/* our hole starts before our delalloc, so we +			 * have to return just the parts of the hole +			 * that go until  the delalloc starts +			 */ +			em->len = min(hole_len, +				      range_start - hole_start); +			em->start = hole_start; +			em->orig_start = hole_start; +			/* +			 * don't adjust block start at all, +			 * it is fixed at EXTENT_MAP_HOLE +			 */ +			em->block_start = hole_em->block_start; +			em->block_len = hole_len; +		} else { +			em->start = range_start; +			em->len = found; +			em->orig_start = range_start; +			em->block_start = EXTENT_MAP_DELALLOC; +			em->block_len = found; +		} +	} else if (hole_em) { +		return hole_em; +	} +out: + +	free_extent_map(hole_em); +	if (err) { +		free_extent_map(em); +		return ERR_PTR(err); +	} +	return em; +} +  static struct extent_map *btrfs_new_extent_direct(struct inode *inode,  						  u64 start, u64 len)  { @@ -5931,6 +6055,7 @@ static void btrfs_submit_direct(int rw, struct bio *bio, struct inode *inode,  	if (!skip_sum) {  		dip->csums = kmalloc(sizeof(u32) * bio->bi_vcnt, GFP_NOFS);  		if (!dip->csums) { +			kfree(dip);  			ret = -ENOMEM;  			goto free_ordered;  		} @@ -6099,7 +6224,7 @@ out:  static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,  		__u64 start, __u64 len)  { -	return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent); +	return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent_fiemap);  }  int btrfs_readpage(struct file *file, struct page *page) @@ -6979,7 +7104,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,  	if (IS_ERR(inode))  		goto out_unlock; -	err = btrfs_init_inode_security(trans, inode, dir); +	err = btrfs_init_inode_security(trans, inode, dir, &dentry->d_name);  	if (err) {  		drop_inode = 1;  		goto out_unlock;  | 
