github.com/jaypipes/ghw@v0.21.1/pkg/snapshot/pack.go (about)

     1  //
     2  // Use and distribution licensed under the Apache license version 2.
     3  //
     4  // See the COPYING file in the root project directory for full text.
     5  //
     6  
     7  package snapshot
     8  
     9  import (
    10  	"archive/tar"
    11  	"compress/gzip"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"os"
    16  	"path/filepath"
    17  )
    18  
    19  // PackFrom creates the snapshot named `snapshotName` from the
    20  // directory tree whose root is `sourceRoot`.
    21  func PackFrom(snapshotName, sourceRoot string) error {
    22  	f, err := OpenDestination(snapshotName)
    23  	if err != nil {
    24  		return err
    25  	}
    26  	defer f.Close()
    27  
    28  	return PackWithWriter(f, sourceRoot)
    29  }
    30  
    31  // OpenDestination opens the `snapshotName` file for writing, bailing out
    32  // if the file seems to exist and have existing content already.
    33  // This is done to avoid accidental overwrites.
    34  func OpenDestination(snapshotName string) (*os.File, error) {
    35  	f, err := os.OpenFile(snapshotName, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
    36  	if err != nil {
    37  		if !errors.Is(err, os.ErrExist) {
    38  			return nil, err
    39  		}
    40  		fs, err := os.Stat(snapshotName)
    41  		if err != nil {
    42  			return nil, err
    43  		}
    44  		if fs.Size() > 0 {
    45  			return nil, fmt.Errorf("file %s already exists and is of size > 0", snapshotName)
    46  		}
    47  		f, err = os.OpenFile(snapshotName, os.O_WRONLY, 0600)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  	}
    52  	return f, nil
    53  }
    54  
    55  // PakcWithWriter creates a snapshot sending all the binary data to the
    56  // given `fw` writer. The snapshot is made from the directory tree whose
    57  // root is `sourceRoot`.
    58  func PackWithWriter(fw io.Writer, sourceRoot string) error {
    59  	gzw := gzip.NewWriter(fw)
    60  	defer gzw.Close()
    61  
    62  	tw := tar.NewWriter(gzw)
    63  	defer tw.Close()
    64  
    65  	return createSnapshot(tw, sourceRoot)
    66  }
    67  
    68  func createSnapshot(tw *tar.Writer, buildDir string) error {
    69  	return filepath.Walk(buildDir, func(path string, fi os.FileInfo, _ error) error {
    70  		if path == buildDir {
    71  			return nil
    72  		}
    73  		var link string
    74  		var err error
    75  
    76  		if fi.Mode()&os.ModeSymlink != 0 {
    77  			trace("processing symlink %s\n", path)
    78  			link, err = os.Readlink(path)
    79  			if err != nil {
    80  				return err
    81  			}
    82  		}
    83  
    84  		hdr, err := tar.FileInfoHeader(fi, link)
    85  		if err != nil {
    86  			return err
    87  		}
    88  		relPath, err := filepath.Rel(buildDir, path)
    89  		if err != nil {
    90  			return err
    91  		}
    92  		hdr.Name = relPath
    93  
    94  		if err = tw.WriteHeader(hdr); err != nil {
    95  			return err
    96  		}
    97  
    98  		switch hdr.Typeflag {
    99  		case tar.TypeReg, tar.TypeRegA:
   100  			f, err := os.Open(path)
   101  			if err != nil {
   102  				return err
   103  			}
   104  			defer f.Close()
   105  			if _, err = io.Copy(tw, f); err != nil {
   106  				return err
   107  			}
   108  		}
   109  		return nil
   110  	})
   111  }