Whamcloud - gitweb
AOSP: e2fsdroid: a tool to create android compatible image
authorAdrien Schildknecht <adriens@google.com>
Wed, 30 Nov 2016 05:36:43 +0000 (21:36 -0800)
committerTheodore Ts'o <tytso@mit.edu>
Wed, 24 May 2017 02:08:44 +0000 (22:08 -0400)
Add an option to generate a block_list file from an existing ext4 or sparse
image.

Test: make_ext4 and e2fsdroid both generate the same list of file.

Change-Id: I5ecc6521797397102904bf510c283dfd50a72721
From AOSP commit: fdc29bee07aef3a379f3ec3ccbaa551ff6500bff

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
contrib/Android.mk
contrib/android/Android.mk [new file with mode: 0644]
contrib/android/block_list.c [new file with mode: 0644]
contrib/android/block_list.h [new file with mode: 0644]
contrib/android/block_range.c [new file with mode: 0644]
contrib/android/block_range.h [new file with mode: 0644]
contrib/android/e2fsdroid.c [new file with mode: 0644]
contrib/android/fsmap.c [new file with mode: 0644]
contrib/android/fsmap.h [new file with mode: 0644]

index f8d74c3..9f57daa 100644 (file)
@@ -78,3 +78,5 @@ LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_HOST_EXECUTABLE)
 
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk
new file mode 100644 (file)
index 0000000..8199037
--- /dev/null
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+
+e2fsdroid_src := e2fsdroid.c \
+    block_range.c \
+    fsmap.c \
+    block_list.c
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(e2fsdroid_src)
+LOCAL_MODULE := e2fsdroid
+LOCAL_SHARED_LIBRARIES := libext2fs-host \
+    libext2_com_err-host
+include $(BUILD_HOST_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(e2fsdroid_src)
+LOCAL_MODULE := e2fsdroid
+LOCAL_SHARED_LIBRARIES := libext2fs \
+    libext2_com_err
+include $(BUILD_EXECUTABLE)
diff --git a/contrib/android/block_list.c b/contrib/android/block_list.c
new file mode 100644 (file)
index 0000000..fec9dec
--- /dev/null
@@ -0,0 +1,94 @@
+#include "block_list.h"
+#include "block_range.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+struct block_list {
+       FILE *f;
+       const char *mountpoint;
+
+       struct {
+               const char *filename;
+               struct block_range *head;
+               struct block_range *tail;
+       } entry;
+};
+
+static void *init(const char *file, const char *mountpoint)
+{
+       struct block_list *params = malloc(sizeof(*params));
+
+       if (!params)
+               return NULL;
+       params->mountpoint = mountpoint;
+       params->f = fopen(file, "w+");
+       if (!params->f) {
+               free(params);
+               return NULL;
+       }
+       return params;
+}
+
+static int start_new_file(char *path, ext2_ino_t ino EXT2FS_ATTR((unused)),
+                         struct ext2_inode *inode EXT2FS_ATTR((unused)),
+                         void *data)
+{
+       struct block_list *params = data;
+
+       params->entry.head = params->entry.tail = NULL;
+       params->entry.filename = LINUX_S_ISREG(inode->i_mode) ? path : NULL;
+       return 0;
+}
+
+static int add_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk64_t blocknr,
+                    int metadata, void *data)
+{
+       struct block_list *params = data;
+
+       if (params->entry.filename && !metadata)
+               add_blocks_to_range(&params->entry.head, &params->entry.tail,
+                                   blocknr, blocknr);
+       return 0;
+}
+
+static int inline_data(void *inline_data EXT2FS_ATTR((unused)),
+                      void *data EXT2FS_ATTR((unused)))
+{
+       return 0;
+}
+
+static int end_new_file(void *data)
+{
+       struct block_list *params = data;
+
+       if (!params->entry.filename)
+               return 0;
+       if (fprintf(params->f, "%s%s ", params->mountpoint,
+                   params->entry.filename) < 0
+           || write_block_ranges(params->f, params->entry.head, " ")
+           || fwrite("\n", 1, 1, params->f) != 1)
+               return -1;
+
+       delete_block_ranges(params->entry.head);
+       return 0;
+}
+
+static int cleanup(void *data)
+{
+       struct block_list *params = data;
+
+       fclose(params->f);
+       free(params);
+       return 0;
+}
+
+struct fsmap_format block_list_format = {
+       .init = init,
+       .start_new_file = start_new_file,
+       .add_block = add_block,
+       .inline_data = inline_data,
+       .end_new_file = end_new_file,
+       .cleanup = cleanup,
+};
diff --git a/contrib/android/block_list.h b/contrib/android/block_list.h
new file mode 100644 (file)
index 0000000..47041e4
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef BLOCK_LIST_H
+# define BLOCK_LIST_H
+
+# include "fsmap.h"
+
+extern struct fsmap_format block_list_format;
+
+#endif /* !BLOCK_LIST_H */
diff --git a/contrib/android/block_range.c b/contrib/android/block_range.c
new file mode 100644 (file)
index 0000000..d054123
--- /dev/null
@@ -0,0 +1,65 @@
+#include "block_range.h"
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <stdio.h>
+
+struct block_range *new_block_range(blk64_t start, blk64_t end)
+{
+       struct block_range *range = malloc(sizeof(*range));
+       range->start = start;
+       range->end = end;
+       range->next = NULL;
+       return range;
+}
+
+void add_blocks_to_range(struct block_range **head, struct block_range **tail,
+                        blk64_t blk_start, blk64_t blk_end)
+{
+       if (*head == NULL)
+               *head = *tail = new_block_range(blk_start, blk_end);
+       else if ((*tail)->end + 1 == blk_start)
+               (*tail)->end += (blk_end - blk_start + 1);
+       else {
+               struct block_range *range = new_block_range(blk_start, blk_end);
+               (*tail)->next = range;
+               *tail = range;
+       }
+}
+
+void delete_block_ranges(struct block_range *head)
+{
+       struct block_range *tmp;
+
+       while (head) {
+               tmp = head->next;
+               free(head);
+               head = tmp;
+       }
+}
+
+int write_block_ranges(FILE *f, struct block_range *range,
+                                    char *sep)
+{
+       int len;
+       char *buf;
+
+       while (range) {
+               if (range->start == range->end)
+                       len = asprintf(&buf, "%llu%s", range->start, sep);
+               else
+                       len = asprintf(&buf, "%llu-%llu%s", range->start,
+                                      range->end, sep);
+               if (fwrite(buf, 1, len, f) != (size_t)len) {
+                       free(buf);
+                       return -1;
+               }
+               free(buf);
+               range = range->next;
+       }
+
+       len = strlen(sep);
+       if (fseek(f, -len, SEEK_CUR) == -len)
+               return -1;
+       return 0;
+}
diff --git a/contrib/android/block_range.h b/contrib/android/block_range.h
new file mode 100644 (file)
index 0000000..31e3c23
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef BLOCK_RANGE_H
+# define BLOCK_RANGE_H
+
+# include <sys/types.h>
+# include <ext2fs/ext2fs.h>
+
+struct block_range {
+       blk64_t start;
+       blk64_t end;
+       struct block_range *next;
+};
+
+void add_blocks_to_range(struct block_range **head, struct block_range **tail,
+                        blk64_t blk_start, blk64_t blk_end);
+void delete_block_ranges(struct block_range *head);
+int write_block_ranges(FILE *f, struct block_range *range, char *sep);
+
+#endif /* !BLOCK_RANGE_H */
diff --git a/contrib/android/e2fsdroid.c b/contrib/android/e2fsdroid.c
new file mode 100644 (file)
index 0000000..816104f
--- /dev/null
@@ -0,0 +1,91 @@
+#include <stdio.h>
+#include <getopt.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <ext2fs/ext2fs.h>
+
+#include "block_list.h"
+
+const char *prog_name = "e2fsdroid";
+const char *in_file;
+const char *block_list;
+const char *mountpoint = "";
+int android_sparse_file = 1;
+
+static void usage(int ret)
+{
+       fprintf(stderr, "%s [-B block_list] [-e] image\n", prog_name);
+       exit(ret);
+}
+
+static char *absolute_path(const char *file)
+{
+       char *ret;
+       char cwd[PATH_MAX];
+
+       if (file[0] != '/') {
+               getcwd(cwd, PATH_MAX);
+               ret = malloc(strlen(cwd) + 1 + strlen(file) + 1);
+               if (ret)
+                       sprintf(ret, "%s/%s", cwd, file);
+       } else
+               ret = strdup(file);
+       return ret;
+}
+
+int main(int argc, char *argv[])
+{
+       int c;
+       int flags = EXT2_FLAG_RW;
+       errcode_t retval;
+       io_manager io_mgr;
+       ext2_filsys fs = NULL;
+
+       add_error_table(&et_ext2_error_table);
+
+       while ((c = getopt (argc, argv, "B:e")) != EOF) {
+               switch (c) {
+               case 'B':
+                       block_list = absolute_path(optarg);
+                       break;
+               case 'e':
+                       android_sparse_file = 0;
+                       break;
+               default:
+                       usage(EXIT_FAILURE);
+               }
+       }
+       if (optind >= argc) {
+               fprintf(stderr, "Expected filename after options\n");
+               exit(EXIT_FAILURE);
+       }
+       in_file = strdup(argv[optind]);
+
+       io_mgr = android_sparse_file ? sparse_io_manager: unix_io_manager;
+       retval = ext2fs_open(in_file, flags, 0, 0, io_mgr, &fs);
+       if (retval) {
+               com_err(prog_name, retval, "while opening file %s\n", in_file);
+               return retval;
+       }
+
+       if (block_list) {
+               retval = fsmap_iter_filsys(fs, &block_list_format, block_list,
+                                          mountpoint);
+               if (retval) {
+                       com_err(prog_name, retval, "%s",
+                               "while creating block_list");
+                       exit(1);
+               }
+       }
+
+       retval = ext2fs_close_free(&fs);
+       if (retval) {
+               com_err(prog_name, retval, "%s",
+                               "while writing superblocks");
+               exit(1);
+       }
+
+       remove_error_table(&et_ext2_error_table);
+       return 0;
+}
diff --git a/contrib/android/fsmap.c b/contrib/android/fsmap.c
new file mode 100644 (file)
index 0000000..0a4867b
--- /dev/null
@@ -0,0 +1,117 @@
+#include "fsmap.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "support/nls-enable.h"
+
+struct walk_ext_priv_data {
+       char                    *path;
+       ext2_filsys             fs;
+       struct fsmap_format     *format;
+};
+
+static int walk_block(ext2_filsys fs  EXT2FS_ATTR((unused)), blk64_t *blocknr,
+                     e2_blkcnt_t blockcnt,
+                     blk64_t ref64_blk EXT2FS_ATTR((unused)),
+                     int ref_offset EXT2FS_ATTR((unused)),
+                     void *priv)
+{
+       struct walk_ext_priv_data *pdata = priv;
+       struct fsmap_format *format = pdata->format;
+
+       return format->add_block(fs, *blocknr, blockcnt < 0, format->private);
+}
+
+static errcode_t ino_iter_blocks(ext2_filsys fs, ext2_ino_t ino,
+                                struct walk_ext_priv_data *pdata)
+{
+       errcode_t retval;
+       struct ext2_inode inode;
+       struct fsmap_format *format = pdata->format;
+
+       retval = ext2fs_read_inode(fs, ino, &inode);
+       if (retval)
+               return retval;
+
+       if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
+               return format->inline_data(&(inode.i_block[0]),
+                                          format->private);
+
+       retval = ext2fs_block_iterate3(fs, ino, 0, NULL, walk_block, pdata);
+       if (retval)
+               com_err(__func__, retval, _("listing blocks of ino \"%u\""),
+                       ino);
+       return retval;
+}
+
+static int is_dir(ext2_filsys fs, ext2_ino_t ino)
+{
+       struct ext2_inode inode;
+
+       if (ext2fs_read_inode(fs, ino, &inode))
+               return 0;
+       return S_ISDIR(inode.i_mode);
+}
+
+static int walk_ext_dir(ext2_ino_t dir EXT2FS_ATTR((unused)),
+                       int flags EXT2FS_ATTR((unused)),
+                       struct ext2_dir_entry *de,
+                       int offset EXT2FS_ATTR((unused)),
+                       int blocksize EXT2FS_ATTR((unused)),
+                       char *buf EXT2FS_ATTR((unused)), void *priv_data)
+{
+       errcode_t retval;
+       struct ext2_inode inode;
+       char *filename, *cur_path, *name = de->name;
+       int name_len = de->name_len & 0xff;
+       struct walk_ext_priv_data *pdata = priv_data;
+       struct fsmap_format *format = pdata->format;
+
+       if (!strncmp(name, ".", name_len)
+           || !strncmp(name, "..", name_len)
+           || !strncmp(name, "lost+found", 10))
+               return 0;
+
+       if (asprintf(&filename, "%s/%.*s", pdata->path, name_len, name) < 0)
+               return -ENOMEM;
+
+       retval = ext2fs_read_inode(pdata->fs, de->inode, &inode);
+       if (retval) {
+               com_err(__func__, retval, _("reading ino \"%u\""), de->inode);
+               goto end;
+       }
+       format->start_new_file(filename, de->inode, &inode, format->private);
+       retval = ino_iter_blocks(pdata->fs, de->inode, pdata);
+       if (retval)
+               return retval;
+       format->end_new_file(format->private);
+
+       if (is_dir(pdata->fs, de->inode)) {
+               cur_path = pdata->path;
+               pdata->path = filename;
+               ext2fs_dir_iterate2(pdata->fs, de->inode, 0, NULL,
+                                   walk_ext_dir, pdata);
+               pdata->path = cur_path;
+       }
+
+end:
+       free(filename);
+       return 0;
+}
+
+errcode_t fsmap_iter_filsys(ext2_filsys fs, struct fsmap_format *format,
+                           const char *file, const char *mountpoint)
+{
+       struct walk_ext_priv_data pdata;
+
+       format->private = format->init(file, mountpoint);
+       pdata.fs = fs;
+       pdata.path = "";
+       pdata.format = format;
+
+       ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_ext_dir, &pdata);
+
+       format->cleanup(format->private);
+       return 0;
+}
diff --git a/contrib/android/fsmap.h b/contrib/android/fsmap.h
new file mode 100644 (file)
index 0000000..9f84a71
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef FSMAP_H
+# define FSMAP_H
+
+# ifndef _GNU_SOURCE
+#  define _GNU_SOURCE // asprintf
+# endif
+# include <stdio.h>
+# include <stdint.h>
+# include <stdbool.h>
+# include <sys/types.h>
+# include <ext2fs/ext2fs.h>
+
+struct fsmap_format {
+       void* (* init)(const char *file, const char *mountpoint);
+       int   (* start_new_file)(char *path, ext2_ino_t ino,
+                                struct ext2_inode *inode, void *data);
+       int   (* add_block)(ext2_filsys fs, blk64_t blocknr, int metadata,
+                           void *data);
+       int   (* inline_data)(void *inline_data, void *data);
+       int   (* end_new_file)(void *data);
+       int   (* cleanup)(void *data);
+
+       void *private;
+};
+
+errcode_t fsmap_iter_filsys(ext2_filsys fs, struct fsmap_format *format,
+                           const char *file, const char *mountpoint);
+
+#endif /* !FSMAP_H */