iso9660: basic directory traversal
This commit is contained in:
parent
d622ee04a7
commit
1a75b90efc
119
iso9660/byte_position.py
Normal file
119
iso9660/byte_position.py
Normal 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
1
iso9660/csv_input.py
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../regs/gen/csv_input.py
|
12
iso9660/directory_record.csv
Normal file
12
iso9660/directory_record.csv
Normal 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"
|
|
32
iso9660/directory_record.hpp
Normal file
32
iso9660/directory_record.hpp
Normal 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
1
iso9660/generate.py
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../regs/gen/generate.py
|
34
iso9660/primary_volume_descriptor.csv
Normal file
34
iso9660/primary_volume_descriptor.csv
Normal 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"
|
|
76
iso9660/primary_volume_descriptor.hpp
Normal file
76
iso9660/primary_volume_descriptor.hpp
Normal 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
66
iso9660/test.cpp
Normal 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
37
iso9660/uint_le_be.hpp
Normal 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);
|
Loading…
x
Reference in New Issue
Block a user