github.com/bosssauce/ponzu@v0.11.1-0.20200102001432-9bc41b703131/system/backup/archive.go (about)

     1  package backup
     2  
     3  import (
     4  	"archive/tar"
     5  	"compress/gzip"
     6  	"context"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"path/filepath"
    11  )
    12  
    13  // ArchiveFS walks the filesystem starting from basedir writing files encountered
    14  // tarred and gzipped to the provided writer
    15  func ArchiveFS(ctx context.Context, basedir string, w io.Writer) error {
    16  	gz := gzip.NewWriter(w)
    17  	tarball := tar.NewWriter(gz)
    18  
    19  	absPath, err := filepath.Abs(basedir)
    20  	if err != nil {
    21  		return err
    22  	}
    23  
    24  	info, err := os.Lstat(absPath)
    25  	if err != nil {
    26  		return err
    27  	}
    28  
    29  	if info.Mode()&os.ModeSymlink == os.ModeSymlink {
    30  		// This is a symlink - we need to follow it
    31  		bdir, err := os.Readlink(absPath)
    32  		if err != nil {
    33  			return err
    34  		}
    35  		basedir = bdir
    36  	}
    37  
    38  	errChan := make(chan error, 1)
    39  	walkFn := func(path string, info os.FileInfo, err error) error {
    40  		if err != nil {
    41  			return err
    42  		}
    43  
    44  		hdr, err := tar.FileInfoHeader(info, "")
    45  		if err != nil {
    46  			return err
    47  		}
    48  
    49  		hdr.Name = path
    50  
    51  		err = tarball.WriteHeader(hdr)
    52  		if err != nil {
    53  			return err
    54  		}
    55  
    56  		if !info.IsDir() {
    57  			src, err := os.Open(path)
    58  			if err != nil {
    59  				return err
    60  			}
    61  			defer src.Close()
    62  
    63  			_, err = io.Copy(tarball, src)
    64  			if err != nil {
    65  				return err
    66  			}
    67  
    68  			err = tarball.Flush()
    69  			if err != nil {
    70  				return err
    71  			}
    72  
    73  			err = gz.Flush()
    74  			if err != nil {
    75  				return err
    76  			}
    77  		}
    78  
    79  		return nil
    80  	}
    81  
    82  	// stop processing if we get a cancellation signal
    83  	err = filepath.Walk(basedir, func(path string, info os.FileInfo, err error) error {
    84  		go func() { errChan <- walkFn(path, info, err) }()
    85  
    86  		select {
    87  		case <-ctx.Done():
    88  			if err := ctx.Err(); err != nil {
    89  				return err
    90  			}
    91  		case err := <-errChan:
    92  			if err != nil {
    93  				return err
    94  			}
    95  		}
    96  
    97  		return nil
    98  	})
    99  	if err != nil {
   100  		fmt.Println(err)
   101  		return err
   102  	}
   103  
   104  	err = gz.Close()
   105  	if err != nil {
   106  		return err
   107  	}
   108  	err = tarball.Close()
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	return nil
   114  }