This repository has been archived by the owner on Nov 13, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfs.go
219 lines (192 loc) · 5.33 KB
/
fs.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
217
218
219
package filestream
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
)
// EncodeOptions are a set of options for encoding files from the filesystem into a filestream.
// They may be used with EncodeFiles.
type EncodeOptions struct {
// Base path which should be used for stream.
// Paths within this archive will be relative to this path.
// By default, it will be set to the top level path provided to EncodeFiles.
// Setting base to "/" will cause the encoding to use absolute paths.
Base string
// IncludePermissions is whether or not to include permission codes from the files.
// Setting this to true will cause permissions to be preserved in the stream.
// This does not control whether owning group/user is sent.
IncludePermissions bool
// IncludeUser is whether or not to include the owning username in the stream.
// Setting this to true will cause the system to look up the username of the owning user.
// Failed username lookups will result in errors.
// This is supported on Linux and Darwin, and may be a no-op on other systems.
IncludeUser bool
// IncludeGroup is whether or not to include the owning group name in the stream.
// Setting this to true will cause the system to look up the group name of the owning group.
// Failed group name lookups will result in errors.
// This is supported on Linux and Darwin, and may be a no-op on other systems.
IncludeGroup bool
}
// EncodeFiles encodes files from a path into a stream.
func EncodeFiles(dst *Writer, path string, opts EncodeOptions) error {
// fix paths to be appropriate and absolute
if opts.Base == "" {
opts.Base = path
}
path, err := filepath.Abs(path)
if err != nil {
return err
}
opts.Base, err = filepath.Abs(opts.Base)
if err != nil {
return err
}
return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
// dont try to handle inaccessible files
if err != nil {
return err
}
// convert paths to relative when appropriate
rawpath := path
if opts.Base != "/" {
path, err = filepath.Rel(opts.Base, path)
if err != nil {
return err
}
}
// load appropriate file options
var fo FileOptions
if opts.IncludePermissions {
fo.Permissions = info.Mode()
}
if opts.IncludeUser {
fo.User, err = getUser(info)
if err != nil {
return err
}
}
if opts.IncludeGroup {
fo.Group, err = getGroup(info)
if err != nil {
return err
}
}
switch {
case info.Mode().IsDir():
// encode directory
return dst.Directory(path, fo)
case info.Mode().IsRegular():
// open file entry stream
fw, err := dst.File(path, fo)
if err != nil {
return err
}
// open file
f, err := os.Open(rawpath)
if err != nil {
return err
}
defer f.Close()
// copy file data to stream
_, err = io.Copy(fw, f)
if err != nil {
return err
}
// close file
err = f.Close()
if err != nil {
return err
}
// terminate file stream entry
err = fw.Close()
if err != nil {
return err
}
return nil
default:
// error if we dont know what to do with a special file
return fmt.Errorf("unsupported special file: %s", rawpath)
}
})
}
// DecodeOptions is a set of options for decoding files from a stream into the filesystem.
type DecodeOptions struct {
// Base is the base directory from which relative paths will be resolved.
Base string
// PreservePermissions is whether or not to preserve the perimission codes from the stream.
PreservePermissions bool
// PreserveUser is whether or not to preserve the owning user info from the stream.
PreserveUser bool
// PreserveGroup is whether or not to preserve the owning group info from the stream.
PreserveGroup bool
// DefaultOpts are the default file options.
// If any given option is not being preserved, the corresponding default will be applied to everything.
// If any given option is being preserved, the corresponding default will be applied where not present in the stream.
// Defaults to 640, current user, current group.
DefaultOpts FileOptions
}
// DecodeFiles decodes a filestream to the filesystem.
func DecodeFiles(src *Reader, opts DecodeOptions) error {
if opts.DefaultOpts.Permissions == 0 {
opts.DefaultOpts.Permissions = 0640
}
if opts.Base == "" {
wd, err := os.Getwd()
if err != nil {
return err
}
opts.Base = wd
}
for src.Next() {
fr := src.File()
path := filepath.Join(opts.Base, fr.Path())
fo := fr.Opts()
if !opts.PreservePermissions {
fo.Permissions = fo.Permissions &^ os.ModePerm
}
if !opts.PreserveUser {
fo.User = ""
}
if !opts.PreserveGroup {
fo.Group = ""
}
if fo.Permissions&os.ModePerm == 0 {
fo.Permissions |= opts.DefaultOpts.Permissions
if (fo.Permissions & os.ModeDir) != 0 {
fo.Permissions |= 0100
}
}
switch {
case fo.Permissions.IsDir():
err := os.MkdirAll(path, fo.Permissions&os.ModePerm)
if err != nil {
return err
}
case fo.Permissions.IsRegular():
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, fo.Permissions)
if err != nil {
return err
}
_, err = io.Copy(f, fr)
if err != nil {
f.Close()
return err
}
err = f.Close()
if err != nil {
return err
}
default:
return errors.New("cannot decode special file")
}
if fo.User != "" || fo.Group != "" {
err := chown(path, fo)
if err != nil {
return err
}
}
}
return src.Err()
}