storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/docs/bucket/versioning/xl-meta.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2020 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 main 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "log" 28 "os" 29 30 "github.com/minio/cli" 31 "github.com/tinylib/msgp/msgp" 32 ) 33 34 func main() { 35 app := cli.NewApp() 36 app.Copyright = "MinIO, Inc." 37 app.Usage = "xl.meta to JSON" 38 app.HideVersion = true 39 app.CustomAppHelpTemplate = `NAME: 40 {{.Name}} - {{.Usage}} 41 42 USAGE: 43 {{.Name}} {{if .VisibleFlags}}[FLAGS]{{end}} METAFILES... 44 {{if .VisibleFlags}} 45 GLOBAL FLAGS: 46 {{range .VisibleFlags}}{{.}} 47 {{end}}{{end}} 48 ` 49 50 app.HideHelpCommand = true 51 52 app.Flags = []cli.Flag{ 53 cli.BoolFlag{ 54 Usage: "Print each file as a separate line without formatting", 55 Name: "ndjson", 56 }, 57 cli.BoolFlag{ 58 Usage: "Display inline data keys and sizes", 59 Name: "data", 60 }, 61 } 62 63 app.Action = func(c *cli.Context) error { 64 files := c.Args() 65 if len(files) == 0 { 66 // If no args, assume xl.meta 67 files = []string{"xl.meta"} 68 } 69 for _, file := range files { 70 var r io.Reader 71 switch file { 72 case "-": 73 r = os.Stdin 74 default: 75 f, err := os.Open(file) 76 if err != nil { 77 return err 78 } 79 defer f.Close() 80 r = f 81 } 82 83 b, err := ioutil.ReadAll(r) 84 if err != nil { 85 return err 86 } 87 b, _, minor, err := checkXL2V1(b) 88 if err != nil { 89 return err 90 } 91 92 buf := bytes.NewBuffer(nil) 93 var data xlMetaInlineData 94 switch minor { 95 case 0: 96 _, err = msgp.CopyToJSON(buf, bytes.NewBuffer(b)) 97 if err != nil { 98 return err 99 } 100 case 1, 2: 101 v, b, err := msgp.ReadBytesZC(b) 102 if err != nil { 103 return err 104 } 105 if _, nbuf, err := msgp.ReadUint32Bytes(b); err == nil { 106 // Read metadata CRC (added in v2, ignore if not found) 107 b = nbuf 108 } 109 110 _, err = msgp.CopyToJSON(buf, bytes.NewBuffer(v)) 111 if err != nil { 112 return err 113 } 114 data = b 115 default: 116 return errors.New("unknown metadata version") 117 } 118 119 if c.Bool("data") { 120 b, err := data.json() 121 if err != nil { 122 return err 123 } 124 buf = bytes.NewBuffer(b) 125 } 126 if c.Bool("ndjson") { 127 fmt.Println(buf.String()) 128 continue 129 } 130 var msi map[string]interface{} 131 dec := json.NewDecoder(buf) 132 // Use number to preserve integers. 133 dec.UseNumber() 134 err = dec.Decode(&msi) 135 if err != nil { 136 return err 137 } 138 b, err = json.MarshalIndent(msi, "", " ") 139 if err != nil { 140 return err 141 } 142 fmt.Println(string(b)) 143 } 144 return nil 145 } 146 err := app.Run(os.Args) 147 if err != nil { 148 log.Fatal(err) 149 } 150 } 151 152 var ( 153 // XL header specifies the format 154 xlHeader = [4]byte{'X', 'L', '2', ' '} 155 156 // Current version being written. 157 xlVersionCurrent [4]byte 158 ) 159 160 const ( 161 // Breaking changes. 162 // Newer versions cannot be read by older software. 163 // This will prevent downgrades to incompatible versions. 164 xlVersionMajor = 1 165 166 // Non breaking changes. 167 // Bumping this is informational, but should be done 168 // if any change is made to the data stored, bumping this 169 // will allow to detect the exact version later. 170 xlVersionMinor = 1 171 ) 172 173 func init() { 174 binary.LittleEndian.PutUint16(xlVersionCurrent[0:2], xlVersionMajor) 175 binary.LittleEndian.PutUint16(xlVersionCurrent[2:4], xlVersionMinor) 176 } 177 178 // checkXL2V1 will check if the metadata has correct header and is a known major version. 179 // The remaining payload and versions are returned. 180 func checkXL2V1(buf []byte) (payload []byte, major, minor uint16, err error) { 181 if len(buf) <= 8 { 182 return payload, 0, 0, fmt.Errorf("xlMeta: no data") 183 } 184 185 if !bytes.Equal(buf[:4], xlHeader[:]) { 186 return payload, 0, 0, fmt.Errorf("xlMeta: unknown XLv2 header, expected %v, got %v", xlHeader[:4], buf[:4]) 187 } 188 189 if bytes.Equal(buf[4:8], []byte("1 ")) { 190 // Set as 1,0. 191 major, minor = 1, 0 192 } else { 193 major, minor = binary.LittleEndian.Uint16(buf[4:6]), binary.LittleEndian.Uint16(buf[6:8]) 194 } 195 if major > xlVersionMajor { 196 return buf[8:], major, minor, fmt.Errorf("xlMeta: unknown major version %d found", major) 197 } 198 199 return buf[8:], major, minor, nil 200 } 201 202 const xlMetaInlineDataVer = 1 203 204 type xlMetaInlineData []byte 205 206 // afterVersion returns the payload after the version, if any. 207 func (x xlMetaInlineData) afterVersion() []byte { 208 if len(x) == 0 { 209 return x 210 } 211 return x[1:] 212 } 213 214 // versionOK returns whether the version is ok. 215 func (x xlMetaInlineData) versionOK() bool { 216 if len(x) == 0 { 217 return true 218 } 219 return x[0] > 0 && x[0] <= xlMetaInlineDataVer 220 } 221 222 func (x xlMetaInlineData) json() ([]byte, error) { 223 if len(x) == 0 { 224 return []byte("{}"), nil 225 } 226 if !x.versionOK() { 227 return nil, errors.New("xlMetaInlineData: unknown version") 228 } 229 230 sz, buf, err := msgp.ReadMapHeaderBytes(x.afterVersion()) 231 if err != nil { 232 return nil, err 233 } 234 res := []byte("{") 235 236 for i := uint32(0); i < sz; i++ { 237 var key, val []byte 238 key, buf, err = msgp.ReadMapKeyZC(buf) 239 if err != nil { 240 return nil, err 241 } 242 if len(key) == 0 { 243 return nil, fmt.Errorf("xlMetaInlineData: key %d is length 0", i) 244 } 245 // Skip data... 246 val, buf, err = msgp.ReadBytesZC(buf) 247 if err != nil { 248 return nil, err 249 } 250 if i > 0 { 251 res = append(res, ',') 252 } 253 s := fmt.Sprintf(`"%s":%d`, string(key), len(val)) 254 res = append(res, []byte(s)...) 255 } 256 res = append(res, '}') 257 return res, nil 258 }