github.com/Microsoft/azure-vhd-utils@v0.0.0-20230613175315-7c30a3748a1b/vhdcore/footer/factory.go (about) 1 package footer 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/Microsoft/azure-vhd-utils/vhdcore" 8 "github.com/Microsoft/azure-vhd-utils/vhdcore/common" 9 "github.com/Microsoft/azure-vhd-utils/vhdcore/reader" 10 ) 11 12 // Factory type is used to create Footer instance by reading vhd footer section. 13 // 14 type Factory struct { 15 vhdReader *reader.VhdReader 16 footerOffset int64 17 } 18 19 // NewFactory creates a new instance of Factory, which can be used to create a Footer 20 // instance by reading the footer section using VhdReader. 21 // 22 func NewFactory(vhdReader *reader.VhdReader) *Factory { 23 return &Factory{vhdReader: vhdReader, footerOffset: vhdReader.Size - vhdcore.VhdFooterSize} 24 } 25 26 // Create creates a Footer instance by reading the footer section of the disk. 27 // This function return error if any error occurs while reading or parsing the footer fields. 28 // 29 func (f *Factory) Create() (*Footer, error) { 30 footer := &Footer{} 31 var err error 32 errDone := func() (*Footer, error) { 33 return nil, err 34 } 35 36 footer.Cookie, err = f.readVhdCookie() 37 if err != nil { 38 return errDone() 39 } 40 41 footer.Features, err = f.readFeatures() 42 if err != nil { 43 return errDone() 44 } 45 46 footer.FileFormatVersion, err = f.readFileFormatVersion() 47 if err != nil { 48 return errDone() 49 } 50 51 footer.HeaderOffset, err = f.readHeaderOffset() 52 if err != nil { 53 return errDone() 54 } 55 56 footer.TimeStamp, err = f.readTimeStamp() 57 if err != nil { 58 return errDone() 59 } 60 footer.CreatorApplication, err = f.readCreatorApplication() 61 if err != nil { 62 return errDone() 63 } 64 65 footer.CreatorVersion, err = f.readCreatorVersion() 66 if err != nil { 67 return errDone() 68 } 69 70 footer.CreatorHostOsType, err = f.readCreatorHostOsType() 71 if err != nil { 72 return errDone() 73 } 74 75 footer.PhysicalSize, err = f.readPhysicalSize() 76 if err != nil { 77 return errDone() 78 } 79 80 footer.VirtualSize, err = f.readVirtualSize() 81 if err != nil { 82 return errDone() 83 } 84 85 footer.DiskGeometry, err = f.readDiskGeometry() 86 if err != nil { 87 return errDone() 88 } 89 90 footer.DiskType, err = f.readDiskType() 91 if err != nil { 92 return errDone() 93 } 94 95 footer.CheckSum, err = f.readCheckSum() 96 if err != nil { 97 return errDone() 98 } 99 100 footer.UniqueID, err = f.readUniqueID() 101 if err != nil { 102 return errDone() 103 } 104 105 footer.SavedState, err = f.readSavedState() 106 if err != nil { 107 return errDone() 108 } 109 110 footer.Reserved, err = f.readReserved() 111 if err != nil { 112 return errDone() 113 } 114 115 footer.RawData, err = f.readWholeFooter() 116 if err != nil { 117 return errDone() 118 } 119 120 return footer, nil 121 } 122 123 // readVhdCookie reads the vhd cookie string and returns it as an instance of VhdCookie. 124 // This function returns error if the cookie is invalid, if no or fewer bytes could be 125 // read. Cookie is stored as eight-character ASCII string starting at offset 0 relative 126 // to the beginning of footer. 127 // 128 func (f *Factory) readVhdCookie() (*vhdcore.Cookie, error) { 129 cookieData := make([]byte, 8) 130 if _, err := f.vhdReader.ReadBytes(f.footerOffset+0, cookieData); err != nil { 131 return nil, NewParseError("Cookie", err) 132 } 133 134 cookie := vhdcore.CreateNewVhdCookie(false, cookieData) 135 if !cookie.IsValid() { 136 return nil, NewParseError("Cookie", fmt.Errorf("Invalid footer cookie data %v", cookieData)) 137 } 138 return cookie, nil 139 } 140 141 // readFeatures reads and return the feature field. This function return error if no or 142 // fewer bytes could be read. 143 // Feature is stored as 4 bytes value starting at offset 8 relative to the beginning of 144 // footer. 145 // 146 func (f *Factory) readFeatures() (VhdFeature, error) { 147 value, err := f.vhdReader.ReadUInt32(f.footerOffset + 8) 148 if err != nil { 149 return VhdFeatureNoFeaturesEnabled, NewParseError("Features", err) 150 } 151 return VhdFeature(value), nil 152 } 153 154 // readFileFormatVersion reads and return the VhdFileFormatVersion field from the footer. 155 // This function is return error if no or fewer bytes could be read. 156 // VhdFileFormatVersion is stored as 4 bytes value starting at offset 12 relative to the 157 // beginning of footer. 158 // 159 func (f *Factory) readFileFormatVersion() (VhdFileFormatVersion, error) { 160 value, err := f.vhdReader.ReadUInt32(f.footerOffset + 12) 161 if err != nil { 162 return VhdFileFormatVersionNone, NewParseError("FileFormatVersion", err) 163 } 164 return VhdFileFormatVersion(value), nil 165 } 166 167 // readHeaderOffset reads and return the absolute offset to the header structure. 168 // This function return error if no or fewer bytes could be read. 169 // Header offset is stored as 8 bytes value starting at offset 16 relative to the beginning 170 // of footer. This value is stored in big-endian format. 171 // 172 func (f *Factory) readHeaderOffset() (int64, error) { 173 value, err := f.vhdReader.ReadInt64(f.footerOffset + 16) 174 if err != nil { 175 return -1, NewParseError("HeaderOffset", err) 176 } 177 return value, nil 178 } 179 180 // readTimeStamp reads the creation time of the disk which is stored as the number of seconds 181 // since January 1, 2000 12:00:00 AM in UTC/GMT and return it as instance of time.Time. 182 // This function return error if no or fewer bytes could be read. 183 // TimeStamp is stored as 4 bytes value starting at offset 24 relative to the beginning 184 // of footer. This value is stored in big-endian format. 185 // 186 func (f *Factory) readTimeStamp() (*time.Time, error) { 187 value, err := f.vhdReader.ReadDateTime(f.footerOffset + 24) 188 if err != nil { 189 return nil, NewParseError("TimeStamp", err) 190 } 191 return value, nil 192 } 193 194 // readCreatorApplication reads the value of the field containing identity of the application 195 // used to create the disk. The field is a left-justified text field. It uses a single-byte 196 // character set. This function return error if no or fewer bytes could be read. 197 // Identifier is stored as 4 bytes value starting at offset 28 relative to the beginning 198 // of footer. 199 // 200 func (f *Factory) readCreatorApplication() (string, error) { 201 creatorApp := make([]byte, 4) 202 _, err := f.vhdReader.ReadBytes(f.footerOffset+28, creatorApp) 203 if err != nil { 204 return "", NewParseError("CreatorApplication", err) 205 } 206 return string(creatorApp), nil 207 } 208 209 // readCreatorVersion reads the value of the field the holds the major/minor version of the 210 // application that created the hard disk image. This function return error if no or fewer 211 // bytes could be read. 212 // Version is stored as 4 bytes value starting at offset 32 relative to the beginning 213 // of footer. 214 // 215 func (f *Factory) readCreatorVersion() (VhdCreatorVersion, error) { 216 value, err := f.vhdReader.ReadUInt32(f.footerOffset + 32) 217 if err != nil { 218 return VhdCreatorVersionNone, NewParseError("CreatorVersion", err) 219 } 220 return VhdCreatorVersion(value), nil 221 } 222 223 // readCreatorHostOsType reads the value of the field that stores the type of host operating 224 // system this disk image is created on. Call to this function return error if no or fewer 225 // bytes could be read. 226 // Version is stored as 4 bytes value starting at offset 36 relative to the beginning 227 // of footer. 228 // 229 func (f *Factory) readCreatorHostOsType() (HostOsType, error) { 230 value, err := f.vhdReader.ReadUInt32(f.footerOffset + 36) 231 if err != nil { 232 return HostOsTypeNone, NewParseError("CreatorHostOsType", err) 233 } 234 return HostOsType(value), nil 235 } 236 237 // readPhysicalSize reads the size of the hard disk in bytes, from the perspective of the 238 // virtual machine, at creation time. This field is for informational purposes. 239 // This function return error if no or fewer bytes could be read. 240 // PhysicalSize is stored as 8 bytes value starting at offset 40 relative to the 241 // beginning of footer. This size does not include the size consumed by vhd metadata such as 242 // header, footer BAT, block's bitmap 243 // This value is stored in big-endian format. 244 // 245 func (f *Factory) readPhysicalSize() (int64, error) { 246 value, err := f.vhdReader.ReadInt64(f.footerOffset + 40) 247 if err != nil { 248 return -1, NewParseError("PhysicalSize", err) 249 } 250 return value, nil 251 } 252 253 // readVirtualSize reads the size of the he current size of the hard disk, in bytes, from the 254 // perspective of the virtual machine. This value is same as the PhysicalSize when the hard 255 // disk is created. This value can change depending on whether the hard disk is expanded 256 // This function return error if no or fewer bytes could be read. 257 // VirtualSize is stored as 8 bytes value starting at offset 48 relative to the 258 // beginning of footer. This size does not include the size consumed by vhd metadata such as 259 // header, footer BAT, block's bitmap 260 // This value is stored in big-endian format. 261 // 262 func (f *Factory) readVirtualSize() (int64, error) { 263 value, err := f.vhdReader.ReadInt64(f.footerOffset + 48) 264 if err != nil { 265 return -1, NewParseError("VirtualSize", err) 266 } 267 return value, nil 268 } 269 270 // readDiskGeometry reads the 4 byte value that stores the cylinder, heads, and sectors per 271 // track value for the hard disk. This function return error if no or fewer bytes could 272 // be read. The value is stored starting starting at offset 56 relative to the beginning of 273 // footer. This value is stored in big-endian format. 274 // 275 func (f *Factory) readDiskGeometry() (*DiskGeometry, error) { 276 diskGeometry := &DiskGeometry{} 277 cylinder, err := f.vhdReader.ReadUInt16(f.footerOffset + 56 + 0) 278 if err != nil { 279 return nil, NewParseError("DiskGeometry::Cylinder", err) 280 } 281 diskGeometry.Cylinder = cylinder 282 heads, err := f.vhdReader.ReadByte(f.footerOffset + 56 + 2) 283 if err != nil { 284 return nil, NewParseError("DiskGeometry::Heads", err) 285 } 286 diskGeometry.Heads = heads 287 sectors, err := f.vhdReader.ReadByte(f.footerOffset + 56 + 3) 288 if err != nil { 289 return nil, NewParseError("DiskGeometry::Sectors", err) 290 } 291 diskGeometry.Sectors = sectors 292 return diskGeometry, nil 293 } 294 295 // readDiskType reads the field stores type of the disk (fixed, differencing, dynamic) 296 // This function return error if no or fewer bytes could be read. 297 // The value is stored as 4 byte value starting at offset 60 relative to the beginning 298 // of footer. This value is stored in big-endian format. 299 // 300 func (f *Factory) readDiskType() (DiskType, error) { 301 value, err := f.vhdReader.ReadUInt32(f.footerOffset + 60) 302 if err != nil { 303 return DiskTypeNone, NewParseError("DiskType", err) 304 } 305 return DiskType(value), nil 306 } 307 308 // readCheckSum reads the field that stores basic checksum of the hard disk footer. 309 // This function return error if no or fewer bytes could be read. 310 // The value is stored as 4 byte value starting at offset 64 relative to the beginning 311 // of footer. This value is stored in big-endian format. 312 // 313 func (f *Factory) readCheckSum() (uint32, error) { 314 value, err := f.vhdReader.ReadUInt32(f.footerOffset + 64) 315 if err != nil { 316 return 0, NewParseError("CheckSum", err) 317 } 318 return value, nil 319 } 320 321 // readUniqueId reads the field that stores unique ID used to identify the hard disk. 322 // This is a 128-bit universally unique identifier (UUID) 323 // This function return error if no or fewer bytes could be read. 324 // The value is stored as 16 byte value starting at offset 68 relative to the beginning 325 // of footer. 326 // 327 func (f *Factory) readUniqueID() (*common.UUID, error) { 328 value, err := f.vhdReader.ReadUUID(f.footerOffset + 68) 329 if err != nil { 330 return nil, NewParseError("UniqueId", err) 331 } 332 return value, nil 333 } 334 335 // readSavedState reads the flag indicating whether the system is in saved state. 336 // This function return error if the byte could be read. 337 // The value is stored as 1 byte value starting at offset 84 relative to the beginning 338 // of footer. 339 // 340 func (f *Factory) readSavedState() (bool, error) { 341 value, err := f.vhdReader.ReadBoolean(f.footerOffset + 84) 342 if err != nil { 343 return false, NewParseError("SavedState", err) 344 } 345 return value, err 346 } 347 348 // readReserved reads the reserved field which currently contains zeroes. 349 // This function return error if the byte could be read. 350 // It is 427 bytes in size starting at offset 85 relative to the beginning 351 // of footer. 352 // 353 func (f *Factory) readReserved() ([]byte, error) { 354 reserved := make([]byte, 427) 355 _, err := f.vhdReader.ReadBytes(f.footerOffset+85, reserved) 356 if err != nil { 357 return nil, NewParseError("Reserved", err) 358 } 359 return reserved, nil 360 } 361 362 // readWholeFooter reads the entire footer as a raw bytes. This function return 363 // error if the byte could be read. 364 // 365 func (f *Factory) readWholeFooter() ([]byte, error) { 366 rawData := make([]byte, 512) 367 _, err := f.vhdReader.ReadBytes(f.footerOffset+0, rawData) 368 if err != nil { 369 return nil, err 370 } 371 return rawData, nil 372 }