From 5866f5e0f31bda773730e1dca5c0a5d0b2a32e7e Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Thu, 14 Oct 2021 18:40:14 +0200 Subject: [PATCH] Report quota per storage space (#2152) --- .../unreleased/get-quota-storage-space.md | 7 +++ .../grpc/services/gateway/storageprovider.go | 2 +- .../storageprovider/storageprovider.go | 8 ++- pkg/storage/fs/nextcloud/nextcloud.go | 2 +- pkg/storage/fs/nextcloud/nextcloud_test.go | 2 +- pkg/storage/fs/owncloud/owncloud_unix.go | 3 +- pkg/storage/fs/owncloud/owncloud_windows.go | 3 +- .../fs/owncloudsql/owncloudsql_unix.go | 3 +- .../fs/owncloudsql/owncloudsql_windows.go | 3 +- pkg/storage/fs/s3/s3.go | 2 +- pkg/storage/storage.go | 2 +- .../utils/decomposedfs/decomposedfs.go | 20 +++++-- pkg/storage/utils/decomposedfs/lookup.go | 39 ++++++++----- pkg/storage/utils/decomposedfs/node/node.go | 46 ++++++++++----- pkg/storage/utils/decomposedfs/spaces.go | 8 +++ pkg/storage/utils/decomposedfs/tree/tree.go | 8 ++- pkg/storage/utils/decomposedfs/upload.go | 58 +++++++++++++------ pkg/storage/utils/eosfs/eosfs.go | 2 +- pkg/storage/utils/localfs/localfs_unix.go | 3 +- pkg/storage/utils/localfs/localfs_windows.go | 3 +- 20 files changed, 156 insertions(+), 68 deletions(-) create mode 100644 changelog/unreleased/get-quota-storage-space.md diff --git a/changelog/unreleased/get-quota-storage-space.md b/changelog/unreleased/get-quota-storage-space.md new file mode 100644 index 0000000000..9fd8208dd1 --- /dev/null +++ b/changelog/unreleased/get-quota-storage-space.md @@ -0,0 +1,7 @@ +Enhancement: Add a reference parameter to the getQuota request + +Implementation of [cs3org/cs3apis#147](https://github.com/cs3org/cs3apis/pull/147) + +Make the cs3apis accept a Reference in the getQuota Request to limit the call to a specific storage space. + +https://github.com/cs3org/reva/issues/2152 \ No newline at end of file diff --git a/internal/grpc/services/gateway/storageprovider.go b/internal/grpc/services/gateway/storageprovider.go index 249aac85e6..52a5999a65 100644 --- a/internal/grpc/services/gateway/storageprovider.go +++ b/internal/grpc/services/gateway/storageprovider.go @@ -2135,7 +2135,7 @@ func (s *svc) GetQuota(ctx context.Context, req *gateway.GetQuotaRequest) (*prov res, err := c.GetQuota(ctx, &provider.GetQuotaRequest{ Opaque: req.GetOpaque(), - // Ref: req.GetRef(), // TODO send which storage space ... or root + Ref: req.GetRef(), }) if err != nil { return nil, errors.Wrap(err, "gateway: error calling GetQuota") diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index 0560c2278d..19e363c9ef 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -1181,7 +1181,13 @@ func (s *service) CreateSymlink(ctx context.Context, req *provider.CreateSymlink } func (s *service) GetQuota(ctx context.Context, req *provider.GetQuotaRequest) (*provider.GetQuotaResponse, error) { - total, used, err := s.storage.GetQuota(ctx) + newRef, err := s.unwrap(ctx, req.Ref) + if err != nil { + return &provider.GetQuotaResponse{ + Status: status.NewInternal(ctx, err, "error unwrapping path"), + }, nil + } + total, used, err := s.storage.GetQuota(ctx, newRef) if err != nil { var st *rpc.Status switch err.(type) { diff --git a/pkg/storage/fs/nextcloud/nextcloud.go b/pkg/storage/fs/nextcloud/nextcloud.go index 43ac7f1fac..aefe5e26d7 100644 --- a/pkg/storage/fs/nextcloud/nextcloud.go +++ b/pkg/storage/fs/nextcloud/nextcloud.go @@ -679,7 +679,7 @@ func (nc *StorageDriver) ListGrants(ctx context.Context, ref *provider.Reference } // GetQuota as defined in the storage.FS interface -func (nc *StorageDriver) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (nc *StorageDriver) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { log := appctx.GetLogger(ctx) log.Info().Msg("GetQuota") diff --git a/pkg/storage/fs/nextcloud/nextcloud_test.go b/pkg/storage/fs/nextcloud/nextcloud_test.go index 6925a12c46..6a94f73a20 100644 --- a/pkg/storage/fs/nextcloud/nextcloud_test.go +++ b/pkg/storage/fs/nextcloud/nextcloud_test.go @@ -881,7 +881,7 @@ var _ = Describe("Nextcloud", func() { It("calls the GetQuota endpoint", func() { nc, called, teardown := setUpNextcloudServer() defer teardown() - maxBytes, maxFiles, err := nc.GetQuota(ctx) + maxBytes, maxFiles, err := nc.GetQuota(ctx, nil) Expect(err).ToNot(HaveOccurred()) Expect(maxBytes).To(Equal(uint64(456))) Expect(maxFiles).To(Equal(uint64(123))) diff --git a/pkg/storage/fs/owncloud/owncloud_unix.go b/pkg/storage/fs/owncloud/owncloud_unix.go index 0bb88308c8..61cc65433d 100755 --- a/pkg/storage/fs/owncloud/owncloud_unix.go +++ b/pkg/storage/fs/owncloud/owncloud_unix.go @@ -30,6 +30,7 @@ import ( "strings" "syscall" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" ) @@ -68,7 +69,7 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } -func (fs *ocfs) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (fs *ocfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { // TODO quota of which storage space? // we could use the logged in user, but when a user has access to multiple storages this falls short // for now return quota of root diff --git a/pkg/storage/fs/owncloud/owncloud_windows.go b/pkg/storage/fs/owncloud/owncloud_windows.go index 69dbbeddb3..80e86e4cd4 100644 --- a/pkg/storage/fs/owncloud/owncloud_windows.go +++ b/pkg/storage/fs/owncloud/owncloud_windows.go @@ -29,6 +29,7 @@ import ( "os" "strings" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" "golang.org/x/sys/windows" ) @@ -55,7 +56,7 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } -func (fs *ocfs) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (fs *ocfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { // TODO quota of which storage space? // we could use the logged in user, but when a user has access to multiple storages this falls short // for now return quota of root diff --git a/pkg/storage/fs/owncloudsql/owncloudsql_unix.go b/pkg/storage/fs/owncloudsql/owncloudsql_unix.go index a63f712df6..95ef01cab2 100755 --- a/pkg/storage/fs/owncloudsql/owncloudsql_unix.go +++ b/pkg/storage/fs/owncloudsql/owncloudsql_unix.go @@ -30,6 +30,7 @@ import ( "strings" "syscall" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" ) @@ -68,7 +69,7 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { return strings.Trim(etag, "\"") } -func (fs *owncloudsqlfs) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (fs *owncloudsqlfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { // TODO quota of which storage space? // we could use the logged in user, but when a user has access to multiple storages this falls short // for now return quota of root diff --git a/pkg/storage/fs/owncloudsql/owncloudsql_windows.go b/pkg/storage/fs/owncloudsql/owncloudsql_windows.go index 6abd8e1098..679c9e77d0 100644 --- a/pkg/storage/fs/owncloudsql/owncloudsql_windows.go +++ b/pkg/storage/fs/owncloudsql/owncloudsql_windows.go @@ -29,6 +29,7 @@ import ( "os" "strings" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" "golang.org/x/sys/windows" ) @@ -55,7 +56,7 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } -func (fs *owncloudsqlfs) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (fs *owncloudsqlfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { // TODO quota of which storage space? // we could use the logged in user, but when a user has access to multiple storages this falls short // for now return quota of root diff --git a/pkg/storage/fs/s3/s3.go b/pkg/storage/fs/s3/s3.go index f1522deb11..7636458a8b 100644 --- a/pkg/storage/fs/s3/s3.go +++ b/pkg/storage/fs/s3/s3.go @@ -268,7 +268,7 @@ func (fs *s3FS) UpdateGrant(ctx context.Context, ref *provider.Reference, g *pro return errtypes.NotSupported("s3: operation not supported") } -func (fs *s3FS) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (fs *s3FS) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { return 0, 0, nil } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index ff39b55b26..31a021e387 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -52,7 +52,7 @@ type FS interface { RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error) - GetQuota(ctx context.Context) ( /*TotalBytes*/ uint64 /*UsedBytes*/, uint64, error) + GetQuota(ctx context.Context, ref *provider.Reference) ( /*TotalBytes*/ uint64 /*UsedBytes*/, uint64, error) CreateReference(ctx context.Context, path string, targetURI *url.URL) error Shutdown(ctx context.Context) error SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error diff --git a/pkg/storage/utils/decomposedfs/decomposedfs.go b/pkg/storage/utils/decomposedfs/decomposedfs.go index f57731c847..59c6b7ecb4 100644 --- a/pkg/storage/utils/decomposedfs/decomposedfs.go +++ b/pkg/storage/utils/decomposedfs/decomposedfs.go @@ -128,10 +128,16 @@ func (fs *Decomposedfs) Shutdown(ctx context.Context) error { // GetQuota returns the quota available // TODO Document in the cs3 should we return quota or free space? -func (fs *Decomposedfs) GetQuota(ctx context.Context) (total uint64, inUse uint64, err error) { +func (fs *Decomposedfs) GetQuota(ctx context.Context, ref *provider.Reference) (total uint64, inUse uint64, err error) { var n *node.Node - if n, err = fs.lu.HomeOrRootNode(ctx); err != nil { - return 0, 0, err + if ref != nil { + if n, err = fs.lu.NodeFromResource(ctx, ref); err != nil { + return 0, 0, err + } + } else { + if n, err = fs.lu.HomeOrRootNode(ctx); err != nil { + return 0, 0, err + } } if !n.Exists { @@ -206,7 +212,7 @@ func (fs *Decomposedfs) CreateHome(ctx context.Context) (err error) { return } - if fs.o.TreeTimeAccounting { + if fs.o.TreeTimeAccounting || fs.o.TreeSizeAccounting { homePath := h.InternalPath() // mark the home node as the end of propagation if err = xattr.Set(homePath, xattrs.PropagationAttr, []byte("1")); err != nil { @@ -215,6 +221,10 @@ func (fs *Decomposedfs) CreateHome(ctx context.Context) (err error) { } } + if err := n.SetMetadata(xattrs.SpaceNameAttr, u.DisplayName); err != nil { + return err + } + // add storage space if err := fs.createStorageSpace(ctx, "personal", h.ID); err != nil { return err @@ -289,7 +299,7 @@ func (fs *Decomposedfs) CreateDir(ctx context.Context, ref *provider.Reference) err = fs.tp.CreateDir(ctx, n) - if fs.o.TreeTimeAccounting { + if fs.o.TreeTimeAccounting || fs.o.TreeSizeAccounting { nodePath := n.InternalPath() // mark the home node as the end of propagation if err = xattr.Set(nodePath, xattrs.PropagationAttr, []byte("1")); err != nil { diff --git a/pkg/storage/utils/decomposedfs/lookup.go b/pkg/storage/utils/decomposedfs/lookup.go index fb85fc1a6c..974edd0e4c 100644 --- a/pkg/storage/utils/decomposedfs/lookup.go +++ b/pkg/storage/utils/decomposedfs/lookup.go @@ -45,23 +45,26 @@ func (lu *Lookup) NodeFromResource(ctx context.Context, ref *provider.Reference) if ref.ResourceId != nil { // check if a storage space reference is used // currently, the decomposed fs uses the root node id as the space id - n, err := lu.NodeFromID(ctx, ref.ResourceId) + spaceRoot, err := lu.NodeFromID(ctx, ref.ResourceId) if err != nil { return nil, err } - - p := filepath.Clean(ref.Path) - if p != "." { - // walk the relative path - n, err = lu.WalkPath(ctx, n, p, false, func(ctx context.Context, n *node.Node) error { - return nil - }) - if err != nil { - return nil, err + n := spaceRoot + // is this a relative reference? + if ref.Path != "" { + p := filepath.Clean(ref.Path) + if p != "." { + // walk the relative path + n, err = lu.WalkPath(ctx, n, p, false, func(ctx context.Context, n *node.Node) error { + return nil + }) + if err != nil { + return nil, err + } } - return n, nil + // use reference id as space root for relative references + n.SpaceRoot = spaceRoot } - return n, nil } @@ -78,23 +81,27 @@ func (lu *Lookup) NodeFromPath(ctx context.Context, fn string, followReferences log := appctx.GetLogger(ctx) log.Debug().Interface("fn", fn).Msg("NodeFromPath()") - n, err := lu.HomeOrRootNode(ctx) + root, err := lu.HomeOrRootNode(ctx) if err != nil { return nil, err } + n := root // TODO collect permissions of the current user on every segment fn = filepath.Clean(fn) if fn != "/" && fn != "." { n, err = lu.WalkPath(ctx, n, fn, followReferences, func(ctx context.Context, n *node.Node) error { log.Debug().Interface("node", n).Msg("NodeFromPath() walk") + if n.SpaceRoot != nil && n.SpaceRoot != root { + root = n.SpaceRoot + } return nil }) if err != nil { return nil, err } } - + n.SpaceRoot = root return n, nil } @@ -129,7 +136,9 @@ func (lu *Lookup) Path(ctx context.Context, n *node.Node) (p string, err error) // RootNode returns the root node of the storage func (lu *Lookup) RootNode(ctx context.Context) (*node.Node, error) { - return node.New("root", "", "", 0, "", nil, lu), nil + n := node.New("root", "", "", 0, "", nil, lu) + n.Exists = true + return n, nil } // HomeNode returns the home node of a user diff --git a/pkg/storage/utils/decomposedfs/node/node.go b/pkg/storage/utils/decomposedfs/node/node.go index 3b67fc7af4..84f05fcfa7 100644 --- a/pkg/storage/utils/decomposedfs/node/node.go +++ b/pkg/storage/utils/decomposedfs/node/node.go @@ -65,13 +65,14 @@ const ( // Node represents a node in the tree and provides methods to get a Parent or Child instance type Node struct { - ParentID string - ID string - Name string - Blobsize int64 - BlobID string - owner *userpb.UserId - Exists bool + ParentID string + ID string + Name string + Blobsize int64 + BlobID string + owner *userpb.UserId + Exists bool + SpaceRoot *Node lu PathLookup } @@ -191,6 +192,10 @@ func ReadNode(ctx context.Context, lu PathLookup, id string) (n *Node, err error default: return nil, errtypes.InternalError(err.Error()) } + // check if this is a space root + if _, err = xattr.Get(nodePath, xattrs.SpaceNameAttr); err == nil { + n.SpaceRoot = n + } // lookup name in extended attributes if attrBytes, err = xattr.Get(nodePath, xattrs.NameAttr); err == nil { n.Name = string(attrBytes) @@ -239,9 +244,10 @@ func (n *Node) Child(ctx context.Context, name string) (*Node, error) { if err != nil { if os.IsNotExist(err) || isNotDir(err) { c := &Node{ - lu: n.lu, - ParentID: n.ID, - Name: name, + lu: n.lu, + ParentID: n.ID, + Name: name, + SpaceRoot: n.SpaceRoot, } return c, nil // if the file does not exist we return a node that has Exists = false } @@ -268,8 +274,9 @@ func (n *Node) Parent() (p *Node, err error) { return nil, fmt.Errorf("Decomposedfs: root has no parent") } p = &Node{ - lu: n.lu, - ID: n.ParentID, + lu: n.lu, + ID: n.ParentID, + SpaceRoot: n.SpaceRoot, } parentPath := n.lu.InternalPath(n.ParentID) @@ -608,11 +615,18 @@ func (n *Node) AsResourceInfo(ctx context.Context, rp *provider.ResourcePermissi // quota if _, ok := mdKeysMap[QuotaKey]; (nodeType == provider.ResourceType_RESOURCE_TYPE_CONTAINER) && returnAllKeys || ok { var quotaPath string - if r, err := n.lu.HomeOrRootNode(ctx); err == nil { - quotaPath = r.InternalPath() - readQuotaIntoOpaque(ctx, quotaPath, ri) + if n.SpaceRoot == nil { + root, err := n.lu.HomeOrRootNode(ctx) + if err == nil { + quotaPath = root.InternalPath() + } else { + sublog.Debug().Err(err).Msg("error determining the space root node for quota") + } } else { - sublog.Error().Err(err).Msg("error determining home or root node for quota") + quotaPath = n.SpaceRoot.InternalPath() + } + if quotaPath != "" { + readQuotaIntoOpaque(ctx, quotaPath, ri) } } diff --git a/pkg/storage/utils/decomposedfs/spaces.go b/pkg/storage/utils/decomposedfs/spaces.go index f6871ea863..c57320878b 100644 --- a/pkg/storage/utils/decomposedfs/spaces.go +++ b/pkg/storage/utils/decomposedfs/spaces.go @@ -66,6 +66,14 @@ func (fs *Decomposedfs) CreateStorageSpace(ctx context.Context, req *provider.Cr return nil, err } + // always enable propagation on the storage space root + nodePath := n.InternalPath() + // mark the space root node as the end of propagation + if err = xattr.Set(nodePath, xattrs.PropagationAttr, []byte("1")); err != nil { + appctx.GetLogger(ctx).Error().Err(err).Interface("node", n).Msg("could not mark node to propagate") + return nil, err + } + if err := fs.createHiddenSpaceFolder(ctx, n); err != nil { return nil, err } diff --git a/pkg/storage/utils/decomposedfs/tree/tree.go b/pkg/storage/utils/decomposedfs/tree/tree.go index 6d0da24d00..30752f16cb 100644 --- a/pkg/storage/utils/decomposedfs/tree/tree.go +++ b/pkg/storage/utils/decomposedfs/tree/tree.go @@ -578,8 +578,12 @@ func (t *Tree) Propagate(ctx context.Context, n *node.Node) (err error) { // is propagation enabled for the parent node? var root *node.Node - if root, err = t.lookup.HomeOrRootNode(ctx); err != nil { - return + if n.SpaceRoot == nil { + if root, err = t.lookup.HomeOrRootNode(ctx); err != nil { + return + } + } else { + root = n.SpaceRoot } // use a sync time and don't rely on the mtime of the current node, as the stat might not change when a rename happened too quickly diff --git a/pkg/storage/utils/decomposedfs/upload.go b/pkg/storage/utils/decomposedfs/upload.go index c810a5d03d..5e69270d26 100644 --- a/pkg/storage/utils/decomposedfs/upload.go +++ b/pkg/storage/utils/decomposedfs/upload.go @@ -31,6 +31,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strconv" "strings" "time" @@ -42,9 +43,11 @@ import ( "github.com/cs3org/reva/pkg/logger" "github.com/cs3org/reva/pkg/storage/utils/chunking" "github.com/cs3org/reva/pkg/storage/utils/decomposedfs/node" + "github.com/cs3org/reva/pkg/storage/utils/decomposedfs/xattrs" "github.com/cs3org/reva/pkg/utils" "github.com/google/uuid" "github.com/pkg/errors" + "github.com/pkg/xattr" "github.com/rs/zerolog" tusd "github.com/tus/tusd/pkg/handler" ) @@ -132,6 +135,9 @@ func (fs *Decomposedfs) InitiateUpload(ctx context.Context, ref *provider.Refere "dir": filepath.Dir(relative), }, Size: uploadLength, + Storage: map[string]string{ + "SpaceRoot": n.SpaceRoot.ID, + }, } if metadata != nil { @@ -157,7 +163,7 @@ func (fs *Decomposedfs) InitiateUpload(ctx context.Context, ref *provider.Refere log.Debug().Interface("info", info).Interface("node", n).Interface("metadata", metadata).Msg("Decomposedfs: resolved filename") - _, err = checkQuota(ctx, fs, uint64(info.Size)) + _, err = checkQuota(ctx, fs, n.SpaceRoot, uint64(info.Size)) if err != nil { return nil, err } @@ -250,6 +256,14 @@ func (fs *Decomposedfs) NewUpload(ctx context.Context, info tusd.FileInfo) (uplo if err != nil { return nil, errors.Wrap(err, "Decomposedfs: error determining owner") } + var spaceRoot string + if info.Storage != nil { + if spaceRoot, ok = info.Storage["SpaceRoot"]; !ok { + spaceRoot = n.SpaceRoot.ID + } + } else { + spaceRoot = n.SpaceRoot.ID + } info.Storage = map[string]string{ "Type": "OCISStore", @@ -258,6 +272,7 @@ func (fs *Decomposedfs) NewUpload(ctx context.Context, info tusd.FileInfo) (uplo "NodeId": n.ID, "NodeParentId": n.ParentID, "NodeName": n.Name, + "SpaceRoot": spaceRoot, "Idp": usr.Id.Idp, "UserId": usr.Id.OpaqueId, @@ -460,11 +475,6 @@ func (upload *fileUpload) FinishUpload(ctx context.Context) (err error) { return } - _, err = checkQuota(upload.ctx, upload.fs, uint64(fi.Size())) - if err != nil { - return err - } - n := node.New( upload.info.Storage["NodeId"], upload.info.Storage["NodeParentId"], @@ -474,6 +484,12 @@ func (upload *fileUpload) FinishUpload(ctx context.Context) (err error) { nil, upload.fs.lu, ) + n.SpaceRoot = node.New(upload.info.Storage["SpaceRoot"], "", "", 0, "", nil, upload.fs.lu) + + _, err = checkQuota(upload.ctx, upload.fs, n.SpaceRoot, uint64(fi.Size())) + if err != nil { + return err + } if n.ID == "" { n.ID = uuid.New().String() @@ -734,19 +750,27 @@ func (upload *fileUpload) ConcatUploads(ctx context.Context, uploads []tusd.Uplo return } -func checkQuota(ctx context.Context, fs *Decomposedfs, fileSize uint64) (quotaSufficient bool, err error) { - total, inUse, err := fs.GetQuota(ctx) - if err != nil { - switch err.(type) { - case errtypes.NotFound: - // no quota for this storage (eg. no user context) - return true, nil - default: - return false, err - } +func checkQuota(ctx context.Context, fs *Decomposedfs, spaceRoot *node.Node, fileSize uint64) (quotaSufficient bool, err error) { + used, _ := spaceRoot.GetTreeSize() + quotaB, _ := xattr.Get(spaceRoot.InternalPath(), xattrs.QuotaAttr) + total, _ := strconv.ParseUint(string(quotaB), 10, 64) + + enoughDiskSpace := enoughDiskSpace(fs, spaceRoot.InternalPath(), fileSize) + if !enoughDiskSpace { + return false, errtypes.InsufficientStorage("disk full") } - if !(total == 0) && fileSize > total-inUse { + + if (fileSize > total-used || total < used) && !enoughDiskSpace { return false, errtypes.InsufficientStorage("quota exceeded") } return true, nil } + +func enoughDiskSpace(fs *Decomposedfs, path string, fileSize uint64) bool { + avalB, err := fs.getAvailableSize(path) + if err != nil { + return false + } + + return avalB > fileSize +} diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index 909c0edbfb..e25eeebd11 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -973,7 +973,7 @@ func (fs *eosfs) CreateStorageSpace(ctx context.Context, req *provider.CreateSto return nil, fmt.Errorf("unimplemented: CreateStorageSpace") } -func (fs *eosfs) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (fs *eosfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { u, err := getUser(ctx) if err != nil { return 0, 0, errors.Wrap(err, "eosfs: no user in ctx") diff --git a/pkg/storage/utils/localfs/localfs_unix.go b/pkg/storage/utils/localfs/localfs_unix.go index 29ea2224af..61cdaf9e86 100644 --- a/pkg/storage/utils/localfs/localfs_unix.go +++ b/pkg/storage/utils/localfs/localfs_unix.go @@ -30,6 +30,7 @@ import ( "strings" "syscall" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" ) @@ -66,7 +67,7 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } -func (fs *localfs) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (fs *localfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { // TODO quota of which storage space? // we could use the logged in user, but when a user has access to multiple storages this falls short // for now return quota of root diff --git a/pkg/storage/utils/localfs/localfs_windows.go b/pkg/storage/utils/localfs/localfs_windows.go index 1cc33dd6c9..235f1867d0 100644 --- a/pkg/storage/utils/localfs/localfs_windows.go +++ b/pkg/storage/utils/localfs/localfs_windows.go @@ -29,6 +29,7 @@ import ( "os" "strings" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" "golang.org/x/sys/windows" ) @@ -55,7 +56,7 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } -func (fs *localfs) GetQuota(ctx context.Context) (uint64, uint64, error) { +func (fs *localfs) GetQuota(ctx context.Context, ref *provider.Reference) (uint64, uint64, error) { // TODO quota of which storage space? // we could use the logged in user, but when a user has access to multiple storages this falls short // for now return quota of root