storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/fs-v1-metadata.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2016, 2017, 2017 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 cmd 18 19 import ( 20 "context" 21 "encoding/hex" 22 "encoding/json" 23 "io" 24 "io/ioutil" 25 "net/http" 26 "os" 27 pathutil "path" 28 "time" 29 30 jsoniter "github.com/json-iterator/go" 31 32 xhttp "storj.io/minio/cmd/http" 33 "storj.io/minio/cmd/logger" 34 "storj.io/minio/pkg/lock" 35 "storj.io/minio/pkg/mimedb" 36 ) 37 38 // FS format, and object metadata. 39 const ( 40 // fs.json object metadata. 41 fsMetaJSONFile = "fs.json" 42 ) 43 44 // FS metadata constants. 45 const ( 46 // FS backend meta 1.0.0 version. 47 fsMetaVersion100 = "1.0.0" 48 49 // FS backend meta 1.0.1 version. 50 fsMetaVersion101 = "1.0.1" 51 52 // FS backend meta 1.0.2 53 // Removed the fields "Format" and "MinIO" from fsMetaV1 as they were unused. Added "Checksum" field - to be used in future for bit-rot protection. 54 fsMetaVersion = "1.0.2" 55 56 // Add more constants here. 57 ) 58 59 // FSChecksumInfoV1 - carries checksums of individual blocks on disk. 60 type FSChecksumInfoV1 struct { 61 Algorithm string 62 Blocksize int64 63 Hashes [][]byte 64 } 65 66 // MarshalJSON marshals the FSChecksumInfoV1 struct 67 func (c FSChecksumInfoV1) MarshalJSON() ([]byte, error) { 68 type checksuminfo struct { 69 Algorithm string `json:"algorithm"` 70 Blocksize int64 `json:"blocksize"` 71 Hashes []string `json:"hashes"` 72 } 73 var hashes []string 74 for _, h := range c.Hashes { 75 hashes = append(hashes, hex.EncodeToString(h)) 76 } 77 info := checksuminfo{ 78 Algorithm: c.Algorithm, 79 Hashes: hashes, 80 Blocksize: c.Blocksize, 81 } 82 return json.Marshal(info) 83 } 84 85 // UnmarshalJSON unmarshals the the given data into the FSChecksumInfoV1 struct 86 func (c *FSChecksumInfoV1) UnmarshalJSON(data []byte) error { 87 type checksuminfo struct { 88 Algorithm string `json:"algorithm"` 89 Blocksize int64 `json:"blocksize"` 90 Hashes []string `json:"hashes"` 91 } 92 93 var info checksuminfo 94 var json = jsoniter.ConfigCompatibleWithStandardLibrary 95 err := json.Unmarshal(data, &info) 96 if err != nil { 97 return err 98 } 99 c.Algorithm = info.Algorithm 100 c.Blocksize = info.Blocksize 101 var hashes [][]byte 102 for _, hashStr := range info.Hashes { 103 h, err := hex.DecodeString(hashStr) 104 if err != nil { 105 return err 106 } 107 hashes = append(hashes, h) 108 } 109 c.Hashes = hashes 110 return nil 111 } 112 113 // A fsMetaV1 represents a metadata header mapping keys to sets of values. 114 type fsMetaV1 struct { 115 Version string `json:"version"` 116 // checksums of blocks on disk. 117 Checksum FSChecksumInfoV1 `json:"checksum,omitempty"` 118 // Metadata map for current object. 119 Meta map[string]string `json:"meta,omitempty"` 120 // parts info for current object - used in encryption. 121 Parts []ObjectPartInfo `json:"parts,omitempty"` 122 } 123 124 // IsValid - tells if the format is sane by validating the version 125 // string and format style. 126 func (m fsMetaV1) IsValid() bool { 127 return isFSMetaValid(m.Version) 128 } 129 130 // Verifies if the backend format metadata is same by validating 131 // the version string. 132 func isFSMetaValid(version string) bool { 133 return (version == fsMetaVersion || version == fsMetaVersion100 || version == fsMetaVersion101) 134 } 135 136 // Converts metadata to object info. 137 func (m fsMetaV1) ToObjectInfo(bucket, object string, fi os.FileInfo) ObjectInfo { 138 if len(m.Meta) == 0 { 139 m.Meta = make(map[string]string) 140 } 141 142 // Guess content-type from the extension if possible. 143 if m.Meta["content-type"] == "" { 144 m.Meta["content-type"] = mimedb.TypeByExtension(pathutil.Ext(object)) 145 } 146 147 if HasSuffix(object, SlashSeparator) { 148 m.Meta["etag"] = emptyETag // For directories etag is d41d8cd98f00b204e9800998ecf8427e 149 m.Meta["content-type"] = "application/octet-stream" 150 } 151 152 objInfo := ObjectInfo{ 153 Bucket: bucket, 154 Name: object, 155 } 156 157 // We set file info only if its valid. 158 objInfo.ModTime = timeSentinel 159 if fi != nil { 160 objInfo.ModTime = fi.ModTime() 161 objInfo.Size = fi.Size() 162 if fi.IsDir() { 163 // Directory is always 0 bytes in S3 API, treat it as such. 164 objInfo.Size = 0 165 objInfo.IsDir = fi.IsDir() 166 } 167 } 168 169 objInfo.ETag = extractETag(m.Meta) 170 objInfo.ContentType = m.Meta["content-type"] 171 objInfo.ContentEncoding = m.Meta["content-encoding"] 172 if storageClass, ok := m.Meta[xhttp.AmzStorageClass]; ok { 173 objInfo.StorageClass = storageClass 174 } else { 175 objInfo.StorageClass = globalMinioDefaultStorageClass 176 } 177 var ( 178 t time.Time 179 e error 180 ) 181 if exp, ok := m.Meta["expires"]; ok { 182 if t, e = time.Parse(http.TimeFormat, exp); e == nil { 183 objInfo.Expires = t.UTC() 184 } 185 } 186 187 // Add user tags to the object info 188 objInfo.UserTags = m.Meta[xhttp.AmzObjectTagging] 189 190 // etag/md5Sum has already been extracted. We need to 191 // remove to avoid it from appearing as part of 192 // response headers. e.g, X-Minio-* or X-Amz-*. 193 // Tags have also been extracted, we remove that as well. 194 objInfo.UserDefined = cleanMetadata(m.Meta) 195 196 // All the parts per object. 197 objInfo.Parts = m.Parts 198 199 // Success.. 200 return objInfo 201 } 202 203 func (m *fsMetaV1) WriteTo(lk *lock.LockedFile) (n int64, err error) { 204 if err = jsonSave(lk, m); err != nil { 205 return 0, err 206 } 207 fi, err := lk.Stat() 208 if err != nil { 209 return 0, err 210 } 211 return fi.Size(), nil 212 } 213 214 func (m *fsMetaV1) ReadFrom(ctx context.Context, lk *lock.LockedFile) (n int64, err error) { 215 var fsMetaBuf []byte 216 fi, err := lk.Stat() 217 if err != nil { 218 logger.LogIf(ctx, err) 219 return 0, err 220 } 221 222 fsMetaBuf, err = ioutil.ReadAll(io.NewSectionReader(lk, 0, fi.Size())) 223 if err != nil { 224 logger.LogIf(ctx, err) 225 return 0, err 226 } 227 228 if len(fsMetaBuf) == 0 { 229 return 0, io.EOF 230 } 231 232 var json = jsoniter.ConfigCompatibleWithStandardLibrary 233 if err = json.Unmarshal(fsMetaBuf, m); err != nil { 234 return 0, err 235 } 236 237 // Verify if the format is valid, return corrupted format 238 // for unrecognized formats. 239 if !isFSMetaValid(m.Version) { 240 logger.GetReqInfo(ctx).AppendTags("file", lk.Name()) 241 logger.LogIf(ctx, errCorruptedFormat) 242 return 0, errCorruptedFormat 243 } 244 245 // Success. 246 return int64(len(fsMetaBuf)), nil 247 } 248 249 // newFSMetaV1 - initializes new fsMetaV1. 250 func newFSMetaV1() (fsMeta fsMetaV1) { 251 fsMeta = fsMetaV1{} 252 fsMeta.Version = fsMetaVersion 253 return fsMeta 254 }