]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
AOSP: e2fsdroid: create incremental images
authorAdrien Schildknecht <adriens@google.com>
Wed, 30 Nov 2016 06:15:18 +0000 (22:15 -0800)
committerTheodore Ts'o <tytso@mit.edu>
Wed, 24 May 2017 02:23:34 +0000 (22:23 -0400)
Add an option to read a base_fs file and allocate the blocks according
to the mapping provided by the file.

Test: 1/ Create a normal image and an incremental one.
         Compare the number of blocks that have changed.
      2/ Create an image.
         Create an incremantal image.
         The basefs file and the block_list file are the same.

Change-Id: Ie000ca48cf000d95e7a45a9752699abfc7484b6c
From AOSP commit: 16babe7b79c4c9b6d75d60e30c04a8e24278e4fa

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

index ac1285787cbf1243df9c2fb6efc9daec9b2e290a..f2216912578998496173ebe677545b31e511572c 100644 (file)
@@ -5,11 +5,15 @@ e2fsdroid_src := e2fsdroid.c \
     fsmap.c \
     block_list.c \
     base_fs.c \
-    perms.c
+    perms.c \
+    basefs_allocator.c \
+    hashmap.c \
+    ../../misc/create_inode.c
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(e2fsdroid_src)
 LOCAL_MODULE := e2fsdroid
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../misc/
 LOCAL_SHARED_LIBRARIES := libext2fs-host \
     libext2_com_err-host \
     libcutils \
@@ -22,6 +26,7 @@ include $(BUILD_HOST_EXECUTABLE)
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(e2fsdroid_src)
 LOCAL_MODULE := e2fsdroid
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../misc/
 LOCAL_SHARED_LIBRARIES := libext2fs \
     libext2_com_err \
     libcutils \
index d2b44f9113eecb9a3f6ae3147ccb330e4cdf3388..2dcb5fe3a3c60b7922611b74f9a22f7f11ff93da 100644 (file)
@@ -9,6 +9,121 @@ struct base_fs {
        struct basefs_entry entry;
 };
 
+static FILE *basefs_open(const char *file)
+{
+       char *line = NULL;
+       size_t len;
+       FILE *f = fopen(file, "r");
+       if (!f)
+               return NULL;
+
+       if (getline(&line, &len, f) == -1 || !line)
+               goto err_getline;
+
+       if (strncmp(line, BASE_FS_VERSION, strlen(BASE_FS_VERSION)))
+               goto err_header;
+
+       free(line);
+       return f;
+
+err_header:
+       free(line);
+err_getline:
+       fclose(f);
+       return NULL;
+}
+
+static struct basefs_entry *basefs_readline(FILE *f, const char *mountpoint,
+                                           int *err)
+{
+       char *line = NULL, *saveptr1, *saveptr2, *block_range, *block;
+       int offset;
+       size_t len;
+       struct basefs_entry *entry = NULL;
+       blk64_t range_start, range_end;
+
+       if (getline(&line, &len, f) == -1) {
+               if (feof(f))
+                       goto end;
+               goto err_getline;
+       }
+
+       entry = calloc(1, sizeof(*entry));
+       if (!entry)
+               goto err_alloc;
+
+       /*
+        * With BASEFS version 1.0, a typical line looks like this:
+        * /bin/mke2fs 5000-5004,8000,9000-9990
+        */
+       if (sscanf(line, "%ms%n", &entry->path, &offset) != 1)
+               goto err_sscanf;
+       len = strlen(mountpoint);
+       memmove(entry->path, entry->path + len, strlen(entry->path) - len + 1);
+
+       while (line[offset] == ' ')
+               ++offset;
+
+       block_range = strtok_r(line + offset, ",\n", &saveptr1);
+       while (block_range) {
+               block = strtok_r(block_range, "-", &saveptr2);
+               if (!block)
+                       break;
+               range_start = atoll(block);
+               block = strtok_r(NULL, "-", &saveptr2);
+               range_end = block ? atoll(block) : range_start;
+               add_blocks_to_range(&entry->head, &entry->tail, range_start,
+                                   range_end);
+               block_range = strtok_r(NULL, ",\n", &saveptr1);
+       }
+end:
+       *err = 0;
+       free(line);
+       return entry;
+
+err_sscanf:
+       free(entry);
+err_alloc:
+       free(line);
+err_getline:
+       *err = 1;
+       return NULL;
+}
+
+static void free_base_fs_entry(void *e)
+{
+       struct basefs_entry *entry = e;
+       if (entry) {
+               free(entry->path);
+               free(entry);
+       }
+}
+
+struct hashmap *basefs_parse(const char *file, const char *mountpoint)
+{
+       int err;
+       struct hashmap *entries = NULL;
+       struct basefs_entry *entry;
+       FILE *f = basefs_open(file);
+       if (!f)
+               return NULL;
+       entries = hashmap_create(djb2_hash, free_base_fs_entry, 1024);
+       if (!entries)
+               goto end;
+
+       while ((entry = basefs_readline(f, mountpoint, &err)))
+               hashmap_add(entries, entry, entry->path);
+
+       if (err) {
+               fclose(f);
+               hashmap_free(entries);
+               return NULL;
+       }
+end:
+       fclose(f);
+       return entries;
+}
+
 static void *init(const char *file, const char *mountpoint)
 {
        struct base_fs *params = malloc(sizeof(*params));
index bd441078ddcb95a3efb5b8a3e866df6374107747..94bae29335f1aeb6995a72e729a72218ce762df6 100644 (file)
@@ -2,6 +2,7 @@
 # define BASE_FS_H
 
 # include "fsmap.h"
+# include "hashmap.h"
 # include "block_range.h"
 
 struct basefs_entry {
@@ -12,4 +13,6 @@ struct basefs_entry {
 
 extern struct fsmap_format base_fs_format;
 
+struct hashmap *basefs_parse(const char *file, const char *mountpoint);
+
 #endif /* !BASE_FS_H */
diff --git a/contrib/android/basefs_allocator.c b/contrib/android/basefs_allocator.c
new file mode 100644 (file)
index 0000000..3d014a2
--- /dev/null
@@ -0,0 +1,147 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "basefs_allocator.h"
+#include "block_range.h"
+#include "hashmap.h"
+#include "base_fs.h"
+
+struct base_fs_allocator {
+       struct hashmap *entries;
+       struct basefs_entry *cur_entry;
+};
+
+static errcode_t basefs_block_allocator(ext2_filsys, blk64_t, blk64_t *,
+                                       struct blk_alloc_ctx *ctx);
+
+static void fs_free_blocks_range(ext2_filsys fs, struct block_range *blocks)
+{
+       while (blocks) {
+               ext2fs_unmark_block_bitmap_range2(fs->block_map, blocks->start,
+                       blocks->end - blocks->start + 1);
+               blocks = blocks->next;
+       }
+}
+
+static void fs_reserve_blocks_range(ext2_filsys fs, struct block_range *blocks)
+{
+       while (blocks) {
+               ext2fs_mark_block_bitmap_range2(fs->block_map,
+                       blocks->start, blocks->end - blocks->start + 1);
+               blocks = blocks->next;
+       }
+}
+
+errcode_t base_fs_alloc_load(ext2_filsys fs, const char *file,
+                            const char *mountpoint)
+{
+       errcode_t retval;
+       struct basefs_entry *e;
+       struct hashmap_entry *it = NULL;
+       struct base_fs_allocator *allocator;
+       struct hashmap *entries = basefs_parse(file, mountpoint);
+       if (!entries)
+               return -1;
+
+       allocator = malloc(sizeof(*allocator));
+       if (!allocator)
+               goto err_alloc;
+
+       retval = ext2fs_read_bitmaps(fs);
+       if (retval)
+               goto err_bitmap;
+       while ((e = hashmap_iter_in_order(entries, &it)))
+               fs_reserve_blocks_range(fs, e->head);
+
+       allocator->cur_entry = NULL;
+       allocator->entries = entries;
+
+       /* Overhide the default allocator */
+       fs->get_alloc_block2 = basefs_block_allocator;
+       fs->priv_data = allocator;
+
+       return 0;
+
+err_bitmap:
+       free(allocator);
+err_alloc:
+       hashmap_free(entries);
+       return EXIT_FAILURE;
+}
+
+static errcode_t basefs_block_allocator(ext2_filsys fs, blk64_t goal,
+                                       blk64_t *ret, struct blk_alloc_ctx *ctx)
+{
+       errcode_t retval;
+       struct block_range *next_range;
+       struct base_fs_allocator *allocator = fs->priv_data;
+       struct basefs_entry *e = allocator->cur_entry;
+
+       /* Try to get a block from the base_fs */
+       if (e && e->head && ctx && (ctx->flags & BLOCK_ALLOC_DATA)) {
+               *ret = e->head->start;
+               e->head->start += 1;
+               if (e->head->start > e->head->end) {
+                       next_range = e->head->next;
+                       free(e->head);
+                       e->head = next_range;
+               }
+       } else { /* Allocate a new block */
+               retval = ext2fs_new_block2(fs, goal, fs->block_map, ret);
+               if (retval)
+                       return retval;
+               ext2fs_mark_block_bitmap2(fs->block_map, *ret);
+       }
+       return 0;
+}
+
+void base_fs_alloc_cleanup(ext2_filsys fs)
+{
+       struct basefs_entry *e;
+       struct hashmap_entry *it = NULL;
+       struct base_fs_allocator *allocator = fs->priv_data;
+
+       while ((e = hashmap_iter_in_order(allocator->entries, &it))) {
+               fs_free_blocks_range(fs, e->head);
+               delete_block_ranges(e->head);
+               e->head = e->tail = NULL;
+       }
+
+       fs->priv_data = NULL;
+       fs->get_alloc_block2 = NULL;
+       hashmap_free(allocator->entries);
+       free(allocator);
+}
+
+errcode_t base_fs_alloc_set_target(ext2_filsys fs, const char *target_path,
+       const char *name EXT2FS_ATTR((unused)),
+       ext2_ino_t parent_ino EXT2FS_ATTR((unused)),
+       ext2_ino_t root EXT2FS_ATTR((unused)), mode_t mode)
+{
+       struct base_fs_allocator *allocator = fs->priv_data;
+
+       if (mode != S_IFREG)
+               return 0;
+
+       if (allocator)
+               allocator->cur_entry = hashmap_lookup(allocator->entries,
+                                                     target_path);
+       return 0;
+}
+
+errcode_t base_fs_alloc_unset_target(ext2_filsys fs,
+        const char *target_path EXT2FS_ATTR((unused)),
+       const char *name EXT2FS_ATTR((unused)),
+       ext2_ino_t parent_ino EXT2FS_ATTR((unused)),
+       ext2_ino_t root EXT2FS_ATTR((unused)), mode_t mode)
+{
+       struct base_fs_allocator *allocator = fs->priv_data;
+
+       if (!allocator || !allocator->cur_entry || mode != S_IFREG)
+               return 0;
+
+       fs_free_blocks_range(fs, allocator->cur_entry->head);
+       delete_block_ranges(allocator->cur_entry->head);
+       allocator->cur_entry->head = allocator->cur_entry->tail = NULL;
+       allocator->cur_entry = NULL;
+       return 0;
+}
diff --git a/contrib/android/basefs_allocator.h b/contrib/android/basefs_allocator.h
new file mode 100644 (file)
index 0000000..f1109cd
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef BASE_FS_ALLOCATOR_H
+# define BASE_FS_ALLOCATOR_H
+
+# include <time.h>
+# include <ext2fs/ext2fs.h>
+
+errcode_t base_fs_alloc_load(ext2_filsys fs, const char *file,
+                            const char *mountpoint);
+void base_fs_alloc_cleanup(ext2_filsys fs);
+
+errcode_t base_fs_alloc_set_target(ext2_filsys fs, const char *target_path,
+       const char *name, ext2_ino_t parent_ino, ext2_ino_t root, mode_t mode);
+errcode_t base_fs_alloc_unset_target(ext2_filsys fs, const char *target_path,
+       const char *name, ext2_ino_t parent_ino, ext2_ino_t root, mode_t mode);
+
+#endif /* !BASE_FS_ALLOCATOR_H */
index 7237030a301efc953d838236181a9c56f22fb428..e3b819fb6a3d86d42a8b9af0ce5d40c62b5b50c7 100644 (file)
@@ -8,24 +8,28 @@
 #include "perms.h"
 #include "base_fs.h"
 #include "block_list.h"
+#include "basefs_allocator.h"
+#include "create_inode.h"
 
-const char *prog_name = "e2fsdroid";
-const char *in_file;
-const char *block_list;
-const char *basefs_out;
-const char *mountpoint = "";
+static char *prog_name = "e2fsdroid";
+static char *in_file;
+static char *block_list;
+static char *basefs_out;
+static char *basefs_in;
+static char *mountpoint = "";
 static time_t fixed_time;
 static char *fs_config_file;
 static char *file_contexts;
 static char *product_out;
+static char *src_dir;
 static int android_configure;
-int android_sparse_file = 1;
+static int android_sparse_file = 1;
 
 static void usage(int ret)
 {
        fprintf(stderr, "%s [-B block_list] [-D basefs_out] [-T timestamp]\n"
                        "\t[-C fs_config] [-S file_contexts] [-p product_out]\n"
-                       "\t[-a mountpoint] [-e] image\n",
+                       "\t[-a mountpoint] [-d basefs_in] [-f src_dir] [-e] image\n",
                 prog_name);
        exit(ret);
 }
@@ -53,10 +57,11 @@ int main(int argc, char *argv[])
        errcode_t retval;
        io_manager io_mgr;
        ext2_filsys fs = NULL;
+        struct fs_ops_callbacks fs_callbacks = { NULL, NULL };
 
        add_error_table(&et_ext2_error_table);
 
-       while ((c = getopt (argc, argv, "T:C:S:p:a:D:B:e")) != EOF) {
+       while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:e")) != EOF) {
                switch (c) {
                case 'T':
                        fixed_time = strtoul(optarg, &p, 0);
@@ -79,9 +84,15 @@ int main(int argc, char *argv[])
                case 'D':
                        basefs_out = absolute_path(optarg);
                        break;
+               case 'd':
+                       basefs_in = absolute_path(optarg);
+                       break;
                case 'B':
                        block_list = absolute_path(optarg);
                        break;
+               case 'f':
+                       src_dir = absolute_path(optarg);
+                       break;
                case 'e':
                        android_sparse_file = 0;
                        break;
@@ -102,6 +113,31 @@ int main(int argc, char *argv[])
                return retval;
        }
 
+       if (src_dir) {
+               ext2fs_read_bitmaps(fs);
+               if (basefs_in) {
+                       retval = base_fs_alloc_load(fs, basefs_in, mountpoint);
+                       if (retval) {
+                               com_err(prog_name, retval, "%s",
+                               "while reading base_fs file");
+                           exit(1);
+                       }
+                       fs_callbacks.create_new_inode =
+                               base_fs_alloc_set_target;
+                       fs_callbacks.end_create_new_inode =
+                               base_fs_alloc_unset_target;
+               }
+               retval = populate_fs2(fs, EXT2_ROOT_INO, src_dir,
+                                     EXT2_ROOT_INO, &fs_callbacks);
+               if (retval) {
+                       com_err(prog_name, retval, "%s",
+                       "while populating file system");
+                   exit(1);
+               }
+               if (basefs_in)
+                       base_fs_alloc_cleanup(fs);
+       }
+
        if (android_configure) {
                retval = android_configure_fs(fs, product_out, mountpoint,
                        file_contexts, fs_config_file, fixed_time);
diff --git a/contrib/android/hashmap.c b/contrib/android/hashmap.c
new file mode 100644 (file)
index 0000000..eee0071
--- /dev/null
@@ -0,0 +1,76 @@
+#include "hashmap.h"
+#include <string.h>
+
+uint32_t djb2_hash(const void *str)
+{
+       int c;
+       const char *s = str;
+       uint32_t hash = 5381;
+
+       while ((c = *s++))
+               hash = ((hash << 5) + hash) + c;
+       return hash;
+}
+
+struct hashmap *hashmap_create(uint32_t(*hash_fct)(const void*),
+                              void(*free_fct)(void*), size_t size)
+{
+       struct hashmap *h = calloc(sizeof(struct hashmap) +
+                                     sizeof(struct hashmap_entry) * size, 1);
+       h->size = size;
+       h->free = free_fct;
+       h->hash = hash_fct;
+       h->first = h->last = NULL;
+       return h;
+}
+
+void hashmap_add(struct hashmap *h, void *data, const void *key)
+{
+       uint32_t hash = h->hash(key) % h->size;
+       struct hashmap_entry *e = malloc(sizeof(*e));
+
+       e->data = data;
+       e->key = key;
+       e->next = h->entries[hash];
+       h->entries[hash] = e;
+
+       e->list_prev = NULL;
+       e->list_next = h->first;
+       if (h->first)
+               h->first->list_prev = e;
+       h->first = e;
+       if (!h->last)
+               h->last = e;
+}
+
+void *hashmap_lookup(struct hashmap *h, const void *key)
+{
+       struct hashmap_entry *iter;
+       uint32_t hash = h->hash(key) % h->size;
+
+       for (iter = h->entries[hash]; iter; iter = iter->next)
+               if (!strcmp(iter->key, key))
+                       return iter->data;
+       return NULL;
+}
+
+void *hashmap_iter_in_order(struct hashmap *h, struct hashmap_entry **it)
+{
+       *it = *it ? (*it)->list_next : h->first;
+       return *it ? (*it)->data : NULL;
+}
+
+void hashmap_free(struct hashmap *h)
+{
+       for (size_t i = 0; i < h->size; ++i) {
+               struct hashmap_entry *it = h->entries[i];
+               while (it) {
+                       struct hashmap_entry *tmp = it->next;
+                       if (h->free)
+                               h->free(it->data);
+                       free(it);
+                       it = tmp;
+               }
+       }
+       free(h);
+}
diff --git a/contrib/android/hashmap.h b/contrib/android/hashmap.h
new file mode 100644 (file)
index 0000000..70d0ed1
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef HASHMAP_H
+# define HASHMAP_H
+
+# include <stdlib.h>
+# include <stdint.h>
+
+struct hashmap {
+       uint32_t size;
+       uint32_t(*hash)(const void *key);
+       void(*free)(void*);
+       struct hashmap_entry *first;
+       struct hashmap_entry *last;
+       struct hashmap_entry {
+               void *data;
+               const void *key;
+               struct hashmap_entry *next;
+               struct hashmap_entry *list_next;
+               struct hashmap_entry *list_prev;
+       } *entries[0];
+};
+
+struct hashmap *hashmap_create(uint32_t(*hash_fct)(const void*),
+                              void(*free_fct)(void*), size_t size);
+void hashmap_add(struct hashmap *h, void *data, const void *key);
+void *hashmap_lookup(struct hashmap *h, const void *key);
+void *hashmap_iter_in_order(struct hashmap *h, struct hashmap_entry **it);
+void hashmap_del(struct hashmap *h, struct hashmap_entry *e);
+void hashmap_free(struct hashmap *h);
+
+uint32_t djb2_hash(const void *str);
+
+#endif /* !HASHMAP_H */