diff options
Diffstat (limited to 's3/fs.go')
-rw-r--r-- | s3/fs.go | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/s3/fs.go b/s3/fs.go new file mode 100644 index 0000000..c5ae6a0 --- /dev/null +++ b/s3/fs.go @@ -0,0 +1,152 @@ +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 + ctx context.Context +} + +func NewS3FS(mc *minio.Client) S3FS { + return S3FS{ + cache: make(map[string]*S3Stat), + mc: mc, + } +} + +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) +} |