// IsoImage:对外入口。打开映像、按路径查目录、列目录。
//
// F1 的范围是「结构 + 目录树」:卷描述符、目录记录、路径解析、目录列举。
// 文件内容读取、Joliet、Rock Ridge、写入分别在后续功能里加。
///|
pub struct IsoImage {
data : Bytes
volume_id : String
block_size : Int32
volume_space_size : Int32
root : DirectoryRecord
}
///|
/// 打开一个 ISO 9660 映像(内存中的完整字节流)。
pub fn IsoImage::open(data : Bytes) -> IsoImage raise IsoError {
let descriptors = scan_volume_descriptors(data)
let mut pvd_lba = -1
for d in descriptors {
if d.0 == 1 {
pvd_lba = d.1
break
}
}
if pvd_lba < 0 {
raise BadDescriptor(0)
}
let pvd = parse_primary_volume_descriptor(data, pvd_lba)
// 声明容量必须落在实际数据范围内,避免后续按越界 extent 读取
if pvd.root.extent < 0 ||
(pvd.root.extent + 1) * pvd.logical_block_size > data.length() {
raise Truncated
}
IsoImage::{
data,
volume_id: pvd.volume_id,
block_size: pvd.logical_block_size,
volume_space_size: pvd.volume_space_size,
root: pvd.root,
}
}
///|
pub fn IsoImage::volume_id(self : IsoImage) -> String {
self.volume_id
}
///|
pub fn IsoImage::block_size(self : IsoImage) -> Int32 {
self.block_size
}
///|
pub fn IsoImage::volume_space_size(self : IsoImage) -> Int32 {
self.volume_space_size
}
///|
pub fn IsoImage::root(self : IsoImage) -> DirectoryRecord {
self.root
}
///|
/// ASCII 大小写不敏感比较:ISO 9660 的文件名一律大写,但用户输入通常是混合大小写。
fn ascii_eq_ignore_case(a : String, b : String) -> Bool {
if a.length() != b.length() {
return false
}
let av = a.to_array()
let bv = b.to_array()
for i = 0; i < av.length(); i = i + 1 {
let mut x = av[i].to_int()
let mut y = bv[i].to_int()
if x >= 97 && x <= 122 {
x = x - 32
}
if y >= 97 && y <= 122 {
y = y - 32
}
if x != y {
return false
}
}
true
}
///|
/// 读取一条目录记录所指向的目录数据,返回其中的全部记录(含 . 与 ..)。
fn IsoImage::read_directory(
self : IsoImage,
dir : DirectoryRecord,
) -> Array[DirectoryRecord] raise IsoError {
if !dir.is_dir() {
raise NotDirectory(dir.name)
}
let start = dir.extent * self.block_size
let end = start + dir.size
if start < 0 || end > self.data.length() {
raise Truncated
}
let reader = ByteReader::new(self.data)
reader.seek(start)
let out : Array[DirectoryRecord] = []
while reader.position() < end {
let (record, len) = parse_directory_record(reader)
if len == 0 {
// 填充:跳到下一个扇区边界
let next = (reader.position() / SECTOR_SIZE + 1) * SECTOR_SIZE
reader.seek(if next > end { end } else { next })
} else {
out.push(record)
}
}
out
}
///|
/// 列出目录内容(默认过滤掉 . 与 ..),保持映像里的物理顺序。
pub fn IsoImage::list_dir(
self : IsoImage,
path : String,
) -> Array[DirectoryRecord] raise IsoError {
let dir = self.find_record(path)
let records = self.read_directory(dir)
let out : Array[DirectoryRecord] = []
for r in records {
if !r.is_special() {
out.push(r)
}
}
out
}
///|
/// 按路径查找一条记录;路径以 / 分隔,忽略开头结尾的 / 与连续 /。
pub fn IsoImage::find_record(
self : IsoImage,
path : String,
) -> DirectoryRecord raise IsoError {
let mut current = self.root
let mut pending = false
for segment in path.split("/") {
let seg = segment.to_owned()
if seg == "" {
continue
}
if !current.is_dir() {
raise NotDirectory(current.name)
}
let records = self.read_directory(current)
let mut found = false
for r in records {
if !r.is_special() && ascii_eq_ignore_case(r.name, seg) {
current = r
found = true
break
}
}
if !found {
raise NotFound(path)
}
pending = true
}
let _ = pending
current
}