Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable: Btrfs: fix fiemap bugs with delalloc Btrfs: set FMODE_EXCL in btrfs_device->mode Btrfs: make btrfs_rm_device() fail gracefully Btrfs: Avoid accessing unmapped kernel address Btrfs: Fix BTRFS_IOC_SUBVOL_SETFLAGS ioctl Btrfs: allow balance to explicitly allocate chunks as it relocates Btrfs: put ENOSPC debugging under a mount option
This commit is contained in:
@@ -1433,12 +1433,13 @@ int extent_clear_unlock_delalloc(struct inode *inode,
|
||||
*/
|
||||
u64 count_range_bits(struct extent_io_tree *tree,
|
||||
u64 *start, u64 search_end, u64 max_bytes,
|
||||
unsigned long bits)
|
||||
unsigned long bits, int contig)
|
||||
{
|
||||
struct rb_node *node;
|
||||
struct extent_state *state;
|
||||
u64 cur_start = *start;
|
||||
u64 total_bytes = 0;
|
||||
u64 last = 0;
|
||||
int found = 0;
|
||||
|
||||
if (search_end <= cur_start) {
|
||||
@@ -1463,7 +1464,9 @@ u64 count_range_bits(struct extent_io_tree *tree,
|
||||
state = rb_entry(node, struct extent_state, rb_node);
|
||||
if (state->start > search_end)
|
||||
break;
|
||||
if (state->end >= cur_start && (state->state & bits)) {
|
||||
if (contig && found && state->start > last + 1)
|
||||
break;
|
||||
if (state->end >= cur_start && (state->state & bits) == bits) {
|
||||
total_bytes += min(search_end, state->end) + 1 -
|
||||
max(cur_start, state->start);
|
||||
if (total_bytes >= max_bytes)
|
||||
@@ -1472,6 +1475,9 @@ u64 count_range_bits(struct extent_io_tree *tree,
|
||||
*start = state->start;
|
||||
found = 1;
|
||||
}
|
||||
last = state->end;
|
||||
} else if (contig && found) {
|
||||
break;
|
||||
}
|
||||
node = rb_next(node);
|
||||
if (!node)
|
||||
@@ -2912,6 +2918,46 @@ out:
|
||||
return sector;
|
||||
}
|
||||
|
||||
/*
|
||||
* helper function for fiemap, which doesn't want to see any holes.
|
||||
* This maps until we find something past 'last'
|
||||
*/
|
||||
static struct extent_map *get_extent_skip_holes(struct inode *inode,
|
||||
u64 offset,
|
||||
u64 last,
|
||||
get_extent_t *get_extent)
|
||||
{
|
||||
u64 sectorsize = BTRFS_I(inode)->root->sectorsize;
|
||||
struct extent_map *em;
|
||||
u64 len;
|
||||
|
||||
if (offset >= last)
|
||||
return NULL;
|
||||
|
||||
while(1) {
|
||||
len = last - offset;
|
||||
if (len == 0)
|
||||
break;
|
||||
len = (len + sectorsize - 1) & ~(sectorsize - 1);
|
||||
em = get_extent(inode, NULL, 0, offset, len, 0);
|
||||
if (!em || IS_ERR(em))
|
||||
return em;
|
||||
|
||||
/* if this isn't a hole return it */
|
||||
if (!test_bit(EXTENT_FLAG_VACANCY, &em->flags) &&
|
||||
em->block_start != EXTENT_MAP_HOLE) {
|
||||
return em;
|
||||
}
|
||||
|
||||
/* this is a hole, advance to the next extent */
|
||||
offset = extent_map_end(em);
|
||||
free_extent_map(em);
|
||||
if (offset >= last)
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
__u64 start, __u64 len, get_extent_t *get_extent)
|
||||
{
|
||||
@@ -2921,16 +2967,19 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
u32 flags = 0;
|
||||
u32 found_type;
|
||||
u64 last;
|
||||
u64 last_for_get_extent = 0;
|
||||
u64 disko = 0;
|
||||
u64 isize = i_size_read(inode);
|
||||
struct btrfs_key found_key;
|
||||
struct extent_map *em = NULL;
|
||||
struct extent_state *cached_state = NULL;
|
||||
struct btrfs_path *path;
|
||||
struct btrfs_file_extent_item *item;
|
||||
int end = 0;
|
||||
u64 em_start = 0, em_len = 0;
|
||||
u64 em_start = 0;
|
||||
u64 em_len = 0;
|
||||
u64 em_end = 0;
|
||||
unsigned long emflags;
|
||||
int hole = 0;
|
||||
|
||||
if (len == 0)
|
||||
return -EINVAL;
|
||||
@@ -2940,6 +2989,10 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
return -ENOMEM;
|
||||
path->leave_spinning = 1;
|
||||
|
||||
/*
|
||||
* lookup the last file extent. We're not using i_size here
|
||||
* because there might be preallocation past i_size
|
||||
*/
|
||||
ret = btrfs_lookup_file_extent(NULL, BTRFS_I(inode)->root,
|
||||
path, inode->i_ino, -1, 0);
|
||||
if (ret < 0) {
|
||||
@@ -2953,18 +3006,38 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]);
|
||||
found_type = btrfs_key_type(&found_key);
|
||||
|
||||
/* No extents, just return */
|
||||
/* No extents, but there might be delalloc bits */
|
||||
if (found_key.objectid != inode->i_ino ||
|
||||
found_type != BTRFS_EXTENT_DATA_KEY) {
|
||||
btrfs_free_path(path);
|
||||
return 0;
|
||||
/* have to trust i_size as the end */
|
||||
last = (u64)-1;
|
||||
last_for_get_extent = isize;
|
||||
} else {
|
||||
/*
|
||||
* remember the start of the last extent. There are a
|
||||
* bunch of different factors that go into the length of the
|
||||
* extent, so its much less complex to remember where it started
|
||||
*/
|
||||
last = found_key.offset;
|
||||
last_for_get_extent = last + 1;
|
||||
}
|
||||
last = found_key.offset;
|
||||
btrfs_free_path(path);
|
||||
|
||||
/*
|
||||
* we might have some extents allocated but more delalloc past those
|
||||
* extents. so, we trust isize unless the start of the last extent is
|
||||
* beyond isize
|
||||
*/
|
||||
if (last < isize) {
|
||||
last = (u64)-1;
|
||||
last_for_get_extent = isize;
|
||||
}
|
||||
|
||||
lock_extent_bits(&BTRFS_I(inode)->io_tree, start, start + len, 0,
|
||||
&cached_state, GFP_NOFS);
|
||||
em = get_extent(inode, NULL, 0, off, max - off, 0);
|
||||
|
||||
em = get_extent_skip_holes(inode, off, last_for_get_extent,
|
||||
get_extent);
|
||||
if (!em)
|
||||
goto out;
|
||||
if (IS_ERR(em)) {
|
||||
@@ -2973,19 +3046,14 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
}
|
||||
|
||||
while (!end) {
|
||||
hole = 0;
|
||||
off = em->start + em->len;
|
||||
off = extent_map_end(em);
|
||||
if (off >= max)
|
||||
end = 1;
|
||||
|
||||
if (em->block_start == EXTENT_MAP_HOLE) {
|
||||
hole = 1;
|
||||
goto next;
|
||||
}
|
||||
|
||||
em_start = em->start;
|
||||
em_len = em->len;
|
||||
|
||||
em_end = extent_map_end(em);
|
||||
emflags = em->flags;
|
||||
disko = 0;
|
||||
flags = 0;
|
||||
|
||||
@@ -3004,37 +3072,29 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
|
||||
if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags))
|
||||
flags |= FIEMAP_EXTENT_ENCODED;
|
||||
|
||||
next:
|
||||
emflags = em->flags;
|
||||
free_extent_map(em);
|
||||
em = NULL;
|
||||
if (!end) {
|
||||
em = get_extent(inode, NULL, 0, off, max - off, 0);
|
||||
if (!em)
|
||||
goto out;
|
||||
if (IS_ERR(em)) {
|
||||
ret = PTR_ERR(em);
|
||||
goto out;
|
||||
}
|
||||
emflags = em->flags;
|
||||
}
|
||||
|
||||
if (test_bit(EXTENT_FLAG_VACANCY, &emflags)) {
|
||||
if ((em_start >= last) || em_len == (u64)-1 ||
|
||||
(last == (u64)-1 && isize <= em_end)) {
|
||||
flags |= FIEMAP_EXTENT_LAST;
|
||||
end = 1;
|
||||
}
|
||||
|
||||
if (em_start == last) {
|
||||
/* now scan forward to see if this is really the last extent. */
|
||||
em = get_extent_skip_holes(inode, off, last_for_get_extent,
|
||||
get_extent);
|
||||
if (IS_ERR(em)) {
|
||||
ret = PTR_ERR(em);
|
||||
goto out;
|
||||
}
|
||||
if (!em) {
|
||||
flags |= FIEMAP_EXTENT_LAST;
|
||||
end = 1;
|
||||
}
|
||||
|
||||
if (!hole) {
|
||||
ret = fiemap_fill_next_extent(fieinfo, em_start, disko,
|
||||
em_len, flags);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
}
|
||||
ret = fiemap_fill_next_extent(fieinfo, em_start, disko,
|
||||
em_len, flags);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
}
|
||||
out_free:
|
||||
free_extent_map(em);
|
||||
|
Reference in New Issue
Block a user