mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-22 07:30:25 +00:00
GCS: add a trailing / to "directories"
This way SFTPGo should be compatible with Google Cloud console. This change should be backward compatibile, testing is welcome Fixes #464
This commit is contained in:
parent
e09bdd43d4
commit
93dfb03eaf
1 changed files with 48 additions and 25 deletions
73
vfs/gcsfs.go
73
vfs/gcsfs.go
|
@ -5,7 +5,6 @@ package vfs
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
|
@ -112,37 +111,18 @@ func (fs *GCSFs) ConnectionID() string {
|
|||
|
||||
// Stat returns a FileInfo describing the named file
|
||||
func (fs *GCSFs) Stat(name string) (os.FileInfo, error) {
|
||||
var result *FileInfo
|
||||
var err error
|
||||
if name == "" || name == "." {
|
||||
err := fs.checkIfBucketExists()
|
||||
if err != nil {
|
||||
return result, err
|
||||
return nil, err
|
||||
}
|
||||
return NewFileInfo(name, true, 0, time.Now(), false), nil
|
||||
}
|
||||
if fs.config.KeyPrefix == name+"/" {
|
||||
return NewFileInfo(name, true, 0, time.Now(), false), nil
|
||||
}
|
||||
attrs, err := fs.headObject(name)
|
||||
if err == nil {
|
||||
objSize := attrs.Size
|
||||
objectModTime := attrs.Updated
|
||||
isDir := attrs.ContentType == dirMimeType || strings.HasSuffix(attrs.Name, "/")
|
||||
return NewFileInfo(name, isDir, objSize, objectModTime, false), nil
|
||||
}
|
||||
if !fs.IsNotExist(err) {
|
||||
return result, err
|
||||
}
|
||||
// now check if this is a prefix (virtual directory)
|
||||
hasContents, err := fs.hasContents(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasContents {
|
||||
return NewFileInfo(name, true, 0, time.Now(), false), nil
|
||||
}
|
||||
return nil, errors.New("404 no such file or directory")
|
||||
_, info, err := fs.getObjectStat(name)
|
||||
return info, err
|
||||
}
|
||||
|
||||
// Lstat returns a FileInfo describing the named file
|
||||
|
@ -229,7 +209,7 @@ func (fs *GCSFs) Rename(source, target string) error {
|
|||
if source == target {
|
||||
return nil
|
||||
}
|
||||
fi, err := fs.Stat(source)
|
||||
realSourceName, fi, err := fs.getObjectStat(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -241,8 +221,11 @@ func (fs *GCSFs) Rename(source, target string) error {
|
|||
if hasContents {
|
||||
return fmt.Errorf("cannot rename non empty directory: %#v", source)
|
||||
}
|
||||
if !strings.HasSuffix(target, "/") {
|
||||
target += "/"
|
||||
}
|
||||
}
|
||||
src := fs.svc.Bucket(fs.config.Bucket).Object(source)
|
||||
src := fs.svc.Bucket(fs.config.Bucket).Object(realSourceName)
|
||||
dst := fs.svc.Bucket(fs.config.Bucket).Object(target)
|
||||
ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(fs.ctxTimeout))
|
||||
defer cancelFn()
|
||||
|
@ -277,11 +260,18 @@ func (fs *GCSFs) Remove(name string, isDir bool) error {
|
|||
if hasContents {
|
||||
return fmt.Errorf("cannot remove non empty directory: %#v", name)
|
||||
}
|
||||
if !strings.HasSuffix(name, "/") {
|
||||
name += "/"
|
||||
}
|
||||
}
|
||||
ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(fs.ctxTimeout))
|
||||
defer cancelFn()
|
||||
|
||||
err := fs.svc.Bucket(fs.config.Bucket).Object(name).Delete(ctx)
|
||||
if fs.IsNotExist(err) && isDir {
|
||||
// we can have directories without a trailing "/" (created using v2.1.0 and before)
|
||||
err = fs.svc.Bucket(fs.config.Bucket).Object(strings.TrimSuffix(name, "/")).Delete(ctx)
|
||||
}
|
||||
metrics.GCSDeleteObjectCompleted(err)
|
||||
return err
|
||||
}
|
||||
|
@ -292,6 +282,9 @@ func (fs *GCSFs) Mkdir(name string) error {
|
|||
if !fs.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if !strings.HasSuffix(name, "/") {
|
||||
name += "/"
|
||||
}
|
||||
_, w, _, err := fs.Create(name, -1)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -613,6 +606,36 @@ func (fs *GCSFs) resolve(name string, prefix string) (string, bool) {
|
|||
return result, isDir
|
||||
}
|
||||
|
||||
// getObjectStat returns the stat result and the real object name as first value
|
||||
func (fs *GCSFs) getObjectStat(name string) (string, os.FileInfo, error) {
|
||||
attrs, err := fs.headObject(name)
|
||||
if err == nil {
|
||||
objSize := attrs.Size
|
||||
objectModTime := attrs.Updated
|
||||
isDir := attrs.ContentType == dirMimeType || strings.HasSuffix(attrs.Name, "/")
|
||||
return name, NewFileInfo(name, isDir, objSize, objectModTime, false), nil
|
||||
}
|
||||
if !fs.IsNotExist(err) {
|
||||
return "", nil, err
|
||||
}
|
||||
// now check if this is a prefix (virtual directory)
|
||||
hasContents, err := fs.hasContents(name)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if hasContents {
|
||||
return name, NewFileInfo(name, true, 0, time.Now(), false), nil
|
||||
}
|
||||
// finally check if this is an object with a trailing /
|
||||
attrs, err = fs.headObject(name + "/")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
objSize := attrs.Size
|
||||
objectModTime := attrs.Updated
|
||||
return name + "/", NewFileInfo(name, true, objSize, objectModTime, false), nil
|
||||
}
|
||||
|
||||
func (fs *GCSFs) checkIfBucketExists() error {
|
||||
ctx, cancelFn := context.WithDeadline(context.Background(), time.Now().Add(fs.ctxTimeout))
|
||||
defer cancelFn()
|
||||
|
|
Loading…
Reference in a new issue