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