github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/put-url.go (about) 1 // Copyright (c) 2015-2024 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 "strings" 24 25 "github.com/minio/mc/pkg/probe" 26 "github.com/minio/minio-go/v7" 27 ) 28 29 // preparePutURLs - prepares target and source clientURLs for copying. 30 func preparePutURLs(ctx context.Context, o prepareCopyURLsOpts) chan URLs { 31 copyURLsCh := make(chan URLs) 32 go func(o prepareCopyURLsOpts) { 33 defer close(copyURLsCh) 34 copyURLsContent, err := guessPutURLType(ctx, o) 35 if err != nil { 36 copyURLsCh <- URLs{Error: err} 37 return 38 } 39 40 switch copyURLsContent.copyType { 41 case copyURLsTypeA: 42 copyURLsCh <- prepareCopyURLsTypeA(ctx, *copyURLsContent, o) 43 case copyURLsTypeB: 44 copyURLsCh <- prepareCopyURLsTypeB(ctx, *copyURLsContent, o) 45 default: 46 copyURLsCh <- URLs{Error: errInvalidArgument().Trace(o.sourceURLs...)} 47 } 48 }(o) 49 50 finalCopyURLsCh := make(chan URLs) 51 go func() { 52 defer close(finalCopyURLsCh) 53 for cpURLs := range copyURLsCh { 54 if cpURLs.Error != nil { 55 finalCopyURLsCh <- cpURLs 56 continue 57 } 58 finalCopyURLsCh <- cpURLs 59 } 60 }() 61 62 return finalCopyURLsCh 63 } 64 65 // guessPutURLType guesses the type of clientURL. This approach all allows prepareURL 66 // functions to accurately report failure causes. 67 func guessPutURLType(ctx context.Context, o prepareCopyURLsOpts) (*copyURLsContent, *probe.Error) { 68 cc := new(copyURLsContent) 69 70 // Extract alias before fiddling with the clientURL. 71 cc.sourceURL = o.sourceURLs[0] 72 cc.sourceAlias, _, _ = mustExpandAlias(cc.sourceURL) 73 // Find alias and expanded clientURL. 74 cc.targetAlias, cc.targetURL, _ = mustExpandAlias(o.targetURL) 75 76 if len(o.sourceURLs) == 1 { // 1 Source, 1 Target 77 var err *probe.Error 78 var client Client 79 client, cc.sourceContent, err = url2Stat(ctx, url2StatOptions{urlStr: cc.sourceURL, versionID: o.versionID, fileAttr: false, encKeyDB: o.encKeyDB, timeRef: o.timeRef, isZip: o.isZip, ignoreBucketExistsCheck: false}) 80 if err != nil { 81 cc.copyType = copyURLsTypeInvalid 82 return cc, err 83 } 84 _, ok := client.(*fsClient) 85 if !ok { 86 cc.copyType = copyURLsTypeInvalid 87 return cc, probe.NewError(fmt.Errorf("Source is not local filepath.")) 88 } 89 // If recursion is ON, it is type C. 90 // If source is a folder, it is Type C. 91 if cc.sourceContent.Type.IsDir() { 92 cc.copyType = copyURLsTypeC 93 return cc, nil 94 } 95 client, err = newClient(o.targetURL) 96 if err != nil { 97 cc.copyType = copyURLsTypeInvalid 98 return cc, err 99 } 100 s3clnt, ok := client.(*S3Client) 101 if !ok { 102 cc.copyType = copyURLsTypeInvalid 103 return cc, probe.NewError(fmt.Errorf("Target is not s3.")) 104 } 105 bucket, path := s3clnt.url2BucketAndObject() 106 if bucket == "" { 107 cc.copyType = copyURLsTypeInvalid 108 return cc, probe.NewError(fmt.Errorf("Bucket should not be empty.")) 109 } 110 cc.targetContent = s3clnt.objectInfo2ClientContent(bucket, minio.ObjectInfo{ 111 Key: bucket, 112 }) 113 // If target is a folder, it is Type B. 114 var isDir bool 115 if path == "" { 116 isDir = true 117 } else { 118 isDir = strings.HasSuffix(path, string(cc.targetContent.URL.Separator)) 119 } 120 121 if isDir { 122 cc.copyType = copyURLsTypeB 123 cc.sourceVersionID = cc.sourceContent.VersionID 124 return cc, nil 125 } 126 127 // else Type A. 128 cc.copyType = copyURLsTypeA 129 cc.sourceVersionID = cc.sourceContent.VersionID 130 return cc, nil 131 } 132 133 cc.copyType = copyURLsTypeInvalid 134 return cc, errInvalidArgument().Trace() 135 }