-
Notifications
You must be signed in to change notification settings - Fork 0
/
process_globs.go
216 lines (185 loc) · 5.32 KB
/
process_globs.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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package main
import (
"errors"
"fmt"
"io"
"io/fs"
"log"
"os"
"path"
"path/filepath"
"strings"
)
var ErrMultiUploadKey = errors.New(
"to upload multiple files, specify a blank -key or a -key ending in slash ('/')")
// processGlobs processes Options.globs, returning each source file via the
// returned channel.
func processGlobs(globs []string, Bucket, Key string, recursive, verbose bool) (chan *uploadObject, error) {
ch := make(chan *uploadObject)
// if globs is empty then assume we want to read from standard input
if len(globs) == 0 {
if Key == "" {
close(ch)
return nil, fmt.Errorf(
"uploading from standard input requires a -key name")
} else if strings.HasSuffix(Key, "/") {
close(ch)
return nil, fmt.Errorf(
"uploading from standard input requires a -key name, not a prefix: %s", Key)
}
if verbose {
log.Printf("reading from standard input")
}
go func(ch chan *uploadObject) {
defer close(ch)
ch <- &uploadObject{
bucket: Bucket,
key: Key,
rc: io.NopCloser(os.Stdin),
}
}(ch)
return ch, nil
}
// otherwise iterate over globs and process each entry as a filepath
// pattern
go func(ch chan *uploadObject, globs []string) {
defer close(ch)
// nqueued tracks how many uploadObject have been returned, and
// is used to return an error if we encounter multiple upload
// targets while the key is set to a non-prefix (ending in /)
// value.
nqueued := 0
for _, pattern := range globs {
// check for one or more filesystem matches for this
// glob pattern
matches, err := filepath.Glob(pattern)
if err != nil {
log.Printf("error processing glob: %s: %s", pattern, err)
continue
}
// if no matches were found log an error and continue
if len(matches) == 0 {
log.Printf("no matches for glob: %s", pattern)
continue
}
// process each matched file or directory
for _, match := range matches {
// if a key value was specified and isn't a
// prefix then we need to log an error if we
// encounter more than one upload, to prevent
// uploading multiple sources to the same
// target.
if nqueued > 1 && Key != "" && !strings.HasSuffix(Key, "/") {
log.Println(ErrMultiUploadKey)
return
}
// stat the source to see what it is, if we
// encounter an error just log the issue and
// continue
fi, err := os.Stat(match)
if err != nil {
log.Printf("cannot stat path: %s: %s", match, err)
continue
}
if fi.Mode().IsRegular() {
// open the file and calculate the
// bucket / key target name
fh, err := os.Open(match)
if err != nil {
log.Printf("cannot open path: %s: %s", match, err)
continue
}
var currentKey string
if Key != "" && !strings.HasSuffix(Key, "/") {
currentKey = Key
} else {
currentKey = filepath.ToSlash(filepath.Base(match))
currentKey = path.Join(Key, currentKey)
}
nqueued += 1
ch <- &uploadObject{
bucket: Bucket,
key: currentKey,
rc: fh,
}
} else if fi.Mode().IsDir() {
// directories specified in the globs
// will be walked to find files to
// upload
err = filepath.WalkDir(match, func(name string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
// process top-level directories; process
// sub-directories if recursive was set.
if d.IsDir() {
if recursive || name == match {
return nil
}
return filepath.SkipDir
}
// stat the source to determine what it is
dFi, dErr := d.Info()
if dErr != nil {
if errors.Is(dErr, fs.ErrNotExist) {
return nil
}
return dErr
}
// if the source wasn't a directory and isn't
// a regular file, skip processing it
if !dFi.Mode().IsRegular() {
return nil
}
// submit sub-directory file for upload
fh, err := os.Open(name)
if err != nil {
log.Printf("cannot open path: %s: %s", name, err)
return nil
}
// strip directory prefixes when a trailing slash
// was specified in the glob, similar to how rsync
// operates on directory paths
currentKey := name
if strings.HasSuffix(match, "/") {
currentKey, err = filepath.Rel(match, name)
if err != nil {
log.Printf("error processing currentKey: %s, %s: %s",
match, name, err)
return nil
}
}
if err != nil {
return err
}
// prepend specified Key prefix to currentKey
currentKey = path.Join(Key, filepath.ToSlash(currentKey))
// prior to submission increment nqueued and confirm
// that Key was either blank or was a prefix if
// multiple files have been queued
nqueued += 1
if nqueued > 1 && Key != "" && !strings.HasSuffix(Key, "/") {
return ErrMultiUploadKey
}
// submit upload source
ch <- &uploadObject{
bucket: Bucket,
key: currentKey,
rc: fh,
}
return nil
})
// log any errors encountered walking the directory
if err != nil {
if errors.Is(err, ErrMultiUploadKey) {
log.Println(err)
return
}
log.Printf("error processing directory: %s: %s", match, err)
}
}
}
}
}(ch, globs)
return ch, nil
}