different CPU’s. It is used by libuuid as well as the uuidgen
program.
-ifdef(`UDEV_PKGS',``
+ifdef(`UDEB_PKGS',``
Package: libuuid1-udeb
XC-Package-Type: udeb
Section: debian-installer
filesystems by hard-coded device names, but via a logical naming
system instead.
-ifdef(`UDEV_PKGS',``
+ifdef(`UDEB_PKGS',``
Package: libblkid1-udeb
XC-Package-Type: udeb
Section: debian-installer
This package contains the development environment for the blkid library.
'')dnl
-ifdef(`UDEV_PKGS',``
+ifdef(`UDEB_PKGS',``
Package: e2fsprogs-udeb
XC-Package-Type: udeb
Section: debian-installer
This package is an e2fsprogs package built for a reduced size, so that
it can help to save space in debian-installer.
.
- Don't attempt to install this package, it has no support for a couple of
+ Don'''``t attempt to install this package, it has no support for a couple of
features you surely want. Anyway it should refuse to install.
'')dnl
inode->i_extra_isize);
return;
}
- storage_size = EXT2_INODE_SIZE(current_fs->super) -
- EXT2_GOOD_OLD_INODE_SIZE -
- inode->i_extra_isize;
- magic = (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
- inode->i_extra_isize);
- if (*magic == EXT2_EXT_ATTR_MAGIC) {
- fprintf(out, "Extended attributes stored in inode body: \n");
- end = (char *) inode + EXT2_INODE_SIZE(current_fs->super);
- start = (char *) magic + sizeof(__u32);
- entry = (struct ext2_ext_attr_entry *) start;
- while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
- struct ext2_ext_attr_entry *next =
- EXT2_EXT_ATTR_NEXT(entry);
- if (entry->e_value_size > storage_size ||
- (char *) next >= end) {
- fprintf(out, "invalid EA entry in inode\n");
- return;
- }
- fprintf(out, " ");
- dump_xattr_string(out, EXT2_EXT_ATTR_NAME(entry),
- entry->e_name_len);
- fprintf(out, " = \"");
- dump_xattr_string(out, start + entry->e_value_offs,
- entry->e_value_size);
- fprintf(out, "\" (%u)\n", entry->e_value_size);
- entry = next;
- }
- }
+}
+
+/* Dump extended attributes */
+static int dump_attr(char *name, char *value, size_t value_len, void *data)
+{
+ FILE *out = data;
+
+ fprintf(out, " ");
+ dump_xattr_string(out, name, strlen(name));
+ fprintf(out, " = \"");
+ dump_xattr_string(out, value, value_len);
+ fprintf(out, "\" (%zu)\n", value_len);
+
+ return 0;
+}
+
+static void dump_inode_attributes(FILE *out, ext2_ino_t ino)
+{
+ struct ext2_xattr_handle *h;
+ errcode_t err;
+
+ err = ext2fs_xattrs_open(current_fs, ino, &h);
+ if (err)
+ return;
+
+ err = ext2fs_xattrs_read(h);
+ if (err)
+ goto out;
+
+ if (ext2fs_xattrs_count(h) == 0)
+ goto out;
+
+ fprintf(out, "Extended attributes:\n");
+ err = ext2fs_xattrs_iterate(h, dump_attr, out);
+ if (err)
+ goto out;
+
+out:
+ err = ext2fs_xattrs_close(&h);
}
static void dump_blocks(FILE *f, const char *prefix, ext2_ino_t inode)
fprintf(f, "\n");
}
+static void dump_inline_data(FILE *out, const char *prefix, ext2_ino_t inode_num)
+{
+ errcode_t retval;
+ size_t size;
+
+ retval = ext2fs_inline_data_size(current_fs, inode_num, &size);
+ if (!retval)
+ fprintf(out, "%sSize of inline data: %d", prefix, size);
+}
+
void internal_dump_inode(FILE *out, const char *prefix,
ext2_ino_t inode_num, struct ext2_inode *inode,
int do_dump_blocks)
if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
internal_dump_inode_extra(out, prefix, inode_num,
(struct ext2_inode_large *) inode);
+ dump_inode_attributes(out, inode_num);
if (current_fs->super->s_creator_os == EXT2_OS_LINUX &&
current_fs->super->s_feature_ro_compat &
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
if (inode->i_flags & EXT4_EXTENTS_FL)
dump_extents(out, prefix, inode_num,
DUMP_LEAF_EXTENTS|DUMP_NODE_EXTENTS, 0, 0);
+ else if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ dump_inline_data(out, prefix, inode_num);
else
dump_blocks(out, prefix, inode_num);
}
inode_buf.i_dtime = current_fs->now ? current_fs->now : time(0);
if (debugfs_write_inode(inode, &inode_buf, 0))
return;
- if (!ext2fs_inode_has_valid_blocks2(current_fs, &inode_buf))
- return;
-
- ext2fs_block_iterate3(current_fs, inode, BLOCK_FLAG_READ_ONLY, NULL,
- release_blocks_proc, NULL);
+ if (ext2fs_inode_has_valid_blocks2(current_fs, &inode_buf)) {
+ ext2fs_block_iterate3(current_fs, inode, BLOCK_FLAG_READ_ONLY,
+ NULL, release_blocks_proc, NULL);
+ }
printf("\n");
ext2fs_inode_alloc_stats2(current_fs, inode, -1,
LINUX_S_ISDIR(inode_buf.i_mode));
fs->name, num_blocks, EXT2_I_SIZE(inode));
}
print_header(fs);
- retval = ext2fs_block_iterate3(current_fs, ino,
- BLOCK_FLAG_READ_ONLY, NULL,
- filefrag_blocks_proc, fs);
- if (retval)
- com_err("ext2fs_block_iterate3", retval, 0);
+ if (ext2fs_inode_has_valid_blocks2(current_fs, inode)) {
+ retval = ext2fs_block_iterate3(current_fs, ino,
+ BLOCK_FLAG_READ_ONLY, NULL,
+ filefrag_blocks_proc, fs);
+ if (retval)
+ com_err("ext2fs_block_iterate3", retval, 0);
+ }
report_filefrag(fs);
fprintf(fs->f, "%s: %d contiguous extents%s\n", fs->name, fs->ext,
lsd.free_blocks = 0;
lsd.bad_blocks = 0;
- retval = ext2fs_block_iterate3(current_fs, ino,
- BLOCK_FLAG_READ_ONLY, block_buf,
- lsdel_proc, &lsd);
- if (retval) {
- com_err("ls_deleted_inodes", retval,
- "while calling ext2fs_block_iterate2");
- goto next;
+ if (ext2fs_inode_has_valid_blocks2(current_fs, &inode)) {
+ retval = ext2fs_block_iterate3(current_fs, ino,
+ BLOCK_FLAG_READ_ONLY,
+ block_buf,
+ lsdel_proc, &lsd);
+ if (retval) {
+ com_err("ls_deleted_inodes", retval,
+ "while calling ext2fs_block_iterate2");
+ goto next;
+ }
}
- if (lsd.free_blocks && !lsd.bad_blocks) {
+ if (lsd.free_blocks && !lsd.bad_blocks ||
+ inode.i_flags & EXT4_INLINE_DATA_FL) {
if (num_delarray >= max_delarray) {
max_delarray += 50;
delarray = realloc(delarray,
struct ext2fs_extent extent;
if ((inode->i_size_high || inode->i_size == 0) ||
- (inode->i_flags & EXT2_INDEX_FL))
+ (inode->i_flags & EXT2_INDEX_FL) ||
+ (inode->i_flags & EXT4_INLINE_DATA_FL))
return 0;
if (inode->i_flags & EXT4_EXTENTS_FL) {
blk64_t blk;
unsigned int i, rec_len, not_device = 0;
int extent_fs;
+ int inlinedata_fs;
/*
* If the mode looks OK, we believe it. If the first block in
* For extent mapped files, we don't do any sanity checking:
* just try to get the phys block of logical block 0 and run
* with it.
+ *
+ * For inline data files, we just try to get the size of inline
+ * data. If it's true, we will treat it as a directory.
*/
extent_fs = (ctx->fs->super->s_feature_incompat &
EXT3_FEATURE_INCOMPAT_EXTENTS);
- if (extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) {
+ inlinedata_fs = (ctx->fs->super->s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA);
+ if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL)) {
+ unsigned int size;
+
+ if (ext2fs_inline_data_size(ctx->fs, pctx->ino, &size))
+ return;
+ /* device files never have a "system.data" entry */
+ goto isdir;
+ } else if (extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) {
/* extent mapped */
if (ext2fs_bmap2(ctx->fs, pctx->ino, inode, 0, 0, 0, 0,
&blk))
(rec_len % 4))
return;
+isdir:
if (fix_problem(ctx, PR_1_TREAT_AS_DIRECTORY, pctx)) {
inode->i_mode = (inode->i_mode & 07777) | LINUX_S_IFDIR;
e2fsck_write_inode_full(ctx, pctx->ino, inode,
struct ext2_super_block *sb = ctx->fs->super;
const char *old_op;
unsigned int save_type;
- int imagic_fs, extent_fs;
+ int imagic_fs, extent_fs, inlinedata_fs;
int busted_fs_time = 0;
int inode_size;
int failed_csum = 0;
imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
extent_fs = (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS);
+ inlinedata_fs = (sb->s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA);
/*
* Allocate bitmaps structures
}
}
+ /* Test for incorrect inline_data flags settings. */
+ if ((inode->i_flags & EXT4_INLINE_DATA_FL) && !inlinedata_fs &&
+ (ino >= EXT2_FIRST_INODE(fs->super))) {
+ size_t size = 0;
+
+ pctx.errcode = ext2fs_inline_data_size(fs, ino, &size);
+ if (!pctx.errcode && size &&
+ !fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) {
+ sb->s_feature_incompat |=
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA;
+ ext2fs_mark_super_dirty(fs);
+ inlinedata_fs = 1;
+ } else if (!fix_problem(ctx, PR_1_INLINE_DATA_SET, &pctx)) {
+ e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
+ continue;
+ }
+ }
+
/*
* Test for incorrect extent flag settings.
*
ctx->fs_sockets_count++;
} else
mark_inode_bad(ctx, ino);
- if (!(inode->i_flags & EXT4_EXTENTS_FL)) {
+ if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
+ !(inode->i_flags & EXT4_INLINE_DATA_FL)) {
if (inode->i_block[EXT2_IND_BLOCK])
ctx->fs_ind_count++;
if (inode->i_block[EXT2_DIND_BLOCK])
ctx->fs_tind_count++;
}
if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
+ !(inode->i_flags & EXT4_INLINE_DATA_FL) &&
(inode->i_block[EXT2_IND_BLOCK] ||
inode->i_block[EXT2_DIND_BLOCK] ||
inode->i_block[EXT2_TIND_BLOCK] ||
}
/*
+ * In fact we don't need to check blocks for an inode with inline data
+ * because this inode doesn't have any blocks. In this function all
+ * we need to do is add this inode into dblist when it is a directory.
+ */
+static void check_blocks_inline_data(e2fsck_t ctx, struct problem_context *pctx,
+ struct process_block_struct *pb)
+{
+ if (!pb->is_dir)
+ return;
+
+ pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist, pb->ino, 0, 0);
+ if (pctx->errcode) {
+ pctx->blk = 0;
+ pctx->num = 0;
+ fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ }
+}
+
+/*
* This subroutine is called on each inode to account for all of the
* blocks used by that inode.
*/
unsigned bad_size = 0;
int dirty_inode = 0;
int extent_fs;
+ int inlinedata_fs;
__u64 size;
pb.ino = ino;
extent_fs = (ctx->fs->super->s_feature_incompat &
EXT3_FEATURE_INCOMPAT_EXTENTS);
+ inlinedata_fs = (ctx->fs->super->s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA);
if (inode->i_flags & EXT2_COMPRBLK_FL) {
if (fs->super->s_feature_incompat &
*/
pb.last_init_lblock = pb.last_block;
}
+ } else {
+ /* check inline data */
+ if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL))
+ check_blocks_inline_data(ctx, pctx, &pb);
}
end_problem_latch(ctx, PR_LATCH_BLOCK);
end_problem_latch(ctx, PR_LATCH_TOOBIG);
}
}
- if (!pb.num_blocks && pb.is_dir) {
+ if (!pb.num_blocks && pb.is_dir &&
+ !(inode->i_flags & EXT4_INLINE_DATA_FL)) {
if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
e2fsck_clear_inode(ctx, ino, inode, 0, "check_blocks");
ctx->fs_directory_count--;
#endif
if (pb.is_dir) {
int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
- if (inode->i_size & (fs->blocksize - 1))
+ if (inode->i_flags & EXT4_INLINE_DATA_FL) {
+ size_t size;
+
+ if (ext2fs_inline_data_size(ctx->fs, pctx->ino, &size))
+ bad_size = 5;
+ if (size != inode->i_size)
+ bad_size = 5;
+ } else if (inode->i_size & (fs->blocksize - 1))
bad_size = 5;
else if (nblock > (pb.last_block + 1))
bad_size = 1;
}
}
+static int is_last_entry(ext2_filsys fs, int inline_data_size,
+ unsigned int offset, int csum_size)
+{
+ if (inline_data_size)
+ return (offset < inline_data_size);
+ else
+ return (offset < fs->blocksize - csum_size);
+}
+
static int check_dir_block(ext2_filsys fs,
struct ext2_db_entry2 *db,
void *priv_data)
#ifdef ENABLE_HTREE
struct dx_dirblock_info *dx_db = 0;
#endif /* ENABLE_HTREE */
- struct ext2_dir_entry *dirent, *prev;
+ struct ext2_dir_entry *dirent, *prev, dot, dotdot;
ext2_dirhash_t hash;
unsigned int offset = 0;
int dir_modified = 0;
int dx_csum_size = 0, de_csum_size = 0;
int failed_csum = 0;
int is_leaf = 1;
+ int inline_data_size = 0;
+ int filetype = 0;
cd = (struct check_dir_struct *) priv_data;
buf = cd->buf;
de_csum_size = sizeof(struct ext2_dir_entry_tail);
}
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ filetype = EXT2_FT_DIR << 8;
+
/*
* Make sure the inode is still in use (could have been
* deleted in the duplicate/bad blocks pass.
cd->pctx.dirent = 0;
cd->pctx.num = 0;
- if (db->blk == 0) {
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ errcode_t ec;
+
+ ec = ext2fs_inline_data_size(fs, ino, &inline_data_size);
+ if (ec && ec != EXT2_ET_NO_INLINE_DATA)
+ return DIRENT_ABORT;
+ }
+
+ if (db->blk == 0 && !inline_data_size) {
if (allocate_dir_block(ctx, db, buf, &cd->pctx))
return 0;
block_nr = db->blk;
#endif
ehandler_operation(_("reading directory block"));
- cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr, buf, 0, ino);
+ if (inline_data_size)
+ cd->pctx.errcode = ext2fs_inline_data_get(fs, ino, 0, buf, 0);
+ else
+ cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr,
+ buf, 0, ino);
ehandler_operation(0);
if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
cd->pctx.errcode = 0; /* We'll handle this ourselves */
#endif /* ENABLE_HTREE */
/* Verify checksum. */
- if (is_leaf && de_csum_size) {
+ if (is_leaf && de_csum_size && !inline_data_size) {
/* No space for csum? Rebuild dirs in pass 3A. */
if (!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
de_csum_size = 0;
unsigned int name_len;
problem = 0;
- dirent = (struct ext2_dir_entry *) (buf + offset);
- (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
- cd->pctx.dirent = dirent;
- cd->pctx.num = offset;
- if (((offset + rec_len) > fs->blocksize) ||
- (rec_len < 12) ||
- ((rec_len % 4) != 0) ||
- ((ext2fs_dirent_name_len(dirent) + 8) > rec_len)) {
- if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
- salvage_directory(fs, dirent, prev, &offset);
- dir_modified++;
- continue;
- } else
- goto abort_free_dict;
+ if (!inline_data_size || dot_state > 1) {
+ dirent = (struct ext2_dir_entry *) (buf + offset);
+ (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
+ cd->pctx.dirent = dirent;
+ cd->pctx.num = offset;
+ if (((offset + rec_len) > fs->blocksize) ||
+ (rec_len < 12) ||
+ ((rec_len % 4) != 0) ||
+ ((ext2fs_dirent_name_len(dirent) + 8) > rec_len)) {
+ if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
+ salvage_directory(fs, dirent, prev, &offset);
+ dir_modified++;
+ continue;
+ } else
+ goto abort_free_dict;
+ }
+ } else {
+ if (dot_state == 0) {
+ memset(&dot, 0, sizeof(dot));
+ dirent = ˙
+ dirent->inode = ino;
+ dirent->rec_len = EXT2_DIR_REC_LEN(1);
+ dirent->name_len = 1 | filetype;
+ dirent->name[0] = '.';
+ } else if (dot_state == 1) {
+ memset(&dotdot, 0, sizeof(dotdot));
+ dirent = &dotdot;
+ dirent->inode =
+ ((struct ext2_dir_entry *)buf)->inode;
+ dirent->rec_len = EXT2_DIR_REC_LEN(2);
+ dirent->name_len = 2 | filetype;
+ dirent->name[0] = '.';
+ dirent->name[1] = '.';
+ } else {
+ fatal_error(ctx, _("Can not continue."));
+ }
+ cd->pctx.dirent = dirent;
+ cd->pctx.num = offset;
}
if (dot_state == 0) {
prev = dirent;
if (dir_modified)
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
- offset += rec_len;
+ if (!inline_data_size || dot_state > 1) {
+ offset += rec_len;
+ } else {
+ if (dot_state == 1)
+ offset = 4;
+ }
dot_state++;
- } while (offset < fs->blocksize - de_csum_size);
+ } while (is_last_entry(fs, inline_data_size, offset, de_csum_size));
#if 0
printf("\n");
#endif
}
#endif /* ENABLE_HTREE */
- if (offset != fs->blocksize - de_csum_size) {
- cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
- offset;
- if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
- dirent->rec_len = cd->pctx.num;
- dir_modified++;
+ if (inline_data_size) {
+ if (offset != inline_data_size) {
+ cd->pctx.num = rec_len + offset - inline_data_size;
+ if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
+ dirent->rec_len = cd->pctx.num;
+ dir_modified++;
+ }
+ }
+ } else {
+ if (offset != fs->blocksize - de_csum_size) {
+ cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
+ offset;
+ if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
+ dirent->rec_len = cd->pctx.num;
+ dir_modified++;
+ }
}
}
if (dir_modified) {
write_and_fix:
if (e2fsck_dir_will_be_rehashed(ctx, ino))
ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
- cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr, buf,
- 0, ino);
+ if (inline_data_size) {
+ cd->pctx.errcode =
+ ext2fs_inline_data_set(fs, ino, 0, buf,
+ inline_data_size);
+ } else
+ cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr,
+ buf, 0, ino);
if (e2fsck_dir_will_be_rehashed(ctx, ino))
ctx->fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
if (cd->pctx.errcode) {
if (retval && !fix)
return 0;
if (!retval) {
+ /* Lost+found shouldn't have inline data */
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (fix && retval)
+ return 0;
+
+ if (fix && (inode.i_flags & EXT4_INLINE_DATA_FL)) {
+ if (!fix_problem(ctx, PR_3_LPF_INLINE_DATA, &pctx))
+ return 0;
+ goto unlink;
+ }
+
if (ext2fs_check_directory(fs, ino) == 0) {
ctx->lost_and_found = ino;
return ino;
if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
return 0;
+unlink:
/* OK, unlink the old /lost+found file. */
pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
if (pctx.errcode) {
N_("@i %i, end of extent exceeds allowed value\n\t(logical @b %c, physical @b %b, len %N)\n"),
PROMPT_CLEAR, 0 },
+ /* Inode has inline data, but superblock is missing INLINE_DATA feature. */
+ { PR_1_INLINE_DATA_FEATURE,
+ N_("@i %i has inline data, but @S is missing INLINE_DATA feature\n"),
+ PROMPT_CLEAR, PR_PREEN_OK },
+
+ /* INLINE_DATA feature is set in a non-inline-data filesystem */
+ { PR_1_INLINE_DATA_SET,
+ N_("@i %i has INLINE_DATA_FL flag on @f without inline data support.\n"),
+ PROMPT_CLEAR, 0 },
/* Pass 1b errors */
N_("/@l is not a @d (ino=%i)\n"),
PROMPT_UNLINK, 0 },
+ /* Lost+found has inline data */
+ { PR_3_LPF_INLINE_DATA,
+ N_("/@l has inline data\n"),
+ PROMPT_CLEAR, 0 },
+
/* Pass 3A Directory Optimization */
/* Pass 3A: Optimizing directories */
#define PR_1_EXTENT_INDEX_START_INVALID 0x01006D
#define PR_1_EXTENT_END_OUT_OF_BOUNDS 0x01006E
+
+/* Inode has inline data, but superblock is missing INLINE_DATA feature. */
+#define PR_1_INLINE_DATA_FEATURE 0x01006F
+
+/* INLINE_DATA feature is set in a non-inline-data filesystem */
+#define PR_1_INLINE_DATA_SET 0x010070
+
/*
* Pass 1b errors
*/
/* Lost+found is not a directory */
#define PR_3_LPF_NOTDIR 0x030017
+/* Lost+found has inline data */
+#define PR_3_LPF_INLINE_DATA 0x030018
+
/*
* Pass 3a --- rehashing diretories
*/
/* Read in the entire directory into memory */
retval = ext2fs_block_iterate3(fs, ino, 0, 0,
fill_dir_block, &fd);
+ if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE)
+ goto errout;
if (fd.err) {
retval = fd.err;
goto errout;
return retval;
*ret_ctx = ctx;
+ e2fsck_global_ctx = ctx;
setvbuf(stdout, NULL, _IONBF, BUFSIZ);
setvbuf(stderr, NULL, _IONBF, BUFSIZ);
#ifdef SA_RESTART
sa.sa_flags = SA_RESTART;
#endif
- e2fsck_global_ctx = ctx;
sa.sa_handler = signal_progress_on;
sigaction(SIGUSR1, &sa, 0);
sa.sa_handler = signal_progress_off;
retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
flags, 0, 0, io_ptr, ret_fs);
- if (retval == 0)
+ if (retval == 0) {
+ (*ret_fs)->priv_data = ctx;
e2fsck_set_bitmap_type(*ret_fs, EXT2FS_BMAP64_RBTREE,
"default", NULL);
+ }
return retval;
}
}
ctx->fs = fs;
- fs->priv_data = ctx;
fs->now = ctx->now;
sb = fs->super;
const char *profile_name, unsigned int *old_type)
{
unsigned type;
+ e2fsck_t ctx = (e2fsck_t) fs->priv_data;
if (old_type)
*old_type = fs->default_bitmap_type;
- profile_get_uint(e2fsck_global_ctx->profile, "bitmaps",
- profile_name, 0, default_type, &type);
- profile_get_uint(e2fsck_global_ctx->profile, "bitmaps",
- "all", 0, type, &type);
+ profile_get_uint(ctx->profile, "bitmaps", profile_name, 0,
+ default_type, &type);
+ profile_get_uint(ctx->profile, "bitmaps", "all", 0, type, &type);
fs->default_bitmap_type = type ? type : default_type;
}
ind_block.o \
initialize.o \
inline.o \
+ inline_data.o \
inode.o \
io_manager.o \
ismounted.o \
$(srcdir)/ind_block.c \
$(srcdir)/initialize.c \
$(srcdir)/inline.c \
+ $(srcdir)/inline_data.c \
$(srcdir)/inode.c \
$(srcdir)/inode_io.c \
$(srcdir)/imager.c \
$(Q) $(CC) -o tst_inline $(srcdir)/inline.c $(ALL_CFLAGS) -DDEBUG \
$(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) $(SYSLIBS)
+tst_inline_data: inline_data.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
+ $(E) " LD $@"
+ $(Q) $(CC) -o tst_inline_data $(srcdir)/inline_data.c $(ALL_CFLAGS) \
+ -DDEBUG $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR)
+
tst_csum: csum.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR) $(STATIC_LIBE2P) \
$(top_srcdir)/lib/e2p/e2p.h
$(E) " LD $@"
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 tst_inline_data
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_bitops
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_badblocks
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_iscan
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_inode_size
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_csum
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_inline
+ LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_inline_data
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_crc32c
LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) \
./tst_bitmaps -f $(srcdir)/tst_bitmaps_cmds > tst_bitmaps_out
$(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
+inline_data.o: $(srcdir)/inline_data.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
inode.o: $(srcdir)/inode.c $(top_builddir)/lib/config.h \
$(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
$(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
icount.obj \
initialize.obj \
inline.obj \
+ inline_data.obj \
inode.obj \
ismounted.obj \
link.obj \
if (ext2fs_file_block_offset_too_big(fs, inode, block))
return EXT2_ET_FILE_TOO_BIG;
+ /*
+ * If an inode has inline data, that means that it doesn't have
+ * any blocks and we shouldn't map any blocks for it.
+ */
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ return EXT2_ET_INLINE_DATA_NO_BLOCK;
+
if (!block_buf) {
retval = ext2fs_get_array(2, fs->blocksize, &buf);
if (retval)
static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info,
void *priv_data)
{
+ struct ext2_inode inode;
struct dir_context *ctx;
int ret;
ctx->dir = db_info->ino;
ctx->errcode = 0;
- ret = ext2fs_process_dir_block(fs, &db_info->blk,
- db_info->blockcnt, 0, 0, priv_data);
+ ctx->errcode = ext2fs_read_inode(fs, ctx->dir, &inode);
+ if (ctx->errcode)
+ return DBLIST_ABORT;
+ if (inode.i_flags & EXT4_INLINE_DATA_FL) {
+ ctx->flags = DIRENT_FLAG_INCLUDE_INLINE_DATA;
+ ret = ext2fs_inline_data_dir_iterate(fs, ctx->dir, ctx);
+ } else {
+ ret = ext2fs_process_dir_block(fs, &db_info->blk,
+ db_info->blockcnt, 0, 0,
+ priv_data);
+ }
if ((ret & BLOCK_ABORT) && !ctx->errcode)
return DBLIST_ABORT;
return 0;
ext2fs_process_dir_block, &ctx);
if (!block_buf)
ext2fs_free_mem(&ctx.buf);
+ if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE) {
+ ctx.flags |= DIRENT_FLAG_INCLUDE_INLINE_DATA;
+ (void) ext2fs_inline_data_dir_iterate(fs, dir, &ctx);
+ retval = 0;
+ }
if (retval)
return retval;
return ctx.errcode;
int ret = 0;
int changed = 0;
int do_abort = 0;
- unsigned int rec_len, size;
+ unsigned int rec_len, size, buflen;
int entry;
struct ext2_dir_entry *dirent;
int csum_size = 0;
+ int inline_data;
+ errcode_t retval = 0;
if (blockcnt < 0)
return 0;
entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
- ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
- ctx->dir);
- if (ctx->errcode)
- return BLOCK_ABORT;
+ /* If a dir has inline data, we don't need to read block */
+ inline_data = !!(ctx->flags & DIRENT_FLAG_INCLUDE_INLINE_DATA);
+ if (!inline_data) {
+ ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
+ ctx->dir);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ /* If we handle a normal dir, we traverse the entire block */
+ buflen = fs->blocksize;
+ } else {
+ buflen = ctx->buflen;
+ }
if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
csum_size = sizeof(struct ext2_dir_entry_tail);
- while (offset < fs->blocksize) {
+ while (offset < buflen) {
dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
if (ext2fs_get_rec_len(fs, dirent, &rec_len))
return BLOCK_ABORT;
- if (((offset + rec_len) > fs->blocksize) ||
+ if (((offset + rec_len) > buflen) ||
(rec_len < 8) ||
((rec_len % 4) != 0) ||
((ext2fs_dirent_name_len(dirent)+8) > rec_len)) {
return BLOCK_ABORT;
}
if (!dirent->inode) {
- if ((offset == fs->blocksize - csum_size) &&
+ /*
+ * We just need to check metadata_csum when this
+ * dir hasn't inline data. That means that 'buflen'
+ * should be blocksize.
+ */
+ if (!inline_data &&
+ (offset == buflen - csum_size) &&
(dirent->rec_len == csum_size) &&
(dirent->name_len == EXT2_DIR_NAME_LEN_CSUM)) {
if (!(ctx->flags & DIRENT_FLAG_INCLUDE_CSUM))
(next_real_entry > offset) ?
DIRENT_DELETED_FILE : entry,
dirent, offset,
- fs->blocksize, ctx->buf,
+ buflen, ctx->buf,
ctx->priv_data);
if (entry < DIRENT_OTHER_FILE)
entry++;
}
if (changed) {
- ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
- 0, ctx->dir);
- if (ctx->errcode)
- return BLOCK_ABORT;
+ if (!inline_data) {
+ ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr,
+ ctx->buf,
+ 0, ctx->dir);
+ if (ctx->errcode)
+ return BLOCK_ABORT;
+ } else {
+ /*
+ * return BLOCK_INLINE_DATA_CHANGED to notify caller
+ * that inline data has been changed.
+ */
+ retval = BLOCK_INLINE_DATA_CHANGED;
+ }
}
if (do_abort)
- return BLOCK_ABORT;
- return 0;
+ return retval | BLOCK_ABORT;
+ return retval;
}
-
retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_APPEND,
0, expand_dir_proc, &es);
+ if (retval == EXT2_ET_INLINE_DATA_CANT_ITERATE)
+ return ext2fs_inline_data_expand(fs, dir);
if (es.err)
return es.err;
ec EXT2_ET_INLINE_DATA_CANT_ITERATE,
"Cannot iterate data blocks of an inode containing inline data"
+ec EXT2_ET_EA_BAD_NAME_LEN,
+ "Extended attribute has an invalid name length"
+
+ec EXT2_ET_EA_BAD_VALUE_SIZE,
+ "Extended attribute has an invalid value length"
+
+ec EXT2_ET_BAD_EA_HASH,
+ "Extended attribute has an incorrect hash"
+
+ec EXT2_ET_BAD_EA_HEADER,
+ "Extended attribute block has a bad header"
+
+ec EXT2_ET_EA_KEY_NOT_FOUND,
+ "Extended attribute key not found"
+
+ec EXT2_ET_EA_NO_SPACE,
+ "Insufficient space to store extended attribute data"
+
+ec EXT2_ET_MISSING_EA_FEATURE,
+ "Filesystem is missing ext_attr or inline_data feature"
+
+ec EXT2_ET_NO_INLINE_DATA,
+ "Inode doesn't have inline data"
+
+ec EXT2_ET_INLINE_DATA_NO_BLOCK,
+ "No block for an inode with inline data"
+
+ec EXT2_ET_INLINE_DATA_NO_SPACE,
+ "No free space in inline data"
+
end
*/
#define EXT4_MMP_MIN_CHECK_INTERVAL 5
+/*
+ * Minimum size of inline data.
+ */
+#define EXT4_MIN_INLINE_DATA_SIZE ((sizeof(__u32) * EXT2_N_BLOCKS))
+
+/*
+ * Size of a parent inode in inline data directory.
+ */
+#define EXT4_INLINE_DATA_DOTDOT_SIZE (4)
+
#endif /* _LINUX_EXT2_FS_H */
/*
* Return flags for the block iterator functions
*/
-#define BLOCK_CHANGED 1
-#define BLOCK_ABORT 2
-#define BLOCK_ERROR 4
+#define BLOCK_CHANGED 1
+#define BLOCK_ABORT 2
+#define BLOCK_ERROR 4
+#define BLOCK_INLINE_DATA_CHANGED 8
/*
* Block interate flags
#define DIRENT_FLAG_INCLUDE_EMPTY 1
#define DIRENT_FLAG_INCLUDE_REMOVED 2
#define DIRENT_FLAG_INCLUDE_CSUM 4
+#define DIRENT_FLAG_INCLUDE_INLINE_DATA 8
#define DIRENT_DOT_FILE 1
#define DIRENT_DOT_DOT_FILE 2
EXT3_FEATURE_INCOMPAT_EXTENTS|\
EXT4_FEATURE_INCOMPAT_FLEX_BG|\
EXT4_LIB_INCOMPAT_MMP|\
- EXT4_FEATURE_INCOMPAT_64BIT)
+ EXT4_FEATURE_INCOMPAT_64BIT|\
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)
#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
#define EXT2_FLAG_FLUSH_NO_SYNC 1
/*
+ * Modify and iterate extended attributes
+ */
+struct ext2_xattr_handle;
+#define XATTR_ABORT 1
+#define XATTR_CHANGED 2
+
+/*
* function prototypes
*/
static inline int ext2fs_has_group_desc_csum(ext2_filsys fs)
char *block_buf,
int adjust, __u32 *newcount,
ext2_ino_t inum);
+errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle);
+errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle);
+errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
+ int (*func)(char *name, char *value,
+ size_t value_len, void *data),
+ void *data);
+errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
+ void **value, size_t *value_len);
+errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
+ const char *key,
+ const void *value,
+ size_t value_len);
+errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
+ const char *key);
+errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_xattr_handle **handle);
+errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle);
+errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode);
+size_t ext2fs_xattrs_count(struct ext2_xattr_handle *handle);
+errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
+ size_t *size);
/* extent.c */
extern errcode_t ext2fs_extent_header_verify(void *ptr, int size);
unsigned long align, void *ptr);
/* inode.c */
+extern errcode_t ext2fs_create_inode_cache(ext2_filsys fs,
+ unsigned int cache_size);
extern void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
/* newdir.c */
extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
ext2_ino_t parent_ino, char **block);
+extern errcode_t ext2fs_new_dir_inline_data(ext2_filsys fs, ext2_ino_t dir_ino,
+ ext2_ino_t parent_ino, __u32 *iblock);
/* mkdir.c */
extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
/* swapfs.c */
+extern errcode_t ext2fs_dirent_swab_in2(ext2_filsys fs, char *buf, size_t size,
+ int flags);
extern errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags);
+extern errcode_t ext2fs_dirent_swab_out2(ext2_filsys fs, char *buf, size_t size,
+ int flags);
extern errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags);
extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
int has_header);
ext2_ino_t dir;
int flags;
char *buf;
+ unsigned int buflen;
int (*func)(ext2_ino_t dir,
int entry,
struct ext2_dir_entry *dirent,
void * buffer;
blk64_t buffer_blk;
int cache_last;
- int cache_size;
+ unsigned int cache_size;
int refcount;
struct ext2_inode_cache_ent *cache;
};
int ref_offset,
void *priv_data);
+extern errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino);
+extern errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
+ size_t *size);
+extern errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino);
+extern errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino);
+extern int ext2fs_inline_data_dir_iterate(ext2_filsys fs,
+ ext2_ino_t ino,
+ void *priv_data);
+extern errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t *size);
+extern errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t size);
+
/* Generic numeric progress meter */
struct ext2fs_numeric_progress_struct {
return ext2fs_adjust_ea_refcount2(fs, blk, block_buf, adjust,
newcount);
}
+
+/* Manipulate the contents of extended attribute regions */
+struct ext2_xattr {
+ char *name;
+ void *value;
+ size_t value_len;
+};
+
+struct ext2_xattr_handle {
+ ext2_filsys fs;
+ struct ext2_xattr *attrs;
+ size_t length, count;
+ ext2_ino_t ino;
+ int dirty;
+};
+
+static errcode_t ext2fs_xattrs_expand(struct ext2_xattr_handle *h,
+ unsigned int expandby)
+{
+ struct ext2_xattr *new_attrs;
+ errcode_t err;
+
+ err = ext2fs_get_arrayzero(h->length + expandby,
+ sizeof(struct ext2_xattr), &new_attrs);
+ if (err)
+ return err;
+
+ memcpy(new_attrs, h->attrs, h->length * sizeof(struct ext2_xattr));
+ ext2fs_free_mem(&h->attrs);
+ h->length += expandby;
+ h->attrs = new_attrs;
+
+ return 0;
+}
+
+struct ea_name_index {
+ int index;
+ const char *name;
+};
+
+/* Keep these names sorted in order of decreasing specificity. */
+static struct ea_name_index ea_names[] = {
+ {3, "system.posix_acl_default"},
+ {2, "system.posix_acl_access"},
+ {8, "system.richacl"},
+ {6, "security."},
+ {4, "trusted."},
+ {7, "system."},
+ {1, "user."},
+ {0, NULL},
+};
+
+static const char *find_ea_prefix(int index)
+{
+ struct ea_name_index *e;
+
+ for (e = ea_names; e->name; e++)
+ if (e->index == index)
+ return e->name;
+
+ return NULL;
+}
+
+static int find_ea_index(const char *fullname, char **name, int *index)
+{
+ struct ea_name_index *e;
+
+ for (e = ea_names; e->name; e++) {
+ if (memcmp(fullname, e->name, strlen(e->name)) == 0) {
+ *name = (char *)fullname + strlen(e->name);
+ *index = e->index;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+errcode_t ext2fs_free_ext_attr(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode)
+{
+ struct ext2_ext_attr_header *header;
+ void *block_buf = NULL;
+ dgrp_t grp;
+ blk64_t blk, goal;
+ errcode_t err;
+ struct ext2_inode_large i;
+
+ /* Read inode? */
+ if (inode == NULL) {
+ err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&i,
+ sizeof(struct ext2_inode_large));
+ if (err)
+ return err;
+ inode = &i;
+ }
+
+ /* Do we already have an EA block? */
+ blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
+ if (blk == 0)
+ return 0;
+
+ /* Find block, zero it, write back */
+ if ((blk < fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out2;
+ }
+
+ header->h_refcount--;
+ err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ /* Erase link to block */
+ ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, 0);
+ if (header->h_refcount == 0)
+ ext2fs_block_alloc_stats2(fs, blk, -1);
+ err = ext2fs_iblk_sub_blocks(fs, (struct ext2_inode *)inode, 1);
+ if (err)
+ goto out2;
+
+ /* Write inode? */
+ if (inode == &i) {
+ err = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&i,
+ sizeof(struct ext2_inode_large));
+ if (err)
+ goto out2;
+ }
+
+out2:
+ ext2fs_free_mem(&block_buf);
+out:
+ return err;
+}
+
+static errcode_t prep_ea_block_for_write(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode_large *inode)
+{
+ struct ext2_ext_attr_header *header;
+ void *block_buf = NULL;
+ dgrp_t grp;
+ blk64_t blk, goal;
+ errcode_t err;
+
+ /* Do we already have an EA block? */
+ blk = ext2fs_file_acl_block(fs, (struct ext2_inode *)inode);
+ if (blk != 0) {
+ if ((blk < fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out2;
+ }
+
+ /* Single-user block. We're done here. */
+ if (header->h_refcount == 1)
+ goto out2;
+
+ /* We need to CoW the block. */
+ header->h_refcount--;
+ err = ext2fs_write_ext_attr3(fs, blk, block_buf, ino);
+ if (err)
+ goto out2;
+ } else {
+ /* No block, we must increment i_blocks */
+ err = ext2fs_iblk_add_blocks(fs, (struct ext2_inode *)inode,
+ 1);
+ if (err)
+ goto out;
+ }
+
+ /* Allocate a block */
+ grp = ext2fs_group_of_ino(fs, ino);
+ goal = ext2fs_inode_table_loc(fs, grp);
+ err = ext2fs_alloc_block2(fs, goal, NULL, &blk);
+ if (err)
+ goto out2;
+ ext2fs_file_acl_block_set(fs, (struct ext2_inode *)inode, blk);
+out2:
+ if (block_buf)
+ ext2fs_free_mem(&block_buf);
+out:
+ return err;
+}
+
+
+static errcode_t write_xattrs_to_buffer(struct ext2_xattr_handle *handle,
+ struct ext2_xattr **pos,
+ void *entries_start,
+ unsigned int storage_size,
+ unsigned int value_offset_correction)
+{
+ struct ext2_xattr *x = *pos;
+ struct ext2_ext_attr_entry *e = entries_start;
+ void *end = entries_start + storage_size;
+ char *shortname;
+ unsigned int entry_size, value_size;
+ int idx, ret;
+
+ /* For all remaining x... */
+ for (; x < handle->attrs + handle->length; x++) {
+ if (!x->name)
+ continue;
+
+ /* Calculate index and shortname position */
+ shortname = x->name;
+ ret = find_ea_index(x->name, &shortname, &idx);
+
+ /* Calculate entry and value size */
+ entry_size = (sizeof(*e) + strlen(shortname) +
+ EXT2_EXT_ATTR_PAD - 1) &
+ ~(EXT2_EXT_ATTR_PAD - 1);
+ value_size = ((x->value_len + EXT2_EXT_ATTR_PAD - 1) /
+ EXT2_EXT_ATTR_PAD) * EXT2_EXT_ATTR_PAD;
+
+ /*
+ * Would entry collide with value?
+ * Note that we must leave sufficient room for a (u32)0 to
+ * mark the end of the entries.
+ */
+ if ((void *)e + entry_size + sizeof(__u32) > end - value_size)
+ break;
+
+ /* Fill out e appropriately */
+ e->e_name_len = strlen(shortname);
+ e->e_name_index = (ret ? idx : 0);
+ e->e_value_offs = end - value_size - (void *)entries_start +
+ value_offset_correction;
+ e->e_value_block = 0;
+ e->e_value_size = x->value_len;
+
+ /* Store name and value */
+ end -= value_size;
+ memcpy((void *)e + sizeof(*e), shortname, e->e_name_len);
+ memcpy(end, x->value, e->e_value_size);
+
+ e->e_hash = ext2fs_ext_attr_hash_entry(e, end);
+
+ e = EXT2_EXT_ATTR_NEXT(e);
+ *(__u32 *)e = 0;
+ }
+ *pos = x;
+
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_write(struct ext2_xattr_handle *handle)
+{
+ struct ext2_xattr *x;
+ struct ext2_inode_large *inode;
+ void *start, *block_buf = NULL;
+ struct ext2_ext_attr_header *header;
+ __u32 ea_inode_magic;
+ blk64_t blk;
+ unsigned int storage_size;
+ unsigned int i;
+ errcode_t err;
+
+ i = EXT2_INODE_SIZE(handle->fs->super);
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(handle->fs, handle->ino,
+ (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(handle->fs->super));
+ if (err)
+ goto out;
+
+ x = handle->attrs;
+ /* Does the inode have size for EA? */
+ if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize +
+ sizeof(__u32))
+ goto write_ea_block;
+
+ /* Write the inode EA */
+ ea_inode_magic = EXT2_EXT_ATTR_MAGIC;
+ memcpy(((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, &ea_inode_magic, sizeof(__u32));
+ storage_size = EXT2_INODE_SIZE(handle->fs->super) -
+ EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
+ sizeof(__u32);
+ start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+
+ err = write_xattrs_to_buffer(handle, &x, start, storage_size, 0);
+ if (err)
+ goto out;
+
+ /* Are we done? */
+ if (x == handle->attrs + handle->length)
+ goto skip_ea_block;
+
+write_ea_block:
+ /* Write the EA block */
+ err = ext2fs_get_memzero(handle->fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ storage_size = handle->fs->blocksize -
+ sizeof(struct ext2_ext_attr_header);
+ start = block_buf + sizeof(struct ext2_ext_attr_header);
+
+ err = write_xattrs_to_buffer(handle, &x, start, storage_size,
+ (void *)start - block_buf);
+ if (err)
+ goto out2;
+
+ if (x < handle->attrs + handle->length) {
+ err = EXT2_ET_EA_NO_SPACE;
+ goto out2;
+ }
+
+ /* Write a header on the EA block */
+ header = block_buf;
+ header->h_magic = EXT2_EXT_ATTR_MAGIC;
+ header->h_refcount = 1;
+ header->h_blocks = 1;
+
+ /* Get a new block for writing */
+ err = prep_ea_block_for_write(handle->fs, handle->ino, inode);
+ if (err)
+ goto out2;
+
+ /* Finally, write the new EA block */
+ blk = ext2fs_file_acl_block(handle->fs,
+ (struct ext2_inode *)inode);
+ err = ext2fs_write_ext_attr3(handle->fs, blk, block_buf,
+ handle->ino);
+ if (err)
+ goto out2;
+
+skip_ea_block:
+ blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
+ if (!block_buf && blk) {
+ /* xattrs shrunk, free the block */
+ err = ext2fs_free_ext_attr(handle->fs, handle->ino, inode);
+ if (err)
+ goto out;
+ }
+
+ /* Write the inode */
+ err = ext2fs_write_inode_full(handle->fs, handle->ino,
+ (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(handle->fs->super));
+ if (err)
+ goto out2;
+
+out2:
+ ext2fs_free_mem(&block_buf);
+out:
+ ext2fs_free_mem(&inode);
+ handle->dirty = 0;
+ return err;
+}
+
+static errcode_t read_xattrs_from_buffer(struct ext2_xattr_handle *handle,
+ struct ext2_ext_attr_entry *entries,
+ unsigned int storage_size,
+ void *value_start,
+ size_t *nr_read)
+{
+ struct ext2_xattr *x;
+ struct ext2_ext_attr_entry *entry;
+ const char *prefix;
+ void *ptr;
+ unsigned int remain, prefix_len;
+ errcode_t err;
+
+ x = handle->attrs;
+ while (x->name)
+ x++;
+
+ entry = entries;
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ __u32 hash;
+
+ /* header eats this space */
+ remain -= sizeof(struct ext2_ext_attr_entry);
+
+ /* is attribute name valid? */
+ if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain)
+ return EXT2_ET_EA_BAD_NAME_LEN;
+
+ /* attribute len eats this space */
+ remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
+
+ /* check value size */
+ if (entry->e_value_size > remain)
+ return EXT2_ET_EA_BAD_VALUE_SIZE;
+
+ /* e_value_block must be 0 in inode's ea */
+ if (entry->e_value_block != 0)
+ return EXT2_ET_BAD_EA_BLOCK_NUM;
+
+ hash = ext2fs_ext_attr_hash_entry(entry, value_start +
+ entry->e_value_offs);
+
+ /* e_hash may be 0 in older inode's ea */
+ if (entry->e_hash != 0 && entry->e_hash != hash)
+ return EXT2_ET_BAD_EA_HASH;
+
+ remain -= entry->e_value_size;
+
+ /* Allocate space for more attrs? */
+ if (x == handle->attrs + handle->length) {
+ err = ext2fs_xattrs_expand(handle, 4);
+ if (err)
+ return err;
+ x = handle->attrs + handle->length - 4;
+ }
+
+ /* Extract name/value */
+ prefix = find_ea_prefix(entry->e_name_index);
+ prefix_len = (prefix ? strlen(prefix) : 0);
+ err = ext2fs_get_memzero(entry->e_name_len + prefix_len + 1,
+ &x->name);
+ if (err)
+ return err;
+ if (prefix)
+ memcpy(x->name, prefix, prefix_len);
+ if (entry->e_name_len)
+ memcpy(x->name + prefix_len,
+ (void *)entry + sizeof(*entry),
+ entry->e_name_len);
+
+ err = ext2fs_get_mem(entry->e_value_size, &x->value);
+ if (err)
+ return err;
+ x->value_len = entry->e_value_size;
+ memcpy(x->value, value_start + entry->e_value_offs,
+ entry->e_value_size);
+ x++;
+ (*nr_read)++;
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+
+ return 0;
+}
+
+static void xattrs_free_keys(struct ext2_xattr_handle *h)
+{
+ struct ext2_xattr *a = h->attrs;
+ size_t i;
+
+ for (i = 0; i < h->length; i++) {
+ if (a[i].name)
+ ext2fs_free_mem(&a[i].name);
+ if (a[i].value)
+ ext2fs_free_mem(&a[i].value);
+ }
+ h->count = 0;
+}
+
+errcode_t ext2fs_xattrs_read(struct ext2_xattr_handle *handle)
+{
+ struct ext2_xattr *attrs = NULL, *x;
+ struct ext2_inode_large *inode;
+ struct ext2_ext_attr_header *header;
+ __u32 ea_inode_magic;
+ unsigned int storage_size;
+ void *start, *block_buf = NULL;
+ blk64_t blk;
+ int i;
+ errcode_t err;
+
+ i = EXT2_INODE_SIZE(handle->fs->super);
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(handle->fs, handle->ino,
+ (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(handle->fs->super));
+ if (err)
+ goto out;
+
+ xattrs_free_keys(handle);
+
+ /* Does the inode have size for EA? */
+ if (EXT2_INODE_SIZE(handle->fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize +
+ sizeof(__u32))
+ goto read_ea_block;
+
+ /* Look for EA in the inode */
+ memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, sizeof(__u32));
+ if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
+ storage_size = EXT2_INODE_SIZE(handle->fs->super) -
+ EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
+ sizeof(__u32);
+ start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+
+ err = read_xattrs_from_buffer(handle, start, storage_size,
+ start, &handle->count);
+ if (err)
+ goto out;
+ }
+
+read_ea_block:
+ /* Look for EA in a separate EA block */
+ blk = ext2fs_file_acl_block(handle->fs, (struct ext2_inode *)inode);
+ if (blk != 0) {
+ if ((blk < handle->fs->super->s_first_data_block) ||
+ (blk >= ext2fs_blocks_count(handle->fs->super))) {
+ err = EXT2_ET_BAD_EA_BLOCK_NUM;
+ goto out;
+ }
+
+ err = ext2fs_get_mem(handle->fs->blocksize, &block_buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_read_ext_attr3(handle->fs, blk, block_buf,
+ handle->ino);
+ if (err)
+ goto out3;
+
+ header = (struct ext2_ext_attr_header *) block_buf;
+ if (header->h_magic != EXT2_EXT_ATTR_MAGIC) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out3;
+ }
+
+ if (header->h_blocks != 1) {
+ err = EXT2_ET_BAD_EA_HEADER;
+ goto out3;
+ }
+
+ /* Read EAs */
+ storage_size = handle->fs->blocksize -
+ sizeof(struct ext2_ext_attr_header);
+ start = block_buf + sizeof(struct ext2_ext_attr_header);
+ err = read_xattrs_from_buffer(handle, start, storage_size,
+ block_buf, &handle->count);
+ if (err)
+ goto out3;
+
+ ext2fs_free_mem(&block_buf);
+ }
+
+ ext2fs_free_mem(&block_buf);
+ ext2fs_free_mem(&inode);
+ return 0;
+
+out3:
+ ext2fs_free_mem(&block_buf);
+out:
+ ext2fs_free_mem(&inode);
+ return err;
+}
+
+errcode_t ext2fs_xattrs_iterate(struct ext2_xattr_handle *h,
+ int (*func)(char *name, char *value,
+ size_t value_len, void *data),
+ void *data)
+{
+ struct ext2_xattr *x;
+ errcode_t err;
+ int ret;
+
+ for (x = h->attrs; x < h->attrs + h->length; x++) {
+ if (!x->name)
+ continue;
+
+ ret = func(x->name, x->value, x->value_len, data);
+ if (ret & XATTR_CHANGED)
+ h->dirty = 1;
+ if (ret & XATTR_ABORT)
+ return 0;
+ }
+
+ return 0;
+}
+
+errcode_t ext2fs_xattr_get(struct ext2_xattr_handle *h, const char *key,
+ void **value, size_t *value_len)
+{
+ struct ext2_xattr *x;
+ void *val;
+ errcode_t err;
+
+ for (x = h->attrs; x < h->attrs + h->length; x++) {
+ if (!x->name)
+ continue;
+
+ if (strcmp(x->name, key) == 0) {
+ err = ext2fs_get_mem(x->value_len, &val);
+ if (err)
+ return err;
+ memcpy(val, x->value, x->value_len);
+ *value = val;
+ *value_len = x->value_len;
+ return 0;
+ }
+ }
+
+ return EXT2_ET_EA_KEY_NOT_FOUND;
+}
+
+errcode_t ext2fs_xattr_inode_max_size(ext2_filsys fs, ext2_ino_t ino,
+ size_t *size)
+{
+ struct ext2_ext_attr_header *header;
+ struct ext2_ext_attr_entry *entry;
+ struct ext2_inode_large *inode;
+ __u32 ea_inode_magic;
+ unsigned int storage_size, freesize, minoff;
+ void *start;
+ int i;
+ errcode_t err;
+
+ i = EXT2_INODE_SIZE(fs->super);
+ if (i < sizeof(*inode))
+ i = sizeof(*inode);
+ err = ext2fs_get_memzero(i, &inode);
+ if (err)
+ return err;
+
+ err = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)inode,
+ EXT2_INODE_SIZE(fs->super));
+ if (err)
+ goto out;
+
+ /* Does the inode have size for EA? */
+ if (EXT2_INODE_SIZE(fs->super) <= EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize +
+ sizeof(__u32)) {
+ err = EXT2_ET_INLINE_DATA_NO_SPACE;
+ goto out;
+ }
+
+ minoff = EXT2_INODE_SIZE(fs->super) - sizeof(*inode) - sizeof(__u32);
+ memcpy(&ea_inode_magic, ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize, sizeof(__u32));
+ if (ea_inode_magic == EXT2_EXT_ATTR_MAGIC) {
+ /* has xattrs. calculate the size */
+ storage_size = EXT2_INODE_SIZE(fs->super) -
+ EXT2_GOOD_OLD_INODE_SIZE - inode->i_extra_isize -
+ sizeof(__u32);
+ start= ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+ inode->i_extra_isize + sizeof(__u32);
+ entry = start;
+ while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ if (!entry->e_value_block && entry->e_value_size) {
+ unsigned int offs = entry->e_value_offs;
+ if (offs < minoff)
+ minoff = offs;
+ }
+ entry = EXT2_EXT_ATTR_NEXT(entry);
+ }
+ *size = minoff - ((char *)entry - (char *)start) - sizeof(__u32);
+ } else {
+ /* no xattr. return a maximum size */
+ *size = EXT2_EXT_ATTR_SIZE(minoff -
+ EXT2_EXT_ATTR_LEN(strlen("data")) -
+ EXT2_EXT_ATTR_ROUND - sizeof(__u32));
+ }
+
+out:
+ ext2fs_free_mem(&inode);
+ return err;
+}
+
+errcode_t ext2fs_xattr_set(struct ext2_xattr_handle *handle,
+ const char *key,
+ const void *value,
+ size_t value_len)
+{
+ struct ext2_xattr *x, *last_empty;
+ char *new_value;
+ errcode_t err;
+
+ last_empty = NULL;
+ for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
+ if (!x->name) {
+ last_empty = x;
+ continue;
+ }
+
+ /* Replace xattr */
+ if (strcmp(x->name, key) == 0) {
+ err = ext2fs_get_mem(value_len, &new_value);
+ if (err)
+ return err;
+ memcpy(new_value, value, value_len);
+ ext2fs_free_mem(&x->value);
+ x->value = new_value;
+ x->value_len = value_len;
+ handle->dirty = 1;
+ return 0;
+ }
+ }
+
+ /* Add attr to empty slot */
+ if (last_empty) {
+ err = ext2fs_get_mem(strlen(key) + 1, &last_empty->name);
+ if (err)
+ return err;
+ strcpy(last_empty->name, key);
+
+ err = ext2fs_get_mem(value_len, &last_empty->value);
+ if (err)
+ return err;
+ memcpy(last_empty->value, value, value_len);
+ last_empty->value_len = value_len;
+ handle->dirty = 1;
+ handle->count++;
+ return 0;
+ }
+
+ /* Expand array, append slot */
+ err = ext2fs_xattrs_expand(handle, 4);
+ if (err)
+ return err;
+
+ x = handle->attrs + handle->length - 4;
+ err = ext2fs_get_mem(strlen(key) + 1, &x->name);
+ if (err)
+ return err;
+ strcpy(x->name, key);
+
+ err = ext2fs_get_mem(value_len, &x->value);
+ if (err)
+ return err;
+ memcpy(x->value, value, value_len);
+ x->value_len = value_len;
+ handle->dirty = 1;
+ handle->count++;
+ return 0;
+}
+
+errcode_t ext2fs_xattr_remove(struct ext2_xattr_handle *handle,
+ const char *key)
+{
+ struct ext2_xattr *x;
+ errcode_t err;
+
+ for (x = handle->attrs; x < handle->attrs + handle->length; x++) {
+ if (!x->name)
+ continue;
+
+ if (strcmp(x->name, key) == 0) {
+ ext2fs_free_mem(&x->name);
+ ext2fs_free_mem(&x->value);
+ x->value_len = 0;
+ handle->dirty = 1;
+ handle->count--;
+ return 0;
+ }
+ }
+
+ return EXT2_ET_EA_KEY_NOT_FOUND;
+}
+
+errcode_t ext2fs_xattrs_open(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_xattr_handle **handle)
+{
+ struct ext2_xattr_handle *h;
+ errcode_t err;
+
+ if (!EXT2_HAS_COMPAT_FEATURE(fs->super,
+ EXT2_FEATURE_COMPAT_EXT_ATTR) &&
+ !EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA))
+ return EXT2_ET_MISSING_EA_FEATURE;
+
+ err = ext2fs_get_memzero(sizeof(*h), &h);
+ if (err)
+ return err;
+
+ h->length = 4;
+ err = ext2fs_get_arrayzero(h->length, sizeof(struct ext2_xattr),
+ &h->attrs);
+ if (err) {
+ ext2fs_free_mem(&h);
+ return err;
+ }
+ h->count = 0;
+ h->ino = ino;
+ h->fs = fs;
+ *handle = h;
+ return 0;
+}
+
+errcode_t ext2fs_xattrs_close(struct ext2_xattr_handle **handle)
+{
+ struct ext2_xattr_handle *h = *handle;
+ errcode_t err;
+
+ if (h->dirty) {
+ err = ext2fs_xattrs_write(h);
+ if (err)
+ return err;
+ }
+
+ xattrs_free_keys(h);
+ ext2fs_free_mem(&h->attrs);
+ ext2fs_free_mem(handle);
+ return 0;
+}
+
+size_t ext2fs_xattrs_count(struct ext2_xattr_handle *handle)
+{
+ return handle->count;
+}
}
+static errcode_t
+ext2fs_file_read_inline_data(ext2_file_t file, void *buf,
+ unsigned int wanted, unsigned int *got)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ unsigned int count = 0;
+ size_t size;
+
+ fs = file->fs;
+ retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
+ file->buf, &size);
+ if (retval)
+ return retval;
+
+ if (file->pos >= size)
+ goto out;
+
+ count = size - file->pos;
+ if (count > wanted)
+ count = wanted;
+ memcpy(buf, file->buf + file->pos, count);
+ file->pos += count;
+ buf += count;
+
+out:
+ if (got)
+ *got = count;
+ return retval;
+}
+
+
errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
unsigned int wanted, unsigned int *got)
{
EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
fs = file->fs;
+ /* If an inode has inline data, things get complicated. */
+ if (file->inode.i_flags & EXT4_INLINE_DATA_FL)
+ return ext2fs_file_read_inline_data(file, buf, wanted, got);
+
while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
retval = sync_buffer_position(file);
if (retval)
}
+static errcode_t
+ext2fs_file_write_inline_data(ext2_file_t file, const void *buf,
+ unsigned int nbytes, unsigned int *written)
+{
+ ext2_filsys fs;
+ errcode_t retval;
+ unsigned int count = 0;
+ size_t size;
+
+ fs = file->fs;
+ retval = ext2fs_inline_data_get(fs, file->ino, &file->inode,
+ file->buf, &size);
+ if (retval)
+ return retval;
+
+ if (file->pos < size) {
+ count = nbytes - file->pos;
+ memcpy(file->buf + file->pos, buf, count);
+
+ retval = ext2fs_inline_data_set(fs, file->ino, &file->inode,
+ file->buf, count);
+ if (retval == EXT2_ET_INLINE_DATA_NO_SPACE)
+ goto expand;
+ if (retval)
+ return retval;
+
+ file->pos += count;
+
+ /* Update inode size */
+ if (count != 0 && EXT2_I_SIZE(&file->inode) < file->pos) {
+ errcode_t rc;
+
+ rc = ext2fs_file_set_size2(file, file->pos);
+ if (retval == 0)
+ retval = rc;
+ }
+
+ if (written)
+ *written = count;
+ return 0;
+ }
+
+expand:
+ retval = ext2fs_inline_data_expand(fs, file->ino);
+ if (retval)
+ return retval;
+ /*
+ * reload inode and return no space error
+ *
+ * XXX: file->inode could be copied from the outside
+ * in ext2fs_file_open2(). We have no way to modify
+ * the outside inode.
+ */
+ retval = ext2fs_read_inode(fs, file->ino, &file->inode);
+ if (retval)
+ return retval;
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+}
+
+
errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
unsigned int nbytes, unsigned int *written)
{
if (!(file->flags & EXT2_FILE_WRITE))
return EXT2_ET_FILE_RO;
+ /* If an inode has inline data, things get complicated. */
+ if (file->inode.i_flags & EXT4_INLINE_DATA_FL) {
+ retval = ext2fs_file_write_inline_data(file, buf, nbytes,
+ written);
+ if (retval != EXT2_ET_INLINE_DATA_NO_SPACE)
+ return retval;
+ /* fall through to read data from the block */
+ retval = 0;
+ }
+
while (nbytes > 0) {
retval = sync_buffer_position(file);
if (retval)
--- /dev/null
+/*
+ * inline_data.c --- data in inode
+ *
+ * Copyright (C) 2012 Zheng Liu <wenqing.lz@taobao.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU library
+ * General Public License, version 2.
+ * %End-Header%
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2_ext_attr.h"
+
+#include "ext2fs.h"
+#include "ext2fsP.h"
+
+struct ext2_inline_data {
+ ext2_filsys fs;
+ ext2_ino_t ino;
+ size_t ea_size; /* the size of inline data in ea area */
+ void *ea_data;
+};
+
+static errcode_t ext2fs_inline_data_ea_set(struct ext2_inline_data *data)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ retval = ext2fs_xattrs_open(data->fs, data->ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (retval)
+ goto err;
+
+ 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;
+}
+
+static errcode_t ext2fs_inline_data_ea_get(struct ext2_inline_data *data)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ data->ea_size = 0;
+ data->ea_data = 0;
+
+ retval = ext2fs_xattrs_open(data->fs, data->ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (retval)
+ goto err;
+
+ retval = ext2fs_xattr_get(handle, "system.data",
+ (void **)&data->ea_data, &data->ea_size);
+ if (retval)
+ goto err;
+
+err:
+ (void) ext2fs_xattrs_close(&handle);
+ return retval;
+}
+
+errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inline_data data;
+
+ data.fs = fs;
+ data.ino = ino;
+ data.ea_size = 0;
+ data.ea_data = "";
+ return ext2fs_inline_data_ea_set(&data);
+}
+
+errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino, size_t *size)
+{
+ struct ext2_inode inode;
+ struct ext2_inline_data data;
+ errcode_t retval;
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
+ return EXT2_ET_NO_INLINE_DATA;
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+
+ *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
+ return ext2fs_free_mem(&data.ea_data);
+}
+
+int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
+ void *priv_data)
+{
+ struct dir_context *ctx;
+ struct ext2_inode inode;
+ struct ext2_dir_entry dirent;
+ struct ext2_inline_data data;
+ int ret = BLOCK_ABORT;
+ e2_blkcnt_t blockcnt = 0;
+
+ ctx = (struct dir_context *)priv_data;
+
+ ctx->errcode = ext2fs_read_inode(fs, ino, &inode);
+ if (ctx->errcode)
+ goto out;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) {
+ ctx->errcode = EXT2_ET_NO_INLINE_DATA;
+ goto out;
+ }
+
+ if (!LINUX_S_ISDIR(inode.i_mode)) {
+ ctx->errcode = EXT2_ET_NO_DIRECTORY;
+ goto out;
+ }
+ ret = 0;
+
+ /* we first check '.' and '..' dir */
+ dirent.inode = ino;
+ dirent.name_len = 1;
+ ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent);
+ dirent.name[0] = '.';
+ dirent.name[1] = '\0';
+ ctx->buf = (char *)&dirent;
+ ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_ABORT)
+ goto out;
+
+ dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]);
+ dirent.name_len = 2;
+ ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent);
+ dirent.name[0] = '.';
+ dirent.name[1] = '.';
+ dirent.name[2] = '\0';
+ ctx->buf = (char *)&dirent;
+ ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
+ errcode_t err;
+
+ inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
+ err = ext2fs_write_inode(fs, ino, &inode);
+ if (err)
+ goto out;
+ ret &= ~BLOCK_INLINE_DATA_CHANGED;
+ }
+ if (ret & BLOCK_ABORT)
+ goto out;
+
+ ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE;
+ ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
+#ifdef WORDS_BIGENDIAN
+ ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out;
+ }
+#endif
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
+#ifdef WORDS_BIGENDIAN
+ ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
+ ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out;
+ }
+#endif
+ ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
+ if (ctx->errcode)
+ ret |= BLOCK_ABORT;
+ ret &= ~BLOCK_INLINE_DATA_CHANGED;
+ }
+ if (ret & BLOCK_ABORT)
+ goto out;
+
+ data.fs = fs;
+ data.ino = ino;
+ ctx->errcode = ext2fs_inline_data_ea_get(&data);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out;
+ }
+ if (data.ea_size <= 0)
+ goto out;
+
+ ctx->buf = data.ea_data;
+ ctx->buflen = data.ea_size;
+#ifdef WORDS_BIGENDIAN
+ ctx.errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
+ if (ctx.errcode) {
+ ret |= BLOCK_ABORT;
+ goto out;
+ }
+#endif
+
+ ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
+ if (ret & BLOCK_INLINE_DATA_CHANGED) {
+#ifdef WORDS_BIGENDIAN
+ ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
+ ctx->buflen, 0);
+ if (ctx->errcode) {
+ ret |= BLOCK_ABORT;
+ goto out1;
+ }
+#endif
+ ctx->errcode = ext2fs_inline_data_ea_set(&data);
+ if (ctx->errcode)
+ ret |= BLOCK_ABORT;
+ }
+
+out1:
+ ext2fs_free_mem(&data.ea_data);
+ ctx->buf = 0;
+
+out:
+ ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
+ return ret;
+}
+
+errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_xattr_handle *handle;
+ errcode_t retval;
+
+ retval = ext2fs_xattrs_open(fs, ino, &handle);
+ if (retval)
+ return retval;
+
+ retval = ext2fs_xattrs_read(handle);
+ if (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;
+}
+
+static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino,
+ char *bbuf, char *ibuf, int size)
+{
+ struct ext2_dir_entry *dir, *dir2;
+ struct ext2_dir_entry_tail *t;
+ errcode_t retval;
+ unsigned int offset;
+ int csum_size = 0;
+ int filetype = 0;
+ int rec_len;
+
+ if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ csum_size = sizeof(struct ext2_dir_entry_tail);
+
+ /* Create '.' and '..' */
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ filetype = EXT2_FT_DIR;
+
+ /*
+ * Set up entry for '.'
+ */
+ dir = (struct ext2_dir_entry *) bbuf;
+ dir->inode = ino;
+ ext2fs_dirent_set_name_len(dir, 1);
+ ext2fs_dirent_set_file_type(dir, filetype);
+ dir->name[0] = '.';
+ rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
+ dir->rec_len = EXT2_DIR_REC_LEN(1);
+
+ /*
+ * Set up entry for '..'
+ */
+ dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len);
+ dir->rec_len = EXT2_DIR_REC_LEN(2);
+ dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]);
+ ext2fs_dirent_set_name_len(dir, 2);
+ ext2fs_dirent_set_file_type(dir, filetype);
+ dir->name[0] = '.';
+ dir->name[1] = '.';
+
+ /*
+ * Ajust the last rec_len
+ */
+ offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
+ dir = (struct ext2_dir_entry *) (bbuf + offset);
+ memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE,
+ size - EXT4_INLINE_DATA_DOTDOT_SIZE);
+ size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) -
+ EXT4_INLINE_DATA_DOTDOT_SIZE;
+
+ do {
+ dir2 = dir;
+ retval = ext2fs_get_rec_len(fs, dir, &rec_len);
+ if (retval)
+ goto err;
+ offset += rec_len;
+ dir = (struct ext2_dir_entry *) (bbuf + offset);
+ } while (offset < size);
+ rec_len += fs->blocksize - csum_size - offset;
+ retval = ext2fs_set_rec_len(fs, rec_len, dir2);
+ if (retval)
+ goto err;
+
+ if (csum_size) {
+ t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize);
+ ext2fs_initialize_dirent_tail(fs, t);
+ }
+
+err:
+ return retval;
+}
+
+static errcode_t
+ext2fs_inline_data_dir_expand(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, char *buf, size_t size)
+{
+ errcode_t retval;
+ blk64_t blk;
+ char *blk_buf;
+
+ retval = ext2fs_get_memzero(fs->blocksize, &blk_buf);
+ if (retval)
+ return retval;
+
+#ifdef WORDS_BIGENDIAN
+ retval = ext2fs_dirent_swab_in2(fs, buf, size);
+ if (retval)
+ goto errout;
+#endif
+
+ /* Adjust the rec_len */
+ retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, buf, size);
+ /* Allocate a new block */
+ retval = ext2fs_new_block2(fs, 0, 0, &blk);
+ if (retval)
+ goto errout;
+ retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
+ if (retval)
+ goto errout;
+
+ /* Update inode */
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
+ inode->i_flags |= EXT4_EXTENTS_FL;
+ inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+ ext2fs_iblk_set(fs, inode, 1);
+ inode->i_size = fs->blocksize;
+ retval = ext2fs_bmap2(fs, ino, inode, 0, BMAP_SET, 0, 0, &blk);
+ if (retval)
+ goto errout;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ goto errout;
+ ext2fs_block_alloc_stats(fs, blk, +1);
+
+errout:
+ ext2fs_free_mem(&blk_buf);
+ return retval;
+}
+
+static errcode_t
+ext2fs_inline_data_file_expand(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode, char *buf, size_t size)
+{
+ ext2_file_t e2_file;
+ errcode_t retval;
+
+ /* Update inode */
+ if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT3_FEATURE_INCOMPAT_EXTENTS)) {
+ int i;
+ struct ext3_extent_header *eh;
+
+ eh = (struct ext3_extent_header *) &inode->i_block[0];
+ eh->eh_depth = 0;
+ eh->eh_entries = 0;
+ eh->eh_magic = EXT3_EXT_MAGIC;
+ i = (sizeof(inode->i_block) - sizeof(*eh)) /
+ sizeof(struct ext3_extent);
+ eh->eh_max = ext2fs_cpu_to_le16(i);
+ inode->i_flags |= EXT4_EXTENTS_FL;
+ }
+ inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+ ext2fs_iblk_set(fs, inode, 0);
+ inode->i_size = 0;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+
+ /* Write out the block buffer */
+ retval = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
+ if (retval)
+ return retval;
+ retval = ext2fs_file_write(e2_file, buf, size, 0);
+ ext2fs_file_close(e2_file);
+ return retval;
+}
+
+errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
+{
+ struct ext2_inode inode;
+ struct ext2_inline_data data;
+ errcode_t retval;
+ size_t inline_size;
+ char *inline_buf = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ retval = ext2fs_read_inode(fs, ino, &inode);
+ if (retval)
+ return retval;
+
+ if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
+ return EXT2_ET_NO_INLINE_DATA;
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+ inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE;
+ retval = ext2fs_get_mem(inline_size, &inline_buf);
+ if (retval)
+ goto errout;
+
+ memcpy(inline_buf, (void *)inode.i_block, EXT4_MIN_INLINE_DATA_SIZE);
+ if (data.ea_size > 0) {
+ memcpy(inline_buf + EXT4_MIN_INLINE_DATA_SIZE,
+ data.ea_data, data.ea_size);
+ }
+
+ memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ retval = ext2fs_inline_data_ea_remove(fs, ino);
+ if (retval)
+ goto errout;
+
+ if (LINUX_S_ISDIR(inode.i_mode)) {
+ retval = ext2fs_inline_data_dir_expand(fs, ino, &inode,
+ inline_buf, inline_size);
+ } else {
+ retval = ext2fs_inline_data_file_expand(fs, ino, &inode,
+ inline_buf, inline_size);
+ }
+
+errout:
+ if (inline_buf)
+ ext2fs_free_mem(&inline_buf);
+ ext2fs_free_mem(&data.ea_data);
+ return retval;
+}
+
+/*
+ * When caller uses this function to retrieve the inline data, it must
+ * allocate a buffer which has the size of inline data. The size of
+ * inline data can be know by ext2fs_inline_data_get_size().
+ */
+errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t *size)
+{
+ struct ext2_inode inode_buf;
+ struct ext2_inline_data data;
+ errcode_t retval;
+
+ if (!inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (retval)
+ return retval;
+ inode = &inode_buf;
+ }
+
+ data.fs = fs;
+ data.ino = ino;
+ retval = ext2fs_inline_data_ea_get(&data);
+ if (retval)
+ return retval;
+
+ memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
+ if (data.ea_size > 0)
+ memcpy(buf + EXT4_MIN_INLINE_DATA_SIZE,
+ data.ea_data, data.ea_size);
+
+ if (size)
+ *size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
+ ext2fs_free_mem(&data.ea_data);
+ return 0;
+}
+
+errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ void *buf, size_t size)
+{
+ struct ext2_inode inode_buf;
+ struct ext2_inline_data data;
+ errcode_t retval;
+ size_t max_size;
+
+ if (!inode) {
+ retval = ext2fs_read_inode(fs, ino, &inode_buf);
+ if (retval)
+ return retval;
+ inode = &inode_buf;
+ }
+
+ if (size <= EXT4_MIN_INLINE_DATA_SIZE) {
+ memcpy((void *)inode->i_block, buf, size);
+ return ext2fs_write_inode(fs, ino, inode);
+ }
+
+ retval = ext2fs_xattr_inode_max_size(fs, ino, &max_size);
+ if (retval)
+ return retval;
+
+ if (size - EXT4_MIN_INLINE_DATA_SIZE > max_size)
+ return EXT2_ET_INLINE_DATA_NO_SPACE;
+
+ memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE);
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+ data.fs = fs;
+ data.ino = ino;
+ data.ea_size = size - EXT4_MIN_INLINE_DATA_SIZE;
+ data.ea_data = buf + EXT4_MIN_INLINE_DATA_SIZE;
+ return ext2fs_inline_data_ea_set(&data);
+}
+
+#ifdef DEBUG
+#include "e2p/e2p.h"
+
+/*
+ * The length of buffer is set to 64 because in inode's i_block member it only
+ * can save 60 bytes. Thus this value can let the data being saved in extra
+ * space.
+ */
+#define BUFF_SIZE (64)
+
+static errcode_t file_test(ext2_filsys fs)
+{
+ struct ext2_inode inode;
+ ext2_ino_t newfile;
+ errcode_t retval;
+ size_t size;
+ char *buf = 0, *cmpbuf = 0;
+ int i;
+
+ /* 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");
+ return 1;
+ }
+
+ memset(&inode, 0, sizeof(inode));
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ inode.i_size = EXT4_MIN_INLINE_DATA_SIZE;
+ 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");
+ return 1;
+ }
+
+ retval = ext2fs_inline_data_init(fs, newfile);
+ if (retval) {
+ com_err("file_test", retval, "while init 'system.data'");
+ return 1;
+ }
+
+ retval = ext2fs_inline_data_size(fs, newfile, &size);
+ if (retval) {
+ com_err("file_test", retval, "while getting size");
+ return 1;
+ }
+
+ if (size != EXT4_MIN_INLINE_DATA_SIZE) {
+ fprintf(stderr,
+ "tst_inline_data: size of inline data is wrong\n");
+ return 1;
+ }
+
+ ext2fs_get_mem(BUFF_SIZE, &buf);
+ memset(buf, 'a', BUFF_SIZE);
+ retval = ext2fs_inline_data_set(fs, newfile, 0, buf, BUFF_SIZE);
+ if (retval) {
+ com_err("file_test", retval,
+ "while setting inline data %s", buf);
+ goto err;
+ }
+
+ ext2fs_get_mem(BUFF_SIZE, &cmpbuf);
+ retval = ext2fs_inline_data_get(fs, newfile, 0, cmpbuf, &size);
+ if (retval) {
+ com_err("file_test", retval, "while getting inline data");
+ goto err;
+ }
+
+ if (size != BUFF_SIZE) {
+ fprintf(stderr,
+ "tst_inline_data: size %lu != buflen %lu\n",
+ size, BUFF_SIZE);
+ retval = 1;
+ goto err;
+ }
+
+ if (memcmp(buf, cmpbuf, BUFF_SIZE)) {
+ fprintf(stderr, "tst_inline_data: buf != cmpbuf\n");
+ retval = 1;
+ goto err;
+ }
+
+ retval = ext2fs_punch(fs, newfile, 0, 0, 0, ~0ULL);
+ if (retval) {
+ com_err("file_test", retval, "while truncating inode");
+ goto err;
+ }
+
+ /* reload inode and check isize */
+ ext2fs_read_inode(fs, newfile, &inode);
+ if (inode.i_size != 0) {
+ fprintf(stderr, "tst_inline_data: i_size should be 0\n");
+ retval = 1;
+ }
+
+err:
+ if (cmpbuf)
+ ext2fs_free_mem(&cmpbuf);
+ if (buf)
+ ext2fs_free_mem(&buf);
+ return retval;
+}
+
+static errcode_t dir_test(ext2_filsys fs)
+{
+ const char *dot_name = ".";
+ const char *stub_name = "stub";
+ const char *parent_name = "test";
+ ext2_ino_t parent, dir, tmp;
+ errcode_t retval;
+ char dirname[PATH_MAX];
+ int i;
+
+ retval = ext2fs_mkdir(fs, 11, 11, stub_name);
+ if (retval) {
+ com_err("dir_test", retval, "while creating %s dir", stub_name);
+ return retval;
+ }
+
+ retval = ext2fs_mkdir(fs, 11, 0, parent_name);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while creating %s dir", parent_name);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, 11, parent_name, strlen(parent_name),
+ 0, &parent);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, parent, dot_name, strlen(dot_name),
+ 0, &tmp);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ if (parent != tmp) {
+ fprintf(stderr, "tst_inline_data: parent (%lu) != tmp (%lu)\n",
+ parent, tmp);
+ return 1;
+ }
+
+ for (i = 0, dir = 13; i < 4; i++, dir++) {
+ tmp = 0;
+ snprintf(dirname, PATH_MAX, "%d", i);
+ retval = ext2fs_mkdir(fs, parent, 0, dirname);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while creating %s dir", dirname);
+ return retval;
+ }
+
+ retval = ext2fs_lookup(fs, parent, dirname, strlen(dirname),
+ 0, &tmp);
+ if (retval) {
+ com_err("dir_test", retval,
+ "while looking up %s dir", parent_name);
+ return retval;
+ }
+
+ if (dir != tmp) {
+ fprintf(stderr, "tst_inline_data: dir (%lu) != tmp (%lu)\n",
+ dir, tmp);
+ return 1;
+ }
+ }
+
+ snprintf(dirname, PATH_MAX, "%d", i);
+ retval = ext2fs_mkdir(fs, parent, 0, dirname);
+ if (retval && retval != EXT2_ET_DIR_NO_SPACE) {
+ com_err("dir_test", retval, "while creating %s dir", dirname);
+ return retval;
+ }
+
+ retval = ext2fs_expand_dir(fs, parent);
+ if (retval) {
+ com_err("dir_test", retval, "while expanding %s dir", parent_name);
+ return retval;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ ext2_filsys fs;
+ struct ext2_super_block param;
+ errcode_t retval;
+ int i;
+
+ /* setup */
+ initialize_ext2_error_table();
+
+ memset(¶m, 0, sizeof(param));
+ ext2fs_blocks_count_set(¶m, 32768);
+ param.s_inodes_count = 100;
+
+ param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_INLINE_DATA;
+ param.s_rev_level = EXT2_DYNAMIC_REV;
+ param.s_inode_size = 256;
+
+ retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m,
+ test_io_manager, &fs);
+ if (retval) {
+ com_err("setup", retval,
+ "while initializing filesystem");
+ exit(1);
+ }
+
+ retval = ext2fs_allocate_tables(fs);
+ if (retval) {
+ com_err("setup", retval,
+ "while allocating tables for test filesysmte");
+ exit(1);
+ }
+
+ /* initialize inode cache */
+ if (!fs->icache) {
+ struct ext2_inode inode;
+ ext2_ino_t first_ino = EXT2_FIRST_INO(fs->super);
+ int i;
+
+ /* we just want to init inode cache. So ignore error */
+ ext2fs_create_inode_cache(fs, 16);
+ if (!fs->icache) {
+ fprintf(stderr,
+ "tst_inline_data: init inode cache failed\n");
+ exit(1);
+ }
+
+ /* setup inode cache */
+ for (i = 0; i < fs->icache->cache_size; i++)
+ fs->icache->cache[i].ino = first_ino++;
+ }
+
+ /* test */
+ if (file_test(fs)) {
+ fprintf(stderr, "tst_inline_data(FILE): FAILED\n");
+ return 1;
+ }
+ printf("tst_inline_data(FILE): OK\n");
+
+ if (dir_test(fs)) {
+ fprintf(stderr, "tst_inline_data(DIR): FAILED\n");
+ return 1;
+ }
+ printf("tst_inline_data(DIR): OK\n");
+
+ return 0;
+}
+#endif
ext2fs_free_mem(&icache);
}
-static errcode_t create_icache(ext2_filsys fs)
+errcode_t ext2fs_create_inode_cache(ext2_filsys fs, unsigned int cache_size)
{
int i;
errcode_t retval;
fs->icache->buffer_blk = 0;
fs->icache->cache_last = -1;
- fs->icache->cache_size = 4;
+ fs->icache->cache_size = cache_size;
fs->icache->refcount = 1;
retval = ext2fs_get_array(fs->icache->cache_size,
sizeof(struct ext2_inode_cache_ent),
return EXT2_ET_BAD_INODE_NUM;
/* Create inode cache if not present */
if (!fs->icache) {
- retval = create_icache(fs);
+ retval = ext2fs_create_inode_cache(fs, 4);
if (retval)
return retval;
}
}
}
} else {
- retval = create_icache(fs);
+ retval = ext2fs_create_inode_cache(fs, 4);
if (retval)
goto errout;
}
ext2_ino_t scratch_ino;
blk64_t blk;
char *block = 0;
+ int inline_data = 0;
EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
/*
+ * Create a new dir with inline data iff this feature is enabled
+ * and ino >= EXT2_FIRST_INO.
+ */
+ if ((!ino || ino >= EXT2_FIRST_INO(fs->super)) &&
+ EXT2_HAS_INCOMPAT_FEATURE(fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA))
+ inline_data = 1;
+
+ /*
* Allocate an inode, if necessary
*/
if (!ino) {
/*
* Allocate a data block for the directory
*/
- retval = ext2fs_new_block2(fs, 0, 0, &blk);
- if (retval)
- goto cleanup;
+ if (!inline_data) {
+ retval = ext2fs_new_block2(fs, 0, 0, &blk);
+ if (retval)
+ goto cleanup;
+ }
/*
* Create a scratch template for the directory
*/
- retval = ext2fs_new_dir_block(fs, ino, parent, &block);
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ if (inline_data)
+ retval = ext2fs_new_dir_inline_data(fs, ino, parent,
+ inode.i_block);
+ else
+ retval = ext2fs_new_dir_block(fs, ino, parent, &block);
if (retval)
goto cleanup;
/*
* Create the inode structure....
*/
- memset(&inode, 0, sizeof(struct ext2_inode));
inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
inode.i_uid = inode.i_gid = 0;
- ext2fs_iblk_set(fs, &inode, 1);
- if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS)
- inode.i_flags |= EXT4_EXTENTS_FL;
- else
- inode.i_block[0] = blk;
+ if (inline_data) {
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ inode.i_size = EXT4_MIN_INLINE_DATA_SIZE;
+ } else {
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS)
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ else
+ inode.i_block[0] = blk;
+ inode.i_size = fs->blocksize;
+ ext2fs_iblk_set(fs, &inode, 1);
+ }
inode.i_links_count = 2;
- inode.i_size = fs->blocksize;
/*
* Write out the inode and inode data block. The inode generation
retval = ext2fs_write_new_inode(fs, ino, &inode);
if (retval)
goto cleanup;
- retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
- if (retval)
- goto cleanup;
-
- if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) {
- retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
- if (retval)
- goto cleanup;
- retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
- ext2fs_extent_free(handle);
+ if (inline_data) {
+ /* init "system.data" for new dir */
+ retval = ext2fs_inline_data_init(fs, ino);
+ } else {
+ retval = ext2fs_write_dir_block4(fs, blk, block, 0, ino);
if (retval)
goto cleanup;
+
+ if (fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS) {
+ retval = ext2fs_extent_open2(fs, ino, &inode, &handle);
+ if (retval)
+ goto cleanup;
+ retval = ext2fs_extent_set_bmap(handle, 0, blk, 0);
+ ext2fs_extent_free(handle);
+ if (retval)
+ goto cleanup;
+ }
}
/*
* Update parent inode's counts
*/
if (parent != ino) {
+ /* reload parent inode due to inline data */
+ retval = ext2fs_read_inode(fs, parent, &parent_inode);
+ if (retval)
+ goto cleanup;
parent_inode.i_links_count++;
retval = ext2fs_write_inode(fs, parent, &parent_inode);
if (retval)
/*
* Update accounting....
*/
- ext2fs_block_alloc_stats2(fs, blk, +1);
+ if (!inline_data)
+ ext2fs_block_alloc_stats2(fs, blk, +1);
ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
cleanup:
*block = buf;
return 0;
}
+
+/*
+ * Create new directory on inline data
+ */
+errcode_t ext2fs_new_dir_inline_data(ext2_filsys fs, ext2_ino_t dir_ino,
+ ext2_ino_t parent_ino, __u32 *iblock)
+{
+ struct ext2_dir_entry *dir = NULL;
+ errcode_t retval;
+ char *buf;
+ int rec_len;
+ int filetype = 0;
+
+ EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+ iblock[0] = ext2fs_cpu_to_le32(parent_ino);
+
+ dir = (struct ext2_dir_entry *)((char *)iblock +
+ EXT4_INLINE_DATA_DOTDOT_SIZE);
+ dir->inode = 0;
+ rec_len = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
+ retval = ext2fs_set_rec_len(fs, rec_len, dir);
+
+ return retval;
+}
return retval;
}
+static errcode_t ext2fs_punch_inline_data(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ blk64_t start, blk64_t end)
+{
+ errcode_t retval;
+
+ /*
+ * In libext2fs ext2fs_punch is based on block unit. So that
+ * means that if start > 0 we don't need to do nothing. Due
+ * to this we will remove all inline data in ext2fs_punch()
+ * now.
+ */
+ if (start > 0)
+ return 0;
+
+ memset((char *)inode->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ inode->i_size = 0;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+
+ return ext2fs_inline_data_ea_remove(fs, ino);
+}
+
/*
* Deallocate all logical blocks starting at start to end, inclusive.
* If end is ~0, then this is effectively truncate.
return retval;
inode = &inode_buf;
}
- if (inode->i_flags & EXT4_EXTENTS_FL)
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ return ext2fs_punch_inline_data(fs, ino, inode, start, end);
+ else if (inode->i_flags & EXT4_EXTENTS_FL)
retval = ext2fs_punch_extent(fs, ino, inode, start, end);
else {
blk_t count;
{
unsigned i, has_data_blocks, extra_isize, attr_magic;
int has_extents = 0;
+ int has_inline_data = 0;
int islnk = 0;
__u32 *eaf, *eat;
(struct ext2_inode *) t);
if (hostorder && (f->i_flags & EXT4_EXTENTS_FL))
has_extents = 1;
+ if (hostorder && (f->i_flags & EXT4_INLINE_DATA_FL))
+ has_inline_data = 1;
t->i_flags = ext2fs_swab32(f->i_flags);
if (!hostorder && (t->i_flags & EXT4_EXTENTS_FL))
has_extents = 1;
+ if (!hostorder && (f->i_flags & EXT4_INLINE_DATA_FL))
+ has_inline_data = 1;
t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
- /* extent data are swapped on access, not here */
- if (!has_extents && (!islnk || has_data_blocks)) {
+ /*
+ * Extent data and inline data are swapped on access, not here
+ */
+ if (!has_extents && !has_inline_data && (!islnk || has_data_blocks)) {
for (i = 0; i < EXT2_N_BLOCKS; i++)
t->i_block[i] = ext2fs_swab32(f->i_block[i]);
} else if (t != f) {
errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
{
+ return ext2fs_dirent_swab_in(fs, buf, fs->blocksize, flags);
+}
+
+errcode_t ext2fs_dirent_swab_in2(ext2_filsys fs, char *buf,
+ size_t size, int flags)
+{
errcode_t retval;
char *p, *end;
struct ext2_dir_entry *dirent;
unsigned int name_len, rec_len;
p = (char *) buf;
- end = (char *) buf + fs->blocksize;
+ end = (char *) buf + size;
while (p < end-8) {
dirent = (struct ext2_dir_entry *) p;
dirent->inode = ext2fs_swab32(dirent->inode);
errcode_t ext2fs_dirent_swab_out(ext2_filsys fs, char *buf, int flags)
{
+ return ext2fs_dirent_swab_out2(fs, buf, fs->blocksize, flags);
+}
+
+errcode_t ext2fs_dirent_swab_out2(ext2_filsys fs, char *buf,
+ size_t size, int flags)
+{
errcode_t retval;
char *p, *end;
unsigned int rec_len;
struct ext2_dir_entry *dirent;
p = buf;
- end = buf + fs->blocksize;
+ end = buf + size;
while (p < end) {
dirent = (struct ext2_dir_entry *) p;
retval = ext2fs_get_rec_len(fs, dirent, &rec_len);
return 0; /* Probably a fast symlink */
}
}
+
+ /*
+ * If this inode has inline data, it shouldn't have valid block
+ * entries.
+ */
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ return 0;
return 1;
}
current_fs->now ? current_fs->now : time(0);
inode.i_links_count = 1;
inode.i_size = statbuf.st_size;
- if (current_fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_EXTENTS) {
+ if (EXT2_HAS_INCOMPAT_FEATURE(current_fs->super,
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ inode.i_flags |= EXT4_INLINE_DATA_FL;
+ } else if (current_fs->super->s_feature_incompat &
+ EXT3_FEATURE_INCOMPAT_EXTENTS) {
int i;
struct ext3_extent_header *eh;
close(fd);
return errno;
}
+ if (inode.i_flags & EXT4_INLINE_DATA_FL) {
+ retval = ext2fs_inline_data_init(current_fs, newfile);
+ if (retval)
+ return;
+ }
if (LINUX_S_ISREG(inode.i_mode)) {
if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) {
make_holes = 1;
or
.BR tune2fs(8).
.TP
+.B inline_data
+Allow data to be stored in the inode and extended attribute area
+.TP
.B large_file
.br
This feature flag is set automatically by modern kernels when a file
EXT2_FEATURE_INCOMPAT_META_BG|
EXT4_FEATURE_INCOMPAT_FLEX_BG|
EXT4_FEATURE_INCOMPAT_MMP |
- EXT4_FEATURE_INCOMPAT_64BIT,
+ EXT4_FEATURE_INCOMPAT_64BIT|
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA,
/* R/O compat */
EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
* 32-bit vs 64-bit block number support.
*/
if ((fs_blocks_count > MAX_32_NUM) &&
+ (fs_param.s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT))
+ fs_param.s_feature_compat &= ~EXT2_FEATURE_COMPAT_RESIZE_INODE;
+ if ((fs_blocks_count > MAX_32_NUM) &&
!(fs_param.s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) &&
get_bool_from_profile(fs_types, "auto_64-bit_support", 0)) {
fs_param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT;
"See https://ext4.wiki.kernel.org/"
"index.php/Quota for more information\n\n"));
- /* Since sparse_super is the default, we would only have a problem
+ /*
+ * Since sparse_super is the default, we would only have a problem
* here if it was explicitly disabled.
*/
if ((fs_param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) &&
blocksize);
exit(1);
}
+ /*
+ * If inode size is 128 and inline data is enabled, we need
+ * to notify users that inline data will never be useful.
+ */
+ if ((fs_param.s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_INLINE_DATA) &&
+ inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
+ com_err(program_name, 0,
+ _("inode size is %d, inline data is useless"),
+ inode_size);
+ exit(1);
+ }
fs_param.s_inode_size = inode_size;
}
inode_size = 256
}
ext4dev = {
- features = has_journal,extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize
+ features = has_journal,extent,huge_file,flex_bg,metadata_csum,inline_data,64bit,dir_nlink,extra_isize
inode_size = 256
options = test_fs=1
}
exit(1);
}
- if (LINUX_S_ISDIR(inode->i_mode)) {
+ if (LINUX_S_ISDIR(inode->i_mode) &&
+ ext2fs_inode_has_valid_blocks2(fs, inode)) {
retval = rewrite_directory(fs, ino, inode);
if (retval) {
com_err("rewrite_directory", retval,
Inode 1 has EXTENTS_FL flag set on filesystem without extents support.
Clear? yes
+Inode 14 has INLINE_DATA_FL flag on filesystem without inline data support.
+Clear? yes
+
+Inode 15 has INLINE_DATA_FL flag on filesystem without inline data support.
+Clear? yes
+
Inode 15 has EXTENTS_FL flag set on filesystem without extents support.
Clear? yes
+Inode 16 has INLINE_DATA_FL flag on filesystem without inline data support.
+Clear? yes
+
Inode 16 has EXTENTS_FL flag set on filesystem without extents support.
Clear? yes
resize2fs test
debugfs -R ''stat file'' test.img 2>&1 | grep ''^Inode\|in inode body\|name = ''
Inode: 1550 Type: regular Mode: 0644 Flags: 0x0
-Extended attributes stored in inode body:
- name = "propervalue" (11)
+ user.name = "propervalue" (11)
Exit status is 0
resize2fs test.img 5M
Resizing the filesystem on test.img to 5120 (1k) blocks.
Exit status is 0
debugfs -R ''stat file'' test.img 2>&1 | grep ''^Inode\|in inode body\|name = ''
Inode: 12 Type: regular Mode: 0644 Flags: 0x0
-Extended attributes stored in inode body:
- name = "propervalue" (11)
+ user.name = "propervalue" (11)
Exit status is 0