]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blobdiff - lib/ext2fs/fileio.c
AOSP: libext2fs: add EXT2_FLAG_SHARE_DUP to de-duplicate data blocks
[thirdparty/e2fsprogs.git] / lib / ext2fs / fileio.c
index 810a7fd8d407de26db7e6034ac8e5a612efd5426..5bc02d03e0dc969b3809feb726bb4202f53b31cf 100644 (file)
@@ -32,6 +32,12 @@ struct ext2_file {
        char                    *buf;
 };
 
+struct block_entry {
+       blk64_t         physblock;
+       unsigned char   sha[EXT2FS_SHA512_LENGTH];
+};
+typedef struct block_entry *block_entry_t;
+
 #define BMAP_BUFFER (file->buf + fs->blocksize)
 
 errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
@@ -389,6 +395,8 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
        errcode_t       retval = 0;
        unsigned int    start, c, count = 0;
        const char      *ptr = (const char *) buf;
+       block_entry_t   new_block = NULL, old_block = NULL;
+       int             bmap_flags = 0;
 
        EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
        fs = file->fs;
@@ -424,22 +432,51 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
                if (retval)
                        goto fail;
 
+               file->flags |= EXT2_FILE_BUF_DIRTY;
+               memcpy(file->buf+start, ptr, c);
+
                /*
                 * OK, the physical block hasn't been allocated yet.
                 * Allocate it.
                 */
                if (!file->physblock) {
+                       bmap_flags = (file->ino ? BMAP_ALLOC : 0);
+                       if (fs->flags & EXT2_FLAG_SHARE_DUP) {
+                               new_block = calloc(1, sizeof(*new_block));
+                               if (!new_block) {
+                                       retval = EXT2_ET_NO_MEMORY;
+                                       goto fail;
+                               }
+                               ext2fs_sha512((const unsigned char*)file->buf,
+                                               fs->blocksize, new_block->sha);
+                               old_block = ext2fs_hashmap_lookup(
+                                                       fs->block_sha_map,
+                                                       new_block->sha,
+                                                       sizeof(new_block->sha));
+                       }
+
+                       if (old_block) {
+                               file->physblock = old_block->physblock;
+                               bmap_flags |= BMAP_SET;
+                               free(new_block);
+                               new_block = NULL;
+                       }
+
                        retval = ext2fs_bmap2(fs, file->ino, &file->inode,
                                              BMAP_BUFFER,
-                                             file->ino ? BMAP_ALLOC : 0,
+                                             bmap_flags,
                                              file->blockno, 0,
                                              &file->physblock);
                        if (retval)
                                goto fail;
+
+                       if (new_block) {
+                               new_block->physblock = file->physblock;
+                               ext2fs_hashmap_add(fs->block_sha_map, new_block,
+                                       new_block->sha, sizeof(new_block->sha));
+                       }
                }
 
-               file->flags |= EXT2_FILE_BUF_DIRTY;
-               memcpy(file->buf+start, ptr, c);
                file->pos += c;
                ptr += c;
                count += c;