mirror of
https://github.com/minio/mc.git
synced 2025-11-10 13:42:32 +03:00
This patch makes rm command to use minio-go.RemoveObjects() to removeobjects recursively.
247 lines
8.5 KiB
Go
247 lines
8.5 KiB
Go
/*
|
|
* Minio Client (C) 2015 Minio, Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/minio/minio/pkg/probe"
|
|
)
|
|
|
|
type copyURLsType uint8
|
|
|
|
// NOTE: All the parse rules should reduced to A: Copy(Source, Target).
|
|
//
|
|
// * VALID RULES
|
|
// =======================
|
|
// A: copy(f, f) -> copy(f, f)
|
|
// B: copy(f, d) -> copy(f, d/f) -> []A
|
|
// C: copy(d1..., d2) -> []copy(f, d2/d1/f) -> []A
|
|
// D: copy([]f, d) -> []B
|
|
|
|
// * INVALID RULES
|
|
// =========================
|
|
// copy(d, f)
|
|
// copy(d..., f)
|
|
// copy([](f|d)..., f)
|
|
|
|
const (
|
|
copyURLsTypeInvalid copyURLsType = iota
|
|
copyURLsTypeA
|
|
copyURLsTypeB
|
|
copyURLsTypeC
|
|
copyURLsTypeD
|
|
)
|
|
|
|
// guessCopyURLType guesses the type of clientURL. This approach all allows prepareURL
|
|
// functions to accurately report failure causes.
|
|
func guessCopyURLType(sourceURLs []string, targetURL string, isRecursive bool) (copyURLsType, *probe.Error) {
|
|
if len(sourceURLs) == 1 { // 1 Source, 1 Target
|
|
sourceURL := sourceURLs[0]
|
|
_, sourceContent, err := url2Stat(sourceURL)
|
|
if err != nil {
|
|
return copyURLsTypeInvalid, err
|
|
}
|
|
|
|
// If recursion is ON, it is type C.
|
|
// If source is a folder, it is Type C.
|
|
if sourceContent.Type.IsDir() || isRecursive {
|
|
return copyURLsTypeC, nil
|
|
}
|
|
|
|
// If target is a folder, it is Type B.
|
|
if isTargetURLDir(targetURL) {
|
|
return copyURLsTypeB, nil
|
|
}
|
|
// else Type A.
|
|
return copyURLsTypeA, nil
|
|
}
|
|
|
|
// Multiple source args and target is a folder. It is Type D.
|
|
if isTargetURLDir(targetURL) {
|
|
return copyURLsTypeD, nil
|
|
}
|
|
|
|
return copyURLsTypeInvalid, errInvalidArgument().Trace()
|
|
}
|
|
|
|
// SINGLE SOURCE - Type A: copy(f, f) -> copy(f, f)
|
|
// prepareCopyURLsTypeA - prepares target and source clientURLs for copying.
|
|
func prepareCopyURLsTypeA(sourceURL string, targetURL string) URLs {
|
|
// Extract alias before fiddling with the clientURL.
|
|
sourceAlias, _, _ := mustExpandAlias(sourceURL)
|
|
// Find alias and expanded clientURL.
|
|
targetAlias, targetURL, _ := mustExpandAlias(targetURL)
|
|
|
|
_, sourceContent, err := url2Stat(sourceURL)
|
|
if err != nil {
|
|
// Source does not exist or insufficient privileges.
|
|
return URLs{Error: err.Trace(sourceURL)}
|
|
}
|
|
if !sourceContent.Type.IsRegular() {
|
|
// Source is not a regular file
|
|
return URLs{Error: errInvalidSource(sourceURL).Trace(sourceURL)}
|
|
}
|
|
if sourceContent.URL.String() == targetURL {
|
|
// source and target can not be same
|
|
return URLs{Error: errSourceTargetSame(sourceURL).Trace(sourceURL)}
|
|
}
|
|
|
|
// All OK.. We can proceed. Type A
|
|
return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, targetURL)
|
|
}
|
|
|
|
// prepareCopyContentTypeA - makes CopyURLs content for copying.
|
|
func makeCopyContentTypeA(sourceAlias string, sourceContent *clientContent, targetAlias string, targetURL string) URLs {
|
|
return URLs{
|
|
SourceAlias: sourceAlias,
|
|
SourceContent: sourceContent,
|
|
TargetAlias: targetAlias,
|
|
TargetContent: &clientContent{URL: *newClientURL(targetURL)},
|
|
}
|
|
}
|
|
|
|
// SINGLE SOURCE - Type B: copy(f, d) -> copy(f, d/f) -> A
|
|
// prepareCopyURLsTypeB - prepares target and source clientURLs for copying.
|
|
func prepareCopyURLsTypeB(sourceURL string, targetURL string) URLs {
|
|
// Extract alias before fiddling with the clientURL.
|
|
sourceAlias, _, _ := mustExpandAlias(sourceURL)
|
|
// Find alias and expanded clientURL.
|
|
targetAlias, targetURL, _ := mustExpandAlias(targetURL)
|
|
|
|
_, sourceContent, err := url2Stat(sourceURL)
|
|
if err != nil {
|
|
// Source does not exist or insufficient privileges.
|
|
return URLs{Error: err.Trace(sourceURL)}
|
|
}
|
|
|
|
if !sourceContent.Type.IsRegular() {
|
|
if sourceContent.Type.IsDir() {
|
|
return URLs{Error: errSourceIsDir(sourceURL).Trace(sourceURL)}
|
|
}
|
|
// Source is not a regular file.
|
|
return URLs{Error: errInvalidSource(sourceURL).Trace(sourceURL)}
|
|
}
|
|
|
|
// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
|
|
return makeCopyContentTypeB(sourceAlias, sourceContent, targetAlias, targetURL)
|
|
}
|
|
|
|
// makeCopyContentTypeB - CopyURLs content for copying.
|
|
func makeCopyContentTypeB(sourceAlias string, sourceContent *clientContent, targetAlias string, targetURL string) URLs {
|
|
// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
|
|
targetURLParse := newClientURL(targetURL)
|
|
targetURLParse.Path = filepath.ToSlash(filepath.Join(targetURLParse.Path, filepath.Base(sourceContent.URL.Path)))
|
|
return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, targetURLParse.String())
|
|
}
|
|
|
|
// SINGLE SOURCE - Type C: copy(d1..., d2) -> []copy(d1/f, d1/d2/f) -> []A
|
|
// prepareCopyRecursiveURLTypeC - prepares target and source clientURLs for copying.
|
|
func prepareCopyURLsTypeC(sourceURL, targetURL string, isRecursive bool) <-chan URLs {
|
|
// Extract alias before fiddling with the clientURL.
|
|
sourceAlias, _, _ := mustExpandAlias(sourceURL)
|
|
// Find alias and expanded clientURL.
|
|
targetAlias, targetURL, _ := mustExpandAlias(targetURL)
|
|
|
|
copyURLsCh := make(chan URLs)
|
|
go func(sourceURL, targetURL string, copyURLsCh chan URLs) {
|
|
defer close(copyURLsCh)
|
|
sourceClient, err := newClient(sourceURL)
|
|
if err != nil {
|
|
// Source initialization failed.
|
|
copyURLsCh <- URLs{Error: err.Trace(sourceURL)}
|
|
return
|
|
}
|
|
|
|
isIncomplete := false
|
|
for sourceContent := range sourceClient.List(isRecursive, isIncomplete, DirNone) {
|
|
if sourceContent.Err != nil {
|
|
// Listing failed.
|
|
copyURLsCh <- URLs{Error: sourceContent.Err.Trace(sourceClient.GetURL().String())}
|
|
continue
|
|
}
|
|
|
|
if !sourceContent.Type.IsRegular() {
|
|
// Source is not a regular file. Skip it for copy.
|
|
continue
|
|
}
|
|
|
|
// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
|
|
copyURLsCh <- makeCopyContentTypeC(sourceAlias, sourceClient.GetURL(), sourceContent, targetAlias, targetURL)
|
|
}
|
|
}(sourceURL, targetURL, copyURLsCh)
|
|
return copyURLsCh
|
|
}
|
|
|
|
// makeCopyContentTypeC - CopyURLs content for copying.
|
|
func makeCopyContentTypeC(sourceAlias string, sourceURL clientURL, sourceContent *clientContent, targetAlias string, targetURL string) URLs {
|
|
newSourceURL := sourceContent.URL
|
|
pathSeparatorIndex := strings.LastIndex(sourceURL.Path, string(sourceURL.Separator))
|
|
newSourceSuffix := filepath.ToSlash(newSourceURL.Path)
|
|
if pathSeparatorIndex > 1 {
|
|
sourcePrefix := filepath.ToSlash(sourceURL.Path[:pathSeparatorIndex])
|
|
newSourceSuffix = strings.TrimPrefix(newSourceSuffix, sourcePrefix)
|
|
}
|
|
newTargetURL := urlJoinPath(targetURL, newSourceSuffix)
|
|
return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, newTargetURL)
|
|
}
|
|
|
|
// MULTI-SOURCE - Type D: copy([](f|d...), d) -> []B
|
|
// prepareCopyURLsTypeE - prepares target and source clientURLs for copying.
|
|
func prepareCopyURLsTypeD(sourceURLs []string, targetURL string, isRecursive bool) <-chan URLs {
|
|
copyURLsCh := make(chan URLs)
|
|
go func(sourceURLs []string, targetURL string, copyURLsCh chan URLs) {
|
|
defer close(copyURLsCh)
|
|
for _, sourceURL := range sourceURLs {
|
|
for cpURLs := range prepareCopyURLsTypeC(sourceURL, targetURL, isRecursive) {
|
|
copyURLsCh <- cpURLs
|
|
}
|
|
}
|
|
}(sourceURLs, targetURL, copyURLsCh)
|
|
return copyURLsCh
|
|
}
|
|
|
|
// prepareCopyURLs - prepares target and source clientURLs for copying.
|
|
func prepareCopyURLs(sourceURLs []string, targetURL string, isRecursive bool) <-chan URLs {
|
|
copyURLsCh := make(chan URLs)
|
|
go func(sourceURLs []string, targetURL string, copyURLsCh chan URLs) {
|
|
defer close(copyURLsCh)
|
|
cpType, err := guessCopyURLType(sourceURLs, targetURL, isRecursive)
|
|
fatalIf(err.Trace(), "Unable to guess the type of copy operation.")
|
|
|
|
switch cpType {
|
|
case copyURLsTypeA:
|
|
copyURLsCh <- prepareCopyURLsTypeA(sourceURLs[0], targetURL)
|
|
case copyURLsTypeB:
|
|
copyURLsCh <- prepareCopyURLsTypeB(sourceURLs[0], targetURL)
|
|
case copyURLsTypeC:
|
|
for cURLs := range prepareCopyURLsTypeC(sourceURLs[0], targetURL, isRecursive) {
|
|
copyURLsCh <- cURLs
|
|
}
|
|
case copyURLsTypeD:
|
|
for cURLs := range prepareCopyURLsTypeD(sourceURLs, targetURL, isRecursive) {
|
|
copyURLsCh <- cURLs
|
|
}
|
|
default:
|
|
copyURLsCh <- URLs{Error: errInvalidArgument().Trace(sourceURLs...)}
|
|
}
|
|
}(sourceURLs, targetURL, copyURLsCh)
|
|
|
|
return copyURLsCh
|
|
}
|