| package common |
| |
| import ( |
| "fmt" |
| "io" |
| "io/fs" |
| "log" |
| "os" |
| "sync" |
| "time" |
| ) |
| |
| // From https://opensource.com/article/18/6/copying-files-go |
| func CopyFile(src string, dst string) error { |
| source, err := os.Open(src) |
| if err != nil { |
| return err |
| } |
| defer source.Close() |
| |
| destination, err := os.Create(dst) |
| if err != nil { |
| return err |
| } |
| defer destination.Close() |
| _, err = io.Copy(destination, source) |
| return err |
| } |
| |
| func Copy(opts CopyOpts) { |
| if !opts.srcInfo.Mode().IsRegular() { |
| log.Fatalf("%s is not a regular file", opts.src) |
| } |
| |
| opModifier := "" |
| const fallback = " (fallback)" |
| |
| if opts.hardlink { |
| // hardlink this file |
| if opts.verbose { |
| fmt.Printf("hardlink%s %v => %v\n", opModifier, opts.src, opts.dst) |
| } |
| err := os.Link(opts.src, opts.dst) |
| if err != nil { |
| if opts.verbose { |
| fmt.Printf("hardlink failed: %v\n", err) |
| opModifier = fallback |
| } |
| // fallback to clonefile or copy |
| } else { |
| return |
| } |
| } |
| |
| // clone this file |
| if opts.verbose { |
| fmt.Printf("clonefile%s %v => %v\n", opModifier, opts.src, opts.dst) |
| } |
| switch supported, err := cloneFile(opts.src, opts.dst); { |
| case !supported: |
| if opts.verbose { |
| fmt.Print("clonefile skipped: not supported by platform\n") |
| } |
| // fallback to copy |
| case supported && err == nil: |
| return |
| case supported && err != nil: |
| if opts.verbose { |
| fmt.Printf("clonefile failed: %v\n", err) |
| opModifier = fallback |
| } |
| // fallback to copy |
| } |
| |
| // copy this file |
| if opts.verbose { |
| fmt.Printf("copy%s %v => %v\n", opModifier, opts.src, opts.dst) |
| } |
| err := CopyFile(opts.src, opts.dst) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| if opts.preserveMTime { |
| accessTime := time.Now() |
| err := os.Chtimes(opts.dst, accessTime, opts.srcInfo.ModTime()) |
| if err != nil { |
| log.Fatal(err) |
| } |
| } |
| } |
| |
| type CopyWorker struct { |
| queue <-chan CopyOpts |
| } |
| |
| func NewCopyWorker(queue <-chan CopyOpts) *CopyWorker { |
| return &CopyWorker{queue: queue} |
| } |
| |
| func (w *CopyWorker) Run(wg *sync.WaitGroup) { |
| defer wg.Done() |
| for opts := range w.queue { |
| Copy(opts) |
| } |
| } |
| |
| type CopyOpts struct { |
| src, dst string |
| srcInfo fs.FileInfo |
| hardlink bool |
| verbose bool |
| preserveMTime bool |
| } |
| |
| func NewCopyOpts(src string, dst string, srcInfo fs.FileInfo, hardlink bool, verbose bool, preserveMTime bool) CopyOpts { |
| return CopyOpts{src: src, dst: dst, srcInfo: srcInfo, hardlink: hardlink, verbose: verbose, preserveMTime: preserveMTime} |
| } |