github.com/searKing/golang/go@v1.2.117/io/copy.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package io 6 7 import ( 8 "errors" 9 "io" 10 "os" 11 "path/filepath" 12 ) 13 14 var ErrNotImplemented = errors.New("not implemented") 15 16 // Mode indicates whether to use hardlink or copy content 17 type Mode int 18 19 const ( 20 // Content creates a new file, and copies the content of the file 21 Content Mode = iota 22 // Hardlink creates a new hardlink to the existing file 23 Hardlink 24 ) 25 26 func CopyRegular(srcPath, dstPath string, fileInfo os.FileInfo) error { 27 srcFile, err := os.Open(srcPath) 28 if err != nil { 29 return err 30 } 31 defer srcFile.Close() 32 33 // If the destination file already exists, we shouldn't blow it away 34 dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, fileInfo.Mode()) 35 if err != nil { 36 return err 37 } 38 defer dstFile.Close() 39 40 if err = doCopyFileClone(srcFile, dstFile); err == nil { 41 return nil 42 } 43 44 if err = doCopyWithFileRange(srcFile, dstFile, fileInfo); err == nil { 45 return nil 46 } 47 48 return legacyCopy(srcFile, dstFile) 49 } 50 51 func doCopyFileClone(srcFile, dstFile *os.File) error { 52 return copyFileClone(dstFile, srcFile) 53 } 54 55 func doCopyWithFileRange(srcFile, dstFile *os.File, fileInfo os.FileInfo) error { 56 amountLeftToCopy := fileInfo.Size() 57 58 for amountLeftToCopy > 0 { 59 n, err := copyFileRange(int(srcFile.Fd()), nil, int(dstFile.Fd()), nil, int(amountLeftToCopy), 0) 60 if err != nil { 61 return err 62 } 63 64 amountLeftToCopy = amountLeftToCopy - int64(n) 65 } 66 67 return nil 68 } 69 70 func legacyCopy(srcFile io.Reader, dstFile io.Writer) error { 71 _, err := io.Copy(dstFile, srcFile) 72 return err 73 } 74 75 // CopyDir copies or hardlinks the contents of one directory to another, 76 // properly handling mods, and soft links 77 func CopyDir(srcDir, dstDir string, copyMode Mode) error { 78 return filepath.Walk(srcDir, func(srcPath string, f os.FileInfo, err error) error { 79 if err != nil { 80 return err 81 } 82 83 // Rebase path 84 relPath, err := filepath.Rel(srcDir, srcPath) 85 if err != nil { 86 return err 87 } 88 89 dstPath := filepath.Join(dstDir, relPath) 90 return copyPath(srcPath, dstPath, f, copyMode) 91 }) 92 }