Add support for EXT2_FEATURE_COMPAT_LAZY_BG

This feature is initially intended for testing purposes; it allows an
ext2/ext3 developer to create very large filesystems using sparse files
where most of the block groups are not initialized and so do not require
much disk space.  Eventually it could be used as a way of speeding up
mke2fs and e2fsck for large filesystem, but that would be best done by 
adding an RO_COMPAT extension to the filesystem to allow the inode table
to be lazily initialized on a per-block basis, instead of being entirely initialized
or entirely unused on a per-blockgroup basis.

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
bitmap-optimize
Theodore Ts'o 2006-05-08 20:17:26 -04:00
parent 49c6b4e947
commit f5fa20078b
15 changed files with 266 additions and 25 deletions

View File

@ -1,3 +1,8 @@
2006-05-08 Theodore Tso <tytso@mit.edu>
* debugfs.c (do_show_super_stats): Print out the block group flags
if they are set.
2006-04-27 Theodore Ts'o <tytso@mit.edu>
* htree.c (do_htree_dump, do_dx_hash), ls.c (do_list_dir): Add

View File

@ -266,13 +266,26 @@ static void print_features(struct ext2_super_block * s, FILE *f)
fputs("\n", f);
}
static void print_bg_opts(struct ext2_group_desc *gdp, int mask,
const char *str, int *first, FILE *f)
{
if (gdp->bg_flags & mask) {
if (*first) {
fputs(" [", f);
*first = 0;
} else
fputs(", ", f);
fputs(str, f);
}
}
void do_show_super_stats(int argc, char *argv[])
{
dgrp_t i;
FILE *out;
struct ext2_group_desc *gdp;
int c, header_only = 0;
int numdirs = 0;
int numdirs = 0, first;
reset_getopt();
while ((c = getopt (argc, argv, "h")) != EOF) {
@ -302,7 +315,7 @@ void do_show_super_stats(int argc, char *argv[])
}
gdp = &current_fs->group_desc[0];
for (i = 0; i < current_fs->group_desc_count; i++, gdp++)
for (i = 0; i < current_fs->group_desc_count; i++, gdp++) {
fprintf(out, " Group %2d: block bitmap at %u, "
"inode bitmap at %u, "
"inode table at %u\n"
@ -318,6 +331,14 @@ void do_show_super_stats(int argc, char *argv[])
gdp->bg_used_dirs_count,
gdp->bg_used_dirs_count != 1 ? "directories"
: "directory");
first = 1;
print_bg_opts(gdp, EXT2_BG_INODE_UNINIT, "Inode not init",
&first, out);
print_bg_opts(gdp, EXT2_BG_BLOCK_UNINIT, "Block not init",
&first, out);
if (!first)
fputs("]\n", out);
}
close_pager(out);
return;
print_usage:

View File

@ -1,3 +1,11 @@
2006-05-08 Theodore Tso <tytso@mit.edu>
* pass5.c (check_block_bitmaps, check_inode_bitmaps): Add support
for the lazy_bg feature; if the block group does not have
an initialized block or inode bitmaps/table, emulate what
the allocation bitmap would look like if no blocks or
inodes have been allocated.
2006-03-27 Theodore Ts'o <tytso@mit.edu>
* e2fsck.8.in: Add badblocks(8) to the See Also section.

View File

@ -111,7 +111,7 @@ static void print_bitmap_problem(e2fsck_t ctx, int problem,
static void check_block_bitmaps(e2fsck_t ctx)
{
ext2_filsys fs = ctx->fs;
blk_t i;
blk_t i, super;
int *free_array;
int group = 0;
unsigned int blocks = 0;
@ -121,6 +121,8 @@ static void check_block_bitmaps(e2fsck_t ctx)
struct problem_context pctx;
int problem, save_problem, fixit, had_problem;
errcode_t retval;
int lazy_bg = 0;
int skip_group = 0;
clear_problem_context(&pctx);
free_array = (int *) e2fsck_allocate_memory(ctx,
@ -156,19 +158,45 @@ static void check_block_bitmaps(e2fsck_t ctx)
return;
}
if (EXT2_HAS_COMPAT_FEATURE(fs->super,
EXT2_FEATURE_COMPAT_LAZY_BG))
lazy_bg++;
redo_counts:
had_problem = 0;
save_problem = 0;
pctx.blk = pctx.blk2 = NO_BLK;
if (lazy_bg && (fs->group_desc[group].bg_flags &
EXT2_BG_BLOCK_UNINIT))
skip_group++;
super = fs->super->s_first_data_block;
for (i = fs->super->s_first_data_block;
i < fs->super->s_blocks_count;
i++) {
actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
if (skip_group) {
if ((i >= super) &&
(i <= super + fs->desc_blocks) &&
ext2fs_bg_has_super(fs, group))
bitmap = 1;
else if (i == fs->group_desc[group].bg_block_bitmap)
bitmap = 1;
else if (i == fs->group_desc[group].bg_inode_bitmap)
bitmap = 1;
else if (i >= fs->group_desc[group].bg_inode_table &&
(i < fs->group_desc[group].bg_inode_table
+ fs->inode_blocks_per_group))
bitmap = 1;
else
bitmap = 0;
actual = (actual != 0);
} else
bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
if (actual == bitmap)
goto do_counts;
if (!actual && bitmap) {
/*
* Block not used, but marked in use in the bitmap.
@ -197,7 +225,7 @@ redo_counts:
had_problem++;
do_counts:
if (!bitmap) {
if (!bitmap && !skip_group) {
group_free++;
free_blocks++;
}
@ -208,10 +236,17 @@ redo_counts:
group ++;
blocks = 0;
group_free = 0;
skip_group = 0;
super += fs->super->s_blocks_per_group;
if (ctx->progress)
if ((ctx->progress)(ctx, 5, group,
fs->group_desc_count*2))
return;
if (lazy_bg &&
(i != fs->super->s_blocks_count-1) &&
(fs->group_desc[group].bg_flags &
EXT2_BG_BLOCK_UNINIT))
skip_group++;
}
}
if (pctx.blk != NO_BLK)
@ -286,6 +321,8 @@ static void check_inode_bitmaps(e2fsck_t ctx)
errcode_t retval;
struct problem_context pctx;
int problem, save_problem, fixit, had_problem;
int lazy_bg = 0;
int skip_group = 0;
clear_problem_context(&pctx);
free_array = (int *) e2fsck_allocate_memory(ctx,
@ -321,14 +358,24 @@ static void check_inode_bitmaps(e2fsck_t ctx)
return;
}
if (EXT2_HAS_COMPAT_FEATURE(fs->super,
EXT2_FEATURE_COMPAT_LAZY_BG))
lazy_bg++;
redo_counts:
had_problem = 0;
save_problem = 0;
pctx.ino = pctx.ino2 = 0;
if (lazy_bg && (fs->group_desc[group].bg_flags &
EXT2_BG_INODE_UNINIT))
skip_group++;
for (i = 1; i <= fs->super->s_inodes_count; i++) {
actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
if (skip_group)
bitmap = 0;
else
bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
if (actual == bitmap)
goto do_counts;
@ -360,12 +407,12 @@ redo_counts:
had_problem++;
do_counts:
if (!bitmap) {
group_free++;
free_inodes++;
} else {
if (bitmap) {
if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
dirs_count++;
} else if (!skip_group) {
group_free++;
free_inodes++;
}
inodes++;
if ((inodes == fs->super->s_inodes_per_group) ||
@ -374,6 +421,7 @@ do_counts:
dir_array[group] = dirs_count;
group ++;
inodes = 0;
skip_group = 0;
group_free = 0;
dirs_count = 0;
if (ctx->progress)
@ -381,6 +429,11 @@ do_counts:
group + fs->group_desc_count,
fs->group_desc_count*2))
return;
if (lazy_bg &&
(i != fs->super->s_inodes_count) &&
(fs->group_desc[group].bg_flags &
EXT2_BG_INODE_UNINIT))
skip_group++;
}
}
if (pctx.ino)

View File

@ -1,3 +1,7 @@
2006-05-08 Theodore Tso <tytso@mit.edu>
* feature.c: Add support for EXT2_FEATURE_COMPAT_LAZY_BG feature.
2006-04-18 Theodore Ts'o <tytso@mit.edu>
* uuid.c (e2p_is_null_uuid): Fix really stupid bug which could

View File

@ -35,6 +35,8 @@ static struct feature feature_list[] = {
"dir_index" },
{ E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE,
"resize_inode" },
{ E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_LAZY_BG,
"lazy_bg" },
{ E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
"sparse_super" },
{ E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE,

View File

@ -1,3 +1,29 @@
2006-05-08 Theodore Tso <tytso@mit.edu>
* rw_bitmaps.c (write_bitmaps, read_bitmaps): Added support for
lazy blockgroups. If a block group has flags indicating
that its block or inode data structures have not been
initialized, skip reading or writing that portion of the
bitmap.
* inode.c (ext2fs_open_inode_scan, ext2fs_read_inode_full): When
scanning a filesystem with lazy blockgroups, skip
uninitialized portions of the inode table when iterating
over the block group.
* swapfs.c (ext2fs_swap_group_desc): Byte swap the bg_flags field.
* ext2fs.h: Define the a new internal flag, EXT2_SF_DO_LAZY, so
that the inode interator knows compat_lazy_bg feature is
enabled. Declare that this version of e2fsprogs supports
the EXT2_FEATURE_COMPAY_LAZY_BG feature.
* ext2_fs.h (struct ext2_group_desc): Use the bg_pad field for
bg_flags, and define the flags EXT2_BG_INODE_UNINIT and
EXT2_BG_BLOCK_UNINIT. These flags are only honored if
EXT2_FEATURE_COMPAT_LAZY_BG feature is enabled (also
added).
2006-04-23 Theodore Ts'o <tytso@mit.edu>
* rw_bitmaps.c (write_bitmaps, ext2fs_write_inode_bitmap,

View File

@ -143,10 +143,13 @@ struct ext2_group_desc
__u16 bg_free_blocks_count; /* Free blocks count */
__u16 bg_free_inodes_count; /* Free inodes count */
__u16 bg_used_dirs_count; /* Directories count */
__u16 bg_pad;
__u16 bg_flags;
__u32 bg_reserved[3];
};
#define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */
#define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */
/*
* Data structures used by the directory indexing feature
*
@ -568,6 +571,7 @@ struct ext2_super_block {
#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
#define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010
#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
#define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040
#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002

View File

@ -341,6 +341,7 @@ typedef struct ext2_struct_inode_scan *ext2_inode_scan;
#define EXT2_SF_BAD_INODE_BLK 0x0002
#define EXT2_SF_BAD_EXTRA_BYTES 0x0004
#define EXT2_SF_SKIP_MISSING_ITABLE 0x0008
#define EXT2_SF_DO_LAZY 0x0010
/*
* ext2fs_check_if_mounted flags
@ -437,6 +438,7 @@ typedef struct ext2_icount *ext2_icount_t;
EXT3_FEATURE_COMPAT_HAS_JOURNAL|\
EXT2_FEATURE_COMPAT_RESIZE_INODE|\
EXT2_FEATURE_COMPAT_DIR_INDEX|\
EXT2_FEATURE_COMPAT_LAZY_BG|\
EXT2_FEATURE_COMPAT_EXT_ATTR)
/* This #ifdef is temporary until compression is fully supported */

View File

@ -164,6 +164,9 @@ errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
}
if (scan->fs->badblocks && scan->fs->badblocks->num)
scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
if (EXT2_HAS_COMPAT_FEATURE(fs->super,
EXT2_FEATURE_COMPAT_LAZY_BG))
scan->scan_flags |= EXT2_SF_DO_LAZY;
*ret_scan = scan;
return 0;
}
@ -407,9 +410,13 @@ errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
return retval;
}
/*
* This is done outside the above if statement so that the
* check can be done for block group #0.
* These checks are done outside the above if statement so
* they can be done for block group #0.
*/
if ((scan->scan_flags & EXT2_SF_DO_LAZY) &&
(scan->fs->group_desc[scan->current_group].bg_flags &
EXT2_BG_INODE_UNINIT))
goto force_new_group;
if (scan->current_block == 0) {
if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
goto force_new_group;

View File

@ -60,12 +60,16 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
errcode_t retval;
char *block_bitmap, *inode_bitmap;
char *block_buf, *inode_buf;
int lazy_flag = 0;
blk_t blk;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
if (!(fs->flags & EXT2_FLAG_RW))
return EXT2_ET_RO_FILSYS;
if (EXT2_HAS_COMPAT_FEATURE(fs->super,
EXT2_FEATURE_COMPAT_LAZY_BG))
lazy_flag = 1;
inode_nbytes = block_nbytes = 0;
block_bitmap = inode_bitmap = 0;
if (do_block) {
@ -89,6 +93,10 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
for (i = 0; i < fs->group_desc_count; i++) {
if (!block_bitmap || !do_block)
goto skip_block_bitmap;
if (lazy_flag && fs->group_desc[i].bg_flags &
EXT2_BG_BLOCK_UNINIT)
goto skip_this_block_bitmap;
memcpy(block_buf, block_bitmap, block_nbytes);
if (i == fs->group_desc_count - 1) {
@ -113,12 +121,17 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
if (retval)
return EXT2_ET_BLOCK_BITMAP_WRITE;
}
skip_this_block_bitmap:
block_bitmap += block_nbytes;
skip_block_bitmap:
if (!inode_bitmap || !do_inode)
continue;
if (lazy_flag && fs->group_desc[i].bg_flags &
EXT2_BG_INODE_UNINIT)
goto skip_this_inode_bitmap;
memcpy(inode_buf, inode_bitmap, inode_nbytes);
blk = fs->group_desc[i].bg_inode_bitmap;
if (blk) {
@ -133,6 +146,7 @@ static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block)
if (retval)
return EXT2_ET_INODE_BITMAP_WRITE;
}
skip_this_inode_bitmap:
inode_bitmap += inode_nbytes;
}
@ -155,12 +169,17 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
errcode_t retval;
int block_nbytes = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8;
int lazy_flag = 0;
blk_t blk;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
fs->write_bitmaps = ext2fs_write_bitmaps;
if (EXT2_HAS_COMPAT_FEATURE(fs->super,
EXT2_FEATURE_COMPAT_LAZY_BG))
lazy_flag = 1;
retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
if (retval)
return retval;
@ -209,6 +228,9 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
for (i = 0; i < fs->group_desc_count; i++) {
if (block_bitmap) {
blk = fs->group_desc[i].bg_block_bitmap;
if (lazy_flag && fs->group_desc[i].bg_flags &
EXT2_BG_BLOCK_UNINIT)
blk = 0;
if (blk) {
retval = io_channel_read_blk(fs->io, blk,
-block_nbytes, block_bitmap);
@ -222,11 +244,14 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes);
#endif
} else
memset(block_bitmap, 0, block_nbytes);
memset(block_bitmap, 0xff, block_nbytes);
block_bitmap += block_nbytes;
}
if (inode_bitmap) {
blk = fs->group_desc[i].bg_inode_bitmap;
if (lazy_flag && fs->group_desc[i].bg_flags &
EXT2_BG_INODE_UNINIT)
blk = 0;
if (blk) {
retval = io_channel_read_blk(fs->io, blk,
-inode_nbytes, inode_bitmap);
@ -240,7 +265,7 @@ static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes);
#endif
} else
memset(inode_bitmap, 0, inode_nbytes);
memset(inode_bitmap, 0xff, inode_nbytes);
inode_bitmap += inode_nbytes;
}
}

View File

@ -78,6 +78,7 @@ void ext2fs_swap_group_desc(struct ext2_group_desc *gdp)
gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count);
gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count);
gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count);
gdp->bg_flags = ext2fs_swab16(gdp->bg_flags);
}
void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header)

View File

@ -1,3 +1,14 @@
2006-05-08 Theodore Tso <tytso@mit.edu>
* mke2fs.c (write_inode_tables, setup_lazy_bg, main): Add support
for the COMPAT_LAZY_BG feature. This is currently
intended for debugging purposes only, as a way to create
very large filesystems stored on sparse files for testing
purposes.
* dumpe2fs.c (list_desc): Print out the block group flags if they
are set.
2006-04-22 Theodore Ts'o <tytso@mit.edu>
* filefrag.c: Make filefrag 32-bit clean, so that it works on

View File

@ -96,6 +96,36 @@ static void print_free (unsigned long group, char * bitmap,
}
}
static void print_bg_opt(int bg_flags, int mask,
const char *str, int *first)
{
if (bg_flags & mask) {
if (*first) {
fputs(" [", stdout);
*first = 0;
} else
fputs(", ", stdout);
fputs(str, stdout);
}
}
static void print_bg_opts(ext2_filsys fs, dgrp_t i)
{
int first = 1, bg_flags;
if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_LAZY_BG)
bg_flags = fs->group_desc[i].bg_flags;
else
bg_flags = 0;
print_bg_opt(bg_flags, EXT2_BG_INODE_UNINIT, "Inode not init",
&first);
print_bg_opt(bg_flags, EXT2_BG_BLOCK_UNINIT, "Block not init",
&first);
if (!first)
fputc(']', stdout);
fputc('\n', stdout);
}
static void list_desc (ext2_filsys fs)
{
unsigned long i;
@ -130,7 +160,8 @@ static void list_desc (ext2_filsys fs)
next_blk = fs->super->s_blocks_count;
printf (_("Group %lu: (Blocks "), i);
print_range(group_blk, next_blk - 1);
fputs(")\n", stdout);
fputs(")", stdout);
print_bg_opts(fs, i);
has_super = ((i==0) || super_blk);
if (has_super) {
printf (_(" %s superblock at "),

View File

@ -403,6 +403,7 @@ static void write_inode_tables(ext2_filsys fs)
dgrp_t i;
int num;
struct progress_struct progress;
int lazy_flag = 0;
if (quiet)
memset(&progress, 0, sizeof(progress));
@ -410,18 +411,25 @@ static void write_inode_tables(ext2_filsys fs)
progress_init(&progress, _("Writing inode tables: "),
fs->group_desc_count);
if (EXT2_HAS_COMPAT_FEATURE(fs->super,
EXT2_FEATURE_COMPAT_LAZY_BG))
lazy_flag = 1;
for (i = 0; i < fs->group_desc_count; i++) {
progress_update(&progress, i);
blk = fs->group_desc[i].bg_inode_table;
num = fs->inode_blocks_per_group;
retval = zero_blocks(fs, blk, num, 0, &blk, &num);
if (retval) {
fprintf(stderr, _("\nCould not write %d blocks "
"in inode table starting at %u: %s\n"),
num, blk, error_message(retval));
exit(1);
if (!(lazy_flag &&
(fs->group_desc[i].bg_flags & EXT2_BG_INODE_UNINIT))) {
retval = zero_blocks(fs, blk, num, 0, &blk, &num);
if (retval) {
fprintf(stderr, _("\nCould not write %d "
"blocks in inode table starting at %u: %s\n"),
num, blk, error_message(retval));
exit(1);
}
}
if (sync_kludge) {
if (sync_kludge == 1)
@ -434,6 +442,37 @@ static void write_inode_tables(ext2_filsys fs)
progress_close(&progress);
}
static void setup_lazy_bg(ext2_filsys fs)
{
dgrp_t i;
int blks;
struct ext2_super_block *sb = fs->super;
struct ext2_group_desc *bg = fs->group_desc;
if (EXT2_HAS_COMPAT_FEATURE(fs->super,
EXT2_FEATURE_COMPAT_LAZY_BG)) {
for (i = 0; i < fs->group_desc_count; i++, bg++) {
if ((i == 0) ||
(i == fs->group_desc_count-1))
continue;
if (bg->bg_free_inodes_count ==
sb->s_inodes_per_group) {
bg->bg_free_inodes_count = 0;
bg->bg_flags |= EXT2_BG_INODE_UNINIT;
sb->s_free_inodes_count -=
sb->s_inodes_per_group;
}
blks = ext2fs_super_and_bgd_loc(fs, i, 0, 0, 0, 0);
if (bg->bg_free_blocks_count == blks) {
bg->bg_free_blocks_count = 0;
bg->bg_flags |= EXT2_BG_BLOCK_UNINIT;
sb->s_free_blocks_count -= blks;
}
}
}
}
static void create_root_dir(ext2_filsys fs)
{
errcode_t retval;
@ -814,7 +853,8 @@ static void parse_extended_opts(struct ext2_super_block *param,
static __u32 ok_features[3] = {
EXT3_FEATURE_COMPAT_HAS_JOURNAL |
EXT2_FEATURE_COMPAT_RESIZE_INODE |
EXT2_FEATURE_COMPAT_DIR_INDEX, /* Compat */
EXT2_FEATURE_COMPAT_DIR_INDEX |
EXT2_FEATURE_COMPAT_LAZY_BG, /* Compat */
EXT2_FEATURE_INCOMPAT_FILETYPE| /* Incompat */
EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
EXT2_FEATURE_INCOMPAT_META_BG,
@ -1557,6 +1597,7 @@ int main (int argc, char *argv[])
_("while zeroing block %u at end of filesystem"),
ret_blk);
}
setup_lazy_bg(fs);
write_inode_tables(fs);
create_root_dir(fs);
create_lost_and_found(fs);