github.com/Cloud-Foundations/Dominator@v0.3.4/lib/fsutil/copy.go (about) 1 package fsutil 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "os" 8 "path" 9 10 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 11 ) 12 13 func copyToFile(destFilename string, perm os.FileMode, reader io.Reader, 14 length uint64) error { 15 tmpFilename := destFilename + "~" 16 destFile, err := os.OpenFile(tmpFilename, 17 os.O_CREATE|os.O_TRUNC|os.O_WRONLY, perm) 18 if err != nil { 19 return err 20 } 21 defer os.Remove(tmpFilename) 22 defer destFile.Close() 23 var nCopied int64 24 iLength := int64(length) 25 if length < 1 { 26 if _, err := io.Copy(destFile, reader); err != nil { 27 return fmt.Errorf("error copying: %s", err) 28 } 29 } else { 30 if nCopied, err = io.CopyN(destFile, reader, iLength); err != nil { 31 return fmt.Errorf("error copying: %s", err) 32 } 33 if nCopied != iLength { 34 return fmt.Errorf("expected length: %d, got: %d for: %s\n", 35 length, nCopied, tmpFilename) 36 } 37 } 38 return os.Rename(tmpFilename, destFilename) 39 } 40 41 func copyTree(destDir, sourceDir string, 42 copyFunc func(destFilename, sourceFilename string, 43 mode os.FileMode) error) error { 44 file, err := os.Open(sourceDir) 45 if err != nil { 46 if os.IsNotExist(err) { 47 return nil 48 } 49 return err 50 } 51 names, err := file.Readdirnames(-1) 52 file.Close() 53 if err != nil { 54 return err 55 } 56 for _, name := range names { 57 sourceFilename := path.Join(sourceDir, name) 58 destFilename := path.Join(destDir, name) 59 var stat wsyscall.Stat_t 60 if err := wsyscall.Lstat(sourceFilename, &stat); err != nil { 61 return errors.New(sourceFilename + ": " + err.Error()) 62 } 63 switch stat.Mode & wsyscall.S_IFMT { 64 case wsyscall.S_IFDIR: 65 if err := os.Mkdir(destFilename, DirPerms); err != nil { 66 if !os.IsExist(err) { 67 return err 68 } 69 } 70 err := copyTree(destFilename, sourceFilename, copyFunc) 71 if err != nil { 72 return err 73 } 74 case wsyscall.S_IFREG: 75 err := copyFunc(destFilename, sourceFilename, 76 os.FileMode(stat.Mode)&os.ModePerm) 77 if err != nil { 78 return err 79 } 80 case wsyscall.S_IFLNK: 81 sourceTarget, err := os.Readlink(sourceFilename) 82 if err != nil { 83 return errors.New(sourceFilename + ": " + err.Error()) 84 } 85 if destTarget, err := os.Readlink(destFilename); err == nil { 86 if sourceTarget == destTarget { 87 continue 88 } 89 } 90 os.Remove(destFilename) 91 if err := os.Symlink(sourceTarget, destFilename); err != nil { 92 return err 93 } 94 default: 95 return errors.New("unsupported file type") 96 } 97 } 98 return nil 99 } 100 101 func copyFile(destFilename, sourceFilename string, mode os.FileMode) error { 102 if mode == 0 { 103 var stat wsyscall.Stat_t 104 if err := wsyscall.Stat(sourceFilename, &stat); err != nil { 105 return errors.New(sourceFilename + ": " + err.Error()) 106 } 107 mode = os.FileMode(stat.Mode & wsyscall.S_IFMT) 108 } 109 sourceFile, err := os.Open(sourceFilename) 110 if err != nil { 111 return errors.New(sourceFilename + ": " + err.Error()) 112 } 113 defer sourceFile.Close() 114 return copyToFile(destFilename, mode, sourceFile, 0) 115 }