# SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2019 Nikolay Borisov, SUSE LLC. All Rights Reserved. # # Parses btrfs' extent tree for holes. Holes are the ranges between 2 adjacent # extent blocks. For example if we have the following 2 metadata items in the # extent tree: # item 6 key (30425088 METADATA_ITEM 0) itemoff 16019 itemsize 33 # item 7 key (30490624 METADATA_ITEM 0) itemoff 15986 itemsize 33 # # There is a whole of 64k between then - 30490624−30425088 = 65536 # Same logic applies for adjacent EXTENT_ITEMS. # # The script requires the following parameters passed on command line: # * sectorsize - how many bytes per sector, used to convert the output of # the script to sectors. # * nodesize - size of metadata extents, used for internal calculations # Given an extent line "item 2 key (13672448 EXTENT_ITEM 65536) itemoff 16153 itemsize 53" # or "item 6 key (30425088 METADATA_ITEM 0) itemoff 16019 itemsize 33" returns # either 65536 (for data extents) or the fixes nodesize value for metadata # extents. function get_extent_size(line, tmp) { if (line ~ data_match || line ~ bg_match) { split(line, tmp) gsub(/\)/,"", tmp[6]) return tmp[6] } else if (line ~ metadata_match) { return nodesize } } # given a 'item 2 key (13672448 EXTENT_ITEM 65536) itemoff 16153 itemsize 53' # and returns 13672448. function get_extent_offset(line, tmp) { split(line, tmp) gsub(/\(/,"",tmp[4]) return tmp[4] } # This function parses all the extents belonging to a particular block group # which are accumulated in lines[] and calculates the offsets of the holes # part of this block group. # # base_offset and bg_line are local variables function print_array(base_offset, bg_line) { if (match(lines[0], bg_match)) { # we don't have an extent at the beginning of of blockgroup, so we # have a hole between blockgroup offset and first extent offset bg_line = lines[0] prev_size=0 prev_offset=get_extent_offset(bg_line) delete lines[0] } else { # we have an extent at the beginning of block group, so initialize # the prev_* vars correctly bg_line = lines[1] prev_size = get_extent_size(lines[0]) prev_offset = get_extent_offset(lines[0]) delete lines[1] delete lines[0] } bg_offset=get_extent_offset(bg_line) bgend=bg_offset + get_extent_size(bg_line) for (i in lines) { cur_size = get_extent_size(lines[i]) cur_offset = get_extent_offset(lines[i]) if (cur_offset != prev_offset + prev_size) print int((prev_size + prev_offset) / sectorsize), int((cur_offset-1) / sectorsize) prev_size = cur_size prev_offset = cur_offset } print int((prev_size + prev_offset) / sectorsize), int((bgend-1) / sectorsize) total_printed++ delete lines } BEGIN { loi_match="^.item [0-9]* key \\([0-9]* (BLOCK_GROUP_ITEM|METADATA_ITEM|EXTENT_ITEM) [0-9]*\\).*" metadata_match="^.item [0-9]* key \\([0-9]* METADATA_ITEM [0-9]*\\).*" data_match="^.item [0-9]* key \\([0-9]* EXTENT_ITEM [0-9]*\\).*" bg_match="^.item [0-9]* key \\([0-9]* BLOCK_GROUP_ITEM [0-9]*\\).*" node_match="^node.*$" leaf_match="^leaf [0-9]* flags" line_count=0 total_printed=0 skip_lines=0 } { # skip lines not belonging to a leaf if (match($0, node_match)) { skip_lines=1 } else if (match($0, leaf_match)) { skip_lines=0 } if (!match($0, loi_match) || skip_lines == 1) next; # we have a line of interest, we need to parse it. First check if there is # anything in the array if (line_count==0) { lines[line_count++]=$0; } else { prev_line=lines[line_count-1] split(prev_line, prev_line_fields) prev_objectid=prev_line_fields[4] objectid=$4 if (objectid == prev_objectid && match($0, bg_match)) { if (total_printed>0) { # We are adding a BG after we have added its first extent # previously, consider this a record ending event and just print # the array delete lines[line_count-1] print_array() # we now start a new array with current and previous lines line_count=0 lines[line_count++]=prev_line lines[line_count++]=$0 } else { # first 2 added lines are EXTENT and BG that match, in this case # just add them lines[line_count++]=$0 } } else if (match($0, bg_match)) { # ordinary end of record print_array() line_count=0 lines[line_count++]=$0 } else { lines[line_count++]=$0 } } } END { print_array() }