github.com/richardwilkes/toolbox@v1.121.0/xio/fs/copy.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  // Package fs provides filesystem-related utilities.
    11  package fs
    12  
    13  import (
    14  	"io"
    15  	"io/fs"
    16  	"os"
    17  	"path/filepath"
    18  
    19  	"github.com/richardwilkes/toolbox/xio"
    20  )
    21  
    22  // Copy src to dst. src may be a directory, file, or symlink.
    23  func Copy(src, dst string) error {
    24  	return CopyWithMask(src, dst, 0o777)
    25  }
    26  
    27  // CopyWithMask src to dst. src may be a directory, file, or symlink.
    28  func CopyWithMask(src, dst string, mask fs.FileMode) error {
    29  	info, err := os.Lstat(src)
    30  	if err != nil {
    31  		return err
    32  	}
    33  	return generalCopy(src, dst, info.Mode(), mask)
    34  }
    35  
    36  func generalCopy(src, dst string, srcMode, mask fs.FileMode) error {
    37  	if srcMode&os.ModeSymlink != 0 {
    38  		return linkCopy(src, dst)
    39  	}
    40  	if srcMode.IsDir() {
    41  		return dirCopy(src, dst, srcMode, mask)
    42  	}
    43  	return fileCopy(src, dst, srcMode, mask)
    44  }
    45  
    46  func fileCopy(src, dst string, srcMode, mask fs.FileMode) (err error) {
    47  	if err = os.MkdirAll(filepath.Dir(dst), 0o755&mask); err != nil {
    48  		return err
    49  	}
    50  	var f *os.File
    51  	if f, err = os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, (srcMode&mask)|0o200); err != nil {
    52  		return err
    53  	}
    54  	defer func() {
    55  		if closeErr := f.Close(); closeErr != nil && err == nil {
    56  			err = closeErr
    57  		}
    58  	}()
    59  	var s *os.File
    60  	if s, err = os.Open(src); err != nil {
    61  		return err
    62  	}
    63  	defer xio.CloseIgnoringErrors(s)
    64  	_, err = io.Copy(f, s)
    65  	return err
    66  }
    67  
    68  func dirCopy(srcDir, dstDir string, srcMode, mask fs.FileMode) error {
    69  	if err := os.MkdirAll(dstDir, srcMode&mask); err != nil {
    70  		return err
    71  	}
    72  	list, err := os.ReadDir(srcDir)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	for _, one := range list {
    77  		name := one.Name()
    78  		if err = generalCopy(filepath.Join(srcDir, name), filepath.Join(dstDir, name), one.Type(), mask); err != nil {
    79  			return err
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  func linkCopy(src, dst string) error {
    86  	s, err := os.Readlink(src)
    87  	if err != nil {
    88  		return err
    89  	}
    90  	return os.Symlink(s, dst)
    91  }