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
|
package s3
import (
"context"
"errors"
"io"
"log"
"os"
"path"
"strings"
"time"
"github.com/minio/minio-go/v7"
"golang.org/x/net/webdav"
)
/*
* S3FS lifetime is limited to a single request
* Conversely, Golang's abstraction has been thought to be shared between users
* Sharing an instance between users would be very dangerous (as we would need many checks between shared values)
*/
type S3FS struct {
cache map[string]*S3Stat
mc *minio.Client
local string
ctx context.Context
}
func NewS3FS(mc *minio.Client, local string) S3FS {
return S3FS{
cache: make(map[string]*S3Stat),
mc: mc,
local: local,
}
}
func (s S3FS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
s.ctx = ctx
p := NewS3Path(name)
if p.Class == ROOT {
return errors.New("Unable to create another root folder")
} else if p.Class == BUCKET {
log.Println("Creating bucket is not implemented yet")
return nil
}
f, err := NewS3File(&s, path.Join(name, ".bagage"))
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, strings.NewReader("This is a placeholder"))
return nil
}
func (s S3FS) OpenFile2(ctx context.Context, name string, flag int, perm os.FileMode) (*S3File, error) {
s.ctx = ctx
// If the file does not exist when opening it, we create a stub
if _, ok := s.cache[name]; !ok {
st := new(S3Stat)
st.fs = &s
st.path = NewS3Path(name)
st.path.Class = OBJECT
st.obj.Key = st.path.Key
st.obj.LastModified = time.Now()
s.cache[name] = st
}
return NewS3File(&s, name)
}
func (s S3FS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
return s.OpenFile2(ctx, name, flag, perm)
}
func (s S3FS) RemoveAll(ctx context.Context, name string) error {
//@FIXME nautilus deletes files one by one, at the end, it does not find its folder as it is "already deleted"
s.ctx = ctx
p := NewS3Path(name)
if p.Class == ROOT {
return errors.New("Unable to create another root folder")
} else if p.Class == BUCKET {
log.Println("Deleting bucket is not implemented yet")
return nil
}
objCh := s.mc.ListObjects(s.ctx, p.Bucket, minio.ListObjectsOptions{Prefix: p.Key, Recursive: true})
rmCh := s.mc.RemoveObjects(s.ctx, p.Bucket, objCh, minio.RemoveObjectsOptions{})
for rErr := range rmCh {
return rErr.Err
}
return nil
}
func (s S3FS) Rename(ctx context.Context, oldName, newName string) error {
s.ctx = ctx
po := NewS3Path(oldName)
pn := NewS3Path(newName)
if po.Class == ROOT || pn.Class == ROOT {
return errors.New("Unable to rename root folder")
} else if po.Class == BUCKET || pn.Class == BUCKET {
log.Println("Moving a bucket is not implemented yet")
return nil
}
//Check that newName is not inside oldName
if len(newName) > len(oldName) && newName[:len(oldName)] == oldName {
return errors.New("Cannot move an entity inside itself (eg. moving /data in /data/test is impossible)")
}
//Gather all keys, copy the object, delete the original
objCh := s.mc.ListObjects(s.ctx, po.Bucket, minio.ListObjectsOptions{Prefix: po.Key, Recursive: true})
for obj := range objCh {
src := minio.CopySrcOptions{
Bucket: po.Bucket,
Object: obj.Key,
}
dst := minio.CopyDestOptions{
Bucket: pn.Bucket,
Object: path.Join(pn.Key, obj.Key[len(po.Key):]),
}
_, err := s.mc.CopyObject(s.ctx, dst, src)
if err != nil {
return err
}
err = s.mc.RemoveObject(s.ctx, po.Bucket, obj.Key, minio.RemoveObjectOptions{})
var e minio.ErrorResponse
log.Println(errors.As(err, &e))
log.Println(e)
if errors.As(err, &e) && e.StatusCode == 200 {
/* @FIXME workaround for garage's bug #98 */
} else if err != nil {
return err
}
}
return nil
}
func (s S3FS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
s.ctx = ctx
return NewS3Stat(&s, name)
}
|