|
| 1 | +(* https://sar.informatik.hu-berlin.de/research/publications/SAR-PR-2006-05/SAR-PR-2006-05_.pdf *) |
| 2 | +let magic_sequence = 0x2144df1cl |
| 3 | +let gpt_sizeof = 92 (* Gpt.sizeof *) |
| 4 | +let gpt_header_crc32_offset = 16 |
| 5 | +let tar_link_indicator_offset = 156 |
| 6 | + |
| 7 | +let marshal_header ~sector_size buf t = |
| 8 | + if Cstruct.length buf < sector_size || |
| 9 | + Cstruct.length buf < gpt_sizeof || |
| 10 | + sector_size < Tar.Header.length + 4 then |
| 11 | + invalid_arg "Gptar.marshal"; |
| 12 | + (* Replace the header crc32 with the magic sequence. This is (most likely) |
| 13 | + not the correct crc32 checksum for that header, but we will fix that |
| 14 | + later. *) |
| 15 | + let t = { t with Gpt.header_crc32 = magic_sequence } in |
| 16 | + let file_name = |
| 17 | + (* sector_size in [Gpt.marshal_header] is only used to figure out how much |
| 18 | + of the reserved space to zero out. We use [buf] even if Tar will |
| 19 | + overwrite the first 512 bytes. The 'reserved' part of the GPT header |
| 20 | + will be zeroed out. *) |
| 21 | + Gpt.marshal_header ~sector_size buf t; |
| 22 | + Cstruct.to_string buf ~len:gpt_sizeof |
| 23 | + in |
| 24 | + (* The "file" is the first LBA minus the tar header size plus the size of the |
| 25 | + partition table rounded up to [sector_size]. *) |
| 26 | + let file_size = |
| 27 | + let partition_table_size = |
| 28 | + Int32.to_int t.num_partition_entries * Int32.to_int t.partition_size |
| 29 | + in |
| 30 | + sector_size - Tar.Header.length + |
| 31 | + ((partition_table_size + pred sector_size) / sector_size) * sector_size |
| 32 | + in |
| 33 | + let header = |
| 34 | + Tar.Header.make file_name (Int64.of_int file_size) |
| 35 | + in |
| 36 | + (* Now we marshal the tar header which will start with the GPT header (as the |
| 37 | + tar file name). The remainder of the tar fields are at least eight bytes |
| 38 | + into the reserved space of the GPT header (which should be all zero, but |
| 39 | + who's checking?). Tar itself has a checksum of its own header which |
| 40 | + includes the non-reserved part of our GPT header. *) |
| 41 | + Tar.Header.marshal buf header; |
| 42 | + (* Let's fix up the link indicator so tar utilities like GNU tar will skip |
| 43 | + the unknown type. The 'G' link indicator seems unused. *) |
| 44 | + Cstruct.set_char buf tar_link_indicator_offset 'G'; |
| 45 | + (* Next, we compute the crc32 of the sector except for the last 4 bytes *) |
| 46 | + let crc32 = |
| 47 | + Checkseum.Crc32.digest_bigstring |
| 48 | + buf.buffer buf.off (buf.len - 4) |
| 49 | + Checkseum.Crc32.default |
| 50 | + in |
| 51 | + (* Since the tar header's checksum covers the GPT header's checksum we can't |
| 52 | + modify that. However, setting [crc32] at the end of the sector the crc32 |
| 53 | + checksum of the buffer will be [magic_sequence]! This will ensure the the |
| 54 | + GPT header's checksum also works. *) |
| 55 | + Cstruct.LE.set_uint32 buf (sector_size - 4) (Optint.to_int32 crc32) |
0 commit comments