iso9660: basic directory traversal

This commit is contained in:
Zack Buhman 2024-02-26 14:19:31 +08:00
parent d622ee04a7
commit 1a75b90efc
10 changed files with 378 additions and 0 deletions

119
iso9660/byte_position.py Normal file
View File

@ -0,0 +1,119 @@
import sys
from dataclasses import dataclass
from os.path import splitext
from csv_input import read_input
from generate import renderer
from pprint import pprint
def parse_bp(s):
if ' to ' in s:
start0, end0 = s.split(' to ')
if '(' in end0 and ')' in end0 and 'LEN_' in end0:
end0 = int(start0) - 1
start, end = int(start0), int(end0)
return start, end
else:
start, end = int(start0), int(end0)
assert start <= end, (start, end)
return start, end
else:
start = int(s)
return start, start
def bp_range(start, end):
return set(range(start, end+1))
reserved = 0
def sanitize_field_name(name):
global reserved
if name == "(Reserved for future standardization)" or name == "Unused field":
reserved += 1
return f"_res{reserved}";
if '(' in name:
assert 'LEN_' in name, name
name = name.split('(')[0].strip()
name = name.lower().replace(' ', '_')
return name
def sanitize_content_name(name):
if name == 'Numerical value':
return 'numerical_value'
else:
return 'bytes'
@dataclass
class Field:
start: int
end: int
name: str
content: str
def parse(rows):
seen_bps = set()
seen_names = set()
for row in rows:
start, end = parse_bp(row['BP'])
_range = bp_range(start, end)
assert seen_bps.intersection(_range) == set(), row
seen_bps = seen_bps.union(_range)
field_name = sanitize_field_name(row["Field name"])
assert field_name not in seen_names
seen_names.add(field_name)
content_name = sanitize_content_name(row["Content"])
yield Field(
start=start,
end=end,
name=field_name,
content=content_name
)
type_dict = {
1: 'uint8_t',
2: 'uint16_t',
4: 'uint16_le_be',
8: 'uint32_le_be',
}
def header():
yield "#pragma once"
yield ""
yield "#include <cstdint>"
yield "#include <cstddef>"
yield ""
yield '#include "uint_le_be.hpp"'
yield ""
def render_fields(input_name, fields):
yield f"struct {input_name} {{"
for field in fields:
field_size = (field.end - field.start) + 1
if field.content == 'numerical_value':
assert field_size in type_dict, field
type = type_dict[field_size]
yield f"const {type} {field.name};"
else:
if field_size == 1:
yield f"const uint8_t {field.name};"
elif field_size == 0:
yield f"const uint8_t {field.name}[];"
else:
yield f"const uint8_t {field.name}[{field_size}];"
yield "};"
for field in fields:
yield f"static_assert((offsetof (struct {input_name}, {field.name})) == {field.start - 1});"
if __name__ == "__main__":
input_file = sys.argv[1]
input_name, _ = splitext(input_file)
rows = read_input(input_file)
fields = list(parse(rows))
render, out = renderer()
render(header())
render(render_fields(input_name, fields))
sys.stdout.write(out.getvalue())

1
iso9660/csv_input.py Symbolic link
View File

@ -0,0 +1 @@
../regs/gen/csv_input.py

View File

@ -0,0 +1,12 @@
"BP","Field name","Content"
1,"Length of directory record (LEN_DR)","Numerical value"
2,"Extended attribute record length","Numerical value"
"3 to 10","Location of extent","Numerical value"
"11 to 18","Data length","Numerical value"
"19 to 25","Recording date and time","Recording date and time"
26,"File flags","8 bits"
27,"File unit size","Numerical value"
28,"Interleave gap size","Numerical value"
"29 to 32","Volume sequence number","Numerical value"
33,"Length of file identifier (LEN_FI)","Numerical value"
"34 to (33 + LEN_FI)","File identifier","d-characters, d1-characters, separator 1, separator 2, (00) or (01) byte"
1 BP Field name Content
2 1 Length of directory record (LEN_DR) Numerical value
3 2 Extended attribute record length Numerical value
4 3 to 10 Location of extent Numerical value
5 11 to 18 Data length Numerical value
6 19 to 25 Recording date and time Recording date and time
7 26 File flags 8 bits
8 27 File unit size Numerical value
9 28 Interleave gap size Numerical value
10 29 to 32 Volume sequence number Numerical value
11 33 Length of file identifier (LEN_FI) Numerical value
12 34 to (33 + LEN_FI) File identifier d-characters, d1-characters, separator 1, separator 2, (00) or (01) byte

View File

@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include "uint_le_be.hpp"
struct directory_record {
const uint8_t length_of_directory_record;
const uint8_t extended_attribute_record_length;
const uint32_le_be location_of_extent;
const uint32_le_be data_length;
const uint8_t recording_date_and_time[7];
const uint8_t file_flags;
const uint8_t file_unit_size;
const uint8_t interleave_gap_size;
const uint16_le_be volume_sequence_number;
const uint8_t length_of_file_identifier;
const uint8_t file_identifier[];
};
static_assert((offsetof (struct directory_record, length_of_directory_ecord)) == 0);
static_assert((offsetof (struct directory_record, extended_attribute_record_length)) == 1);
static_assert((offsetof (struct directory_record, location_of_extent)) == 2);
static_assert((offsetof (struct directory_record, data_length)) == 10);
static_assert((offsetof (struct directory_record, recording_date_and_time)) == 18);
static_assert((offsetof (struct directory_record, file_flags)) == 25);
static_assert((offsetof (struct directory_record, file_unit_size)) == 26);
static_assert((offsetof (struct directory_record, interleave_gap_size)) == 27);
static_assert((offsetof (struct directory_record, volume_sequence_number)) == 28);
static_assert((offsetof (struct directory_record, length_of_file_identifier)) == 32);
static_assert((offsetof (struct directory_record, file_identifier)) == 33);

Binary file not shown.

1
iso9660/generate.py Symbolic link
View File

@ -0,0 +1 @@
../regs/gen/generate.py

View File

@ -0,0 +1,34 @@
"BP","Field name","Content"
"1","Volume descriptor type","Numerical value"
"2 to 6","Standard identifier","CD001"
"7","Volume descriptor version","Numerical value"
"8","Unused field","(00) byte"
"9 to 40","System identifier","a-characters"
"41 to 72","Volume identifier","d-characters"
"73 to 80","Unused field","(00) bytes"
"81 to 88","Volume space size","Numerical value"
"89 to 120","Unused field","(00) bytes"
"121 to 124","Volume set size","Numerical value"
"125 to 128","Volume sequence number","Numerical value"
"129 to 132","Logical block size","Numerical value"
"133 to 140","Path table size","Numerical value"
"141 to 144","Location of occurrence of type L path table","Numerical value"
"145 to 148","Location of optional occurence of type L path table","Numerical value"
"149 to 152","Location of occurence of type M path table","Numerical value"
"153 to 156","Location of optional occurence of type M path table","Numerical value"
"157 to 190","Directory record for root directory","34 bytes"
"191 to 318","Volume set identifier","d-characters"
"319 to 446","Publisher identifier","a-characters"
"447 to 574","Data preparer identifier","a-characters"
"575 to 702","Application identifier","a-characters"
"703 to 739","Copyright file identifier","d-characters, separator 1, separator 2"
"740 to 776","Abstract file identifier","d-characters, separator 1, separator 3"
"777 to 813","Bibliographic file identifier","d-characters, separator 1, separator 4"
"814 to 830","Volume creation date and time","Digit(s), numerical value"
"831 to 847","Volume modification date and time","Digit(s), numerical value"
"848 to 864","Volume expiration date and time","Digit(s), numerical value"
"865 to 881","Volume effective date and time","Digit(s), numerical value"
"882","File structure version","numerical value"
"883","(Reserved for future standardization)","(00) byte"
"884 to 1395","Application use","not specified"
"1396 to 2048","(Reserved for future standardization)","(00) bytes"
1 BP Field name Content
2 1 Volume descriptor type Numerical value
3 2 to 6 Standard identifier CD001
4 7 Volume descriptor version Numerical value
5 8 Unused field (00) byte
6 9 to 40 System identifier a-characters
7 41 to 72 Volume identifier d-characters
8 73 to 80 Unused field (00) bytes
9 81 to 88 Volume space size Numerical value
10 89 to 120 Unused field (00) bytes
11 121 to 124 Volume set size Numerical value
12 125 to 128 Volume sequence number Numerical value
13 129 to 132 Logical block size Numerical value
14 133 to 140 Path table size Numerical value
15 141 to 144 Location of occurrence of type L path table Numerical value
16 145 to 148 Location of optional occurence of type L path table Numerical value
17 149 to 152 Location of occurence of type M path table Numerical value
18 153 to 156 Location of optional occurence of type M path table Numerical value
19 157 to 190 Directory record for root directory 34 bytes
20 191 to 318 Volume set identifier d-characters
21 319 to 446 Publisher identifier a-characters
22 447 to 574 Data preparer identifier a-characters
23 575 to 702 Application identifier a-characters
24 703 to 739 Copyright file identifier d-characters, separator 1, separator 2
25 740 to 776 Abstract file identifier d-characters, separator 1, separator 3
26 777 to 813 Bibliographic file identifier d-characters, separator 1, separator 4
27 814 to 830 Volume creation date and time Digit(s), numerical value
28 831 to 847 Volume modification date and time Digit(s), numerical value
29 848 to 864 Volume expiration date and time Digit(s), numerical value
30 865 to 881 Volume effective date and time Digit(s), numerical value
31 882 File structure version numerical value
32 883 (Reserved for future standardization) (00) byte
33 884 to 1395 Application use not specified
34 1396 to 2048 (Reserved for future standardization) (00) bytes

View File

@ -0,0 +1,76 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include "uint_le_be.hpp"
struct primary_volume_descriptor {
const uint8_t volume_descriptor_type;
const uint8_t standard_identifier[5];
const uint8_t volume_descriptor_version;
const uint8_t _res1;
const uint8_t system_identifier[32];
const uint8_t volume_identifier[32];
const uint8_t _res2[8];
const uint32_le_be volume_space_size;
const uint8_t _res3[32];
const uint16_le_be volume_set_size;
const uint16_le_be volume_sequence_number;
const uint16_le_be logical_block_size;
const uint32_le_be path_table_size;
const uint16_le_be location_of_occurrence_of_type_l_path_table;
const uint16_le_be location_of_optional_occurence_of_type_l_path_table;
const uint16_le_be location_of_occurence_of_type_m_path_table;
const uint16_le_be location_of_optional_occurence_of_type_m_path_table;
const uint8_t directory_record_for_root_directory[34];
const uint8_t volume_set_identifier[128];
const uint8_t publisher_identifier[128];
const uint8_t data_preparer_identifier[128];
const uint8_t application_identifier[128];
const uint8_t copyright_file_identifier[37];
const uint8_t abstract_file_identifier[37];
const uint8_t bibliographic_file_identifier[37];
const uint8_t volume_creation_date_and_time[17];
const uint8_t volume_modification_date_and_time[17];
const uint8_t volume_expiration_date_and_time[17];
const uint8_t volume_effective_date_and_time[17];
const uint8_t file_structure_version;
const uint8_t _res4;
const uint8_t application_use[512];
const uint8_t _res5[653];
};
static_assert((offsetof (struct primary_volume_descriptor, volume_descriptor_type)) == 0);
static_assert((offsetof (struct primary_volume_descriptor, standard_identifier)) == 1);
static_assert((offsetof (struct primary_volume_descriptor, volume_descriptor_version)) == 6);
static_assert((offsetof (struct primary_volume_descriptor, _res1)) == 7);
static_assert((offsetof (struct primary_volume_descriptor, system_identifier)) == 8);
static_assert((offsetof (struct primary_volume_descriptor, volume_identifier)) == 40);
static_assert((offsetof (struct primary_volume_descriptor, _res2)) == 72);
static_assert((offsetof (struct primary_volume_descriptor, volume_space_size)) == 80);
static_assert((offsetof (struct primary_volume_descriptor, _res3)) == 88);
static_assert((offsetof (struct primary_volume_descriptor, volume_set_size)) == 120);
static_assert((offsetof (struct primary_volume_descriptor, volume_sequence_number)) == 124);
static_assert((offsetof (struct primary_volume_descriptor, logical_block_size)) == 128);
static_assert((offsetof (struct primary_volume_descriptor, path_table_size)) == 132);
static_assert((offsetof (struct primary_volume_descriptor, location_of_occurrence_of_type_l_path_table)) == 140);
static_assert((offsetof (struct primary_volume_descriptor, location_of_optional_occurence_of_type_l_path_table)) == 144);
static_assert((offsetof (struct primary_volume_descriptor, location_of_occurence_of_type_m_path_table)) == 148);
static_assert((offsetof (struct primary_volume_descriptor, location_of_optional_occurence_of_type_m_path_table)) == 152);
static_assert((offsetof (struct primary_volume_descriptor, directory_record_for_root_directory)) == 156);
static_assert((offsetof (struct primary_volume_descriptor, volume_set_identifier)) == 190);
static_assert((offsetof (struct primary_volume_descriptor, publisher_identifier)) == 318);
static_assert((offsetof (struct primary_volume_descriptor, data_preparer_identifier)) == 446);
static_assert((offsetof (struct primary_volume_descriptor, application_identifier)) == 574);
static_assert((offsetof (struct primary_volume_descriptor, copyright_file_identifier)) == 702);
static_assert((offsetof (struct primary_volume_descriptor, abstract_file_identifier)) == 739);
static_assert((offsetof (struct primary_volume_descriptor, bibliographic_file_identifier)) == 776);
static_assert((offsetof (struct primary_volume_descriptor, volume_creation_date_and_time)) == 813);
static_assert((offsetof (struct primary_volume_descriptor, volume_modification_date_and_time)) == 830);
static_assert((offsetof (struct primary_volume_descriptor, volume_expiration_date_and_time)) == 847);
static_assert((offsetof (struct primary_volume_descriptor, volume_effective_date_and_time)) == 864);
static_assert((offsetof (struct primary_volume_descriptor, file_structure_version)) == 881);
static_assert((offsetof (struct primary_volume_descriptor, _res4)) == 882);
static_assert((offsetof (struct primary_volume_descriptor, application_use)) == 883);
static_assert((offsetof (struct primary_volume_descriptor, _res5)) == 1395);

66
iso9660/test.cpp Normal file
View File

@ -0,0 +1,66 @@
#include <fstream>
#include <string>
#include <iostream>
#include "primary_volume_descriptor.hpp"
#include "directory_record.hpp"
void write_field(const uint8_t * s, const int len)
{
for (int i = 0; i < len; i++) {
std::cout << s[i];
}
std::cout << '\n';
}
int main()
{
std::ifstream ifs("test.iso");
std::string content( (std::istreambuf_iterator<char>(ifs) ),
(std::istreambuf_iterator<char>() ) );
const char * str = content.c_str();
auto pvd = reinterpret_cast<const primary_volume_descriptor *>(&str[2048 * 16]);
write_field(pvd->standard_identifier, 5);
auto root_dr = reinterpret_cast<const directory_record *>(&pvd->directory_record_for_root_directory);
std::cout << "root directory record:" << '\n';
std::cout << root_dr->location_of_extent.get() << '\n';
std::cout << root_dr->data_length.get() << '\n';
uint32_t offset = root_dr->location_of_extent.get() * 2048;
while (true) {
std::cout << "\n\n";
std::cout << "directory entry offset: " << std::hex << offset << '\n';
auto dr = reinterpret_cast<const directory_record *>(&str[offset]);
if (dr->length_of_directory_record == 0)
break;
std::cout << "length_of_directory_record: ";
std::cout << std::dec << (int)dr->length_of_directory_record << '\n';
std::cout << "length_of_file_identifier: ";
std::cout << (int)dr->length_of_file_identifier << '\n';
std::cout << "file_identifier: ";
write_field(dr->file_identifier, dr->length_of_file_identifier);
std::cout << std::hex << "file_flags: " << (int)dr->file_flags << '\n';
if (dr->file_flags == 0) {
std::cout << "location_of_extent: " << dr->location_of_extent.get() << '\n';
std::cout << "data_length: " << std::dec << dr->data_length.get() << '\n';
if (dr->file_identifier[0] != '1') {
const uint32_t extent = dr->location_of_extent.get() * 2048;
auto file = reinterpret_cast<const uint8_t *>(&str[extent]);
std::cout << "---begin file content---\n";
write_field(file, dr->data_length.get());
std::cout << "---end file content---\n";
}
}
offset += dr->length_of_directory_record;
}
return 0;
}

37
iso9660/uint_le_be.hpp Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <cstdint>
#include <bit>
#include <iostream>
template <typename T>
struct uint_le_be {
const uint8_t le[(sizeof (T))];
const uint8_t be[(sizeof (T))];
T get() const {
union {
uint8_t u8[(sizeof (T))];
T uint;
} value;
static_assert((sizeof (value)) == (sizeof (T)));
if constexpr (std::endian::native == std::endian::little) {
for (uint32_t i = 0; i < (sizeof (T)); i++) {
value.u8[i] = le[i];
}
return value.uint;
} else {
for (uint32_t i = 0; i < (sizeof (T)); i++) {
value.u8[i] = be[i];
}
return value.uint;
}
}
};
using uint16_le_be = uint_le_be<uint16_t>;
using uint32_le_be = uint_le_be<uint32_t>;
static_assert((sizeof (uint16_le_be)) == 4);
static_assert((sizeof (uint32_le_be)) == 8);