github.com/Microsoft/azure-vhd-utils@v0.0.0-20230613175315-7c30a3748a1b/vhdcore/header/factory.go (about)

     1  package header
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/Microsoft/azure-vhd-utils/vhdcore"
     9  	"github.com/Microsoft/azure-vhd-utils/vhdcore/common"
    10  	"github.com/Microsoft/azure-vhd-utils/vhdcore/header/parentlocator"
    11  	"github.com/Microsoft/azure-vhd-utils/vhdcore/reader"
    12  )
    13  
    14  // Factory type is used to create VhdHeader instance by reading vhd header section.
    15  //
    16  type Factory struct {
    17  	vhdReader    *reader.VhdReader
    18  	headerOffset int64
    19  }
    20  
    21  // NewFactory creates a new instance of Factory, which can be used to create
    22  // a VhdHeader instance by reading the header section using vhdReader.
    23  //
    24  func NewFactory(vhdReader *reader.VhdReader, headerOffset int64) *Factory {
    25  	return &Factory{vhdReader: vhdReader, headerOffset: headerOffset}
    26  }
    27  
    28  // Create creates a Header instance by reading the header section of a expandable disk.
    29  // This function return error if any error occurs while reading or parsing the header fields.
    30  //
    31  func (f *Factory) Create() (*Header, error) {
    32  	header := &Header{}
    33  	var err error
    34  	errDone := func() (*Header, error) {
    35  		return nil, err
    36  	}
    37  
    38  	header.Cookie, err = f.readHeaderCookie()
    39  	if err != nil {
    40  		return errDone()
    41  	}
    42  
    43  	header.DataOffset, err = f.readDataOffset()
    44  	if err != nil {
    45  		return errDone()
    46  	}
    47  
    48  	header.TableOffset, err = f.readBATOffset()
    49  	if err != nil {
    50  		return errDone()
    51  	}
    52  
    53  	header.HeaderVersion, err = f.readHeaderVersion()
    54  	if err != nil {
    55  		return errDone()
    56  	}
    57  
    58  	header.MaxTableEntries, err = f.readMaxBATEntries()
    59  	if err != nil {
    60  		return errDone()
    61  	}
    62  
    63  	header.BlockSize, err = f.readBlockSize()
    64  	if err != nil {
    65  		return errDone()
    66  	}
    67  
    68  	header.CheckSum, err = f.readCheckSum()
    69  	if err != nil {
    70  		return errDone()
    71  	}
    72  
    73  	header.ParentUniqueID, err = f.readParentUniqueID()
    74  	if err != nil {
    75  		return errDone()
    76  	}
    77  
    78  	header.ParentTimeStamp, err = f.readParentTimeStamp()
    79  	if err != nil {
    80  		return errDone()
    81  	}
    82  
    83  	header.Reserved, err = f.readReserved()
    84  	if err != nil {
    85  		return errDone()
    86  	}
    87  
    88  	header.ParentPath, err = f.readParentPath()
    89  	if err != nil {
    90  		return errDone()
    91  	}
    92  
    93  	header.ParentLocators, err = f.readParentLocators()
    94  	if err != nil {
    95  		return errDone()
    96  	}
    97  
    98  	header.RawData, err = f.readWholeHeader()
    99  	if err != nil {
   100  		return errDone()
   101  	}
   102  
   103  	return header, nil
   104  }
   105  
   106  // readHeaderCookie reads the vhd cookie string and returns it as an instance of VhdCookie.
   107  // This function return error if the cookie is invalid, if no or fewer bytes could be read.
   108  // Cookie is stored as eight-character ASCII string starting at offset 0 relative to the beginning
   109  // of header.
   110  //
   111  func (f *Factory) readHeaderCookie() (*vhdcore.Cookie, error) {
   112  	cookieData := make([]byte, 8)
   113  	if _, err := f.vhdReader.ReadBytes(f.headerOffset+0, cookieData); err != nil {
   114  		return nil, NewParseError("Cookie", err)
   115  	}
   116  
   117  	cookie := vhdcore.CreateNewVhdCookie(true, cookieData)
   118  	if !cookie.IsValid() {
   119  		return nil, NewParseError("Cookie", fmt.Errorf("Invalid header cookie data %v", cookieData))
   120  	}
   121  	return cookie, nil
   122  }
   123  
   124  // readDataOffset reads and return the absolute offset to the next structure in the disk. This field
   125  // is currently unused and holds the value 0xFFFFFFFF. This function return error if no or fewer
   126  // bytes could be read.
   127  // This value is stored as 8 bytes value starting at offset 8 relative to the beginning of header.
   128  // This value is stored in big-endian format.
   129  //
   130  func (f *Factory) readDataOffset() (int64, error) {
   131  	value, err := f.vhdReader.ReadInt64(f.headerOffset + 8)
   132  	if err != nil {
   133  		return -1, NewParseError("DataOffset", err)
   134  	}
   135  	return value, nil
   136  }
   137  
   138  // readBATOffset reads and return the absolute offset to the the Block Allocation Table (BAT) in the
   139  // disk. This function return error if no or fewer bytes could be read.
   140  // BATOffset is stored as 8 bytes value starting at offset 16 relative to the beginning of header.
   141  // This value is stored in big-endian format.
   142  //
   143  func (f *Factory) readBATOffset() (int64, error) {
   144  	value, err := f.vhdReader.ReadInt64(f.headerOffset + 16)
   145  	if err != nil {
   146  		return -1, NewParseError("BATOffset", err)
   147  	}
   148  	return value, nil
   149  }
   150  
   151  // readHeaderVersion reads the value of the field the holds the major/minor version of the disk header.
   152  // This function return error if no or fewer bytes could be read. HeaderVersion is stored as 4 bytes
   153  // value starting at offset 24 relative to the beginning of header.
   154  //
   155  func (f *Factory) readHeaderVersion() (VhdHeaderVersion, error) {
   156  	value, err := f.vhdReader.ReadUInt32(f.headerOffset + 24)
   157  	if err != nil {
   158  		return VhdHeaderVersionNone, NewParseError("HeaderVersion", err)
   159  	}
   160  	v := VhdHeaderVersion(value)
   161  	if !v.IsSupported() {
   162  		return VhdHeaderVersionNone,
   163  			NewParseError("HeaderVersion", fmt.Errorf("Invalid header version %v, unsupported format", v))
   164  	}
   165  	return v, nil
   166  }
   167  
   168  // readMaxTableEntries reads and return maximum entries present in the BAT. This function return
   169  // error if no or fewer bytes could be read.
   170  // MaxTableEntries is stored as 4 bytes value starting at offset 28 relative to the beginning of
   171  // header. This value is stored in big-endian format.
   172  //
   173  func (f *Factory) readMaxBATEntries() (uint32, error) {
   174  	value, err := f.vhdReader.ReadUInt32(f.headerOffset + 28)
   175  	if err != nil {
   176  		return 0, NewParseError("MaxBATEntries", err)
   177  	}
   178  	return value, nil
   179  }
   180  
   181  // readBlockSize reads size of the 'data section' of a block, this does not include size of 'block
   182  // bitmap section'. This function return error if no or fewer bytes could be read.
   183  // BlockSize is stored as 4 bytes value starting at offset 32 relative to the beginning of header.
   184  // This value is stored in big-endian format.
   185  //
   186  func (f *Factory) readBlockSize() (uint32, error) {
   187  	value, err := f.vhdReader.ReadUInt32(f.headerOffset + 32)
   188  	if err != nil {
   189  		return 0, NewParseError("BlockSize", err)
   190  	}
   191  	return value, nil
   192  }
   193  
   194  // readCheckSum reads the field that stores basic checksum of the hard disk header.
   195  // This function return error if no or fewer bytes could be read.
   196  // The value is stored as 4 byte value starting at offset 36 relative to the beginning of header.
   197  // This value is stored in big-endian format.
   198  //
   199  func (f *Factory) readCheckSum() (uint32, error) {
   200  	value, err := f.vhdReader.ReadUInt32(f.headerOffset + 36)
   201  	if err != nil {
   202  		return 0, NewParseError("CheckSum", err)
   203  	}
   204  	return value, nil
   205  }
   206  
   207  // readParentUniqueId reads the field that stores unique ID used to identify the parent disk. This
   208  // field is used only for differencing disk.  This is a 128-bit universally unique identifier (UUID).
   209  // This function return error if no or fewer bytes could be read.
   210  // The value is stored as 16 byte value starting at offset 40 relative to the beginning of header.
   211  //
   212  func (f *Factory) readParentUniqueID() (*common.UUID, error) {
   213  	value, err := f.vhdReader.ReadUUID(f.headerOffset + 40)
   214  	if err != nil {
   215  		return nil, NewParseError("ParentUniqueId", err)
   216  	}
   217  	return value, nil
   218  }
   219  
   220  // readTimeStamp reads the field storing modification time stamp of the parent hard disk which is
   221  // stored as the number of seconds since January 1, 2000 12:00:00 AM in UTC/GMT and return it as
   222  // instance of time.Time. This function return error if no or fewer bytes could be read.
   223  // TimeStamp is stored as 4 bytes value starting at offset 56 relative to the beginning of header.
   224  // This value is stored in big-endian format.
   225  //
   226  func (f *Factory) readParentTimeStamp() (*time.Time, error) {
   227  	value, err := f.vhdReader.ReadDateTime(f.headerOffset + 56)
   228  	if err != nil {
   229  		return nil, NewParseError("ParentTimeStamp", err)
   230  	}
   231  	return value, nil
   232  }
   233  
   234  // readReserved reads the reserved field which is not used and all set to zero. This function return
   235  // error if no or fewer bytes could be read. Reserved is stored as 4 bytes value starting at offset
   236  // 60 relative to the beginning of header. This value is stored in big-endian format.
   237  //
   238  func (f *Factory) readReserved() (uint32, error) {
   239  	value, err := f.vhdReader.ReadUInt32(f.headerOffset + 60)
   240  	if err != nil {
   241  		return 0, NewParseError("Reserved", err)
   242  	}
   243  	return value, nil
   244  }
   245  
   246  // readParentPath reads the field storing parent hard disk file name. This function return error if
   247  // no or fewer bytes could be read. ParentPath is stored in UTF-16 as big-endian format, its length is
   248  // 512 bytes, starting at offset 64 relative to the beginning of header.
   249  //
   250  func (f *Factory) readParentPath() (string, error) {
   251  	parentPath := make([]byte, 512)
   252  	_, err := f.vhdReader.ReadBytes(f.headerOffset+64, parentPath)
   253  	if err != nil {
   254  		return "", NewParseError("ParentPath", err)
   255  	}
   256  	return strings.TrimSuffix(common.Utf16BytesToStringBE(parentPath), "\x00"), nil
   257  }
   258  
   259  // readParentLocators reads the collection of parent locator entries. This function return error if
   260  // no or fewer bytes could be read. There are 8 entries, each 24 bytes, starting at offset 576 relative
   261  // to the beginning of header.
   262  //
   263  func (f *Factory) readParentLocators() (parentlocator.ParentLocators, error) {
   264  	var err error
   265  	count := 8
   266  	parentLocators := make(parentlocator.ParentLocators, count)
   267  	offset := f.headerOffset + 576
   268  	for i := 0; i < count; i++ {
   269  		parentLocFac := parentlocator.NewFactory(f.vhdReader, offset)
   270  		parentLocators[i], err = parentLocFac.Create()
   271  		if err != nil {
   272  			return nil, NewParseError("ParentLocator", err)
   273  		}
   274  		offset += 24
   275  	}
   276  
   277  	return parentLocators, nil
   278  }
   279  
   280  // readWholeHeader reads the entire header as a raw bytes. This function return error if the byte
   281  // could be read.
   282  //
   283  func (f *Factory) readWholeHeader() ([]byte, error) {
   284  	rawData := make([]byte, 1024)
   285  	_, err := f.vhdReader.ReadBytes(f.headerOffset+0, rawData)
   286  	if err != nil {
   287  		return nil, err
   288  	}
   289  	return rawData, nil
   290  }