1use std::io::{Read, Seek, SeekFrom};
47
48use crate::error::{Error, Result};
49
50pub const SECTOR_SIZE: u64 = 2048;
52pub const AVDP_SECTOR_PRIMARY: u64 = 256;
55pub const AVDP_SECTOR_SECONDARY: u64 = 512;
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66#[repr(u16)]
67pub enum TagId {
68 PrimaryVolume = 1,
69 AnchorVolumeDescriptorPointer = 2,
70 VolumeDescriptorPointer = 3,
71 ImplementationUseVolume = 4,
72 Partition = 5,
73 LogicalVolume = 6,
74 UnallocatedSpace = 7,
75 Terminating = 8,
76 LogicalVolumeIntegrity = 9,
77 FileSet = 256,
78 FileIdentifier = 257,
79 AllocationExtent = 258,
80 Indirect = 259,
81 Terminal = 260,
82 FileEntry = 261,
83 ExtendedAttributeHeader = 262,
84 UnallocatedSpaceEntry = 263,
85 SpaceBitmap = 264,
86 PartitionIntegrityEntry = 265,
87 ExtendedFileEntry = 266,
88}
89
90impl TagId {
91 pub fn from_raw(v: u16) -> Option<Self> {
92 Some(match v {
93 1 => Self::PrimaryVolume,
94 2 => Self::AnchorVolumeDescriptorPointer,
95 3 => Self::VolumeDescriptorPointer,
96 4 => Self::ImplementationUseVolume,
97 5 => Self::Partition,
98 6 => Self::LogicalVolume,
99 7 => Self::UnallocatedSpace,
100 8 => Self::Terminating,
101 9 => Self::LogicalVolumeIntegrity,
102 256 => Self::FileSet,
103 257 => Self::FileIdentifier,
104 258 => Self::AllocationExtent,
105 259 => Self::Indirect,
106 260 => Self::Terminal,
107 261 => Self::FileEntry,
108 262 => Self::ExtendedAttributeHeader,
109 263 => Self::UnallocatedSpaceEntry,
110 264 => Self::SpaceBitmap,
111 265 => Self::PartitionIntegrityEntry,
112 266 => Self::ExtendedFileEntry,
113 _ => return None,
114 })
115 }
116}
117
118#[derive(Debug, Clone, Copy)]
132pub struct DescriptorTag {
133 pub id: TagId,
134 pub descriptor_version: u16,
135 pub serial_number: u16,
136 pub crc: u16,
137 pub crc_length: u16,
138 pub location: u32,
139}
140
141impl DescriptorTag {
142 pub const SIZE: usize = 16;
143
144 pub fn parse(bytes: &[u8]) -> Result<Self> {
145 if bytes.len() < Self::SIZE {
146 return Err(Error::InvalidUdf("descriptor tag truncated"));
147 }
148 let id_raw = u16::from_le_bytes([bytes[0], bytes[1]]);
149 let id = TagId::from_raw(id_raw).ok_or(Error::InvalidUdf("unknown TagId"))?;
150 let descriptor_version = u16::from_le_bytes([bytes[2], bytes[3]]);
151 let checksum = bytes[4];
152 if bytes[5] != 0 {
153 return Err(Error::InvalidUdf("DescriptorTag reserved byte non-zero"));
154 }
155 let serial_number = u16::from_le_bytes([bytes[6], bytes[7]]);
156 let crc = u16::from_le_bytes([bytes[8], bytes[9]]);
157 let crc_length = u16::from_le_bytes([bytes[10], bytes[11]]);
158 let location = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
159
160 let calc: u32 = bytes[..Self::SIZE]
161 .iter()
162 .enumerate()
163 .filter(|(i, _)| *i != 4)
164 .map(|(_, b)| *b as u32)
165 .sum();
166 if (calc & 0xFF) as u8 != checksum {
167 return Err(Error::InvalidUdf("DescriptorTag checksum mismatch"));
168 }
169
170 Ok(Self {
171 id,
172 descriptor_version,
173 serial_number,
174 crc,
175 crc_length,
176 location,
177 })
178 }
179
180 pub fn encode(&self) -> [u8; Self::SIZE] {
181 let mut out = [0u8; Self::SIZE];
182 let id = self.id as u16;
183 out[0..2].copy_from_slice(&id.to_le_bytes());
184 out[2..4].copy_from_slice(&self.descriptor_version.to_le_bytes());
185 out[6..8].copy_from_slice(&self.serial_number.to_le_bytes());
186 out[8..10].copy_from_slice(&self.crc.to_le_bytes());
187 out[10..12].copy_from_slice(&self.crc_length.to_le_bytes());
188 out[12..16].copy_from_slice(&self.location.to_le_bytes());
189 let sum: u32 = out
190 .iter()
191 .enumerate()
192 .filter(|(i, _)| *i != 4)
193 .map(|(_, b)| *b as u32)
194 .sum();
195 out[4] = (sum & 0xFF) as u8;
196 out
197 }
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub struct ExtentAd {
204 pub length: u32, pub location: u32, }
207
208impl ExtentAd {
209 pub const SIZE: usize = 8;
210 pub fn parse(bytes: &[u8]) -> Result<Self> {
211 if bytes.len() < Self::SIZE {
212 return Err(Error::InvalidUdf("extent_ad truncated"));
213 }
214 Ok(Self {
215 length: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
216 location: u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
217 })
218 }
219
220 pub fn encode(self) -> [u8; Self::SIZE] {
221 let mut out = [0u8; Self::SIZE];
222 out[0..4].copy_from_slice(&self.length.to_le_bytes());
223 out[4..8].copy_from_slice(&self.location.to_le_bytes());
224 out
225 }
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231pub struct ShortAd {
232 pub length: u32,
233 pub extent_type: u8,
234 pub block_location: u32,
235}
236
237impl ShortAd {
238 pub const SIZE: usize = 8;
239 pub fn parse(bytes: &[u8]) -> Result<Self> {
240 if bytes.len() < Self::SIZE {
241 return Err(Error::InvalidUdf("short_ad truncated"));
242 }
243 let raw_len = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
244 let block_location = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
245 Ok(Self {
246 length: raw_len & 0x3FFF_FFFF,
247 extent_type: ((raw_len >> 30) & 0b11) as u8,
248 block_location,
249 })
250 }
251
252 pub fn encode(self) -> [u8; Self::SIZE] {
253 let mut out = [0u8; Self::SIZE];
254 let raw_len = (self.length & 0x3FFF_FFFF) | ((self.extent_type as u32 & 0b11) << 30);
255 out[0..4].copy_from_slice(&raw_len.to_le_bytes());
256 out[4..8].copy_from_slice(&self.block_location.to_le_bytes());
257 out
258 }
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub struct LbAddr {
265 pub block: u32,
266 pub partition_ref: u16,
267}
268
269impl LbAddr {
270 pub const SIZE: usize = 6;
271 pub fn parse(bytes: &[u8]) -> Result<Self> {
272 if bytes.len() < Self::SIZE {
273 return Err(Error::InvalidUdf("lb_addr truncated"));
274 }
275 Ok(Self {
276 block: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
277 partition_ref: u16::from_le_bytes([bytes[4], bytes[5]]),
278 })
279 }
280
281 pub fn encode(self) -> [u8; Self::SIZE] {
282 let mut out = [0u8; Self::SIZE];
283 out[0..4].copy_from_slice(&self.block.to_le_bytes());
284 out[4..6].copy_from_slice(&self.partition_ref.to_le_bytes());
285 out
286 }
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290pub struct LongAd {
291 pub length: u32,
292 pub extent_type: u8,
293 pub location: LbAddr,
294 pub implementation_use: [u8; 6],
295}
296
297impl LongAd {
298 pub const SIZE: usize = 16;
299 pub fn parse(bytes: &[u8]) -> Result<Self> {
300 if bytes.len() < Self::SIZE {
301 return Err(Error::InvalidUdf("long_ad truncated"));
302 }
303 let raw_len = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
304 let location = LbAddr::parse(&bytes[4..10])?;
305 let mut impl_use = [0u8; 6];
306 impl_use.copy_from_slice(&bytes[10..16]);
307 Ok(Self {
308 length: raw_len & 0x3FFF_FFFF,
309 extent_type: ((raw_len >> 30) & 0b11) as u8,
310 location,
311 implementation_use: impl_use,
312 })
313 }
314
315 pub fn encode(self) -> [u8; Self::SIZE] {
316 let mut out = [0u8; Self::SIZE];
317 let raw_len = (self.length & 0x3FFF_FFFF) | ((self.extent_type as u32 & 0b11) << 30);
318 out[0..4].copy_from_slice(&raw_len.to_le_bytes());
319 out[4..10].copy_from_slice(&self.location.encode());
320 out[10..16].copy_from_slice(&self.implementation_use);
321 out
322 }
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq)]
330pub struct ExtAd {
331 pub length: u32,
332 pub extent_type: u8,
333 pub recorded_length: u32,
334 pub information_length: u32,
335 pub location: LbAddr,
336 pub implementation_use: [u8; 2],
337}
338
339impl ExtAd {
340 pub const SIZE: usize = 20;
341 pub fn parse(bytes: &[u8]) -> Result<Self> {
342 if bytes.len() < Self::SIZE {
343 return Err(Error::InvalidUdf("ext_ad truncated"));
344 }
345 let raw_len = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
346 let recorded_length = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
347 let information_length = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
348 let location = LbAddr::parse(&bytes[12..18])?;
349 let mut impl_use = [0u8; 2];
350 impl_use.copy_from_slice(&bytes[18..20]);
351 Ok(Self {
352 length: raw_len & 0x3FFF_FFFF,
353 extent_type: ((raw_len >> 30) & 0b11) as u8,
354 recorded_length,
355 information_length,
356 location,
357 implementation_use: impl_use,
358 })
359 }
360
361 pub fn encode(self) -> [u8; Self::SIZE] {
362 let mut out = [0u8; Self::SIZE];
363 let raw_len = (self.length & 0x3FFF_FFFF) | ((self.extent_type as u32 & 0b11) << 30);
364 out[0..4].copy_from_slice(&raw_len.to_le_bytes());
365 out[4..8].copy_from_slice(&self.recorded_length.to_le_bytes());
366 out[8..12].copy_from_slice(&self.information_length.to_le_bytes());
367 out[12..18].copy_from_slice(&self.location.encode());
368 out[18..20].copy_from_slice(&self.implementation_use);
369 out
370 }
371}
372
373pub fn decode_dstring(payload: &[u8]) -> Result<String> {
379 if payload.is_empty() {
380 return Ok(String::new());
381 }
382 match payload[0] {
383 0 => Ok(String::new()),
384 8 => {
385 let mut s = String::with_capacity(payload.len() - 1);
386 for &b in &payload[1..] {
387 if b == 0 {
388 break;
389 }
390 s.push(b as char);
391 }
392 Ok(s)
393 }
394 16 => {
395 let body = &payload[1..];
396 if body.len() % 2 != 0 {
397 return Err(Error::InvalidUdf("16-bit d-string with odd byte count"));
398 }
399 let mut s = String::with_capacity(body.len() / 2);
400 for chunk in body.chunks_exact(2) {
401 let cp = u16::from_be_bytes([chunk[0], chunk[1]]);
402 if cp == 0 {
403 break;
404 }
405 if let Some(c) = char::from_u32(cp as u32) {
406 s.push(c);
407 }
408 }
409 Ok(s)
410 }
411 _ => Err(Error::InvalidUdf("unknown d-string compression id")),
412 }
413}
414
415pub fn decode_dstring_field(field: &[u8]) -> Result<String> {
418 if field.is_empty() {
419 return Ok(String::new());
420 }
421 let len = *field.last().unwrap() as usize;
422 if len > field.len() - 1 {
423 return Err(Error::InvalidUdf("d-string length overflows field"));
424 }
425 decode_dstring(&field[..len])
426}
427
428#[derive(Debug, Clone, Copy)]
431pub struct AnchorVolumeDescriptorPointer {
432 pub tag: DescriptorTag,
433 pub main_volume_descriptor_sequence: ExtentAd,
434 pub reserve_volume_descriptor_sequence: ExtentAd,
435}
436
437impl AnchorVolumeDescriptorPointer {
438 pub fn parse(bytes: &[u8]) -> Result<Self> {
439 let tag = DescriptorTag::parse(bytes)?;
440 if tag.id != TagId::AnchorVolumeDescriptorPointer {
441 return Err(Error::InvalidUdf("expected AVDP tag"));
442 }
443 let main = ExtentAd::parse(&bytes[16..24])?;
444 let reserve = ExtentAd::parse(&bytes[24..32])?;
445 Ok(Self {
446 tag,
447 main_volume_descriptor_sequence: main,
448 reserve_volume_descriptor_sequence: reserve,
449 })
450 }
451}
452
453#[derive(Debug, Clone)]
456pub struct PrimaryVolumeDescriptor {
457 pub tag: DescriptorTag,
458 pub volume_descriptor_sequence_number: u32,
459 pub primary_volume_descriptor_number: u32,
460 pub volume_identifier: String,
461}
462
463impl PrimaryVolumeDescriptor {
464 pub fn parse(bytes: &[u8]) -> Result<Self> {
465 let tag = DescriptorTag::parse(bytes)?;
466 if tag.id != TagId::PrimaryVolume {
467 return Err(Error::InvalidUdf("expected PVD tag"));
468 }
469 if bytes.len() < 56 {
470 return Err(Error::InvalidUdf("PVD truncated"));
471 }
472 let vds_n = u32::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]);
473 let pvd_n = u32::from_le_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]);
474 let volume_identifier = decode_dstring_field(&bytes[24..56])?;
475 Ok(Self {
476 tag,
477 volume_descriptor_sequence_number: vds_n,
478 primary_volume_descriptor_number: pvd_n,
479 volume_identifier,
480 })
481 }
482}
483
484#[derive(Debug, Clone)]
487pub struct PartitionDescriptor {
488 pub tag: DescriptorTag,
489 pub volume_descriptor_sequence_number: u32,
490 pub partition_flags: u16,
491 pub partition_number: u16,
492 pub partition_starting_location: u32,
493 pub partition_length: u32,
494}
495
496impl PartitionDescriptor {
497 pub fn parse(bytes: &[u8]) -> Result<Self> {
498 let tag = DescriptorTag::parse(bytes)?;
499 if tag.id != TagId::Partition {
500 return Err(Error::InvalidUdf("expected PD tag"));
501 }
502 if bytes.len() < 196 {
503 return Err(Error::InvalidUdf("PD truncated"));
504 }
505 let vds_n = u32::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]);
506 let part_flags = u16::from_le_bytes([bytes[20], bytes[21]]);
507 let part_num = u16::from_le_bytes([bytes[22], bytes[23]]);
508 let part_start = u32::from_le_bytes([bytes[188], bytes[189], bytes[190], bytes[191]]);
509 let part_len = u32::from_le_bytes([bytes[192], bytes[193], bytes[194], bytes[195]]);
510 Ok(Self {
511 tag,
512 volume_descriptor_sequence_number: vds_n,
513 partition_flags: part_flags,
514 partition_number: part_num,
515 partition_starting_location: part_start,
516 partition_length: part_len,
517 })
518 }
519}
520
521#[derive(Debug, Clone)]
524pub struct LogicalVolumeDescriptor {
525 pub tag: DescriptorTag,
526 pub volume_descriptor_sequence_number: u32,
527 pub logical_volume_identifier: String,
528 pub logical_block_size: u32,
529 pub file_set_descriptor_location: LongAd,
530}
531
532impl LogicalVolumeDescriptor {
533 pub fn parse(bytes: &[u8]) -> Result<Self> {
534 let tag = DescriptorTag::parse(bytes)?;
535 if tag.id != TagId::LogicalVolume {
536 return Err(Error::InvalidUdf("expected LVD tag"));
537 }
538 if bytes.len() < 440 {
539 return Err(Error::InvalidUdf("LVD truncated"));
540 }
541 let vds_n = u32::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]);
542 let lvi = decode_dstring_field(&bytes[84..212])?;
543 let lbs = u32::from_le_bytes([bytes[212], bytes[213], bytes[214], bytes[215]]);
544 let fsd = LongAd::parse(&bytes[248..264])?;
545 Ok(Self {
546 tag,
547 volume_descriptor_sequence_number: vds_n,
548 logical_volume_identifier: lvi,
549 logical_block_size: lbs,
550 file_set_descriptor_location: fsd,
551 })
552 }
553}
554
555#[derive(Debug, Clone)]
558pub struct LogicalVolumeIntegrityDescriptor {
559 pub tag: DescriptorTag,
560 pub number_of_partitions: u32,
561 pub length_of_implementation_use: u32,
562}
563
564impl LogicalVolumeIntegrityDescriptor {
565 pub fn parse(bytes: &[u8]) -> Result<Self> {
566 let tag = DescriptorTag::parse(bytes)?;
567 if tag.id != TagId::LogicalVolumeIntegrity {
568 return Err(Error::InvalidUdf("expected LVID tag"));
569 }
570 if bytes.len() < 80 {
571 return Err(Error::InvalidUdf("LVID truncated"));
572 }
573 let nop = u32::from_le_bytes([bytes[72], bytes[73], bytes[74], bytes[75]]);
578 let liu = u32::from_le_bytes([bytes[76], bytes[77], bytes[78], bytes[79]]);
579 Ok(Self {
580 tag,
581 number_of_partitions: nop,
582 length_of_implementation_use: liu,
583 })
584 }
585}
586
587#[derive(Debug, Clone)]
590pub struct FileSetDescriptor {
591 pub tag: DescriptorTag,
592 pub root_directory_icb: LongAd,
593}
594
595impl FileSetDescriptor {
596 pub fn parse(bytes: &[u8]) -> Result<Self> {
597 let tag = DescriptorTag::parse(bytes)?;
598 if tag.id != TagId::FileSet {
599 return Err(Error::InvalidUdf("expected FSD tag"));
600 }
601 if bytes.len() < 416 {
602 return Err(Error::InvalidUdf("FSD truncated"));
603 }
604 let root = LongAd::parse(&bytes[400..416])?;
605 Ok(Self {
606 tag,
607 root_directory_icb: root,
608 })
609 }
610}
611
612#[derive(Debug, Clone)]
615pub struct FileIdentifierDescriptor {
616 pub tag: DescriptorTag,
617 pub file_version_number: u16,
618 pub file_characteristics: u8,
619 pub identifier: String,
620 pub icb: LongAd,
621 pub total_size: usize,
624}
625
626impl FileIdentifierDescriptor {
627 pub fn parse(bytes: &[u8]) -> Result<Self> {
628 if bytes.len() < 38 {
629 return Err(Error::InvalidUdf("FID truncated"));
630 }
631 let tag = DescriptorTag::parse(bytes)?;
632 if tag.id != TagId::FileIdentifier {
633 return Err(Error::InvalidUdf("expected FID tag"));
634 }
635 let file_version_number = u16::from_le_bytes([bytes[16], bytes[17]]);
636 let file_characteristics = bytes[18];
637 let len_fi = bytes[19] as usize;
638 let icb = LongAd::parse(&bytes[20..36])?;
639 let len_impl_use = u16::from_le_bytes([bytes[36], bytes[37]]) as usize;
640 let id_off = 38 + len_impl_use;
641 let id_end = id_off + len_fi;
642 if bytes.len() < id_end {
643 return Err(Error::InvalidUdf("FID identifier overruns buffer"));
644 }
645 let identifier = decode_dstring(&bytes[id_off..id_end])?;
646 let total = id_end.div_ceil(4) * 4;
647 Ok(Self {
648 tag,
649 file_version_number,
650 file_characteristics,
651 identifier,
652 icb,
653 total_size: total,
654 })
655 }
656
657 pub fn is_deleted(&self) -> bool {
658 self.file_characteristics & 0x04 != 0
659 }
660 pub fn is_directory(&self) -> bool {
661 self.file_characteristics & 0x02 != 0
662 }
663 pub fn is_parent(&self) -> bool {
664 self.file_characteristics & 0x08 != 0
665 }
666}
667
668#[derive(Debug, Clone, Copy)]
671pub struct IcbTag {
672 pub prior_recorded_entries: u32,
673 pub strategy_type: u16,
674 pub strategy_parameter: u16,
675 pub max_entries: u16,
676 pub file_type: u8,
677 pub parent_icb: LbAddr,
678 pub flags: u16,
679}
680
681impl IcbTag {
682 pub const SIZE: usize = 20;
683 pub fn parse(bytes: &[u8]) -> Result<Self> {
684 if bytes.len() < Self::SIZE {
685 return Err(Error::InvalidUdf("ICB tag truncated"));
686 }
687 Ok(Self {
688 prior_recorded_entries: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
689 strategy_type: u16::from_le_bytes([bytes[4], bytes[5]]),
690 strategy_parameter: u16::from_le_bytes([bytes[6], bytes[7]]),
691 max_entries: u16::from_le_bytes([bytes[8], bytes[9]]),
692 file_type: bytes[11],
693 parent_icb: LbAddr::parse(&bytes[12..18])?,
694 flags: u16::from_le_bytes([bytes[18], bytes[19]]),
695 })
696 }
697}
698
699#[derive(Debug, Clone, Copy, PartialEq, Eq)]
702pub enum AdType {
703 Short,
704 Long,
705 Extended,
706 EmbeddedInIcb,
707}
708
709impl AdType {
710 pub fn from_flags(flags: u16) -> Result<Self> {
711 match flags & 0b111 {
712 0 => Ok(Self::Short),
713 1 => Ok(Self::Long),
714 2 => Ok(Self::Extended),
715 3 => Ok(Self::EmbeddedInIcb),
716 _ => Err(Error::InvalidUdf("unknown ad_type")),
717 }
718 }
719}
720
721#[derive(Debug, Clone, Copy)]
725pub struct Extent {
726 pub length: u32,
728 pub block_location: u32,
730 pub extent_type: u8,
733}
734
735#[derive(Debug, Clone)]
736pub struct FileEntry {
737 pub tag: DescriptorTag,
738 pub icb_tag: IcbTag,
739 pub uid: u32,
740 pub gid: u32,
741 pub permissions: u32,
742 pub file_link_count: u16,
743 pub record_format: u8,
744 pub record_display_attributes: u8,
745 pub record_length: u32,
746 pub information_length: u64,
747 pub logical_blocks_recorded: u64,
748 pub length_of_extended_attributes: u32,
749 pub length_of_allocation_descriptors: u32,
750 pub extents: Vec<Extent>,
751 pub embedded_data: Vec<u8>,
752 pub ad_type: AdType,
753}
754
755impl FileEntry {
756 pub const PREFIX_SIZE: usize = 176;
757
758 pub fn parse(bytes: &[u8]) -> Result<Self> {
759 if bytes.len() < Self::PREFIX_SIZE {
760 return Err(Error::InvalidUdf("FileEntry truncated"));
761 }
762 let tag = DescriptorTag::parse(bytes)?;
763 if tag.id != TagId::FileEntry {
764 return Err(Error::InvalidUdf("expected FileEntry tag (no EFE in 1.02)"));
766 }
767 let icb_tag = IcbTag::parse(&bytes[16..36])?;
768 let uid = u32::from_le_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]);
769 let gid = u32::from_le_bytes([bytes[40], bytes[41], bytes[42], bytes[43]]);
770 let permissions = u32::from_le_bytes([bytes[44], bytes[45], bytes[46], bytes[47]]);
771 let flc = u16::from_le_bytes([bytes[48], bytes[49]]);
772 let rec_format = bytes[50];
773 let rec_disp_attr = bytes[51];
774 let rec_len = u32::from_le_bytes([bytes[52], bytes[53], bytes[54], bytes[55]]);
775 let info_len = u64::from_le_bytes([
776 bytes[56], bytes[57], bytes[58], bytes[59], bytes[60], bytes[61], bytes[62], bytes[63],
777 ]);
778 let lbr = u64::from_le_bytes([
779 bytes[64], bytes[65], bytes[66], bytes[67], bytes[68], bytes[69], bytes[70], bytes[71],
780 ]);
781 let l_ea = u32::from_le_bytes([bytes[168], bytes[169], bytes[170], bytes[171]]);
782 let l_ad = u32::from_le_bytes([bytes[172], bytes[173], bytes[174], bytes[175]]);
783
784 let ad_type = AdType::from_flags(icb_tag.flags)?;
785
786 let ea_off = Self::PREFIX_SIZE;
787 let ea_end = ea_off + l_ea as usize;
788 let ad_off = ea_end;
789 let ad_end = ad_off + l_ad as usize;
790 if bytes.len() < ad_end {
791 return Err(Error::InvalidUdf("FE allocation area overruns FE"));
792 }
793
794 let mut extents = Vec::new();
795 let mut embedded_data = Vec::new();
796 match ad_type {
797 AdType::Short => {
798 let mut o = 0;
799 while o + ShortAd::SIZE <= l_ad as usize {
800 let ad = ShortAd::parse(&bytes[ad_off + o..ad_off + o + ShortAd::SIZE])?;
801 if ad.length == 0 {
802 break;
803 }
804 if ad.extent_type == 3 {
805 return Err(Error::InvalidUdf(
806 "Allocation Extent Descriptor continuation unsupported",
807 ));
808 }
809 extents.push(Extent {
810 length: ad.length,
811 block_location: ad.block_location,
812 extent_type: ad.extent_type,
813 });
814 o += ShortAd::SIZE;
815 }
816 }
817 AdType::Long => {
818 let mut o = 0;
819 while o + LongAd::SIZE <= l_ad as usize {
820 let ad = LongAd::parse(&bytes[ad_off + o..ad_off + o + LongAd::SIZE])?;
821 if ad.length == 0 {
822 break;
823 }
824 if ad.extent_type == 3 {
825 return Err(Error::InvalidUdf(
826 "Allocation Extent Descriptor continuation unsupported",
827 ));
828 }
829 extents.push(Extent {
830 length: ad.length,
831 block_location: ad.location.block,
832 extent_type: ad.extent_type,
833 });
834 o += LongAd::SIZE;
835 }
836 }
837 AdType::Extended => {
838 let mut o = 0;
839 while o + ExtAd::SIZE <= l_ad as usize {
840 let ad = ExtAd::parse(&bytes[ad_off + o..ad_off + o + ExtAd::SIZE])?;
841 if ad.length == 0 {
842 break;
843 }
844 if ad.extent_type == 3 {
845 return Err(Error::InvalidUdf(
846 "Allocation Extent Descriptor continuation unsupported",
847 ));
848 }
849 extents.push(Extent {
850 length: ad.length,
851 block_location: ad.location.block,
852 extent_type: ad.extent_type,
853 });
854 o += ExtAd::SIZE;
855 }
856 }
857 AdType::EmbeddedInIcb => {
858 embedded_data.extend_from_slice(&bytes[ad_off..ad_end]);
859 }
860 }
861
862 Ok(Self {
863 tag,
864 icb_tag,
865 uid,
866 gid,
867 permissions,
868 file_link_count: flc,
869 record_format: rec_format,
870 record_display_attributes: rec_disp_attr,
871 record_length: rec_len,
872 information_length: info_len,
873 logical_blocks_recorded: lbr,
874 length_of_extended_attributes: l_ea,
875 length_of_allocation_descriptors: l_ad,
876 extents,
877 embedded_data,
878 ad_type,
879 })
880 }
881
882 pub fn is_directory(&self) -> bool {
883 self.icb_tag.file_type == 4
884 }
885}
886
887#[derive(Debug, Clone)]
891pub struct UdfFile {
892 pub name: String,
893 pub is_dir: bool,
894 pub extents: Vec<Extent>,
895 pub length: u64,
896 pub icb: LongAd,
899}
900
901pub struct UdfVolume<R: Read + Seek> {
903 pub reader: R,
904 pub partition_start_sector: u64,
905 pub logical_block_size: u32,
906 pub volume_identifier: String,
907 pub logical_volume_identifier: String,
908 pub root_directory_icb: LongAd,
909}
910
911impl<R: Read + Seek> std::fmt::Debug for UdfVolume<R> {
912 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
913 f.debug_struct("UdfVolume")
914 .field("partition_start_sector", &self.partition_start_sector)
915 .field("logical_block_size", &self.logical_block_size)
916 .field("volume_identifier", &self.volume_identifier)
917 .field("logical_volume_identifier", &self.logical_volume_identifier)
918 .field("root_directory_icb", &self.root_directory_icb)
919 .finish()
920 }
921}
922
923impl<R: Read + Seek> UdfVolume<R> {
924 pub fn open(mut reader: R) -> Result<Self> {
927 let avdp = find_avdp(&mut reader)?;
928 let main = avdp.main_volume_descriptor_sequence;
929 let mut pvd: Option<PrimaryVolumeDescriptor> = None;
930 let mut pd: Option<PartitionDescriptor> = None;
931 let mut lvd: Option<LogicalVolumeDescriptor> = None;
932 let max_sectors = (main.length as u64).div_ceil(SECTOR_SIZE);
933 for i in 0..max_sectors {
934 let sec = main.location as u64 + i;
935 let buf = read_sector(&mut reader, sec)?;
936 let id_raw = u16::from_le_bytes([buf[0], buf[1]]);
937 let tag_id = match TagId::from_raw(id_raw) {
938 Some(t) => t,
939 None => continue,
940 };
941 match tag_id {
942 TagId::PrimaryVolume => pvd = Some(PrimaryVolumeDescriptor::parse(&buf)?),
943 TagId::Partition => pd = Some(PartitionDescriptor::parse(&buf)?),
944 TagId::LogicalVolume => lvd = Some(LogicalVolumeDescriptor::parse(&buf)?),
945 TagId::Terminating => break,
946 _ => {}
947 }
948 }
949 let pvd = pvd.ok_or(Error::InvalidUdf("no Primary Volume Descriptor"))?;
950 let pd = pd.ok_or(Error::InvalidUdf("no Partition Descriptor"))?;
951 let lvd = lvd.ok_or(Error::InvalidUdf("no Logical Volume Descriptor"))?;
952
953 if lvd.logical_block_size as u64 != SECTOR_SIZE {
954 return Err(Error::InvalidUdf(
955 "logical_block_size != 2048 (DVD mandates 2048)",
956 ));
957 }
958 if lvd.file_set_descriptor_location.location.partition_ref != pd.partition_number {
959 return Err(Error::InvalidUdf("FSD references non-default partition"));
960 }
961
962 let fsd_sec = pd.partition_starting_location as u64
963 + lvd.file_set_descriptor_location.location.block as u64;
964 let fsd_buf = read_sector_into_vec(&mut reader, fsd_sec)?;
965 let fsd = FileSetDescriptor::parse(&fsd_buf)?;
966
967 Ok(Self {
968 reader,
969 partition_start_sector: pd.partition_starting_location as u64,
970 logical_block_size: lvd.logical_block_size,
971 volume_identifier: pvd.volume_identifier,
972 logical_volume_identifier: lvd.logical_volume_identifier,
973 root_directory_icb: fsd.root_directory_icb,
974 })
975 }
976
977 pub fn read_partition_block(&mut self, partition_block: u64) -> Result<Vec<u8>> {
980 let sec = self.partition_start_sector + partition_block;
981 read_sector_into_vec(&mut self.reader, sec)
982 }
983
984 pub fn read_file_entry(&mut self, icb: LongAd) -> Result<FileEntry> {
986 if icb.length == 0 {
987 return Err(Error::InvalidUdf("FE ICB length 0"));
988 }
989 let buf = self.read_partition_block(icb.location.block as u64)?;
990 FileEntry::parse(&buf)
991 }
992
993 pub fn read_file(&mut self, icb: LongAd) -> Result<Vec<u8>> {
995 let fe = self.read_file_entry(icb)?;
996 let want = fe.information_length as usize;
997 if fe.ad_type == AdType::EmbeddedInIcb {
998 return Ok(fe.embedded_data[..want.min(fe.embedded_data.len())].to_vec());
999 }
1000 let mut out = Vec::with_capacity(want);
1001 for ad in &fe.extents {
1002 if ad.extent_type != 0 {
1003 return Err(Error::InvalidUdf("non-recorded extent in file"));
1004 }
1005 let blocks = (ad.length as u64).div_ceil(SECTOR_SIZE);
1006 for i in 0..blocks {
1007 let buf = self.read_partition_block(ad.block_location as u64 + i)?;
1008 let to_copy =
1009 (ad.length as usize).saturating_sub(i as usize * SECTOR_SIZE as usize);
1010 let take = to_copy.min(SECTOR_SIZE as usize);
1011 out.extend_from_slice(&buf[..take]);
1012 if out.len() >= want {
1013 break;
1014 }
1015 }
1016 if out.len() >= want {
1017 break;
1018 }
1019 }
1020 out.truncate(want);
1021 Ok(out)
1022 }
1023
1024 pub fn read_directory(&mut self, dir_icb: LongAd) -> Result<Vec<UdfFile>> {
1026 let raw = self.read_file(dir_icb)?;
1027 let mut out = Vec::new();
1028 let mut o = 0;
1029 while o + 38 <= raw.len() {
1030 let fid = FileIdentifierDescriptor::parse(&raw[o..])?;
1031 let step = fid.total_size;
1032 if step == 0 {
1033 break;
1034 }
1035 o += step;
1036 if fid.is_deleted() || fid.is_parent() {
1037 continue;
1038 }
1039 let fe = self.read_file_entry(fid.icb)?;
1045 let extents = fe.extents.clone();
1046 out.push(UdfFile {
1047 name: fid.identifier.clone(),
1048 is_dir: fid.is_directory(),
1049 extents,
1050 length: fe.information_length,
1051 icb: fid.icb,
1052 });
1053 }
1054 Ok(out)
1055 }
1056
1057 pub fn enumerate(&mut self) -> Result<Vec<(String, UdfFile)>> {
1060 let mut out = Vec::new();
1061 let root_icb = self.root_directory_icb;
1062 self.enumerate_into("", root_icb, &mut out)?;
1063 Ok(out)
1064 }
1065
1066 fn enumerate_into(
1067 &mut self,
1068 prefix: &str,
1069 dir_icb: LongAd,
1070 out: &mut Vec<(String, UdfFile)>,
1071 ) -> Result<()> {
1072 let entries = self.read_directory(dir_icb)?;
1073 for entry in entries {
1074 let p = if prefix.is_empty() {
1075 entry.name.clone()
1076 } else {
1077 format!("{prefix}/{}", entry.name)
1078 };
1079 if entry.is_dir {
1080 let icb = entry.icb;
1081 out.push((p.clone(), entry));
1082 self.enumerate_into(&p, icb, out)?;
1083 } else {
1084 out.push((p, entry));
1085 }
1086 }
1087 Ok(())
1088 }
1089}
1090
1091pub fn find_avdp<R: Read + Seek>(reader: &mut R) -> Result<AnchorVolumeDescriptorPointer> {
1094 for sec in [AVDP_SECTOR_PRIMARY, AVDP_SECTOR_SECONDARY] {
1095 let Ok(buf) = read_sector(reader, sec) else {
1096 continue;
1097 };
1098 if let Ok(avdp) = AnchorVolumeDescriptorPointer::parse(&buf) {
1099 return Ok(avdp);
1100 }
1101 }
1102 if let Ok(end) = reader.seek(SeekFrom::End(0)) {
1104 let total_sectors = end / SECTOR_SIZE;
1105 if total_sectors > 256 {
1106 let sec = total_sectors - 256;
1107 if let Ok(buf) = read_sector(reader, sec) {
1108 if let Ok(avdp) = AnchorVolumeDescriptorPointer::parse(&buf) {
1109 return Ok(avdp);
1110 }
1111 }
1112 }
1113 }
1114 Err(Error::InvalidUdf(
1115 "no Anchor Volume Descriptor Pointer found",
1116 ))
1117}
1118
1119fn read_sector<R: Read + Seek>(r: &mut R, sector: u64) -> Result<[u8; SECTOR_SIZE as usize]> {
1122 r.seek(SeekFrom::Start(sector * SECTOR_SIZE))?;
1123 let mut buf = [0u8; SECTOR_SIZE as usize];
1124 r.read_exact(&mut buf)?;
1125 Ok(buf)
1126}
1127
1128fn read_sector_into_vec<R: Read + Seek>(r: &mut R, sector: u64) -> Result<Vec<u8>> {
1129 Ok(read_sector(r, sector)?.to_vec())
1130}
1131
1132#[cfg(test)]
1133mod tests {
1134 use super::*;
1135
1136 #[test]
1137 fn descriptor_tag_round_trip() {
1138 let tag = DescriptorTag {
1139 id: TagId::FileSet,
1140 descriptor_version: 2,
1141 serial_number: 0x1234,
1142 crc: 0xABCD,
1143 crc_length: 200,
1144 location: 0xDEAD_BEEF,
1145 };
1146 let bytes = tag.encode();
1147 let parsed = DescriptorTag::parse(&bytes).unwrap();
1148 assert_eq!(parsed.id, TagId::FileSet);
1149 assert_eq!(parsed.descriptor_version, 2);
1150 assert_eq!(parsed.serial_number, 0x1234);
1151 assert_eq!(parsed.location, 0xDEAD_BEEF);
1152 }
1153
1154 #[test]
1155 fn tag_checksum_detects_corruption() {
1156 let tag = DescriptorTag {
1157 id: TagId::FileEntry,
1158 descriptor_version: 2,
1159 serial_number: 1,
1160 crc: 0,
1161 crc_length: 0,
1162 location: 100,
1163 };
1164 let mut bytes = tag.encode();
1165 bytes[12] ^= 0xFF;
1166 assert!(matches!(
1167 DescriptorTag::parse(&bytes),
1168 Err(Error::InvalidUdf(_))
1169 ));
1170 }
1171
1172 #[test]
1173 fn lb_addr_round_trip() {
1174 let a = LbAddr {
1175 block: 0x12345678,
1176 partition_ref: 7,
1177 };
1178 assert_eq!(LbAddr::parse(&a.encode()).unwrap(), a);
1179 }
1180
1181 #[test]
1182 fn short_ad_packs_extent_type() {
1183 let ad = ShortAd {
1184 length: 0x12345678 & 0x3FFF_FFFF,
1185 extent_type: 2,
1186 block_location: 99,
1187 };
1188 let parsed = ShortAd::parse(&ad.encode()).unwrap();
1189 assert_eq!(parsed.length, ad.length);
1190 assert_eq!(parsed.extent_type, ad.extent_type);
1191 assert_eq!(parsed.block_location, ad.block_location);
1192 }
1193
1194 #[test]
1195 fn long_ad_round_trip() {
1196 let a = LongAd {
1197 length: 4096,
1198 extent_type: 0,
1199 location: LbAddr {
1200 block: 12,
1201 partition_ref: 0,
1202 },
1203 implementation_use: [0xAA; 6],
1204 };
1205 let parsed = LongAd::parse(&a.encode()).unwrap();
1206 assert_eq!(parsed, a);
1207 }
1208
1209 #[test]
1210 fn ext_ad_round_trip() {
1211 let a = ExtAd {
1212 length: 2048,
1213 extent_type: 0,
1214 recorded_length: 2048,
1215 information_length: 2048,
1216 location: LbAddr {
1217 block: 77,
1218 partition_ref: 0,
1219 },
1220 implementation_use: [0xCC, 0xDD],
1221 };
1222 let parsed = ExtAd::parse(&a.encode()).unwrap();
1223 assert_eq!(parsed, a);
1224 }
1225
1226 #[test]
1227 fn dstring_8bit() {
1228 let payload = b"\x08VIDEO_TS";
1229 assert_eq!(decode_dstring(payload).unwrap(), "VIDEO_TS");
1230 }
1231
1232 #[test]
1233 fn dstring_16bit_be() {
1234 let mut payload = vec![16u8];
1235 for c in "DVD".chars() {
1236 let v = c as u16;
1237 payload.push((v >> 8) as u8);
1238 payload.push(v as u8);
1239 }
1240 assert_eq!(decode_dstring(&payload).unwrap(), "DVD");
1241 }
1242
1243 #[test]
1244 fn ad_type_from_flags_table() {
1245 assert_eq!(AdType::from_flags(0).unwrap(), AdType::Short);
1246 assert_eq!(AdType::from_flags(1).unwrap(), AdType::Long);
1247 assert_eq!(AdType::from_flags(2).unwrap(), AdType::Extended);
1248 assert_eq!(AdType::from_flags(3).unwrap(), AdType::EmbeddedInIcb);
1249 assert_eq!(AdType::from_flags(0xFFF8).unwrap(), AdType::Short);
1250 }
1251
1252 #[test]
1253 fn lvid_parses_partition_count() {
1254 let mut buf = vec![0u8; SECTOR_SIZE as usize];
1255 let tag = DescriptorTag {
1257 id: TagId::LogicalVolumeIntegrity,
1258 descriptor_version: 2,
1259 serial_number: 0,
1260 crc: 0,
1261 crc_length: 0,
1262 location: 200,
1263 };
1264 buf[..16].copy_from_slice(&tag.encode());
1265 buf[72..76].copy_from_slice(&1u32.to_le_bytes());
1267 buf[76..80].copy_from_slice(&0u32.to_le_bytes());
1268 let lvid = LogicalVolumeIntegrityDescriptor::parse(&buf).unwrap();
1269 assert_eq!(lvid.number_of_partitions, 1);
1270 }
1271}