storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/gateway/s3/gateway-s3-metadata.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package s3 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "errors" 24 "net/http" 25 "time" 26 27 jsoniter "github.com/json-iterator/go" 28 29 minio "storj.io/minio/cmd" 30 "storj.io/minio/cmd/logger" 31 "storj.io/minio/pkg/hash" 32 ) 33 34 var ( 35 errGWMetaNotFound = errors.New("dare.meta file not found") 36 errGWMetaInvalidFormat = errors.New("dare.meta format is invalid") 37 ) 38 39 // A gwMetaV1 represents `gw.json` metadata header. 40 type gwMetaV1 struct { 41 Version string `json:"version"` // Version of the current `gw.json`. 42 Format string `json:"format"` // Format of the current `gw.json`. 43 Stat minio.StatInfo `json:"stat"` // Stat of the current object `gw.json`. 44 ETag string `json:"etag"` // ETag of the current object 45 46 // Metadata map for current object `gw.json`. 47 Meta map[string]string `json:"meta,omitempty"` 48 // Captures all the individual object `gw.json`. 49 Parts []minio.ObjectPartInfo `json:"parts,omitempty"` 50 } 51 52 // Gateway metadata constants. 53 const ( 54 // Gateway meta version. 55 gwMetaVersion = "1.0.0" 56 57 // Gateway meta version. 58 gwMetaVersion100 = "1.0.0" 59 60 // Gateway meta format string. 61 gwMetaFormat = "gw" 62 63 // Add new constants here. 64 ) 65 66 // newGWMetaV1 - initializes new gwMetaV1, adds version. 67 func newGWMetaV1() (gwMeta gwMetaV1) { 68 gwMeta = gwMetaV1{} 69 gwMeta.Version = gwMetaVersion 70 gwMeta.Format = gwMetaFormat 71 return gwMeta 72 } 73 74 // IsValid - tells if the format is sane by validating the version 75 // string, format fields. 76 func (m gwMetaV1) IsValid() bool { 77 return ((m.Version == gwMetaVersion || m.Version == gwMetaVersion100) && 78 m.Format == gwMetaFormat) 79 } 80 81 // Converts metadata to object info. 82 func (m gwMetaV1) ToObjectInfo(bucket, object string) minio.ObjectInfo { 83 filterKeys := append([]string{ 84 "ETag", 85 "Content-Length", 86 "Last-Modified", 87 "Content-Type", 88 "Expires", 89 }, defaultFilterKeys...) 90 objInfo := minio.ObjectInfo{ 91 IsDir: false, 92 Bucket: bucket, 93 Name: object, 94 Size: m.Stat.Size, 95 ModTime: m.Stat.ModTime, 96 ContentType: m.Meta["content-type"], 97 ContentEncoding: m.Meta["content-encoding"], 98 ETag: minio.CanonicalizeETag(m.ETag), 99 UserDefined: minio.CleanMinioInternalMetadataKeys(minio.CleanMetadataKeys(m.Meta, filterKeys...)), 100 Parts: m.Parts, 101 } 102 103 if sc, ok := m.Meta["x-amz-storage-class"]; ok { 104 objInfo.StorageClass = sc 105 } 106 var ( 107 t time.Time 108 e error 109 ) 110 if exp, ok := m.Meta["expires"]; ok { 111 if t, e = time.Parse(http.TimeFormat, exp); e == nil { 112 objInfo.Expires = t.UTC() 113 } 114 } 115 // Success. 116 return objInfo 117 } 118 119 // ObjectToPartOffset - translate offset of an object to offset of its individual part. 120 func (m gwMetaV1) ObjectToPartOffset(ctx context.Context, offset int64) (partIndex int, partOffset int64, err error) { 121 if offset == 0 { 122 // Special case - if offset is 0, then partIndex and partOffset are always 0. 123 return 0, 0, nil 124 } 125 partOffset = offset 126 // Seek until object offset maps to a particular part offset. 127 for i, part := range m.Parts { 128 partIndex = i 129 // Offset is smaller than size we have reached the proper part offset. 130 if partOffset < part.Size { 131 return partIndex, partOffset, nil 132 } 133 // Continue to towards the next part. 134 partOffset -= part.Size 135 } 136 logger.LogIf(ctx, minio.InvalidRange{}) 137 // Offset beyond the size of the object return InvalidRange. 138 return 0, 0, minio.InvalidRange{} 139 } 140 141 // Constructs GWMetaV1 using `jsoniter` lib to retrieve each field. 142 func gwMetaUnmarshalJSON(ctx context.Context, gwMetaBuf []byte) (gwMeta gwMetaV1, err error) { 143 var json = jsoniter.ConfigCompatibleWithStandardLibrary 144 err = json.Unmarshal(gwMetaBuf, &gwMeta) 145 return gwMeta, err 146 } 147 148 // readGWMeta reads `dare.meta` and returns back GW metadata structure. 149 func readGWMetadata(ctx context.Context, buf bytes.Buffer) (gwMeta gwMetaV1, err error) { 150 if buf.Len() == 0 { 151 return gwMetaV1{}, errGWMetaNotFound 152 } 153 gwMeta, err = gwMetaUnmarshalJSON(ctx, buf.Bytes()) 154 if err != nil { 155 return gwMetaV1{}, err 156 } 157 if !gwMeta.IsValid() { 158 return gwMetaV1{}, errGWMetaInvalidFormat 159 } 160 // Return structured `dare.meta`. 161 return gwMeta, nil 162 } 163 164 // getGWMetadata - unmarshals dare.meta into a *minio.PutObjReader 165 func getGWMetadata(ctx context.Context, bucket, prefix string, gwMeta gwMetaV1) (*minio.PutObjReader, error) { 166 // Marshal json. 167 metadataBytes, err := json.Marshal(&gwMeta) 168 if err != nil { 169 logger.LogIf(ctx, err) 170 return nil, err 171 } 172 hashReader, err := hash.NewReader(bytes.NewReader(metadataBytes), int64(len(metadataBytes)), "", "", int64(len(metadataBytes))) 173 if err != nil { 174 return nil, err 175 } 176 return minio.NewPutObjReader(hashReader), nil 177 }