From a498b9b3a271f18b0a550db10d982d9640fd48d9 Mon Sep 17 00:00:00 2001 From: Anis Elleuch Date: Tue, 12 Mar 2019 22:03:59 +0100 Subject: [PATCH] rm: Force removing an object when it is encrypted without key specified (#2700) Currently, a single remove HEAD an object, but this latter can return 400 Bad Request when the object is encrypted and the user doesn't specify any encryption key. So when HEAD fail, this PR will use GET listing to find the object and remove it if existent. We can avoid calling HEAD in the first place and use only listing, but this has some penality on the server. --- cmd/rm-main.go | 26 ++++++++++++++++---------- cmd/stat-main.go | 18 ++++++++++++------ cmd/stat.go | 30 +++++++++++++++++++----------- cmd/stat_test.go | 2 +- cmd/typed-errors.go | 7 +++++++ 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/cmd/rm-main.go b/cmd/rm-main.go index 15a6c4f0..aa403a9a 100644 --- a/cmd/rm-main.go +++ b/cmd/rm-main.go @@ -176,21 +176,20 @@ func checkRmSyntax(ctx *cli.Context, encKeyDB map[string][]prefixSSEPair) { } func removeSingle(url string, isIncomplete bool, isFake bool, olderThan, newerThan string, encKeyDB map[string][]prefixSSEPair) error { - targetAlias, targetURL, _ := mustExpandAlias(url) - clnt, pErr := newClientFromAlias(targetAlias, targetURL) - if pErr != nil { - errorIf(pErr.Trace(url), "Invalid argument `"+url+"`.") - return exitStatus(globalErrorExitStatus) // End of journey. - } - isFetchMeta := true - alias, _ := url2Alias(url) - sseKey := getSSE(url, encKeyDB[alias]) - content, pErr := clnt.Stat(isIncomplete, isFetchMeta, sseKey) + isRecursive := false + contents, pErr := statURL(url, isIncomplete, isRecursive, encKeyDB) if pErr != nil { errorIf(pErr.Trace(url), "Failed to remove `"+url+"`.") return exitStatus(globalErrorExitStatus) } + if len(contents) == 0 { + errorIf(errDummy().Trace(url), "Failed to remove `"+url+"`. Target object is not found") + return exitStatus(globalErrorExitStatus) + } + + content := contents[0] + // Skip objects older than older--than parameter if specified if olderThan != "" && isOlder(content.Time, olderThan) { return nil @@ -207,6 +206,13 @@ func removeSingle(url string, isIncomplete bool, isFake bool, olderThan, newerTh }) if !isFake { + targetAlias, targetURL, _ := mustExpandAlias(url) + clnt, pErr := newClientFromAlias(targetAlias, targetURL) + if pErr != nil { + errorIf(pErr.Trace(url), "Invalid argument `"+url+"`.") + return exitStatus(globalErrorExitStatus) // End of journey. + } + contentCh := make(chan *clientContent, 1) contentCh <- &clientContent{URL: *newClientURL(targetURL)} close(contentCh) diff --git a/cmd/stat-main.go b/cmd/stat-main.go index 3c5166b2..02a2d60b 100644 --- a/cmd/stat-main.go +++ b/cmd/stat-main.go @@ -121,12 +121,18 @@ func mainStat(ctx *cli.Context) error { var cErr error for _, targetURL := range args { - var clnt Client - clnt, err := newClient(targetURL) - fatalIf(err.Trace(targetURL), "Unable to initialize target `"+targetURL+"`.") - - targetAlias, _, _ := mustExpandAlias(targetURL) - return doStat(clnt, isRecursive, targetAlias, targetURL, encKeyDB) + stats, err := statURL(targetURL, false, isRecursive, encKeyDB) + if err != nil { + fatalIf(err, "Unable to stat `"+targetURL+"`.") + } + for _, stat := range stats { + st := parseStat(stat) + if !globalJSON { + printStat(st) + } else { + console.Println(st.JSON()) + } + } } return cErr diff --git a/cmd/stat.go b/cmd/stat.go index f4a117e3..7f577966 100644 --- a/cmd/stat.go +++ b/cmd/stat.go @@ -92,7 +92,7 @@ func (c statMessage) JSON() string { } // parseStat parses client Content container into statMessage struct. -func parseStat(targetAlias string, c *clientContent) statMessage { +func parseStat(c *clientContent) statMessage { content := statMessage{} content.Date = c.Time.Local() // guess file type. @@ -112,8 +112,16 @@ func parseStat(targetAlias string, c *clientContent) statMessage { return content } -// doStat - list all entities inside a folder. -func doStat(clnt Client, isRecursive bool, targetAlias, targetURL string, encKeyDB map[string][]prefixSSEPair) error { +// statURL - simple or recursive listing +func statURL(targetURL string, isIncomplete, isRecursive bool, encKeyDB map[string][]prefixSSEPair) ([]*clientContent, *probe.Error) { + var stats []*clientContent + var clnt Client + clnt, err := newClient(targetURL) + if err != nil { + return nil, err + } + + targetAlias, _, _ := mustExpandAlias(targetURL) prefixPath := clnt.GetURL().Path separator := string(clnt.GetURL().Separator) @@ -121,7 +129,6 @@ func doStat(clnt Client, isRecursive bool, targetAlias, targetURL string, encKey prefixPath = prefixPath[:strings.LastIndex(prefixPath, separator)+1] } var cErr error - isIncomplete := false for content := range clnt.List(isRecursive, isIncomplete, DirNone) { if content.Err != nil { switch content.Err.ToGoError().(type) { @@ -147,6 +154,11 @@ func doStat(clnt Client, isRecursive bool, targetAlias, targetURL string, encKey continue } url := targetAlias + getKey(content) + + if !isRecursive && url != targetURL { + return nil, errTargetNotFound(targetURL) + } + _, stat, err := url2Stat(url, true, encKeyDB) if err != nil { stat = content @@ -157,12 +169,8 @@ func doStat(clnt Client, isRecursive bool, targetAlias, targetURL string, encKey // Trim prefix path from the content path. contentURL = strings.TrimPrefix(contentURL, prefixPath) stat.URL.Path = contentURL - st := parseStat(targetAlias, stat) - if !globalJSON { - printStat(st) - } else { - console.Println(st.JSON()) - } + stats = append(stats, stat) } - return cErr + + return stats, probe.NewError(cErr) } diff --git a/cmd/stat_test.go b/cmd/stat_test.go index c0401427..543bc22f 100644 --- a/cmd/stat_test.go +++ b/cmd/stat_test.go @@ -40,7 +40,7 @@ func (s *TestSuite) TestParseStat(c *C) { "play"}, } for _, testCase := range testCases { - statMsg := parseStat(testCase.targetAlias, &testCase.content) + statMsg := parseStat(&testCase.content) c.Assert(testCase.content.Metadata, DeepEquals, statMsg.Metadata) c.Assert(testCase.content.EncryptionHeaders, DeepEquals, statMsg.EncryptionHeaders) c.Assert(testCase.content.Size, Equals, statMsg.Size) diff --git a/cmd/typed-errors.go b/cmd/typed-errors.go index c1b4114e..731b6100 100644 --- a/cmd/typed-errors.go +++ b/cmd/typed-errors.go @@ -96,6 +96,13 @@ var errInvalidTarget = func(URL string) *probe.Error { return probe.NewError(invalidTargetErr(errors.New(msg))).Untrace() } +type targetNotFoundErr error + +var errTargetNotFound = func(URL string) *probe.Error { + msg := "Target `" + URL + "` not found." + return probe.NewError(targetNotFoundErr(errors.New(msg))).Untrace() +} + type overwriteNotAllowedErr struct { error }