| package fs | |
| import ( | |
| "archive/tar" | |
| "compress/gzip" | |
| "io" | |
| "os" | |
| "os/exec" | |
| "path/filepath" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/utils" | |
| ) | |
| const gzipFileExt = ".gz" | |
| // TarFile compresses the file at src to dst. | |
| func TarFile(src, dst string) error { | |
| args := []string{"cf", dst} | |
| dirPath := filepath.Dir(src) | |
| baseName := filepath.Base(src) | |
| // Add the compression type based on the dst, if the | |
| // file type is not supported tar using default compression. | |
| if filepath.Ext(dst) == gzipFileExt { | |
| args = append(args, "-I", "/bin/gzip") | |
| } | |
| // add inputFilePath args | |
| args = append(args, "-C", dirPath, baseName) | |
| cmd := exec.Command("tar", args...) | |
| cmd.Stdout = os.Stdout | |
| cmd.Stderr = os.Stderr | |
| return cmd.Run() | |
| } | |
| // TarDir compresses the directory at root to dst. | |
| func TarDir(root, dst string) error { | |
| args := []string{"cf", dst, "-C", root} | |
| inputFiles, err := filepath.Glob(filepath.Join(root, "*")) | |
| if err != nil { | |
| return err | |
| } | |
| var relInputFiles []string | |
| for _, path := range inputFiles { | |
| relPath, err := filepath.Rel(root, path) | |
| if err != nil { | |
| return err | |
| } | |
| relInputFiles = append(relInputFiles, relPath) | |
| } | |
| if relInputFiles == nil { | |
| relInputFiles = []string{"."} | |
| } | |
| args = append(args, relInputFiles...) | |
| cmd := exec.Command("tar", args...) | |
| cmd.Stdout = os.Stdout | |
| cmd.Stderr = os.Stderr | |
| return cmd.Run() | |
| } | |
| // ExtractFile decompresses the tar file at inputFile to destDir. | |
| func ExtractFile(inputFile, destDir string) error { | |
| var reader io.Reader | |
| fileReader, err := os.Open(inputFile) | |
| if err != nil { | |
| return err | |
| } | |
| defer utils.CheckClose(fileReader, "error closing the file reader", &err) | |
| if filepath.Ext(inputFile) == ".gz" { | |
| reader, err = gzip.NewReader(fileReader) | |
| if err != nil { | |
| return err | |
| } | |
| } else { | |
| reader = fileReader | |
| } | |
| return extractFile(reader, destDir) | |
| } | |
| // extractFile decompresses the tar file reader at inputFile to destDir. | |
| func extractFile(reader io.Reader, destDir string) error { | |
| // Open the file for read | |
| // Use gzip to read from the file | |
| tarReader := tar.NewReader(reader) | |
| // Read the file sequentially | |
| for { | |
| fileHeader, err := tarReader.Next() | |
| switch { | |
| // If no more files are found, return | |
| case err == io.EOF: | |
| return nil | |
| // Return if hit any error | |
| case err != nil: | |
| return err | |
| // If next file's header is nil, just skip it. | |
| case fileHeader == nil: | |
| continue | |
| } | |
| // Create a target file locally | |
| localTarget := filepath.Join(destDir, fileHeader.Name) | |
| switch fileHeader.Typeflag { | |
| case tar.TypeDir: | |
| if err := os.MkdirAll(localTarget, 0755); err != nil { | |
| return err | |
| } | |
| // This should be tar.TypeReg, e.g regular file. | |
| default: | |
| localDir := filepath.Dir(localTarget) | |
| // Create a dir if it doesn't exist but it should have created dir already. | |
| if err := os.MkdirAll(localDir, 0755); err != nil { | |
| return err | |
| } | |
| localFile, err := os.Create(localTarget) | |
| if err != nil { | |
| return err | |
| } | |
| // Copy over the contents. | |
| if _, err = io.Copy(localFile, tarReader); err != nil { | |
| localFile.Close() | |
| return err | |
| } | |
| localFile.Close() | |
| } | |
| } | |
| return nil | |
| } |