From e5dc20675722525bc9ff8cb9cb58cee9b67c53b4 Mon Sep 17 00:00:00 2001 From: Willy Kloucek Date: Mon, 13 Dec 2021 12:17:17 +0100 Subject: [PATCH 1/5] implement cs3org/cs3apis#154 --- go.mod | 2 +- go.sum | 4 +++ internal/grpc/interceptors/auth/scope.go | 2 ++ .../grpc/interceptors/readonly/readonly.go | 6 +++- .../grpc/services/gateway/storageprovider.go | 19 ++++++++++++ .../publicstorageprovider.go | 13 ++++++++ .../storageprovider/storageprovider.go | 30 +++++++++++++++++++ pkg/auth/scope/publicshare.go | 2 ++ pkg/auth/scope/resourceinfo.go | 2 ++ pkg/auth/scope/share.go | 2 ++ pkg/storage/fs/nextcloud/nextcloud.go | 5 ++++ pkg/storage/fs/owncloud/owncloud.go | 4 +++ pkg/storage/fs/owncloudsql/owncloudsql.go | 4 +++ pkg/storage/fs/s3/s3.go | 4 +++ pkg/storage/storage.go | 1 + .../utils/decomposedfs/decomposedfs.go | 5 ++++ pkg/storage/utils/eosfs/eosfs.go | 22 ++++++++++++++ pkg/storage/utils/localfs/localfs.go | 4 +++ 18 files changed, 129 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 167002151e..eec435d854 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/cheggaaa/pb v1.0.29 github.com/coreos/go-oidc v2.2.1+incompatible github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e - github.com/cs3org/go-cs3apis v0.0.0-20211104090126-8e972dca8304 + github.com/cs3org/go-cs3apis v0.0.0-20211213090556-12c0d565f51d github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59 github.com/gdexlab/go-render v1.0.1 diff --git a/go.sum b/go.sum index dd5a0265b6..9ce85e88a0 100644 --- a/go.sum +++ b/go.sum @@ -111,8 +111,12 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJffz4pz0o1WuQxJ28+5x5JgaHD8= github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= +github.com/cs3org/go-cs3apis v0.0.0-20210325133324-32b03d75a535 h1:555D8A3ddKqb4OyK9v5mdphw2zDLWKGXOkcnf1RQwTA= +github.com/cs3org/go-cs3apis v0.0.0-20210325133324-32b03d75a535/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cs3org/go-cs3apis v0.0.0-20211104090126-8e972dca8304 h1:e/nIPR518vyvrulo9goAZTtYD6gFfu/2/9MDe6mTGcw= github.com/cs3org/go-cs3apis v0.0.0-20211104090126-8e972dca8304/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20211213090556-12c0d565f51d h1:gnb2ciU4N+RwUug/nwe54wenWi7vSp5bAAjXINlgHZ8= +github.com/cs3org/go-cs3apis v0.0.0-20211213090556-12c0d565f51d/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8 h1:Z9lwXumT5ACSmJ7WGnFl+OMLLjpz5uR2fyz7dC255FI= github.com/cubewise-code/go-mime v0.0.0-20200519001935-8c5762b177d8/go.mod h1:4abs/jPXcmJzYoYGF91JF9Uq9s/KL5n1jvFDix8KcqY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/grpc/interceptors/auth/scope.go b/internal/grpc/interceptors/auth/scope.go index 8c9a907ce3..8bad148478 100644 --- a/internal/grpc/interceptors/auth/scope.go +++ b/internal/grpc/interceptors/auth/scope.go @@ -241,6 +241,8 @@ func extractRef(req interface{}, hasEditorRole bool) (*provider.Reference, bool) // Write Requests case *provider.CreateContainerRequest: return v.GetRef(), true + case *provider.TouchFileRequest: + return v.GetRef(), true case *provider.DeleteRequest: return v.GetRef(), true case *provider.MoveRequest: diff --git a/internal/grpc/interceptors/readonly/readonly.go b/internal/grpc/interceptors/readonly/readonly.go index f82a7ab46b..566d5f369f 100644 --- a/internal/grpc/interceptors/readonly/readonly.go +++ b/internal/grpc/interceptors/readonly/readonly.go @@ -99,7 +99,11 @@ func NewUnary(map[string]interface{}) (grpc.UnaryServerInterceptor, int, error) }, nil case *provider.CreateContainerRequest: return &provider.CreateContainerResponse{ - Status: rstatus.NewPermissionDenied(ctx, nil, "permission denied: tried to create resoure on readonly storage"), + Status: rstatus.NewPermissionDenied(ctx, nil, "permission denied: tried to create ressource on readonly storage"), + }, nil + case *provider.TouchFileRequest: + return &provider.TouchFileResponse{ + Status: rstatus.NewPermissionDenied(ctx, nil, "permission denied: tried to create ressource on readonly storage"), }, nil case *provider.CreateHomeRequest: return &provider.CreateHomeResponse{ diff --git a/internal/grpc/services/gateway/storageprovider.go b/internal/grpc/services/gateway/storageprovider.go index 66599a324e..cdd622e9c7 100644 --- a/internal/grpc/services/gateway/storageprovider.go +++ b/internal/grpc/services/gateway/storageprovider.go @@ -851,6 +851,25 @@ func (s *svc) createContainer(ctx context.Context, req *provider.CreateContainer return res, nil } +func (s *svc) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) { + c, err := s.find(ctx, req.Ref) + if err != nil { + return &provider.TouchFileResponse{ + Status: status.NewStatusFromErrType(ctx, "TouchFile ref="+req.Ref.String(), err), + }, nil + } + + res, err := c.TouchFile(ctx, req) + if err != nil { + if gstatus.Code(err) == codes.PermissionDenied { + return &provider.TouchFileResponse{Status: &rpc.Status{Code: rpc.Code_CODE_PERMISSION_DENIED}}, nil + } + return nil, errors.Wrap(err, "gateway: error calling TouchFile") + } + + return res, nil +} + // check if the path contains the prefix of the shared folder func (s *svc) inSharedFolder(ctx context.Context, p string) bool { sharedFolder := s.getSharedFolder(ctx) diff --git a/internal/grpc/services/publicstorageprovider/publicstorageprovider.go b/internal/grpc/services/publicstorageprovider/publicstorageprovider.go index c6ec4e983e..80b64f4e99 100644 --- a/internal/grpc/services/publicstorageprovider/publicstorageprovider.go +++ b/internal/grpc/services/publicstorageprovider/publicstorageprovider.go @@ -363,6 +363,19 @@ func (s *service) CreateContainer(ctx context.Context, req *provider.CreateConta return res, nil } +func (s *service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) { + ref, _, _, st, err := s.translatePublicRefToCS3Ref(ctx, req.Ref) + switch { + case err != nil: + return nil, err + case st != nil: + return &provider.TouchFileResponse{ + Status: st, + }, nil + } + return s.gateway.TouchFile(ctx, &provider.TouchFileRequest{Opaque: req.Opaque, Ref: ref}) +} + func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) { ctx, span := rtrace.Provider.Tracer("publicstorageprovider").Start(ctx, "Delete") defer span.End() diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index aac7f68453..a0d659005a 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -544,6 +544,36 @@ func (s *service) CreateContainer(ctx context.Context, req *provider.CreateConta return res, nil } +func (s *service) TouchFile(ctx context.Context, req *provider.TouchFileRequest) (*provider.TouchFileResponse, error) { + newRef, err := s.unwrap(ctx, req.Ref) + if err != nil { + return &provider.TouchFileResponse{ + Status: status.NewInternal(ctx, err, "error unwrapping path"), + }, nil + } + if err := s.storage.TouchFile(ctx, newRef); err != nil { + var st *rpc.Status + switch err.(type) { + case errtypes.IsNotFound: + st = status.NewNotFound(ctx, "path not found when touching the file") + case errtypes.AlreadyExists: + st = status.NewAlreadyExists(ctx, err, "file already exists") + case errtypes.PermissionDenied: + st = status.NewPermissionDenied(ctx, err, "permission denied") + default: + st = status.NewInternal(ctx, err, "error touching file: "+req.Ref.String()) + } + return &provider.TouchFileResponse{ + Status: st, + }, nil + } + + res := &provider.TouchFileResponse{ + Status: status.NewOK(ctx), + } + return res, nil +} + func (s *service) Delete(ctx context.Context, req *provider.DeleteRequest) (*provider.DeleteResponse, error) { newRef, err := s.unwrap(ctx, req.Ref) if err != nil { diff --git a/pkg/auth/scope/publicshare.go b/pkg/auth/scope/publicshare.go index b7f7d078b6..7de007ca87 100644 --- a/pkg/auth/scope/publicshare.go +++ b/pkg/auth/scope/publicshare.go @@ -57,6 +57,8 @@ func publicshareScope(ctx context.Context, scope *authpb.Scope, resource interfa // need to return appropriate status codes in the ocs/ocdav layers. case *provider.CreateContainerRequest: return hasRoleEditor(*scope) && checkStorageRef(ctx, &share, v.GetRef()), nil + case *provider.TouchFileRequest: + return hasRoleEditor(*scope) && checkStorageRef(ctx, &share, v.GetRef()), nil case *provider.DeleteRequest: return hasRoleEditor(*scope) && checkStorageRef(ctx, &share, v.GetRef()), nil case *provider.MoveRequest: diff --git a/pkg/auth/scope/resourceinfo.go b/pkg/auth/scope/resourceinfo.go index f5afe94cfe..1a6c41a40f 100644 --- a/pkg/auth/scope/resourceinfo.go +++ b/pkg/auth/scope/resourceinfo.go @@ -55,6 +55,8 @@ func resourceinfoScope(_ context.Context, scope *authpb.Scope, resource interfac // need to return appropriate status codes in the ocs/ocdav layers. case *provider.CreateContainerRequest: return hasRoleEditor(*scope) && checkResourceInfo(&r, v.GetRef()), nil + case *provider.TouchFileRequest: + return hasRoleEditor(*scope) && checkResourceInfo(&r, v.GetRef()), nil case *provider.DeleteRequest: return hasRoleEditor(*scope) && checkResourceInfo(&r, v.GetRef()), nil case *provider.MoveRequest: diff --git a/pkg/auth/scope/share.go b/pkg/auth/scope/share.go index 1e29fd44e1..48683dea17 100644 --- a/pkg/auth/scope/share.go +++ b/pkg/auth/scope/share.go @@ -56,6 +56,8 @@ func shareScope(_ context.Context, scope *authpb.Scope, resource interface{}, lo // need to return appropriate status codes in the ocs/ocdav layers. case *provider.CreateContainerRequest: return checkShareStorageRef(&share, v.GetRef()), nil + case *provider.TouchFileRequest: + return checkShareStorageRef(&share, v.GetRef()), nil case *provider.DeleteRequest: return checkShareStorageRef(&share, v.GetRef()), nil case *provider.MoveRequest: diff --git a/pkg/storage/fs/nextcloud/nextcloud.go b/pkg/storage/fs/nextcloud/nextcloud.go index 7ee908beda..567a880863 100644 --- a/pkg/storage/fs/nextcloud/nextcloud.go +++ b/pkg/storage/fs/nextcloud/nextcloud.go @@ -253,6 +253,11 @@ func (nc *StorageDriver) CreateDir(ctx context.Context, ref *provider.Reference) return err } +// TouchFile as defined in the storage.FS interface +func (nc *StorageDriver) TouchFile(ctx context.Context, ref *provider.Reference) error { + return fmt.Errorf("unimplemented: TouchFile") +} + // Delete as defined in the storage.FS interface func (nc *StorageDriver) Delete(ctx context.Context, ref *provider.Reference) error { bodyStr, err := json.Marshal(ref) diff --git a/pkg/storage/fs/owncloud/owncloud.go b/pkg/storage/fs/owncloud/owncloud.go index 51a1299ec3..02a9bb1123 100644 --- a/pkg/storage/fs/owncloud/owncloud.go +++ b/pkg/storage/fs/owncloud/owncloud.go @@ -1186,6 +1186,10 @@ func (fs *ocfs) CreateDir(ctx context.Context, ref *provider.Reference) (err err return fs.propagate(ctx, ip) } +func (fs *ocfs) TouchFile(ctx context.Context, ref *provider.Reference) error { + return fmt.Errorf("unimplemented: TouchFile") +} + func (fs *ocfs) isShareFolderChild(sp string) bool { return strings.HasPrefix(sp, fs.c.ShareFolder) } diff --git a/pkg/storage/fs/owncloudsql/owncloudsql.go b/pkg/storage/fs/owncloudsql/owncloudsql.go index 43db8f3b89..7bcba3a3ef 100644 --- a/pkg/storage/fs/owncloudsql/owncloudsql.go +++ b/pkg/storage/fs/owncloudsql/owncloudsql.go @@ -766,6 +766,10 @@ func (fs *owncloudsqlfs) CreateDir(ctx context.Context, ref *provider.Reference) return fs.propagate(ctx, filepath.Dir(ip)) } +func (fs *owncloudsqlfs) TouchFile(ctx context.Context, ref *provider.Reference) error { + return fmt.Errorf("unimplemented: TouchFile") +} + func (fs *owncloudsqlfs) CreateReference(ctx context.Context, sp string, targetURI *url.URL) error { return errtypes.NotSupported("owncloudsql: operation not supported") } diff --git a/pkg/storage/fs/s3/s3.go b/pkg/storage/fs/s3/s3.go index 39d9fb7d6a..66467ec45a 100644 --- a/pkg/storage/fs/s3/s3.go +++ b/pkg/storage/fs/s3/s3.go @@ -326,6 +326,10 @@ func (fs *s3FS) CreateDir(ctx context.Context, ref *provider.Reference) error { return nil } +func (fs *s3FS) TouchFile(ctx context.Context, ref *provider.Reference) error { + return fmt.Errorf("unimplemented: TouchFile") +} + func (fs *s3FS) Delete(ctx context.Context, ref *provider.Reference) error { log := appctx.GetLogger(ctx) diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index f43a938152..8b61b6f616 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -32,6 +32,7 @@ type FS interface { GetHome(ctx context.Context) (string, error) CreateHome(ctx context.Context) error CreateDir(ctx context.Context, ref *provider.Reference) error + TouchFile(ctx context.Context, ref *provider.Reference) error Delete(ctx context.Context, ref *provider.Reference) error Move(ctx context.Context, oldRef, newRef *provider.Reference) error GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string) (*provider.ResourceInfo, error) diff --git a/pkg/storage/utils/decomposedfs/decomposedfs.go b/pkg/storage/utils/decomposedfs/decomposedfs.go index 4ebb5c3cb2..e36733d5bd 100644 --- a/pkg/storage/utils/decomposedfs/decomposedfs.go +++ b/pkg/storage/utils/decomposedfs/decomposedfs.go @@ -23,6 +23,7 @@ package decomposedfs import ( "context" + "fmt" "io" "net/url" "os" @@ -320,6 +321,10 @@ func (fs *Decomposedfs) CreateDir(ctx context.Context, ref *provider.Reference) return } +func (fs *Decomposedfs) TouchFile(ctx context.Context, ref *provider.Reference) error { + return fmt.Errorf("unimplemented: TouchFile") +} + // CreateReference creates a reference as a node folder with the target stored in extended attributes // There is no difference between the /Shares folder and normal nodes because the storage is not supposed to be accessible without the storage provider. // In effect everything is a shadow namespace. diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index 3d045dd553..582d6579ba 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -1214,6 +1214,28 @@ func (fs *eosfs) CreateDir(ctx context.Context, ref *provider.Reference) error { return fs.c.CreateDir(ctx, auth, fn) } +func (fs *eosfs) TouchFile(ctx context.Context, ref *provider.Reference) error { + log := appctx.GetLogger(ctx) + u, err := getUser(ctx) + if err != nil { + return errors.Wrap(err, "eosfs: no user in ctx") + } + p, err := fs.resolve(ctx, ref) + if err != nil { + return nil + } + + auth, err := fs.getUserAuth(ctx, u, p) + if err != nil { + return err + } + + log.Info().Msgf("eosfs: touch file: path=%s", p) + + fn := fs.wrap(ctx, p) + return fs.c.Touch(ctx, auth, fn) +} + func (fs *eosfs) CreateReference(ctx context.Context, p string, targetURI *url.URL) error { // TODO(labkode): for the time being we only allow creating references // in the virtual share folder to not pollute the nominal user tree. diff --git a/pkg/storage/utils/localfs/localfs.go b/pkg/storage/utils/localfs/localfs.go index 9f807d9ad2..0d99e7de0e 100644 --- a/pkg/storage/utils/localfs/localfs.go +++ b/pkg/storage/utils/localfs/localfs.go @@ -782,6 +782,10 @@ func (fs *localfs) CreateDir(ctx context.Context, ref *provider.Reference) error return fs.propagate(ctx, path.Dir(fn)) } +func (fs *localfs) TouchFile(ctx context.Context, ref *provider.Reference) error { + return fmt.Errorf("unimplemented: TouchFile") +} + func (fs *localfs) Delete(ctx context.Context, ref *provider.Reference) error { fn, err := fs.resolve(ctx, ref) if err != nil { From b3b323632e81839bf12456d1031addae0da7baed Mon Sep 17 00:00:00 2001 From: Willy Kloucek Date: Mon, 13 Dec 2021 12:18:01 +0100 Subject: [PATCH 2/5] use TouchFile for the app provider --- .../http/services/appprovider/appprovider.go | 53 ++----------------- 1 file changed, 5 insertions(+), 48 deletions(-) diff --git a/internal/http/services/appprovider/appprovider.go b/internal/http/services/appprovider/appprovider.go index ed101a0f3c..4cf5c78237 100644 --- a/internal/http/services/appprovider/appprovider.go +++ b/internal/http/services/appprovider/appprovider.go @@ -27,11 +27,8 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - "github.com/cs3org/reva/internal/http/services/datagateway" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" - "github.com/cs3org/reva/pkg/rhttp" "github.com/cs3org/reva/pkg/rhttp/global" "github.com/cs3org/reva/pkg/rhttp/router" "github.com/cs3org/reva/pkg/sharedconf" @@ -203,59 +200,19 @@ func (s *svc) handleNew(w http.ResponseWriter, r *http.Request) { return } - // Create empty file via storageprovider - createReq := &provider.InitiateFileUploadRequest{ + // Create the file + touchReq := provider.TouchFileRequest{ Ref: fileRef, - Opaque: &typespb.Opaque{ - Map: map[string]*typespb.OpaqueEntry{ - "Upload-Length": { - Decoder: "plain", - Value: []byte("0"), - }, - }, - }, } - // having a client.CreateFile() function would come in handy here... - - createRes, err := client.InitiateFileUpload(ctx, createReq) - if err != nil { - writeError(w, r, appErrorServerError, "error calling InitiateFileUpload", err) - return - } - if createRes.Status.Code != rpc.Code_CODE_OK { - writeError(w, r, appErrorServerError, "error calling InitiateFileUpload", nil) - return - } - - // Do a HTTP PUT with an empty body - var ep, token string - for _, p := range createRes.Protocols { - if p.Protocol == "simple" { - ep, token = p.UploadEndpoint, p.Token - } - } - httpReq, err := rhttp.NewRequest(ctx, http.MethodPut, ep, nil) + touchRes, err := client.TouchFile(ctx, &touchReq) if err != nil { writeError(w, r, appErrorServerError, "failed to create the file", err) return } - httpReq.Header.Set(datagateway.TokenTransportHeader, token) - httpRes, err := rhttp.GetHTTPClient( - rhttp.Context(ctx), - rhttp.Insecure(s.conf.Insecure), - ).Do(httpReq) - if err != nil { - writeError(w, r, appErrorServerError, "failed to create the file", err) - return - } - defer httpRes.Body.Close() - if httpRes.StatusCode == http.StatusForbidden { - // the file upload was already finished since it is a zero byte file - // TODO: why do we get a 401 then!? - } else if httpRes.StatusCode != http.StatusOK { - writeError(w, r, appErrorServerError, "failed to create the file", nil) + if touchRes.Status.Code != rpc.Code_CODE_OK { + writeError(w, r, appErrorServerError, "statting the created file failed", nil) return } From 8998a9c10ffcb006979abc90a3e96f9b4d48e7f0 Mon Sep 17 00:00:00 2001 From: Willy Kloucek Date: Mon, 13 Dec 2021 12:22:45 +0100 Subject: [PATCH 3/5] add changelog and comments --- changelog/unreleased/enhancement-add-touch-file.md | 6 ++++++ pkg/storage/fs/owncloud/owncloud.go | 1 + pkg/storage/fs/owncloudsql/owncloudsql.go | 1 + pkg/storage/fs/s3/s3.go | 1 + pkg/storage/utils/decomposedfs/decomposedfs.go | 1 + pkg/storage/utils/eosfs/eosfs.go | 1 + pkg/storage/utils/localfs/localfs.go | 1 + 7 files changed, 12 insertions(+) create mode 100644 changelog/unreleased/enhancement-add-touch-file.md diff --git a/changelog/unreleased/enhancement-add-touch-file.md b/changelog/unreleased/enhancement-add-touch-file.md new file mode 100644 index 0000000000..8e9c04e0b9 --- /dev/null +++ b/changelog/unreleased/enhancement-add-touch-file.md @@ -0,0 +1,6 @@ +Enhancement: Implement TouchFile from the CS3apis + +We've updated the CS3apis and implemented the TouchFile method. + +https://github.com/cs3org/reva/pull/2369 +https://github.com/cs3org/cs3apis/pull/154 diff --git a/pkg/storage/fs/owncloud/owncloud.go b/pkg/storage/fs/owncloud/owncloud.go index 02a9bb1123..51a5ef8b05 100644 --- a/pkg/storage/fs/owncloud/owncloud.go +++ b/pkg/storage/fs/owncloud/owncloud.go @@ -1186,6 +1186,7 @@ func (fs *ocfs) CreateDir(ctx context.Context, ref *provider.Reference) (err err return fs.propagate(ctx, ip) } +// TouchFile as defined in the storage.FS interface func (fs *ocfs) TouchFile(ctx context.Context, ref *provider.Reference) error { return fmt.Errorf("unimplemented: TouchFile") } diff --git a/pkg/storage/fs/owncloudsql/owncloudsql.go b/pkg/storage/fs/owncloudsql/owncloudsql.go index 7bcba3a3ef..05b929330f 100644 --- a/pkg/storage/fs/owncloudsql/owncloudsql.go +++ b/pkg/storage/fs/owncloudsql/owncloudsql.go @@ -766,6 +766,7 @@ func (fs *owncloudsqlfs) CreateDir(ctx context.Context, ref *provider.Reference) return fs.propagate(ctx, filepath.Dir(ip)) } +// TouchFile as defined in the storage.FS interface func (fs *owncloudsqlfs) TouchFile(ctx context.Context, ref *provider.Reference) error { return fmt.Errorf("unimplemented: TouchFile") } diff --git a/pkg/storage/fs/s3/s3.go b/pkg/storage/fs/s3/s3.go index 66467ec45a..330706aae0 100644 --- a/pkg/storage/fs/s3/s3.go +++ b/pkg/storage/fs/s3/s3.go @@ -326,6 +326,7 @@ func (fs *s3FS) CreateDir(ctx context.Context, ref *provider.Reference) error { return nil } +// TouchFile as defined in the storage.FS interface func (fs *s3FS) TouchFile(ctx context.Context, ref *provider.Reference) error { return fmt.Errorf("unimplemented: TouchFile") } diff --git a/pkg/storage/utils/decomposedfs/decomposedfs.go b/pkg/storage/utils/decomposedfs/decomposedfs.go index e36733d5bd..6b968febf2 100644 --- a/pkg/storage/utils/decomposedfs/decomposedfs.go +++ b/pkg/storage/utils/decomposedfs/decomposedfs.go @@ -321,6 +321,7 @@ func (fs *Decomposedfs) CreateDir(ctx context.Context, ref *provider.Reference) return } +// TouchFile as defined in the storage.FS interface func (fs *Decomposedfs) TouchFile(ctx context.Context, ref *provider.Reference) error { return fmt.Errorf("unimplemented: TouchFile") } diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index 582d6579ba..ce62dde0c5 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -1214,6 +1214,7 @@ func (fs *eosfs) CreateDir(ctx context.Context, ref *provider.Reference) error { return fs.c.CreateDir(ctx, auth, fn) } +// TouchFile as defined in the storage.FS interface func (fs *eosfs) TouchFile(ctx context.Context, ref *provider.Reference) error { log := appctx.GetLogger(ctx) u, err := getUser(ctx) diff --git a/pkg/storage/utils/localfs/localfs.go b/pkg/storage/utils/localfs/localfs.go index 0d99e7de0e..4dc7c37e78 100644 --- a/pkg/storage/utils/localfs/localfs.go +++ b/pkg/storage/utils/localfs/localfs.go @@ -782,6 +782,7 @@ func (fs *localfs) CreateDir(ctx context.Context, ref *provider.Reference) error return fs.propagate(ctx, path.Dir(fn)) } +// TouchFile as defined in the storage.FS interface func (fs *localfs) TouchFile(ctx context.Context, ref *provider.Reference) error { return fmt.Errorf("unimplemented: TouchFile") } From 816a20455ede70b3d04347a0a90fc21a1c273ab2 Mon Sep 17 00:00:00 2001 From: Willy Kloucek Date: Tue, 14 Dec 2021 10:17:41 +0100 Subject: [PATCH 4/5] revert use TouchFile in app provider --- .../http/services/appprovider/appprovider.go | 53 +++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/internal/http/services/appprovider/appprovider.go b/internal/http/services/appprovider/appprovider.go index 4cf5c78237..ed101a0f3c 100644 --- a/internal/http/services/appprovider/appprovider.go +++ b/internal/http/services/appprovider/appprovider.go @@ -27,8 +27,11 @@ import ( gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/internal/http/services/datagateway" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/pkg/rhttp" "github.com/cs3org/reva/pkg/rhttp/global" "github.com/cs3org/reva/pkg/rhttp/router" "github.com/cs3org/reva/pkg/sharedconf" @@ -200,19 +203,59 @@ func (s *svc) handleNew(w http.ResponseWriter, r *http.Request) { return } - // Create the file - touchReq := provider.TouchFileRequest{ + // Create empty file via storageprovider + createReq := &provider.InitiateFileUploadRequest{ Ref: fileRef, + Opaque: &typespb.Opaque{ + Map: map[string]*typespb.OpaqueEntry{ + "Upload-Length": { + Decoder: "plain", + Value: []byte("0"), + }, + }, + }, } - touchRes, err := client.TouchFile(ctx, &touchReq) + // having a client.CreateFile() function would come in handy here... + + createRes, err := client.InitiateFileUpload(ctx, createReq) + if err != nil { + writeError(w, r, appErrorServerError, "error calling InitiateFileUpload", err) + return + } + if createRes.Status.Code != rpc.Code_CODE_OK { + writeError(w, r, appErrorServerError, "error calling InitiateFileUpload", nil) + return + } + + // Do a HTTP PUT with an empty body + var ep, token string + for _, p := range createRes.Protocols { + if p.Protocol == "simple" { + ep, token = p.UploadEndpoint, p.Token + } + } + httpReq, err := rhttp.NewRequest(ctx, http.MethodPut, ep, nil) if err != nil { writeError(w, r, appErrorServerError, "failed to create the file", err) return } - if touchRes.Status.Code != rpc.Code_CODE_OK { - writeError(w, r, appErrorServerError, "statting the created file failed", nil) + httpReq.Header.Set(datagateway.TokenTransportHeader, token) + httpRes, err := rhttp.GetHTTPClient( + rhttp.Context(ctx), + rhttp.Insecure(s.conf.Insecure), + ).Do(httpReq) + if err != nil { + writeError(w, r, appErrorServerError, "failed to create the file", err) + return + } + defer httpRes.Body.Close() + if httpRes.StatusCode == http.StatusForbidden { + // the file upload was already finished since it is a zero byte file + // TODO: why do we get a 401 then!? + } else if httpRes.StatusCode != http.StatusOK { + writeError(w, r, appErrorServerError, "failed to create the file", nil) return } From 7556c00d63efd6cce225cca3f604dffef9d60ed5 Mon Sep 17 00:00:00 2001 From: Willy Kloucek <34452982+wkloucek@users.noreply.github.com> Date: Tue, 14 Dec 2021 14:19:43 +0100 Subject: [PATCH 5/5] fix resource typo Co-authored-by: Giuseppe Lo Presti --- internal/grpc/interceptors/readonly/readonly.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/grpc/interceptors/readonly/readonly.go b/internal/grpc/interceptors/readonly/readonly.go index 566d5f369f..d1d8801c2d 100644 --- a/internal/grpc/interceptors/readonly/readonly.go +++ b/internal/grpc/interceptors/readonly/readonly.go @@ -99,11 +99,11 @@ func NewUnary(map[string]interface{}) (grpc.UnaryServerInterceptor, int, error) }, nil case *provider.CreateContainerRequest: return &provider.CreateContainerResponse{ - Status: rstatus.NewPermissionDenied(ctx, nil, "permission denied: tried to create ressource on readonly storage"), + Status: rstatus.NewPermissionDenied(ctx, nil, "permission denied: tried to create resource on read-only storage"), }, nil case *provider.TouchFileRequest: return &provider.TouchFileResponse{ - Status: rstatus.NewPermissionDenied(ctx, nil, "permission denied: tried to create ressource on readonly storage"), + Status: rstatus.NewPermissionDenied(ctx, nil, "permission denied: tried to create resource on read-only storage"), }, nil case *provider.CreateHomeRequest: return &provider.CreateHomeResponse{