1
0
mirror of https://github.com/minio/mc.git synced 2025-11-12 01:02:26 +03:00
Files
mc/remove-main.go
2015-10-19 15:11:59 -07:00

202 lines
5.4 KiB
Go

/*
* Minio Client (C) 2014, 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 main
import (
"os"
"strings"
"github.com/minio/cli"
"github.com/minio/mc/pkg/client"
"github.com/minio/minio-xl/pkg/probe"
)
// remove a file or folder.
var rmCmd = cli.Command{
Name: "rm",
Usage: "Remove file or bucket.",
Action: mainRm,
CustomHelpTemplate: `NAME:
mc {{.Name}} - {{.Usage}}
USAGE:
mc {{.Name}} TARGET
EXAMPLES:
1. Remove a file on Cloud storage
$ mc {{.Name}} https://s3.amazonaws.com/jazz-songs/louis/file01.mp3
2. Remove a folder recursively on Cloud storage
$ mc {{.Name}} force https://s3.amazonaws.com/jazz-songs/louis/...
3. Remove a bucket on Minio cloud storage
$ mc {{.Name}} https://play.minio.io:9000/mongodb-backup
4. Remove a bucket on Cloud storage recursively
$ mc {{.Name}} force https://s3.amazonaws.com/jazz-songs/...
5. Remove a file on local filesystem:
$ mc {{.Name}} march/expenses.doc
6. Remove a file named "force" on local filesystem:
$ mc {{.Name}} force force
`,
}
func rmList(url string) (<-chan string, *probe.Error) {
clnt, err := url2Client(url)
if err != nil {
errorIf(err.Trace(), "Unable to get client object for "+url)
return nil, err.Trace()
}
in := clnt.List(true)
out := make(chan string)
var depthFirst func(currentDir string) (*client.Content, bool)
depthFirst = func(currentDir string) (*client.Content, bool) {
entry, ok := <-in
for {
if !ok || !strings.HasPrefix(entry.Content.Name, currentDir) {
return entry.Content, ok
}
if entry.Content.Type.IsRegular() {
out <- entry.Content.Name
}
if entry.Content.Type.IsDir() {
var content *client.Content
content, ok = depthFirst(entry.Content.Name)
out <- entry.Content.Name
entry = client.ContentOnChannel{Content: content}
continue
}
entry, ok = <-in
}
}
go func() {
depthFirst("")
close(out)
}()
return out, nil
}
func rm(url string) {
clnt, err := url2Client(url)
if err != nil {
errorIf(err.Trace(), "Unable to get client object for "+url)
return
}
err = clnt.Remove()
errorIf(err.Trace(), "Unable to remove "+url)
}
func rmAll(url string) {
clnt, err := url2Client(url)
if err != nil {
errorIf(err.Trace(), "Unable to get client object for "+url)
return
}
urlPartial1 := url2Dir(url)
out, err := rmList(url)
if err != nil {
errorIf(err.Trace(), "Unable to List "+url)
return
}
for urlPartial2 := range out {
urlFull := urlPartial1 + urlPartial2
newclnt, e := url2Client(urlFull)
if e != nil {
errorIf(e, "Unable to create client object : "+urlFull)
continue
}
err = newclnt.Remove()
errorIf(err, "Unable to remove : "+urlFull)
}
_, err = clnt.Stat()
if err == nil {
err = clnt.Remove()
errorIf(err, "Unable to remove : "+clnt.URL().String())
}
}
func checkRmSyntax(ctx *cli.Context) {
args, err := args2URLs(ctx.Args())
fatalIf(err.Trace(), "args2URL failed")
var force bool
if len(args) == 0 {
cli.ShowCommandHelpAndExit(ctx, "rm", 1) // last argument is exit code.
}
if len(args) == 1 && args[0] == "force" {
cli.ShowCommandHelpAndExit(ctx, "rm", 1)
}
if args[0] == "force" {
force = true
args = args[1:]
}
// If input validation fails then provide context sensitive help without displaying generic help message.
// The context sensitive help is shown per argument instead of all arguments to keep the help display
// as well as the code simple. Also most of the times there will be just one arg
for _, arg := range args {
url := client.NewURL(arg)
if strings.HasSuffix(arg, string(url.Separator)) {
helpStr := "Usage : mc rm force " + arg + recursiveSeparator
fatalIf(errDummy().Trace(), helpStr)
}
if isURLRecursive(arg) && !force {
helpStr := "Usage : mc rm force " + arg
fatalIf(errDummy().Trace(), helpStr)
}
if url.Type == client.Filesystem {
// For local file system we don't support "mc rm fileprefix..." just like the behavior of "mc ls fileprefix..."
// So recursive delete has to be of the form "mc rm dir1/dir2/..."
isRecursive := isURLRecursive(arg)
path := stripRecursiveURL(arg)
if isRecursive && (strings.HasSuffix(path, string(url.Separator)) == false) {
helpStr := "Usage : mc rm force " + path + string(url.Separator) + recursiveSeparator
fatalIf(errDummy().Trace(), helpStr)
}
_, content, err := url2Stat(path)
if err != nil {
fatalIf(err.Trace(), "url2stat error on "+arg)
}
if content.Type&os.ModeDir != 0 && !isRecursive {
helpStr := "Usage : mc rm force " + arg + string(url.Separator) + recursiveSeparator
fatalIf(errDummy().Trace(), helpStr)
}
continue
}
}
}
func mainRm(ctx *cli.Context) {
checkRmSyntax(ctx)
args, err := args2URLs(ctx.Args())
fatalIf(err.Trace(), "args2URL failed")
if args[0] == "force" {
args = args[1:]
}
for _, arg := range args {
if isURLRecursive(arg) {
url := stripRecursiveURL(arg)
rmAll(url)
} else {
rm(arg)
}
}
}