github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/xl-storage-meta-inline.go (about) 1 // Copyright (c) 2015-2021 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 "errors" 22 "fmt" 23 24 "github.com/minio/minio/internal/logger" 25 "github.com/tinylib/msgp/msgp" 26 ) 27 28 // xlMetaInlineData is serialized data in [string][]byte pairs. 29 type xlMetaInlineData []byte 30 31 // xlMetaInlineDataVer indicates the version of the inline data structure. 32 const xlMetaInlineDataVer = 1 33 34 // versionOK returns whether the version is ok. 35 func (x xlMetaInlineData) versionOK() bool { 36 if len(x) == 0 { 37 return true 38 } 39 return x[0] > 0 && x[0] <= xlMetaInlineDataVer 40 } 41 42 // afterVersion returns the payload after the version, if any. 43 func (x xlMetaInlineData) afterVersion() []byte { 44 if len(x) == 0 { 45 return x 46 } 47 return x[1:] 48 } 49 50 // find the data with key s. 51 // Returns nil if not for or an error occurs. 52 func (x xlMetaInlineData) find(key string) []byte { 53 if len(x) == 0 || !x.versionOK() { 54 return nil 55 } 56 sz, buf, err := msgp.ReadMapHeaderBytes(x.afterVersion()) 57 if err != nil || sz == 0 { 58 return nil 59 } 60 for i := uint32(0); i < sz; i++ { 61 var found []byte 62 found, buf, err = msgp.ReadMapKeyZC(buf) 63 if err != nil || sz == 0 { 64 return nil 65 } 66 if string(found) == key { 67 val, _, _ := msgp.ReadBytesZC(buf) 68 return val 69 } 70 // Skip it 71 _, buf, err = msgp.ReadBytesZC(buf) 72 if err != nil { 73 return nil 74 } 75 } 76 return nil 77 } 78 79 // validate checks if the data is valid. 80 // It does not check integrity of the stored data. 81 func (x xlMetaInlineData) validate() error { 82 if len(x) == 0 { 83 return nil 84 } 85 86 if !x.versionOK() { 87 return fmt.Errorf("xlMetaInlineData: unknown version 0x%x", x[0]) 88 } 89 90 sz, buf, err := msgp.ReadMapHeaderBytes(x.afterVersion()) 91 if err != nil { 92 return fmt.Errorf("xlMetaInlineData: %w", err) 93 } 94 95 for i := uint32(0); i < sz; i++ { 96 var key []byte 97 key, buf, err = msgp.ReadMapKeyZC(buf) 98 if err != nil { 99 return fmt.Errorf("xlMetaInlineData: %w", err) 100 } 101 if len(key) == 0 { 102 return fmt.Errorf("xlMetaInlineData: key %d is length 0", i) 103 } 104 _, buf, err = msgp.ReadBytesZC(buf) 105 if err != nil { 106 return fmt.Errorf("xlMetaInlineData: %w", err) 107 } 108 } 109 110 return nil 111 } 112 113 // repair will copy all seemingly valid data entries from a corrupted set. 114 // This does not ensure that data is correct, but will allow all operations to complete. 115 func (x *xlMetaInlineData) repair() { 116 data := *x 117 if len(data) == 0 { 118 return 119 } 120 121 if !data.versionOK() { 122 *x = nil 123 return 124 } 125 126 sz, buf, err := msgp.ReadMapHeaderBytes(data.afterVersion()) 127 if err != nil { 128 *x = nil 129 return 130 } 131 132 // Remove all current data 133 keys := make([][]byte, 0, sz) 134 vals := make([][]byte, 0, sz) 135 for i := uint32(0); i < sz; i++ { 136 var key, val []byte 137 key, buf, err = msgp.ReadMapKeyZC(buf) 138 if err != nil { 139 break 140 } 141 if len(key) == 0 { 142 break 143 } 144 val, buf, err = msgp.ReadBytesZC(buf) 145 if err != nil { 146 break 147 } 148 keys = append(keys, key) 149 vals = append(vals, val) 150 } 151 x.serialize(-1, keys, vals) 152 } 153 154 // validate checks if the data is valid. 155 // It does not check integrity of the stored data. 156 func (x xlMetaInlineData) list() ([]string, error) { 157 if len(x) == 0 { 158 return nil, nil 159 } 160 if !x.versionOK() { 161 return nil, errors.New("xlMetaInlineData: unknown version") 162 } 163 164 sz, buf, err := msgp.ReadMapHeaderBytes(x.afterVersion()) 165 if err != nil { 166 return nil, err 167 } 168 keys := make([]string, 0, sz) 169 for i := uint32(0); i < sz; i++ { 170 var key []byte 171 key, buf, err = msgp.ReadMapKeyZC(buf) 172 if err != nil { 173 return keys, err 174 } 175 if len(key) == 0 { 176 return keys, fmt.Errorf("xlMetaInlineData: key %d is length 0", i) 177 } 178 keys = append(keys, string(key)) 179 // Skip data... 180 _, buf, err = msgp.ReadBytesZC(buf) 181 if err != nil { 182 return keys, err 183 } 184 } 185 return keys, nil 186 } 187 188 // serialize will serialize the provided keys and values. 189 // The function will panic if keys/value slices aren't of equal length. 190 // Payload size can give an indication of expected payload size. 191 // If plSize is <= 0 it will be calculated. 192 func (x *xlMetaInlineData) serialize(plSize int, keys [][]byte, vals [][]byte) { 193 if len(keys) != len(vals) { 194 panic(fmt.Errorf("xlMetaInlineData.serialize: keys/value number mismatch")) 195 } 196 if len(keys) == 0 { 197 *x = nil 198 return 199 } 200 if plSize <= 0 { 201 plSize = 1 + msgp.MapHeaderSize 202 for i := range keys { 203 plSize += len(keys[i]) + len(vals[i]) + msgp.StringPrefixSize + msgp.ArrayHeaderSize 204 } 205 } 206 payload := make([]byte, 1, plSize) 207 payload[0] = xlMetaInlineDataVer 208 payload = msgp.AppendMapHeader(payload, uint32(len(keys))) 209 for i := range keys { 210 payload = msgp.AppendStringFromBytes(payload, keys[i]) 211 payload = msgp.AppendBytes(payload, vals[i]) 212 } 213 *x = payload 214 } 215 216 // entries returns the number of entries in the data. 217 func (x xlMetaInlineData) entries() int { 218 if len(x) == 0 || !x.versionOK() { 219 return 0 220 } 221 sz, _, _ := msgp.ReadMapHeaderBytes(x.afterVersion()) 222 return int(sz) 223 } 224 225 // replace will add or replace a key/value pair. 226 func (x *xlMetaInlineData) replace(key string, value []byte) { 227 in := x.afterVersion() 228 sz, buf, _ := msgp.ReadMapHeaderBytes(in) 229 keys := make([][]byte, 0, sz+1) 230 vals := make([][]byte, 0, sz+1) 231 232 // Version plus header... 233 plSize := 1 + msgp.MapHeaderSize 234 replaced := false 235 for i := uint32(0); i < sz; i++ { 236 var found, foundVal []byte 237 var err error 238 found, buf, err = msgp.ReadMapKeyZC(buf) 239 if err != nil { 240 break 241 } 242 foundVal, buf, err = msgp.ReadBytesZC(buf) 243 if err != nil { 244 break 245 } 246 plSize += len(found) + msgp.StringPrefixSize + msgp.ArrayHeaderSize 247 keys = append(keys, found) 248 if string(found) == key { 249 vals = append(vals, value) 250 plSize += len(value) 251 replaced = true 252 } else { 253 vals = append(vals, foundVal) 254 plSize += len(foundVal) 255 } 256 } 257 258 // Add one more. 259 if !replaced { 260 keys = append(keys, []byte(key)) 261 vals = append(vals, value) 262 plSize += len(key) + len(value) + msgp.StringPrefixSize + msgp.ArrayHeaderSize 263 } 264 265 // Reserialize... 266 x.serialize(plSize, keys, vals) 267 } 268 269 // rename will rename a key. 270 // Returns whether the key was found. 271 func (x *xlMetaInlineData) rename(oldKey, newKey string) bool { 272 in := x.afterVersion() 273 sz, buf, _ := msgp.ReadMapHeaderBytes(in) 274 keys := make([][]byte, 0, sz) 275 vals := make([][]byte, 0, sz) 276 277 // Version plus header... 278 plSize := 1 + msgp.MapHeaderSize 279 found := false 280 for i := uint32(0); i < sz; i++ { 281 var foundKey, foundVal []byte 282 var err error 283 foundKey, buf, err = msgp.ReadMapKeyZC(buf) 284 if err != nil { 285 break 286 } 287 foundVal, buf, err = msgp.ReadBytesZC(buf) 288 if err != nil { 289 break 290 } 291 plSize += len(foundVal) + msgp.StringPrefixSize + msgp.ArrayHeaderSize 292 vals = append(vals, foundVal) 293 if string(foundKey) != oldKey { 294 keys = append(keys, foundKey) 295 plSize += len(foundKey) 296 } else { 297 keys = append(keys, []byte(newKey)) 298 plSize += len(newKey) 299 found = true 300 } 301 } 302 // If not found, just return. 303 if !found { 304 return false 305 } 306 307 // Reserialize... 308 x.serialize(plSize, keys, vals) 309 return true 310 } 311 312 // remove will remove one or more keys. 313 // Returns true if any key was found. 314 func (x *xlMetaInlineData) remove(keys ...string) bool { 315 in := x.afterVersion() 316 sz, buf, _ := msgp.ReadMapHeaderBytes(in) 317 newKeys := make([][]byte, 0, sz) 318 newVals := make([][]byte, 0, sz) 319 var removeKey func(s []byte) bool 320 321 // Copy if big number of compares... 322 if len(keys) > 5 && sz > 5 { 323 mKeys := make(map[string]struct{}, len(keys)) 324 for _, key := range keys { 325 mKeys[key] = struct{}{} 326 } 327 removeKey = func(s []byte) bool { 328 _, ok := mKeys[string(s)] 329 return ok 330 } 331 } else { 332 removeKey = func(s []byte) bool { 333 for _, key := range keys { 334 if key == string(s) { 335 return true 336 } 337 } 338 return false 339 } 340 } 341 342 // Version plus header... 343 plSize := 1 + msgp.MapHeaderSize 344 found := false 345 for i := uint32(0); i < sz; i++ { 346 var foundKey, foundVal []byte 347 var err error 348 foundKey, buf, err = msgp.ReadMapKeyZC(buf) 349 if err != nil { 350 break 351 } 352 foundVal, buf, err = msgp.ReadBytesZC(buf) 353 if err != nil { 354 break 355 } 356 if !removeKey(foundKey) { 357 plSize += msgp.StringPrefixSize + msgp.ArrayHeaderSize + len(foundKey) + len(foundVal) 358 newKeys = append(newKeys, foundKey) 359 newVals = append(newVals, foundVal) 360 } else { 361 found = true 362 } 363 } 364 // If not found, just return. 365 if !found { 366 return false 367 } 368 // If none left... 369 if len(newKeys) == 0 { 370 *x = nil 371 return true 372 } 373 374 // Reserialize... 375 x.serialize(plSize, newKeys, newVals) 376 return true 377 } 378 379 // xlMetaV2TrimData will trim any data from the metadata without unmarshalling it. 380 // If any error occurs the unmodified data is returned. 381 func xlMetaV2TrimData(buf []byte) []byte { 382 metaBuf, min, maj, err := checkXL2V1(buf) 383 if err != nil { 384 return buf 385 } 386 if maj == 1 && min < 1 { 387 // First version to carry data. 388 return buf 389 } 390 // Skip header 391 _, metaBuf, err = msgp.ReadBytesZC(metaBuf) 392 if err != nil { 393 logger.LogIf(GlobalContext, err) 394 return buf 395 } 396 // Skip CRC 397 if maj > 1 || min >= 2 { 398 _, metaBuf, err = msgp.ReadUint32Bytes(metaBuf) 399 logger.LogIf(GlobalContext, err) 400 } 401 // = input - current pos 402 ends := len(buf) - len(metaBuf) 403 if ends > len(buf) { 404 return buf 405 } 406 407 return buf[:ends] 408 }