diff --git a/changelog/unreleased/ocis-quota.md b/changelog/unreleased/ocis-quota.md index 0d58ad70c8..5388c5c856 100644 --- a/changelog/unreleased/ocis-quota.md +++ b/changelog/unreleased/ocis-quota.md @@ -2,4 +2,5 @@ Enhancement: quota querying and tree accounting The ocs api now returns the user quota for the users home storage. Furthermore, the ocis storage driver now reads the quota from the extended attributes of the user home or root node and implements tree size accounting. Finally, ocdav PROPFINDS now handle the `DAV:quota-used-bytes` and `DAV:quote-available-bytes` properties. -https://github.com/cs3org/reva/pull/1405 \ No newline at end of file +https://github.com/cs3org/reva/pull/1405 +https://github.com/cs3org/reva/pull/1491 diff --git a/go.mod b/go.mod index d8dc08b628..cbbafd422e 100644 --- a/go.mod +++ b/go.mod @@ -47,6 +47,7 @@ require ( golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 google.golang.org/grpc v1.35.0 ) diff --git a/go.sum b/go.sum index 2ddf9ab822..c94803a7bc 100644 --- a/go.sum +++ b/go.sum @@ -1194,6 +1194,8 @@ golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7Ow golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b h1:lAZ0/chPUDWwjqosYR0X4M490zQhMsiJ4K3DbA7o+3g= +golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/storage/fs/ocis/ocis.go b/pkg/storage/fs/ocis/ocis.go index ff643cf94b..fe51235e81 100644 --- a/pkg/storage/fs/ocis/ocis.go +++ b/pkg/storage/fs/ocis/ocis.go @@ -26,7 +26,6 @@ import ( "path/filepath" "strconv" "strings" - "syscall" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" @@ -219,13 +218,12 @@ func (fs *ocisfs) GetQuota(ctx context.Context) (uint64, uint64, error) { if ri.Opaque != nil && ri.Opaque.Map != nil && ri.Opaque.Map["quota"] != nil && ri.Opaque.Map["quota"].Decoder == "plain" { quotaStr = string(ri.Opaque.Map["quota"].Value) } - stat := syscall.Statfs_t{} - err = syscall.Statfs(fs.lu.toInternalPath(node.ID), &stat) + + avail, err := fs.getAvailableSize(fs.lu.toInternalPath(node.ID)) if err != nil { return 0, 0, err } - - total := ri.Size + (stat.Bavail * uint64(stat.Bsize)) // used treesize + available space + total := avail + ri.Size switch { case quotaStr == _quotaUncalculated, quotaStr == _quotaUnknown, quotaStr == _quotaUnlimited: diff --git a/pkg/storage/fs/ocis/ocis_unix.go b/pkg/storage/fs/ocis/ocis_unix.go new file mode 100644 index 0000000000..d291514dbb --- /dev/null +++ b/pkg/storage/fs/ocis/ocis_unix.go @@ -0,0 +1,32 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// +build !windows + +package ocis + +import "syscall" + +func (fs *ocisfs) getAvailableSize(path string) (uint64, error) { + stat := syscall.Statfs_t{} + err := syscall.Statfs(path, &stat) + if err != nil { + return 0, err + } + return stat.Bavail * uint64(stat.Bsize), nil +} diff --git a/pkg/storage/fs/ocis/ocis_windows.go b/pkg/storage/fs/ocis/ocis_windows.go new file mode 100644 index 0000000000..f1663b491e --- /dev/null +++ b/pkg/storage/fs/ocis/ocis_windows.go @@ -0,0 +1,36 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// +build windows + +package ocis + +import "golang.org/x/sys/windows" + +func (fs *ocisfs) getAvailableSize(path string) (uint64, error) { + var free, total, avail uint64 + pathPtr, err := windows.UTF16PtrFromString(path) + if err != nil { + return 0, err + } + err = windows.GetDiskFreeSpaceEx(pathPtr, &avail, &total, &free) + if err != nil { + return 0, err + } + return avail, nil +} diff --git a/pkg/storage/utils/localfs/localfs.go b/pkg/storage/utils/localfs/localfs.go index 3b61580788..153baf0878 100644 --- a/pkg/storage/utils/localfs/localfs.go +++ b/pkg/storage/utils/localfs/localfs.go @@ -29,7 +29,6 @@ import ( "path" "strconv" "strings" - "syscall" "time" grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" @@ -518,20 +517,6 @@ func (fs *localfs) UpdateGrant(ctx context.Context, ref *provider.Reference, g * return fs.AddGrant(ctx, ref, g) } -func (fs *localfs) GetQuota(ctx context.Context) (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 - stat := syscall.Statfs_t{} - err := syscall.Statfs(fs.conf.Root, &stat) - if err != nil { - return 0, 0, err - } - total := stat.Blocks * uint64(stat.Bsize) // Total data blocks in filesystem - used := (stat.Blocks - stat.Bavail) * uint64(stat.Bsize) // Free blocks available to unprivileged user - return total, used, nil -} - func (fs *localfs) CreateReference(ctx context.Context, path string, targetURI *url.URL) error { if !fs.isShareFolder(ctx, path) { return errtypes.PermissionDenied("localfs: cannot create references outside the share folder") diff --git a/pkg/storage/utils/localfs/localfs_unix.go b/pkg/storage/utils/localfs/localfs_unix.go index 5cac1b7e59..abcf56553f 100644 --- a/pkg/storage/utils/localfs/localfs_unix.go +++ b/pkg/storage/utils/localfs/localfs_unix.go @@ -64,3 +64,17 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { etag := fmt.Sprintf(`"%x"`, h.Sum(nil)) return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } + +func (fs *localfs) GetQuota(ctx context.Context) (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 + stat := syscall.Statfs_t{} + err := syscall.Statfs(fs.wrap(ctx, "/"), &stat) + if err != nil { + return 0, 0, err + } + total := stat.Blocks * uint64(stat.Bsize) // Total data blocks in filesystem + used := (stat.Blocks - stat.Bavail) * uint64(stat.Bsize) // Free blocks available to unprivileged user + return total, used, nil +} diff --git a/pkg/storage/utils/localfs/localfs_windows.go b/pkg/storage/utils/localfs/localfs_windows.go index 31a7590199..7dcab15cc4 100644 --- a/pkg/storage/utils/localfs/localfs_windows.go +++ b/pkg/storage/utils/localfs/localfs_windows.go @@ -29,6 +29,7 @@ import ( "strings" "github.com/cs3org/reva/pkg/appctx" + "golang.org/x/sys/windows" ) // calcEtag will create an etag based on the md5 of @@ -52,3 +53,22 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { etag := fmt.Sprintf(`"%x"`, h.Sum(nil)) return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } + +func (fs *localfs) GetQuota(ctx context.Context) (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 + var free, total, avail uint64 + + pathPtr, err := windows.UTF16PtrFromString(fs.wrap(ctx, "/")) + if err != nil { + return 0, 0, err + } + err = windows.GetDiskFreeSpaceEx(pathPtr, &avail, &total, &free) + if err != nil { + return 0, 0, err + } + + used := total - free + return total, used, nil +}