forked from rclone/rclone
/
files.go
136 lines (120 loc) · 2.76 KB
/
files.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Package bilib provides common stuff for bisync and bisync_test
// Here it's got local file/directory helpers (nice to have in lib/file)
package bilib
import (
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"runtime"
)
// PermSecure is a Unix permission for a file accessible only by its owner
const PermSecure = 0600
var (
regexLocalPath = regexp.MustCompile(`^[./\\]`)
regexWindowsPath = regexp.MustCompile(`^[a-zA-Z]:`)
regexRemotePath = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*:`)
)
// IsLocalPath returns true if its argument is a non-remote path.
// Empty string or a relative path will be considered local.
// Note: `c:dir` will be considered local on Windows but remote on Linux.
func IsLocalPath(path string) bool {
if path == "" || regexLocalPath.MatchString(path) {
return true
}
if runtime.GOOS == "windows" && regexWindowsPath.MatchString(path) {
return true
}
return !regexRemotePath.MatchString(path)
}
// FileExists returns true if the local file exists
func FileExists(file string) bool {
_, err := os.Stat(file)
return !os.IsNotExist(err)
}
// CopyFileIfExists is like CopyFile but does not fail if source does not exist
func CopyFileIfExists(srcFile, dstFile string) error {
if !FileExists(srcFile) {
return nil
}
return CopyFile(srcFile, dstFile)
}
// CopyFile copies a local file
func CopyFile(src, dst string) (err error) {
var (
rd io.ReadCloser
wr io.WriteCloser
info os.FileInfo
)
if info, err = os.Stat(src); err != nil {
return
}
if rd, err = os.Open(src); err != nil {
return
}
defer func() {
_ = rd.Close()
}()
if wr, err = os.Create(dst); err != nil {
return
}
_, err = io.Copy(wr, rd)
if e := wr.Close(); err == nil {
err = e
}
if e := os.Chmod(dst, info.Mode()); err == nil {
err = e
}
if e := os.Chtimes(dst, info.ModTime(), info.ModTime()); err == nil {
err = e
}
return
}
// CopyDir copies a local directory
func CopyDir(src string, dst string) (err error) {
src = filepath.Clean(src)
dst = filepath.Clean(dst)
si, err := os.Stat(src)
if err != nil {
return err
}
if !si.IsDir() {
return fmt.Errorf("source is not a directory")
}
_, err = os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
return
}
if err == nil {
return fmt.Errorf("destination already exists")
}
err = os.MkdirAll(dst, si.Mode())
if err != nil {
return
}
entries, err := os.ReadDir(src)
if err != nil {
return
}
for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())
if entry.IsDir() {
err = CopyDir(srcPath, dstPath)
if err != nil {
return
}
} else {
// Skip symlinks.
if entry.Type()&os.ModeSymlink != 0 {
continue
}
err = CopyFile(srcPath, dstPath)
if err != nil {
return
}
}
}
return
}