github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/post-policy-fan-out.go (about) 1 // Copyright (c) 2015-2023 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 "bytes" 22 "context" 23 "sync" 24 25 "github.com/minio/minio-go/v7" 26 "github.com/minio/minio-go/v7/pkg/s3utils" 27 "github.com/minio/minio/internal/crypto" 28 "github.com/minio/minio/internal/hash" 29 xhttp "github.com/minio/minio/internal/http" 30 "github.com/minio/minio/internal/kms" 31 ) 32 33 type fanOutOptions struct { 34 Kind crypto.Type 35 KeyID string 36 Key []byte 37 KmsCtx kms.Context 38 Checksum *hash.Checksum 39 MD5Hex string 40 } 41 42 // fanOutPutObject takes an input source reader and fans out multiple PUT operations 43 // based on the incoming fan-out request, a context cancellation by the caller 44 // would ensure all fan-out operations are canceled. 45 func fanOutPutObject(ctx context.Context, bucket string, objectAPI ObjectLayer, fanOutEntries []minio.PutObjectFanOutEntry, fanOutBuf []byte, opts fanOutOptions) ([]ObjectInfo, []error) { 46 errs := make([]error, len(fanOutEntries)) 47 objInfos := make([]ObjectInfo, len(fanOutEntries)) 48 49 var wg sync.WaitGroup 50 for i, req := range fanOutEntries { 51 wg.Add(1) 52 go func(idx int, req minio.PutObjectFanOutEntry) { 53 defer wg.Done() 54 55 objInfos[idx] = ObjectInfo{Name: req.Key} 56 57 hopts := hash.Options{ 58 Size: int64(len(fanOutBuf)), 59 MD5Hex: opts.MD5Hex, 60 SHA256Hex: "", 61 ActualSize: -1, 62 DisableMD5: true, 63 } 64 hr, err := hash.NewReaderWithOpts(ctx, bytes.NewReader(fanOutBuf), hopts) 65 if err != nil { 66 errs[idx] = err 67 return 68 } 69 70 reader := NewPutObjReader(hr) 71 defer func() { 72 if err := reader.Close(); err != nil { 73 errs[idx] = err 74 } 75 if err := hr.Close(); err != nil { 76 errs[idx] = err 77 } 78 }() 79 80 userDefined := make(map[string]string, len(req.UserMetadata)) 81 for k, v := range req.UserMetadata { 82 userDefined[k] = v 83 } 84 userDefined[xhttp.AmzObjectTagging] = s3utils.TagEncode(req.UserTags) 85 86 if opts.Kind != nil { 87 encrd, objectEncryptionKey, err := newEncryptReader(ctx, hr, opts.Kind, opts.KeyID, opts.Key, bucket, req.Key, userDefined, opts.KmsCtx) 88 if err != nil { 89 errs[idx] = err 90 return 91 } 92 93 // do not try to verify encrypted content/ 94 hr, err = hash.NewReader(ctx, encrd, -1, "", "", -1) 95 if err != nil { 96 errs[idx] = err 97 return 98 } 99 100 reader, err = reader.WithEncryption(hr, &objectEncryptionKey) 101 if err != nil { 102 errs[idx] = err 103 return 104 } 105 } 106 107 objInfo, err := objectAPI.PutObject(ctx, bucket, req.Key, reader, ObjectOptions{ 108 Versioned: globalBucketVersioningSys.PrefixEnabled(bucket, req.Key), 109 VersionSuspended: globalBucketVersioningSys.PrefixSuspended(bucket, req.Key), 110 UserDefined: userDefined, 111 }) 112 if err != nil { 113 errs[idx] = err 114 return 115 } 116 objInfos[idx] = objInfo 117 }(i, req) 118 } 119 wg.Wait() 120 121 return objInfos, errs 122 }