Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/spf13/afero/copyOnWriteFs.go
2875 views
1
package afero
2
3
import (
4
"fmt"
5
"os"
6
"path/filepath"
7
"syscall"
8
"time"
9
)
10
11
var _ Lstater = (*CopyOnWriteFs)(nil)
12
13
// The CopyOnWriteFs is a union filesystem: a read only base file system with
14
// a possibly writeable layer on top. Changes to the file system will only
15
// be made in the overlay: Changing an existing file in the base layer which
16
// is not present in the overlay will copy the file to the overlay ("changing"
17
// includes also calls to e.g. Chtimes(), Chmod() and Chown()).
18
//
19
// Reading directories is currently only supported via Open(), not OpenFile().
20
type CopyOnWriteFs struct {
21
base Fs
22
layer Fs
23
}
24
25
func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
26
return &CopyOnWriteFs{base: base, layer: layer}
27
}
28
29
// Returns true if the file is not in the overlay
30
func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
31
if _, err := u.layer.Stat(name); err == nil {
32
return false, nil
33
}
34
_, err := u.base.Stat(name)
35
if err != nil {
36
if oerr, ok := err.(*os.PathError); ok {
37
if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT ||
38
oerr.Err == syscall.ENOTDIR {
39
return false, nil
40
}
41
}
42
if err == syscall.ENOENT {
43
return false, nil
44
}
45
}
46
return true, err
47
}
48
49
func (u *CopyOnWriteFs) copyToLayer(name string) error {
50
return copyToLayer(u.base, u.layer, name)
51
}
52
53
func (u *CopyOnWriteFs) Chtimes(name string, atime, mtime time.Time) error {
54
b, err := u.isBaseFile(name)
55
if err != nil {
56
return err
57
}
58
if b {
59
if err := u.copyToLayer(name); err != nil {
60
return err
61
}
62
}
63
return u.layer.Chtimes(name, atime, mtime)
64
}
65
66
func (u *CopyOnWriteFs) Chmod(name string, mode os.FileMode) error {
67
b, err := u.isBaseFile(name)
68
if err != nil {
69
return err
70
}
71
if b {
72
if err := u.copyToLayer(name); err != nil {
73
return err
74
}
75
}
76
return u.layer.Chmod(name, mode)
77
}
78
79
func (u *CopyOnWriteFs) Chown(name string, uid, gid int) error {
80
b, err := u.isBaseFile(name)
81
if err != nil {
82
return err
83
}
84
if b {
85
if err := u.copyToLayer(name); err != nil {
86
return err
87
}
88
}
89
return u.layer.Chown(name, uid, gid)
90
}
91
92
func (u *CopyOnWriteFs) Stat(name string) (os.FileInfo, error) {
93
fi, err := u.layer.Stat(name)
94
if err != nil {
95
isNotExist := u.isNotExist(err)
96
if isNotExist {
97
return u.base.Stat(name)
98
}
99
return nil, err
100
}
101
return fi, nil
102
}
103
104
func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
105
llayer, ok1 := u.layer.(Lstater)
106
lbase, ok2 := u.base.(Lstater)
107
108
if ok1 {
109
fi, b, err := llayer.LstatIfPossible(name)
110
if err == nil {
111
return fi, b, nil
112
}
113
114
if !u.isNotExist(err) {
115
return nil, b, err
116
}
117
}
118
119
if ok2 {
120
fi, b, err := lbase.LstatIfPossible(name)
121
if err == nil {
122
return fi, b, nil
123
}
124
if !u.isNotExist(err) {
125
return nil, b, err
126
}
127
}
128
129
fi, err := u.Stat(name)
130
131
return fi, false, err
132
}
133
134
func (u *CopyOnWriteFs) SymlinkIfPossible(oldname, newname string) error {
135
if slayer, ok := u.layer.(Linker); ok {
136
return slayer.SymlinkIfPossible(oldname, newname)
137
}
138
139
return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink}
140
}
141
142
func (u *CopyOnWriteFs) ReadlinkIfPossible(name string) (string, error) {
143
if rlayer, ok := u.layer.(LinkReader); ok {
144
return rlayer.ReadlinkIfPossible(name)
145
}
146
147
if rbase, ok := u.base.(LinkReader); ok {
148
return rbase.ReadlinkIfPossible(name)
149
}
150
151
return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink}
152
}
153
154
func (u *CopyOnWriteFs) isNotExist(err error) bool {
155
if e, ok := err.(*os.PathError); ok {
156
err = e.Err
157
}
158
if err == os.ErrNotExist || err == syscall.ENOENT || err == syscall.ENOTDIR {
159
return true
160
}
161
return false
162
}
163
164
// Renaming files present only in the base layer is not permitted
165
func (u *CopyOnWriteFs) Rename(oldname, newname string) error {
166
b, err := u.isBaseFile(oldname)
167
if err != nil {
168
return err
169
}
170
if b {
171
return syscall.EPERM
172
}
173
return u.layer.Rename(oldname, newname)
174
}
175
176
// Removing files present only in the base layer is not permitted. If
177
// a file is present in the base layer and the overlay, only the overlay
178
// will be removed.
179
func (u *CopyOnWriteFs) Remove(name string) error {
180
err := u.layer.Remove(name)
181
switch err {
182
case syscall.ENOENT:
183
_, err = u.base.Stat(name)
184
if err == nil {
185
return syscall.EPERM
186
}
187
return syscall.ENOENT
188
default:
189
return err
190
}
191
}
192
193
func (u *CopyOnWriteFs) RemoveAll(name string) error {
194
err := u.layer.RemoveAll(name)
195
switch err {
196
case syscall.ENOENT:
197
_, err = u.base.Stat(name)
198
if err == nil {
199
return syscall.EPERM
200
}
201
return syscall.ENOENT
202
default:
203
return err
204
}
205
}
206
207
func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
208
b, err := u.isBaseFile(name)
209
if err != nil {
210
return nil, err
211
}
212
213
if flag&(os.O_WRONLY|os.O_RDWR|os.O_APPEND|os.O_CREATE|os.O_TRUNC) != 0 {
214
if b {
215
if err = u.copyToLayer(name); err != nil {
216
return nil, err
217
}
218
return u.layer.OpenFile(name, flag, perm)
219
}
220
221
dir := filepath.Dir(name)
222
isaDir, err := IsDir(u.base, dir)
223
if err != nil && !os.IsNotExist(err) {
224
return nil, err
225
}
226
if isaDir {
227
if err = u.layer.MkdirAll(dir, 0o777); err != nil {
228
return nil, err
229
}
230
return u.layer.OpenFile(name, flag, perm)
231
}
232
233
isaDir, err = IsDir(u.layer, dir)
234
if err != nil {
235
return nil, err
236
}
237
if isaDir {
238
return u.layer.OpenFile(name, flag, perm)
239
}
240
241
return nil, &os.PathError{
242
Op: "open",
243
Path: name,
244
Err: syscall.ENOTDIR,
245
} // ...or os.ErrNotExist?
246
}
247
if b {
248
return u.base.OpenFile(name, flag, perm)
249
}
250
return u.layer.OpenFile(name, flag, perm)
251
}
252
253
// This function handles the 9 different possibilities caused
254
// by the union which are the intersection of the following...
255
//
256
// layer: doesn't exist, exists as a file, and exists as a directory
257
// base: doesn't exist, exists as a file, and exists as a directory
258
func (u *CopyOnWriteFs) Open(name string) (File, error) {
259
// Since the overlay overrides the base we check that first
260
b, err := u.isBaseFile(name)
261
if err != nil {
262
return nil, err
263
}
264
265
// If overlay doesn't exist, return the base (base state irrelevant)
266
if b {
267
return u.base.Open(name)
268
}
269
270
// If overlay is a file, return it (base state irrelevant)
271
dir, err := IsDir(u.layer, name)
272
if err != nil {
273
return nil, err
274
}
275
if !dir {
276
return u.layer.Open(name)
277
}
278
279
// Overlay is a directory, base state now matters.
280
// Base state has 3 states to check but 2 outcomes:
281
// A. It's a file or non-readable in the base (return just the overlay)
282
// B. It's an accessible directory in the base (return a UnionFile)
283
284
// If base is file or nonreadable, return overlay
285
dir, err = IsDir(u.base, name)
286
if !dir || err != nil {
287
return u.layer.Open(name)
288
}
289
290
// Both base & layer are directories
291
// Return union file (if opens are without error)
292
bfile, bErr := u.base.Open(name)
293
lfile, lErr := u.layer.Open(name)
294
295
// If either have errors at this point something is very wrong. Return nil and the errors
296
if bErr != nil || lErr != nil {
297
return nil, fmt.Errorf("BaseErr: %v\nOverlayErr: %v", bErr, lErr)
298
}
299
300
return &UnionFile{Base: bfile, Layer: lfile}, nil
301
}
302
303
func (u *CopyOnWriteFs) Mkdir(name string, perm os.FileMode) error {
304
dir, err := IsDir(u.base, name)
305
if err != nil {
306
return u.layer.MkdirAll(name, perm)
307
}
308
if dir {
309
return ErrFileExists
310
}
311
return u.layer.MkdirAll(name, perm)
312
}
313
314
func (u *CopyOnWriteFs) Name() string {
315
return "CopyOnWriteFs"
316
}
317
318
func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error {
319
dir, err := IsDir(u.base, name)
320
if err != nil {
321
return u.layer.MkdirAll(name, perm)
322
}
323
if dir {
324
// This is in line with how os.MkdirAll behaves.
325
return nil
326
}
327
return u.layer.MkdirAll(name, perm)
328
}
329
330
func (u *CopyOnWriteFs) Create(name string) (File, error) {
331
return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o666)
332
}
333
334