github.com/hashicorp/go-getter/v2@v2.2.2/get_file_copy.go (about)

     1  package getter
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  )
     9  
    10  // readerFunc is syntactic sugar for read interface.
    11  type readerFunc func(p []byte) (n int, err error)
    12  
    13  func (rf readerFunc) Read(p []byte) (n int, err error) { return rf(p) }
    14  
    15  // Copy is a io.Copy cancellable by context
    16  func Copy(ctx context.Context, dst io.Writer, src io.Reader) (int64, error) {
    17  	// Copy will call the Reader and Writer interface multiple time, in order
    18  	// to copy by chunk (avoiding loading the whole file in memory).
    19  	return io.Copy(dst, readerFunc(func(p []byte) (int, error) {
    20  
    21  		select {
    22  		case <-ctx.Done():
    23  			// context has been canceled
    24  			// stop process and propagate "context canceled" error
    25  			return 0, ctx.Err()
    26  		default:
    27  			// otherwise just run default io.Reader implementation
    28  			return src.Read(p)
    29  		}
    30  	}))
    31  }
    32  
    33  // copyReader copies from an io.Reader into a file, using umask to create the dst file
    34  func copyReader(dst string, src io.Reader, fmode, umask os.FileMode, fileSizeLimit int64) error {
    35  	dstF, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fmode)
    36  	if err != nil {
    37  		return err
    38  	}
    39  	defer dstF.Close()
    40  
    41  	if fileSizeLimit > 0 {
    42  		src = io.LimitReader(src, fileSizeLimit)
    43  	}
    44  
    45  	_, err = io.Copy(dstF, src)
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	// Explicitly chmod; the process umask is unconditionally applied otherwise.
    51  	// We'll mask the mode with our own umask, but that may be different than
    52  	// the process umask
    53  	return os.Chmod(dst, mode(fmode, umask))
    54  }
    55  
    56  // copyFile copies a file in chunks from src path to dst path, using umask to create the dst file
    57  func copyFile(ctx context.Context, dst, src string, disableSymlinks bool, fmode, umask os.FileMode) (int64, error) {
    58  
    59  	if disableSymlinks {
    60  		fileInfo, err := os.Lstat(src)
    61  		if err != nil {
    62  			return 0, fmt.Errorf("failed to check copy file source for symlinks: %w", err)
    63  		}
    64  
    65  		if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink {
    66  			return 0, ErrSymlinkCopy
    67  		}
    68  	}
    69  
    70  	srcF, err := os.Open(src)
    71  	if err != nil {
    72  		return 0, err
    73  	}
    74  	defer srcF.Close()
    75  
    76  	dstF, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fmode)
    77  	if err != nil {
    78  		return 0, err
    79  	}
    80  	defer dstF.Close()
    81  
    82  	count, err := Copy(ctx, dstF, srcF)
    83  	if err != nil {
    84  		return 0, err
    85  	}
    86  
    87  	// Explicitly chmod; the process umask is unconditionally applied otherwise.
    88  	// We'll mask the mode with our own umask, but that may be different than
    89  	// the process umask
    90  	err = os.Chmod(dst, mode(fmode, umask))
    91  	return count, err
    92  }