github.com/kaydxh/golang@v0.0.131/go/io/copy.go (about)

     1  /*
     2  MIT License
     3  
     4  Copyright (c) 2020 kay
     5  
     6  Permission is hereby granted, free of charge, to any person obtaining a copy
     7  of this software and associated documentation files (the "Software"), to deal
     8  in the Software without restriction, including without limitation the rights
     9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    10  copies of the Software, and to permit persons to whom the Software is
    11  furnished to do so, subject to the following conditions:
    12  
    13  The above copyright notice and this permission notice shall be included in all
    14  copies or substantial portions of the Software.
    15  
    16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    22  SOFTWARE.
    23  */
    24  
    25  package io
    26  
    27  import (
    28  	"fmt"
    29  	"io"
    30  	"os"
    31  	"path/filepath"
    32  
    33  	os_ "github.com/kaydxh/golang/go/os"
    34  )
    35  
    36  // Mode indicates whether to use hardlink or copy content
    37  type Mode int
    38  
    39  const (
    40  	// Content creates a new file, and copies the content of the file
    41  	Content Mode = iota
    42  	// Hardlink creates a new hardlink to the existing file
    43  	Hardlink
    44  )
    45  
    46  func CopyAll(src, dst string) (err error) {
    47  	isDir, err := os_.IsDir(src)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	if isDir {
    53  		return CopyDir(src, dst, Content)
    54  	}
    55  
    56  	return CopyFile(src, dst)
    57  }
    58  
    59  func CopyDir(srcDir, dstDir string, copyMode Mode) (err error) {
    60  	return filepath.Walk(srcDir, func(srcPath string, f os.FileInfo, err error) error {
    61  		if err != nil {
    62  			return err
    63  		}
    64  
    65  		// Rebase path
    66  		relPath, err := filepath.Rel(srcDir, srcPath)
    67  		if err != nil {
    68  			return err
    69  		}
    70  
    71  		dstPath := filepath.Join(dstDir, relPath)
    72  
    73  		return CopyPath(srcPath, dstPath, f, copyMode)
    74  	})
    75  }
    76  
    77  func CopyRegular(srcPath, dstPath string, fileInfo os.FileInfo) error {
    78  	srcFile, err := os.Open(srcPath)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	defer srcFile.Close()
    83  
    84  	// If the destination file already exists, we shouldn't blow it away
    85  	dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, fileInfo.Mode())
    86  	if err != nil {
    87  		return err
    88  	}
    89  	defer dstFile.Close()
    90  
    91  	if err = doCopyWithFileClone(srcFile, dstFile); err == nil {
    92  		return nil
    93  	}
    94  
    95  	if err = doCopyWithFileRange(srcFile, dstFile, fileInfo); err == nil {
    96  		return nil
    97  	}
    98  
    99  	return legacyCopy(srcFile, dstFile)
   100  }
   101  
   102  func legacyCopy(srcFile io.Reader, dstFile io.Writer) error {
   103  	_, err := io.Copy(dstFile, srcFile)
   104  
   105  	return err
   106  }
   107  
   108  func CopyFile(src, dst string) (err error) {
   109  	srcAbs, err := filepath.Abs(src)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	dstAbs, err := filepath.Abs(dst)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	// open source file
   120  	sfi, err := os.Stat(srcAbs)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	if !sfi.Mode().IsRegular() {
   126  		return fmt.Errorf(
   127  			"CopyFile: non-regular source file %s (%q)",
   128  			sfi.Name(),
   129  			sfi.Mode().String(),
   130  		)
   131  	}
   132  
   133  	// open dest file
   134  	dfi, err := os.Stat(dstAbs)
   135  	if err != nil {
   136  		if !os.IsNotExist(err) {
   137  			return
   138  		}
   139  
   140  		// file doesn't exist
   141  		err := os.MkdirAll(filepath.Dir(dst), 0755)
   142  		if err != nil {
   143  			return err
   144  		}
   145  
   146  	} else {
   147  
   148  		if dfi.Mode().IsRegular() {
   149  			if os.SameFile(sfi, dfi) {
   150  				return
   151  			}
   152  
   153  		} else if dfi.Mode().IsDir() {
   154  			// dst path is exist and dst is dir, so copy file to dst dir
   155  			// src: src/1.jpg  dst: dst/dir/ => dst/dir/1.jpg
   156  			dst = filepath.Join(dst, filepath.Base(src))
   157  
   158  		} else {
   159  			return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
   160  		}
   161  	}
   162  
   163  	return copyFileContents(src, dst)
   164  }
   165  
   166  //copyFileContentes copies the contents of the file named src to the file named dst
   167  //The destination file will be created if it does not alreay exist. If the destination
   168  //file exists, all it's contents will be replaced by the contents of the source file
   169  func copyFileContents(src, dst string) (err error) {
   170  	srcFile, err := os.Open(src)
   171  	if err != nil {
   172  		return
   173  	}
   174  	defer srcFile.Close()
   175  
   176  	dstFile, err := os.Create(dst)
   177  	if err != nil {
   178  		return
   179  	}
   180  
   181  	defer func() {
   182  		cerr := dstFile.Close()
   183  		if err == nil {
   184  			err = cerr
   185  		}
   186  	}()
   187  
   188  	if _, err = io.Copy(dstFile, srcFile); err != nil {
   189  		return
   190  	}
   191  
   192  	err = dstFile.Sync()
   193  	return
   194  }