1#[cfg(feature = "std")]
4use super::io::{self, Read, ReadExt, Writable};
5
6use crate::types::U32LsbMsb;
7
8#[repr(C)]
9#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
10pub struct SystemUseHeader {
12 pub sig: [u8; 2],
14 pub length: u8,
16 pub version: u8,
18}
19
20#[repr(C)]
24#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
25pub struct ContinuationArea {
26 pub sector: U32LsbMsb,
28 pub offset: U32LsbMsb,
30 pub length: U32LsbMsb,
32}
33
34#[cfg(feature = "std")]
35impl ContinuationArea {
36 pub fn header(&self) -> SystemUseHeader {
38 SystemUseHeader {
39 sig: *b"CE",
40 length: 28,
41 version: 1,
42 }
43 }
44}
45
46#[derive(Debug, Clone, Copy)]
47pub struct PaddingField {
49 pub length: u8,
51}
52
53#[cfg(feature = "std")]
54impl PaddingField {
55 pub fn header(&self) -> SystemUseHeader {
57 SystemUseHeader {
58 sig: *b"PD",
59 length: 4 + self.length,
60 version: 1,
61 }
62 }
63}
64
65#[repr(C)]
66#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
67pub struct SuspIdentifier {
69 pub check_bytes: [u8; 2],
71 pub bytes_skipped: u8,
73}
74
75impl SuspIdentifier {
76 pub fn new(bytes_skipped: u8) -> Self {
78 Self {
79 check_bytes: [0xBE, 0xEF],
80 bytes_skipped,
81 }
82 }
83
84 pub fn is_valid(&self) -> bool {
86 self.check_bytes == [0xBE, 0xEF]
87 }
88}
89
90#[cfg(feature = "std")]
91impl SuspIdentifier {
92 pub fn header(&self) -> SystemUseHeader {
94 SystemUseHeader {
95 sig: *b"SP",
96 length: 7,
97 version: 1,
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy)]
103pub struct SuspTerminator;
105
106#[cfg(feature = "std")]
107impl SuspTerminator {
108 pub fn header(&self) -> SystemUseHeader {
110 SystemUseHeader {
111 sig: *b"ST",
112 length: 4,
113 version: 1,
114 }
115 }
116}
117
118#[repr(C)]
119#[derive(Debug, Clone, Copy)]
120pub struct ExtensionReference {
122 pub version: u8,
124 pub identifier_len: u8,
126 pub descriptor_len: u8,
128 pub source_len: u8,
130 pub buf: [u8; 252],
132}
133
134#[cfg(feature = "std")]
135impl ExtensionReference {
136 pub fn header(&self) -> SystemUseHeader {
138 SystemUseHeader {
139 sig: *b"ER",
140 length: 8 + self.identifier_len + self.descriptor_len + self.source_len,
141 version: 1,
142 }
143 }
144}
145
146io_transform! {
147#[cfg(feature = "std")]
148impl Writable for SystemUseHeader {
149 async fn write<R: io::Write>(&self, writer: &mut R) -> io::Result<()> {
150 writer.write_all(bytemuck::bytes_of(self)).await?;
151 Ok(())
152 }
153}
154
155#[cfg(feature = "std")]
156impl Writable for ContinuationArea {
157 async fn write<R: io::Write>(&self, writer: &mut R) -> io::Result<()> {
158 self.header().write(writer).await?;
159 writer.write_all(bytemuck::bytes_of(self)).await?;
160 Ok(())
161 }
162}
163
164#[cfg(feature = "std")]
165impl ContinuationArea {
166 pub async fn parse_data<R: Read>(header: SystemUseHeader, data: &mut R) -> io::Result<Self> {
168 assert_eq!(&header.sig, b"CE");
169 data.read_struct().await
170 }
171}
172
173#[cfg(feature = "std")]
174impl Writable for PaddingField {
175 async fn write<R: io::Write>(&self, writer: &mut R) -> io::Result<()> {
176 self.header().write(writer).await?;
177 let zeros = alloc::vec![0u8; self.length as usize];
178 writer.write_all(&zeros).await?;
179 Ok(())
180 }
181}
182
183#[cfg(feature = "std")]
184impl PaddingField {
185 pub async fn parse_data<R: Read>(header: SystemUseHeader, data: &mut R) -> io::Result<Self> {
187 let len = header.length - 4;
188 let mut buf = [0];
189 for _ in 0..len {
190 data.read_exact(&mut buf).await?;
191 }
192 Ok(Self { length: len })
193 }
194}
195
196#[cfg(feature = "std")]
197impl Writable for SuspIdentifier {
198 async fn write<R: io::Write>(&self, writer: &mut R) -> io::Result<()> {
199 self.header().write(writer).await?;
200 writer.write_all(bytemuck::bytes_of(self)).await?;
201 Ok(())
202 }
203}
204
205#[cfg(feature = "std")]
206impl SuspIdentifier {
207 pub async fn parse_data<R: Read>(header: SystemUseHeader, data: &mut R) -> io::Result<Self> {
209 assert_eq!(&header.sig, b"SP");
210 data.read_struct().await
211 }
212}
213
214#[cfg(feature = "std")]
215impl Writable for SuspTerminator {
216 async fn write<R: io::Write>(&self, writer: &mut R) -> io::Result<()> {
217 self.header().write(writer).await
218 }
219}
220
221#[cfg(feature = "std")]
222impl SuspTerminator {
223 pub async fn parse_data<R: Read>(_header: SystemUseHeader, _data: &mut R) -> io::Result<Self> {
225 Ok(Self)
226 }
227}
228
229#[cfg(feature = "std")]
230impl Writable for ExtensionReference {
231 async fn write<R: io::Write>(&self, writer: &mut R) -> io::Result<()> {
232 self.header().write(writer).await?;
233 let meta = [
234 self.identifier_len,
235 self.descriptor_len,
236 self.source_len,
237 self.version,
238 ];
239 writer.write_all(&meta).await?;
240 let total_len = self.identifier_len + self.descriptor_len + self.source_len;
241 writer.write_all(&self.buf[0..total_len as usize]).await?;
242 Ok(())
243 }
244}
245
246#[cfg(feature = "std")]
247impl ExtensionReference {
248 pub async fn parse_data<R: Read>(header: SystemUseHeader, data: &mut R) -> io::Result<Self> {
250 assert_eq!(&header.sig, b"ER");
251 let mut buf = [0u8; 252];
252 let meta: [u8; 4] = data.read_struct().await?;
253 data.read_exact(&mut buf[0..header.length as usize - size_of::<SystemUseHeader>()]).await?;
254 Ok(Self {
255 identifier_len: meta[0],
256 descriptor_len: meta[1],
257 source_len: meta[2],
258 version: meta[3],
259 buf,
260 })
261 }
262}
263} #[derive(Debug, Clone)]
267pub enum SystemUseField {
268 SuspIdentifier(SuspIdentifier),
270 Terminator,
272 ContinuationArea(ContinuationArea),
274 Padding(PaddingField),
276 ExtensionReference(ExtensionReference),
278
279 PosixAttributes(super::rrip::PxEntry),
282 DeviceNumber(super::rrip::PnEntry),
284 AlternateName(super::rrip::NmEntry),
286 SymbolicLink(super::rrip::SlEntry),
288 Timestamps(super::rrip::TfEntry),
290 ChildLink(super::rrip::ClEntry),
292 ParentLink(super::rrip::PlEntry),
294 Relocated,
296
297 ExtensionSelector {
299 extension_sequence: u8,
301 },
302
303 Unknown(SystemUseHeader, [u8; 252]),
305}
306
307impl SystemUseField {
308 pub fn signature(&self) -> [u8; 2] {
310 match self {
311 Self::SuspIdentifier(_) => *b"SP",
312 Self::Terminator => *b"ST",
313 Self::ContinuationArea(_) => *b"CE",
314 Self::Padding(_) => *b"PD",
315 Self::ExtensionReference(_) => *b"ER",
316 Self::PosixAttributes(_) => *b"PX",
317 Self::DeviceNumber(_) => *b"PN",
318 Self::AlternateName(_) => *b"NM",
319 Self::SymbolicLink(_) => *b"SL",
320 Self::Timestamps(_) => *b"TF",
321 Self::ChildLink(_) => *b"CL",
322 Self::ParentLink(_) => *b"PL",
323 Self::Relocated => *b"RE",
324 Self::ExtensionSelector { .. } => *b"ES",
325 Self::Unknown(header, _) => header.sig,
326 }
327 }
328
329 pub fn as_posix_attributes(&self) -> Option<&super::rrip::PxEntry> {
331 match self {
332 Self::PosixAttributes(px) => Some(px),
333 _ => None,
334 }
335 }
336
337 pub fn as_alternate_name(&self) -> Option<&super::rrip::NmEntry> {
339 match self {
340 Self::AlternateName(nm) => Some(nm),
341 _ => None,
342 }
343 }
344
345 pub fn as_timestamps(&self) -> Option<&super::rrip::TfEntry> {
347 match self {
348 Self::Timestamps(tf) => Some(tf),
349 _ => None,
350 }
351 }
352
353 pub fn as_symbolic_link(&self) -> Option<&super::rrip::SlEntry> {
355 match self {
356 Self::SymbolicLink(sl) => Some(sl),
357 _ => None,
358 }
359 }
360}
361
362pub struct SystemUseIter<'a> {
364 data: &'a [u8],
365 offset: usize,
366}
367
368impl<'a> SystemUseIter<'a> {
369 pub fn new(data: &'a [u8], bytes_to_skip: usize) -> Self {
375 Self {
376 data,
377 offset: bytes_to_skip,
378 }
379 }
380}
381
382impl Iterator for SystemUseIter<'_> {
383 type Item = SystemUseField;
384
385 fn next(&mut self) -> Option<Self::Item> {
386 if self.offset + 4 > self.data.len() {
388 return None;
389 }
390
391 let header_bytes = &self.data[self.offset..self.offset + 4];
392 let header: SystemUseHeader = *bytemuck::from_bytes(header_bytes);
393
394 if header.length == 0 {
396 return None;
397 }
398
399 if (header.length as usize) < 4 {
404 return None;
405 }
406
407 let entry_end = self.offset + header.length as usize;
409 if entry_end > self.data.len() {
410 return None;
411 }
412
413 let entry_data = &self.data[self.offset + 4..entry_end];
414 self.offset = entry_end;
415
416 let field = match &header.sig {
417 b"SP" if entry_data.len() >= 3 => {
418 let sp: SuspIdentifier = *bytemuck::from_bytes(&entry_data[0..3]);
419 SystemUseField::SuspIdentifier(sp)
420 }
421 b"ST" => SystemUseField::Terminator,
422 b"CE" if entry_data.len() >= 24 => {
423 let ce: ContinuationArea = *bytemuck::from_bytes(&entry_data[0..24]);
424 SystemUseField::ContinuationArea(ce)
425 }
426 b"PD" => {
427 let len = header.length.saturating_sub(4);
428 SystemUseField::Padding(PaddingField { length: len })
429 }
430 b"ER" if entry_data.len() >= 4 => {
431 let mut buf = [0u8; 252];
432 let copy_len = entry_data.len().min(252);
433 buf[..copy_len].copy_from_slice(&entry_data[..copy_len]);
434 SystemUseField::ExtensionReference(ExtensionReference {
435 identifier_len: entry_data[0],
436 descriptor_len: entry_data[1],
437 source_len: entry_data[2],
438 version: entry_data[3],
439 buf,
440 })
441 }
442 b"PX" if entry_data.len() >= 32 => {
444 let len = entry_data.len().min(40);
445 let px = super::rrip::PxEntry {
446 file_mode: *bytemuck::from_bytes(&entry_data[0..8]),
447 file_links: *bytemuck::from_bytes(&entry_data[8..16]),
448 file_uid: *bytemuck::from_bytes(&entry_data[16..24]),
449 file_gid: *bytemuck::from_bytes(&entry_data[24..32]),
450 file_serial: if len >= 40 {
451 *bytemuck::from_bytes(&entry_data[32..40])
452 } else {
453 crate::types::U32LsbMsb::new(0)
454 },
455 };
456 SystemUseField::PosixAttributes(px)
457 }
458 b"PN" if entry_data.len() >= 16 => {
459 let pn = super::rrip::PnEntry {
460 dev_high: *bytemuck::from_bytes(&entry_data[0..8]),
461 dev_low: *bytemuck::from_bytes(&entry_data[8..16]),
462 };
463 SystemUseField::DeviceNumber(pn)
464 }
465 b"NM" if !entry_data.is_empty() => {
466 let flags = super::rrip::NmFlags::from_bits_truncate(entry_data[0]);
467 let name = entry_data[1..].to_vec();
468 SystemUseField::AlternateName(super::rrip::NmEntry { flags, name })
469 }
470 b"SL" if !entry_data.is_empty() => {
471 let flags = entry_data[0];
472 let mut components = alloc::vec::Vec::new();
473 let mut pos = 1;
474 while pos + 2 <= entry_data.len() {
475 let comp_flags =
476 super::rrip::SlComponentFlags::from_bits_truncate(entry_data[pos]);
477 let comp_len = entry_data[pos + 1] as usize;
478 pos += 2;
479 let end = (pos + comp_len).min(entry_data.len());
480 let content = entry_data[pos..end].to_vec();
481 pos = end;
482 components.push(super::rrip::SlComponent {
483 flags: comp_flags,
484 content,
485 });
486 }
487 SystemUseField::SymbolicLink(super::rrip::SlEntry { flags, components })
488 }
489 b"TF" if !entry_data.is_empty() => {
490 let flags = super::rrip::TfFlags::from_bits_truncate(entry_data[0]);
491 let timestamps = entry_data[1..].to_vec();
492 SystemUseField::Timestamps(super::rrip::TfEntry { flags, timestamps })
493 }
494 b"CL" if entry_data.len() >= 8 => {
495 let cl = super::rrip::ClEntry {
496 child_directory_location: *bytemuck::from_bytes(&entry_data[0..8]),
497 };
498 SystemUseField::ChildLink(cl)
499 }
500 b"PL" if entry_data.len() >= 8 => {
501 let pl = super::rrip::PlEntry {
502 parent_directory_location: *bytemuck::from_bytes(&entry_data[0..8]),
503 };
504 SystemUseField::ParentLink(pl)
505 }
506 b"RE" => SystemUseField::Relocated,
507 b"ES" if !entry_data.is_empty() => SystemUseField::ExtensionSelector {
508 extension_sequence: entry_data[0],
509 },
510 _ => {
511 let mut buf = [0u8; 252];
512 let copy_len = entry_data.len().min(252);
513 buf[..copy_len].copy_from_slice(&entry_data[..copy_len]);
514 SystemUseField::Unknown(header, buf)
515 }
516 };
517
518 Some(field)
519 }
520}
521
522#[cfg(feature = "alloc")]
524pub struct SystemUseBuilder {
525 entries: alloc::vec::Vec<alloc::vec::Vec<u8>>,
526}
527
528#[cfg(feature = "alloc")]
529impl SystemUseBuilder {
530 pub fn new() -> Self {
532 Self {
533 entries: alloc::vec::Vec::new(),
534 }
535 }
536
537 pub fn add_sp(&mut self, bytes_skipped: u8) -> &mut Self {
541 let sp = SuspIdentifier::new(bytes_skipped);
542 let mut buf = alloc::vec![0u8; 7];
543 buf[0..2].copy_from_slice(b"SP");
544 buf[2] = 7; buf[3] = 1; buf[4..7].copy_from_slice(bytemuck::bytes_of(&sp));
547 self.entries.push(buf);
548 self
549 }
550
551 pub fn add_st(&mut self) -> &mut Self {
553 let buf = alloc::vec![b'S', b'T', 4, 1];
554 self.entries.push(buf);
555 self
556 }
557
558 pub fn add_ce(&mut self, ce: ContinuationArea) -> &mut Self {
560 let mut buf = alloc::vec![0u8; 28];
561 buf[0..2].copy_from_slice(b"CE");
562 buf[2] = 28;
563 buf[3] = 1;
564 buf[4..28].copy_from_slice(bytemuck::bytes_of(&ce));
565 self.entries.push(buf);
566 self
567 }
568
569 pub fn add_padding(&mut self, length: u8) -> &mut Self {
571 let mut buf = alloc::vec![0u8; 4 + length as usize];
572 buf[0..2].copy_from_slice(b"PD");
573 buf[2] = 4 + length;
574 buf[3] = 1;
575 self.entries.push(buf);
576 self
577 }
578
579 pub fn add_er(
581 &mut self,
582 identifier: &str,
583 descriptor: &str,
584 source: &str,
585 version: u8,
586 ) -> &mut Self {
587 let id_bytes = identifier.as_bytes();
588 let desc_bytes = descriptor.as_bytes();
589 let src_bytes = source.as_bytes();
590
591 let len = 8 + id_bytes.len() + desc_bytes.len() + src_bytes.len();
592 let mut buf = alloc::vec![0u8; len];
593 buf[0..2].copy_from_slice(b"ER");
594 buf[2] = len as u8;
595 buf[3] = 1;
596 buf[4] = id_bytes.len() as u8;
597 buf[5] = desc_bytes.len() as u8;
598 buf[6] = src_bytes.len() as u8;
599 buf[7] = version;
600
601 let mut offset = 8;
602 buf[offset..offset + id_bytes.len()].copy_from_slice(id_bytes);
603 offset += id_bytes.len();
604 buf[offset..offset + desc_bytes.len()].copy_from_slice(desc_bytes);
605 offset += desc_bytes.len();
606 buf[offset..offset + src_bytes.len()].copy_from_slice(src_bytes);
607
608 self.entries.push(buf);
609 self
610 }
611
612 pub fn add_raw(&mut self, data: alloc::vec::Vec<u8>) -> &mut Self {
614 self.entries.push(data);
615 self
616 }
617
618 pub fn size(&self) -> usize {
620 self.entries.iter().map(|e| e.len()).sum()
621 }
622
623 pub fn build(&self) -> alloc::vec::Vec<u8> {
625 let total_size = self.size();
626 let mut result = alloc::vec::Vec::with_capacity(total_size);
627 for entry in &self.entries {
628 result.extend_from_slice(entry);
629 }
630 result
631 }
632
633 pub fn is_empty(&self) -> bool {
635 self.entries.is_empty()
636 }
637
638 pub fn build_split(&self, max_inline: usize) -> SplitSu {
644 let total = self.size();
645 if total <= max_inline {
646 return SplitSu {
647 inline: self.build(),
648 overflow: alloc::vec::Vec::new(),
649 ce_offset: None,
650 };
651 }
652
653 const CE_SIZE: usize = 28;
654 let budget = max_inline.saturating_sub(CE_SIZE);
655
656 let mut accumulated = 0;
658 let mut split_at = 0;
659 for (i, entry) in self.entries.iter().enumerate() {
660 if accumulated + entry.len() > budget {
661 split_at = i;
662 break;
663 }
664 accumulated += entry.len();
665 split_at = i + 1;
666 }
667
668 let mut inline = alloc::vec::Vec::with_capacity(accumulated + CE_SIZE);
670 for entry in &self.entries[..split_at] {
671 inline.extend_from_slice(entry);
672 }
673 let ce_offset = inline.len();
674 let mut ce_buf = alloc::vec![0u8; CE_SIZE];
676 ce_buf[0..2].copy_from_slice(b"CE");
677 ce_buf[2] = CE_SIZE as u8;
678 ce_buf[3] = 1;
679 inline.extend_from_slice(&ce_buf);
680
681 let overflow_size: usize = self.entries[split_at..].iter().map(|e| e.len()).sum();
683 let mut overflow = alloc::vec::Vec::with_capacity(overflow_size);
684 for entry in &self.entries[split_at..] {
685 overflow.extend_from_slice(entry);
686 }
687
688 SplitSu {
689 inline,
690 overflow,
691 ce_offset: Some(ce_offset),
692 }
693 }
694}
695
696#[cfg(feature = "alloc")]
706#[derive(Debug, Clone)]
707pub struct SplitSu {
708 pub inline: alloc::vec::Vec<u8>,
710 pub overflow: alloc::vec::Vec<u8>,
712 ce_offset: Option<usize>,
714}
715
716#[cfg(feature = "alloc")]
717impl SplitSu {
718 pub fn empty() -> Self {
720 Self {
721 inline: alloc::vec::Vec::new(),
722 overflow: alloc::vec::Vec::new(),
723 ce_offset: None,
724 }
725 }
726
727 pub fn has_overflow(&self) -> bool {
729 !self.overflow.is_empty()
730 }
731
732 pub fn patch_ce(&mut self, sector: u32, byte_offset: u32) {
734 if let Some(off) = self.ce_offset {
735 let data_start = off + 4; let length = self.overflow.len() as u32;
737 self.inline[data_start..data_start + 4].copy_from_slice(§or.to_le_bytes());
739 self.inline[data_start + 4..data_start + 8].copy_from_slice(§or.to_be_bytes());
740 self.inline[data_start + 8..data_start + 12]
742 .copy_from_slice(&byte_offset.to_le_bytes());
743 self.inline[data_start + 12..data_start + 16]
744 .copy_from_slice(&byte_offset.to_be_bytes());
745 self.inline[data_start + 16..data_start + 20].copy_from_slice(&length.to_le_bytes());
747 self.inline[data_start + 20..data_start + 24].copy_from_slice(&length.to_be_bytes());
748 }
749 }
750}
751
752#[cfg(feature = "alloc")]
753impl Default for SystemUseBuilder {
754 fn default() -> Self {
755 Self::new()
756 }
757}
758
759sync_only! {
760#[cfg(all(feature = "std", test))]
761mod tests {
762 use super::*;
763 use alloc::vec;
764 use alloc::vec::Vec;
765
766 static_assertions::const_assert_eq!(size_of::<SystemUseHeader>(), 4);
767 static_assertions::const_assert_eq!(size_of::<ContinuationArea>(), 24);
768 static_assertions::const_assert_eq!(size_of::<SuspIdentifier>(), 3);
769
770 #[test]
771 fn short_entry_length_does_not_panic() {
772 for len in 1u8..=3 {
776 let data = [b'A', b'A', len, 1];
777 let mut iter = SystemUseIter::new(&data, 0);
778 assert!(iter.next().is_none(), "length {len} should terminate");
779 }
780 }
781
782 #[test]
783 fn test_susp_identifier_valid() {
784 let sp = SuspIdentifier::new(0);
785 assert!(sp.is_valid());
786 assert_eq!(sp.check_bytes, [0xBE, 0xEF]);
787 assert_eq!(sp.bytes_skipped, 0);
788 }
789
790 #[test]
791 fn test_susp_identifier_invalid() {
792 let sp = SuspIdentifier {
793 check_bytes: [0x00, 0x00],
794 bytes_skipped: 0,
795 };
796 assert!(!sp.is_valid());
797 }
798
799 #[test]
800 fn test_system_use_iter_empty() {
801 let data: &[u8] = &[];
802 let mut iter = SystemUseIter::new(data, 0);
803 assert!(iter.next().is_none());
804 }
805
806 #[test]
807 fn test_system_use_iter_sp_entry() {
808 let data: &[u8] = &[
810 b'S', b'P', 7, 1, 0xBE, 0xEF, 0, ];
813 let mut iter = SystemUseIter::new(data, 0);
814 match iter.next() {
815 Some(SystemUseField::SuspIdentifier(sp)) => {
816 assert!(sp.is_valid());
817 assert_eq!(sp.bytes_skipped, 0);
818 }
819 other => panic!("expected SP entry, got {other:?}"),
820 }
821 assert!(iter.next().is_none());
822 }
823
824 #[test]
825 fn test_system_use_iter_st_entry() {
826 let data: &[u8] = &[b'S', b'T', 4, 1];
828 let mut iter = SystemUseIter::new(data, 0);
829 match iter.next() {
830 Some(SystemUseField::Terminator) => {}
831 other => panic!("expected ST entry, got {other:?}"),
832 }
833 assert!(iter.next().is_none());
834 }
835
836 #[test]
837 fn test_system_use_iter_ce_entry() {
838 let mut data = vec![b'C', b'E', 28, 1];
840 data.extend_from_slice(&[10, 0, 0, 0, 10, 0, 0, 0]); data.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); data.extend_from_slice(&[100, 0, 0, 0, 100, 0, 0, 0]); let mut iter = SystemUseIter::new(&data, 0);
846 match iter.next() {
847 Some(SystemUseField::ContinuationArea(ce)) => {
848 assert_eq!(ce.sector.read(), 10);
849 assert_eq!(ce.offset.read(), 0);
850 assert_eq!(ce.length.read(), 100);
851 }
852 other => panic!("expected CE entry, got {other:?}"),
853 }
854 assert!(iter.next().is_none());
855 }
856
857 #[test]
858 fn test_system_use_iter_pd_entry() {
859 let data: &[u8] = &[b'P', b'D', 8, 1, 0, 0, 0, 0];
861 let mut iter = SystemUseIter::new(data, 0);
862 match iter.next() {
863 Some(SystemUseField::Padding(pd)) => {
864 assert_eq!(pd.length, 4);
865 }
866 other => panic!("expected PD entry, got {other:?}"),
867 }
868 assert!(iter.next().is_none());
869 }
870
871 #[test]
872 fn test_system_use_iter_unknown_entry() {
873 let data: &[u8] = &[b'X', b'X', 6, 1, 0x42, 0x43];
875 let mut iter = SystemUseIter::new(data, 0);
876 match iter.next() {
877 Some(SystemUseField::Unknown(header, buf)) => {
878 assert_eq!(&header.sig, b"XX");
879 assert_eq!(header.length, 6);
880 assert_eq!(&buf[0..2], &[0x42, 0x43]);
881 }
882 other => panic!("expected Unknown entry, got {other:?}"),
883 }
884 assert!(iter.next().is_none());
885 }
886
887 #[test]
888 fn test_system_use_iter_multiple_entries() {
889 let data: &[u8] = &[
891 b'S', b'P', 7, 1, 0xBE, 0xEF, 0, b'S', b'T', 4, 1, ];
894 let mut iter = SystemUseIter::new(data, 0);
895 assert!(matches!(
896 iter.next(),
897 Some(SystemUseField::SuspIdentifier(_))
898 ));
899 assert!(matches!(iter.next(), Some(SystemUseField::Terminator)));
900 assert!(iter.next().is_none());
901 }
902
903 #[test]
904 fn test_system_use_iter_with_skip() {
905 let data: &[u8] = &[
907 0xFF, 0xFF, 0xFF, b'S', b'T', 4, 1, ];
910 let mut iter = SystemUseIter::new(data, 3);
911 assert!(matches!(iter.next(), Some(SystemUseField::Terminator)));
912 assert!(iter.next().is_none());
913 }
914
915 #[test]
916 fn test_system_use_iter_zero_length_terminates() {
917 let data: &[u8] = &[0, 0, 0, 0]; let mut iter = SystemUseIter::new(data, 0);
919 assert!(iter.next().is_none());
920 }
921
922 #[test]
923 fn test_system_use_builder_empty() {
924 let builder = SystemUseBuilder::new();
925 assert!(builder.is_empty());
926 assert_eq!(builder.size(), 0);
927 assert_eq!(builder.build(), Vec::<u8>::new());
928 }
929
930 #[test]
931 fn test_system_use_builder_sp() {
932 let mut builder = SystemUseBuilder::new();
933 builder.add_sp(0);
934 assert!(!builder.is_empty());
935 assert_eq!(builder.size(), 7);
936
937 let data = builder.build();
938 assert_eq!(&data[0..2], b"SP");
939 assert_eq!(data[2], 7); assert_eq!(data[3], 1); assert_eq!(&data[4..6], &[0xBE, 0xEF]); assert_eq!(data[6], 0); }
944
945 #[test]
946 fn test_system_use_builder_st() {
947 let mut builder = SystemUseBuilder::new();
948 builder.add_st();
949 assert_eq!(builder.size(), 4);
950
951 let data = builder.build();
952 assert_eq!(&data, &[b'S', b'T', 4, 1]);
953 }
954
955 #[test]
956 fn test_system_use_builder_padding() {
957 let mut builder = SystemUseBuilder::new();
958 builder.add_padding(4);
959 assert_eq!(builder.size(), 8);
960
961 let data = builder.build();
962 assert_eq!(&data[0..2], b"PD");
963 assert_eq!(data[2], 8); assert_eq!(data[3], 1); }
966
967 #[test]
968 fn test_system_use_builder_er() {
969 let mut builder = SystemUseBuilder::new();
970 builder.add_er("RRIP", "Rock Ridge", "TEST", 1);
971
972 let data = builder.build();
973 assert_eq!(&data[0..2], b"ER");
974 let len = data[2] as usize;
975 assert_eq!(len, 8 + 4 + 10 + 4); assert_eq!(data[3], 1); assert_eq!(data[4], 4); assert_eq!(data[5], 10); assert_eq!(data[6], 4); assert_eq!(data[7], 1); let id = &data[8..12];
983 assert_eq!(id, b"RRIP");
984 }
985
986 #[test]
987 fn test_system_use_builder_roundtrip() {
988 let mut builder = SystemUseBuilder::new();
989 builder.add_sp(0).add_st();
990
991 let data = builder.build();
992 let mut iter = SystemUseIter::new(&data, 0);
993
994 assert!(matches!(
995 iter.next(),
996 Some(SystemUseField::SuspIdentifier(_))
997 ));
998 assert!(matches!(iter.next(), Some(SystemUseField::Terminator)));
999 assert!(iter.next().is_none());
1000 }
1001
1002 #[test]
1003 fn test_build_split_no_overflow() {
1004 let mut builder = SystemUseBuilder::new();
1006 builder.add_sp(0).add_st();
1007 let split = builder.build_split(100);
1008 assert!(!split.has_overflow());
1009 assert!(split.overflow.is_empty());
1010 assert_eq!(split.inline, builder.build());
1011 }
1012
1013 #[test]
1014 fn test_build_split_with_overflow() {
1015 let mut builder = SystemUseBuilder::new();
1017 builder.add_sp(0);
1018 builder.add_er(
1019 "RRIP_1991A",
1020 "THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS",
1021 "PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE. SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR CONTACT INFORMATION.",
1022 1,
1023 );
1024 let total = builder.size();
1025 assert!(total > 100); let mut split = builder.build_split(40);
1029 assert!(split.has_overflow());
1030 assert_eq!(split.inline.len(), 7 + 28);
1032 assert_eq!(&split.overflow[0..2], b"ER");
1034 assert_eq!(&split.inline[0..2], b"SP");
1036 assert_eq!(&split.inline[7..9], b"CE");
1038 assert_eq!(split.inline[9], 28); split.patch_ce(42, 100);
1042 let ce_data = &split.inline[7..35];
1043 let sector_le = u32::from_le_bytes(ce_data[4..8].try_into().unwrap());
1045 let sector_be = u32::from_be_bytes(ce_data[8..12].try_into().unwrap());
1046 assert_eq!(sector_le, 42);
1047 assert_eq!(sector_be, 42);
1048 let offset_le = u32::from_le_bytes(ce_data[12..16].try_into().unwrap());
1049 let offset_be = u32::from_be_bytes(ce_data[16..20].try_into().unwrap());
1050 assert_eq!(offset_le, 100);
1051 assert_eq!(offset_be, 100);
1052 let length_le = u32::from_le_bytes(ce_data[20..24].try_into().unwrap());
1053 let length_be = u32::from_be_bytes(ce_data[24..28].try_into().unwrap());
1054 assert_eq!(length_le, split.overflow.len() as u32);
1055 assert_eq!(length_be, split.overflow.len() as u32);
1056 }
1057
1058 fn u32_lsb_msb_bytes(v: u32) -> [u8; 8] {
1062 let mut buf = [0u8; 8];
1063 buf[0..4].copy_from_slice(&v.to_le_bytes());
1064 buf[4..8].copy_from_slice(&v.to_be_bytes());
1065 buf
1066 }
1067
1068 #[test]
1069 fn test_iter_px_entry() {
1070 let mut data = vec![b'P', b'X', 44, 1];
1072 data.extend_from_slice(&u32_lsb_msb_bytes(0o100644)); data.extend_from_slice(&u32_lsb_msb_bytes(1)); data.extend_from_slice(&u32_lsb_msb_bytes(1000)); data.extend_from_slice(&u32_lsb_msb_bytes(2000)); data.extend_from_slice(&u32_lsb_msb_bytes(42)); let mut iter = SystemUseIter::new(&data, 0);
1079 match iter.next() {
1080 Some(SystemUseField::PosixAttributes(px)) => {
1081 assert_eq!(px.file_mode.read(), 0o100644);
1082 assert_eq!(px.file_links.read(), 1);
1083 assert_eq!(px.file_uid.read(), 1000);
1084 assert_eq!(px.file_gid.read(), 2000);
1085 assert_eq!(px.file_serial.read(), 42);
1086 }
1087 other => panic!("expected PX entry, got {other:?}"),
1088 }
1089 assert!(iter.next().is_none());
1090 }
1091
1092 #[test]
1093 fn test_iter_nm_entry() {
1094 let name = b"hello.txt";
1096 let total_len = 4 + 1 + name.len();
1097 let mut data = vec![b'N', b'M', total_len as u8, 1];
1098 data.push(0x00); data.extend_from_slice(name);
1100
1101 let mut iter = SystemUseIter::new(&data, 0);
1102 match iter.next() {
1103 Some(SystemUseField::AlternateName(nm)) => {
1104 assert!(nm.flags.is_empty());
1105 assert_eq!(nm.name, b"hello.txt");
1106 }
1107 other => panic!("expected NM entry, got {other:?}"),
1108 }
1109 assert!(iter.next().is_none());
1110 }
1111
1112 #[test]
1113 fn test_iter_tf_entry() {
1114 let total_len = 4 + 1 + 14; let mut data = vec![b'T', b'F', total_len as u8, 1];
1117 data.push(0x06); data.extend_from_slice(&[126, 1, 15, 10, 30, 0, 0]);
1120 data.extend_from_slice(&[126, 1, 15, 12, 0, 0, 0]);
1122
1123 let mut iter = SystemUseIter::new(&data, 0);
1124 match iter.next() {
1125 Some(SystemUseField::Timestamps(tf)) => {
1126 use super::super::rrip::TfFlags;
1127 assert!(tf.flags.contains(TfFlags::MODIFY));
1128 assert!(tf.flags.contains(TfFlags::ACCESS));
1129 assert!(!tf.flags.contains(TfFlags::LONG_FORM));
1130 assert_eq!(tf.timestamps.len(), 14);
1131 }
1132 other => panic!("expected TF entry, got {other:?}"),
1133 }
1134 assert!(iter.next().is_none());
1135 }
1136
1137 #[test]
1138 fn test_iter_sl_entry() {
1139 let mut data = vec![b'S', b'L', 0, 1]; data.push(0x00); data.push(0x08);
1144 data.push(0x00);
1145 data.push(0x00);
1147 data.push(0x03);
1148 data.extend_from_slice(b"usr");
1149 data.push(0x00);
1151 data.push(0x03);
1152 data.extend_from_slice(b"bin");
1153 data[2] = data.len() as u8;
1155
1156 let mut iter = SystemUseIter::new(&data, 0);
1157 match iter.next() {
1158 Some(SystemUseField::SymbolicLink(sl)) => {
1159 assert_eq!(sl.flags, 0);
1160 assert_eq!(sl.components.len(), 3);
1161 assert!(
1162 sl.components[0]
1163 .flags
1164 .contains(super::super::rrip::SlComponentFlags::ROOT)
1165 );
1166 assert!(sl.components[0].content.is_empty());
1167 assert_eq!(sl.components[1].content, b"usr");
1168 assert_eq!(sl.components[2].content, b"bin");
1169 }
1170 other => panic!("expected SL entry, got {other:?}"),
1171 }
1172 assert!(iter.next().is_none());
1173 }
1174
1175 #[test]
1176 fn test_iter_cl_pl_re() {
1177 let mut data = Vec::new();
1178 data.extend_from_slice(&[b'C', b'L', 12, 1]);
1180 data.extend_from_slice(&u32_lsb_msb_bytes(100));
1181 data.extend_from_slice(&[b'P', b'L', 12, 1]);
1183 data.extend_from_slice(&u32_lsb_msb_bytes(50));
1184 data.extend_from_slice(&[b'R', b'E', 4, 1]);
1186
1187 let mut iter = SystemUseIter::new(&data, 0);
1188 match iter.next() {
1189 Some(SystemUseField::ChildLink(cl)) => {
1190 assert_eq!(cl.child_directory_location.read(), 100);
1191 }
1192 other => panic!("expected CL entry, got {other:?}"),
1193 }
1194 match iter.next() {
1195 Some(SystemUseField::ParentLink(pl)) => {
1196 assert_eq!(pl.parent_directory_location.read(), 50);
1197 }
1198 other => panic!("expected PL entry, got {other:?}"),
1199 }
1200 match iter.next() {
1201 Some(SystemUseField::Relocated) => {}
1202 other => panic!("expected RE entry, got {other:?}"),
1203 }
1204 assert!(iter.next().is_none());
1205 }
1206
1207 #[test]
1208 fn test_iter_pn_entry() {
1209 let mut data = vec![b'P', b'N', 20, 1];
1211 data.extend_from_slice(&u32_lsb_msb_bytes(8)); data.extend_from_slice(&u32_lsb_msb_bytes(1)); let mut iter = SystemUseIter::new(&data, 0);
1215 match iter.next() {
1216 Some(SystemUseField::DeviceNumber(pn)) => {
1217 assert_eq!(pn.dev_high.read(), 8);
1218 assert_eq!(pn.dev_low.read(), 1);
1219 }
1220 other => panic!("expected PN entry, got {other:?}"),
1221 }
1222 assert!(iter.next().is_none());
1223 }
1224
1225 #[test]
1226 fn test_iter_mixed_susp_rrip() {
1227 let mut data = Vec::new();
1228 data.extend_from_slice(&[b'S', b'P', 7, 1, 0xBE, 0xEF, 0]);
1230 data.extend_from_slice(&[b'P', b'X', 44, 1]);
1232 data.extend_from_slice(&u32_lsb_msb_bytes(0o040755)); data.extend_from_slice(&u32_lsb_msb_bytes(2));
1234 data.extend_from_slice(&u32_lsb_msb_bytes(0));
1235 data.extend_from_slice(&u32_lsb_msb_bytes(0));
1236 data.extend_from_slice(&u32_lsb_msb_bytes(1));
1237 data.extend_from_slice(&[b'N', b'M', 10, 1, 0x00]);
1239 data.extend_from_slice(b"mydir");
1240 data.extend_from_slice(&[b'T', b'F', 12, 1, 0x02]);
1242 data.extend_from_slice(&[126, 2, 16, 8, 0, 0, 0]);
1243 data.extend_from_slice(&[b'S', b'T', 4, 1]);
1245
1246 let fields: Vec<_> = SystemUseIter::new(&data, 0).collect();
1247 assert_eq!(fields.len(), 5);
1248 assert!(matches!(fields[0], SystemUseField::SuspIdentifier(_)));
1249 assert!(matches!(fields[1], SystemUseField::PosixAttributes(_)));
1250 assert!(matches!(fields[2], SystemUseField::AlternateName(_)));
1251 assert!(matches!(fields[3], SystemUseField::Timestamps(_)));
1252 assert!(matches!(fields[4], SystemUseField::Terminator));
1253
1254 assert_eq!(fields[0].signature(), *b"SP");
1256 assert_eq!(fields[1].signature(), *b"PX");
1257 assert_eq!(fields[2].signature(), *b"NM");
1258 assert_eq!(fields[3].signature(), *b"TF");
1259 assert_eq!(fields[4].signature(), *b"ST");
1260 }
1261}
1262}