Bagage is the bridge between our users and garage, it enables them to synchronize files that matter for them from their computer to garage through WebDAV
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

148 lines
3.6 KiB

package main
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) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, 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) 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)
}