github.com/vmware/govmomi@v0.51.0/vmdk/descriptor.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package vmdk 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "io" 12 "slices" 13 "strconv" 14 "strings" 15 "text/template" 16 17 "github.com/vmware/govmomi/units" 18 ) 19 20 type Descriptor struct { 21 Encoding string `json:"encoding"` 22 Version int `json:"version"` 23 CID DiskContentID `json:"cid"` 24 ParentCID DiskContentID `json:"parentCID"` 25 Type string `json:"type"` 26 Extent []Extent `json:"extent"` 27 DDB map[string]string `json:"ddb"` 28 } 29 30 type DiskContentID uint32 31 32 func (cid DiskContentID) String() string { 33 return fmt.Sprintf("%0.8x", uint32(cid)) 34 } 35 36 type Extent struct { 37 Type string `json:"type"` 38 Permission string `json:"permission"` 39 Size int64 `json:"size"` 40 Info string `json:"info"` 41 } 42 43 func NewDescriptor(extent ...Extent) *Descriptor { 44 for i := range extent { 45 if extent[i].Type == "" { 46 extent[i].Type = "VMFS" 47 } 48 if extent[i].Permission == "" { 49 extent[i].Permission = "RW" 50 } 51 } 52 return &Descriptor{ 53 Version: 1, 54 Encoding: "UTF-8", 55 Type: "vmfs", 56 DDB: map[string]string{}, 57 Extent: extent, 58 } 59 } 60 61 func ParseDescriptor(r io.Reader) (*Descriptor, error) { 62 d := NewDescriptor() 63 64 scanner := bufio.NewScanner(r) 65 66 // NOTE: not doing any validation currently, or using this function yet. 67 // Will add validation as needed when use-cases are implemented. 68 for scanner.Scan() { 69 token := bytes.Trim(scanner.Bytes(), "\x00") 70 line := strings.TrimSpace(string(token)) 71 if line == "" || strings.HasPrefix(line, "#") { 72 continue 73 } 74 if d.parseExtent(line) { 75 continue 76 } 77 78 s := strings.SplitN(line, "=", 2) 79 80 if len(s) != 2 { 81 continue 82 } 83 84 key, val := strings.TrimSpace(s[0]), strings.TrimSpace(s[1]) 85 val = strings.Trim(val, `"`) 86 if k := strings.TrimPrefix(key, "ddb."); k != key { 87 d.DDB[k] = val 88 continue 89 } 90 91 switch strings.ToLower(key) { 92 case "encoding": 93 d.Encoding = val 94 case "version": 95 d.Version, _ = strconv.Atoi(val) 96 case "cid": 97 _, _ = fmt.Sscanf(val, "%x", &d.CID) 98 case "parentcid": 99 _, _ = fmt.Sscanf(val, "%x", &d.ParentCID) 100 case "createType": 101 d.Type = val 102 } 103 } 104 105 return d, scanner.Err() 106 } 107 108 var permissions = []string{"RDONLY", "RW", "NOACCESS"} 109 110 func (d *Descriptor) parseExtent(line string) bool { 111 // Each extent is defined by a line following this pattern: 112 // perm size type "%s" 113 114 s := strings.SplitN(line, " ", 2) 115 116 if len(s) != 2 || !slices.Contains(permissions, s[0]) { 117 return false 118 } 119 120 x := Extent{ 121 Permission: s[0], 122 } 123 124 s = strings.SplitN(s[1], " ", 2) 125 size, err := strconv.ParseInt(s[0], 10, 64) 126 if len(s) != 2 || err != nil { 127 return false 128 } 129 130 x.Size = size 131 132 s = strings.SplitN(s[1], " ", 2) 133 x.Type = s[0] 134 135 if len(s) == 2 { 136 x.Info = strings.Trim(s[1], `"`) 137 } 138 139 d.Extent = append(d.Extent, x) 140 141 return true 142 } 143 144 var descriptor = `# Disk DescriptorFile 145 version={{ .Version }} 146 encoding="{{ .Encoding }}" 147 CID={{ .CID }} 148 parentCID={{ .ParentCID }} 149 createType="{{ .Type }}" 150 151 # Extent description ({{ cap }} capacity){{range .Extent }} 152 {{ .Permission }} {{ .Size }} {{ .Type }} "{{ .Info }}"{{end}} 153 154 # The Disk Data Base 155 #DDB{{ range $key, $val := .DDB }} 156 ddb.{{ $key }} = "{{ $val }}"{{end}} 157 ` 158 159 func (d *Descriptor) Write(w io.Writer) error { 160 t, err := template.New("vmdk").Funcs(template.FuncMap{ 161 "cap": func() string { 162 return units.ByteSize(d.Capacity()).String() 163 }, 164 }).Parse(descriptor) 165 if err != nil { 166 return err 167 } 168 return t.Execute(w, d) 169 } 170 171 // Capacity in bytes of the vmdk 172 func (d *Descriptor) Capacity() int64 { 173 var size int64 174 175 for i := range d.Extent { 176 size += d.Extent[i].Size * SectorSize 177 } 178 179 return size 180 }