github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/mv-main.go (about) 1 // Copyright (c) 2015-2022 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "context" 22 "fmt" 23 "sync" 24 25 "github.com/fatih/color" 26 "github.com/minio/cli" 27 "github.com/minio/mc/pkg/probe" 28 "github.com/minio/pkg/v2/console" 29 ) 30 31 // mv command flags. 32 var ( 33 mvFlags = []cli.Flag{ 34 cli.BoolFlag{ 35 Name: "recursive, r", 36 Usage: "move recursively", 37 }, 38 cli.StringFlag{ 39 Name: "older-than", 40 Usage: "move objects older than value in duration string (e.g. 7d10h31s)", 41 }, 42 cli.StringFlag{ 43 Name: "newer-than", 44 Usage: "move objects newer than value in duration string (e.g. 7d10h31s)", 45 }, 46 cli.StringFlag{ 47 Name: "storage-class, sc", 48 Usage: "set storage class for new object(s) on target", 49 }, 50 cli.StringFlag{ 51 Name: "attr", 52 Usage: "add custom metadata for the object", 53 }, 54 cli.BoolFlag{ 55 Name: "preserve, a", 56 Usage: "preserve filesystem attributes (mode, ownership, timestamps)", 57 }, 58 cli.BoolFlag{ 59 Name: "disable-multipart", 60 Usage: "disable multipart upload feature", 61 }, 62 } 63 ) 64 65 // Move command. 66 var mvCmd = cli.Command{ 67 Name: "mv", 68 Usage: "move objects", 69 Action: mainMove, 70 OnUsageError: onUsageError, 71 Before: setGlobalsFromContext, 72 Flags: append(append(mvFlags, encFlags...), globalFlags...), 73 CustomHelpTemplate: `NAME: 74 {{.HelpName}} - {{.Usage}} 75 76 USAGE: 77 {{.HelpName}} [FLAGS] SOURCE [SOURCE...] TARGET 78 79 FLAGS: 80 {{range .VisibleFlags}}{{.}} 81 {{end}} 82 83 ENVIRONMENT VARIABLES: 84 MC_ENC_KMS: KMS encryption key in the form of (alias/prefix=key). 85 MC_ENC_S3: S3 encryption key in the form of (alias/prefix=key). 86 87 EXAMPLES: 88 01. Move a list of objects from local file system to Amazon S3 cloud storage. 89 {{.Prompt}} {{.HelpName}} Music/*.ogg s3/jukebox/ 90 91 02. Move a folder recursively from MinIO cloud storage to Amazon S3 cloud storage. 92 {{.Prompt}} {{.HelpName}} --recursive play/mybucket/ s3/mybucket/ 93 94 03. Move multiple local folders recursively to MinIO cloud storage. 95 {{.Prompt}} {{.HelpName}} --recursive backup/2014/ backup/2015/ play/archive/ 96 97 04. Move a bucket recursively from aliased Amazon S3 cloud storage to local filesystem on Windows. 98 {{.Prompt}} {{.HelpName}} --recursive s3\documents\2014\ C:\Backups\2014 99 100 05. Move files older than 7 days and 10 hours from MinIO cloud storage to Amazon S3 cloud storage. 101 {{.Prompt}} {{.HelpName}} --older-than 7d10h play/mybucket/myfolder/ s3/mybucket/ 102 103 06. Move files newer than 7 days and 10 hours from MinIO cloud storage to a local path. 104 {{.Prompt}} {{.HelpName}} --newer-than 7d10h play/mybucket/myfolder/ ~/latest/ 105 106 07. Move an object with name containing unicode characters to Amazon S3 cloud storage. 107 {{.Prompt}} {{.HelpName}} 本語 s3/andoria/ 108 109 08. Move a local folder with space separated characters to Amazon S3 cloud storage. 110 {{.Prompt}} {{.HelpName}} --recursive 'workdir/documents/May 2014/' s3/miniocloud 111 112 09. Move a list of objects from local file system to MinIO cloud storage with specified metadata, separated by ";" 113 {{.Prompt}} {{.HelpName}} --attr "key1=value1;key2=value2" Music/*.mp4 play/mybucket/ 114 115 10. Move a folder recursively from MinIO cloud storage to Amazon S3 cloud storage with Cache-Control and custom metadata, separated by ";". 116 {{.Prompt}} {{.HelpName}} --attr "Cache-Control=max-age=90000,min-fresh=9000;key1=value1;key2=value2" --recursive play/mybucket/myfolder/ s3/mybucket/ 117 118 11. Move a text file to an object storage and assign REDUCED_REDUNDANCY storage-class to the uploaded object. 119 {{.Prompt}} {{.HelpName}} --storage-class REDUCED_REDUNDANCY myobject.txt play/mybucket 120 121 12. Move a text file to an object storage and preserve the file system attribute as metadata. 122 {{.Prompt}} {{.HelpName}} -a myobject.txt play/mybucket 123 124 13. Move a text file to an object storage and disable multipart upload feature. 125 {{.Prompt}} {{.HelpName}} --disable-multipart myobject.txt play/mybucket 126 127 14. Move a folder using client provided encryption keys from Amazon S3 to MinIO cloud storage. 128 {{.Prompt}} {{.HelpName}} --r --enc-c "s3/documents/=MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MBB" --enc-c "myminio/documents/=MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDA" s3/documents/ myminio/documents/ 129 130 15. Move a folder using specific server managed encryption keys from Amazon S3 to MinIO cloud storage. 131 {{.Prompt}} {{.HelpName}} --r --enc-s3 "s3/documents/=my-s3-key" --enc-s3 "myminio/documents/=my-minio-key" s3/documents/ myminio/documents/ 132 `, 133 } 134 135 type removeClientInfo struct { 136 client Client 137 contentCh chan *ClientContent 138 resultCh <-chan RemoveResult 139 } 140 141 type removeManager struct { 142 removeMap map[string]*removeClientInfo 143 removeMapMutex sync.RWMutex 144 wg sync.WaitGroup 145 } 146 147 func (rm *removeManager) readErrors(resultCh <-chan RemoveResult, targetURL string) { 148 rm.wg.Add(1) 149 go func() { 150 defer rm.wg.Done() 151 for result := range resultCh { 152 if result.Err != nil { 153 errorIf(result.Err.Trace(targetURL), "Failed to remove in`"+targetURL+"`.") 154 } 155 } 156 }() 157 } 158 159 // This function should be parallel-safe because it is executed by ParallelManager 160 // If targetAlias is empty, it means we will target local FS contents 161 func (rm *removeManager) add(ctx context.Context, targetAlias, targetURL string) { 162 rm.removeMapMutex.Lock() 163 clientInfo := rm.removeMap[targetAlias] 164 if clientInfo == nil { 165 client, pErr := newClientFromAlias(targetAlias, targetURL) 166 if pErr != nil { 167 errorIf(pErr.Trace(targetURL), "Invalid argument `"+targetURL+"`.") 168 return 169 } 170 171 contentCh := make(chan *ClientContent, 10000) 172 resultCh := client.Remove(ctx, false, false, false, false, contentCh) 173 rm.readErrors(resultCh, targetURL) 174 175 clientInfo = &removeClientInfo{ 176 client: client, 177 contentCh: contentCh, 178 resultCh: resultCh, 179 } 180 181 rm.removeMap[targetAlias] = clientInfo 182 } 183 rm.removeMapMutex.Unlock() 184 185 clientInfo.contentCh <- &ClientContent{URL: *newClientURL(targetURL)} 186 } 187 188 func (rm *removeManager) close() { 189 for _, clientInfo := range rm.removeMap { 190 close(clientInfo.contentCh) 191 } 192 193 // Wait until all on-going client.Remove() operations to finish 194 rm.wg.Wait() 195 } 196 197 var rmManager = &removeManager{ 198 removeMap: make(map[string]*removeClientInfo), 199 } 200 201 // mainMove is the entry point for mv command. 202 func mainMove(cliCtx *cli.Context) error { 203 ctx, cancelMove := context.WithCancel(globalContext) 204 defer cancelMove() 205 206 checkCopySyntax(cliCtx) 207 console.SetColor("Copy", color.New(color.FgGreen, color.Bold)) 208 209 if cliCtx.NArg() == 2 { 210 args := cliCtx.Args() 211 srcURL := args.Get(0) 212 dstURL := args.Get(1) 213 if srcURL == dstURL { 214 fatalIf(errDummy().Trace(), fmt.Sprintf("Source and destination urls cannot be the same: %v.", srcURL)) 215 } 216 } 217 218 var err *probe.Error 219 220 encKeyDB, err := validateAndCreateEncryptionKeys(cliCtx) 221 fatalIf(err, "Unable to parse encryption keys.") 222 223 e := doCopySession(ctx, cancelMove, cliCtx, encKeyDB, true) 224 225 console.Colorize("Copy", "Waiting for move operations to complete") 226 rmManager.close() 227 228 return e 229 }