720 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Copyright (C) 2017 Oracle. All Rights Reserved.4 *5 * Author: Darrick J. Wong <darrick.wong@oracle.com>6 */7#include "ext4.h"8#include <linux/fsmap.h>9#include "fsmap.h"10#include "mballoc.h"11#include <linux/sort.h>12#include <linux/list_sort.h>13#include <trace/events/ext4.h>14 15/* Convert an ext4_fsmap to an fsmap. */16void ext4_fsmap_from_internal(struct super_block *sb, struct fsmap *dest,17 struct ext4_fsmap *src)18{19 dest->fmr_device = src->fmr_device;20 dest->fmr_flags = src->fmr_flags;21 dest->fmr_physical = src->fmr_physical << sb->s_blocksize_bits;22 dest->fmr_owner = src->fmr_owner;23 dest->fmr_offset = 0;24 dest->fmr_length = src->fmr_length << sb->s_blocksize_bits;25 dest->fmr_reserved[0] = 0;26 dest->fmr_reserved[1] = 0;27 dest->fmr_reserved[2] = 0;28}29 30/* Convert an fsmap to an ext4_fsmap. */31void ext4_fsmap_to_internal(struct super_block *sb, struct ext4_fsmap *dest,32 struct fsmap *src)33{34 dest->fmr_device = src->fmr_device;35 dest->fmr_flags = src->fmr_flags;36 dest->fmr_physical = src->fmr_physical >> sb->s_blocksize_bits;37 dest->fmr_owner = src->fmr_owner;38 dest->fmr_length = src->fmr_length >> sb->s_blocksize_bits;39}40 41/* getfsmap query state */42struct ext4_getfsmap_info {43 struct ext4_fsmap_head *gfi_head;44 ext4_fsmap_format_t gfi_formatter; /* formatting fn */45 void *gfi_format_arg;/* format buffer */46 ext4_fsblk_t gfi_next_fsblk; /* next fsblock we expect */47 u32 gfi_dev; /* device id */48 ext4_group_t gfi_agno; /* bg number, if applicable */49 struct ext4_fsmap gfi_low; /* low rmap key */50 struct ext4_fsmap gfi_high; /* high rmap key */51 struct ext4_fsmap gfi_lastfree; /* free ext at end of last bg */52 struct list_head gfi_meta_list; /* fixed metadata list */53 bool gfi_last; /* last extent? */54};55 56/* Associate a device with a getfsmap handler. */57struct ext4_getfsmap_dev {58 int (*gfd_fn)(struct super_block *sb,59 struct ext4_fsmap *keys,60 struct ext4_getfsmap_info *info);61 u32 gfd_dev;62};63 64/* Compare two getfsmap device handlers. */65static int ext4_getfsmap_dev_compare(const void *p1, const void *p2)66{67 const struct ext4_getfsmap_dev *d1 = p1;68 const struct ext4_getfsmap_dev *d2 = p2;69 70 return d1->gfd_dev - d2->gfd_dev;71}72 73/* Compare a record against our starting point */74static bool ext4_getfsmap_rec_before_low_key(struct ext4_getfsmap_info *info,75 struct ext4_fsmap *rec)76{77 return rec->fmr_physical < info->gfi_low.fmr_physical;78}79 80/*81 * Format a reverse mapping for getfsmap, having translated rm_startblock82 * into the appropriate daddr units.83 */84static int ext4_getfsmap_helper(struct super_block *sb,85 struct ext4_getfsmap_info *info,86 struct ext4_fsmap *rec)87{88 struct ext4_fsmap fmr;89 struct ext4_sb_info *sbi = EXT4_SB(sb);90 ext4_fsblk_t rec_fsblk = rec->fmr_physical;91 ext4_group_t agno;92 ext4_grpblk_t cno;93 int error;94 95 if (fatal_signal_pending(current))96 return -EINTR;97 98 /*99 * Filter out records that start before our startpoint, if the100 * caller requested that.101 */102 if (ext4_getfsmap_rec_before_low_key(info, rec)) {103 rec_fsblk += rec->fmr_length;104 if (info->gfi_next_fsblk < rec_fsblk)105 info->gfi_next_fsblk = rec_fsblk;106 return EXT4_QUERY_RANGE_CONTINUE;107 }108 109 /* Are we just counting mappings? */110 if (info->gfi_head->fmh_count == 0) {111 if (info->gfi_head->fmh_entries == UINT_MAX)112 return EXT4_QUERY_RANGE_ABORT;113 114 if (rec_fsblk > info->gfi_next_fsblk)115 info->gfi_head->fmh_entries++;116 117 if (info->gfi_last)118 return EXT4_QUERY_RANGE_CONTINUE;119 120 info->gfi_head->fmh_entries++;121 122 rec_fsblk += rec->fmr_length;123 if (info->gfi_next_fsblk < rec_fsblk)124 info->gfi_next_fsblk = rec_fsblk;125 return EXT4_QUERY_RANGE_CONTINUE;126 }127 128 /*129 * If the record starts past the last physical block we saw,130 * then we've found a gap. Report the gap as being owned by131 * whatever the caller specified is the missing owner.132 */133 if (rec_fsblk > info->gfi_next_fsblk) {134 if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count)135 return EXT4_QUERY_RANGE_ABORT;136 137 ext4_get_group_no_and_offset(sb, info->gfi_next_fsblk,138 &agno, &cno);139 trace_ext4_fsmap_mapping(sb, info->gfi_dev, agno,140 EXT4_C2B(sbi, cno),141 rec_fsblk - info->gfi_next_fsblk,142 EXT4_FMR_OWN_UNKNOWN);143 144 fmr.fmr_device = info->gfi_dev;145 fmr.fmr_physical = info->gfi_next_fsblk;146 fmr.fmr_owner = EXT4_FMR_OWN_UNKNOWN;147 fmr.fmr_length = rec_fsblk - info->gfi_next_fsblk;148 fmr.fmr_flags = FMR_OF_SPECIAL_OWNER;149 error = info->gfi_formatter(&fmr, info->gfi_format_arg);150 if (error)151 return error;152 info->gfi_head->fmh_entries++;153 }154 155 if (info->gfi_last)156 goto out;157 158 /* Fill out the extent we found */159 if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count)160 return EXT4_QUERY_RANGE_ABORT;161 162 ext4_get_group_no_and_offset(sb, rec_fsblk, &agno, &cno);163 trace_ext4_fsmap_mapping(sb, info->gfi_dev, agno, EXT4_C2B(sbi, cno),164 rec->fmr_length, rec->fmr_owner);165 166 fmr.fmr_device = info->gfi_dev;167 fmr.fmr_physical = rec_fsblk;168 fmr.fmr_owner = rec->fmr_owner;169 fmr.fmr_flags = FMR_OF_SPECIAL_OWNER;170 fmr.fmr_length = rec->fmr_length;171 error = info->gfi_formatter(&fmr, info->gfi_format_arg);172 if (error)173 return error;174 info->gfi_head->fmh_entries++;175 176out:177 rec_fsblk += rec->fmr_length;178 if (info->gfi_next_fsblk < rec_fsblk)179 info->gfi_next_fsblk = rec_fsblk;180 return EXT4_QUERY_RANGE_CONTINUE;181}182 183static inline ext4_fsblk_t ext4_fsmap_next_pblk(struct ext4_fsmap *fmr)184{185 return fmr->fmr_physical + fmr->fmr_length;186}187 188/* Transform a blockgroup's free record into a fsmap */189static int ext4_getfsmap_datadev_helper(struct super_block *sb,190 ext4_group_t agno, ext4_grpblk_t start,191 ext4_grpblk_t len, void *priv)192{193 struct ext4_fsmap irec;194 struct ext4_getfsmap_info *info = priv;195 struct ext4_fsmap *p;196 struct ext4_fsmap *tmp;197 struct ext4_sb_info *sbi = EXT4_SB(sb);198 ext4_fsblk_t fsb;199 ext4_fsblk_t fslen;200 int error;201 202 fsb = (EXT4_C2B(sbi, start) + ext4_group_first_block_no(sb, agno));203 fslen = EXT4_C2B(sbi, len);204 205 /* If the retained free extent record is set... */206 if (info->gfi_lastfree.fmr_owner) {207 /* ...and abuts this one, lengthen it and return. */208 if (ext4_fsmap_next_pblk(&info->gfi_lastfree) == fsb) {209 info->gfi_lastfree.fmr_length += fslen;210 return 0;211 }212 213 /*214 * There's a gap between the two free extents; emit the215 * retained extent prior to merging the meta_list.216 */217 error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree);218 if (error)219 return error;220 info->gfi_lastfree.fmr_owner = 0;221 }222 223 /* Merge in any relevant extents from the meta_list */224 list_for_each_entry_safe(p, tmp, &info->gfi_meta_list, fmr_list) {225 if (p->fmr_physical + p->fmr_length <= info->gfi_next_fsblk) {226 list_del(&p->fmr_list);227 kfree(p);228 } else if (p->fmr_physical < fsb) {229 error = ext4_getfsmap_helper(sb, info, p);230 if (error)231 return error;232 233 list_del(&p->fmr_list);234 kfree(p);235 }236 }237 238 irec.fmr_device = 0;239 irec.fmr_physical = fsb;240 irec.fmr_length = fslen;241 irec.fmr_owner = EXT4_FMR_OWN_FREE;242 irec.fmr_flags = 0;243 244 /* If this is a free extent at the end of a bg, buffer it. */245 if (ext4_fsmap_next_pblk(&irec) ==246 ext4_group_first_block_no(sb, agno + 1)) {247 info->gfi_lastfree = irec;248 return 0;249 }250 251 /* Otherwise, emit it */252 return ext4_getfsmap_helper(sb, info, &irec);253}254 255/* Execute a getfsmap query against the log device. */256static int ext4_getfsmap_logdev(struct super_block *sb, struct ext4_fsmap *keys,257 struct ext4_getfsmap_info *info)258{259 journal_t *journal = EXT4_SB(sb)->s_journal;260 struct ext4_fsmap irec;261 262 /* Set up search keys */263 info->gfi_low = keys[0];264 info->gfi_low.fmr_length = 0;265 266 memset(&info->gfi_high, 0xFF, sizeof(info->gfi_high));267 268 trace_ext4_fsmap_low_key(sb, info->gfi_dev, 0,269 info->gfi_low.fmr_physical,270 info->gfi_low.fmr_length,271 info->gfi_low.fmr_owner);272 273 trace_ext4_fsmap_high_key(sb, info->gfi_dev, 0,274 info->gfi_high.fmr_physical,275 info->gfi_high.fmr_length,276 info->gfi_high.fmr_owner);277 278 if (keys[0].fmr_physical > 0)279 return 0;280 281 /* Fabricate an rmap entry for the external log device. */282 irec.fmr_physical = journal->j_blk_offset;283 irec.fmr_length = journal->j_total_len;284 irec.fmr_owner = EXT4_FMR_OWN_LOG;285 irec.fmr_flags = 0;286 287 return ext4_getfsmap_helper(sb, info, &irec);288}289 290/* Helper to fill out an ext4_fsmap. */291static inline int ext4_getfsmap_fill(struct list_head *meta_list,292 ext4_fsblk_t fsb, ext4_fsblk_t len,293 uint64_t owner)294{295 struct ext4_fsmap *fsm;296 297 fsm = kmalloc(sizeof(*fsm), GFP_NOFS);298 if (!fsm)299 return -ENOMEM;300 fsm->fmr_device = 0;301 fsm->fmr_flags = 0;302 fsm->fmr_physical = fsb;303 fsm->fmr_owner = owner;304 fsm->fmr_length = len;305 list_add_tail(&fsm->fmr_list, meta_list);306 307 return 0;308}309 310/*311 * This function returns the number of file system metadata blocks at312 * the beginning of a block group, including the reserved gdt blocks.313 */314static unsigned int ext4_getfsmap_find_sb(struct super_block *sb,315 ext4_group_t agno,316 struct list_head *meta_list)317{318 struct ext4_sb_info *sbi = EXT4_SB(sb);319 ext4_fsblk_t fsb = ext4_group_first_block_no(sb, agno);320 ext4_fsblk_t len;321 unsigned long first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg);322 unsigned long metagroup = agno / EXT4_DESC_PER_BLOCK(sb);323 int error;324 325 /* Record the superblock. */326 if (ext4_bg_has_super(sb, agno)) {327 error = ext4_getfsmap_fill(meta_list, fsb, 1, EXT4_FMR_OWN_FS);328 if (error)329 return error;330 fsb++;331 }332 333 /* Record the group descriptors. */334 len = ext4_bg_num_gdb(sb, agno);335 if (!len)336 return 0;337 error = ext4_getfsmap_fill(meta_list, fsb, len,338 EXT4_FMR_OWN_GDT);339 if (error)340 return error;341 fsb += len;342 343 /* Reserved GDT blocks */344 if (!ext4_has_feature_meta_bg(sb) || metagroup < first_meta_bg) {345 len = le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks);346 error = ext4_getfsmap_fill(meta_list, fsb, len,347 EXT4_FMR_OWN_RESV_GDT);348 if (error)349 return error;350 }351 352 return 0;353}354 355/* Compare two fsmap items. */356static int ext4_getfsmap_compare(void *priv,357 const struct list_head *a,358 const struct list_head *b)359{360 struct ext4_fsmap *fa;361 struct ext4_fsmap *fb;362 363 fa = container_of(a, struct ext4_fsmap, fmr_list);364 fb = container_of(b, struct ext4_fsmap, fmr_list);365 if (fa->fmr_physical < fb->fmr_physical)366 return -1;367 else if (fa->fmr_physical > fb->fmr_physical)368 return 1;369 return 0;370}371 372/* Merge adjacent extents of fixed metadata. */373static void ext4_getfsmap_merge_fixed_metadata(struct list_head *meta_list)374{375 struct ext4_fsmap *p;376 struct ext4_fsmap *prev = NULL;377 struct ext4_fsmap *tmp;378 379 list_for_each_entry_safe(p, tmp, meta_list, fmr_list) {380 if (!prev) {381 prev = p;382 continue;383 }384 385 if (prev->fmr_owner == p->fmr_owner &&386 prev->fmr_physical + prev->fmr_length == p->fmr_physical) {387 prev->fmr_length += p->fmr_length;388 list_del(&p->fmr_list);389 kfree(p);390 } else391 prev = p;392 }393}394 395/* Free a list of fixed metadata. */396static void ext4_getfsmap_free_fixed_metadata(struct list_head *meta_list)397{398 struct ext4_fsmap *p;399 struct ext4_fsmap *tmp;400 401 list_for_each_entry_safe(p, tmp, meta_list, fmr_list) {402 list_del(&p->fmr_list);403 kfree(p);404 }405}406 407/* Find all the fixed metadata in the filesystem. */408static int ext4_getfsmap_find_fixed_metadata(struct super_block *sb,409 struct list_head *meta_list)410{411 struct ext4_group_desc *gdp;412 ext4_group_t agno;413 int error;414 415 INIT_LIST_HEAD(meta_list);416 417 /* Collect everything. */418 for (agno = 0; agno < EXT4_SB(sb)->s_groups_count; agno++) {419 gdp = ext4_get_group_desc(sb, agno, NULL);420 if (!gdp) {421 error = -EFSCORRUPTED;422 goto err;423 }424 425 /* Superblock & GDT */426 error = ext4_getfsmap_find_sb(sb, agno, meta_list);427 if (error)428 goto err;429 430 /* Block bitmap */431 error = ext4_getfsmap_fill(meta_list,432 ext4_block_bitmap(sb, gdp), 1,433 EXT4_FMR_OWN_BLKBM);434 if (error)435 goto err;436 437 /* Inode bitmap */438 error = ext4_getfsmap_fill(meta_list,439 ext4_inode_bitmap(sb, gdp), 1,440 EXT4_FMR_OWN_INOBM);441 if (error)442 goto err;443 444 /* Inodes */445 error = ext4_getfsmap_fill(meta_list,446 ext4_inode_table(sb, gdp),447 EXT4_SB(sb)->s_itb_per_group,448 EXT4_FMR_OWN_INODES);449 if (error)450 goto err;451 }452 453 /* Sort the list */454 list_sort(NULL, meta_list, ext4_getfsmap_compare);455 456 /* Merge adjacent extents */457 ext4_getfsmap_merge_fixed_metadata(meta_list);458 459 return 0;460err:461 ext4_getfsmap_free_fixed_metadata(meta_list);462 return error;463}464 465/* Execute a getfsmap query against the buddy bitmaps */466static int ext4_getfsmap_datadev(struct super_block *sb,467 struct ext4_fsmap *keys,468 struct ext4_getfsmap_info *info)469{470 struct ext4_sb_info *sbi = EXT4_SB(sb);471 ext4_fsblk_t start_fsb;472 ext4_fsblk_t end_fsb;473 ext4_fsblk_t bofs;474 ext4_fsblk_t eofs;475 ext4_group_t start_ag;476 ext4_group_t end_ag;477 ext4_grpblk_t first_cluster;478 ext4_grpblk_t last_cluster;479 int error = 0;480 481 bofs = le32_to_cpu(sbi->s_es->s_first_data_block);482 eofs = ext4_blocks_count(sbi->s_es);483 if (keys[0].fmr_physical >= eofs)484 return 0;485 else if (keys[0].fmr_physical < bofs)486 keys[0].fmr_physical = bofs;487 if (keys[1].fmr_physical >= eofs)488 keys[1].fmr_physical = eofs - 1;489 if (keys[1].fmr_physical < keys[0].fmr_physical)490 return 0;491 start_fsb = keys[0].fmr_physical;492 end_fsb = keys[1].fmr_physical;493 494 /* Determine first and last group to examine based on start and end */495 ext4_get_group_no_and_offset(sb, start_fsb, &start_ag, &first_cluster);496 ext4_get_group_no_and_offset(sb, end_fsb, &end_ag, &last_cluster);497 498 /*499 * Convert the fsmap low/high keys to bg based keys. Initialize500 * low to the fsmap low key and max out the high key to the end501 * of the bg.502 */503 info->gfi_low = keys[0];504 info->gfi_low.fmr_physical = EXT4_C2B(sbi, first_cluster);505 info->gfi_low.fmr_length = 0;506 507 memset(&info->gfi_high, 0xFF, sizeof(info->gfi_high));508 509 /* Assemble a list of all the fixed-location metadata. */510 error = ext4_getfsmap_find_fixed_metadata(sb, &info->gfi_meta_list);511 if (error)512 goto err;513 514 /* Query each bg */515 for (info->gfi_agno = start_ag;516 info->gfi_agno <= end_ag;517 info->gfi_agno++) {518 /*519 * Set the bg high key from the fsmap high key if this520 * is the last bg that we're querying.521 */522 if (info->gfi_agno == end_ag) {523 info->gfi_high = keys[1];524 info->gfi_high.fmr_physical = EXT4_C2B(sbi,525 last_cluster);526 info->gfi_high.fmr_length = 0;527 }528 529 trace_ext4_fsmap_low_key(sb, info->gfi_dev, info->gfi_agno,530 info->gfi_low.fmr_physical,531 info->gfi_low.fmr_length,532 info->gfi_low.fmr_owner);533 534 trace_ext4_fsmap_high_key(sb, info->gfi_dev, info->gfi_agno,535 info->gfi_high.fmr_physical,536 info->gfi_high.fmr_length,537 info->gfi_high.fmr_owner);538 539 error = ext4_mballoc_query_range(sb, info->gfi_agno,540 EXT4_B2C(sbi, info->gfi_low.fmr_physical),541 EXT4_B2C(sbi, info->gfi_high.fmr_physical),542 ext4_getfsmap_datadev_helper, info);543 if (error)544 goto err;545 546 /*547 * Set the bg low key to the start of the bg prior to548 * moving on to the next bg.549 */550 if (info->gfi_agno == start_ag)551 memset(&info->gfi_low, 0, sizeof(info->gfi_low));552 }553 554 /* Do we have a retained free extent? */555 if (info->gfi_lastfree.fmr_owner) {556 error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree);557 if (error)558 goto err;559 }560 561 /* Report any gaps at the end of the bg */562 info->gfi_last = true;563 error = ext4_getfsmap_datadev_helper(sb, end_ag, last_cluster, 0, info);564 if (error)565 goto err;566 567err:568 ext4_getfsmap_free_fixed_metadata(&info->gfi_meta_list);569 return error;570}571 572/* Do we recognize the device? */573static bool ext4_getfsmap_is_valid_device(struct super_block *sb,574 struct ext4_fsmap *fm)575{576 if (fm->fmr_device == 0 || fm->fmr_device == UINT_MAX ||577 fm->fmr_device == new_encode_dev(sb->s_bdev->bd_dev))578 return true;579 if (EXT4_SB(sb)->s_journal_bdev_file &&580 fm->fmr_device ==581 new_encode_dev(file_bdev(EXT4_SB(sb)->s_journal_bdev_file)->bd_dev))582 return true;583 return false;584}585 586/* Ensure that the low key is less than the high key. */587static bool ext4_getfsmap_check_keys(struct ext4_fsmap *low_key,588 struct ext4_fsmap *high_key)589{590 if (low_key->fmr_device > high_key->fmr_device)591 return false;592 if (low_key->fmr_device < high_key->fmr_device)593 return true;594 595 if (low_key->fmr_physical > high_key->fmr_physical)596 return false;597 if (low_key->fmr_physical < high_key->fmr_physical)598 return true;599 600 if (low_key->fmr_owner > high_key->fmr_owner)601 return false;602 if (low_key->fmr_owner < high_key->fmr_owner)603 return true;604 605 return false;606}607 608#define EXT4_GETFSMAP_DEVS 2609/*610 * Get filesystem's extents as described in head, and format for611 * output. Calls formatter to fill the user's buffer until all612 * extents are mapped, until the passed-in head->fmh_count slots have613 * been filled, or until the formatter short-circuits the loop, if it614 * is tracking filled-in extents on its own.615 *616 * Key to Confusion617 * ----------------618 * There are multiple levels of keys and counters at work here:619 * _fsmap_head.fmh_keys -- low and high fsmap keys passed in;620 * these reflect fs-wide block addrs.621 * dkeys -- fmh_keys used to query each device;622 * these are fmh_keys but w/ the low key623 * bumped up by fmr_length.624 * _getfsmap_info.gfi_next_fsblk-- next fs block we expect to see; this625 * is how we detect gaps in the fsmap626 * records and report them.627 * _getfsmap_info.gfi_low/high -- per-bg low/high keys computed from628 * dkeys; used to query the free space.629 */630int ext4_getfsmap(struct super_block *sb, struct ext4_fsmap_head *head,631 ext4_fsmap_format_t formatter, void *arg)632{633 struct ext4_fsmap dkeys[2]; /* per-dev keys */634 struct ext4_getfsmap_dev handlers[EXT4_GETFSMAP_DEVS];635 struct ext4_getfsmap_info info = { NULL };636 int i;637 int error = 0;638 639 if (head->fmh_iflags & ~FMH_IF_VALID)640 return -EINVAL;641 if (!ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[0]) ||642 !ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[1]))643 return -EINVAL;644 645 head->fmh_entries = 0;646 647 /* Set up our device handlers. */648 memset(handlers, 0, sizeof(handlers));649 handlers[0].gfd_dev = new_encode_dev(sb->s_bdev->bd_dev);650 handlers[0].gfd_fn = ext4_getfsmap_datadev;651 if (EXT4_SB(sb)->s_journal_bdev_file) {652 handlers[1].gfd_dev = new_encode_dev(653 file_bdev(EXT4_SB(sb)->s_journal_bdev_file)->bd_dev);654 handlers[1].gfd_fn = ext4_getfsmap_logdev;655 }656 657 sort(handlers, EXT4_GETFSMAP_DEVS, sizeof(struct ext4_getfsmap_dev),658 ext4_getfsmap_dev_compare, NULL);659 660 /*661 * To continue where we left off, we allow userspace to use the662 * last mapping from a previous call as the low key of the next.663 * This is identified by a non-zero length in the low key. We664 * have to increment the low key in this scenario to ensure we665 * don't return the same mapping again, and instead return the666 * very next mapping.667 *668 * Bump the physical offset as there can be no other mapping for669 * the same physical block range.670 */671 dkeys[0] = head->fmh_keys[0];672 dkeys[0].fmr_physical += dkeys[0].fmr_length;673 dkeys[0].fmr_owner = 0;674 dkeys[0].fmr_length = 0;675 memset(&dkeys[1], 0xFF, sizeof(struct ext4_fsmap));676 677 if (!ext4_getfsmap_check_keys(dkeys, &head->fmh_keys[1]))678 return -EINVAL;679 680 info.gfi_next_fsblk = head->fmh_keys[0].fmr_physical +681 head->fmh_keys[0].fmr_length;682 info.gfi_formatter = formatter;683 info.gfi_format_arg = arg;684 info.gfi_head = head;685 686 /* For each device we support... */687 for (i = 0; i < EXT4_GETFSMAP_DEVS; i++) {688 /* Is this device within the range the user asked for? */689 if (!handlers[i].gfd_fn)690 continue;691 if (head->fmh_keys[0].fmr_device > handlers[i].gfd_dev)692 continue;693 if (head->fmh_keys[1].fmr_device < handlers[i].gfd_dev)694 break;695 696 /*697 * If this device number matches the high key, we have698 * to pass the high key to the handler to limit the699 * query results. If the device number exceeds the700 * low key, zero out the low key so that we get701 * everything from the beginning.702 */703 if (handlers[i].gfd_dev == head->fmh_keys[1].fmr_device)704 dkeys[1] = head->fmh_keys[1];705 if (handlers[i].gfd_dev > head->fmh_keys[0].fmr_device)706 memset(&dkeys[0], 0, sizeof(struct ext4_fsmap));707 708 info.gfi_dev = handlers[i].gfd_dev;709 info.gfi_last = false;710 info.gfi_agno = -1;711 error = handlers[i].gfd_fn(sb, dkeys, &info);712 if (error)713 break;714 info.gfi_next_fsblk = 0;715 }716 717 head->fmh_oflags = FMH_OF_DEV_T;718 return error;719}720