Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions code/go/0chain.net/blobbercore/handler/file_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package handler

import (
"context"
"fmt"
"net/http"

"github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation"
"github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp"
"github.com/0chain/blobber/code/go/0chain.net/blobbercore/reference"
"github.com/0chain/blobber/code/go/0chain.net/core/common"
"github.com/0chain/blobber/code/go/0chain.net/core/logging"
)

// FileCommand execute command for a file operation
Expand Down Expand Up @@ -37,3 +41,27 @@ func createFileCommand(req *http.Request) FileCommand {
return &AddFileCommand{}
}
}

// validateParentPathType validates against any parent path not being directory.
func validateParentPathType(ctx context.Context, allocationID, fPath string) error {
paths, err := common.GetParentPaths(fPath)
if err != nil {
return err
}

refs, err := reference.GetRefsTypeFromPaths(ctx, allocationID, paths)
if err != nil {
logging.Logger.Error(err.Error())
return common.NewError("database_error", "Got error while getting parent refs")
}

for _, ref := range refs {
if ref == nil {
continue
}
if ref.Type == reference.FILE {
return common.NewError("invalid_path", fmt.Sprintf("parent path %v is of file type", ref.Path))
}
}
return nil
}
19 changes: 17 additions & 2 deletions code/go/0chain.net/blobbercore/handler/file_command_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package handler
import (
"context"
"encoding/json"
"fmt"
"net/http"
"path/filepath"

"github.com/0chain/blobber/code/go/0chain.net/blobbercore/allocation"
"github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp"
Expand Down Expand Up @@ -36,12 +38,25 @@ func (cmd *AddFileCommand) IsAuthorized(ctx context.Context, req *http.Request,
return common.NewError("invalid_parameters",
"Invalid parameters. Error parsing the meta data for upload."+err.Error())
}
exisitingFileRef, _ := reference.GetReference(ctx, allocationObj.ID, fileChanger.Path)

if exisitingFileRef != nil {
if !filepath.IsAbs(fileChanger.Path) {
return common.NewError("invalid_path", fmt.Sprintf("%v is not absolute path", fileChanger.Path))
}

isExist, err := reference.IsRefExist(ctx, allocationObj.ID, fileChanger.Path)
if err != nil {
logging.Logger.Error(err.Error())
return common.NewError("database_error", "Got db error while getting ref")
}

if isExist {
return common.NewError("duplicate_file", "File at path already exists")
}

if err := validateParentPathType(ctx, allocationObj.ID, fileChanger.Path); err != nil {
return err
}

//create a FixedMerkleTree instance first, it will be reloaded from db in cmd.reloadChange if it is not first chunk
//cmd.fileChanger.FixedMerkleTree = &util.FixedMerkleTree{}

Expand Down
28 changes: 19 additions & 9 deletions code/go/0chain.net/blobbercore/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package handler
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
Expand Down Expand Up @@ -864,7 +863,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) {
q.Set("path", path)
q.Set("new_name", newName)
q.Set("connection_id", connectionID)
q.Set("dest", "dest")
q.Set("dest", "/dest")
url.RawQuery = q.Encode()

r, err := http.NewRequest(http.MethodPost, url.String(), nil)
Expand Down Expand Up @@ -915,13 +914,15 @@ func TestHandlers_Requiring_Signature(t *testing.T) {
mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "reference_objects" WHERE`)).
WithArgs(alloc.ID, lookUpHash).
WillReturnRows(
sqlmock.NewRows([]string{"type"}).
AddRow(reference.FILE),
sqlmock.NewRows([]string{"type", "name"}).
AddRow(reference.FILE, "path"),
)

mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "reference_objects" WHERE`)).
WithArgs(aa, aa).
WillReturnError(errors.New(""))
mock.ExpectQuery(regexp.QuoteMeta(`SELECT "path","type" FROM "reference_objects" WHERE`)).
WillReturnRows(
sqlmock.NewRows([]string{"path", "type"}).
AddRow("/dest", reference.DIRECTORY),
)

mock.ExpectExec(`INSERT INTO "allocation_connections"`).
WithArgs(aa, aa, aa, aa, aa, aa, aa).
Expand Down Expand Up @@ -1034,7 +1035,9 @@ func TestHandlers_Requiring_Signature(t *testing.T) {
}

q := url.Query()
formFieldByt, err := json.Marshal(&allocation.UpdateFileChanger{})
formFieldByt, err := json.Marshal(
&allocation.UpdateFileChanger{
BaseFileChanger: allocation.BaseFileChanger{Path: path}})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -1110,9 +1113,16 @@ func TestHandlers_Requiring_Signature(t *testing.T) {
)

mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "reference_objects"`)).
WithArgs(aa).
WithArgs(aa, aa).
WillReturnError(gorm.ErrRecordNotFound)

mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "reference_objects"`)).
WithArgs(aa, aa).
WillReturnRows(
sqlmock.NewRows([]string{"count"}).
AddRow(0),
)

mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "allocation_connections" WHERE`)).
WithArgs(connectionID, alloc.ID, alloc.OwnerID, allocation.DeletedConnection).
WillReturnRows(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"

"github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp"
"github.com/0chain/blobber/code/go/0chain.net/blobbercore/stats"
Expand Down Expand Up @@ -790,10 +791,30 @@ func (fsh *StorageHandler) CopyObject(ctx context.Context, r *http.Request) (int
if err != nil {
return nil, common.NewError("invalid_parameters", "Invalid file path. "+err.Error())
}

newPath := filepath.Join(destPath, objectRef.Name)
destRef, _ := reference.GetReference(ctx, allocationID, newPath)
if destRef != nil {
return nil, common.NewError("invalid_parameters", "Invalid destination path. Object Already exists.")
paths, err := common.GetParentPaths(newPath)
if err != nil {
return nil, err
}

paths = append(paths, newPath)

refs, err := reference.GetRefsTypeFromPaths(ctx, allocationID, paths)
if err != nil {
Logger.Error("Database error", zap.Error(err))
return nil, common.NewError("database_error", fmt.Sprintf("Got db error while getting refs for %v", paths))
}

for _, ref := range refs {
switch ref.Path {
case newPath:
return nil, common.NewError("invalid_parameters", "Invalid destination path. Object Already exists.")
default:
if ref.Type == reference.FILE {
return nil, common.NewError("invalid_path", fmt.Sprintf("%v is of file type", ref.Path))
}
}
}

allocationChange := &allocation.AllocationChange{}
Expand Down Expand Up @@ -880,6 +901,10 @@ func (fsh *StorageHandler) CreateDir(ctx context.Context, r *http.Request) (*blo
return nil, common.NewError("invalid_parameters", "Invalid dir path passed")
}

if !filepath.IsAbs(dirPath) {
return nil, common.NewError("invalid_path", fmt.Sprintf("%v is not absolute path", dirPath))
}

exisitingRef := fsh.checkIfFileAlreadyExists(ctx, allocationID, dirPath)
if allocationObj.OwnerID != clientID && allocationObj.PayerID != clientID {
return nil, common.NewError("invalid_operation", "Operation needs to be performed by the owner or the payer of the allocation")
Expand All @@ -889,6 +914,10 @@ func (fsh *StorageHandler) CreateDir(ctx context.Context, r *http.Request) (*blo
return nil, common.NewError("duplicate_file", "File at path already exists")
}

if err := validateParentPathType(ctx, allocationID, dirPath); err != nil {
return nil, err
}

connectionID := r.FormValue("connection_id")
if connectionID == "" {
return nil, common.NewError("invalid_parameters", "Invalid connection id passed")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ func TestBlobberGRPCService_UploadFile(t *testing.T) {
pubKeyBytes, _ := hex.DecodeString(pubKey)
clientId := encryption.Hash(pubKeyBytes)

formFieldByt, err := json.Marshal(&allocation.UpdateFileChanger{BaseFileChanger: allocation.BaseFileChanger{Filename: `helper_integration_test.go`}})
path := "/some_file"
formFieldByt, err := json.Marshal(&allocation.UpdateFileChanger{BaseFileChanger: allocation.BaseFileChanger{Filename: `helper_integration_test.go`, Path: path}})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -68,7 +69,7 @@ func TestBlobberGRPCService_UploadFile(t *testing.T) {

input: &blobbergrpc.UploadFileRequest{
Allocation: allocationTx,
Path: "/some_file",
Path: path,
ConnectionId: "connection_id",
Method: "POST",
UploadMeta: string(formFieldByt),
Expand Down
49 changes: 49 additions & 0 deletions code/go/0chain.net/blobbercore/reference/ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,55 @@ func GetReferenceFromLookupHash(ctx context.Context, allocationID, path_hash str
return nil, err
}

// GetRefType Select type from ref and return it
func GetRefType(ctx context.Context, allocationID, path string) (string, error) {
ref := new(Ref)
db := datastore.GetStore().GetTransaction(ctx)
err := db.Select("type").Where("allocation_id=? AND path=?", allocationID, path).First(ref).Error
if err != nil {
return "", err
}
return ref.Type, nil
}

// GetRefWithID Return Ref with only ID selected in sql query
func GetRefWithID(ctx context.Context, allocationID, path string) (*Ref, error) {
ref := new(Ref)
db := datastore.GetStore().GetTransaction(ctx)
err := db.Select("id").Where("allocation_id=? AND path=?", allocationID, path).First(ref).Error
if err != nil {
return nil, err
}
return ref, nil
}

// IsRefExist checks if ref with given path exists and returns error other than gorm.ErrRecordNotFound
func IsRefExist(ctx context.Context, allocationID, path string) (bool, error) {
db := datastore.GetStore().GetTransaction(ctx)
var count int64
if err := db.Model(&Ref{}).Where("allocation_id=? AND path=?", allocationID, path).Count(&count).Error; err != nil {
return false, err
}

return count > 0, nil
}

// GetRefsTypeFromPaths Give list of paths it will return refs of respective path with only Type and Path selected in sql query
func GetRefsTypeFromPaths(ctx context.Context, allocationID string, paths []string) (refs []*Ref, err error) {
if len(paths) == 0 {
return
}

db := datastore.GetStore().GetTransaction(ctx)
db = db.Select("path", "type")
for _, p := range paths {
db = db.Or(Ref{AllocationID: allocationID, Path: p})
}

err = db.Find(&refs).Error
return
}

func GetSubDirsFromPath(p string) []string {
path := p
parent, cur := filepath.Split(path)
Expand Down
26 changes: 25 additions & 1 deletion code/go/0chain.net/core/common/utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package common

import "fmt"
import (
"fmt"
"path/filepath"
"strings"
)

// IsEmpty checks whether the input string is empty or not
func IsEmpty(s string) bool {
Expand All @@ -22,3 +26,23 @@ func ToKey(key interface{}) string {
func IsEqual(key1, key2 string) bool {
return key1 == key2
}

// getParentPaths For path /a/b/c.txt, will return [/a,/a/b]
func GetParentPaths(fPath string) ([]string, error) {
if fPath == "" {
return nil, nil
}

fPath = filepath.Clean(fPath)
if !filepath.IsAbs(fPath) {
return nil, NewError("invalid_path", fmt.Sprintf("%v is not absolute path", fPath))
}
splittedPaths := strings.Split(fPath, "/")
var paths []string

for i := 0; i < len(splittedPaths); i++ {
subPath := strings.Join(splittedPaths[0:i], "/")
paths = append(paths, subPath)
}
return paths[2:], nil
}