diff --git a/changelog/unreleased/enhancement-cback-storage-driver.md b/changelog/unreleased/enhancement-cback-storage-driver.md new file mode 100644 index 0000000000..2e36b97651 --- /dev/null +++ b/changelog/unreleased/enhancement-cback-storage-driver.md @@ -0,0 +1,5 @@ +Enhancement: Implementation of cback storage driver for REVA + +This is a read only fs interface. + +https://github.com/cs3org/reva/pull/3116 \ No newline at end of file diff --git a/pkg/storage/fs/cback/cback.go b/pkg/storage/fs/cback/cback.go new file mode 100644 index 0000000000..22a60e106f --- /dev/null +++ b/pkg/storage/fs/cback/cback.go @@ -0,0 +1,497 @@ +// 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. + +package cback + +import ( + "context" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "time" + + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + v1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + ctxpkg "github.com/cs3org/reva/pkg/ctx" + "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/mime" + "github.com/cs3org/reva/pkg/rhttp" + "github.com/cs3org/reva/pkg/storage" + "github.com/cs3org/reva/pkg/storage/fs/registry" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +type cback struct { + conf *Options + client *http.Client +} + +func init() { + registry.Register("cback", New) +} + +// New returns an implementation to the storage.FS interface that talks to +// cback +func New(m map[string]interface{}) (fs storage.FS, err error) { + + c := &Options{} + if err = mapstructure.Decode(m, c); err != nil { + return nil, errors.Wrap(err, "Error Decoding Configuration") + } + + httpClient := rhttp.GetHTTPClient() + + // Returns the storage.FS interface + return &cback{conf: c, client: httpClient}, nil + +} + +func (fs *cback) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string) (*provider.ResourceInfo, error) { + var ssID, searchPath string + + user, inContext := ctxpkg.ContextGetUser(ctx) + + if !inContext { + return nil, errtypes.UserRequired("user not found in context") + } + + resp, err := fs.matchBackups(user.Username, ref.Path) + + if err != nil { + return nil, err + } + + snapshotList, err := fs.listSnapshots(user.Username, resp.ID) + + if err != nil { + return nil, err + } + + ssID, searchPath = fs.pathTrimmer(snapshotList, resp) + + if resp.Source == ref.Path { + setTime := v1beta1.Timestamp{ + Seconds: uint64(time.Now().Unix()), + Nanos: 0, + } + + ident := provider.ResourceId{ + OpaqueId: ref.Path, + StorageId: "cback", + } + + ri := &provider.ResourceInfo{ + Etag: "", + PermissionSet: &permID, + Checksum: &checkSum, + Mtime: &setTime, + Id: &ident, + Owner: user.Id, + Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, + Size: 0, + Path: ref.Path, + MimeType: mime.Detect(true, ref.Path), + } + + return ri, nil + + } + + ret, err := fs.statResource(resp.ID, ssID, user.Username, searchPath, resp.Source) + + if err != nil { + return nil, err + } + + setTime := v1beta1.Timestamp{ + Seconds: ret.Mtime, + Nanos: 0, + } + + ident := provider.ResourceId{ + OpaqueId: ret.Path, + StorageId: "cback", + } + + ri := &provider.ResourceInfo{ + Etag: "", + PermissionSet: &permID, + Checksum: &checkSum, + Mtime: &setTime, + Id: &ident, + Owner: user.Id, + Type: ret.Type, + Size: ret.Size, + Path: ret.Path, + } + + if ret.Type == 2 { + ri.MimeType = mime.Detect(true, ret.Path) + } else { + ri.MimeType = mime.Detect(false, ret.Path) + } + + return ri, nil +} + +func (fs *cback) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys []string) ([]*provider.ResourceInfo, error) { + + var path string = ref.GetPath() + var ssID, searchPath string + + user, inContext := ctxpkg.ContextGetUser(ctx) + + if !inContext { + return nil, errtypes.UserRequired("no user found in context") + } + + resp, err := fs.matchBackups(user.Username, path) + + if err != nil { + return nil, err + } + + // Code executed before snapshot being inputted + if resp == nil { + pathList, err := fs.pathFinder(user.Username, ref.Path) + + if err != nil { + return nil, err + } + + files := make([]*provider.ResourceInfo, 0, len(pathList)) + + for _, paths := range pathList { + + setTime := v1beta1.Timestamp{ + Seconds: 0, + Nanos: 0, + } + + ident := provider.ResourceId{ + OpaqueId: paths, + StorageId: "cback", + } + + f := provider.ResourceInfo{ + Mtime: &setTime, + Id: &ident, + Checksum: &checkSum, + Path: paths, + Owner: user.Id, + PermissionSet: &permID, + Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, + Size: 0, + Etag: "", + MimeType: mime.Detect(true, paths), + } + files = append(files, &f) + } + + return files, nil + } + + snapshotList, err := fs.listSnapshots(user.Username, resp.ID) + + if err != nil { + fmt.Print(err) + return nil, err + } + + if resp.Substring != "" { + ssID, searchPath = fs.pathTrimmer(snapshotList, resp) + + if ssID == "" { + return nil, errtypes.NotFound("cback: snapshotID invalid") + } + + // If no match in path, therefore prints the files + fmt.Printf("The ssID is: %v\nThe Path is %v\n", ssID, searchPath) + ret, err := fs.fileSystem(resp.ID, ssID, user.Username, searchPath, resp.Source) + + if err != nil { + fmt.Print(err) + return nil, err + } + + files := make([]*provider.ResourceInfo, 0, len(ret)) + + for _, j := range ret { + + setTime := v1beta1.Timestamp{ + Seconds: j.Mtime, + Nanos: 0, + } + + ident := provider.ResourceId{ + OpaqueId: j.Path, + StorageId: "cback", + } + + f := provider.ResourceInfo{ + Mtime: &setTime, + Id: &ident, + Checksum: &checkSum, + Path: j.Path, + Owner: user.Id, + PermissionSet: &permID, + Type: j.Type, + Size: j.Size, + Etag: "", + } + + if j.Type == 2 { + f.MimeType = mime.Detect(true, j.Path) + } else { + f.MimeType = mime.Detect(false, j.Path) + } + + files = append(files, &f) + } + + return files, nil + + } + + // If match in path, therefore print the Snapshot IDs + files := make([]*provider.ResourceInfo, 0, len(snapshotList)) + + for _, snapshot := range snapshotList { + + epochTime, err := fs.timeConv(snapshot.Time) + + if err != nil { + return nil, err + } + + ident := provider.ResourceId{ + OpaqueId: ref.Path + "/" + snapshot.ID, + StorageId: "cback", + } + + setTime := v1beta1.Timestamp{ + Seconds: uint64(epochTime), + Nanos: 0, + } + + f := provider.ResourceInfo{ + Path: ref.Path + "/" + snapshot.ID, + Checksum: &checkSum, + Etag: "", + Owner: user.Id, + PermissionSet: &permID, + Id: &ident, + MimeType: mime.Detect(true, ref.Path+"/"+snapshot.ID), + Size: 0, + Mtime: &setTime, + Type: provider.ResourceType_RESOURCE_TYPE_CONTAINER, + } + + files = append(files, &f) + + } + + return files, nil + +} + +func (fs *cback) Download(ctx context.Context, ref *provider.Reference) (io.ReadCloser, error) { + var path string = ref.GetPath() + var ssID, searchPath string + user, _ := ctxpkg.ContextGetUser(ctx) + + resp, err := fs.matchBackups(user.Username, path) + + if err != nil { + fmt.Print(err) + return nil, err + } + + snapshotList, err := fs.listSnapshots(user.Username, resp.ID) + + if err != nil { + fmt.Print(err) + return nil, err + } + + if resp.Substring != "" { + + ssID, searchPath = fs.pathTrimmer(snapshotList, resp) + + url := fs.conf.APIURL + "/backups/" + strconv.Itoa(resp.ID) + "/snapshots/" + ssID + "/" + searchPath + requestType := "GET" + md, err := fs.GetMD(ctx, ref, nil) + + if err != nil { + return nil, err + } + + if md.Type == provider.ResourceType_RESOURCE_TYPE_FILE { + + responseData, err := fs.getRequest(user.Username, url, requestType, nil) + + if err != nil { + return nil, err + } + + return responseData, nil + } + + return nil, errtypes.BadRequest("can only download files") + + } + + return nil, errtypes.NotFound("cback: resource not found") +} + +func (fs *cback) GetHome(ctx context.Context) (string, error) { + return "", errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) CreateHome(ctx context.Context) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) CreateDir(ctx context.Context, ref *provider.Reference) error { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) TouchFile(ctx context.Context, ref *provider.Reference) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) Delete(ctx context.Context, ref *provider.Reference) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) Move(ctx context.Context, oldRef, newRef *provider.Reference) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) ListRevisions(ctx context.Context, ref *provider.Reference) (fvs []*provider.FileVersion, err error) { + return nil, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) DownloadRevision(ctx context.Context, ref *provider.Reference, key string) (file io.ReadCloser, err error) { + return nil, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) RestoreRevision(ctx context.Context, ref *provider.Reference, key string) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) GetPathByID(ctx context.Context, id *provider.ResourceId) (str string, err error) { + return "", errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) ListGrants(ctx context.Context, ref *provider.Reference) (glist []*provider.Grant, err error) { + return nil, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) GetQuota(ctx context.Context, ref *provider.Reference) (total uint64, used uint64, err error) { + return 0, 0, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) CreateReference(ctx context.Context, path string, targetURI *url.URL) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) Shutdown(ctx context.Context) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) (err error) { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) EmptyRecycle(ctx context.Context) error { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) CreateStorageSpace(ctx context.Context, req *provider.CreateStorageSpaceRequest) (r *provider.CreateStorageSpaceResponse, err error) { + return nil, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) ListRecycle(ctx context.Context, basePath, key, relativePath string) ([]*provider.RecycleItem, error) { + return nil, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) RestoreRecycleItem(ctx context.Context, basePath, key, relativePath string, restoreRef *provider.Reference) error { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) PurgeRecycleItem(ctx context.Context, basePath, key, relativePath string) error { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error) { + return nil, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) UpdateStorageSpace(ctx context.Context, req *provider.UpdateStorageSpaceRequest) (*provider.UpdateStorageSpaceResponse, error) { + return nil, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) SetLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error) { + return nil, errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) RefreshLock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error { + return errtypes.NotSupported("Operation Not Permitted") +} + +func (fs *cback) Upload(ctx context.Context, ref *provider.Reference, r io.ReadCloser) error { + return errtypes.NotSupported("Operation Not Permitted") + +} + +func (fs *cback) InitiateUpload(ctx context.Context, ref *provider.Reference, uploadLength int64, metadata map[string]string) (map[string]string, error) { + return nil, errtypes.NotSupported("Operation Not Permitted") + +} diff --git a/pkg/storage/fs/cback/options.go b/pkg/storage/fs/cback/options.go new file mode 100644 index 0000000000..098905fa7b --- /dev/null +++ b/pkg/storage/fs/cback/options.go @@ -0,0 +1,25 @@ +// 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. + +package cback + +// Options for the CBACK module +type Options struct { + ImpersonatorToken string `mapstructure:"token"` + APIURL string `mapstructure:"api_url"` +} diff --git a/pkg/storage/fs/cback/utilities.go b/pkg/storage/fs/cback/utilities.go new file mode 100644 index 0000000000..4be7bfe579 --- /dev/null +++ b/pkg/storage/fs/cback/utilities.go @@ -0,0 +1,398 @@ +// 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. + +package cback + +import ( + "encoding/json" + "errors" + "io" + "net/http" + "strconv" + "strings" + "time" + + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/pkg/errtypes" +) + +type backUpResponse struct { + Detail string `json:"detail"` + ID int `json:"id"` + Name string `json:"name"` + Source string `json:"source"` + Substring string // Used in function +} + +type snapshotResponse struct { + Detail string `json:"detail"` + ID string `json:"id"` + Time string `json:"time"` + Paths []string `json:"paths"` +} + +type contents struct { + Name string `json:"name"` + Type string `json:"type"` + Mode uint64 `json:"mode"` + Mtime float64 `json:"mtime"` + Atime float64 `json:"atime"` + Ctime float64 `json:"ctime"` + Inode uint64 `json:"inode"` + Size uint64 `json:"size"` + Detail string `json:"detail"` +} + +type fsReturn struct { + Type provider.ResourceType + Mtime uint64 + Size uint64 + Path string + Detail string +} + +var permID = provider.ResourcePermissions{ + AddGrant: false, + CreateContainer: false, + Delete: false, + GetPath: true, + GetQuota: true, + InitiateFileDownload: true, + InitiateFileUpload: false, + ListGrants: true, + ListContainer: true, + ListFileVersions: true, + ListRecycle: false, + Move: false, + RemoveGrant: false, + PurgeRecycle: false, + RestoreFileVersion: true, + RestoreRecycleItem: false, + Stat: true, + UpdateGrant: false, + DenyGrant: false} + +var checkSum = provider.ResourceChecksum{ + Sum: "", + Type: provider.ResourceChecksumType_RESOURCE_CHECKSUM_TYPE_UNSET, +} + +func mapReturn(fileType string) (provider.ResourceType, error) { + /* This function can be changed accordingly, depending on the file type + being return by the APIs */ + + switch fileType { + case "file": + return provider.ResourceType_RESOURCE_TYPE_FILE, nil + + case "dir": + return provider.ResourceType_RESOURCE_TYPE_CONTAINER, nil + + default: + return provider.ResourceType_RESOURCE_TYPE_INVALID, errtypes.NotFound("Resource type unrecognized") + } +} + +func (fs *cback) getRequest(userName, url string, reqType string, body io.Reader) (io.ReadCloser, error) { + + req, err := http.NewRequest(reqType, url, body) + req.SetBasicAuth(userName, fs.conf.ImpersonatorToken) + + if err != nil { + return nil, err + } + + if body != nil { + req.Header.Add("Content-Type", "application/json") + } + + req.Header.Add("accept", `application/json`) + + resp, err := fs.client.Do(req) + + if err != nil { + return nil, err + } + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + + switch resp.StatusCode { + + case http.StatusNotFound: + return nil, errtypes.NotFound("cback: resource not found") + case http.StatusForbidden: + return nil, errtypes.PermissionDenied("cback: user has no permissions to get the backup") + case http.StatusBadRequest: + return nil, errtypes.BadRequest("cback") + default: + return nil, errtypes.InternalError("cback: internal server error") + } + } + + return resp.Body, nil + +} + +func (fs *cback) listSnapshots(userName string, backupID int) ([]snapshotResponse, error) { + + url := fs.conf.APIURL + "/backups/" + strconv.Itoa(backupID) + "/snapshots" + responseData, err := fs.getRequest(userName, url, http.MethodGet, nil) + + if err != nil { + return nil, err + } + + defer responseData.Close() + + /*Unmarshalling the JSON response into the Response struct*/ + responseObject := []snapshotResponse{} + + err = json.NewDecoder(responseData).Decode(&responseObject) + + if err != nil { + return nil, err + } + + return responseObject, nil +} + +func (fs *cback) matchBackups(userName, pathInput string) (*backUpResponse, error) { + + url := fs.conf.APIURL + "/backups/" + responseData, err := fs.getRequest(userName, url, http.MethodGet, nil) + + if err != nil { + return nil, err + } + + defer responseData.Close() + + /*Unmarshalling the JSON response into the Response struct*/ + responseObject := []backUpResponse{} + + err = json.NewDecoder(responseData).Decode(&responseObject) + + if err != nil { + return nil, err + } + + if len(responseObject) == 0 { + err = errors.New("no match found") + return nil, err + } + + for _, response := range responseObject { + if response.Detail != "" { + err = errors.New(response.Detail) + return nil, err + } + + if strings.Compare(pathInput, response.Source) == 0 { + return &response, nil + } + } + + for _, response := range responseObject { + if response.Detail != "" { + err = errors.New(response.Detail) + return nil, err + } + + if strings.HasPrefix(pathInput, response.Source) { + substr := strings.TrimPrefix(pathInput, response.Source) + substr = strings.TrimLeft(substr, "/") + response.Substring = substr + return &response, nil + } + } + + /*If there is no error, but also no match found in the backup path the response is nil. + This means that the LSFolder function will know that no match has been found using the exact path, + and will therefore start checking if there is a substring of the backup job included in the inputted path*/ + return nil, nil +} + +func (fs *cback) statResource(backupID int, snapID, userName, path, source string) (*fsReturn, error) { + + url := fs.conf.APIURL + "/backups/" + strconv.Itoa(backupID) + "/snapshots/" + snapID + "/" + path + "?content=false" + responseData, err := fs.getRequest(userName, url, http.MethodOptions, nil) + + if err != nil { + return nil, err + } + + defer responseData.Close() + + responseObject := contents{} + + err = json.NewDecoder(responseData).Decode(&responseObject) + + if err != nil { + return nil, err + } + + m, err := mapReturn(responseObject.Type) + + if err != nil { + return nil, err + } + + retObject := fsReturn{ + Path: source + "/" + snapID + strings.TrimPrefix(responseObject.Name, source), + Type: m, + Mtime: uint64(responseObject.Mtime), + Size: responseObject.Size, + Detail: responseObject.Detail, + } + + return &retObject, nil +} + +func (fs *cback) fileSystem(backupID int, snapID, userName, path, source string) ([]*fsReturn, error) { + + url := fs.conf.APIURL + "/backups/" + strconv.Itoa(backupID) + "/snapshots/" + snapID + "/" + path + "?content=true" + responseData, err := fs.getRequest(userName, url, http.MethodOptions, nil) + + if err != nil { + return nil, err + } + + defer responseData.Close() + + /*Unmarshalling the JSON response into the Response struct*/ + responseObject := []contents{} + + err = json.NewDecoder(responseData).Decode(&responseObject) + + if err != nil { + return nil, err + } + + resp := make([]*fsReturn, 0, len(responseObject)) + + for _, response := range responseObject { + + m, err := mapReturn(response.Type) + + if err != nil { + return nil, err + } + + temp := fsReturn{ + Size: response.Size, + Type: m, + Mtime: uint64(response.Mtime), + Path: source + "/" + snapID + strings.TrimPrefix(response.Name, source), + } + + resp = append(resp, &temp) + } + + return resp, nil +} + +func (fs *cback) timeConv(timeInput string) (int64, error) { + tm, err := time.Parse(time.RFC3339, timeInput) + + if err != nil { + return 0, err + } + + epoch := tm.Unix() + return epoch, nil +} + +func (fs *cback) pathFinder(userName, path string) ([]string, error) { + url := fs.conf.APIURL + "/backups/" + responseData, err := fs.getRequest(userName, url, http.MethodGet, nil) + matchFound := false + + if err != nil { + return nil, err + } + + defer responseData.Close() + + /*Unmarshalling the JSON response into the Response struct*/ + responseObject := []backUpResponse{} + + err = json.NewDecoder(responseData).Decode(&responseObject) + + if err != nil { + return nil, err + } + + returnString := make([]string, 0, len(responseObject)) + + for _, response := range responseObject { + if response.Detail != "" { + err = errors.New(response.Detail) + return nil, err + } + + if strings.HasPrefix(response.Source, path) { + substr := strings.TrimPrefix(response.Source, path) + substr = strings.TrimLeft(substr, "/") + temp := strings.Split(substr, "/") + returnString = append(returnString, temp[0]) + matchFound = true + } + } + + if matchFound { + return duplicateRemoval(returnString), nil + } + + return nil, errtypes.NotFound("cback: resource not found") + +} + +func (fs *cback) pathTrimmer(snapshotList []snapshotResponse, resp *backUpResponse) (string, string) { + + var ssID, searchPath string + + for _, snapshot := range snapshotList { + + if snapshot.ID == resp.Substring { + ssID = resp.Substring + searchPath = resp.Source + break + + } else if strings.HasPrefix(resp.Substring, snapshot.ID) { + searchPath = strings.TrimPrefix(resp.Substring, snapshot.ID) + searchPath = resp.Source + searchPath + ssID = snapshot.ID + break + } + } + + return ssID, searchPath + +} + +func duplicateRemoval(strSlice []string) []string { + inList := make(map[string]bool) + var list []string + for _, str := range strSlice { + if _, value := inList[str]; !value { + inList[str] = true + list = append(list, str) + } + } + return list +} diff --git a/pkg/storage/fs/loader/loader.go b/pkg/storage/fs/loader/loader.go index cd88c5ddc7..e0b9f53575 100644 --- a/pkg/storage/fs/loader/loader.go +++ b/pkg/storage/fs/loader/loader.go @@ -20,6 +20,7 @@ package loader import ( // Load core storage filesystem backends. + _ "github.com/cs3org/reva/pkg/storage/fs/cback" _ "github.com/cs3org/reva/pkg/storage/fs/cephfs" _ "github.com/cs3org/reva/pkg/storage/fs/eos" _ "github.com/cs3org/reva/pkg/storage/fs/eosgrpc"