iso9660.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. // Package iso9660 implements reading and creating basic ISO9660 images.
  2. package iso9660
  3. import (
  4. "encoding"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "strconv"
  10. "strings"
  11. "time"
  12. )
  13. // ISO 9660 Overview
  14. // https://archive.fo/xs9ac
  15. const (
  16. sectorSize uint32 = 2048
  17. systemAreaSize = sectorSize * 16
  18. standardIdentifier = "CD001"
  19. udfIdentifier = "BEA01"
  20. volumeTypeBoot byte = 0
  21. volumeTypePrimary byte = 1
  22. volumeTypeSupplementary byte = 2
  23. volumeTypePartition byte = 3
  24. volumeTypeTerminator byte = 255
  25. volumeDescriptorBodySize = sectorSize - 7
  26. aCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_!\"%&'()*+,-./:;<=>?"
  27. dCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
  28. // ECMA-119 7.4.2.2 defines d1-characters as
  29. // "subject to agreement between the originator and the recipient of the volume".
  30. d1Characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_!\"%&'()*+,-./:;<=>?"
  31. )
  32. const (
  33. dirFlagHidden = 1 << iota
  34. dirFlagDir
  35. dirFlagAssociated
  36. dirFlagRecord
  37. dirFlagProtection
  38. _
  39. _
  40. dirFlagMultiExtent
  41. )
  42. var standardIdentifierBytes = [5]byte{'C', 'D', '0', '0', '1'}
  43. var ErrUDFNotSupported = errors.New("UDF volumes are not supported")
  44. // volumeDescriptorHeader represents the data in bytes 0-6
  45. // of a Volume Descriptor as defined in ECMA-119 8.1
  46. type volumeDescriptorHeader struct {
  47. Type byte
  48. Identifier [5]byte
  49. Version byte
  50. }
  51. var _ encoding.BinaryUnmarshaler = &volumeDescriptorHeader{}
  52. var _ encoding.BinaryMarshaler = &volumeDescriptorHeader{}
  53. // UnmarshalBinary decodes a volumeDescriptorHeader from binary form
  54. func (vdh *volumeDescriptorHeader) UnmarshalBinary(data []byte) error {
  55. if len(data) < 7 {
  56. return io.ErrUnexpectedEOF
  57. }
  58. vdh.Type = data[0]
  59. copy(vdh.Identifier[:], data[1:6])
  60. vdh.Version = data[6]
  61. return nil
  62. }
  63. func (vdh volumeDescriptorHeader) MarshalBinary() ([]byte, error) {
  64. data := make([]byte, 7)
  65. data[0] = vdh.Type
  66. data[6] = vdh.Version
  67. copy(data[1:6], vdh.Identifier[:])
  68. return data, nil
  69. }
  70. // BootVolumeDescriptorBody represents the data in bytes 7-2047
  71. // of a Boot Record as defined in ECMA-119 8.2
  72. type BootVolumeDescriptorBody struct {
  73. BootSystemIdentifier string
  74. BootIdentifier string
  75. BootSystemUse [1977]byte
  76. }
  77. var _ encoding.BinaryUnmarshaler = &BootVolumeDescriptorBody{}
  78. // PrimaryVolumeDescriptorBody represents the data in bytes 7-2047
  79. // of a Primary Volume Descriptor as defined in ECMA-119 8.4
  80. type PrimaryVolumeDescriptorBody struct {
  81. SystemIdentifier string
  82. VolumeIdentifier string
  83. VolumeSpaceSize int32
  84. VolumeSetSize int16
  85. VolumeSequenceNumber int16
  86. LogicalBlockSize int16
  87. PathTableSize int32
  88. TypeLPathTableLoc int32
  89. OptTypeLPathTableLoc int32
  90. TypeMPathTableLoc int32
  91. OptTypeMPathTableLoc int32
  92. RootDirectoryEntry *DirectoryEntry
  93. VolumeSetIdentifier string
  94. PublisherIdentifier string
  95. DataPreparerIdentifier string
  96. ApplicationIdentifier string
  97. CopyrightFileIdentifier string
  98. AbstractFileIdentifier string
  99. BibliographicFileIdentifier string
  100. VolumeCreationDateAndTime VolumeDescriptorTimestamp
  101. VolumeModificationDateAndTime VolumeDescriptorTimestamp
  102. VolumeExpirationDateAndTime VolumeDescriptorTimestamp
  103. VolumeEffectiveDateAndTime VolumeDescriptorTimestamp
  104. FileStructureVersion byte
  105. ApplicationUsed [512]byte
  106. }
  107. var _ encoding.BinaryUnmarshaler = &PrimaryVolumeDescriptorBody{}
  108. var _ encoding.BinaryMarshaler = PrimaryVolumeDescriptorBody{}
  109. // DirectoryEntry contains data from a Directory Descriptor
  110. // as described by ECMA-119 9.1
  111. type DirectoryEntry struct {
  112. ExtendedAtributeRecordLength byte
  113. ExtentLocation int32
  114. ExtentLength uint32
  115. RecordingDateTime RecordingTimestamp
  116. FileFlags byte
  117. FileUnitSize byte
  118. InterleaveGap byte
  119. VolumeSequenceNumber int16
  120. Identifier string
  121. SystemUse []byte
  122. SystemUseEntries SystemUseEntrySlice
  123. }
  124. var _ encoding.BinaryUnmarshaler = &DirectoryEntry{}
  125. var _ encoding.BinaryMarshaler = &DirectoryEntry{}
  126. // UnmarshalBinary decodes a DirectoryEntry from binary form
  127. func (de *DirectoryEntry) UnmarshalBinary(data []byte) error {
  128. length := data[0]
  129. if length == 0 {
  130. return io.EOF
  131. }
  132. var err error
  133. de.ExtendedAtributeRecordLength = data[1]
  134. if de.ExtentLocation, err = UnmarshalInt32LSBMSB(data[2:10]); err != nil {
  135. return err
  136. }
  137. if de.ExtentLength, err = UnmarshalUint32LSBMSB(data[10:18]); err != nil {
  138. return err
  139. }
  140. if err = de.RecordingDateTime.UnmarshalBinary(data[18:25]); err != nil {
  141. return err
  142. }
  143. de.FileFlags = data[25]
  144. de.FileUnitSize = data[26]
  145. de.InterleaveGap = data[27]
  146. if de.VolumeSequenceNumber, err = UnmarshalInt16LSBMSB(data[28:32]); err != nil {
  147. return err
  148. }
  149. identifierLen := data[32]
  150. de.Identifier = string(data[33 : 33+identifierLen])
  151. // add padding if identifier length was even]
  152. idPaddingLen := (identifierLen + 1) % 2
  153. de.SystemUse = data[33+identifierLen+idPaddingLen : length]
  154. return nil
  155. }
  156. // MarshalBinary encodes a DirectoryEntry to binary form
  157. func (de *DirectoryEntry) MarshalBinary() ([]byte, error) {
  158. identifierLen := len(de.Identifier)
  159. idPaddingLen := (identifierLen + 1) % 2
  160. totalLen := 33 + identifierLen + idPaddingLen + len(de.SystemUse)
  161. if totalLen > 255 {
  162. return nil, fmt.Errorf("identifier %q is too long", de.Identifier)
  163. }
  164. data := make([]byte, totalLen)
  165. data[0] = byte(totalLen)
  166. data[1] = de.ExtendedAtributeRecordLength
  167. WriteInt32LSBMSB(data[2:10], de.ExtentLocation)
  168. WriteInt32LSBMSB(data[10:18], int32(de.ExtentLength))
  169. de.RecordingDateTime.MarshalBinary(data[18:25])
  170. data[25] = de.FileFlags
  171. data[26] = de.FileUnitSize
  172. data[27] = de.InterleaveGap
  173. WriteInt16LSBMSB(data[28:32], de.VolumeSequenceNumber)
  174. data[32] = byte(identifierLen)
  175. copy(data[33:33+identifierLen], []byte(de.Identifier))
  176. copy(data[33+identifierLen+idPaddingLen:totalLen], de.SystemUse)
  177. return data, nil
  178. }
  179. // Clone creates a copy of the DirectoryEntry
  180. func (de *DirectoryEntry) Clone() DirectoryEntry {
  181. newDE := DirectoryEntry{
  182. ExtendedAtributeRecordLength: de.ExtendedAtributeRecordLength,
  183. ExtentLocation: de.ExtentLocation,
  184. ExtentLength: de.ExtentLength,
  185. RecordingDateTime: de.RecordingDateTime,
  186. FileFlags: de.FileFlags,
  187. FileUnitSize: de.FileUnitSize,
  188. InterleaveGap: de.InterleaveGap,
  189. VolumeSequenceNumber: de.VolumeSequenceNumber,
  190. Identifier: de.Identifier,
  191. SystemUse: make([]byte, len(de.SystemUse)),
  192. }
  193. copy(newDE.SystemUse, de.SystemUse)
  194. return newDE
  195. }
  196. // UnmarshalBinary decodes a PrimaryVolumeDescriptorBody from binary form as defined in ECMA-119 8.4
  197. func (pvd *PrimaryVolumeDescriptorBody) UnmarshalBinary(data []byte) error {
  198. if len(data) < 2048 {
  199. return io.ErrUnexpectedEOF
  200. }
  201. var err error
  202. pvd.SystemIdentifier = strings.TrimRight(string(data[8:40]), " ")
  203. pvd.VolumeIdentifier = strings.TrimRight(string(data[40:72]), " ")
  204. if pvd.VolumeSpaceSize, err = UnmarshalInt32LSBMSB(data[80:88]); err != nil {
  205. return err
  206. }
  207. if pvd.VolumeSetSize, err = UnmarshalInt16LSBMSB(data[120:124]); err != nil {
  208. return err
  209. }
  210. if pvd.VolumeSequenceNumber, err = UnmarshalInt16LSBMSB(data[124:128]); err != nil {
  211. return err
  212. }
  213. if pvd.LogicalBlockSize, err = UnmarshalInt16LSBMSB(data[128:132]); err != nil {
  214. return err
  215. }
  216. if pvd.PathTableSize, err = UnmarshalInt32LSBMSB(data[132:140]); err != nil {
  217. return err
  218. }
  219. pvd.TypeLPathTableLoc = int32(binary.LittleEndian.Uint32(data[140:144]))
  220. pvd.OptTypeLPathTableLoc = int32(binary.LittleEndian.Uint32(data[144:148]))
  221. pvd.TypeMPathTableLoc = int32(binary.BigEndian.Uint32(data[148:152]))
  222. pvd.OptTypeMPathTableLoc = int32(binary.BigEndian.Uint32(data[152:156]))
  223. if pvd.RootDirectoryEntry == nil {
  224. pvd.RootDirectoryEntry = &DirectoryEntry{}
  225. }
  226. if err = pvd.RootDirectoryEntry.UnmarshalBinary(data[156:190]); err != nil {
  227. return err
  228. }
  229. pvd.VolumeSetIdentifier = strings.TrimRight(string(data[190:318]), " ")
  230. pvd.PublisherIdentifier = strings.TrimRight(string(data[318:446]), " ")
  231. pvd.DataPreparerIdentifier = strings.TrimRight(string(data[446:574]), " ")
  232. pvd.ApplicationIdentifier = strings.TrimRight(string(data[574:702]), " ")
  233. pvd.CopyrightFileIdentifier = strings.TrimRight(string(data[702:740]), " ")
  234. pvd.AbstractFileIdentifier = strings.TrimRight(string(data[740:776]), " ")
  235. pvd.BibliographicFileIdentifier = strings.TrimRight(string(data[776:813]), " ")
  236. if pvd.VolumeCreationDateAndTime.UnmarshalBinary(data[813:830]) != nil {
  237. return err
  238. }
  239. if pvd.VolumeModificationDateAndTime.UnmarshalBinary(data[830:847]) != nil {
  240. return err
  241. }
  242. if pvd.VolumeExpirationDateAndTime.UnmarshalBinary(data[847:864]) != nil {
  243. return err
  244. }
  245. if pvd.VolumeEffectiveDateAndTime.UnmarshalBinary(data[864:881]) != nil {
  246. return err
  247. }
  248. pvd.FileStructureVersion = data[881]
  249. copy(pvd.ApplicationUsed[:], data[883:1395])
  250. return nil
  251. }
  252. // MarshalBinary encodes the PrimaryVolumeDescriptorBody to its binary form
  253. func (pvd PrimaryVolumeDescriptorBody) MarshalBinary() ([]byte, error) {
  254. output := make([]byte, sectorSize)
  255. d := MarshalString(pvd.SystemIdentifier, 32)
  256. copy(output[8:40], d)
  257. d = MarshalString(pvd.VolumeIdentifier, 32)
  258. copy(output[40:72], d)
  259. WriteInt32LSBMSB(output[80:88], pvd.VolumeSpaceSize)
  260. WriteInt16LSBMSB(output[120:124], pvd.VolumeSetSize)
  261. WriteInt16LSBMSB(output[124:128], pvd.VolumeSequenceNumber)
  262. WriteInt16LSBMSB(output[128:132], pvd.LogicalBlockSize)
  263. WriteInt32LSBMSB(output[132:140], pvd.PathTableSize)
  264. binary.LittleEndian.PutUint32(output[140:144], uint32(pvd.TypeLPathTableLoc))
  265. binary.LittleEndian.PutUint32(output[144:148], uint32(pvd.OptTypeLPathTableLoc))
  266. binary.BigEndian.PutUint32(output[148:152], uint32(pvd.TypeMPathTableLoc))
  267. binary.BigEndian.PutUint32(output[152:156], uint32(pvd.OptTypeMPathTableLoc))
  268. binaryRDE, err := pvd.RootDirectoryEntry.MarshalBinary()
  269. if err != nil {
  270. return nil, err
  271. }
  272. copy(output[156:190], binaryRDE)
  273. copy(output[190:318], MarshalString(pvd.VolumeSetIdentifier, 128))
  274. copy(output[318:446], MarshalString(pvd.PublisherIdentifier, 128))
  275. copy(output[446:574], MarshalString(pvd.DataPreparerIdentifier, 128))
  276. copy(output[574:702], MarshalString(pvd.ApplicationIdentifier, 128))
  277. copy(output[702:740], MarshalString(pvd.CopyrightFileIdentifier, 38))
  278. copy(output[740:776], MarshalString(pvd.AbstractFileIdentifier, 36))
  279. copy(output[776:813], MarshalString(pvd.BibliographicFileIdentifier, 37))
  280. d, err = pvd.VolumeCreationDateAndTime.MarshalBinary()
  281. if err != nil {
  282. return nil, err
  283. }
  284. copy(output[813:830], d)
  285. d, err = pvd.VolumeModificationDateAndTime.MarshalBinary()
  286. if err != nil {
  287. return nil, err
  288. }
  289. copy(output[830:847], d)
  290. d, err = pvd.VolumeExpirationDateAndTime.MarshalBinary()
  291. if err != nil {
  292. return nil, err
  293. }
  294. copy(output[847:864], d)
  295. d, err = pvd.VolumeEffectiveDateAndTime.MarshalBinary()
  296. if err != nil {
  297. return nil, err
  298. }
  299. copy(output[864:881], d)
  300. output[881] = pvd.FileStructureVersion
  301. output[882] = 0
  302. copy(output[883:1395], pvd.ApplicationUsed[:])
  303. for i := 1395; i < 2048; i++ {
  304. output[i] = 0
  305. }
  306. return output, nil
  307. }
  308. // UnmarshalBinary decodes a BootVolumeDescriptorBody from binary form
  309. func (bvd *BootVolumeDescriptorBody) UnmarshalBinary(data []byte) error {
  310. bvd.BootSystemIdentifier = strings.TrimRight(string(data[7:39]), " ")
  311. bvd.BootIdentifier = strings.TrimRight(string(data[39:71]), " ")
  312. if n := copy(bvd.BootSystemUse[:], data[71:2048]); n != 1977 {
  313. return fmt.Errorf("BootVolumeDescriptorBody.UnmarshalBinary: copied %d bytes", n)
  314. }
  315. return nil
  316. }
  317. type volumeDescriptor struct {
  318. Header volumeDescriptorHeader
  319. Boot *BootVolumeDescriptorBody
  320. Primary *PrimaryVolumeDescriptorBody
  321. }
  322. var _ encoding.BinaryUnmarshaler = &volumeDescriptor{}
  323. var _ encoding.BinaryMarshaler = &volumeDescriptor{}
  324. func (vd volumeDescriptor) Type() byte {
  325. return vd.Header.Type
  326. }
  327. // UnmarshalBinary decodes a volumeDescriptor from binary form
  328. func (vd *volumeDescriptor) UnmarshalBinary(data []byte) error {
  329. if uint32(len(data)) < sectorSize {
  330. return io.ErrUnexpectedEOF
  331. }
  332. if err := vd.Header.UnmarshalBinary(data); err != nil {
  333. // this should never fail, since volumeDescriptorHeader.UnmarshalBinary( ) only checks data size too
  334. return err
  335. }
  336. id := string(vd.Header.Identifier[:])
  337. if id != standardIdentifier {
  338. if id == udfIdentifier {
  339. return ErrUDFNotSupported
  340. }
  341. return fmt.Errorf("volume descriptor %q != %q", id, standardIdentifier)
  342. }
  343. switch vd.Header.Type {
  344. case volumeTypeBoot:
  345. vd.Boot = &BootVolumeDescriptorBody{}
  346. return vd.Boot.UnmarshalBinary(data)
  347. case volumeTypePartition:
  348. return errors.New("partition volumes are not yet supported")
  349. case volumeTypePrimary, volumeTypeSupplementary:
  350. vd.Primary = &PrimaryVolumeDescriptorBody{}
  351. return vd.Primary.UnmarshalBinary(data)
  352. case volumeTypeTerminator:
  353. return nil
  354. }
  355. return fmt.Errorf("unknown volume type 0x%X", vd.Header.Type)
  356. }
  357. // UnmarshalBinary decodes a volumeDescriptor from binary form
  358. func (vd volumeDescriptor) MarshalBinary() ([]byte, error) {
  359. var output []byte
  360. var err error
  361. switch vd.Header.Type {
  362. case volumeTypeBoot:
  363. return nil, errors.New("boot volumes are not yet supported")
  364. case volumeTypePartition:
  365. return nil, errors.New("partition volumes are not yet supported")
  366. case volumeTypePrimary, volumeTypeSupplementary:
  367. if output, err = vd.Primary.MarshalBinary(); err != nil {
  368. return nil, err
  369. }
  370. case volumeTypeTerminator:
  371. output = make([]byte, sectorSize)
  372. }
  373. data, err := vd.Header.MarshalBinary()
  374. if err != nil {
  375. return nil, err
  376. }
  377. copy(output[0:7], data)
  378. return output, nil
  379. }
  380. // VolumeDescriptorTimestamp represents a time and date format
  381. // that can be encoded according to ECMA-119 8.4.26.1
  382. type VolumeDescriptorTimestamp struct {
  383. Year int
  384. Month int
  385. Day int
  386. Hour int
  387. Minute int
  388. Second int
  389. Hundredth int
  390. Offset int
  391. }
  392. var _ encoding.BinaryMarshaler = &VolumeDescriptorTimestamp{}
  393. var _ encoding.BinaryUnmarshaler = &VolumeDescriptorTimestamp{}
  394. // MarshalBinary encodes the timestamp into a binary form
  395. func (ts *VolumeDescriptorTimestamp) MarshalBinary() ([]byte, error) {
  396. formatted := fmt.Sprintf("%04d%02d%02d%02d%02d%02d%02d", ts.Year, ts.Month, ts.Day, ts.Hour, ts.Minute, ts.Second, ts.Hundredth)
  397. formattedBytes := append([]byte(formatted), byte(ts.Offset))
  398. if len(formattedBytes) != 17 {
  399. return nil, fmt.Errorf("VolumeDescriptorTimestamp.MarshalBinary: the formatted timestamp is %d bytes long", len(formatted))
  400. }
  401. return formattedBytes, nil
  402. }
  403. // UnmarshalBinary decodes a VolumeDescriptorTimestamp from binary form
  404. func (ts *VolumeDescriptorTimestamp) UnmarshalBinary(data []byte) error {
  405. if len(data) < 17 {
  406. return io.ErrUnexpectedEOF
  407. }
  408. year, err := strconv.Atoi(strings.TrimSpace(string(data[0:4])))
  409. if err != nil {
  410. return err
  411. }
  412. month, err := strconv.Atoi(strings.TrimSpace(string(data[4:6])))
  413. if err != nil {
  414. return err
  415. }
  416. day, err := strconv.Atoi(strings.TrimSpace(string(data[6:8])))
  417. if err != nil {
  418. return err
  419. }
  420. hour, err := strconv.Atoi(strings.TrimSpace(string(data[8:10])))
  421. if err != nil {
  422. return err
  423. }
  424. min, err := strconv.Atoi(strings.TrimSpace(string(data[10:12])))
  425. if err != nil {
  426. return err
  427. }
  428. sec, err := strconv.Atoi(strings.TrimSpace(string(data[12:14])))
  429. if err != nil {
  430. return err
  431. }
  432. hundredth, err := strconv.Atoi(strings.TrimSpace(string(data[14:16])))
  433. if err != nil {
  434. return err
  435. }
  436. *ts = VolumeDescriptorTimestamp{
  437. Year: year,
  438. Month: month,
  439. Day: day,
  440. Hour: hour,
  441. Minute: min,
  442. Second: sec,
  443. Hundredth: hundredth,
  444. Offset: int(data[16]),
  445. }
  446. return nil
  447. }
  448. // RecordingTimestamp represents a time and date format
  449. // that can be encoded according to ECMA-119 9.1.5
  450. type RecordingTimestamp time.Time
  451. var _ encoding.BinaryUnmarshaler = &RecordingTimestamp{}
  452. // UnmarshalBinary decodes a RecordingTimestamp from binary form
  453. func (ts *RecordingTimestamp) UnmarshalBinary(data []byte) error {
  454. if len(data) < 7 {
  455. return io.ErrUnexpectedEOF
  456. }
  457. year := 1900 + int(data[0])
  458. month := int(data[1])
  459. day := int(data[2])
  460. hour := int(data[3])
  461. min := int(data[4])
  462. sec := int(data[5])
  463. tzOffset := int(data[6])
  464. secondsInAQuarter := 60 * 15
  465. tz := time.FixedZone("", tzOffset*secondsInAQuarter)
  466. *ts = RecordingTimestamp(time.Date(year, time.Month(month), day, hour, min, sec, 0, tz))
  467. return nil
  468. }
  469. // MarshalBinary encodes the RecordingTimestamp in its binary form to a buffer
  470. // of the length of 7 or more bytes
  471. func (ts RecordingTimestamp) MarshalBinary(dst []byte) {
  472. _ = dst[6] // early bounds check to guarantee safety of writes below
  473. t := time.Time(ts)
  474. year, month, day := t.Date()
  475. hour, min, sec := t.Clock()
  476. _, secOffset := t.Zone()
  477. secondsInAQuarter := 60 * 15
  478. offsetInQuarters := secOffset / secondsInAQuarter
  479. dst[0] = byte(year - 1900)
  480. dst[1] = byte(month)
  481. dst[2] = byte(day)
  482. dst[3] = byte(hour)
  483. dst[4] = byte(min)
  484. dst[5] = byte(sec)
  485. dst[6] = byte(offsetInQuarters)
  486. }
  487. // VolumeDescriptorTimestampFromTime converts time.Time to VolumeDescriptorTimestamp
  488. func VolumeDescriptorTimestampFromTime(t time.Time) VolumeDescriptorTimestamp {
  489. t = t.UTC()
  490. year, month, day := t.Date()
  491. hour, minute, second := t.Clock()
  492. hundredth := t.Nanosecond() / 10000000
  493. return VolumeDescriptorTimestamp{
  494. Year: year,
  495. Month: int(month),
  496. Day: day,
  497. Hour: hour,
  498. Minute: minute,
  499. Second: second,
  500. Hundredth: hundredth,
  501. Offset: 0, // we converted to UTC
  502. }
  503. }