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  }