From fe65742adb294d3d305a67df5c1fbe63cf309f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Tue, 12 Nov 2024 15:29:27 +0100 Subject: [PATCH] bump reva to v2.26.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jörn Friedrich Dreyer --- changelog/unreleased/bump-reva.md | 8 ++- go.mod | 2 +- go.sum | 4 +- .../pkg/cbox/storage/eoswrapper/eoswrapper.go | 11 ++-- .../reva/v2/pkg/ocm/storage/received/ocm.go | 32 +++++++-- .../rhttp/datatx/utils/download/download.go | 66 ++++++++++++------- .../reva/v2/pkg/storage/fs/cephfs/cephfs.go | 41 ++++++++++-- .../reva/v2/pkg/storage/fs/hello/hello.go | 16 +++-- .../v2/pkg/storage/fs/hello/unimplemented.go | 5 +- .../v2/pkg/storage/fs/nextcloud/nextcloud.go | 66 +++++++++++++++++-- .../fs/nextcloud/nextcloud_server_mock.go | 1 + .../pkg/storage/fs/owncloudsql/owncloudsql.go | 57 ++++++++++++---- .../cs3org/reva/v2/pkg/storage/fs/s3/s3.go | 23 +++++-- .../cs3org/reva/v2/pkg/storage/storage.go | 7 +- .../utils/decomposedfs/decomposedfs.go | 53 +++++++-------- .../storage/utils/decomposedfs/revisions.go | 48 ++++++++++---- .../pkg/storage/utils/decomposedfs/upload.go | 4 ++ .../reva/v2/pkg/storage/utils/eosfs/eosfs.go | 42 ++++++++---- .../v2/pkg/storage/utils/localfs/localfs.go | 57 ++++++++++++---- .../reva/v2/pkg/storage/utils/metadata/cs3.go | 9 +-- .../storage/utils/middleware/middleware.go | 22 +++---- .../cs3org/reva/v2/pkg/utils/utils.go | 5 ++ vendor/modules.txt | 2 +- 23 files changed, 409 insertions(+), 172 deletions(-) diff --git a/changelog/unreleased/bump-reva.md b/changelog/unreleased/bump-reva.md index 06ea1e462..d2bdfda95 100644 --- a/changelog/unreleased/bump-reva.md +++ b/changelog/unreleased/bump-reva.md @@ -1,5 +1,9 @@ -Bugfix: Bump Reva +Bugfix: Bump Reva to v2.26.5 -bumps reva version + * Fix [cs3org/reva#4926](https://github.com/cs3org/reva/issues/4926): Make etag always match content on downloads + * Fix [cs3org/reva#4920](https://github.com/cs3org/reva/issues/4920): Return correct status codes for simple uploads + * Fix [cs3org/reva#4924](https://github.com/cs3org/reva/issues/4924): Fix sync propagation + * Fix [cs3org/reva#4916](https://github.com/cs3org/reva/issues/4916): Improve posixfs stability and performanc +https://github.com/owncloud/ocis/pull/10552 https://github.com/owncloud/ocis/pull/10539 diff --git a/go.mod b/go.mod index 45a14aced..577be495b 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/coreos/go-oidc/v3 v3.11.0 github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1 - github.com/cs3org/reva/v2 v2.26.5-0.20241111162950-e77dd61e7edb + github.com/cs3org/reva/v2 v2.26.5 github.com/davidbyttow/govips/v2 v2.15.0 github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8 github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e diff --git a/go.sum b/go.sum index b23ce59e8..4115e711a 100644 --- a/go.sum +++ b/go.sum @@ -255,8 +255,8 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c= github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME= github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1 h1:RU6LT6mkD16xZs011+8foU7T3LrPvTTSWeTQ9OgfhkA= github.com/cs3org/go-cs3apis v0.0.0-20241105092511-3ad35d174fc1/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ= -github.com/cs3org/reva/v2 v2.26.5-0.20241111162950-e77dd61e7edb h1:owRv9x5GlKKdqCCM70kZKCsLAcDkFPkyOb129Jmklt0= -github.com/cs3org/reva/v2 v2.26.5-0.20241111162950-e77dd61e7edb/go.mod h1:KP0Zomt3dNIr/kU2M1mXzTIVFOtxBVS4qmBDMRCfrOQ= +github.com/cs3org/reva/v2 v2.26.5 h1:LWIOSpmgoVQDfe9S2renzqqAXorFs6lT+5Vodhr3M68= +github.com/cs3org/reva/v2 v2.26.5/go.mod h1:KP0Zomt3dNIr/kU2M1mXzTIVFOtxBVS4qmBDMRCfrOQ= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= diff --git a/vendor/github.com/cs3org/reva/v2/pkg/cbox/storage/eoswrapper/eoswrapper.go b/vendor/github.com/cs3org/reva/v2/pkg/cbox/storage/eoswrapper/eoswrapper.go index 0158dc074..f9ef19690 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/cbox/storage/eoswrapper/eoswrapper.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/cbox/storage/eoswrapper/eoswrapper.go @@ -28,6 +28,9 @@ import ( "github.com/Masterminds/sprig" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" + ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/events" @@ -36,8 +39,6 @@ import ( "github.com/cs3org/reva/v2/pkg/storage/utils/eosfs" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" - "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" ) func init() { @@ -193,12 +194,12 @@ func (w *wrapper) ListRevisions(ctx context.Context, ref *provider.Reference) ([ return w.FS.ListRevisions(ctx, ref) } -func (w *wrapper) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string) (io.ReadCloser, error) { +func (w *wrapper) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { if err := w.userIsProjectAdmin(ctx, ref); err != nil { - return nil, err + return nil, nil, err } - return w.FS.DownloadRevision(ctx, ref, revisionKey) + return w.FS.DownloadRevision(ctx, ref, revisionKey, openReaderfunc) } func (w *wrapper) RestoreRevision(ctx context.Context, ref *provider.Reference, revisionKey string) error { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go b/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go index 40c6eac29..f43c24cd3 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/ocm/storage/received/ocm.go @@ -38,6 +38,8 @@ import ( ocmpb "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typepb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/studio-b12/gowebdav" + "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/mime" @@ -49,7 +51,6 @@ import ( "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "github.com/cs3org/reva/v2/pkg/utils/cfg" - "github.com/studio-b12/gowebdav" ) func init() { @@ -364,13 +365,30 @@ func (d *driver) ListFolder(ctx context.Context, ref *provider.Reference, _ []st return res, nil } -func (d *driver) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { - client, _, rel, err := d.webdavClient(ctx, nil, ref) +func (d *driver) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { + client, share, rel, err := d.webdavClient(ctx, nil, ref) if err != nil { - return nil, err + return nil, nil, err } - return client.ReadStream(rel) + info, err := client.StatWithProps(rel, []string{}) // request all properties by giving an empty list + if err != nil { + if gowebdav.IsErrNotFound(err) { + return nil, nil, errtypes.NotFound(ref.GetPath()) + } + return nil, nil, err + } + md, err := convertStatToResourceInfo(ref, info, share) + if err != nil { + return nil, nil, err + } + + if !openReaderfunc(md) { + return md, nil, nil + } + + reader, err := client.ReadStream(rel) + return md, reader, err } func (d *driver) GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error) { @@ -396,8 +414,8 @@ func (d *driver) ListRevisions(ctx context.Context, ref *provider.Reference) ([] return nil, errtypes.NotSupported("operation not supported") } -func (d *driver) DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (io.ReadCloser, error) { - return nil, errtypes.NotSupported("operation not supported") +func (d *driver) DownloadRevision(ctx context.Context, ref *provider.Reference, key string, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { + return nil, nil, errtypes.NotSupported("operation not supported") } func (d *driver) RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/rhttp/datatx/utils/download/download.go b/vendor/github.com/cs3org/reva/v2/pkg/rhttp/datatx/utils/download/download.go index 51a61092f..ac85fbdeb 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/rhttp/datatx/utils/download/download.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/rhttp/datatx/utils/download/download.go @@ -30,6 +30,8 @@ import ( "strings" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/rs/zerolog" + "github.com/cs3org/reva/v2/internal/grpc/services/storageprovider" "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" "github.com/cs3org/reva/v2/pkg/appctx" @@ -37,7 +39,6 @@ import ( "github.com/cs3org/reva/v2/pkg/storage" "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" - "github.com/rs/zerolog" ) type contextKey struct{} @@ -91,27 +92,47 @@ func GetOrHeadFile(w http.ResponseWriter, r *http.Request, fs storage.FS, spaceI // TODO check preconditions like If-Range, If-Match ... var md *provider.ResourceInfo + var content io.ReadCloser var err error + var notModified bool - // do a stat to set a Content-Length header + // do a stat to set Content-Length and etag headers - if md, err = fs.GetMD(ctx, ref, nil, []string{"size", "mimetype", "etag"}); err != nil { - handleError(w, &sublog, err, "stat") + md, content, err = fs.Download(ctx, ref, func(md *provider.ResourceInfo) bool { + // range requests always need to open the reader to check if it is seekable + if r.Header.Get("Range") != "" { + return true + } + // otherwise, HEAD requests do not need to open a reader + if r.Method == "HEAD" { + return false + } + + // check etag, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match + for _, etag := range r.Header.Values(net.HeaderIfNoneMatch) { + if md.Etag == etag { + // When the condition fails for GET and HEAD methods, then the server must return + // HTTP status code 304 (Not Modified). [...] Note that the server generating a + // 304 response MUST generate any of the following header fields that would have + // been sent in a 200 (OK) response to the same request: + // Cache-Control, Content-Location, Date, ETag, Expires, and Vary. + notModified = true + return false + } + } + return true + }) + if err != nil { + handleError(w, &sublog, err, "download") return } - - // check etag, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match - for _, etag := range r.Header.Values(net.HeaderIfNoneMatch) { - if md.Etag == etag { - // When the condition fails for GET and HEAD methods, then the server must return - // HTTP status code 304 (Not Modified). [...] Note that the server generating a - // 304 response MUST generate any of the following header fields that would have - // been sent in a 200 (OK) response to the same request: - // Cache-Control, Content-Location, Date, ETag, Expires, and Vary. - w.Header().Set(net.HeaderETag, md.Etag) - w.WriteHeader(http.StatusNotModified) - return - } + if content != nil { + defer content.Close() + } + if notModified { + w.Header().Set(net.HeaderETag, md.Etag) + w.WriteHeader(http.StatusNotModified) + return } // fill in storage provider id if it is missing @@ -141,14 +162,6 @@ func GetOrHeadFile(w http.ResponseWriter, r *http.Request, fs storage.FS, spaceI } } - ctx = ContextWithEtag(ctx, md.Etag) - content, err := fs.Download(ctx, ref) - if err != nil { - handleError(w, &sublog, err, "download") - return - } - defer content.Close() - code := http.StatusOK sendSize := int64(md.Size) var sendContent io.Reader = content @@ -259,6 +272,9 @@ func handleError(w http.ResponseWriter, log *zerolog.Logger, err error, action s case errtypes.IsPermissionDenied: log.Debug().Err(err).Str("action", action).Msg("permission denied") w.WriteHeader(http.StatusForbidden) + case errtypes.Aborted: + log.Debug().Err(err).Str("action", action).Msg("etags do not match") + w.WriteHeader(http.StatusPreconditionFailed) default: log.Error().Err(err).Str("action", action).Msg("unexpected error") w.WriteHeader(http.StatusInternalServerError) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/cephfs/cephfs.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/cephfs/cephfs.go index 84b43aafe..08180f522 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/cephfs/cephfs.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/cephfs/cephfs.go @@ -33,13 +33,14 @@ import ( cephfs2 "github.com/ceph/go-ceph/cephfs" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" + "github.com/cs3org/reva/v2/pkg/appctx" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/storage" "github.com/cs3org/reva/v2/pkg/storage/fs/registry" - "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" ) const ( @@ -276,11 +277,11 @@ func (fs *cephfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKey return files, getRevaError(err) } -func (fs *cephfs) Download(ctx context.Context, ref *provider.Reference) (rc io.ReadCloser, err error) { +func (fs *cephfs) Download(ctx context.Context, ref *provider.Reference, openReaderFunc func(md *provider.ResourceInfo) bool) (ri *provider.ResourceInfo, rc io.ReadCloser, err error) { var path string user := fs.makeUser(ctx) if path, err = user.resolveRef(ref); err != nil { - return nil, errors.Wrap(err, "cephfs: error resolving ref") + return nil, nil, errors.Wrap(err, "cephfs: error resolving ref") } user.op(func(cv *cacheVal) { @@ -288,10 +289,24 @@ func (fs *cephfs) Download(ctx context.Context, ref *provider.Reference) (rc io. err = errtypes.PermissionDenied("cephfs: cannot download under the virtual share folder") return } + + var stat Statx + if stat, err = cv.mount.Statx(path, cephfs2.StatxBasicStats, 0); err != nil { + return + } + ri, err = user.fileAsResourceInfo(cv, path, stat, nil) + if err != nil { + return + } + + if !openReaderFunc(ri) { + return + } + rc, err = cv.mount.Open(path, os.O_RDONLY, 0) }) - return rc, getRevaError(err) + return ri, rc, getRevaError(err) } func (fs *cephfs) ListRevisions(ctx context.Context, ref *provider.Reference) (fvs []*provider.FileVersion, err error) { @@ -341,7 +356,7 @@ func (fs *cephfs) ListRevisions(ctx context.Context, ref *provider.Reference) (f return fvs, getRevaError(err) } -func (fs *cephfs) DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (file io.ReadCloser, err error) { +func (fs *cephfs) DownloadRevision(ctx context.Context, ref *provider.Reference, key string, openReaderFunc func(md *provider.ResourceInfo) bool) (ri *provider.ResourceInfo, file io.ReadCloser, err error) { //TODO(tmourati): Fix entry id logic user := fs.makeUser(ctx) @@ -352,10 +367,22 @@ func (fs *cephfs) DownloadRevision(ctx context.Context, ref *provider.Reference, return } + var stat Statx + stat, err = cv.mount.Statx(revPath, cephfs2.StatxMtime|cephfs2.StatxSize, 0) + if err != nil { + return + } + ri, err = user.fileAsResourceInfo(cv, revPath, stat, nil) + if err != nil { + return + } + if !openReaderFunc(ri) { + return + } file, err = cv.mount.Open(revPath, os.O_RDONLY, 0) }) - return file, getRevaError(err) + return ri, file, getRevaError(err) } func (fs *cephfs) RestoreRevision(ctx context.Context, ref *provider.Reference, key string) (err error) { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/hello/hello.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/hello/hello.go index 0a9d72e2d..f87062feb 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/hello/hello.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/hello/hello.go @@ -29,6 +29,7 @@ import ( "time" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/storage" @@ -222,20 +223,23 @@ func (fs *hellofs) ListFolder(ctx context.Context, ref *provider.Reference, mdKe } // Download returns a ReadCloser for the content of the referenced resource -func (fs *hellofs) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { +func (fs *hellofs) Download(ctx context.Context, ref *provider.Reference, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { info, err := fs.lookup(ctx, ref) if err != nil { - return nil, err + return nil, nil, err } - if info.Type != provider.ResourceType_RESOURCE_TYPE_FILE { - return nil, errtypes.InternalError("expected a file") + return nil, nil, errtypes.InternalError("expected a file") } if info.GetId().GetOpaqueId() != fileid { - return nil, errtypes.InternalError("unknown file") + return nil, nil, errtypes.InternalError("unknown file") + } + + if !openReaderFunc(info) { + return info, nil, nil } b := &bytes.Buffer{} b.WriteString(content) - return io.NopCloser(b), nil + return info, io.NopCloser(b), nil } diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/hello/unimplemented.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/hello/unimplemented.go index 3490b8108..24079d290 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/hello/unimplemented.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/hello/unimplemented.go @@ -24,6 +24,7 @@ import ( "net/url" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/storage" ) @@ -153,8 +154,8 @@ func (fs *hellofs) ListRevisions(ctx context.Context, ref *provider.Reference) ( } // DownloadRevision downloads a revision -func (fs *hellofs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string) (io.ReadCloser, error) { - return nil, errtypes.NotSupported("unimplemented") +func (fs *hellofs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { + return nil, nil, errtypes.NotSupported("unimplemented") } // RestoreRevision restores a revision diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud/nextcloud.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud/nextcloud.go index 86146b24f..efad4295e 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud/nextcloud.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud/nextcloud.go @@ -25,18 +25,21 @@ import ( "io" "net/http" "net/url" + "path" "strings" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" + "github.com/cs3org/reva/v2/pkg/appctx" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/storage" "github.com/cs3org/reva/v2/pkg/storage/fs/registry" - "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" ) func init() { @@ -413,8 +416,17 @@ func (nc *StorageDriver) Upload(ctx context.Context, req storage.UploadRequest, } // Download as defined in the storage.FS interface -func (nc *StorageDriver) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { - return nc.doDownload(ctx, ref.Path) +func (nc *StorageDriver) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { + md, err := nc.GetMD(ctx, ref, []string{}, nil) + if err != nil { + return nil, nil, err + } + if !openReaderfunc(md) { + return md, nil, nil + } + + reader, err := nc.doDownload(ctx, ref.Path) + return md, reader, err } // ListRevisions as defined in the storage.FS interface @@ -441,12 +453,54 @@ func (nc *StorageDriver) ListRevisions(ctx context.Context, ref *provider.Refere } // DownloadRevision as defined in the storage.FS interface -func (nc *StorageDriver) DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (io.ReadCloser, error) { +func (nc *StorageDriver) DownloadRevision(ctx context.Context, ref *provider.Reference, key string, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { log := appctx.GetLogger(ctx) log.Info().Msgf("DownloadRevision %s %s", ref.Path, key) + md, err := nc.GetMD(ctx, ref, []string{}, nil) + if err != nil { + return nil, nil, err + } + revs, err := nc.ListRevisions(ctx, ref) + if err != nil { + return nil, nil, err + } + var ri *provider.ResourceInfo + for _, rev := range revs { + if rev.Key == key { + + ri = &provider.ResourceInfo{ + // TODO(jfd) we cannot access version content, this will be a problem when trying to fetch version thumbnails + // Opaque + Type: provider.ResourceType_RESOURCE_TYPE_FILE, + Id: &provider.ResourceId{ + StorageId: "versions", + OpaqueId: md.Id.OpaqueId + "@" + rev.GetKey(), + }, + // Checksum + Etag: rev.Etag, + // MimeType + Mtime: &types.Timestamp{ + Seconds: rev.Mtime, + // TODO cs3apis FileVersion should use types.Timestamp instead of uint64 + }, + Path: path.Join("v", rev.Key), + // PermissionSet + Size: rev.Size, + Owner: md.Owner, + } + } + } + if ri == nil { + return nil, nil, errtypes.NotFound("revision not found") + } + + if !openReaderfunc(ri) { + return ri, nil, nil + } + readCloser, err := nc.doDownloadRevision(ctx, ref.Path, key) - return readCloser, err + return ri, readCloser, err } // RestoreRevision as defined in the storage.FS interface diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud/nextcloud_server_mock.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud/nextcloud_server_mock.go index 97507df71..5bfdfe8b5 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud/nextcloud_server_mock.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/nextcloud/nextcloud_server_mock.go @@ -164,6 +164,7 @@ var responses = map[string]Response{ `POST /apps/sciencemesh/~tester/api/storage/GetMD {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"mdKeys":[]}`: {200, `{"opaque":{},"type":1,"id":{"opaque_id":"fileid-/some/path"},"checksum":{},"etag":"deadbeef","mime_type":"text/plain","mtime":{"seconds":1234567890},"path":"/versionedFile","permission_set":{},"size":12345,"canonical_metadata":{},"arbitrary_metadata":{"metadata":{"key1":"val1","key2":"val2","key3":"val3"}}}`, serverStateEmpty}, `GET /apps/sciencemesh/~tester/api/storage/Download/some/file/path.txt `: {200, `the contents of the file`, serverStateEmpty}, `POST /apps/sciencemesh/~tester/api/storage/ListRevisions {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"/some/path"}`: {200, `[{"opaque":{"map":{"some":{"value":"ZGF0YQ=="}}},"key":"version-12","size":12345,"mtime":1234567890,"etag":"deadb00f"},{"opaque":{"map":{"different":{"value":"c3R1ZmY="}}},"key":"asdf","size":12345,"mtime":1234567890,"etag":"deadbeef"}]`, serverStateEmpty}, + `POST /apps/sciencemesh/~tester/api/storage/ListRevisions {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}`: {200, `[{"key":"some/revision","size":12345,"mtime":1234567890,"etag":"deadb00f"}]`, serverStateEmpty}, `GET /apps/sciencemesh/~tester/api/storage/DownloadRevision/some%2Frevision/some/file/path.txt `: {200, `the contents of that revision`, serverStateEmpty}, `POST /apps/sciencemesh/~tester/api/storage/RestoreRevision {"ref":{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"},"key":"asdf"}`: {200, ``, serverStateEmpty}, `POST /apps/sciencemesh/~tester/api/storage/ListRecycle {"key":"asdf","path":"/some/file.txt"}`: {200, `[{"opaque":{},"key":"some-deleted-version","ref":{"resource_id":{},"path":"/some/file.txt"},"size":12345,"deletion_time":{"seconds":1234567890}}]`, serverStateEmpty}, diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql.go index 0c51f1c55..5cfaaca8b 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/owncloudsql/owncloudsql.go @@ -39,6 +39,11 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" + "github.com/pkg/xattr" + "github.com/rs/zerolog/log" + "github.com/cs3org/reva/v2/internal/grpc/services/storageprovider" "github.com/cs3org/reva/v2/pkg/appctx" "github.com/cs3org/reva/v2/pkg/conversions" @@ -54,10 +59,6 @@ import ( "github.com/cs3org/reva/v2/pkg/storage/fs/registry" "github.com/cs3org/reva/v2/pkg/storage/utils/chunking" "github.com/cs3org/reva/v2/pkg/storage/utils/templates" - "github.com/mitchellh/mapstructure" - "github.com/pkg/errors" - "github.com/pkg/xattr" - "github.com/rs/zerolog/log" ) const ( @@ -1544,32 +1545,60 @@ func (fs *owncloudsqlfs) archiveRevision(ctx context.Context, vbp string, ip str return err } -func (fs *owncloudsqlfs) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { +func (fs *owncloudsqlfs) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { ip, err := fs.resolve(ctx, ref) if err != nil { - return nil, errors.Wrap(err, "owncloudsql: error resolving reference") + return nil, nil, errors.Wrap(err, "owncloudsql: error resolving reference") + } + p := fs.toStoragePath(ctx, ip) + + // If Download is called for a path shared with the user then the path is + // already wrapped. (fs.resolve wraps the path) + if strings.HasPrefix(p, fs.c.DataDirectory) { + ip = p } // check permissions if perm, err := fs.readPermissions(ctx, ip); err == nil { if !perm.InitiateFileDownload { - return nil, errtypes.PermissionDenied("") + return nil, nil, errtypes.PermissionDenied("") } } else { if isNotFound(err) { - return nil, errtypes.NotFound(fs.toStoragePath(ctx, filepath.Dir(ip))) + return nil, nil, errtypes.NotFound(fs.toStoragePath(ctx, filepath.Dir(ip))) } - return nil, errors.Wrap(err, "owncloudsql: error reading permissions") + return nil, nil, errors.Wrap(err, "owncloudsql: error reading permissions") + } + + ownerStorageID, err := fs.filecache.GetNumericStorageID(ctx, "home::"+fs.getOwner(ip)) + if err != nil { + return nil, nil, err + } + entry, err := fs.filecache.Get(ctx, ownerStorageID, fs.toDatabasePath(ip)) + switch { + case err == sql.ErrNoRows: + return nil, nil, errtypes.NotFound(fs.toStoragePath(ctx, filepath.Dir(ip))) + case err != nil: + return nil, nil, err + } + + md, err := fs.convertToResourceInfo(ctx, entry, ip, nil) + if err != nil { + return nil, nil, err + } + + if !openReaderfunc(md) { + return md, nil, nil } r, err := os.Open(ip) if err != nil { if os.IsNotExist(err) { - return nil, errtypes.NotFound(fs.toStoragePath(ctx, ip)) + return nil, nil, errtypes.NotFound(fs.toStoragePath(ctx, ip)) } - return nil, errors.Wrap(err, "owncloudsql: error reading "+ip) + return nil, nil, errors.Wrap(err, "owncloudsql: error reading "+ip) } - return r, nil + return md, r, nil } func (fs *owncloudsqlfs) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) { @@ -1623,8 +1652,8 @@ func (fs *owncloudsqlfs) ListRevisions(ctx context.Context, ref *provider.Refere return revisions, nil } -func (fs *owncloudsqlfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string) (io.ReadCloser, error) { - return nil, errtypes.NotSupported("download revision") +func (fs *owncloudsqlfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { + return nil, nil, errtypes.NotSupported("download revision") } func (fs *owncloudsqlfs) RestoreRevision(ctx context.Context, ref *provider.Reference, revisionKey string) error { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/s3/s3.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/s3/s3.go index 3cb8380df..7717b716c 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/s3/s3.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/fs/s3/s3.go @@ -616,12 +616,21 @@ func (fs *s3FS) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys, return finfos, nil } -func (fs *s3FS) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { +func (fs *s3FS) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { log := appctx.GetLogger(ctx) fn, err := fs.resolve(ctx, ref) if err != nil { - return nil, errors.Wrap(err, "error resolving ref") + return nil, nil, errors.Wrap(err, "error resolving ref") + } + + ri, err := fs.GetMD(ctx, ref, nil, []string{"size", "mimetype", "etag"}) + if err != nil { + return nil, nil, errors.Wrap(err, "error getting metadata") + } + + if !openReaderfunc(ri) { + return ri, nil, nil } // use GetObject instead of s3manager.Downloader: @@ -637,20 +646,20 @@ func (fs *s3FS) Download(ctx context.Context, ref *provider.Reference) (io.ReadC switch aerr.Code() { case s3.ErrCodeNoSuchBucket: case s3.ErrCodeNoSuchKey: - return nil, errtypes.NotFound(fn) + return nil, nil, errtypes.NotFound(fn) } } - return nil, errors.Wrap(err, "s3fs: error deleting "+fn) + return nil, nil, errors.Wrap(err, "s3fs: error deleting "+fn) } - return r.Body, nil + return ri, r.Body, nil } func (fs *s3FS) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) { return nil, errtypes.NotSupported("list revisions") } -func (fs *s3FS) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string) (io.ReadCloser, error) { - return nil, errtypes.NotSupported("download revision") +func (fs *s3FS) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { + return nil, nil, errtypes.NotSupported("download revision") } func (fs *s3FS) RestoreRevision(ctx context.Context, ref *provider.Reference, revisionKey string) error { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/storage.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/storage.go index 11cab0c5e..e89c804b2 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/storage.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/storage.go @@ -23,10 +23,9 @@ import ( "io" "net/url" - tusd "github.com/tus/tusd/v2/pkg/handler" - provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1" + tusd "github.com/tus/tusd/v2/pkg/handler" ) // FS is the interface to implement access to the storage. @@ -48,7 +47,7 @@ type FS interface { // ListFolder returns the resource infos for all children of the referenced resource ListFolder(ctx context.Context, ref *provider.Reference, mdKeys, fieldMask []string) ([]*provider.ResourceInfo, error) // Download returns a ReadCloser for the content of the referenced resource - Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) + Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) // GetPathByID returns the path for the given resource id relative to the space root // It should only reveal the path visible to the current user to not leak the names uf unshared parent resources @@ -80,7 +79,7 @@ type FS interface { // ListRevisions lists all revisions for the referenced resource ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) // DownloadRevision downloads a revision - DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (io.ReadCloser, error) + DownloadRevision(ctx context.Context, ref *provider.Reference, key string, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) // RestoreRevision restores a revision RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go index 22ccb4ba7..8b9564088 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/decomposedfs.go @@ -33,13 +33,20 @@ import ( user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/jellydator/ttlcache/v2" + "github.com/pkg/errors" + tusd "github.com/tus/tusd/v2/pkg/handler" + microstore "go-micro.dev/v4/store" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" + "golang.org/x/sync/errgroup" + ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/events" "github.com/cs3org/reva/v2/pkg/logger" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/rhttp/datatx/metrics" - "github.com/cs3org/reva/v2/pkg/rhttp/datatx/utils/download" "github.com/cs3org/reva/v2/pkg/storage" "github.com/cs3org/reva/v2/pkg/storage/utils/chunking" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects" @@ -60,13 +67,6 @@ import ( "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/store" "github.com/cs3org/reva/v2/pkg/utils" - "github.com/jellydator/ttlcache/v2" - "github.com/pkg/errors" - tusd "github.com/tus/tusd/v2/pkg/handler" - microstore "go-micro.dev/v4/store" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/trace" - "golang.org/x/sync/errgroup" ) type CtxKey int @@ -1054,54 +1054,49 @@ func (fs *Decomposedfs) Delete(ctx context.Context, ref *provider.Reference) (er } // Download returns a reader to the specified resource -func (fs *Decomposedfs) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { +func (fs *Decomposedfs) Download(ctx context.Context, ref *provider.Reference, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { ctx, span := tracer.Start(ctx, "Download") defer span.End() // check if we are trying to download a revision // TODO the CS3 api should allow initiating a revision download if ref.ResourceId != nil && strings.Contains(ref.ResourceId.OpaqueId, node.RevisionIDDelimiter) { - return fs.DownloadRevision(ctx, ref, ref.ResourceId.OpaqueId) + return fs.DownloadRevision(ctx, ref, ref.ResourceId.OpaqueId, openReaderFunc) } n, err := fs.lu.NodeFromResource(ctx, ref) if err != nil { - return nil, errors.Wrap(err, "Decomposedfs: error resolving ref") + return nil, nil, err } if !n.Exists { err = errtypes.NotFound(filepath.Join(n.ParentID, n.Name)) - return nil, err + return nil, nil, err } rp, err := fs.p.AssemblePermissions(ctx, n) switch { case err != nil: - return nil, err + return nil, nil, err case !rp.InitiateFileDownload: f, _ := storagespace.FormatReference(ref) if rp.Stat { - return nil, errtypes.PermissionDenied(f) + return nil, nil, errtypes.PermissionDenied(f) } - return nil, errtypes.NotFound(f) + return nil, nil, errtypes.NotFound(f) } - mtime, err := n.GetMTime(ctx) + ri, err := n.AsResourceInfo(ctx, rp, nil, []string{"size", "mimetype", "etag"}, true) if err != nil { - return nil, errors.Wrap(err, "Decomposedfs: error getting mtime for '"+n.ID+"'") + return nil, nil, err } - currentEtag, err := node.CalculateEtag(n.ID, mtime) - if err != nil { - return nil, errors.Wrap(err, "Decomposedfs: error calculating etag for '"+n.ID+"'") + var reader io.ReadCloser + if openReaderFunc(ri) { + reader, err = fs.tp.ReadBlob(n) + if err != nil { + return nil, nil, errors.Wrap(err, "Decomposedfs: error download blob '"+n.ID+"'") + } } - expectedEtag := download.EtagFromContext(ctx) - if currentEtag != expectedEtag { - return nil, errtypes.Aborted(fmt.Sprintf("file changed from etag %s to %s", expectedEtag, currentEtag)) - } - reader, err := fs.tp.ReadBlob(n) - if err != nil { - return nil, errors.Wrap(err, "Decomposedfs: error download blob '"+n.ID+"'") - } - return reader, nil + return ri, reader, nil } // GetLock returns an existing lock on the given reference diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/revisions.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/revisions.go index 771d60878..db7d730aa 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/revisions.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/revisions.go @@ -27,13 +27,15 @@ import ( "time" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/pkg/errors" + "github.com/rogpeppe/go-internal/lockedfile" + "github.com/cs3org/reva/v2/pkg/appctx" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" "github.com/cs3org/reva/v2/pkg/storagespace" - "github.com/pkg/errors" - "github.com/rogpeppe/go-internal/lockedfile" + "github.com/cs3org/reva/v2/pkg/utils" ) // Revision entries are stored inside the node folder and start with the same uuid as the current version. @@ -112,14 +114,14 @@ func (fs *Decomposedfs) ListRevisions(ctx context.Context, ref *provider.Referen // DownloadRevision returns a reader for the specified revision // FIXME the CS3 api should explicitly allow initiating revision and trash download, a related issue is https://github.com/cs3org/reva/issues/1813 -func (fs *Decomposedfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string) (io.ReadCloser, error) { +func (fs *Decomposedfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { log := appctx.GetLogger(ctx) // verify revision key format kp := strings.SplitN(revisionKey, node.RevisionIDDelimiter, 2) if len(kp) != 2 { log.Error().Str("revisionKey", revisionKey).Msg("malformed revisionKey") - return nil, errtypes.NotFound(revisionKey) + return nil, nil, errtypes.NotFound(revisionKey) } log.Debug().Str("revisionKey", revisionKey).Msg("DownloadRevision") @@ -127,39 +129,59 @@ func (fs *Decomposedfs) DownloadRevision(ctx context.Context, ref *provider.Refe // check if the node is available and has not been deleted n, err := node.ReadNode(ctx, fs.lu, spaceID, kp[0], false, nil, false) if err != nil { - return nil, err + return nil, nil, err } if !n.Exists { err = errtypes.NotFound(filepath.Join(n.ParentID, n.Name)) - return nil, err + return nil, nil, err } rp, err := fs.p.AssemblePermissions(ctx, n) switch { case err != nil: - return nil, err + return nil, nil, err case !rp.ListFileVersions || !rp.InitiateFileDownload: // TODO add explicit permission in the CS3 api? f, _ := storagespace.FormatReference(ref) if rp.Stat { - return nil, errtypes.PermissionDenied(f) + return nil, nil, errtypes.PermissionDenied(f) } - return nil, errtypes.NotFound(f) + return nil, nil, errtypes.NotFound(f) } contentPath := fs.lu.InternalPath(spaceID, revisionKey) blobid, blobsize, err := fs.lu.ReadBlobIDAndSizeAttr(ctx, contentPath, nil) if err != nil { - return nil, errors.Wrapf(err, "Decomposedfs: could not read blob id and size for revision '%s' of node '%s'", n.ID, revisionKey) + return nil, nil, errors.Wrapf(err, "Decomposedfs: could not read blob id and size for revision '%s' of node '%s'", kp[1], n.ID) } revisionNode := node.Node{SpaceID: spaceID, BlobID: blobid, Blobsize: blobsize} // blobsize is needed for the s3ng blobstore - reader, err := fs.tp.ReadBlob(&revisionNode) + ri, err := n.AsResourceInfo(ctx, rp, nil, []string{"size", "mimetype", "etag"}, true) if err != nil { - return nil, errors.Wrapf(err, "Decomposedfs: could not download blob of revision '%s' for node '%s'", n.ID, revisionKey) + return nil, nil, err } - return reader, nil + + // update resource info with revision data + mtime, err := time.Parse(time.RFC3339Nano, kp[1]) + if err != nil { + return nil, nil, errors.Wrapf(err, "Decomposedfs: could not parse mtime for revision '%s' of node '%s'", kp[1], n.ID) + } + ri.Size = uint64(blobsize) + ri.Mtime = utils.TimeToTS(mtime) + ri.Etag, err = node.CalculateEtag(n.ID, mtime) + if err != nil { + return nil, nil, errors.Wrapf(err, "error calculating etag for revision '%s' of node '%s'", kp[1], n.ID) + } + + var reader io.ReadCloser + if openReaderFunc(ri) { + reader, err = fs.tp.ReadBlob(&revisionNode) + if err != nil { + return nil, nil, errors.Wrapf(err, "Decomposedfs: could not download blob of revision '%s' for node '%s'", n.ID, revisionKey) + } + } + return ri, reader, nil } // RestoreRevision restores the specified revision of the resource diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload.go index eba3c9180..fcd70d4e2 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload.go @@ -228,6 +228,10 @@ func (fs *Decomposedfs) InitiateUpload(ctx context.Context, ref *provider.Refere } } + if session.MTime().IsZero() { + session.SetMetadata("mtime", utils.TimeToOCMtime(time.Now())) + } + log.Debug().Str("uploadid", session.ID()).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Interface("metadata", metadata).Msg("Decomposedfs: resolved filename") _, err = node.CheckQuota(ctx, n.SpaceRoot, n.Exists, uint64(n.Blobsize), uint64(session.Size())) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/eosfs/eosfs.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/eosfs/eosfs.go index 2d11ecdfa..818151005 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/eosfs/eosfs.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/eosfs/eosfs.go @@ -1834,13 +1834,23 @@ func (fs *eosfs) moveShadow(ctx context.Context, oldPath, newPath string) error return fs.c.Rename(ctx, auth, oldfn, newfn) } -func (fs *eosfs) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { +func (fs *eosfs) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { fn, auth, err := fs.resolveRefForbidShareFolder(ctx, ref) if err != nil { - return nil, err + return nil, nil, err } - return fs.c.Read(ctx, auth, fn) + md, err := fs.GetMD(ctx, ref, nil, nil) + if err != nil { + return nil, nil, err + } + + if !openReaderfunc(md) { + return md, nil, nil + } + + reader, err := fs.c.Read(ctx, auth, fn) + return md, reader, err } func (fs *eosfs) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) { @@ -1886,37 +1896,43 @@ func (fs *eosfs) ListRevisions(ctx context.Context, ref *provider.Reference) ([] return revisions, nil } -func (fs *eosfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string) (io.ReadCloser, error) { +func (fs *eosfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { var auth eosclient.Authorization var fn string var err error + md, err := fs.GetMD(ctx, ref, nil, nil) + if err != nil { + return nil, nil, err + } + if !fs.conf.EnableHome && fs.conf.ImpersonateOwnerforRevisions { // We need to access the revisions for a non-home reference. // We'll get the owner of the particular resource and impersonate them // if we have access to it. - md, err := fs.GetMD(ctx, ref, nil, nil) - if err != nil { - return nil, err - } - fn = fs.wrap(ctx, md.Path) + fn = fs.wrap(ctx, md.Path) if md.PermissionSet.InitiateFileDownload { auth, err = fs.getUIDGateway(ctx, md.Owner) if err != nil { - return nil, err + return nil, nil, err } } else { - return nil, errtypes.PermissionDenied("eosfs: user doesn't have permissions to download revisions") + return nil, nil, errtypes.PermissionDenied("eosfs: user doesn't have permissions to download revisions") } } else { fn, auth, err = fs.resolveRefForbidShareFolder(ctx, ref) if err != nil { - return nil, err + return nil, nil, err } } - return fs.c.ReadVersion(ctx, auth, fn, revisionKey) + if !openReaderfunc(md) { + return md, nil, nil + } + + reader, err := fs.c.ReadVersion(ctx, auth, fn, revisionKey) + return md, reader, err } func (fs *eosfs) RestoreRevision(ctx context.Context, ref *provider.Reference, revisionKey string) error { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/localfs/localfs.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/localfs/localfs.go index a37c084f1..15fc49105 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/localfs/localfs.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/localfs/localfs.go @@ -1045,25 +1045,42 @@ func (fs *localfs) listShareFolderRoot(ctx context.Context, home string, mdKeys return finfos, nil } -func (fs *localfs) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { +func (fs *localfs) Download(ctx context.Context, ref *provider.Reference, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { fn, err := fs.resolve(ctx, ref) if err != nil { - return nil, errors.Wrap(err, "localfs: error resolving ref") + return nil, nil, errors.Wrap(err, "localfs: error resolving ref") } if fs.isShareFolder(ctx, fn) { - return nil, errtypes.PermissionDenied("localfs: cannot download under the virtual share folder") + return nil, nil, errtypes.PermissionDenied("localfs: cannot download under the virtual share folder") } fn = fs.wrap(ctx, fn) + md, err := os.Stat(fn) + if err != nil { + if os.IsNotExist(err) { + return nil, nil, errtypes.NotFound(fn) + } + return nil, nil, errors.Wrap(err, "localfs: error stating "+fn) + } + + ri, err := fs.normalize(ctx, md, fn, []string{"size", "mimetype", "etag"}) + if err != nil { + return nil, nil, err + } + + if !openReaderfunc(ri) { + return ri, nil, nil + } + r, err := os.Open(fn) if err != nil { if os.IsNotExist(err) { - return nil, errtypes.NotFound(fn) + return nil, nil, errtypes.NotFound(fn) } - return nil, errors.Wrap(err, "localfs: error reading "+fn) + return nil, nil, errors.Wrap(err, "localfs: error reading "+fn) } - return r, nil + return ri, r, nil } func (fs *localfs) archiveRevision(ctx context.Context, np string) error { @@ -1117,28 +1134,42 @@ func (fs *localfs) ListRevisions(ctx context.Context, ref *provider.Reference) ( return revisions, nil } -func (fs *localfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string) (io.ReadCloser, error) { +func (fs *localfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string, openReaderfunc func(*provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { np, err := fs.resolve(ctx, ref) if err != nil { - return nil, errors.Wrap(err, "localfs: error resolving ref") + return nil, nil, errors.Wrap(err, "localfs: error resolving ref") } if fs.isShareFolder(ctx, np) { - return nil, errtypes.PermissionDenied("localfs: cannot download revisions under the virtual share folder") + return nil, nil, errtypes.PermissionDenied("localfs: cannot download revisions under the virtual share folder") } versionsDir := fs.wrapVersions(ctx, np) vp := path.Join(versionsDir, revisionKey) - r, err := os.Open(vp) + md, err := os.Stat(vp) if err != nil { if os.IsNotExist(err) { - return nil, errtypes.NotFound(vp) + return nil, nil, errtypes.NotFound(vp) } - return nil, errors.Wrap(err, "localfs: error reading "+vp) + return nil, nil, errors.Wrap(err, "localfs: error stating "+vp) } - return r, nil + ri, err := fs.normalize(ctx, md, vp, []string{"size", "mimetype", "etag"}) + if err != nil { + return nil, nil, err + } + + if !openReaderfunc(ri) { + return ri, nil, nil + } + + r, err := os.Open(vp) + if err != nil { + return nil, nil, errors.Wrap(err, "localfs: error reading "+vp) + } + + return ri, r, nil } func (fs *localfs) RestoreRevision(ctx context.Context, ref *provider.Reference, revisionKey string) error { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/metadata/cs3.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/metadata/cs3.go index 0c2656c9c..aa7150244 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/metadata/cs3.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/metadata/cs3.go @@ -34,14 +34,15 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/metadata" + "github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/net" ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/errtypes" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/utils" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/trace" - "google.golang.org/grpc/metadata" ) var tracer trace.Tracer @@ -212,7 +213,7 @@ func (cs3 *CS3) Upload(ctx context.Context, req UploadRequest) (*UploadResponse, } if req.MTime != (time.Time{}) { // The format of the X-OC-Mtime header is ., e.g. '1691053416.934129485' - ifuReq.Opaque = utils.AppendPlainToOpaque(ifuReq.Opaque, "X-OC-Mtime", strconv.Itoa(int(req.MTime.Unix()))+"."+strconv.Itoa(req.MTime.Nanosecond())) + ifuReq.Opaque = utils.AppendPlainToOpaque(ifuReq.Opaque, "X-OC-Mtime", utils.TimeToOCMtime(req.MTime)) } ifuReq.Opaque = utils.AppendPlainToOpaque(ifuReq.Opaque, net.HeaderUploadLength, strconv.FormatInt(int64(len(req.Content)), 10)) diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/middleware/middleware.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/middleware/middleware.go index b90cb2a08..c210aaece 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/middleware/middleware.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/middleware/middleware.go @@ -23,9 +23,9 @@ import ( "io" "net/url" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" tusd "github.com/tus/tusd/v2/pkg/handler" - provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/storage" "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload" "github.com/cs3org/reva/v2/pkg/storagespace" @@ -361,7 +361,7 @@ func (f *FS) Upload(ctx context.Context, req storage.UploadRequest, uploadFunc s return res0, res1 } -func (f *FS) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { +func (f *FS) Download(ctx context.Context, ref *provider.Reference, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { var ( err error unhook UnHook @@ -370,22 +370,22 @@ func (f *FS) Download(ctx context.Context, ref *provider.Reference) (io.ReadClos for _, hook := range f.hooks { ctx, unhook, err = hook("Download", ctx, ref.GetResourceId().GetSpaceId()) if err != nil { - return nil, err + return nil, nil, err } if unhook != nil { unhooks = append(unhooks, unhook) } } - res0, res1 := f.next.Download(ctx, ref) + res0, res1, res2 := f.next.Download(ctx, ref, openReaderFunc) for _, unhook := range unhooks { if err := unhook(); err != nil { - return nil, err + return nil, nil, err } } - return res0, res1 + return res0, res1, res2 } func (f *FS) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error) { @@ -415,7 +415,7 @@ func (f *FS) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*pro return res0, res1 } -func (f *FS) DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (io.ReadCloser, error) { +func (f *FS) DownloadRevision(ctx context.Context, ref *provider.Reference, key string, openReaderFunc func(md *provider.ResourceInfo) bool) (*provider.ResourceInfo, io.ReadCloser, error) { var ( err error unhook UnHook @@ -424,22 +424,22 @@ func (f *FS) DownloadRevision(ctx context.Context, ref *provider.Reference, key for _, hook := range f.hooks { ctx, unhook, err = hook("DownloadRevision", ctx, ref.GetResourceId().GetSpaceId()) if err != nil { - return nil, err + return nil, nil, err } if unhook != nil { unhooks = append(unhooks, unhook) } } - res0, res1 := f.next.DownloadRevision(ctx, ref, key) + res0, res1, res2 := f.next.DownloadRevision(ctx, ref, key, openReaderFunc) for _, unhook := range unhooks { if err := unhook(); err != nil { - return nil, err + return nil, nil, err } } - return res0, res1 + return res0, res1, res2 } func (f *FS) RestoreRevision(ctx context.Context, ref *provider.Reference, key string) error { diff --git a/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go b/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go index 77e1c9ef0..c10313687 100644 --- a/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go +++ b/vendor/github.com/cs3org/reva/v2/pkg/utils/utils.go @@ -201,6 +201,11 @@ func MTimeToTime(v string) (t time.Time, err error) { return time.Unix(sec, nsec), err } +// TimeToOCMtime converts a Go time.Time to a string in the form "." +func TimeToOCMtime(t time.Time) string { + return strconv.FormatInt(t.Unix(), 10) + "." + strconv.FormatInt(int64(t.Nanosecond()), 10) +} + // ExtractGranteeID returns the ID, user or group, set in the GranteeId object func ExtractGranteeID(grantee *provider.Grantee) (*userpb.UserId, *grouppb.GroupId) { switch t := grantee.Id.(type) { diff --git a/vendor/modules.txt b/vendor/modules.txt index d14e7b085..ff4010fe2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -367,7 +367,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1 github.com/cs3org/go-cs3apis/cs3/types/v1beta1 -# github.com/cs3org/reva/v2 v2.26.5-0.20241111162950-e77dd61e7edb +# github.com/cs3org/reva/v2 v2.26.5 ## explicit; go 1.22.0 github.com/cs3org/reva/v2/cmd/revad/internal/grace github.com/cs3org/reva/v2/cmd/revad/runtime