]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
debugfs: make expand command support inline data
authorZheng Liu <wenqing.lz@taobao.com>
Mon, 3 Mar 2014 05:31:27 +0000 (00:31 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 4 Mar 2014 13:46:14 +0000 (08:46 -0500)
This commit defines a ext2fs_inline_data_expand() to expand an inode with
inline data.  In this commit this function only can expand a directory.
But later it will expand a file.

Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
lib/ext2fs/expanddir.c
lib/ext2fs/ext2fsP.h
lib/ext2fs/inline_data.c

index 09a15fa8799d1a13cfc05e4ce733a39547564ad0..7cff343d13989660a17a32a6339bc31da073e637 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 696603a04c12b008aee8dcf9ae564fbf85a3e7af..47863a913a03cb2b7f9c4416eae9a5e7d9495a12 100644 (file)
@@ -90,6 +90,7 @@ extern int ext2fs_process_dir_block(ext2_filsys       fs,
 
 extern errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino,
                                         size_t *size);
+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);
index a9b6681a1e62adb27117f39e4c019642e31b6067..b06b052c53e44c958e1e47fa262ea5db89064693 100644 (file)
@@ -230,3 +230,190 @@ out:
        ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
        return ret;
 }
+
+static 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;
+}
+
+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;
+       blk64_t blk;
+       char *inline_buf = 0;
+       char *blk_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;
+
+       /* Get inline data first */
+       data.fs = fs;
+       data.ino = ino;
+       retval = ext2fs_inline_data_ea_get(&data);
+       if (retval)
+               return retval;
+       retval = ext2fs_get_mem(EXT4_MIN_INLINE_DATA_SIZE + data.ea_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);
+       }
+
+#ifdef WORDS_BIGENDIAN
+       retval = ext2fs_dirent_swab_in2(fs, inline_buf,
+                       data.ea_size + EXT4_MIN_INLINE_DATA_SIZE);
+       if (retval)
+               goto errout;
+#endif
+
+       memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+       retval = ext2fs_inline_data_ea_remove(fs, ino);
+       if (retval)
+               goto errout;
+
+       retval = ext2fs_get_mem(fs->blocksize, &blk_buf);
+       if (retval)
+               goto errout;
+
+       /* Adjust the rec_len */
+       retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, inline_buf,
+                                               EXT4_MIN_INLINE_DATA_SIZE +
+                                                       data.ea_size);
+       if (retval)
+               goto errout;
+
+       /* 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_stats2(fs, blk, +1);
+
+errout:
+       if (blk_buf)
+               ext2fs_free_mem(&blk_buf);
+       if (inline_buf)
+               ext2fs_free_mem(&inline_buf);
+       ext2fs_free_mem(&data.ea_data);
+       return retval;
+}