blob: e767b09f9e85a2df44f376698f42a92df50a3f81 [file] [log] [blame]
Marcel van Lohuizen75b9c7f2019-09-21 14:03:34 +00001// Copyright 2019 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package copy provides utilities to copy files and directories.
16package copy
17
18import (
19 "fmt"
20 "io"
21 "io/ioutil"
22 "os"
23 "path/filepath"
24)
25
26// File creates dst and copies the contents src to it.
27func File(src, dst string) error {
28 src = filepath.Clean(src)
29 dst = filepath.Clean(dst)
30
31 stat, err := os.Stat(src)
32 if err != nil {
33 return fmt.Errorf("copy file: %v", err)
34 }
35 err = copyFile(stat, src, dst)
36 if err != nil {
37 return fmt.Errorf("copy file: %v", err)
38 }
39 return nil
40}
41
42// Dir copies a src directory to its destination.
43func Dir(src, dst string) error {
44 src = filepath.Clean(src)
45 dst = filepath.Clean(dst)
46
47 stat, err := os.Stat(src)
48 if err != nil {
49 return fmt.Errorf("copy failed: %v", err)
50 } else if !stat.IsDir() {
51 return fmt.Errorf("copy failed: source is not a directory")
52 }
53
54 err = copyDir(stat, src, dst)
55 if err != nil {
56 return fmt.Errorf("copy failed: %v", err)
57 }
58 return nil
59}
60
61func copyDir(info os.FileInfo, src, dst string) error {
62 if _, err := os.Stat(dst); err != nil {
63 if !os.IsNotExist(err) {
64 return fmt.Errorf("dest err %s: %v", dst, err)
65 }
66 if err := os.MkdirAll(dst, info.Mode()); err != nil {
67 return fmt.Errorf("making dest %s: %v", dst, err)
68 }
69 }
70
71 entries, err := ioutil.ReadDir(src)
72 if err != nil {
73 return fmt.Errorf("reading dir %s: %v", src, err)
74 }
75
76 for _, e := range entries {
77 srcPath := filepath.Join(src, e.Name())
78 dstPath := filepath.Join(dst, e.Name())
79
80 switch mode := e.Mode(); mode & os.ModeType {
81 case os.ModeSymlink:
82 err = copySymLink(e, srcPath, dstPath)
83 case os.ModeDir:
84 err = copyDir(e, srcPath, dstPath)
85 default:
86 err = copyFile(e, srcPath, dstPath)
87 }
88 if err != nil {
89 return err
90 }
91 }
92 return nil
93}
94
95func copySymLink(info os.FileInfo, src, dst string) error {
96 mode := info.Mode()
97 link, err := os.Readlink(src)
98 if err != nil {
99 return err
100 }
101 err = os.Symlink(link, dst)
102 if err != nil {
103 return err
104 }
105 return os.Chmod(dst, mode)
106}
107
108func copyFile(info os.FileInfo, src, dst string) (err error) {
109 in, err := os.Open(src)
110 if err != nil {
111 return fmt.Errorf("error reading %s: %v", src, err)
112 }
113 defer in.Close()
114
115 out, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, info.Mode())
116 if err != nil {
117 return fmt.Errorf("error creating %s: %v", dst, err)
118 }
119 defer func() {
120 cErr := out.Close()
121 if err == nil {
122 err = cErr
123 }
124 }()
125
126 _, err = io.Copy(out, in)
127 if err != nil {
128 return fmt.Errorf("error copying %s: %v", dst, err)
129 }
130 return err
131}