fi
AC_USE_SYSTEM_EXTENSIONS
dnl
- dnl Set default values for library extentions. Will be dealt with after
- dnl parsing configuration opions, which may modify these
+ dnl Set default values for library extensions. Will be dealt with after
+ dnl parsing configuration options, which may modify these
dnl
LIB_EXT=.a
STATIC_LIB_EXT=.a
attr/xattr.h
linux/falloc.h
linux/fd.h
+ linux/fsmap.h
linux/major.h
linux/loop.h
+ linux/types.h
net/if_dl.h
netinet/in.h
sys/acl.h
]])
AC_FUNC_VPRINTF
dnl Check to see if dirent has member d_reclen. On cygwin those d_reclen
- dnl is not decleared.
+ dnl is not declared.
AC_CHECK_MEMBER(struct dirent.d_reclen,[AC_DEFINE(HAVE_RECLEN_DIRENT, 1,
[Define to 1 if dirent has d_reclen])],,
[#include <dirent.h>])
fallocate
fallocate64
fchown
+ fcntl
fdatasync
fstat64
+ fsync
ftruncate64
futimes
getcwd
$(DEPSTATIC_LIBCOM_ERR) $(DEPSTATIC_LIBUUID) \
$(DEPSTATIC_LIBE2P)
- # This nastyness is needed because of jfs_user.h hackery; when we finally
+ # This nastiness is needed because of jfs_user.h hackery; when we finally
# clean up this mess, we should be able to drop it
LOCAL_CFLAGS = -I$(srcdir)/../e2fsck -DDEBUGFS
DEPEND_CFLAGS = -I$(srcdir)
$(Q) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(SYSLIBS) -DUNITTEST \
-o tst_set_fields $(srcdir)/set_fields.c $(srcdir)/util.c $(LIBS)
-check:: tst_set_fields
+fullcheck check:: tst_set_fields
$(TESTENV) ./tst_set_fields
# +++ Dependency line eater +++
fprintf(pager, "\t Indirect levels: %d\n", rootnode->indirect_levels);
fprintf(pager, "\t Flags: %d\n", rootnode->unused_flags);
- ent = (struct ext2_dx_entry *) (buf + 24 + rootnode->info_length);
+ ent = (struct ext2_dx_entry *)
+ ((char *)rootnode + rootnode->info_length);
htree_dump_int_node(current_fs, ino, &inode, rootnode, ent,
buf + current_fs->blocksize,
err = ext2fs_dirhash(hash_version, argv[optind], strlen(argv[optind]),
hash_seed, &hash, &minor_hash);
if (err) {
- com_err(argv[0], err, "while caclulating hash");
+ com_err(argv[0], err, "while calculating hash");
return;
}
printf("Hash of %s is 0x%0x (minor 0x%0x)\n", argv[optind],
/* Special case: i_file_acl_high is 2 bytes */
{ "file_acl", &set_inode.i_file_acl,
&set_inode.osd2.linux2.l_i_file_acl_high, 6, parse_uint },
- { "dir_acl", &set_inode.i_dir_acl, NULL, 4, parse_uint, FLAG_ALIAS },
{ "faddr", &set_inode.i_faddr, NULL, 4, parse_uint },
{ "frag", &set_inode.osd2.hurd2.h_i_frag, NULL, 1, parse_uint, FLAG_ALIAS },
{ "fsize", &set_inode.osd2.hurd2.h_i_fsize, NULL, 1, parse_uint },
/*
* Note: info->size == 6 is special; this means a base size 4 bytes,
- * and secondiory (high) size of 2 bytes. This is needed for the
+ * and secondary (high) size of 2 bytes. This is needed for the
* special case of i_blocks_high and i_file_acl_high.
*/
static errcode_t parse_uint(struct field_set_info *info, char *field,
* %IM <inode> -> i_mtime
* %IF <inode> -> i_faddr
* %If <inode> -> i_file_acl
- * %Id <inode> -> i_dir_acl
+ * %Id <inode> -> i_size_high
* %Iu <inode> -> i_uid
* %Ig <inode> -> i_gid
* %It <inode type>
static int do_gmt = -1;
#ifdef __dietlibc__
- /* The diet libc doesn't respect the TZ environemnt variable */
+ /* The diet libc doesn't respect the TZ environment variable */
if (do_gmt == -1) {
time_str = getenv("TZ");
if (!time_str)
case 's':
if (LINUX_S_ISDIR(inode->i_mode))
fprintf(f, "%u", inode->i_size);
- else {
-#ifdef EXT2_NO_64_TYPE
- if (inode->i_size_high)
- fprintf(f, "0x%x%08x", inode->i_size_high,
- inode->i_size);
- else
- fprintf(f, "%u", inode->i_size);
-#else
+ else
fprintf(f, "%llu", EXT2_I_SIZE(inode));
-#endif
- }
break;
case 'S':
fprintf(f, "%u", large_inode->i_extra_isize);
break;
case 'd':
fprintf(f, "%u", (LINUX_S_ISDIR(inode->i_mode) ?
- inode->i_dir_acl : 0));
+ inode->i_size_high : 0));
break;
case 'u':
fprintf(f, "%d", inode_uid(*inode));
fputc('%', f);
break;
case 'b':
-#ifdef EXT2_NO_64_TYPE
- fprintf(f, "%*u", width, (unsigned long) ctx->blk);
-#else
fprintf(f, "%*llu", width, (unsigned long long) ctx->blk);
-#endif
break;
case 'B':
if (ctx->blkcount == BLOCK_COUNT_IND)
if (*first && islower(m[0]))
fputc(toupper(*m++), f);
fputs(m, f);
- if (ctx->blkcount >= 0) {
-#ifdef EXT2_NO_64_TYPE
- fprintf(f, "%d", ctx->blkcount);
-#else
+ if (ctx->blkcount >= 0)
fprintf(f, "%lld", (long long) ctx->blkcount);
-#endif
- }
break;
case 'c':
-#ifdef EXT2_NO_64_TYPE
- fprintf(f, "%*u", width, (unsigned long) ctx->blk2);
-#else
fprintf(f, "%*llu", width, (unsigned long long) ctx->blk2);
-#endif
break;
case 'd':
fprintf(f, "%*u", width, ctx->dir);
fprintf(f, "%*s", width, error_message(ctx->errcode));
break;
case 'N':
-#ifdef EXT2_NO_64_TYPE
- fprintf(f, "%*u", width, ctx->num);
-#else
fprintf(f, "%*llu", width, (long long)ctx->num);
-#endif
+ break;
+ case 'n':
+ fprintf(f, "%*llu", width, (long long)ctx->num2);
break;
case 'p':
print_pathname(f, fs, ctx->ino, 0);
print_pathname(f, fs, ctx->dir, ctx->ino);
break;
case 'r':
-#ifdef EXT2_NO_64_TYPE
- fprintf(f, "%*d", width, ctx->blkcount);
-#else
fprintf(f, "%*lld", width, (long long) ctx->blkcount);
-#endif
break;
case 'S':
fprintf(f, "%llu", get_backup_sb(NULL, fs, NULL, NULL));
fprintf(f, "0x%0*x", width, ctx->csum1);
break;
case 'X':
-#ifdef EXT2_NO_64_TYPE
- fprintf(f, "0x%0*x", width, ctx->num);
-#else
fprintf(f, "0x%0*llx", width, (long long)ctx->num);
-#endif
break;
case 'y':
fprintf(f, "0x%0*x", width, ctx->csum2);
* - A bitmap of which blocks are in use. (block_found_map)
* - A bitmap of which blocks are in use by two inodes (block_dup_map)
* - The data blocks of the directory inodes. (dir_map)
+ * - Ref counts for ea_inodes. (ea_inode_refs)
*
* Pass 1 is designed to stash away enough information so that the
* other passes should not need to read in the inode information
- * during the normal course of a filesystem check. (Althogh if an
+ * during the normal course of a filesystem check. (Although if an
* inconsistency is detected, other passes may need to read in an
* inode to fix it.)
*
#undef DEBUG
+struct ea_quota {
+ blk64_t blocks;
+ __u64 inodes;
+};
+
static int process_block(ext2_filsys fs, blk64_t *blocknr,
e2_blkcnt_t blockcnt, blk64_t ref_blk,
int ref_offset, void *priv_data);
e2_blkcnt_t blockcnt, blk64_t ref_blk,
int ref_offset, void *priv_data);
static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf);
+ char *block_buf,
+ const struct ea_quota *ea_ibody_quota);
static void mark_table_blocks(e2fsck_t ctx);
static void alloc_bb_map(e2fsck_t ctx);
static void alloc_imagic_map(e2fsck_t ctx);
struct process_inode_block {
ext2_ino_t ino;
+ struct ea_quota ea_ibody_quota;
struct ext2_inode_large inode;
};
{
unsigned int len;
int i;
- blk64_t blocks;
ext2_extent_handle_t handle;
struct ext2_extent_info info;
struct ext2fs_extent extent;
return 1;
}
- blocks = ext2fs_inode_data_blocks2(fs, inode);
- if (blocks) {
- if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ if (ext2fs_is_fast_symlink(inode)) {
+ if (inode->i_size >= sizeof(inode->i_block))
+ return 0;
+
+ len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
+ if (len == sizeof(inode->i_block))
return 0;
+ } else {
if ((inode->i_size >= fs->blocksize) ||
- (blocks != fs->blocksize >> 9) ||
(inode->i_block[0] < fs->super->s_first_data_block) ||
(inode->i_block[0] >= ext2fs_blocks_count(fs->super)))
return 0;
}
if (len == fs->blocksize)
return 0;
- } else if (inode->i_flags & EXT4_INLINE_DATA_FL) {
- char *inline_buf = NULL;
- size_t inline_sz = 0;
-
- if (ext2fs_inline_data_size(fs, ino, &inline_sz))
- return 0;
- if (inode->i_size != inline_sz)
- return 0;
- if (ext2fs_get_mem(inline_sz + 1, &inline_buf))
- return 0;
- i = 0;
- if (ext2fs_inline_data_get(fs, ino, inode, inline_buf, NULL))
- goto exit_inline;
- inline_buf[inline_sz] = 0;
- len = strnlen(inline_buf, inline_sz);
- if (len != inline_sz)
- goto exit_inline;
- i = 1;
-exit_inline:
- ext2fs_free_mem(&inline_buf);
- return i;
- } else {
- if (inode->i_size >= sizeof(inode->i_block))
- return 0;
-
- len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
- if (len == sizeof(inode->i_block))
- return 0;
}
if (len != inode->i_size)
if ((inode->i_flags & EXT4_ENCRYPT_FL) == 0)
e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
}
-static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
+/*
+ * For a given size, calculate how many blocks would be charged towards quota.
+ */
+static blk64_t size_to_quota_blocks(ext2_filsys fs, size_t size)
+{
+ blk64_t clusters;
+
+ clusters = DIV_ROUND_UP(size, fs->blocksize << fs->cluster_ratio_bits);
+ return EXT2FS_C2B(fs, clusters);
+}
+
+/*
+ * Check validity of EA inode. Return 0 if EA inode is valid, otherwise return
+ * the problem code.
+ */
+static problem_t check_large_ea_inode(e2fsck_t ctx,
+ struct ext2_ext_attr_entry *entry,
+ struct problem_context *pctx,
+ blk64_t *quota_blocks)
+{
+ struct ext2_inode inode;
+ __u32 hash;
+ errcode_t retval;
+
+ /* Check if inode is within valid range */
+ if ((entry->e_value_inum < EXT2_FIRST_INODE(ctx->fs->super)) ||
+ (entry->e_value_inum > ctx->fs->super->s_inodes_count)) {
+ pctx->num = entry->e_value_inum;
+ return PR_1_ATTR_VALUE_EA_INODE;
+ }
+
+ e2fsck_read_inode(ctx, entry->e_value_inum, &inode, "pass1");
+
+ retval = ext2fs_ext_attr_hash_entry2(ctx->fs, entry, NULL, &hash);
+ if (retval) {
+ com_err("check_large_ea_inode", retval,
+ _("while hashing entry with e_value_inum = %u"),
+ entry->e_value_inum);
+ fatal_error(ctx, 0);
+ }
+
+ if (hash == entry->e_hash) {
+ *quota_blocks = size_to_quota_blocks(ctx->fs,
+ entry->e_value_size);
+ } else {
+ /* This might be an old Lustre-style ea_inode reference. */
+ if (inode.i_mtime == pctx->ino &&
+ inode.i_generation == pctx->inode->i_generation) {
+ *quota_blocks = 0;
+ } else {
+ /* If target inode is also missing EA_INODE flag,
+ * this is likely to be a bad reference.
+ */
+ if (!(inode.i_flags & EXT4_EA_INODE_FL)) {
+ pctx->num = entry->e_value_inum;
+ return PR_1_ATTR_VALUE_EA_INODE;
+ } else {
+ pctx->num = entry->e_hash;
+ return PR_1_ATTR_HASH;
+ }
+ }
+ }
+
+ if (!(inode.i_flags & EXT4_EA_INODE_FL)) {
+ pctx->num = entry->e_value_inum;
+ if (fix_problem(ctx, PR_1_ATTR_SET_EA_INODE_FL, pctx)) {
+ inode.i_flags |= EXT4_EA_INODE_FL;
+ ext2fs_write_inode(ctx->fs, entry->e_value_inum,
+ &inode);
+ } else {
+ return PR_1_ATTR_NO_EA_INODE_FL;
+ }
+ }
+ return 0;
+}
+
+static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
+ struct ext2_ext_attr_entry *first, void *end)
+{
+ struct ext2_ext_attr_entry *entry;
+
+ for (entry = first;
+ (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry);
+ entry = EXT2_EXT_ATTR_NEXT(entry)) {
+ if (!entry->e_value_inum)
+ continue;
+ if (!ctx->ea_inode_refs) {
+ pctx->errcode = ea_refcount_create(0,
+ &ctx->ea_inode_refs);
+ if (pctx->errcode) {
+ pctx->num = 4;
+ fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+ ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum,
+ 0);
+ }
+}
+
+static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
+ struct ea_quota *ea_ibody_quota)
{
struct ext2_super_block *sb = ctx->fs->super;
struct ext2_inode_large *inode;
struct ext2_ext_attr_entry *entry;
- char *start, *header;
+ char *start, *header, *end;
unsigned int storage_size, remain;
problem_t problem = 0;
region_t region = 0;
+ ea_ibody_quota->blocks = 0;
+ ea_ibody_quota->inodes = 0;
+
inode = (struct ext2_inode_large *) pctx->inode;
storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
inode->i_extra_isize;
header = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize;
+ end = header + storage_size;
start = header + sizeof(__u32);
entry = (struct ext2_ext_attr_entry *) start;
/* attribute len eats this space */
remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
- /* check value size */
- if (entry->e_value_size > remain) {
- pctx->num = entry->e_value_size;
- problem = PR_1_ATTR_VALUE_SIZE;
- goto fix;
- }
+ if (entry->e_value_inum == 0) {
+ /* check value size */
+ if (entry->e_value_size > remain) {
+ pctx->num = entry->e_value_size;
+ problem = PR_1_ATTR_VALUE_SIZE;
+ goto fix;
+ }
- /* e_value_block must be 0 in inode's ea */
- if (entry->e_value_block != 0) {
- pctx->num = entry->e_value_block;
- problem = PR_1_ATTR_VALUE_BLOCK;
- goto fix;
- }
+ if (entry->e_value_size &&
+ region_allocate(region,
+ sizeof(__u32) + entry->e_value_offs,
+ EXT2_EXT_ATTR_SIZE(
+ entry->e_value_size))) {
+ problem = PR_1_INODE_EA_ALLOC_COLLISION;
+ goto fix;
+ }
- if (entry->e_value_size &&
- region_allocate(region, sizeof(__u32) + entry->e_value_offs,
- EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
- problem = PR_1_INODE_EA_ALLOC_COLLISION;
- goto fix;
- }
+ hash = ext2fs_ext_attr_hash_entry(entry,
+ start + entry->e_value_offs);
+
+ /* e_hash may be 0 in older inode's ea */
+ if (entry->e_hash != 0 && entry->e_hash != hash) {
+ pctx->num = entry->e_hash;
+ problem = PR_1_ATTR_HASH;
+ goto fix;
+ }
+ } else {
+ blk64_t quota_blocks;
- hash = ext2fs_ext_attr_hash_entry(entry,
- start + entry->e_value_offs);
+ problem = check_large_ea_inode(ctx, entry, pctx,
+ "a_blocks);
+ if (problem != 0)
+ goto fix;
- /* e_hash may be 0 in older inode's ea */
- if (entry->e_hash != 0 && entry->e_hash != hash) {
- pctx->num = entry->e_hash;
- problem = PR_1_ATTR_HASH;
- goto fix;
+ ea_ibody_quota->blocks += quota_blocks;
+ ea_ibody_quota->inodes++;
}
- remain -= entry->e_value_size;
+ /* If EA value is stored in external inode then it does not
+ * consume space here */
+ if (entry->e_value_inum == 0)
+ remain -= entry->e_value_size;
entry = EXT2_EXT_ATTR_NEXT(entry);
}
* it seems like a corruption. it's very unlikely we could repair
* EA(s) in automatic fashion -bzzz
*/
- if (problem == 0 || !fix_problem(ctx, problem, pctx))
+ if (problem == 0 || !fix_problem(ctx, problem, pctx)) {
+ inc_ea_inode_refs(ctx, pctx,
+ (struct ext2_ext_attr_entry *)start, end);
return;
+ }
/* simply remove all possible EA(s) */
*((__u32 *)header) = 0UL;
e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
EXT2_INODE_SIZE(sb), "pass1");
+ ea_ibody_quota->blocks = 0;
+ ea_ibody_quota->inodes = 0;
}
static int check_inode_extra_negative_epoch(__u32 xtime, __u32 extra) {
*/
#define EXT4_EXTRA_NEGATIVE_DATE_CUTOFF 2 * (1LL << 32)
-static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
+static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,
+ struct ea_quota *ea_ibody_quota)
{
struct ext2_super_block *sb = ctx->fs->super;
struct ext2_inode_large *inode;
__u32 *eamagic;
int min, max;
+ ea_ibody_quota->blocks = 0;
+ ea_ibody_quota->inodes = 0;
+
inode = (struct ext2_inode_large *) pctx->inode;
if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
/* this isn't large inode. so, nothing to check */
inode->i_extra_isize = (inode->i_extra_isize + 3) & ~3;
e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
EXT2_INODE_SIZE(sb), "pass1");
- return;
}
/* check if there is no place for an EA header */
inode->i_extra_isize);
if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
/* it seems inode has an extended attribute(s) in body */
- check_ea_in_inode(ctx, pctx);
+ check_ea_in_inode(ctx, pctx, ea_ibody_quota);
}
/*
int failed_csum = 0;
ext2_ino_t ino_threshold = 0;
dgrp_t ra_group = 0;
+ struct ea_quota ea_ibody_quota;
init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&pctx);
/*
* Make sure the root inode is a directory; if
* not, offer to clear it. It will be
- * regnerated in pass #3.
+ * regenerated in pass #3.
*/
if (!LINUX_S_ISDIR(inode->i_mode)) {
if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx))
"pass1");
failed_csum = 0;
}
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
"pass1");
failed_csum = 0;
}
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
failed_csum = 0;
}
}
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
}
if (inode->i_faddr || frag || fsize ||
- (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
+ (!ext2fs_has_feature_largedir(fs->super) &&
+ (LINUX_S_ISDIR(inode->i_mode) && inode->i_size_high)))
mark_inode_bad(ctx, ino);
if ((fs->super->s_creator_os != EXT2_OS_HURD) &&
!ext2fs_has_feature_64bit(fs->super) &&
}
}
- check_inode_extra_space(ctx, &pctx);
+ check_inode_extra_space(ctx, &pctx, &ea_ibody_quota);
check_is_really_dir(ctx, &pctx, block_buf);
/*
if (inode->i_flags & EXT4_INLINE_DATA_FL) {
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
- } else if (ext2fs_inode_data_blocks(fs, inode) == 0) {
+ } else if (ext2fs_is_fast_symlink(inode)) {
ctx->fs_fast_symlinks_count++;
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf,
+ &ea_ibody_quota);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
inode->i_block[EXT2_DIND_BLOCK] ||
inode->i_block[EXT2_TIND_BLOCK] ||
ext2fs_file_acl_block(fs, inode))) {
- struct ext2_inode_large *ip;
+ struct process_inode_block *itp;
- inodes_to_process[process_inode_count].ino = ino;
- ip = &inodes_to_process[process_inode_count].inode;
+ itp = &inodes_to_process[process_inode_count];
+ itp->ino = ino;
+ itp->ea_ibody_quota = ea_ibody_quota;
if (inode_size < sizeof(struct ext2_inode_large))
- memcpy(ip, inode, inode_size);
+ memcpy(&itp->inode, inode, inode_size);
else
- memcpy(ip, inode, sizeof(*ip));
+ memcpy(&itp->inode, inode, sizeof(itp->inode));
process_inode_count++;
} else
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf, &ea_ibody_quota);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
ctx->refcount_extra = 0;
}
+ if (ctx->ea_block_quota_blocks) {
+ ea_refcount_free(ctx->ea_block_quota_blocks);
+ ctx->ea_block_quota_blocks = 0;
+ }
+
+ if (ctx->ea_block_quota_inodes) {
+ ea_refcount_free(ctx->ea_block_quota_inodes);
+ ctx->ea_block_quota_inodes = 0;
+ }
+
if (ctx->invalid_bitmaps)
handle_fs_bad_blocks(ctx);
sprintf(buf, _("reading indirect blocks of inode %u"),
pctx.ino);
ehandler_operation(buf);
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf,
+ &inodes_to_process[i].ea_ibody_quota);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
break;
}
}
}
+/*
+ * When cluster size is greater than one block, it is caller's responsibility
+ * to make sure block parameter starts at a cluster boundary.
+ */
static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
unsigned int num)
{
if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num))
ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num);
- else
- while (num--)
- mark_block_used(ctx, block++);
+ else {
+ int i;
+ for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs))
+ mark_block_used(ctx, block + i);
+ }
}
/*
ext2_filsys fs = ctx->fs;
blk64_t blk;
__u32 should_be;
- int count;
+ ea_value_t count;
clear_problem_context(&pctx);
}
header = (struct ext2_ext_attr_header *) block_buf;
pctx.blkcount = header->h_refcount;
- should_be = header->h_refcount + adjust_sign * count;
+ should_be = header->h_refcount + adjust_sign * (int)count;
pctx.num = should_be;
if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
header->h_refcount = should_be;
* Handle processing the extended attribute blocks
*/
static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf)
+ char *block_buf, struct ea_quota *ea_block_quota)
{
ext2_filsys fs = ctx->fs;
ext2_ino_t ino = pctx->ino;
blk64_t blk;
char * end;
struct ext2_ext_attr_header *header;
- struct ext2_ext_attr_entry *entry;
- int count;
+ struct ext2_ext_attr_entry *first, *entry;
+ blk64_t quota_blocks = EXT2FS_C2B(fs, 1);
+ __u64 quota_inodes = 0;
region_t region = 0;
int failed_csum = 0;
+ ea_block_quota->blocks = 0;
+ ea_block_quota->inodes = 0;
+
blk = ext2fs_file_acl_block(fs, inode);
if (blk == 0)
return 0;
/* Have we seen this EA block before? */
if (ext2fs_fast_test_block_bitmap2(ctx->block_ea_map, blk)) {
+ ea_block_quota->blocks = EXT2FS_C2B(fs, 1);
+ ea_block_quota->inodes = 0;
+
+ if (ctx->ea_block_quota_blocks) {
+ ea_refcount_fetch(ctx->ea_block_quota_blocks, blk,
+ "a_blocks);
+ if (quota_blocks)
+ ea_block_quota->blocks = quota_blocks;
+ }
+
+ if (ctx->ea_block_quota_inodes)
+ ea_refcount_fetch(ctx->ea_block_quota_inodes, blk,
+ &ea_block_quota->inodes);
+
if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
return 1;
/* Ooops, this EA was referenced more than it stated */
goto clear_extattr;
}
- entry = (struct ext2_ext_attr_entry *)(header+1);
+ first = (struct ext2_ext_attr_entry *)(header+1);
end = block_buf + fs->blocksize;
+ entry = first;
while ((char *)entry < end && *(__u32 *)entry) {
__u32 hash;
goto clear_extattr;
break;
}
- if (entry->e_value_block != 0) {
- if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
- goto clear_extattr;
- }
- if (entry->e_value_offs + entry->e_value_size > fs->blocksize) {
- if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
- goto clear_extattr;
- break;
- }
- if (entry->e_value_size &&
- region_allocate(region, entry->e_value_offs,
- EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
- if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
- goto clear_extattr;
- }
+ if (entry->e_value_inum == 0) {
+ if (entry->e_value_offs + entry->e_value_size >
+ fs->blocksize) {
+ if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
+ goto clear_extattr;
+ break;
+ }
+ if (entry->e_value_size &&
+ region_allocate(region, entry->e_value_offs,
+ EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
+ if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION,
+ pctx))
+ goto clear_extattr;
+ }
- hash = ext2fs_ext_attr_hash_entry(entry, block_buf +
- entry->e_value_offs);
+ hash = ext2fs_ext_attr_hash_entry(entry, block_buf +
+ entry->e_value_offs);
- if (entry->e_hash != hash) {
- pctx->num = entry->e_hash;
- if (fix_problem(ctx, PR_1_ATTR_HASH, pctx))
+ if (entry->e_hash != hash) {
+ pctx->num = entry->e_hash;
+ if (fix_problem(ctx, PR_1_ATTR_HASH, pctx))
+ goto clear_extattr;
+ entry->e_hash = hash;
+ }
+ } else {
+ problem_t problem;
+ blk64_t entry_quota_blocks;
+
+ problem = check_large_ea_inode(ctx, entry, pctx,
+ &entry_quota_blocks);
+ if (problem && fix_problem(ctx, problem, pctx))
goto clear_extattr;
- entry->e_hash = hash;
+
+ quota_blocks += entry_quota_blocks;
+ quota_inodes++;
}
entry = EXT2_EXT_ATTR_NEXT(entry);
return 0;
}
- count = header->h_refcount - 1;
- if (count)
- ea_refcount_store(ctx->refcount, blk, count);
+ if (quota_blocks != EXT2FS_C2B(fs, 1)) {
+ if (!ctx->ea_block_quota_blocks) {
+ pctx->errcode = ea_refcount_create(0,
+ &ctx->ea_block_quota_blocks);
+ if (pctx->errcode) {
+ pctx->num = 3;
+ goto refcount_fail;
+ }
+ }
+ ea_refcount_store(ctx->ea_block_quota_blocks, blk,
+ quota_blocks);
+ }
+
+ if (quota_inodes) {
+ if (!ctx->ea_block_quota_inodes) {
+ pctx->errcode = ea_refcount_create(0,
+ &ctx->ea_block_quota_inodes);
+ if (pctx->errcode) {
+ pctx->num = 4;
+refcount_fail:
+ fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return 0;
+ }
+ }
+
+ ea_refcount_store(ctx->ea_block_quota_inodes, blk,
+ quota_inodes);
+ }
+ ea_block_quota->blocks = quota_blocks;
+ ea_block_quota->inodes = quota_inodes;
+
+ inc_ea_inode_refs(ctx, pctx, first, end);
+ ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
mark_block_used(ctx, blk);
ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
return 1;
return 1;
pctx->num = root->indirect_levels;
- if ((root->indirect_levels > 1) &&
+ if ((root->indirect_levels > ext2_dir_htree_level(fs)) &&
fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
return 1;
{
struct ext2fs_extent extent;
blk64_t blk, last_lblk;
- e2_blkcnt_t blockcnt;
- unsigned int i;
+ unsigned int i, n;
int is_dir, is_leaf;
problem_t problem;
struct ext2_extent_info info;
}
}
alloc_later:
- while (is_dir && (++pb->last_db_block <
- (e2_blkcnt_t) extent.e_lblk)) {
- pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist,
- pb->ino, 0,
- pb->last_db_block);
- if (pctx->errcode) {
- pctx->blk = 0;
- pctx->num = pb->last_db_block;
- goto failed_add_dir_block;
- }
- }
- if (!ctx->fs->cluster_ratio_bits) {
- mark_blocks_used(ctx, extent.e_pblk, extent.e_len);
- pb->num_blocks += extent.e_len;
- }
- for (blk = extent.e_pblk, blockcnt = extent.e_lblk, i = 0;
- i < extent.e_len;
- blk++, blockcnt++, i++) {
- if (ctx->fs->cluster_ratio_bits &&
- !(pb->previous_block &&
- (EXT2FS_B2C(ctx->fs, blk) ==
- EXT2FS_B2C(ctx->fs, pb->previous_block)) &&
- (blk & EXT2FS_CLUSTER_MASK(ctx->fs)) ==
- ((unsigned) blockcnt & EXT2FS_CLUSTER_MASK(ctx->fs)))) {
- mark_block_used(ctx, blk);
- pb->num_blocks++;
- }
- if (has_unaligned_cluster_map(ctx, pb->previous_block,
- pb->last_block, blk,
- blockcnt)) {
- pctx->blk = blockcnt;
- pctx->blk2 = blk;
- fix_problem(ctx, PR_1_MISALIGNED_CLUSTER, pctx);
- mark_block_used(ctx, blk);
- mark_block_used(ctx, blk);
+ if (is_dir) {
+ while (++pb->last_db_block <
+ (e2_blkcnt_t) extent.e_lblk) {
+ pctx->errcode = ext2fs_add_dir_block2(
+ ctx->fs->dblist,
+ pb->ino, 0,
+ pb->last_db_block);
+ if (pctx->errcode) {
+ pctx->blk = 0;
+ pctx->num = pb->last_db_block;
+ goto failed_add_dir_block;
+ }
}
- pb->last_block = blockcnt;
- pb->previous_block = blk;
- if (is_dir) {
- pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist, pctx->ino, blk, blockcnt);
+ for (i = 0; i < extent.e_len; i++) {
+ pctx->errcode = ext2fs_add_dir_block2(
+ ctx->fs->dblist,
+ pctx->ino,
+ extent.e_pblk + i,
+ extent.e_lblk + i);
if (pctx->errcode) {
- pctx->blk = blk;
- pctx->num = blockcnt;
+ pctx->blk = extent.e_pblk + i;
+ pctx->num = extent.e_lblk + i;
failed_add_dir_block:
fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
/* Should never get here */
return;
}
}
+ if (extent.e_len > 0)
+ pb->last_db_block = extent.e_lblk + extent.e_len - 1;
+ }
+ if (has_unaligned_cluster_map(ctx, pb->previous_block,
+ pb->last_block,
+ extent.e_pblk,
+ extent.e_lblk)) {
+ for (i = 0; i < extent.e_len; i++) {
+ pctx->blk = extent.e_lblk + i;
+ pctx->blk2 = extent.e_pblk + i;
+ fix_problem(ctx, PR_1_MISALIGNED_CLUSTER, pctx);
+ mark_block_used(ctx, extent.e_pblk + i);
+ mark_block_used(ctx, extent.e_pblk + i);
+ }
+ }
+
+ /*
+ * Check whether first cluster got marked in previous iteration.
+ */
+ if (ctx->fs->cluster_ratio_bits &&
+ pb->previous_block &&
+ (EXT2FS_B2C(ctx->fs, extent.e_pblk) ==
+ EXT2FS_B2C(ctx->fs, pb->previous_block)))
+ /* Set blk to the beginning of next cluster. */
+ blk = EXT2FS_C2B(
+ ctx->fs,
+ EXT2FS_B2C(ctx->fs, extent.e_pblk) + 1);
+ else
+ /* Set blk to the beginning of current cluster. */
+ blk = EXT2FS_C2B(ctx->fs,
+ EXT2FS_B2C(ctx->fs, extent.e_pblk));
+
+ if (blk < extent.e_pblk + extent.e_len) {
+ mark_blocks_used(ctx, blk,
+ extent.e_pblk + extent.e_len - blk);
+ n = DIV_ROUND_UP(extent.e_pblk + extent.e_len - blk,
+ EXT2FS_CLUSTER_RATIO(ctx->fs));
+ pb->num_blocks += n;
}
- if (is_dir && extent.e_len > 0)
- pb->last_db_block = blockcnt - 1;
+ pb->last_block = extent.e_lblk + extent.e_len - 1;
pb->previous_block = extent.e_pblk + extent.e_len - 1;
start_block = pb->last_block = last_lblk;
if (is_leaf && !is_dir &&
* blocks used by that inode.
*/
static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf)
+ char *block_buf, const struct ea_quota *ea_ibody_quota)
{
ext2_filsys fs = ctx->fs;
struct process_block_struct pb;
int extent_fs;
int inlinedata_fs;
__u64 size;
+ struct ea_quota ea_block_quota;
pb.ino = ino;
- pb.num_blocks = 0;
+ pb.num_blocks = EXT2FS_B2C(ctx->fs,
+ ea_ibody_quota ? ea_ibody_quota->blocks : 0);
pb.last_block = ~0;
pb.last_init_lblock = -1;
pb.last_db_block = -1;
extent_fs = ext2fs_has_feature_extents(ctx->fs->super);
inlinedata_fs = ext2fs_has_feature_inline_data(ctx->fs->super);
- if (check_ext_attr(ctx, pctx, block_buf)) {
+ if (check_ext_attr(ctx, pctx, block_buf, &ea_block_quota)) {
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
goto out;
- pb.num_blocks++;
+ pb.num_blocks += EXT2FS_B2C(ctx->fs, ea_block_quota.blocks);
}
if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL))
}
if (ino != quota_type2inum(PRJQUOTA, fs->super) &&
- (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super))) {
+ (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
+ !(inode->i_flags & EXT4_EA_INODE_FL)) {
quota_data_add(ctx->qctx, (struct ext2_inode_large *) inode,
ino,
pb.num_blocks * EXT2_CLUSTER_SIZE(fs->super));
quota_data_inodes(ctx->qctx, (struct ext2_inode_large *) inode,
- ino, +1);
+ ino, (ea_ibody_quota ?
+ ea_ibody_quota->inodes : 0) +
+ ea_block_quota.inodes + 1);
}
if (!ext2fs_has_feature_huge_file(fs->super) ||
}
/*
- * Thes subroutines short circuits ext2fs_get_blocks and
+ * These subroutines short circuits ext2fs_get_blocks and
* ext2fs_check_directory; we use them since we already have the inode
* structure, so there's no point in letting the ext2fs library read
* the inode again.
unsigned long long next_ra_off;
};
+static void update_parents(struct dx_dir_info *dx_dir, int type)
+{
+ struct dx_dirblock_info *dx_db, *dx_parent, *dx_previous;
+ int b;
+
+ for (b = 0, dx_db = dx_dir->dx_block;
+ b < dx_dir->numblocks;
+ b++, dx_db++) {
+ dx_parent = &dx_dir->dx_block[dx_db->parent];
+ if (dx_db->type != type)
+ continue;
+
+ /*
+ * XXX Make sure dx_parent->min_hash > dx_db->min_hash
+ */
+ if (dx_db->flags & DX_FLAG_FIRST) {
+ dx_parent->min_hash = dx_db->min_hash;
+ if (dx_parent->previous) {
+ dx_previous =
+ &dx_dir->dx_block[dx_parent->previous];
+ dx_previous->node_max_hash =
+ dx_parent->min_hash;
+ }
+ }
+ /*
+ * XXX Make sure dx_parent->max_hash < dx_db->max_hash
+ */
+ if (dx_db->flags & DX_FLAG_LAST) {
+ dx_parent->max_hash = dx_db->max_hash;
+ }
+ }
+}
+
void e2fsck_pass2(e2fsck_t ctx)
{
struct ext2_super_block *sb = ctx->fs->super;
* Find all of the first and last leaf blocks, and
* update their parent's min and max hash values
*/
- for (b=0, dx_db = dx_dir->dx_block;
- b < dx_dir->numblocks;
- b++, dx_db++) {
- if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
- !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
- continue;
- dx_parent = &dx_dir->dx_block[dx_db->parent];
- /*
- * XXX Make sure dx_parent->min_hash > dx_db->min_hash
- */
- if (dx_db->flags & DX_FLAG_FIRST)
- dx_parent->min_hash = dx_db->min_hash;
- /*
- * XXX Make sure dx_parent->max_hash < dx_db->max_hash
- */
- if (dx_db->flags & DX_FLAG_LAST)
- dx_parent->max_hash = dx_db->max_hash;
- }
+ update_parents(dx_dir, DX_DIRBLOCK_LEAF);
+
+ /* for 3 level htree: update 2 level parent's min
+ * and max hash values */
+ update_parents(dx_dir, DX_DIRBLOCK_NODE);
for (b=0, dx_db = dx_dir->dx_block;
b < dx_dir->numblocks;
dx_db->flags |= DX_FLAG_REFERENCED;
dx_db->parent = db->blockcnt;
}
+
+ dx_db->previous =
+ i ? ext2fs_le32_to_cpu(ent[i-1].block & 0x0ffffff) : 0;
+
if (hash < min_hash)
min_hash = hash;
if (hash > max_hash)
return DIRENT_ABORT;
}
+ /* This will allow (at some point in the future) to punch out empty
+ * directory blocks and reduce the space used by a directory that grows
+ * very large and then the files are deleted. For now, all that is
+ * needed is to avoid e2fsck filling in these holes as part of
+ * feature flag. */
+ if (db->blk == 0 && ext2fs_has_feature_largedir(fs->super))
+ return 0;
+
if (db->blk == 0 && !inline_data_size) {
if (allocate_dir_block(ctx, db, buf, &cd->pctx))
return 0;
dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
if ((root->reserved_zero ||
root->info_length < 8 ||
- root->indirect_levels > 1) &&
+ root->indirect_levels >=
+ ext2_dir_htree_level(fs)) &&
fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
clear_htree(ctx, ino);
dx_dir->numblocks = 0;
}
/*
- * This fuction deallocates an inode
+ * This function deallocates an inode
*/
static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
{
}
/*
- * This fuction clears the htree flag on an inode
+ * This function clears the htree flag on an inode
*/
static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
{
} else
not_fixed++;
}
- if (inode.i_dir_acl &&
+ if (inode.i_size_high && !ext2fs_has_feature_largedir(fs->super) &&
LINUX_S_ISDIR(inode.i_mode)) {
- if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
- inode.i_dir_acl = 0;
+ if (fix_problem(ctx, PR_2_DIR_SIZE_HIGH_ZERO, &pctx)) {
+ inode.i_size_high = 0;
inode_modified++;
} else
not_fixed++;
#include "e2fsck.h"
#include "problem.h"
-#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
-
static void check_block_bitmaps(e2fsck_t ctx);
static void check_inode_bitmaps(e2fsck_t ctx);
static void check_inode_end(e2fsck_t ctx);
pctx->ino = pctx->ino2 = 0;
}
- /* Just to be more succint */
+ /* Just to be more succinct */
#define B2C(x) EXT2FS_B2C(fs, (x))
#define EQ_CLSTR(x, y) (B2C(x) == B2C(y))
#define LE_CLSTR(x, y) (B2C(x) <= B2C(y))
N_("Corruption found in @S. (%s = %N).\n"),
PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
- /* Error determing physical device size of filesystem */
+ /* Error determining physical device size of filesystem */
{ PR_0_GETSIZE_ERROR,
N_("Error determining size of the physical @v: %m\n"),
PROMPT_NONE, PR_FATAL },
N_("Timestamp(s) on @i %i beyond 2310-04-04 are likely pre-1970.\n"),
PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
+ /* Inode has illegal extended attribute value inode */
+ { PR_1_ATTR_VALUE_EA_INODE,
+ N_("@i %i has @I @a value @i %N.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* Inode has invalid extended attribute. EA inode missing
+ * EA_INODE flag. */
+ { PR_1_ATTR_NO_EA_INODE_FL,
+ N_("@i %i has @n @a. EA @i %N missing EA_INODE flag.\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* EA inode for parent inode missing EA_INODE flag. */
+ { PR_1_ATTR_SET_EA_INODE_FL,
+ N_("EA @i %N for parent @i %i missing EA_INODE flag.\n "),
+ PROMPT_FIX, PR_PREEN_OK },
+
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
N_("@E has @D/unused @i %Di. "),
PROMPT_CLEAR, PR_PREEN_OK },
- /* Directry entry is link to '.' */
+ /* Directory entry is link to '.' */
{ PR_2_LINK_DOT,
N_("@E @L to '.' "),
PROMPT_CLEAR, 0 },
N_("@E @L to @d %P (%Di).\n"),
PROMPT_CLEAR, 0 },
- /* Directory entry contains a link to the root directry */
+ /* Directory entry contains a link to the root directory */
{ PR_2_LINK_ROOT,
N_("@E @L to the @r.\n"),
PROMPT_CLEAR, 0 },
N_("i_file_acl @F %If, @s zero.\n"),
PROMPT_CLEAR, 0 },
- /* i_dir_acl should be zero */
- { PR_2_DIR_ACL_ZERO,
- N_("i_dir_acl @F %Id, @s zero.\n"),
+ /* i_size_high should be zero */
+ { PR_2_DIR_SIZE_HIGH_ZERO,
+ N_("i_size_high @F %Id, @s zero.\n"),
PROMPT_CLEAR, 0 },
/* i_frag should be zero */
"They @s the same!\n"),
PROMPT_NONE, 0 },
+ { PR_4_EA_INODE_REF_COUNT,
+ N_("@a @i %i ref count is %N, @s %n. "),
+ PROMPT_FIX, PR_PREEN_OK },
+
/* Pass 5 errors */
/* Pass 5: Checking group summary information */
e2_blkcnt_t blkcount;
dgrp_t group;
__u32 csum1, csum2;
- __u64 num;
+ __u64 num, num2;
const char *str;
};
/* Miscellaneous superblock corruption */
#define PR_0_MISC_CORRUPT_SUPER 0x00000B
- /* Error determing physical device size of filesystem */
+ /* Error determining physical device size of filesystem */
#define PR_0_GETSIZE_ERROR 0x00000C
/* Inode count in the superblock incorrect */
/* Timestamp(s) on inode beyond 2310-04-04 are likely pre-1970. */
#define PR_1_EA_TIME_OUT_OF_RANGE 0x010082
+/* Inode has illegal EA value inode */
+#define PR_1_ATTR_VALUE_EA_INODE 0x010083
+
+/* Parent inode has invalid EA entry. EA inode does not have
+ * EXT4_EA_INODE_FL flag. Delete EA entry? */
+#define PR_1_ATTR_NO_EA_INODE_FL 0x010085
+
+/* EA inode for parent inode does not have EXT4_EA_INODE_FL flag */
+#define PR_1_ATTR_SET_EA_INODE_FL 0x010086
+
+
/*
* Pass 1b errors
*/
/* Directory entry has deleted or unused inode */
#define PR_2_UNUSED_INODE 0x020003
- /* Directry entry is link to '.' */
+ /* Directory entry is link to '.' */
#define PR_2_LINK_DOT 0x020004
/* Directory entry points to inode now located in a bad block */
/* Directory entry contains a link to a directory */
#define PR_2_LINK_DIR 0x020006
- /* Directory entry contains a link to the root directry */
+ /* Directory entry contains a link to the root directory */
#define PR_2_LINK_ROOT 0x020007
/* Directory entry has illegal characters in its name */
/* i_file_acl should be zero */
#define PR_2_FILE_ACL_ZERO 0x02000E
-/* i_dir_acl should be zero */
-#define PR_2_DIR_ACL_ZERO 0x02000F
+/* i_size_high should be zero */
+#define PR_2_DIR_SIZE_HIGH_ZERO 0x02000F
/* i_frag should be zero */
#define PR_2_FRAG_ZERO 0x020010
#define PR_3_LPF_ENCRYPTED 0x03001B
/*
- * Pass 3a --- rehashing diretories
+ * Pass 3a --- rehashing directories
*/
/* Pass 3a: Reindexing directories */
#define PR_3A_PASS_HEADER 0x031000
/* Inconsistent inode count information cached */
#define PR_4_INCONSISTENT_COUNT 0x040004
+/* Extended attribute inode ref count wrong */
+#define PR_4_EA_INODE_REF_COUNT 0x040005
+
/*
* Pass 5 errors
*/
/* Inode range not used, but marked in bitmap */
#define PR_5_INODE_RANGE_UNUSED 0x050016
- /* Inode rangeused, but not marked used in bitmap */
+ /* Inode range used, but not marked used in bitmap */
#define PR_5_INODE_RANGE_USED 0x050017
/* Block in use but group is marked BLOCK_UNINIT */
<Para>
The "Engineer" way
- Learn the subject throughly before I get to the programming itself.
+ Learn the subject thoroughly before I get to the programming itself.
Then, I could easily see the entire picture and select the best
course of action, taking all the factors into account.
</Para>
The "Explorer - Progressive" way.
Jump immediately into the cold water - Start programming and
- learning the material parallelly.
+ learning the material in parallel.
</Para>
</ListItem>
<Para>
It seems that starting with the <Literal remap="tt">superblock</Literal> was a good bet - Just from
the list of variables, one can learn a lot. I didn't understand all of them
- at the time, but it seemed that the following keywords were repeating themself
+ at the time, but it seemed that the following keywords were repeating themselves
in various variables:
<ItemizedList>
<Title>Source files in EXT2ED</Title>
<Para>
- The project was getting large enough to be splitted into several source
- files. I splitted the source as much as I could into self-contained
+ The project was getting large enough to be split into several source
+ files. I split the source as much as I could into self-contained
source files. The source files consist of the following blocks:
<ItemizedList>
store the necessary information about the inode in a specific structure
of type struct_file_info which will be available for use by the file_com.c
functions. Only then it will set the type to file. This is also the reason
- that a direct asynchronic set of the object type to a file through a settype
+ that a direct asynchronous set of the object type to a file through a settype
command will fail - The above data structure will not be initialized
properly because the user never was at the inode of the file.
__u32 i_block[EXT2_N_BLOCKS]; /* Pointers to blocks */
__u32 i_version; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
- __u32 i_dir_acl; /* Directory ACL */
+ __u32 i_size_high; /* High 32bits of size */
__u32 i_faddr; /* Fragment address */
union {
struct {
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_version; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
- __u32 i_dir_acl; /* Directory ACL */
+ __u32 i_size_high; /* High 32bits of size */
__u32 i_faddr; /* Fragment address */
union {
struct {
</Para>
<Para>
- It was found experimently that many of the files in the filesystem are
+ It was found experimentally that many of the files in the filesystem are
actually quite small. To take advantage of this effect, the kernel provides
storage of up to 12 block numbers in the inode itself. Those blocks are
called <Literal remap="tt">direct blocks</Literal>. The advantage is that once the kernel has the
<Title>Time and date</Title>
<Para>
- Linux records the last time in which various operations occured with the
+ Linux records the last time in which various operations occurred with the
file. The time and date are saved in the standard C library format - The
number of seconds which passed since 00:00:00 GMT, January 1, 1970. The
following times are recorded:
<Para>
The variable <Literal remap="tt">rec_len</Literal> is provided because the directory entries are
padded with zeroes so that the next entry will be in an offset which is
- a multiplition of 4. The resulting directory entry size is stored in
+ a multiplication of 4. The resulting directory entry size is stored in
<Literal remap="tt">rec_len</Literal>. If the directory entry is the last in the block, it is
padded with zeroes till the end of the block, and rec_len is updated
accordingly.
E2fsck also records the last time in which the file system was checked in
the <Literal remap="tt">s_lastcheck</Literal> variable. The user tunable parameter
<Literal remap="tt">s_checkinterval</Literal> will contain the number of seconds which are allowed
- to pass since <Literal remap="tt">s_lastcheck</Literal> until a check is reforced. A value of
+ to pass since <Literal remap="tt">s_lastcheck</Literal> until a check is forced. A value of
<Literal remap="tt">0</Literal> disables time-based check.
</Para>
My parser is very primitive - It only searches for the struct keywords,
and uses the variables in there. The rest of the file is just ignored.
- You will find at the end a few additional types which are not aviable in
+ You will find at the end a few additional types which are not available in
the original include file, such as the types "file" and "dir". They have
no variables, but are necessary due to the way ext2ed binds C commands
to specific types.
__u32 i_block[14]; /* Pointers to blocks */
__u32 i_version; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
- __u32 i_dir_acl; /* Directory ACL */
+ __u32 i_size_high; /* High 32bits of size */
__u32 i_faddr; /* Fragment address */
__u8 l_i_frag; /* Fragment number */
__u8 l_i_fsize; /* Fragment size */
my_dir = lib/ext2fs
INSTALL = @INSTALL@
DEPEND_CFLAGS = -I$(top_srcdir)/debugfs -I$(srcdir)/../../e2fsck -DDEBUGFS
- # This nastyness is needed because of jfs_user.h hackery; when we finally
+ # This nastiness is needed because of jfs_user.h hackery; when we finally
# clean up this mess, we should be able to drop it
DEBUGFS_CFLAGS = -I$(srcdir)/../../e2fsck $(ALL_CFLAGS) -DDEBUGFS
$(TDB_OBJ) \
undo_io.o \
unix_io.o \
+ sparse_io.o \
unlink.o \
valid_blk.o \
version.o \
$(srcdir)/tst_iscan.c \
$(srcdir)/undo_io.c \
$(srcdir)/unix_io.c \
+ $(srcdir)/sparse_io.c \
$(srcdir)/unlink.c \
$(srcdir)/valid_blk.c \
$(srcdir)/version.c \
$(Q) $(CC) -o mkjournal $(srcdir)/mkjournal.c -DDEBUG \
$(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(ALL_CFLAGS) $(SYSLIBS)
-check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
+fullcheck check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
tst_super_size tst_types tst_inode_size tst_csum tst_crc32c tst_bitmaps \
tst_inline tst_inline_data tst_libext2fs tst_sha256 tst_sha512 \
tst_digest_encode tst_getsize tst_getsectsize
$(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
$(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
$(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/ext2fsP.h
+sparse_io.o: $(srcdir)/sparse_io.c $(top_builddir)/lib/config.h \
+ $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
+ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h
unlink.o: $(srcdir)/unlink.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
+#if HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
#include "ext2_fs.h"
#include "ext2fsP.h"
retval = 1;
continue;
} else {
- /* modify the last extent in reigon to be removed */
+ /* modify the last extent in region to be removed */
ext->count -= ((start + count) - ext->start);
ext->start = start + count;
retval = 1;
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_generation; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
- __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */
+ __u32 i_size_high;
__u32 i_faddr; /* Fragment address */
union {
struct {
__u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__u32 i_generation; /* File version (for NFS) */
__u32 i_file_acl; /* File ACL */
- __u32 i_size_high; /* Formerly i_dir_acl, directory ACL */
+ __u32 i_size_high;
__u32 i_faddr; /* Fragment address */
union {
struct {
#define EXT4_EPOCH_BITS 2
#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
-#define i_dir_acl i_size_high
-
#define i_checksum_lo osd2.linux2.l_i_checksum_lo
#define inode_includes(size, field) \
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
- EXT4_FEATURE_INCOMPAT_MMP)
+ EXT4_FEATURE_INCOMPAT_MMP| \
+ EXT4_FEATURE_INCOMPAT_LARGEDIR| \
+ EXT4_FEATURE_INCOMPAT_EA_INODE)
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
* bigger than 255 chars, it's safe to reclaim the extra byte for the
* file_type field.
*
- * This structure is deprecated due to endianity issues. Please use struct
+ * This structure is deprecated due to endian issues. Please use struct
* ext2_dir_entry and accessor functions
* ext2fs_dirent_name_len
* ext2fs_dirent_set_name_len
* regardless of how old the timestamp is.
*
* The timestamp in the MMP structure will be updated by e2fsck at some
- * arbitary intervals (start of passes, after every few groups of inodes
+ * arbitrary intervals (start of passes, after every few groups of inodes
* in pass1 and pass1b). There is no guarantee that e2fsck is updating
* the MMP block in a timely manner, and the updates it does are purely
* for the convenience of the sysadmin and not for automatic validation.
#define EXT2_MKJOURNAL_LAZYINIT 0x0000002 /* don't zero journal inode before use*/
#define EXT2_MKJOURNAL_NO_MNT_CHECK 0x0000004 /* don't check mount status */
+struct blk_alloc_ctx;
struct opaque_ext2_group_desc;
struct struct_ext2_filsys {
*/
errcode_t (*get_alloc_block)(ext2_filsys fs, blk64_t goal,
blk64_t *ret);
+ errcode_t (*get_alloc_block2)(ext2_filsys fs, blk64_t goal,
+ blk64_t *ret, struct blk_alloc_ctx *ctx);
void (*block_alloc_stats)(ext2_filsys fs, blk64_t blk, int inuse);
/*
*
* BLOCK_FLAG_DEPTH_TRAVERSE indicates that the iterator function for
* the indirect, doubly indirect, etc. blocks should be called after
- * all of the blocks containined in the indirect blocks are processed.
+ * all of the blocks contained in the indirect blocks are processed.
* This is useful if you are going to be deallocating blocks from an
* inode.
*
#define BLOCK_COUNT_TIND (-3)
#define BLOCK_COUNT_TRANSLATOR (-4)
+#define BLOCK_ALLOC_UNKNOWN 0
+#define BLOCK_ALLOC_DATA 1
+#define BLOCK_ALLOC_METADATA 2
+
+struct blk_alloc_ctx {
+ ext2_ino_t ino;
+ struct ext2_inode *inode;
+ blk64_t lblk;
+ int flags;
+};
+
#if 0
/*
* Flags for ext2fs_move_blocks
/*
* Flags used by ext2fs_extent_delete()
*/
- #define EXT2_EXTENT_DELETE_KEEP_EMPTY 0x001 /* keep node if last extnt gone */
+ #define EXT2_EXTENT_DELETE_KEEP_EMPTY 0x001 /* keep node if last extent gone */
/*
* Flags used by ext2fs_extent_set_bmap()
EXT3_FEATURE_INCOMPAT_RECOVER|\
EXT3_FEATURE_INCOMPAT_EXTENTS|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
+ EXT4_FEATURE_INCOMPAT_EA_INODE|\
EXT4_LIB_INCOMPAT_MMP|\
EXT4_FEATURE_INCOMPAT_64BIT|\
EXT4_FEATURE_INCOMPAT_INLINE_DATA|\
EXT4_FEATURE_INCOMPAT_ENCRYPT|\
- EXT4_FEATURE_INCOMPAT_CSUM_SEED)
+ EXT4_FEATURE_INCOMPAT_CSUM_SEED|\
+ EXT4_FEATURE_INCOMPAT_LARGEDIR)
#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
ext2fs_block_bitmap map, blk_t *ret);
extern errcode_t ext2fs_new_block2(ext2_filsys fs, blk64_t goal,
ext2fs_block_bitmap map, blk64_t *ret);
+extern errcode_t ext2fs_new_block3(ext2_filsys fs, blk64_t goal,
+ ext2fs_block_bitmap map, blk64_t *ret,
+ struct blk_alloc_ctx *ctx);
extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start,
blk_t finish, int num,
ext2fs_block_bitmap map,
char *block_buf, blk_t *ret);
extern errcode_t ext2fs_alloc_block2(ext2_filsys fs, blk64_t goal,
char *block_buf, blk64_t *ret);
+extern errcode_t ext2fs_alloc_block3(ext2_filsys fs, blk64_t goal,
+ char *block_buf, blk64_t *ret,
+ struct blk_alloc_ctx *ctx);
+
extern void ext2fs_set_alloc_block_callback(ext2_filsys fs,
errcode_t (*func)(ext2_filsys fs,
blk64_t goal,
/* ext_attr.c */
extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry,
void *data);
+extern errcode_t ext2fs_ext_attr_hash_entry2(ext2_filsys fs,
+ struct ext2_ext_attr_entry *entry,
+ void *data, __u32 *hash);
extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
extern errcode_t ext2fs_read_ext_attr2(ext2_filsys fs, blk64_t block,
void *buf);
#define XATTR_HANDLE_FLAG_RAW 0x0001
errcode_t ext2fs_xattrs_flags(struct ext2_xattr_handle *handle,
unsigned int *new_flags, unsigned int *old_flags);
+extern void ext2fs_ext_attr_block_rehash(struct ext2_ext_attr_header *header,
+ struct ext2_ext_attr_entry *end);
+extern __u32 ext2fs_get_ea_inode_hash(struct ext2_inode *inode);
+extern void ext2fs_set_ea_inode_hash(struct ext2_inode *inode, __u32 hash);
+extern __u64 ext2fs_get_ea_inode_ref(struct ext2_inode *inode);
+extern void ext2fs_set_ea_inode_ref(struct ext2_inode *inode, __u64 ref_count);
/* extent.c */
extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
/* symlink.c */
errcode_t ext2fs_symlink(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino,
const char *name, const char *target);
+int ext2fs_is_fast_symlink(struct ext2_inode *inode);
/* mmp.c */
errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf);
return (blk_t) ext2fs_inode_data_blocks2(fs, inode);
}
+/* htree levels for ext4 */
+#define EXT4_HTREE_LEVEL_COMPAT 2
+#define EXT4_HTREE_LEVEL 3
+
+static inline unsigned int ext2_dir_htree_level(ext2_filsys fs)
+{
+ if (ext2fs_has_feature_largedir(fs->super))
+ return EXT4_HTREE_LEVEL;
+
+ return EXT4_HTREE_LEVEL_COMPAT;
+}
+
+_INLINE_ int ext2fs_htree_intnode_maxrecs(ext2_filsys fs, int blocks)
+{
+ return blocks * ((fs->blocksize - 8) / sizeof(struct ext2_dx_entry));
+}
+
/*
* This is an efficient, overflow safe way of calculating ceil((1.0 * a) / b)
*/
retval = ext2fs_xattr_set(handle, "system.data",
data->ea_data, data->ea_size);
- if (retval)
- goto err;
-
- retval = ext2fs_xattrs_write(handle);
-
err:
(void) ext2fs_xattrs_close(&handle);
return retval;
goto err;
retval = ext2fs_xattr_remove(handle, "system.data");
- if (retval)
- goto err;
-
- retval = ext2fs_xattrs_write(handle);
-
err:
(void) ext2fs_xattrs_close(&handle);
return retval;
dir->name[1] = '.';
/*
- * Ajust the last rec_len
+ * Adjust the last rec_len
*/
offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
dir = (struct ext2_dir_entry *) (bbuf + offset);
/* create a new file */
retval = ext2fs_new_inode(fs, 2, 010755, 0, &newfile);
if (retval) {
- com_err("file_test", retval, "while allocaing a new inode");
+ com_err("file_test", retval, "while allocating a new inode");
return 1;
}
inode.i_mode = LINUX_S_IFREG;
retval = ext2fs_write_new_inode(fs, newfile, &inode);
if (retval) {
- com_err("file_test", retval, "while writting a new inode");
+ com_err("file_test", retval, "while writing a new inode");
return 1;
}
retval = ext2fs_allocate_tables(fs);
if (retval) {
com_err("setup", retval,
- "while allocating tables for test filesysmte");
+ "while allocating tables for test filesystem");
exit(1);
}
COMPILE_ET= _ET_DIR_OVERRIDE=$(srcdir)/../lib/et/et ../lib/et/compile_et
- # This nastyness is needed because of jfs_user.h hackery; when we finally
+ # This nastiness is needed because of jfs_user.h hackery; when we finally
# clean up this mess, we should be able to drop it
JOURNAL_CFLAGS = -I$(srcdir)/../e2fsck $(ALL_CFLAGS) -DDEBUGFS
DEPEND_CFLAGS = -I$(top_srcdir)/e2fsck
$(Q) $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(srcdir)/base_device.c \
-DDEBUG -o base_device $(SYSLIBS)
-check:: base_device
+fullcheck check:: base_device
./base_device < $(srcdir)/base_device.tst > base_device.out
cmp $(srcdir)/base_device.tst base_device.out
#include "create_inode.h"
#include "support/nls-enable.h"
- /* 64KiB is the minimium blksize to best minimize system call overhead. */
+ /* 64KiB is the minimum blksize to best minimize system call overhead. */
#define COPY_FILE_BUFLEN 65536
static int ext2_file_type(unsigned int mode)
return retval;
}
+struct file_info {
+ char *path;
+ size_t path_len;
+ size_t path_max_len;
+};
+
+static errcode_t path_append(struct file_info *target, const char *file)
+{
+ if (strlen(file) + target->path_len + 1 > target->path_max_len) {
+ target->path_max_len *= 2;
+ target->path = realloc(target->path, target->path_max_len);
+ if (!target->path)
+ return EXT2_ET_NO_MEMORY;
+ }
+ target->path_len += sprintf(target->path + target->path_len, "/%s",
+ file);
+ return 0;
+}
+
/* Copy files from source_dir to fs */
static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
const char *source_dir, ext2_ino_t root,
- struct hdlinks_s *hdlinks)
+ struct hdlinks_s *hdlinks,
+ struct file_info *target,
+ struct fs_ops_callbacks *fs_callbacks)
{
const char *name;
DIR *dh;
errcode_t retval = 0;
int read_cnt;
int hdlink;
+ size_t cur_dir_path_len;
if (chdir(source_dir) < 0) {
retval = errno;
save_inode = 1;
}
+ cur_dir_path_len = target->path_len;
+ retval = path_append(target, name);
+ if (retval)
+ return retval;
+
+ if (fs_callbacks && fs_callbacks->create_new_inode) {
+ retval = fs_callbacks->create_new_inode(fs,
+ target->path, name, parent_ino, root,
+ st.st_mode & S_IFMT);
+ if (retval)
+ goto out;
+ }
+
switch(st.st_mode & S_IFMT) {
case S_IFCHR:
case S_IFBLK:
goto out;
}
/* Populate the dir recursively*/
- retval = __populate_fs(fs, ino, name, root, hdlinks);
+ retval = __populate_fs(fs, ino, name, root, hdlinks,
+ target, fs_callbacks);
if (retval)
goto out;
if (chdir("..")) {
goto out;
}
+ if (fs_callbacks && fs_callbacks->end_create_new_inode) {
+ retval = fs_callbacks->end_create_new_inode(fs,
+ target->path, name, parent_ino, root,
+ st.st_mode & S_IFMT);
+ if (retval)
+ goto out;
+ }
+
/* Save the hardlink ino */
if (save_inode) {
/*
hdlinks->hdl[hdlinks->count].dst_ino = ino;
hdlinks->count++;
}
+ target->path_len = cur_dir_path_len;
+ target->path[target->path_len] = 0;
}
out:
return retval;
}
-errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
- const char *source_dir, ext2_ino_t root)
+errcode_t populate_fs2(ext2_filsys fs, ext2_ino_t parent_ino,
+ const char *source_dir, ext2_ino_t root,
+ struct fs_ops_callbacks *fs_callbacks)
{
+ struct file_info file_info;
struct hdlinks_s hdlinks;
errcode_t retval;
return retval;
}
- retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks);
+ file_info.path_len = 0;
+ file_info.path_max_len = 255;
+ file_info.path = calloc(file_info.path_max_len, 1);
+ retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks,
+ &file_info, fs_callbacks);
+
+ free(file_info.path);
free(hdlinks.hdl);
return retval;
}
+
+errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
+ const char *source_dir, ext2_ino_t root)
+{
+ return populate_fs2(fs, parent_ino, source_dir, root, NULL);
+}
* Copyright (C) 1997, 1998 by Theodore Ts'o and
* PowerQuest, Inc.
*
- * Copyright (C) 1999, 2000 by Theosore Ts'o
+ * Copyright (C) 1999, 2000 by Theodore Ts'o
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
fs->inode_blocks_per_group);
}
- /* Some bigalloc helper macros which are more succint... */
+ /* Some bigalloc helper macros which are more succinct... */
#define B2C(x) EXT2FS_B2C(fs, (x))
#define C2B(x) EXT2FS_C2B(fs, (x))
#define EQ_CLSTR(x, y) (B2C(x) == B2C(y))
}
/*
- * Clean up the bitmaps for unitialized bitmaps
+ * Clean up the bitmaps for uninitialized bitmaps
*/
static void fix_uninit_block_bitmaps(ext2_filsys fs)
{
/*
* If we changed the number of block_group descriptor blocks,
* we need to make sure they are all marked as reserved in the
- * file systems's block allocation map.
+ * filesystem's block allocation map.
*/
for (i = 0; i < old_fs->group_desc_count; i++)
ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
/*
* For those structures that have changed, we need to
- * do bookkeepping.
+ * do bookkeeping.
*/
if (ext2fs_block_bitmap_loc(old_fs, i) !=
(blk = ext2fs_block_bitmap_loc(fs, i))) {
{
}
+static int fix_ea_entries(ext2_extent imap, struct ext2_ext_attr_entry *entry,
+ struct ext2_ext_attr_entry *end, ext2_ino_t last_ino)
+{
+ int modified = 0;
+ ext2_ino_t new_ino;
+ errcode_t retval;
+
+ while (entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ if (entry->e_value_inum > last_ino) {
+ new_ino = ext2fs_extent_translate(imap,
+ entry->e_value_inum);
+ entry->e_value_inum = new_ino;
+ modified = 1;
+ }
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+ return modified;
+}
+
+static int fix_ea_ibody_entries(ext2_extent imap,
+ struct ext2_inode_large *inode, int inode_size,
+ ext2_ino_t last_ino)
+{
+ struct ext2_ext_attr_entry *start, *end;
+ __u32 *ea_magic;
+
+ if (inode->i_extra_isize == 0)
+ return 0;
+
+ ea_magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize);
+ if (*ea_magic != EXT2_EXT_ATTR_MAGIC)
+ return 0;
+
+ start = (struct ext2_ext_attr_entry *)(ea_magic + 1);
+ end = (struct ext2_ext_attr_entry *)((char *)inode + inode_size);
+
+ return fix_ea_entries(imap, start, end, last_ino);
+}
+
+static int fix_ea_block_entries(ext2_extent imap, char *block_buf,
+ unsigned int blocksize, ext2_ino_t last_ino)
+{
+ struct ext2_ext_attr_header *header;
+ struct ext2_ext_attr_entry *start, *end;
+
+ header = (struct ext2_ext_attr_header *)block_buf;
+ start = (struct ext2_ext_attr_entry *)(header+1);
+ end = (struct ext2_ext_attr_entry *)(block_buf + blocksize);
+
+ return fix_ea_entries(imap, start, end, last_ino);
+}
+
+/* A simple LRU cache to check recently processed blocks. */
+struct blk_cache {
+ int cursor;
+ blk64_t blks[4];
+};
+
+#define BLK_IN_CACHE(b,c) ((b) == (c).blks[0] || (b) == (c).blks[1] || \
+ (b) == (c).blks[2] || (b) == (c).blks[3])
+#define BLK_ADD_CACHE(b,c) { \
+ (c).blks[(c).cursor] = (b); \
+ (c).cursor = ((c).cursor + 1) % 4; \
+}
+
+static errcode_t fix_ea_inode_refs(ext2_resize_t rfs, struct ext2_inode *inode,
+ char *block_buf, ext2_ino_t last_ino)
+{
+ ext2_filsys fs = rfs->new_fs;
+ ext2_inode_scan scan = NULL;
+ ext2_ino_t ino;
+ int inode_size = EXT2_INODE_SIZE(fs->super);
+ blk64_t blk;
+ int modified;
+ struct blk_cache blk_cache = { 0 };
+ struct ext2_ext_attr_header *header;
+ errcode_t retval;
+
+ header = (struct ext2_ext_attr_header *)block_buf;
+
+ retval = ext2fs_open_inode_scan(fs, 0, &scan);
+ if (retval)
+ goto out;
+
+ while (1) {
+ retval = ext2fs_get_next_inode_full(scan, &ino, inode,
+ inode_size);
+ if (retval)
+ goto out;
+ if (!ino)
+ break;
+
+ if (inode->i_links_count == 0 && ino != EXT2_RESIZE_INO)
+ continue; /* inode not in use */
+
+ if (inode_size != EXT2_GOOD_OLD_INODE_SIZE) {
+ modified = fix_ea_ibody_entries(rfs->imap,
+ (struct ext2_inode_large *)inode,
+ inode_size, last_ino);
+ if (modified) {
+ retval = ext2fs_write_inode_full(fs, ino, inode,
+ inode_size);
+ if (retval)
+ goto out;
+ }
+ }
+
+ blk = ext2fs_file_acl_block(fs, inode);
+ if (blk && !BLK_IN_CACHE(blk, blk_cache)) {
+ retval = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
+ if (retval)
+ goto out;
+
+ modified = fix_ea_block_entries(rfs->imap, block_buf,
+ fs->blocksize,
+ last_ino);
+ if (modified) {
+ retval = ext2fs_write_ext_attr3(fs, blk,
+ block_buf, ino);
+ if (retval)
+ goto out;
+ /*
+ * If refcount is greater than 1, we might see
+ * the same block referenced by other inodes
+ * later.
+ */
+ if (header->h_refcount > 1)
+ BLK_ADD_CACHE(blk, blk_cache);
+ }
+ }
+ }
+ retval = 0;
+out:
+ if (scan)
+ ext2fs_close_inode_scan(scan);
+ return retval;
+
+}
static errcode_t inode_scan_and_fix(ext2_resize_t rfs)
{
struct process_block_struct pb;
char *block_buf = 0;
ext2_ino_t start_to_move;
int inode_size;
+ int update_ea_inode_refs = 0;
if ((rfs->old_fs->group_desc_count <=
rfs->new_fs->group_desc_count) &&
ext2fs_inode_alloc_stats2(rfs->new_fs, new_inode, +1,
pb.is_dir);
- inode->i_ctime = time(0);
+ /*
+ * i_ctime field in xattr inodes contain a portion of the ref
+ * count, do not overwrite.
+ */
+ if (inode->i_flags & EXT4_EA_INODE_FL)
+ update_ea_inode_refs = 1;
+ else
+ inode->i_ctime = time(0);
+
retval = ext2fs_write_inode_full(rfs->old_fs, new_inode,
inode, inode_size);
if (retval)
goto errout;
}
}
+
+ if (update_ea_inode_refs &&
+ ext2fs_has_feature_ea_inode(rfs->new_fs->super)) {
+ retval = fix_ea_inode_refs(rfs, inode, block_buf,
+ start_to_move);
+ if (retval)
+ goto errout;
+ }
io_channel_flush(rfs->old_fs->io);
errout:
/*
- * calcluate the minimum number of blocks the given fs can be resized to
+ * calculate the minimum number of blocks the given fs can be resized to
*/
blk64_t calculate_minimum_resize_size(ext2_filsys fs, int flags)
{
#endif
/*
- * if we need more group descriptors in order to accomodate our data
+ * if we need more group descriptors in order to accommodate our data
* then we need to add them here
*/
blks_needed = data_needed;