Whamcloud - gitweb
Merge branch 'ry/mke2fs-populate' into next
authorTheodore Ts'o <tytso@mit.edu>
Thu, 6 Mar 2014 16:22:42 +0000 (11:22 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 6 Mar 2014 16:22:42 +0000 (11:22 -0500)
Conflicts:
debugfs/debugfs.c

38 files changed:
debian/control.in
debugfs/debugfs.c
debugfs/filefrag.c
debugfs/lsdel.c
e2fsck/pass1.c
e2fsck/pass2.c
e2fsck/pass3.c
e2fsck/problem.c
e2fsck/problem.h
e2fsck/rehash.c
e2fsck/unix.c
e2fsck/util.c
lib/ext2fs/Makefile.in
lib/ext2fs/Makefile.pq
lib/ext2fs/bmap.c
lib/ext2fs/dblist_dir.c
lib/ext2fs/dir_iterate.c
lib/ext2fs/expanddir.c
lib/ext2fs/ext2_err.et.in
lib/ext2fs/ext2_fs.h
lib/ext2fs/ext2fs.h
lib/ext2fs/ext2fsP.h
lib/ext2fs/ext_attr.c
lib/ext2fs/fileio.c
lib/ext2fs/inline_data.c [new file with mode: 0644]
lib/ext2fs/inode.c
lib/ext2fs/mkdir.c
lib/ext2fs/newdir.c
lib/ext2fs/punch.c
lib/ext2fs/swapfs.c
lib/ext2fs/valid_blk.c
misc/create_inode.c
misc/ext4.5.in
misc/mke2fs.c
misc/mke2fs.conf.in
misc/tune2fs.c
tests/f_bad_disconnected_inode/expect.1
tests/r_inline_xattr/expect

index 3e81c36..3982974 100644 (file)
@@ -124,7 +124,7 @@ Description: runtime components for the Universally Unique ID library
  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
@@ -161,7 +161,7 @@ Description: block device id library
  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
@@ -193,7 +193,7 @@ Description: block device id library - headers and static libraries
  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
@@ -204,7 +204,7 @@ Description: stripped-down versions of e2fsprogs, for 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
 
index 1706815..5554017 100644 (file)
@@ -529,34 +529,45 @@ static void internal_dump_inode_extra(FILE *out,
                                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)
@@ -707,6 +718,16 @@ static void dump_extents(FILE *f, const char *prefix, ext2_ino_t ino,
                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)
@@ -804,6 +825,7 @@ void internal_dump_inode(FILE *out, const char *prefix,
        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) {
@@ -840,6 +862,8 @@ void internal_dump_inode(FILE *out, const char *prefix,
                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);
        }
@@ -1669,11 +1693,10 @@ static void kill_file_by_inode(ext2_ino_t inode)
        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));
index 6219d7c..49345ad 100644 (file)
@@ -154,11 +154,13 @@ static void filefrag(ext2_ino_t ino, struct ext2_inode *inode,
                        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,
index e5b2d20..5276014 100644 (file)
@@ -141,15 +141,19 @@ void do_lsdel(int argc, char **argv)
                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,
index 7554f4e..11b3dde 100644 (file)
@@ -177,7 +177,8 @@ int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
        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) {
@@ -407,6 +408,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
        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
@@ -434,11 +436,23 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
         * 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))
@@ -501,6 +515,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
            (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,
@@ -592,7 +607,7 @@ void e2fsck_pass1(e2fsck_t ctx)
        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;
@@ -626,6 +641,8 @@ void e2fsck_pass1(e2fsck_t ctx)
 
        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
@@ -804,6 +821,24 @@ void e2fsck_pass1(e2fsck_t ctx)
                        }
                }
 
+               /* 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.
                 *
@@ -1170,7 +1205,8 @@ void e2fsck_pass1(e2fsck_t ctx)
                        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])
@@ -1179,6 +1215,7 @@ void e2fsck_pass1(e2fsck_t ctx)
                                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] ||
@@ -2114,6 +2151,26 @@ static void check_blocks_extents(e2fsck_t ctx, struct problem_context *pctx,
 }
 
 /*
+ * 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.
  */
@@ -2127,6 +2184,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        unsigned        bad_size = 0;
        int             dirty_inode = 0;
        int             extent_fs;
+       int             inlinedata_fs;
        __u64           size;
 
        pb.ino = ino;
@@ -2150,6 +2208,8 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 
        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 &
@@ -2183,6 +2243,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                         */
                        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);
@@ -2215,7 +2279,8 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                }
        }
 
-       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--;
@@ -2241,7 +2306,14 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
 #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;
index 5a2745a..586f3a8 100644 (file)
@@ -730,6 +730,15 @@ static void salvage_directory(ext2_filsys fs,
        }
 }
 
+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)
@@ -738,7 +747,7 @@ static int check_dir_block(ext2_filsys fs,
 #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;
@@ -761,6 +770,8 @@ static int check_dir_block(ext2_filsys fs,
        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;
@@ -778,6 +789,10 @@ static int check_dir_block(ext2_filsys fs,
                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.
@@ -792,7 +807,16 @@ static int check_dir_block(ext2_filsys fs,
        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;
@@ -813,7 +837,11 @@ static int check_dir_block(ext2_filsys fs,
 #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 */
@@ -884,7 +912,7 @@ out_htree:
 #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;
@@ -923,20 +951,44 @@ skip_checksum:
                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 = &dot;
+                               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) {
@@ -1175,9 +1227,14 @@ skip_checksum:
                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
@@ -1195,12 +1252,22 @@ skip_checksum:
        }
 #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) {
@@ -1214,8 +1281,13 @@ skip_checksum:
 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) {
index aaf177c..6f7f855 100644 (file)
@@ -376,6 +376,17 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
        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;
@@ -388,6 +399,7 @@ ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
                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) {
index be9d3ec..1282858 100644 (file)
@@ -1020,6 +1020,15 @@ static struct e2fsck_problem problem_table[] = {
          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 */
 
@@ -1590,6 +1599,11 @@ static struct e2fsck_problem problem_table[] = {
          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 */
index 8999a64..61cbbef 100644 (file)
@@ -593,6 +593,13 @@ struct problem_context {
 #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
  */
@@ -948,6 +955,9 @@ struct problem_context {
 /* 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
  */
index 9b90353..8a99453 100644 (file)
@@ -822,6 +822,8 @@ retry_nohash:
        /* 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;
index 429f1cd..b39383d 100644 (file)
@@ -749,6 +749,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
                return retval;
 
        *ret_ctx = ctx;
+       e2fsck_global_ctx = ctx;
 
        setvbuf(stdout, NULL, _IONBF, BUFSIZ);
        setvbuf(stderr, NULL, _IONBF, BUFSIZ);
@@ -990,7 +991,6 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 #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;
@@ -1066,9 +1066,11 @@ static errcode_t try_open_fs(e2fsck_t ctx, int flags, io_manager io_ptr,
                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;
 }
 
@@ -1423,7 +1425,6 @@ failure:
        }
 
        ctx->fs = fs;
-       fs->priv_data = ctx;
        fs->now = ctx->now;
        sb = fs->super;
 
index e7e8704..fec6179 100644 (file)
@@ -794,13 +794,13 @@ void e2fsck_set_bitmap_type(ext2_filsys fs, unsigned int default_type,
                            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;
 }
 
index 88808a3..7a3cbcd 100644 (file)
@@ -59,6 +59,7 @@ OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
        ind_block.o \
        initialize.o \
        inline.o \
+       inline_data.o \
        inode.o \
        io_manager.o \
        ismounted.o \
@@ -133,6 +134,7 @@ SRCS= ext2_err.c \
        $(srcdir)/ind_block.c \
        $(srcdir)/initialize.c \
        $(srcdir)/inline.c \
+       $(srcdir)/inline_data.c \
        $(srcdir)/inode.c \
        $(srcdir)/inode_io.c \
        $(srcdir)/imager.c \
@@ -395,6 +397,11 @@ tst_inline: $(srcdir)/inline.c $(STATIC_LIBEXT2FS) $(DEPSTATIC_LIBCOM_ERR)
        $(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 $@"
@@ -414,7 +421,7 @@ mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR)
 
 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
@@ -424,6 +431,7 @@ check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount \
        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
@@ -767,6 +775,12 @@ inline.o: $(srcdir)/inline.c $(top_builddir)/lib/config.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
+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 \
index 2f7b654..89082a7 100644 (file)
@@ -27,6 +27,7 @@ OBJS=         alloc.obj \
        icount.obj \
        initialize.obj \
        inline.obj \
+       inline_data.obj \
        inode.obj \
        ismounted.obj \
        link.obj \
index db2fd72..c1d0e6f 100644 (file)
@@ -321,6 +321,13 @@ errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
        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)
index d4d5111..2fbb772 100644 (file)
@@ -65,6 +65,7 @@ errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist,
 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;
 
@@ -72,8 +73,17 @@ static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry2 *db_info,
        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;
index 306ffb0..8cb6740 100644 (file)
@@ -127,6 +127,11 @@ errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
                                       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;
@@ -189,30 +194,40 @@ int ext2fs_process_dir_block(ext2_filsys fs,
        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)) {
@@ -220,7 +235,13 @@ int ext2fs_process_dir_block(ext2_filsys fs,
                        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))
@@ -234,7 +255,7 @@ int ext2fs_process_dir_block(ext2_filsys fs,
                                  (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++;
@@ -272,13 +293,21 @@ next:
        }
 
        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;
 }
-
index 09a15fa..7cff343 100644 (file)
@@ -116,6 +116,8 @@ errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
 
        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;
index 93a1106..007103d 100644 (file)
@@ -482,4 +482,34 @@ ec EXT2_ET_BLOCK_BITMAP_CSUM_INVALID,
 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
index ac46ba8..21a8187 100644 (file)
@@ -916,4 +916,14 @@ struct mmp_struct {
  */
 #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 */
index 7f7fd1f..a7b6116 100644 (file)
@@ -298,9 +298,10 @@ struct struct_ext2_filsys {
 /*
  * 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
@@ -438,6 +439,7 @@ struct ext2_extent_info {
 #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
@@ -595,7 +597,8 @@ typedef struct ext2_icount *ext2_icount_t;
                                         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|\
@@ -638,6 +641,13 @@ typedef struct stat ext2fs_struct_stat;
 #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)
@@ -1154,6 +1164,28 @@ extern errcode_t ext2fs_adjust_ea_refcount3(ext2_filsys fs, blk64_t blk,
                                           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);
@@ -1355,6 +1387,8 @@ extern errcode_t ext2fs_get_memalign(unsigned long 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,
@@ -1431,6 +1465,8 @@ int ext2fs_native_flag(void);
 /* 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,
@@ -1519,7 +1555,11 @@ extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
 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);
index 319e9b0..20257e2 100644 (file)
@@ -50,6 +50,7 @@ struct dir_context {
        ext2_ino_t              dir;
        int             flags;
        char            *buf;
+       unsigned int    buflen;
        int (*func)(ext2_ino_t  dir,
                    int entry,
                    struct ext2_dir_entry *dirent,
@@ -68,7 +69,7 @@ struct ext2_inode_cache {
        void *                          buffer;
        blk64_t                         buffer_blk;
        int                             cache_last;
-       int                             cache_size;
+       unsigned int                    cache_size;
        int                             refcount;
        struct ext2_inode_cache_ent     *cache;
 };
@@ -87,6 +88,21 @@ extern int ext2fs_process_dir_block(ext2_filsys      fs,
                                    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 {
index 9649a14..e8dee53 100644 (file)
@@ -186,3 +186,843 @@ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
        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;
+}
index 5a39c32..14eaed3 100644 (file)
@@ -224,6 +224,38 @@ errcode_t ext2fs_file_close(ext2_file_t file)
 }
 
 
+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)
 {
@@ -236,6 +268,10 @@ errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
        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)
@@ -266,6 +302,66 @@ fail:
 }
 
 
+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)
 {
@@ -280,6 +376,16 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
        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)
diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c
new file mode 100644 (file)
index 0000000..9a786fc
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+ * 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(&param, 0, sizeof(param));
+       ext2fs_blocks_count_set(&param, 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, &param,
+                                  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
index 46c1c58..0cea9f0 100644 (file)
@@ -91,7 +91,7 @@ void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
        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;
@@ -109,7 +109,7 @@ static errcode_t create_icache(ext2_filsys fs)
 
        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),
@@ -596,7 +596,7 @@ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
                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;
        }
@@ -732,7 +732,7 @@ errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
                        }
                }
        } else {
-               retval = create_icache(fs);
+               retval = ext2fs_create_inode_cache(fs, 4);
                if (retval)
                        goto errout;
        }
index 4a85439..06c2c7e 100644 (file)
@@ -41,10 +41,20 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
        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) {
@@ -57,14 +67,21 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
        /*
         * 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;
 
@@ -81,16 +98,21 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
        /*
         * 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
@@ -100,18 +122,24 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
        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;
+               }
        }
 
        /*
@@ -136,6 +164,10 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
         * 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)
@@ -145,7 +177,8 @@ errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
        /*
         * 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:
index 44e4ca9..cd32d0a 100644 (file)
@@ -93,3 +93,28 @@ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
        *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;
+}
index a3d020e..95e19d9 100644 (file)
@@ -423,6 +423,30 @@ errout:
        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.
@@ -445,7 +469,9 @@ errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
                        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;
index d72b666..e3628b3 100644 (file)
@@ -209,6 +209,7 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
 {
        unsigned i, has_data_blocks, extra_isize, attr_magic;
        int has_extents = 0;
+       int has_inline_data = 0;
        int islnk = 0;
        __u32 *eaf, *eat;
 
@@ -235,12 +236,18 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
                                           (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) {
@@ -356,13 +363,19 @@ void ext2fs_swap_mmp(struct mmp_struct *mmp)
 
 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);
@@ -387,13 +400,19 @@ errcode_t ext2fs_dirent_swab_in(ext2_filsys fs, char *buf, int flags)
 
 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);
index 895e36e..db5d90a 100644 (file)
@@ -52,6 +52,13 @@ int ext2fs_inode_has_valid_blocks2(ext2_filsys fs, struct ext2_inode *inode)
                        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;
 }
 
index 763504d..cf4a58f 100644 (file)
@@ -391,8 +391,11 @@ errcode_t do_write_internal(ext2_ino_t cwd, const char *src, const char *dest)
                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;
 
@@ -411,6 +414,11 @@ errcode_t do_write_internal(ext2_ino_t cwd, const char *src, const char *dest)
                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;
index 5ec39f5..a8ebf67 100644 (file)
@@ -149,6 +149,9 @@ option to
 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
index 3c71b80..61aced2 100644 (file)
@@ -1058,7 +1058,8 @@ static __u32 ok_features[3] = {
                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|
@@ -1982,6 +1983,9 @@ profile_error:
         * 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;
@@ -2221,7 +2225,8 @@ profile_error:
                                  "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) &&
@@ -2277,6 +2282,18 @@ profile_error:
                                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;
        }
 
index 178733f..4c5dba7 100644 (file)
@@ -16,7 +16,7 @@
                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
        }
index 09dd973..6571764 100644 (file)
@@ -693,7 +693,8 @@ static void rewrite_inodes(ext2_filsys fs)
                        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,
index 11862f6..9f9b447 100644 (file)
@@ -2,9 +2,18 @@ Pass 1: Checking inodes, blocks, and sizes
 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
 
index 9e71264..c7aa088 100644 (file)
@@ -1,8 +1,7 @@
 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.
@@ -11,6 +10,5 @@ The filesystem on test.img is now 5120 blocks long.
 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