mirror of
https://github.com/minio/mc.git
synced 2025-07-30 07:23:03 +03:00
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.
This commit is contained in:
@ -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 {
|
func removeSingle(url string, isIncomplete bool, isFake bool, olderThan, newerThan string, encKeyDB map[string][]prefixSSEPair) error {
|
||||||
targetAlias, targetURL, _ := mustExpandAlias(url)
|
isRecursive := false
|
||||||
clnt, pErr := newClientFromAlias(targetAlias, targetURL)
|
contents, pErr := statURL(url, isIncomplete, isRecursive, encKeyDB)
|
||||||
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)
|
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
errorIf(pErr.Trace(url), "Failed to remove `"+url+"`.")
|
errorIf(pErr.Trace(url), "Failed to remove `"+url+"`.")
|
||||||
return exitStatus(globalErrorExitStatus)
|
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
|
// Skip objects older than older--than parameter if specified
|
||||||
if olderThan != "" && isOlder(content.Time, olderThan) {
|
if olderThan != "" && isOlder(content.Time, olderThan) {
|
||||||
return nil
|
return nil
|
||||||
@ -207,6 +206,13 @@ func removeSingle(url string, isIncomplete bool, isFake bool, olderThan, newerTh
|
|||||||
})
|
})
|
||||||
|
|
||||||
if !isFake {
|
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 := make(chan *clientContent, 1)
|
||||||
contentCh <- &clientContent{URL: *newClientURL(targetURL)}
|
contentCh <- &clientContent{URL: *newClientURL(targetURL)}
|
||||||
close(contentCh)
|
close(contentCh)
|
||||||
|
@ -121,12 +121,18 @@ func mainStat(ctx *cli.Context) error {
|
|||||||
|
|
||||||
var cErr error
|
var cErr error
|
||||||
for _, targetURL := range args {
|
for _, targetURL := range args {
|
||||||
var clnt Client
|
stats, err := statURL(targetURL, false, isRecursive, encKeyDB)
|
||||||
clnt, err := newClient(targetURL)
|
if err != nil {
|
||||||
fatalIf(err.Trace(targetURL), "Unable to initialize target `"+targetURL+"`.")
|
fatalIf(err, "Unable to stat `"+targetURL+"`.")
|
||||||
|
}
|
||||||
targetAlias, _, _ := mustExpandAlias(targetURL)
|
for _, stat := range stats {
|
||||||
return doStat(clnt, isRecursive, targetAlias, targetURL, encKeyDB)
|
st := parseStat(stat)
|
||||||
|
if !globalJSON {
|
||||||
|
printStat(st)
|
||||||
|
} else {
|
||||||
|
console.Println(st.JSON())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return cErr
|
return cErr
|
||||||
|
|
||||||
|
30
cmd/stat.go
30
cmd/stat.go
@ -92,7 +92,7 @@ func (c statMessage) JSON() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseStat parses client Content container into statMessage struct.
|
// parseStat parses client Content container into statMessage struct.
|
||||||
func parseStat(targetAlias string, c *clientContent) statMessage {
|
func parseStat(c *clientContent) statMessage {
|
||||||
content := statMessage{}
|
content := statMessage{}
|
||||||
content.Date = c.Time.Local()
|
content.Date = c.Time.Local()
|
||||||
// guess file type.
|
// guess file type.
|
||||||
@ -112,8 +112,16 @@ func parseStat(targetAlias string, c *clientContent) statMessage {
|
|||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
// doStat - list all entities inside a folder.
|
// statURL - simple or recursive listing
|
||||||
func doStat(clnt Client, isRecursive bool, targetAlias, targetURL string, encKeyDB map[string][]prefixSSEPair) error {
|
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
|
prefixPath := clnt.GetURL().Path
|
||||||
separator := string(clnt.GetURL().Separator)
|
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]
|
prefixPath = prefixPath[:strings.LastIndex(prefixPath, separator)+1]
|
||||||
}
|
}
|
||||||
var cErr error
|
var cErr error
|
||||||
isIncomplete := false
|
|
||||||
for content := range clnt.List(isRecursive, isIncomplete, DirNone) {
|
for content := range clnt.List(isRecursive, isIncomplete, DirNone) {
|
||||||
if content.Err != nil {
|
if content.Err != nil {
|
||||||
switch content.Err.ToGoError().(type) {
|
switch content.Err.ToGoError().(type) {
|
||||||
@ -147,6 +154,11 @@ func doStat(clnt Client, isRecursive bool, targetAlias, targetURL string, encKey
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
url := targetAlias + getKey(content)
|
url := targetAlias + getKey(content)
|
||||||
|
|
||||||
|
if !isRecursive && url != targetURL {
|
||||||
|
return nil, errTargetNotFound(targetURL)
|
||||||
|
}
|
||||||
|
|
||||||
_, stat, err := url2Stat(url, true, encKeyDB)
|
_, stat, err := url2Stat(url, true, encKeyDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stat = content
|
stat = content
|
||||||
@ -157,12 +169,8 @@ func doStat(clnt Client, isRecursive bool, targetAlias, targetURL string, encKey
|
|||||||
// Trim prefix path from the content path.
|
// Trim prefix path from the content path.
|
||||||
contentURL = strings.TrimPrefix(contentURL, prefixPath)
|
contentURL = strings.TrimPrefix(contentURL, prefixPath)
|
||||||
stat.URL.Path = contentURL
|
stat.URL.Path = contentURL
|
||||||
st := parseStat(targetAlias, stat)
|
stats = append(stats, stat)
|
||||||
if !globalJSON {
|
|
||||||
printStat(st)
|
|
||||||
} else {
|
|
||||||
console.Println(st.JSON())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return cErr
|
|
||||||
|
return stats, probe.NewError(cErr)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func (s *TestSuite) TestParseStat(c *C) {
|
|||||||
"play"},
|
"play"},
|
||||||
}
|
}
|
||||||
for _, testCase := range testCases {
|
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.Metadata, DeepEquals, statMsg.Metadata)
|
||||||
c.Assert(testCase.content.EncryptionHeaders, DeepEquals, statMsg.EncryptionHeaders)
|
c.Assert(testCase.content.EncryptionHeaders, DeepEquals, statMsg.EncryptionHeaders)
|
||||||
c.Assert(testCase.content.Size, Equals, statMsg.Size)
|
c.Assert(testCase.content.Size, Equals, statMsg.Size)
|
||||||
|
@ -96,6 +96,13 @@ var errInvalidTarget = func(URL string) *probe.Error {
|
|||||||
return probe.NewError(invalidTargetErr(errors.New(msg))).Untrace()
|
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 {
|
type overwriteNotAllowedErr struct {
|
||||||
error
|
error
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user