github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/tools/zip/unzip.go (about)

     1  package unzip
     2  
     3  import (
     4  	"archive/zip"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  )
     9  
    10  func Unzip(src, dest string) error {
    11  	r, err := zip.OpenReader(src)
    12  	if err != nil {
    13  		return err
    14  	}
    15  	defer func() {
    16  		if err := r.Close(); err != nil {
    17  			panic(err)
    18  		}
    19  	}()
    20  
    21  	if err := os.MkdirAll(dest, 0755); err != nil {
    22  		return err
    23  	}
    24  
    25  	// Closure to address file descriptors issue with all the deferred .Close() methods
    26  	extractAndWriteFile := func(f *zip.File) error {
    27  		rc, err := f.Open()
    28  		if err != nil {
    29  			return err
    30  		}
    31  		defer func() {
    32  			if err := rc.Close(); err != nil {
    33  				panic(err)
    34  			}
    35  		}()
    36  
    37  		filePath := filepath.Join(dest, f.Name)
    38  		fileInfo := f.FileInfo()
    39  
    40  		if fileInfo.IsDir() {
    41  			err := os.MkdirAll(filePath, fileInfo.Mode())
    42  			if err != nil {
    43  				return err
    44  			}
    45  		} else {
    46  			err := os.MkdirAll(filepath.Dir(filePath), 0755)
    47  			if err != nil {
    48  				return err
    49  			}
    50  
    51  			if fileInfo.Mode()&os.ModeSymlink != 0 {
    52  				linkName, err := io.ReadAll(rc)
    53  				if err != nil {
    54  					return err
    55  				}
    56  				return os.Symlink(string(linkName), filePath)
    57  			}
    58  
    59  			fileCopy, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileInfo.Mode())
    60  			if err != nil {
    61  				return err
    62  			}
    63  			defer fileCopy.Close()
    64  
    65  			_, err = io.Copy(fileCopy, rc)
    66  			if err != nil {
    67  				return err
    68  			}
    69  		}
    70  
    71  		return nil
    72  	}
    73  
    74  	for _, f := range r.File {
    75  		err := extractAndWriteFile(f)
    76  		if err != nil {
    77  			return err
    78  		}
    79  	}
    80  
    81  	return nil
    82  }