首页 > 代码库 > 基于Dedup的数据打包技术

基于Dedup的数据打包技术

基于Dedup的数据打包技术

0、引言
    Tar, winrar, winzip是最为常见的数据打包工具软件,它们把文件集体封装成一个单独的数据包,从而方便数据的分布、传输、归档以及持久保存等目的。这类工具通常都支持数据压缩技术,从而有效减少数据的存储空间,常用压缩算法有Huffman编码、Z77/z78、LZW等。压缩算法的原理是通过对数据的重新编码,高频率数据片段采用较短的编码,低频率数据片段采用较长的编码,从而获得全局的上数据量较小的文件表示。

1、Dedup原理
   Deduplication,即重复数据删除,它是一种非常新的且流行度很高的存储技术,可以大大减少数据的数量。重复数据删除技术,通过数据集中重复的数据,从而消除冗余数据。借助dedup技术,可以提高存储系统的效率,有效节约成本、减少传输过程中的网络带宽。同时它也是一种绿色存储技术,能有效降低能耗(存储空间小了,所需要存储系统磁盘也就少了,自然所需要电能就减少了)。
   dedup按照消重的粒度可以分为文件级和数据块级。文件级的dedup技术也称为单一实例存储(SIS, Single Instance Store),数据块级的重复数据删除,其消重粒度更小,可以达到4-24KB之间。显然,数据块级的可以提供更高的数据消重率,因此目前主流的dedup产品都是数据块级的。重复数据删除原理如下图所示。将文件都分割成数据块(可以是定长或变长的数据块),采用MD5或SHA1等Hash算法(可以同时使用两种或以上hash算法,或CRC校验等,以获得非常小概率的数据碰撞发生)为数据块计算FingerPrint。具有相同FP指纹的数据块即可认为是相同的数据块,存储系统中仅需要保留一份。这样,一个物理文件在存储系统中就对应一个逻辑表示,由一组FP组成的元数据。当进行读取文件时,先读取逻辑文件,然后根据FP序列,从存储系统中取出相应数据块,还原物理文件副本。
   重复数据删除目前主要应用于数据备份,因此对数据进行多次备份后,存在大量重复数据,非常适合dedup技术。事实上,dedup技术可以用于很多场合,包括在线数据、近线数据、离线数据存储系统,甚至可以在文件系统、卷管理器、NAS、SAN中实施。还可以用于网络数据传输,当然也可以应用于数据打包技术。dedup技术可以帮助众多应用降低数据存储量,节省网络带宽,提高存储效率、减小备份窗口,绿色节能。这里,基于dedup实现一种数据打包技术。

 



2、基于Dedup的数据打包模型

  数据包文件的数据布局:

Header

Unique block data

File metadata

 数据包由三部分组成:文件头(header)、唯一数据块集(unique block data)和逻辑文件元数据(file metadata)。其中,header为一个结构体,定义了数据块大小、唯一数据块数量、数据块ID大小、包中文件数量、元数据在包中的位置等元信息。文件头后紧接就存储着所有唯一的数据块,大小和数量由文件头中元信息指示。在数据块之后,就是数据包中文件的逻辑表示元数据,由多个实体组成,结构如下所示,一个实体表示一个文件。解包时根据文件的元数据,逐一提取数据块,还原出当初的物理文件。
 
  逻辑文件的元数据表示:

 

 

Entry header

pathname

Entry data

Last block data

 

  逻辑文件的实体头中记录着文件名长度、数据块数量、数据块ID大小和最后一个数据块大小等信息。紧接着是文件名数据,长度在实体头中定义。文件名数据之后,存储着一组唯一数据块的编号,编号与唯一数据块集中的数据块一一对应。最后存储着文件最后一个数据块,由于这个数据块大小通常比正常数据块小,重复概率非常小,因此单独保存。


3、原型实现
基于上面的数据布局,就可以实现支持重复数据删除的数据打包方法。本人在Linux系统上实现了一个原型,实现中使用了hashtable来记录和查询唯一数据块信息,使用MD5算法计算数据块指纹,并使用zlib中的z77压缩算法对删除了重复数据后的数据包进行压缩。hashtable, MD5, z77算法和实现,这里不作介绍,有兴趣的读者可以参考相关资源。下面给出dedup.h, dedup.c undedup.c源码文件。目前实现的原型还相对比较粗糙。

/* dedup.h */

 

[cpp] view plain copy
 
 print?
  1. #ifndef _DEDUP_H  
  2. #define _DEDUP_H  
  3. #include "md5.h"  
  4. #include "hash.h"  
  5. #include "hashtable.h"  
  6. #include "libz.h"  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /*  
  11.  * deduplication file data layout 
  12.  * -------------------------------------------------- 
  13.  * |  header  |  unique block data |  file metadata | 
  14.  * -------------------------------------------------- 
  15.  * 
  16.  * file metedata entry layout 
  17.  * ----------------------------------------------------------------- 
  18.  * |  entry header  |  pathname  |  entry data  |  last block data | 
  19.  * ----------------------------------------------------------------- 
  20.  */  
  21. typedef unsigned int block_id_t;  
  22. #define BLOCK_SIZE  4096     /* 4K Bytes */  
  23. #define BACKET_SIZE     10240  
  24. #define MAX_PATH_LEN    255  
  25. #define BLOCK_ID_SIZE   (sizeof(block_id_t))  
  26. /* deduplication package header */  
  27. #define DEDUP_MAGIC_NUM 0x1329149  
  28. typedef struct _dedup_package_header {  
  29.     unsigned int block_size;  
  30.     unsigned int block_num;  
  31.     unsigned int blockid_size;  
  32.     unsigned int magic_num;  
  33.     unsigned int file_num;  
  34.     unsigned long long metadata_offset;  
  35. } dedup_package_header;  
  36. #define DEDUP_PKGHDR_SIZE   (sizeof(dedup_package_header))  
  37. /* deduplication metadata entry header */  
  38. typedef struct _dedup_entry_header {  
  39.     unsigned int path_len;  
  40.     unsigned int block_num;  
  41.     unsigned int entry_size;  
  42.     unsigned int last_block_size;  
  43.     int mode;  
  44. } dedup_entry_header;  
  45. #define DEDUP_ENTRYHDR_SIZE (sizeof(dedup_entry_header))  
  46. #ifdef __cplusplus  
  47. }  
  48. #endif  
  49. #endif  


/* dedup.c */

 

 

[cpp] view plain copy
 
 print?
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <sys/types.h>  
  4. #include <sys/stat.h>  
  5. #include <dirent.h>  
  6. #include <unistd.h>  
  7. #include <getopt.h>  
  8. #include <fcntl.h>  
  9. #include <dirent.h>  
  10. #include <errno.h>  
  11. #include "dedup.h"  
  12. /* unique block number in package */  
  13. static unsigned int g_unique_block_nr = 0;  
  14. /* regular file number in package */  
  15. static unsigned int g_regular_file_nr = 0;  
  16. /* block length */  
  17. static unsigned int g_block_size = BLOCK_SIZE;  
  18. /* hashtable backet number */  
  19. static unsigned int g_htab_backet_nr = BACKET_SIZE;  
  20. void show_md5(unsigned char md5_checksum[16])  
  21. {  
  22.     int i;  
  23.     for (i = 0; i < 16; i++)  
  24.     {  
  25.             printf("%02x", md5_checksum[i]);  
  26.     }  
  27. }  
  28. void show_pkg_header(dedup_package_header dedup_pkg_hdr)  
  29. {  
  30.         printf("block_size = %d/n", dedup_pkg_hdr.block_size);  
  31.         printf("block_num = %d/n", dedup_pkg_hdr.block_num);  
  32.     printf("blockid_size = %d/n", dedup_pkg_hdr.blockid_size);  
  33.     printf("magic_num = 0x%x/n", dedup_pkg_hdr.magic_num);  
  34.     printf("file_num = %d/n", dedup_pkg_hdr.file_num);  
  35.     printf("metadata_offset = %lld/n", dedup_pkg_hdr.metadata_offset);  
  36. }  
  37. int dedup_regfile(char *fullpath, int prepos, int fd_bdata, int fd_mdata, hashtable *htable, int debug)  
  38. {  
  39.     int fd;  
  40.     char *buf = NULL;  
  41.     unsigned int rwsize, pos;  
  42.     unsigned char md5_checksum[16 + 1] = {0};  
  43.     unsigned int *metadata = NULL;  
  44.     unsigned int block_num = 0;  
  45.     struct stat statbuf;  
  46.     dedup_entry_header dedup_entry_hdr;  
  47.     if (-1 == (fd = open(fullpath, O_RDONLY)))  
  48.     {  
  49.         perror("open regulae file");  
  50.         return errno;  
  51.     }  
  52.     if (-1 == fstat(fd, &statbuf))  
  53.     {  
  54.         perror("fstat regular file");  
  55.         goto _DEDUP_REGFILE_EXIT;  
  56.     }  
  57.     block_num = statbuf.st_size / g_block_size;  
  58.     metadata = (unsigned int *)malloc(BLOCK_ID_SIZE * block_num);  
  59.     if (metadata == NULL)  
  60.     {  
  61.         perror("malloc metadata for regfile");  
  62.         goto _DEDUP_REGFILE_EXIT;  
  63.     }  
  64.     buf = (char *)malloc(g_block_size);  
  65.     if (buf == NULL)  
  66.     {  
  67.         perror("malloc buf for regfile");  
  68.         goto _DEDUP_REGFILE_EXIT;  
  69.     }  
  70.     pos = 0;  
  71.     while (rwsize = read(fd, buf, g_block_size))   
  72.     {  
  73.         /* if the last block */  
  74.         if (rwsize != g_block_size)  
  75.             break;  
  76.         /* calculate md5 */  
  77.         md5(buf, rwsize, md5_checksum);  
  78.         /* check hashtable with hashkey */  
  79.         unsigned int *bindex = (block_id_t *)hash_value((void *)md5_checksum, htable);  
  80.         if (bindex == NULL)  
  81.         {  
  82.             bindex = (unsigned int *)malloc(BLOCK_ID_SIZE);  
  83.             if (NULL == bindex)  
  84.             {  
  85.                 perror("malloc in dedup_regfile");  
  86.                 break;  
  87.             }  
  88.             /* insert hash entry and write unique block into bdata*/  
  89.             *bindex = g_unique_block_nr;  
  90.             hash_insert((void *)strdup(md5_checksum), (void *)bindex, htable);  
  91.             write(fd_bdata, buf, rwsize);  
  92.             g_unique_block_nr++;  
  93.         }  
  94.         metadata[pos] = *bindex;  
  95.         memset(buf, 0, g_block_size);  
  96.         memset(md5_checksum, 0, 16 + 1);  
  97.         pos++;  
  98.     }  
  99.     /* write metadata into mdata */  
  100.     dedup_entry_hdr.path_len = strlen(fullpath) - prepos;  
  101.     dedup_entry_hdr.block_num = block_num;  
  102.     dedup_entry_hdr.entry_size = BLOCK_ID_SIZE;  
  103.     dedup_entry_hdr.last_block_size = rwsize;  
  104.     dedup_entry_hdr.mode = statbuf.st_mode;  
  105.     write(fd_mdata, &dedup_entry_hdr, sizeof(dedup_entry_header));  
  106.     write(fd_mdata, fullpath + prepos, dedup_entry_hdr.path_len);  
  107.     write(fd_mdata, metadata, BLOCK_ID_SIZE * block_num);  
  108.     write(fd_mdata, buf, rwsize);  
  109.     g_regular_file_nr++;  
  110. _DEDUP_REGFILE_EXIT:  
  111.     close(fd);  
  112.     if (metadata) free(metadata);  
  113.     if (buf) free(buf);  
  114.     return 0;  
  115. }  
  116. int dedup_dir(char *fullpath, int prepos, int fd_bdata, int fd_mdata, hashtable *htable, int debug)  
  117. {  
  118.     DIR *dp;  
  119.     struct dirent *dirp;  
  120.     struct stat statbuf;  
  121.     char subpath[MAX_PATH_LEN] = {0};  
  122.     if (NULL == (dp = opendir(fullpath)))  
  123.     {  
  124.         return errno;  
  125.     }  
  126.     while ((dirp = readdir(dp)) != NULL)  
  127.     {  
  128.         if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)  
  129.             continue;  
  130.         sprintf(subpath, "%s/%s", fullpath, dirp->d_name);  
  131.         if (0 == lstat(subpath, &statbuf))  
  132.         {  
  133.             if (debug)  
  134.                 printf("%s/n", subpath);  
  135.             if (S_ISREG(statbuf.st_mode))   
  136.                 dedup_regfile(subpath, prepos, fd_bdata, fd_mdata, htable,debug);  
  137.             else if (S_ISDIR(statbuf.st_mode))  
  138.                 dedup_dir(subpath, prepos, fd_bdata, fd_mdata, htable, debug);  
  139.         }  
  140.     }  
  141.     closedir(dp);  
  142.     return 0;  
  143. }  
  144. int dedup_package(int path_nr, char **src_paths, char *dest_file, int debug)  
  145. {  
  146.     int fd, fd_bdata, fd_mdata, ret = 0;  
  147.     struct stat statbuf;  
  148.     hashtable *htable = NULL;  
  149.     dedup_package_header dedup_pkg_hdr;  
  150.     char **paths = src_paths;  
  151.     int i, rwsize, prepos;  
  152.     char buf[1024 * 1024] = {0};  
  153.     if (-1 == (fd = open(dest_file, O_WRONLY | O_CREAT, 0755)))  
  154.     {  
  155.         perror("open dest file");  
  156.         ret = errno;  
  157.         goto _DEDUP_PKG_EXIT;  
  158.     }  
  159.     htable = create_hashtable(g_htab_backet_nr);  
  160.     if (NULL == htable)  
  161.     {  
  162.         perror("create_hashtable");  
  163.         ret = errno;  
  164.         goto _DEDUP_PKG_EXIT;  
  165.     }  
  166.     fd_bdata = open("./.bdata", O_RDWR | O_CREAT, 0777);  
  167.     fd_mdata = open("./.mdata", O_RDWR | O_CREAT, 0777);  
  168.     if (-1 == fd_bdata || -1 == fd_mdata)  
  169.     {  
  170.         perror("open bdata or mdata");  
  171.         ret = errno;  
  172.         goto _DEDUP_PKG_EXIT;  
  173.     }  
  174.     g_unique_block_nr = 0;  
  175.     g_regular_file_nr = 0;  
  176.     for (i = 0; i < path_nr; i++)  
  177.     {  
  178.         if (lstat(paths[i], &statbuf) < 0)  
  179.         {  
  180.             perror("lstat source path");  
  181.             ret = errno;  
  182.             goto _DEDUP_PKG_EXIT;  
  183.         }  
  184.         if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode))  
  185.         {  
  186.             if (debug)  
  187.                 printf("%s/n", paths[i]);  
  188.             /* get filename position in pathname */   
  189.             prepos = strlen(paths[i]) - 1;  
  190.             if (strcmp(paths[i], "/") != 0 && *(paths[i] + prepos) == ‘/‘)  
  191.             {  
  192.                 *(paths[i] + prepos--) = ‘/0‘;  
  193.             }  
  194.             while(*(paths[i] + prepos) != ‘/‘ && prepos >= 0) prepos--;  
  195.             prepos++;  
  196.             if (S_ISREG(statbuf.st_mode))  
  197.                 dedup_regfile(paths[i], prepos, fd_bdata, fd_mdata, htable, debug);  
  198.             else  
  199.                 dedup_dir(paths[i], prepos, fd_bdata, fd_mdata, htable, debug);  
  200.         }     
  201.         else   
  202.         {  
  203.             if (debug)  
  204.                 printf("%s is not regular file or directory./n", paths[i]);  
  205.         }  
  206.     }  
  207.     /* fill up dedup package header */  
  208.     dedup_pkg_hdr.block_size = g_block_size;  
  209.     dedup_pkg_hdr.block_num = g_unique_block_nr;  
  210.     dedup_pkg_hdr.blockid_size = BLOCK_ID_SIZE;  
  211.     dedup_pkg_hdr.magic_num = DEDUP_MAGIC_NUM;  
  212.     dedup_pkg_hdr.file_num = g_regular_file_nr;   
  213.     dedup_pkg_hdr.metadata_offset = DEDUP_PKGHDR_SIZE + g_block_size * g_unique_block_nr;  
  214.     write(fd, &dedup_pkg_hdr, DEDUP_PKGHDR_SIZE);  
  215.     /* fill up dedup package unique blocks*/  
  216.     lseek(fd_bdata, 0, SEEK_SET);  
  217.     while(rwsize = read(fd_bdata, buf, 1024 * 1024))  
  218.     {  
  219.         write(fd, buf, rwsize);  
  220.         memset(buf, 0, 1024 * 1024);  
  221.     }  
  222.     /* fill up dedup package metadata */  
  223.     lseek(fd_mdata, 0, SEEK_SET);  
  224.     while(rwsize = read(fd_mdata, buf, 1024 * 1024))  
  225.     {  
  226.         write(fd, buf, rwsize);  
  227.         memset(buf, 0, 1024 * 1024);  
  228.     }  
  229.     if (debug)  
  230.         show_pkg_header(dedup_pkg_hdr);  
  231. _DEDUP_PKG_EXIT:  
  232.     close(fd);  
  233.     close(fd_bdata);  
  234.     close(fd_mdata);  
  235.     unlink("./.bdata");  
  236.     unlink("./.mdata");  
  237.     hash_free(htable);  
  238.       
  239.     return ret;  
  240. }  
  241. void usage()  
  242. {  
  243.     printf("Usage:  dedup [OPTION...] <target file> <source files ...>/n");  
  244.     printf("/nPackage files with deduplicaton technique./n");  
  245.     printf("Mandatory arguments to long options are mandatory for short options too./n");  
  246.     printf("  -z, --compress   filter the archive through compress/n");  
  247.     printf("  -b, --block      block size for deduplication, default is 4096/n");  
  248.     printf("  -t, --hashtable  hashtable backet number, default is 10240/n");  
  249.     printf("  -d, --debug      print debug messages/n");  
  250.     printf("  -h, --help       give this help list/n");  
  251.     printf("/nReport bugs to <Aigui.Liu@gmail.com>./n");  
  252. }  
  253. int main(int argc, char *argv[])  
  254. {  
  255.     char tmp_file[] = "./.dedup/0";  
  256.     int bz = 0, bhelp = 0, bdebug = 0;  
  257.     int ret = -1, c;  
  258.     struct option longopts[] =  
  259.     {  
  260.         {"compress", 0, &bz, ‘z‘},  
  261.         {"block", 1, 0, ‘b‘},  
  262.         {"hashtable", 1, 0, ‘t‘},  
  263.         {"debug", 0, &bdebug, ‘d‘},  
  264.         {"help", 0, &bhelp, ‘h‘},  
  265.         {0, 0, 0, 0}  
  266.     };  
  267.     /* parse options */  
  268.     while ((c = getopt_long (argc, argv, "zb:t:dh", longopts, NULL)) != EOF)  
  269.     {  
  270.         switch(c)   
  271.         {  
  272.         case ‘z‘:  
  273.             bz = 1;  
  274.             break;  
  275.         case ‘b‘:  
  276.             g_block_size = atoi(optarg);  
  277.             break;  
  278.         case ‘t‘:  
  279.             g_htab_backet_nr = atoi(optarg);  
  280.             break;  
  281.         case ‘d‘:  
  282.             bdebug = 1;  
  283.             break;  
  284.         case ‘h‘:  
  285.         case ‘?‘:  
  286.         default:  
  287.             bhelp = 1;  
  288.             break;  
  289.         }  
  290.     }  
  291.     if (bhelp == 1 || (argc - optind) < 2)  
  292.     {  
  293.         usage();  
  294.         return 0;  
  295.     }  
  296.     if (bz)  
  297.     {  
  298.         /* dedup and compress */  
  299.         ret = dedup_package(argc - optind -1 , argv + optind + 1, tmp_file, bdebug);  
  300.         if (ret == 0)  
  301.         {  
  302.             ret = zlib_compress_file(tmp_file, argv[optind]);  
  303.             unlink(tmp_file);  
  304.         }  
  305.     }   
  306.     else   
  307.     {  
  308.         /* dedup only */  
  309.         ret = dedup_package(argc - optind - 1, argv + optind + 1, argv[optind], bdebug);  
  310.     }  
  311.     return ret;  
  312. }   

 

/* undedup.c */

 

[cpp] view plain copy
 
 print?
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <sys/types.h>  
  4. #include <sys/stat.h>  
  5. #include <unistd.h>  
  6. #include <getopt.h>  
  7. #include <fcntl.h>  
  8. #include <errno.h>  
  9. #include "dedup.h"  
  10. /* block length */  
  11. static unsigned int g_block_size = BLOCK_SIZE;  
  12. void show_pkg_header(dedup_package_header dedup_pkg_hdr)  
  13. {  
  14.         printf("block_size = %d/n", dedup_pkg_hdr.block_size);  
  15.         printf("block_num = %d/n", dedup_pkg_hdr.block_num);  
  16.         printf("blockid_size = %d/n", dedup_pkg_hdr.blockid_size);  
  17.         printf("magic_num = 0x%x/n", dedup_pkg_hdr.magic_num);  
  18.         printf("file_num = %d/n", dedup_pkg_hdr.file_num);  
  19.         printf("metadata_offset = %lld/n", dedup_pkg_hdr.metadata_offset);  
  20. }  
  21. int prepare_target_file(char *pathname, char *basepath, int mode)  
  22. {  
  23.     char fullpath[MAX_PATH_LEN] = {0};  
  24.     char path[MAX_PATH_LEN] = {0};  
  25.     char *p = NULL;  
  26.     int pos = 0, fd;  
  27.     sprintf(fullpath, "%s/%s", basepath, pathname);  
  28.     p = fullpath;  
  29.     while (*p != ‘/0‘)  
  30.     {  
  31.         path[pos++] = *p;  
  32.         if (*p == ‘/‘)  
  33.             mkdir(path, 0755);  
  34.         p++;  
  35.     }   
  36.     fd = open(fullpath, O_WRONLY | O_CREAT, mode);  
  37.     return fd;  
  38. }  
  39. int undedup_regfile(int fd, dedup_entry_header dedup_entry_hdr, char *dest_dir, int debug)  
  40. {  
  41.     char pathname[MAX_PATH_LEN] = {0};  
  42.     block_id_t *metadata = NULL;  
  43.     unsigned int block_num = 0;  
  44.     char *buf = NULL;  
  45.     char *last_block_buf = NULL;  
  46.     long long offset, i;  
  47.     int fd_dest, ret = 0;  
  48.     metadata = (block_id_t *) malloc(BLOCK_ID_SIZE * dedup_entry_hdr.block_num);  
  49.     if (NULL == metadata)  
  50.         return errno;  
  51.     buf = (char *)malloc(g_block_size);  
  52.     last_block_buf = (char *)malloc(g_block_size);  
  53.     if (NULL == buf || NULL == last_block_buf)  
  54.     {  
  55.         ret = errno;  
  56.         goto _UNDEDUP_REGFILE_EXIT;  
  57.     }  
  58.     read(fd, pathname, dedup_entry_hdr.path_len);  
  59.     read(fd, metadata, BLOCK_ID_SIZE * dedup_entry_hdr.block_num);  
  60.     read(fd, last_block_buf, dedup_entry_hdr.last_block_size);  
  61.     fd_dest = prepare_target_file(pathname, dest_dir, dedup_entry_hdr.mode);  
  62.     if (fd_dest == -1)  
  63.     {  
  64.         ret = errno;  
  65.         goto _UNDEDUP_REGFILE_EXIT;  
  66.     }  
  67.     if (debug)  
  68.         printf("%s/%s/n", dest_dir, pathname);  
  69.     /* write regular block */  
  70.     block_num = dedup_entry_hdr.block_num;  
  71.     for(i = 0; i < block_num; ++i)  
  72.     {  
  73.         offset = DEDUP_PKGHDR_SIZE + metadata[i] * g_block_size;  
  74.         lseek(fd, offset, SEEK_SET);  
  75.         read(fd, buf, g_block_size);  
  76.         write(fd_dest, buf, g_block_size);  
  77.     }  
  78.     /* write last block */  
  79.     write(fd_dest, last_block_buf, dedup_entry_hdr.last_block_size);  
  80.     close(fd_dest);  
  81. _UNDEDUP_REGFILE_EXIT:  
  82.     if (metadata) free(metadata);  
  83.     if (buf) free(buf);  
  84.     if (last_block_buf) free(last_block_buf);  
  85.     return ret;  
  86. }  
  87. int undedup_package(char *src_file, char *dest_dir, int debug)  
  88. {  
  89.     int fd, i, ret = 0;  
  90.     dedup_package_header dedup_pkg_hdr;  
  91.     dedup_entry_header dedup_entry_hdr;  
  92.     unsigned long long offset;  
  93.         if (-1 == (fd = open(src_file, O_RDONLY)))  
  94.         {  
  95.                 perror("open source file");  
  96.                 return errno;  
  97.         }  
  98.     if (read(fd, &dedup_pkg_hdr, DEDUP_PKGHDR_SIZE) != DEDUP_PKGHDR_SIZE)  
  99.     {  
  100.         perror("read dedup_package_header");  
  101.         ret = errno;  
  102.         goto _UNDEDUP_PKG_EXIT;  
  103.     }  
  104.     if (debug)  
  105.         show_pkg_header(dedup_pkg_hdr);  
  106.     offset = dedup_pkg_hdr.metadata_offset;  
  107.     for (i = 0; i < dedup_pkg_hdr.file_num; ++i)  
  108.     {  
  109.         if (lseek(fd, offset, SEEK_SET) == -1)  
  110.         {  
  111.             ret = errno;  
  112.             break;  
  113.         }  
  114.               
  115.         if (read(fd, &dedup_entry_hdr, DEDUP_ENTRYHDR_SIZE) != DEDUP_ENTRYHDR_SIZE)  
  116.         {  
  117.             ret = errno;  
  118.             break;  
  119.         }  
  120.         ret = undedup_regfile(fd, dedup_entry_hdr, dest_dir, debug);  
  121.         if (ret != 0)  
  122.             break;  
  123.         offset += DEDUP_ENTRYHDR_SIZE;  
  124.         offset += dedup_entry_hdr.path_len;  
  125.         offset += dedup_entry_hdr.block_num * dedup_entry_hdr.entry_size;  
  126.         offset += dedup_entry_hdr.last_block_size;  
  127.     }  
  128. _UNDEDUP_PKG_EXIT:  
  129.     close(fd);  
  130.     return ret;  
  131. }  
  132. void usage()  
  133. {  
  134.         printf("Usage:  undedup [OPTION...] <source file>/n");  
  135.     printf("/nUnpackage files with deduplicaton technique./n");  
  136.     printf("Mandatory arguments to long options are mandatory for short options too./n");  
  137.     printf("  -z, --uncompress  filter the archive through uncompress/n");  
  138.     printf("  -c, --directory   change to directory, default is PWD/n");  
  139.     printf("  -d, --debug       print debug messages/n");  
  140.     printf("  -h, --help        give this help list/n");  
  141.     printf("/nReport bugs to <Aigui.Liu@gmail.com>./n");  
  142. }  
  143. int main(int argc, char *argv[])  
  144. {  
  145.     char tmp_file[] = "./.dedup/0";  
  146.     char path[MAX_PATH_LEN] = "./0";  
  147.     int bz = 0, bhelp = 0, bdebug = 0;  
  148.     int ret = -1, c;  
  149.     struct option longopts[] =  
  150.     {  
  151.         {"compress", 0, &bz, ‘z‘},  
  152.         {"directory", 1, 0, ‘c‘},  
  153.         {"debug", 0, &bdebug, ‘d‘},  
  154.         {"help", 0, &bhelp, ‘h‘},  
  155.         {0, 0, 0, 0}  
  156.     };  
  157.     while ((c = getopt_long (argc, argv, "zc:dh", longopts, NULL)) != EOF)  
  158.     {  
  159.         switch(c)  
  160.         {  
  161.         case ‘z‘:  
  162.             bz = 1;  
  163.             break;  
  164.         case ‘c‘:  
  165.             sprintf(path, "%s", optarg);  
  166.             break;  
  167.         case ‘d‘:  
  168.             bdebug = 1;  
  169.             break;  
  170.         case ‘h‘:  
  171.         case ‘?‘:  
  172.         default:  
  173.             bhelp = 1;  
  174.             break;  
  175.         }  
  176.     }  
  177.     if (bhelp == 1 || (argc - optind) < 1)  
  178.     {  
  179.         usage();  
  180.         return 0;  
  181.     }  
  182.     if (bz)  
  183.     {  
  184.         /* uncompress and undedup */  
  185.         ret = zlib_decompress_file(argv[optind], tmp_file);  
  186.         if (ret == 0)  
  187.         {  
  188.             ret = undedup_package(tmp_file, path, bdebug);  
  189.             unlink(tmp_file);  
  190.         }  
  191.     }  
  192.     else  
  193.     {  
  194.         /* only undedup */  
  195.         ret = undedup_package(argv[optind], path, bdebug);  
  196.     }  
  197.     return ret;  
  198. }  


/* dedup usage */
Usage:  dedup [OPTION...] <target file> <source files ...>

Package files with deduplicaton technique.

  -z, --compress   filter the archive through compress
  -b, --block      block size for deduplication, default is 4096
  -t, --hashtable  hashtable backet number, default is 10240
  -d, --debug      print debug messages
  -h, --help       give this help list

/* undedup usage */
Usage:  undedup [OPTION...] <source file>

Unpackage files with deduplicaton technique.

 


  -z, --uncompress  filter the archive through uncompress
  -c, --directory   change to directory, default is PWD
  -d, --debug       print debug messages
  -h, --help        give this help list


4、初步测试
  这里使用linux最新的kernel源码进行测试,并与tar工具进行比较。从www.kernel.org 下载linux-2.6.32.tar.gz文件,并解压出源文件,然后分别使用tar和dedup工具进行打包,分别得到以下几个文件。

 

Filename

File size

commands

linux-2.6.32.tar

382392320 (365MB)

tar cvf linux-2.6.32.tar linux-2.6.32/

linux-2.6.32.tar.dd 

380381944 (363M)

dedup linux-2.6.32.tar.dd linux-2.6.32.tar

linux-2.6.32.dd

357325910 (341MB)

dedup linux-2.6.32.dd linux-2.6.32/

linux-2.6.32.tar.gz

84322110 (81MB)

gzip -c linux-2.6.32.tar > linux-2.6.32.tar.gz

linux-2.6.32.tar.dd.gz

83978234 (81MB)

gzip -c linux-2.6.32.tar.dd > linux-2.6.32.tar.dd.gz

linux-2.6.32.dd.gz

83674306 (80MB)

gzip -c linux-2.6.32.dd > linux-2.6.32.dd.gz 

 

    linux-2.6.32.tar.gz解压出来的kernel源码文件数据很多,使用这个文件来测试应该具有普遍的意义。通过初步的测试结果,我们可以看出,即使在这样不明确数据是否具备较高重复率的情况下,dedup技术也能较明显地减少数据包的数据量。在数据重复率很高的测试用例下,比如全0或全1的大文件,dedup要远远优于tar。比如,全0的64MB文件,tar+gzip的结果为65KB,而dedup的结果才有286字节。

5、TODO
  1、变长数据块。目前是定长数据块的实现,技术上较为简单,变长数据块可能会获得更高的数据压缩率。
  2、相似文件识别。如果两个文件只有很小的差别,比如在某处插入了若干字节,找出这些数据块并单独处理,可能会提高数据压缩率。

 

原文博客地址:

http://blog.csdn.net/liuaigui/article/details/5166538

 

 

 

 

 

 

 

 

(转)

 

基于Dedup的数据打包技术