对于拥有多台电脑,或者经常换电脑的人来说,文件系统备份是一个常见性问题。比如,将个人电脑和办公室电脑的home目录进行备份,每个人的不同电脑上,home目录上应该有一些相同的文件,如Document、sources(个人存放代码和工具的目录)、read(个人存放电子书的目录)、install(个人安装程序的目录)等等。在长期使用的过程中,不同电脑上都会对原本相同的文件目录做不同的修改(大部分是添加新文件),这样怎么备份不同电脑上的home目录呢?
方法一,对每个电脑上的home目录都做独立备份。如果home目录比较小到还好,如果非常大(这应该是常见情况),这样会导致空间极大浪费,没有利用不同home目录上存在相同文件的特征。
方法二,使用cp -u,只拷贝比较新的或者目标目录中不存在的文件。在实践过程中,发现即使在文件的内容没有改变的情况下,文件的modify time也是很容易被修改的,cp一个文件时,本身也会修改文件的modify time。所以使用cp -u时会拷贝原本并没有改变的文件,浪费大量时间。
方法三,增量备份,首先记录当前home目录与备份home目录中改变的部分,再将改变的部分从当前home目录拷贝到备份home目录中。这样原本没有变化的部分不用拷贝,可以节约时间。而且对于多台电脑,可以只备份不同的部分,共用原本相同的部分,类似合并操作。
所以综合比较,增量备份对于文件系统的备份有非常大的优势。最近在学习docker时,受到naiveDiffDriver实现的启发,根据它实现了一个增量备份工具deepcopy。该工具在备份时,分为两个步骤:1 记录改变的部分,2 将改变的部分增量拷贝到备份的文件系统中。下面简单介绍下它的实现。
记录改变部分,该工具首先利用path/filepath.walk来同时遍历当前home目录和备份home目录,并利用system.Lstat获取每个文件的状态信息,通过树形结构的FileInfo保存下来,然后根据文件的size大小来判断不同:
for name, newChild := range info.children { oldChild, _ := oldChildren[name] if oldChild != nil { // change? oldStat := oldChild.stat newStat := newChild.stat if (oldStat.Size() != newStat.Size() && oldStat.Mode()&syscall.S_IFDIR != syscall.S_IFDIR) || bytes.Compare(oldChild.capability, newChild.capability) != 0 { change := Change{ Path: newChild.path(), Kind: ChangeModify, } *changes = append(*changes, change) newChild.added = true } // Remove from copy so we can detect deletions delete(oldChildren, name) } newChild.addChanges(oldChild, changes) }
写代码:
func() { ta := &tarAppender{ TarWriter: tar.NewWriter(writer), Buffer: pools.BufioWriter32KPool.Get(nil), SeenFiles: make(map[uint64]string), } // this buffer is needed for the duration of this piped stream defer pools.BufioWriter32KPool.Put(ta.Buffer) for _, change := range changes { if change.Kind != ChangeDelete { path := filepath.Join(dir, change.Path) if err := ta.addTarFile(path, change.Path[1:]); err != nil { log.Debugf("Can't add file %s to tar: %s", path, err) } } } // Make sure to check the error on Close. if err := ta.TarWriter.Close(); err != nil { log.Debugf("Can't close layer: %s", err) } if err := writer.Close(); err != nil { log.Debugf("failed close Changes writer: %s", err) } }
func UnpackLayer(dest string, layer ArchiveReader) (size int64, err error) { tr := tar.NewReader(layer) trBuf := pools.BufioReader32KPool.Get(tr) defer pools.BufioReader32KPool.Put(trBuf) var dirs []*tar.Header aufsTempdir := "" aufsHardlinks := make(map[string]*tar.Header) // Iterate through the files in the archive. for { hdr, err := tr.Next() if err == io.EOF { // end of tar archive break } if err != nil { return 0, err } size += hdr.Size // Normalize name, for safety and for a simple is-root check hdr.Name = filepath.Clean(hdr.Name) if !strings.HasSuffix(hdr.Name, "/") { parent := filepath.Dir(hdr.Name) parentPath := filepath.Join(dest, parent) if _, err := os.Lstat(parentPath); err != nil && os.IsNotExist(err) { err = os.MkdirAll(parentPath, 0600) if err != nil { return 0, err } } } path := filepath.Join(dest, hdr.Name) rel, err := filepath.Rel(dest, path) if err != nil { return 0, err } if strings.HasPrefix(rel, "..") { return 0, breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest)) } base := filepath.Base(path) if true { if fi, err := os.Lstat(path); err == nil { if !(fi.IsDir() && hdr.Typeflag == tar.TypeDir) { if err := os.RemoveAll(path); err != nil { return 0, err } } } trBuf.Reset(tr) srcData := io.Reader(trBuf) srcHdr := hdr // Hard links into /.wh..wh.plnk don't work, as we don't extract that directory, so // we manually retarget these into the temporary files we extracted them into if hdr.Typeflag == tar.TypeLink && strings.HasPrefix(filepath.Clean(hdr.Linkname), ".wh..wh.plnk") { linkBasename := filepath.Base(hdr.Linkname) srcHdr = aufsHardlinks[linkBasename] if srcHdr == nil { return 0, fmt.Errorf("Invalid aufs hardlink") } tmpFile, err := os.Open(filepath.Join(aufsTempdir, linkBasename)) if err != nil { return 0, err } defer tmpFile.Close() srcData = tmpFile } if err := createTarFile(path, dest, srcHdr, srcData, true); err != nil { return 0, err } // Directory mtimes must be handled at the end to avoid further // file creation in them to modify the directory mtime if hdr.Typeflag == tar.TypeDir { dirs = append(dirs, hdr) } } }该工具的完整代码请看https://github.com/xuriwuyun/deepcopy。该代码非常原始,仅提供参考。
原文地址:http://blog.csdn.net/xuriwuyun/article/details/45692079