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 }