// SPDX-License-Identifier: GPL-2.0
#include "backref.h"
#include "btrfs_inode.h"
#include "fiemap.h"
#include "file.h"
#include "file-item.h"
struct btrfs_fiemap_entry {
u64 offset;
u64 phys;
u64 len;
u32 flags;
};
/*
* Indicate the caller of emit_fiemap_extent ( ) that it needs to unlock the file
* range from the inode ' s io tree , unlock the subvolume tree search path , flush
* the fiemap cache and relock the file range and research the subvolume tree .
* The value here is something negative that can ' t be confused with a valid
* errno value and different from 1 because that ' s also a return value from
* fiemap_fill_next_extent ( ) and also it ' s often used to mean some btree search
* did not find a key , so make it some distinct negative value .
*/
#define BTRFS_FIEMAP_FLUSH_CACHE (-(MAX_ERRNO + 1 ))
/*
* Used to :
*
* - Cache the next entry to be emitted to the fiemap buffer , so that we can
* merge extents that are contiguous and can be grouped as a single one ;
*
* - Store extents ready to be written to the fiemap buffer in an intermediary
* buffer . This intermediary buffer is to ensure that in case the fiemap
* buffer is memory mapped to the fiemap target file , we don ' t deadlock
* during btrfs_page_mkwrite ( ) . This is because during fiemap we are locking
* an extent range in order to prevent races with delalloc flushing and
* ordered extent completion , which is needed in order to reliably detect
* delalloc in holes and prealloc extents . And this can lead to a deadlock
* if the fiemap buffer is memory mapped to the file we are running fiemap
* against ( a silly , useless in practice scenario , but possible ) because
* btrfs_page_mkwrite ( ) will try to lock the same extent range .
*/
struct fiemap_cache {
/* An array of ready fiemap entries. */
struct btrfs_fiemap_entry *entries;
/* Number of entries in the entries array. */
int entries_size;
/* Index of the next entry in the entries array to write to. */
int entries_pos;
/*
* Once the entries array is full , this indicates what ' s the offset for
* the next file extent item we must search for in the inode ' s subvolume
* tree after unlocking the extent range in the inode ' s io tree and
* releasing the search path .
*/
u64 next_search_offset;
/*
* This matches struct fiemap_extent_info : : fi_mapped_extents , we use it
* to count ourselves emitted extents and stop instead of relying on
* fiemap_fill_next_extent ( ) because we buffer ready fiemap entries at
* the @ entries array , and we want to stop as soon as we hit the max
* amount of extents to map , not just to save time but also to make the
* logic at extent_fiemap ( ) simpler .
*/
unsigned int extents_mapped;
/* Fields for the cached extent (unsubmitted, not ready, extent). */
u64 offset;
u64 phys;
u64 len;
u32 flags;
bool cached;
};
static int flush_fiemap_cache(struct fiemap_extent_info *fieinfo,
struct fiemap_cache *cache)
{
for (int i = 0 ; i < cache->entries_pos; i++) {
struct btrfs_fiemap_entry *entry = &cache->entries[i];
int ret;
ret = fiemap_fill_next_extent(fieinfo, entry->offset,
entry->phys, entry->len,
entry->flags);
/*
* Ignore 1 ( reached max entries ) because we keep track of that
* ourselves in emit_fiemap_extent ( ) .
*/
if (ret < 0 )
return ret;
}
cache->entries_pos = 0 ;
return 0 ;
}
/*
* Helper to submit fiemap extent .
*
* Will try to merge current fiemap extent specified by @ offset , @ phys ,
* @ len and @ flags with cached one .
* And only when we fails to merge , cached one will be submitted as
* fiemap extent .
*
* Return value is the same as fiemap_fill_next_extent ( ) .
*/
static int emit_fiemap_extent(struct fiemap_extent_info *fieinfo,
struct fiemap_cache *cache,
u64 offset, u64 phys, u64 len, u32 flags)
{
struct btrfs_fiemap_entry *entry;
u64 cache_end;
/* Set at the end of extent_fiemap(). */
ASSERT((flags & FIEMAP_EXTENT_LAST) == 0 );
if (!cache->cached)
goto assign;
/*
* When iterating the extents of the inode , at extent_fiemap ( ) , we may
* find an extent that starts at an offset behind the end offset of the
* previous extent we processed . This happens if fiemap is called
* without FIEMAP_FLAG_SYNC and there are ordered extents completing
* after we had to unlock the file range , release the search path , emit
* the fiemap extents stored in the buffer ( cache - > entries array ) and
* the lock the remainder of the range and re - search the btree .
*
* For example we are in leaf X processing its last item , which is the
* file extent item for file range [ 512 K , 1 M [ , and after
* btrfs_next_leaf ( ) releases the path , there ' s an ordered extent that
* completes for the file range [ 768 K , 2 M [ , and that results in trimming
* the file extent item so that it now corresponds to the file range
* [ 512 K , 768 K [ and a new file extent item is inserted for the file
* range [ 768 K , 2 M [ , which may end up as the last item of leaf X or as
* the first item of the next leaf - in either case btrfs_next_leaf ( )
* will leave us with a path pointing to the new extent item , for the
* file range [ 768 K , 2 M [ , since that ' s the first key that follows the
* last one we processed . So in order not to report overlapping extents
* to user space , we trim the length of the previously cached extent and
* emit it .
*
* Upon calling btrfs_next_leaf ( ) we may also find an extent with an
* offset smaller than or equals to cache - > offset , and this happens
* when we had a hole or prealloc extent with several delalloc ranges in
* it , but after btrfs_next_leaf ( ) released the path , delalloc was
* flushed and the resulting ordered extents were completed , so we can
* now have found a file extent item for an offset that is smaller than
* or equals to what we have in cache - > offset . We deal with this as
* described below .
*/
cache_end = cache->offset + cache->len;
if (cache_end > offset) {
if (offset == cache->offset) {
/*
* We cached a dealloc range ( found in the io tree ) for
* a hole or prealloc extent and we have now found a
* file extent item for the same offset . What we have
* now is more recent and up to date , so discard what
* we had in the cache and use what we have just found .
*/
goto assign;
} else if (offset > cache->offset) {
/*
* The extent range we previously found ends after the
* offset of the file extent item we found and that
* offset falls somewhere in the middle of that previous
* extent range . So adjust the range we previously found
* to end at the offset of the file extent item we have
* just found , since this extent is more up to date .
* Emit that adjusted range and cache the file extent
* item we have just found . This corresponds to the case
* where a previously found file extent item was split
* due to an ordered extent completing .
*/
cache->len = offset - cache->offset;
goto emit;
} else {
const u64 range_end = offset + len;
/*
* The offset of the file extent item we have just found
* is behind the cached offset . This means we were
* processing a hole or prealloc extent for which we
* have found delalloc ranges ( in the io tree ) , so what
* we have in the cache is the last delalloc range we
* found while the file extent item we found can be
* either for a whole delalloc range we previously
* emitted or only a part of that range .
*
* We have two cases here :
*
* 1 ) The file extent item ' s range ends at or behind the
* cached extent ' s end . In this case just ignore the
* current file extent item because we don ' t want to
* overlap with previous ranges that may have been
* emitted already ;
*
* 2 ) The file extent item starts behind the currently
* cached extent but its end offset goes beyond the
* end offset of the cached extent . We don ' t want to
* overlap with a previous range that may have been
* emitted already , so we emit the currently cached
* extent and then partially store the current file
* extent item ' s range in the cache , for the subrange
* going the cached extent ' s end to the end of the
* file extent item .
*/
if (range_end <= cache_end)
return 0 ;
if (!(flags & (FIEMAP_EXTENT_ENCODED | FIEMAP_EXTENT_DELALLOC)))
phys += cache_end - offset;
offset = cache_end;
len = range_end - cache_end;
goto emit;
}
}
/*
* Only merges fiemap extents if
* 1 ) Their logical addresses are continuous
*
* 2 ) Their physical addresses are continuous
* So truly compressed ( physical size smaller than logical size )
* extents won ' t get merged with each other
*
* 3 ) Share same flags
*/
if (cache->offset + cache->len == offset &&
cache->phys + cache->len == phys &&
cache->flags == flags) {
cache->len += len;
return 0 ;
}
emit:
/* Not mergeable, need to submit cached one */
if (cache->entries_pos == cache->entries_size) {
/*
* We will need to research for the end offset of the last
* stored extent and not from the current offset , because after
* unlocking the range and releasing the path , if there ' s a hole
* between that end offset and this current offset , a new extent
* may have been inserted due to a new write , so we don ' t want
* to miss it .
*/
entry = &cache->entries[cache->entries_size - 1 ];
cache->next_search_offset = entry->offset + entry->len;
cache->cached = false ;
return BTRFS_FIEMAP_FLUSH_CACHE;
}
entry = &cache->entries[cache->entries_pos];
entry->offset = cache->offset;
entry->phys = cache->phys;
entry->len = cache->len;
entry->flags = cache->flags;
cache->entries_pos++;
cache->extents_mapped++;
if (cache->extents_mapped == fieinfo->fi_extents_max) {
cache->cached = false ;
return 1 ;
}
assign:
cache->cached = true ;
cache->offset = offset;
cache->phys = phys;
cache->len = len;
cache->flags = flags;
return 0 ;
}
/*
* Emit last fiemap cache
*
* The last fiemap cache may still be cached in the following case :
* 0 4 k 8 k
* | < - Fiemap range - > |
* | < - - - - - - - - - - - - First extent - - - - - - - - - - - > |
*
* In this case , the first extent range will be cached but not emitted .
* So we must emit it before ending extent_fiemap ( ) .
*/
static int emit_last_fiemap_cache(struct fiemap_extent_info *fieinfo,
struct fiemap_cache *cache)
{
int ret;
if (!cache->cached)
return 0 ;
ret = fiemap_fill_next_extent(fieinfo, cache->offset, cache->phys,
cache->len, cache->flags);
cache->cached = false ;
if (ret > 0 )
ret = 0 ;
return ret;
}
static int fiemap_next_leaf_item(struct btrfs_inode *inode, struct btrfs_path *path)
{
struct extent_buffer *clone = path->nodes[0 ];
struct btrfs_key key;
int slot;
int ret;
path->slots[0 ]++;
if (path->slots[0 ] < btrfs_header_nritems(path->nodes[0 ]))
return 0 ;
/*
* Add a temporary extra ref to an already cloned extent buffer to
* prevent btrfs_next_leaf ( ) freeing it , we want to reuse it to avoid
* the cost of allocating a new one .
*/
ASSERT(test_bit(EXTENT_BUFFER_UNMAPPED, &clone->bflags));
refcount_inc(&clone->refs);
ret = btrfs_next_leaf(inode->root, path);
if (ret != 0 )
goto out;
/*
* Don ' t bother with cloning if there are no more file extent items for
* our inode .
*/
btrfs_item_key_to_cpu(path->nodes[0 ], &key, path->slots[0 ]);
if (key.objectid != btrfs_ino(inode) || key.type != BTRFS_EXTENT_DATA_KEY) {
ret = 1 ;
goto out;
}
/*
* Important to preserve the start field , for the optimizations when
* checking if extents are shared ( see extent_fiemap ( ) ) .
*
* We must set - > start before calling copy_extent_buffer_full ( ) . If we
* are on sub - pagesize blocksize , we use - > start to determine the offset
* into the folio where our eb exists , and if we update - > start after
* the fact then any subsequent reads of the eb may read from a
* different offset in the folio than where we originally copied into .
*/
clone->start = path->nodes[0 ]->start;
/* See the comment at fiemap_search_slot() about why we clone. */
copy_extent_buffer_full(clone, path->nodes[0 ]);
slot = path->slots[0 ];
btrfs_release_path(path);
path->nodes[0 ] = clone;
path->slots[0 ] = slot;
out:
if (ret)
free_extent_buffer(clone);
return ret;
}
/*
* Search for the first file extent item that starts at a given file offset or
* the one that starts immediately before that offset .
* Returns : 0 on success , < 0 on error , 1 if not found .
*/
static int fiemap_search_slot(struct btrfs_inode *inode, struct btrfs_path *path,
u64 file_offset)
{
const u64 ino = btrfs_ino(inode);
struct btrfs_root *root = inode->root;
struct extent_buffer *clone;
struct btrfs_key key;
int slot;
int ret;
key.objectid = ino;
key.type = BTRFS_EXTENT_DATA_KEY;
key.offset = file_offset;
ret = btrfs_search_slot(NULL, root, &key, path, 0 , 0 );
if (ret < 0 )
return ret;
if (ret > 0 && path->slots[0 ] > 0 ) {
btrfs_item_key_to_cpu(path->nodes[0 ], &key, path->slots[0 ] - 1 );
if (key.objectid == ino && key.type == BTRFS_EXTENT_DATA_KEY)
path->slots[0 ]--;
}
if (path->slots[0 ] >= btrfs_header_nritems(path->nodes[0 ])) {
ret = btrfs_next_leaf(root, path);
if (ret != 0 )
return ret;
btrfs_item_key_to_cpu(path->nodes[0 ], &key, path->slots[0 ]);
if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY)
return 1 ;
}
/*
* We clone the leaf and use it during fiemap . This is because while
* using the leaf we do expensive things like checking if an extent is
* shared , which can take a long time . In order to prevent blocking
* other tasks for too long , we use a clone of the leaf . We have locked
* the file range in the inode ' s io tree , so we know none of our file
* extent items can change . This way we avoid blocking other tasks that
* want to insert items for other inodes in the same leaf or b + tree
* rebalance operations ( triggered for example when someone is trying
* to push items into this leaf when trying to insert an item in a
* neighbour leaf ) .
* We also need the private clone because holding a read lock on an
* extent buffer of the subvolume ' s b + tree will make lockdep unhappy
* when we check if extents are shared , as backref walking may need to
* lock the same leaf we are processing .
*/
clone = btrfs_clone_extent_buffer(path->nodes[0 ]);
if (!clone)
return -ENOMEM;
slot = path->slots[0 ];
btrfs_release_path(path);
path->nodes[0 ] = clone;
path->slots[0 ] = slot;
return 0 ;
}
/*
* Process a range which is a hole or a prealloc extent in the inode ' s subvolume
* btree . If @ disk_bytenr is 0 , we are dealing with a hole , otherwise a prealloc
* extent . The end offset ( @ end ) is inclusive .
*/
static int fiemap_process_hole(struct btrfs_inode *inode,
struct fiemap_extent_info *fieinfo,
struct fiemap_cache *cache,
struct extent_state **delalloc_cached_state,
struct btrfs_backref_share_check_ctx *backref_ctx,
u64 disk_bytenr, u64 extent_offset,
u64 extent_gen,
u64 start, u64 end)
{
const u64 i_size = i_size_read(&inode->vfs_inode);
u64 cur_offset = start;
u64 last_delalloc_end = 0 ;
u32 prealloc_flags = FIEMAP_EXTENT_UNWRITTEN;
bool checked_extent_shared = false ;
int ret;
/*
* There can be no delalloc past i_size , so don ' t waste time looking for
* it beyond i_size .
*/
while (cur_offset < end && cur_offset < i_size) {
u64 delalloc_start;
u64 delalloc_end;
u64 prealloc_start;
u64 prealloc_len = 0 ;
bool delalloc;
delalloc = btrfs_find_delalloc_in_range(inode, cur_offset, end,
delalloc_cached_state,
&delalloc_start,
&delalloc_end);
if (!delalloc)
break ;
/*
* If this is a prealloc extent we have to report every section
* of it that has no delalloc .
*/
if (disk_bytenr != 0 ) {
if (last_delalloc_end == 0 ) {
prealloc_start = start;
prealloc_len = delalloc_start - start;
} else {
prealloc_start = last_delalloc_end + 1 ;
prealloc_len = delalloc_start - prealloc_start;
}
}
if (prealloc_len > 0 ) {
if (!checked_extent_shared && fieinfo->fi_extents_max) {
ret = btrfs_is_data_extent_shared(inode,
disk_bytenr,
extent_gen,
backref_ctx);
if (ret < 0 )
return ret;
else if (ret > 0 )
prealloc_flags |= FIEMAP_EXTENT_SHARED;
checked_extent_shared = true ;
}
ret = emit_fiemap_extent(fieinfo, cache, prealloc_start,
disk_bytenr + extent_offset,
prealloc_len, prealloc_flags);
if (ret)
return ret;
extent_offset += prealloc_len;
}
ret = emit_fiemap_extent(fieinfo, cache, delalloc_start, 0 ,
delalloc_end + 1 - delalloc_start,
FIEMAP_EXTENT_DELALLOC |
FIEMAP_EXTENT_UNKNOWN);
if (ret)
return ret;
last_delalloc_end = delalloc_end;
cur_offset = delalloc_end + 1 ;
extent_offset += cur_offset - delalloc_start;
cond_resched();
}
/*
* Either we found no delalloc for the whole prealloc extent or we have
* a prealloc extent that spans i_size or starts at or after i_size .
*/
if (disk_bytenr != 0 && last_delalloc_end < end) {
u64 prealloc_start;
u64 prealloc_len;
if (last_delalloc_end == 0 ) {
prealloc_start = start;
prealloc_len = end + 1 - start;
} else {
prealloc_start = last_delalloc_end + 1 ;
prealloc_len = end + 1 - prealloc_start;
}
if (!checked_extent_shared && fieinfo->fi_extents_max) {
ret = btrfs_is_data_extent_shared(inode,
disk_bytenr,
extent_gen,
backref_ctx);
if (ret < 0 )
return ret;
else if (ret > 0 )
prealloc_flags |= FIEMAP_EXTENT_SHARED;
}
ret = emit_fiemap_extent(fieinfo, cache, prealloc_start,
disk_bytenr + extent_offset,
prealloc_len, prealloc_flags);
if (ret)
return ret;
}
return 0 ;
}
static int fiemap_find_last_extent_offset(struct btrfs_inode *inode,
struct btrfs_path *path,
u64 *last_extent_end_ret)
{
const u64 ino = btrfs_ino(inode);
struct btrfs_root *root = inode->root;
struct extent_buffer *leaf;
struct btrfs_file_extent_item *ei;
struct btrfs_key key;
u64 disk_bytenr;
int ret;
/*
* 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, root, path, ino, (u64)-1 , 0 );
/* There can't be a file extent item at offset (u64)-1 */
ASSERT(ret != 0 );
if (ret < 0 )
return ret;
/*
* For a non - existing key , btrfs_search_slot ( ) always leaves us at a
* slot > 0 , except if the btree is empty , which is impossible because
* at least it has the inode item for this inode and all the items for
* the root inode 256 .
*/
ASSERT(path->slots[0 ] > 0 );
path->slots[0 ]--;
leaf = path->nodes[0 ];
btrfs_item_key_to_cpu(leaf, &key, path->slots[0 ]);
if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY) {
/* No file extent items in the subvolume tree. */
*last_extent_end_ret = 0 ;
return 0 ;
}
/*
* For an inline extent , the disk_bytenr is where inline data starts at ,
* so first check if we have an inline extent item before checking if we
* have an implicit hole ( disk_bytenr = = 0 ) .
*/
ei = btrfs_item_ptr(leaf, path->slots[0 ], struct btrfs_file_extent_item);
if (btrfs_file_extent_type(leaf, ei) == BTRFS_FILE_EXTENT_INLINE) {
*last_extent_end_ret = btrfs_file_extent_end(path);
return 0 ;
}
/*
* Find the last file extent item that is not a hole ( when NO_HOLES is
* not enabled ) . This should take at most 2 iterations in the worst
* case : we have one hole file extent item at slot 0 of a leaf and
* another hole file extent item as the last item in the previous leaf .
* This is because we merge file extent items that represent holes .
*/
disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, ei);
while (disk_bytenr == 0 ) {
ret = btrfs_previous_item(root, path, ino, BTRFS_EXTENT_DATA_KEY);
if (ret < 0 ) {
return ret;
} else if (ret > 0 ) {
/* No file extent items that are not holes. */
*last_extent_end_ret = 0 ;
return 0 ;
}
leaf = path->nodes[0 ];
ei = btrfs_item_ptr(leaf, path->slots[0 ],
struct btrfs_file_extent_item);
disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, ei);
}
*last_extent_end_ret = btrfs_file_extent_end(path);
return 0 ;
}
static int extent_fiemap(struct btrfs_inode *inode,
struct fiemap_extent_info *fieinfo,
u64 start, u64 len)
{
const u64 ino = btrfs_ino(inode);
struct extent_state *cached_state = NULL;
struct extent_state *delalloc_cached_state = NULL;
BTRFS_PATH_AUTO_FREE(path);
struct fiemap_cache cache = { 0 };
struct btrfs_backref_share_check_ctx *backref_ctx;
u64 last_extent_end = 0 ;
u64 prev_extent_end;
u64 range_start;
u64 range_end;
const u64 sectorsize = inode->root->fs_info->sectorsize;
bool stopped = false ;
int ret;
cache.entries_size = PAGE_SIZE / sizeof (struct btrfs_fiemap_entry);
cache.entries = kmalloc_array(cache.entries_size,
sizeof (struct btrfs_fiemap_entry),
GFP_KERNEL);
backref_ctx = btrfs_alloc_backref_share_check_ctx();
path = btrfs_alloc_path();
if (!cache.entries || !backref_ctx || !path) {
ret = -ENOMEM;
goto out;
}
restart:
range_start = round_down(start, sectorsize);
range_end = round_up(start + len, sectorsize);
prev_extent_end = range_start;
btrfs_lock_extent(&inode->io_tree, range_start, range_end, &cached_state);
ret = fiemap_find_last_extent_offset(inode, path, &last_extent_end);
if (ret < 0 )
goto out_unlock;
btrfs_release_path(path);
path->reada = READA_FORWARD;
ret = fiemap_search_slot(inode, path, range_start);
if (ret < 0 ) {
goto out_unlock;
} else if (ret > 0 ) {
/*
* No file extent item found , but we may have delalloc between
* the current offset and i_size . So check for that .
*/
ret = 0 ;
goto check_eof_delalloc;
}
while (prev_extent_end < range_end) {
struct extent_buffer *leaf = path->nodes[0 ];
struct btrfs_file_extent_item *ei;
struct btrfs_key key;
u64 extent_end;
u64 extent_len;
u64 extent_offset = 0 ;
u64 extent_gen;
u64 disk_bytenr = 0 ;
u64 flags = 0 ;
int extent_type;
u8 compression;
btrfs_item_key_to_cpu(leaf, &key, path->slots[0 ]);
if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY)
break ;
extent_end = btrfs_file_extent_end(path);
/*
* The first iteration can leave us at an extent item that ends
* before our range ' s start . Move to the next item .
*/
if (extent_end <= range_start)
goto next_item;
backref_ctx->curr_leaf_bytenr = leaf->start;
/* We have in implicit hole (NO_HOLES feature enabled). */
if (prev_extent_end < key.offset) {
const u64 hole_end = min(key.offset, range_end) - 1 ;
ret = fiemap_process_hole(inode, fieinfo, &cache,
&delalloc_cached_state,
backref_ctx, 0 , 0 , 0 ,
prev_extent_end, hole_end);
if (ret < 0 ) {
goto out_unlock;
} else if (ret > 0 ) {
/* fiemap_fill_next_extent() told us to stop. */
stopped = true ;
break ;
}
/* We've reached the end of the fiemap range, stop. */
if (key.offset >= range_end) {
stopped = true ;
break ;
}
}
extent_len = extent_end - key.offset;
ei = btrfs_item_ptr(leaf, path->slots[0 ],
struct btrfs_file_extent_item);
compression = btrfs_file_extent_compression(leaf, ei);
extent_type = btrfs_file_extent_type(leaf, ei);
extent_gen = btrfs_file_extent_generation(leaf, ei);
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, ei);
if (compression == BTRFS_COMPRESS_NONE)
extent_offset = btrfs_file_extent_offset(leaf, ei);
}
if (compression != BTRFS_COMPRESS_NONE)
flags |= FIEMAP_EXTENT_ENCODED;
if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
flags |= FIEMAP_EXTENT_DATA_INLINE;
flags |= FIEMAP_EXTENT_NOT_ALIGNED;
ret = emit_fiemap_extent(fieinfo, &cache, key.offset, 0 ,
extent_len, flags);
} else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
ret = fiemap_process_hole(inode, fieinfo, &cache,
&delalloc_cached_state,
backref_ctx,
disk_bytenr, extent_offset,
extent_gen, key.offset,
extent_end - 1 );
} else if (disk_bytenr == 0 ) {
/* We have an explicit hole. */
ret = fiemap_process_hole(inode, fieinfo, &cache,
&delalloc_cached_state,
backref_ctx, 0 , 0 , 0 ,
key.offset, extent_end - 1 );
} else {
/* We have a regular extent. */
if (fieinfo->fi_extents_max) {
ret = btrfs_is_data_extent_shared(inode,
disk_bytenr,
extent_gen,
backref_ctx);
if (ret < 0 )
goto out_unlock;
else if (ret > 0 )
flags |= FIEMAP_EXTENT_SHARED;
}
ret = emit_fiemap_extent(fieinfo, &cache, key.offset,
disk_bytenr + extent_offset,
extent_len, flags);
}
if (ret < 0 ) {
goto out_unlock;
} else if (ret > 0 ) {
/* emit_fiemap_extent() told us to stop. */
stopped = true ;
break ;
}
prev_extent_end = extent_end;
next_item:
if (fatal_signal_pending(current)) {
ret = -EINTR;
goto out_unlock;
}
ret = fiemap_next_leaf_item(inode, path);
if (ret < 0 ) {
goto out_unlock;
} else if (ret > 0 ) {
/* No more file extent items for this inode. */
break ;
}
cond_resched();
}
check_eof_delalloc:
if (!stopped && prev_extent_end < range_end) {
ret = fiemap_process_hole(inode, fieinfo, &cache,
&delalloc_cached_state, backref_ctx,
0 , 0 , 0 , prev_extent_end, range_end - 1 );
if (ret < 0 )
goto out_unlock;
prev_extent_end = range_end;
}
if (cache.cached && cache.offset + cache.len >= last_extent_end) {
const u64 i_size = i_size_read(&inode->vfs_inode);
if (prev_extent_end < i_size) {
u64 delalloc_start;
u64 delalloc_end;
bool delalloc;
delalloc = btrfs_find_delalloc_in_range(inode,
prev_extent_end,
i_size - 1 ,
&delalloc_cached_state,
&delalloc_start,
&delalloc_end);
if (!delalloc)
cache.flags |= FIEMAP_EXTENT_LAST;
} else {
cache.flags |= FIEMAP_EXTENT_LAST;
}
}
out_unlock:
btrfs_unlock_extent(&inode->io_tree, range_start, range_end, &cached_state);
if (ret == BTRFS_FIEMAP_FLUSH_CACHE) {
btrfs_release_path(path);
ret = flush_fiemap_cache(fieinfo, &cache);
if (ret)
goto out;
len -= cache.next_search_offset - start;
start = cache.next_search_offset;
goto restart;
} else if (ret < 0 ) {
goto out;
}
/*
* Must free the path before emitting to the fiemap buffer because we
* may have a non - cloned leaf and if the fiemap buffer is memory mapped
* to a file , a write into it ( through btrfs_page_mkwrite ( ) ) may trigger
* waiting for an ordered extent that in order to complete needs to
* modify that leaf , therefore leading to a deadlock .
*/
btrfs_free_path(path);
path = NULL;
ret = flush_fiemap_cache(fieinfo, &cache);
if (ret)
goto out;
ret = emit_last_fiemap_cache(fieinfo, &cache);
out:
btrfs_free_extent_state(delalloc_cached_state);
kfree(cache.entries);
btrfs_free_backref_share_ctx(backref_ctx);
return ret;
}
int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 len)
{
struct btrfs_inode *btrfs_inode = BTRFS_I(inode);
int ret;
ret = fiemap_prep(inode, fieinfo, start, &len, 0 );
if (ret)
return ret;
/*
* fiemap_prep ( ) called filemap_write_and_wait ( ) for the whole possible
* file range ( 0 to LLONG_MAX ) , but that is not enough if we have
* compression enabled . The first filemap_fdatawrite_range ( ) only kicks
* in the compression of data ( in an async thread ) and will return
* before the compression is done and writeback is started . A second
* filemap_fdatawrite_range ( ) is needed to wait for the compression to
* complete and writeback to start . We also need to wait for ordered
* extents to complete , because our fiemap implementation uses mainly
* file extent items to list the extents , searching for extent maps
* only for file ranges with holes or prealloc extents to figure out
* if we have delalloc in those ranges .
*/
if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC) {
ret = btrfs_wait_ordered_range(btrfs_inode, 0 , LLONG_MAX);
if (ret)
return ret;
}
btrfs_inode_lock(btrfs_inode, BTRFS_ILOCK_SHARED);
/*
* We did an initial flush to avoid holding the inode ' s lock while
* triggering writeback and waiting for the completion of IO and ordered
* extents . Now after we locked the inode we do it again , because it ' s
* possible a new write may have happened in between those two steps .
*/
if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC) {
ret = btrfs_wait_ordered_range(btrfs_inode, 0 , LLONG_MAX);
if (ret) {
btrfs_inode_unlock(btrfs_inode, BTRFS_ILOCK_SHARED);
return ret;
}
}
ret = extent_fiemap(btrfs_inode, fieinfo, start, len);
btrfs_inode_unlock(btrfs_inode, BTRFS_ILOCK_SHARED);
return ret;
}
Messung V0.5 in Prozent C=95 H=82 G=88
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland