/* Compare a record against our starting point */ staticbool ext4_getfsmap_rec_before_low_key(struct ext4_getfsmap_info *info, struct ext4_fsmap *rec)
{ return rec->fmr_physical + rec->fmr_length <=
info->gfi_low.fmr_physical;
}
/* * Format a reverse mapping for getfsmap, having translated rm_startblock * into the appropriate daddr units.
*/ staticint ext4_getfsmap_helper(struct super_block *sb, struct ext4_getfsmap_info *info, struct ext4_fsmap *rec)
{ struct ext4_fsmap fmr; struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_fsblk_t rec_fsblk = rec->fmr_physical;
ext4_group_t agno;
ext4_grpblk_t cno; int error;
if (fatal_signal_pending(current)) return -EINTR;
/* * Filter out records that start before our startpoint, if the * caller requested that.
*/ if (ext4_getfsmap_rec_before_low_key(info, rec)) {
rec_fsblk += rec->fmr_length; if (info->gfi_next_fsblk < rec_fsblk)
info->gfi_next_fsblk = rec_fsblk; return EXT4_QUERY_RANGE_CONTINUE;
}
/* Are we just counting mappings? */ if (info->gfi_head->fmh_count == 0) { if (info->gfi_head->fmh_entries == UINT_MAX) return EXT4_QUERY_RANGE_ABORT;
if (rec_fsblk > info->gfi_next_fsblk)
info->gfi_head->fmh_entries++;
if (info->gfi_last) return EXT4_QUERY_RANGE_CONTINUE;
/* * If the record starts past the last physical block we saw, * then we've found a gap. Report the gap as being owned by * whatever the caller specified is the missing owner.
*/ if (rec_fsblk > info->gfi_next_fsblk) { if (info->gfi_head->fmh_entries >= info->gfi_head->fmh_count) return EXT4_QUERY_RANGE_ABORT;
/* If the retained free extent record is set... */ if (info->gfi_lastfree.fmr_owner) { /* ...and abuts this one, lengthen it and return. */ if (ext4_fsmap_next_pblk(&info->gfi_lastfree) == fsb) {
info->gfi_lastfree.fmr_length += fslen; return 0;
}
/* * There's a gap between the two free extents; emit the * retained extent prior to merging the meta_list.
*/
error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree); if (error) return error;
info->gfi_lastfree.fmr_owner = 0;
}
/* Merge in any relevant extents from the meta_list */
list_for_each_entry_safe(p, tmp, &info->gfi_meta_list, fmr_list) { if (p->fmr_physical + p->fmr_length <= info->gfi_next_fsblk) {
list_del(&p->fmr_list);
kfree(p);
} elseif (p->fmr_physical < fsb) {
error = ext4_getfsmap_helper(sb, info, p); if (error) return error;
/* If this is a free extent at the end of a bg, buffer it. */ if (ext4_fsmap_next_pblk(&irec) ==
ext4_group_first_block_no(sb, agno + 1)) {
info->gfi_lastfree = irec; return 0;
}
/* Otherwise, emit it */ return ext4_getfsmap_helper(sb, info, &irec);
}
/* Execute a getfsmap query against the log device. */ staticint ext4_getfsmap_logdev(struct super_block *sb, struct ext4_fsmap *keys, struct ext4_getfsmap_info *info)
{
journal_t *journal = EXT4_SB(sb)->s_journal; struct ext4_fsmap irec;
/* Set up search keys */
info->gfi_low = keys[0];
info->gfi_low.fmr_length = 0;
/* * This function returns the number of file system metadata blocks at * the beginning of a block group, including the reserved gdt blocks.
*/ staticunsignedint ext4_getfsmap_find_sb(struct super_block *sb,
ext4_group_t agno, struct list_head *meta_list)
{ struct ext4_sb_info *sbi = EXT4_SB(sb);
ext4_fsblk_t fsb = ext4_group_first_block_no(sb, agno);
ext4_fsblk_t len; unsignedlong first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg); unsignedlong metagroup = agno / EXT4_DESC_PER_BLOCK(sb); int error;
/* Record the superblock. */ if (ext4_bg_has_super(sb, agno)) {
error = ext4_getfsmap_fill(meta_list, fsb, 1, EXT4_FMR_OWN_FS); if (error) return error;
fsb++;
}
/* Record the group descriptors. */
len = ext4_bg_num_gdb(sb, agno); if (!len) return 0;
error = ext4_getfsmap_fill(meta_list, fsb, len,
EXT4_FMR_OWN_GDT); if (error) return error;
fsb += len;
/* Reserved GDT blocks */ if (!ext4_has_feature_meta_bg(sb) || metagroup < first_meta_bg) {
len = le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks);
/* * mkfs.ext4 can set s_reserved_gdt_blocks as 0 in some cases, * check for that.
*/ if (!len) return 0;
/* Find all the fixed metadata in the filesystem. */ staticint ext4_getfsmap_find_fixed_metadata(struct super_block *sb, struct list_head *meta_list)
{ struct ext4_group_desc *gdp;
ext4_group_t agno; int error;
INIT_LIST_HEAD(meta_list);
/* Collect everything. */ for (agno = 0; agno < EXT4_SB(sb)->s_groups_count; agno++) {
gdp = ext4_get_group_desc(sb, agno, NULL); if (!gdp) {
error = -EFSCORRUPTED; goto err;
}
/* Determine first and last group to examine based on start and end */
ext4_get_group_no_and_offset(sb, start_fsb, &start_ag, &first_cluster);
ext4_get_group_no_and_offset(sb, end_fsb, &end_ag, &last_cluster);
/* * Convert the fsmap low/high keys to bg based keys. Initialize * low to the fsmap low key and max out the high key to the end * of the bg.
*/
info->gfi_low = keys[0];
info->gfi_low.fmr_physical = EXT4_C2B(sbi, first_cluster);
info->gfi_low.fmr_length = 0;
/* Assemble a list of all the fixed-location metadata. */
error = ext4_getfsmap_find_fixed_metadata(sb, &info->gfi_meta_list); if (error) goto err;
/* Query each bg */ for (info->gfi_agno = start_ag;
info->gfi_agno <= end_ag;
info->gfi_agno++) { /* * Set the bg high key from the fsmap high key if this * is the last bg that we're querying.
*/ if (info->gfi_agno == end_ag) {
info->gfi_high = keys[1];
info->gfi_high.fmr_physical = EXT4_C2B(sbi,
last_cluster);
info->gfi_high.fmr_length = 0;
}
/* * Set the bg low key to the start of the bg prior to * moving on to the next bg.
*/ if (info->gfi_agno == start_ag)
memset(&info->gfi_low, 0, sizeof(info->gfi_low));
}
/* Do we have a retained free extent? */ if (info->gfi_lastfree.fmr_owner) {
error = ext4_getfsmap_helper(sb, info, &info->gfi_lastfree); if (error) goto err;
}
/* * The dummy record below will cause ext4_getfsmap_helper() to report * any allocated blocks at the end of the range.
*/
irec.fmr_device = 0;
irec.fmr_physical = end_fsb + 1;
irec.fmr_length = 0;
irec.fmr_owner = EXT4_FMR_OWN_FREE;
irec.fmr_flags = 0;
/* Do we recognize the device? */ staticbool ext4_getfsmap_is_valid_device(struct super_block *sb, struct ext4_fsmap *fm)
{ if (fm->fmr_device == 0 || fm->fmr_device == UINT_MAX ||
fm->fmr_device == new_encode_dev(sb->s_bdev->bd_dev)) returntrue; if (EXT4_SB(sb)->s_journal_bdev_file &&
fm->fmr_device ==
new_encode_dev(file_bdev(EXT4_SB(sb)->s_journal_bdev_file)->bd_dev)) returntrue; returnfalse;
}
/* Ensure that the low key is less than the high key. */ staticbool ext4_getfsmap_check_keys(struct ext4_fsmap *low_key, struct ext4_fsmap *high_key)
{ if (low_key->fmr_device > high_key->fmr_device) returnfalse; if (low_key->fmr_device < high_key->fmr_device) returntrue;
if (low_key->fmr_physical > high_key->fmr_physical) returnfalse; if (low_key->fmr_physical < high_key->fmr_physical) returntrue;
if (low_key->fmr_owner > high_key->fmr_owner) returnfalse; if (low_key->fmr_owner < high_key->fmr_owner) returntrue;
returnfalse;
}
#define EXT4_GETFSMAP_DEVS 2 /* * Get filesystem's extents as described in head, and format for * output. Calls formatter to fill the user's buffer until all * extents are mapped, until the passed-in head->fmh_count slots have * been filled, or until the formatter short-circuits the loop, if it * is tracking filled-in extents on its own. * * Key to Confusion * ---------------- * There are multiple levels of keys and counters at work here: * _fsmap_head.fmh_keys -- low and high fsmap keys passed in; * these reflect fs-wide block addrs. * dkeys -- fmh_keys used to query each device; * these are fmh_keys but w/ the low key * bumped up by fmr_length. * _getfsmap_info.gfi_next_fsblk-- next fs block we expect to see; this * is how we detect gaps in the fsmap * records and report them. * _getfsmap_info.gfi_low/high -- per-bg low/high keys computed from * dkeys; used to query the free space.
*/ int ext4_getfsmap(struct super_block *sb, struct ext4_fsmap_head *head,
ext4_fsmap_format_t formatter, void *arg)
{ struct ext4_fsmap dkeys[2]; /* per-dev keys */ struct ext4_getfsmap_dev handlers[EXT4_GETFSMAP_DEVS]; struct ext4_getfsmap_info info = { NULL }; int i; int error = 0;
if (head->fmh_iflags & ~FMH_IF_VALID) return -EINVAL; if (!ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[0]) ||
!ext4_getfsmap_is_valid_device(sb, &head->fmh_keys[1])) return -EINVAL;
head->fmh_entries = 0;
/* Set up our device handlers. */
memset(handlers, 0, sizeof(handlers));
handlers[0].gfd_dev = new_encode_dev(sb->s_bdev->bd_dev);
handlers[0].gfd_fn = ext4_getfsmap_datadev; if (EXT4_SB(sb)->s_journal_bdev_file) {
handlers[1].gfd_dev = new_encode_dev(
file_bdev(EXT4_SB(sb)->s_journal_bdev_file)->bd_dev);
handlers[1].gfd_fn = ext4_getfsmap_logdev;
}
/* * To continue where we left off, we allow userspace to use the * last mapping from a previous call as the low key of the next. * This is identified by a non-zero length in the low key. We * have to increment the low key in this scenario to ensure we * don't return the same mapping again, and instead return the * very next mapping. * * Bump the physical offset as there can be no other mapping for * the same physical block range.
*/
dkeys[0] = head->fmh_keys[0];
dkeys[0].fmr_physical += dkeys[0].fmr_length;
dkeys[0].fmr_owner = 0;
dkeys[0].fmr_length = 0;
memset(&dkeys[1], 0xFF, sizeof(struct ext4_fsmap));
if (!ext4_getfsmap_check_keys(dkeys, &head->fmh_keys[1])) return -EINVAL;
/* For each device we support... */ for (i = 0; i < EXT4_GETFSMAP_DEVS; i++) { /* Is this device within the range the user asked for? */ if (!handlers[i].gfd_fn) continue; if (head->fmh_keys[0].fmr_device > handlers[i].gfd_dev) continue; if (head->fmh_keys[1].fmr_device < handlers[i].gfd_dev) break;
/* * If this device number matches the high key, we have * to pass the high key to the handler to limit the * query results. If the device number exceeds the * low key, zero out the low key so that we get * everything from the beginning.
*/ if (handlers[i].gfd_dev == head->fmh_keys[1].fmr_device)
dkeys[1] = head->fmh_keys[1]; if (handlers[i].gfd_dev > head->fmh_keys[0].fmr_device)
memset(&dkeys[0], 0, sizeof(struct ext4_fsmap));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.