Skip to main content

powerio_core/
source.rs

1use std::collections::BTreeMap;
2use std::fmt;
3use std::io::Read;
4use std::path::{Component, Path, PathBuf};
5use std::sync::{Arc, Mutex, MutexGuard};
6
7use crate::validation::{MAX_FORMAT_ID_BYTES, valid_nonempty_text};
8use crate::{Error, SourceId};
9
10/// Referenced files one source may acquire. Matches the OpenDSS include
11/// budget the distribution reader has enforced since 0.7.
12const MAX_REFERENCED_FILES: usize = 4_096;
13
14/// Total bytes of referenced files one source may acquire.
15const MAX_REFERENCED_BYTES: u64 = 64 << 20;
16const DEFAULT_PRIMARY_BYTES: u64 = 64 << 20;
17
18fn parse_primary_limit(value: Option<&std::ffi::OsStr>) -> Result<u64, Error> {
19    let Some(value) = value else {
20        return Ok(DEFAULT_PRIMARY_BYTES);
21    };
22    let limit = value
23        .to_str()
24        .filter(|value| !value.is_empty() && value.bytes().all(|b| b.is_ascii_digit()))
25        .and_then(|value| value.parse::<u64>().ok())
26        .filter(|&limit| limit > 0 && isize::try_from(limit).is_ok());
27    limit.ok_or_else(|| Error::new(&crate::codes::REQUEST_SOURCE_INVALID_LIMIT,
28        "POWERIO_MAX_PRIMARY_BYTES must be a positive decimal byte count within this platform's allocation limit"))
29}
30
31#[derive(Clone, Copy)]
32enum ReadBudget {
33    Primary(u64),
34    Referenced(u64),
35}
36
37impl ReadBudget {
38    fn bytes(self) -> u64 {
39        match self {
40            Self::Primary(n) | Self::Referenced(n) => n,
41        }
42    }
43    fn exceeded(self, name: &str) -> Error {
44        match self {
45            Self::Primary(limit) => Error::new(
46                &crate::codes::READ_IO_PRIMARY_BUDGET,
47                format!("primary source `{name}` exceeds its {limit} byte limit"),
48            ),
49            Self::Referenced(_) => Error::new(
50                &crate::codes::READ_IO_REFERENCE_BUDGET,
51                format!(
52                    "referenced file `{name}` would take this source past its {MAX_REFERENCED_BYTES} byte acquisition budget"
53                ),
54            ),
55        }
56    }
57}
58
59/// Deepest resolved or walked path beneath one acquisition root, in segments.
60const MAX_REFERENCED_DEPTH: usize = 64;
61
62const UTF8_BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
63
64/// Open stable identifier used to select a parser or writer.
65#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
66pub struct FormatId(Box<str>);
67
68impl FormatId {
69    /// Validate the shared C, Python, Julia, JSON, MCP, and Rust spelling.
70    pub fn new(id: impl Into<String>) -> Result<Self, Error> {
71        let id = id.into();
72        if !valid_format_id(&id) {
73            return Err(Error::new(
74                &crate::codes::REQUEST_FORMAT_INVALID_ID,
75                "a format ID must be bounded lower case ASCII segments separated by single hyphens",
76            ));
77        }
78        Ok(Self(id.into_boxed_str()))
79    }
80
81    #[must_use]
82    pub fn as_str(&self) -> &str {
83        &self.0
84    }
85}
86
87impl fmt::Display for FormatId {
88    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
89        formatter.write_str(&self.0)
90    }
91}
92
93fn valid_format_id(id: &str) -> bool {
94    if id.is_empty() || id.len() > MAX_FORMAT_ID_BYTES {
95        return false;
96    }
97    let bytes = id.as_bytes();
98    if !bytes[0].is_ascii_lowercase() || bytes.last() == Some(&b'-') {
99        return false;
100    }
101    let mut previous_hyphen = false;
102    for byte in bytes {
103        if *byte == b'-' {
104            if previous_hyphen {
105                return false;
106            }
107            previous_hyphen = true;
108        } else if byte.is_ascii_lowercase() || byte.is_ascii_digit() {
109            previous_hyphen = false;
110        } else {
111            return false;
112        }
113    }
114    true
115}
116
117#[derive(Debug)]
118struct SourceBufferData {
119    id: SourceId,
120    name: Box<str>,
121    bytes: Arc<[u8]>,
122    /// Directory of this buffer relative to the acquisition root, as the
123    /// segments a referenced name resolves against. Empty for memory buffers
124    /// and for files sitting directly in the root.
125    directory: Box<[Box<str>]>,
126}
127
128/// Immutable named bytes retained by a [`Source`].
129#[derive(Clone, Debug)]
130pub struct SourceBuffer(Arc<SourceBufferData>);
131
132impl SourceBuffer {
133    fn new(
134        id: SourceId,
135        name: impl Into<String>,
136        bytes: Arc<[u8]>,
137        directory: Vec<String>,
138    ) -> Self {
139        Self(Arc::new(SourceBufferData {
140            id,
141            name: name.into().into_boxed_str(),
142            bytes,
143            directory: directory.into_iter().map(String::into_boxed_str).collect(),
144        }))
145    }
146
147    #[must_use]
148    pub fn id(&self) -> &SourceId {
149        &self.0.id
150    }
151
152    #[must_use]
153    pub fn name(&self) -> &str {
154        &self.0.name
155    }
156
157    /// The exact retained bytes, including a UTF-8 byte order mark when the
158    /// input carried one. Same format writing echoes these bytes.
159    #[must_use]
160    pub fn bytes(&self) -> &[u8] {
161        &self.0.bytes
162    }
163
164    #[must_use]
165    pub fn shared_bytes(&self) -> Arc<[u8]> {
166        Arc::clone(&self.0.bytes)
167    }
168
169    /// True when the retained bytes begin with a UTF-8 byte order mark.
170    #[must_use]
171    pub fn has_utf8_bom(&self) -> bool {
172        self.0.bytes.starts_with(&UTF8_BOM)
173    }
174
175    /// The bytes a parser decodes: the retained bytes with a leading UTF-8
176    /// byte order mark skipped. This is a subslice of the one retained buffer,
177    /// never a second decoded copy.
178    #[must_use]
179    pub fn content_bytes(&self) -> &[u8] {
180        let bytes: &[u8] = &self.0.bytes;
181        if bytes.starts_with(&UTF8_BOM) {
182            &bytes[UTF8_BOM.len()..]
183        } else {
184            bytes
185        }
186    }
187
188    /// Directory of this buffer relative to the acquisition root, as path
189    /// segments. Empty for in-memory buffers and for files sitting directly
190    /// in the root. A parser that resolves referenced names itself joins
191    /// these onto the root to seed its resolution base.
192    pub fn directory_segments(&self) -> impl Iterator<Item = &str> {
193        self.0.directory.iter().map(AsRef::as_ref)
194    }
195}
196
197/// Files acquired beneath one pinned root directory.
198///
199/// The root is pinned as an open directory handle where the platform supports
200/// it, opened once on the first acquisition and reused for every later one, so
201/// the number of descriptors a process holds does not grow with the number of
202/// live sources that never acquire a referenced file. Referenced names are
203/// resolved lexically first and then walked one component at a time relative
204/// to that handle with symbolic links refused, so a path component replaced
205/// during acquisition cannot redirect the read outside the root. Acquisition
206/// is serialized by the one lock, which also makes the cache and the budget
207/// exact: a file is read and retained once, and concurrent requests for one
208/// name share the same buffer.
209#[derive(Debug)]
210struct FileAcquisition {
211    root_display: PathBuf,
212    /// True when the root was selected with [`Source::with_acquisition_root`]
213    /// rather than defaulted to the containing directory.
214    selected: bool,
215    state: Mutex<AcquisitionState>,
216}
217
218#[derive(Debug, Default)]
219struct AcquisitionState {
220    root: Option<platform::RootHandle>,
221    cache: BTreeMap<String, SourceBuffer>,
222    /// The one directory listing, cached on the first successful walk. Every
223    /// caller then reads the same immutable view, and a directory handle
224    /// whose stream position a platform shares across duplicated descriptors
225    /// cannot silently return an empty second listing.
226    listed: Option<Vec<crate::ArtifactPath>>,
227    files: usize,
228    bytes: u64,
229}
230
231impl AcquisitionState {
232    /// The pinned root handle, opened on first use with symbolic links at the
233    /// root refused and the directory confirmed on the opened descriptor.
234    fn pinned_root(&mut self, root_display: &Path) -> Result<&platform::RootHandle, Error> {
235        if self.root.is_none() {
236            let root = platform::open_root(root_display)
237                .map_err(|cause| open_error(&crate::codes::READ_IO_OPEN, root_display, cause))?;
238            self.root = Some(root);
239        }
240        Ok(self.root.as_ref().expect("pinned above"))
241    }
242}
243
244#[derive(Debug)]
245enum SourceProvider {
246    Memory {
247        primary: SourceBuffer,
248        named: BTreeMap<String, SourceBuffer>,
249    },
250    File {
251        primary: SourceBuffer,
252        acquisition: FileAcquisition,
253    },
254    Directory {
255        acquisition: FileAcquisition,
256    },
257}
258
259/// Opaque owner or provider of named immutable input buffers.
260///
261/// File acquisition policy belongs here rather than to parser entry points.
262/// The primary buffer's reserved identity. The leading slash is a spelling
263/// [`resolve_segments`] can never produce (a referenced name must be
264/// relative), so an acquired or named buffer's identity is disjoint from the
265/// primary's by construction.
266pub const PRIMARY_SOURCE_ID: &str = "/input";
267
268/// [`Source::open`] on a file retains the primary bytes and permits
269/// constrained acquisition of referenced files beneath the file's canonical
270/// containing directory; [`Source::with_acquisition_root`] widens that root at
271/// construction, and never from a parser. [`Source::from_memory`] grants no
272/// filesystem access; referenced content reaches an in-memory source only
273/// through [`Source::with_named_buffer`].
274#[derive(Clone)]
275pub struct Source {
276    name: Arc<str>,
277    provider: Arc<SourceProvider>,
278    declared_format: Option<FormatId>,
279}
280
281impl Source {
282    /// Acquire a file eagerly or a directory lazily.
283    ///
284    /// Primary files are limited to 64 MiB unless `POWERIO_MAX_PRIMARY_BYTES`
285    /// supplies a positive decimal byte count. The limit is checked before
286    /// allocation. Referenced files have a separate cumulative budget.
287    pub fn open(path: impl Into<PathBuf>) -> Result<Self, Error> {
288        let path = path.into();
289        if path.as_os_str().is_empty() {
290            return Err(Error::new(
291                &crate::codes::REQUEST_SOURCE_INVALID_PATH,
292                "source path cannot be empty",
293            ));
294        }
295        let name: Arc<str> = path.to_string_lossy().into_owned().into();
296
297        // The open itself refuses a symbolic link at the named path, so there
298        // is no metadata inspection that a concurrent replacement could
299        // invalidate before the read; the file or directory decision reads
300        // the already opened handle.
301        let file = match platform::open_no_follow(&path) {
302            Ok(file) => file,
303            Err(error) if platform::is_symlink_refusal(&error) => {
304                return Err(Error::new(
305                    &crate::codes::REQUEST_SOURCE_SYMLINK_REFUSED,
306                    format!("source `{}` is a symbolic link", path.display()),
307                ));
308            }
309            Err(error) if platform::is_directory_open_failure(&error, &path) => {
310                return Self::open_directory(name, &path);
311            }
312            Err(cause) => return Err(open_error(&crate::codes::READ_IO_OPEN, &path, cause)),
313        };
314        let metadata = file
315            .metadata()
316            .map_err(|cause| open_error(&crate::codes::READ_IO_METADATA, &path, cause))?;
317        if metadata.is_dir() {
318            drop(file);
319            return Self::open_directory(name, &path);
320        }
321        let limit = parse_primary_limit(std::env::var_os("POWERIO_MAX_PRIMARY_BYTES").as_deref())?;
322        let bytes = read_open_file(file, &name, ReadBudget::Primary(limit))?;
323        let root_display = canonical_parent(&path)?;
324        let primary = SourceBuffer::new(
325            SourceId::new(PRIMARY_SOURCE_ID)?,
326            name.to_string(),
327            bytes,
328            Vec::new(),
329        );
330        Ok(Self {
331            name,
332            provider: Arc::new(SourceProvider::File {
333                primary,
334                acquisition: FileAcquisition {
335                    root_display,
336                    selected: false,
337                    state: Mutex::new(AcquisitionState::default()),
338                },
339            }),
340            declared_format: None,
341        })
342    }
343
344    fn open_directory(name: Arc<str>, path: &Path) -> Result<Self, Error> {
345        let root_display = std::fs::canonicalize(path)
346            .map_err(|cause| open_error(&crate::codes::READ_IO_METADATA, path, cause))?;
347        Ok(Self {
348            name,
349            provider: Arc::new(SourceProvider::Directory {
350                acquisition: FileAcquisition {
351                    root_display,
352                    selected: false,
353                    state: Mutex::new(AcquisitionState::default()),
354                },
355            }),
356            declared_format: None,
357        })
358    }
359
360    /// Retain a caller-owned binary or text buffer. An `Arc<[u8]>` argument
361    /// is retained without copying; a `Vec<u8>` is copied once into the
362    /// shared buffer, since `Arc<[u8]>` needs its own allocation.
363    pub fn from_memory(
364        name: impl Into<String>,
365        bytes: impl Into<Arc<[u8]>>,
366    ) -> Result<Self, Error> {
367        let name = name.into();
368        if !valid_nonempty_text(&name) {
369            return Err(Error::new(
370                &crate::codes::REQUEST_SOURCE_INVALID_NAME,
371                "an in-memory source requires a nonempty bounded name",
372            ));
373        }
374        let primary = SourceBuffer::new(
375            SourceId::new(PRIMARY_SOURCE_ID)?,
376            name.clone(),
377            bytes.into(),
378            Vec::new(),
379        );
380        Ok(Self {
381            name: name.into(),
382            provider: Arc::new(SourceProvider::Memory {
383                primary,
384                named: BTreeMap::new(),
385            }),
386            declared_format: None,
387        })
388    }
389
390    /// Supply one referenced buffer to an in-memory source under the relative
391    /// name a format uses to refer to it. This is the only way referenced
392    /// content reaches a source built by [`Source::from_memory`]; such a source
393    /// never touches the filesystem.
394    pub fn with_named_buffer(
395        self,
396        name: impl Into<String>,
397        bytes: impl Into<Arc<[u8]>>,
398    ) -> Result<Self, Error> {
399        let name = name.into();
400        let segments = resolve_segments(&[], &name)?;
401        let key = segments.join("/");
402        let mut provider = Arc::try_unwrap(self.provider).map_err(|_| {
403            Error::new(
404                &crate::codes::REQUEST_SOURCE_INVALID_NAME,
405                "named buffers are supplied while constructing a source, before it is shared",
406            )
407        })?;
408        let SourceProvider::Memory { named, .. } = &mut provider else {
409            return Err(Error::new(
410                &crate::codes::REQUEST_SOURCE_INVALID_NAME,
411                "named buffers belong to in-memory sources; a file source acquires referenced files beneath its root",
412            ));
413        };
414        let directory = segments[..segments.len() - 1].to_vec();
415        let buffer = SourceBuffer::new(SourceId::new(&key)?, key.clone(), bytes.into(), directory);
416        named.insert(key, buffer);
417        Ok(Self {
418            name: self.name,
419            provider: Arc::new(provider),
420            declared_format: self.declared_format,
421        })
422    }
423
424    /// Widen the acquisition root of a file source to a directory that
425    /// contains the file, selected while constructing the source. A parser
426    /// can never widen the root.
427    pub fn with_acquisition_root(self, root: impl Into<PathBuf>) -> Result<Self, Error> {
428        let requested = root.into();
429        let SourceProvider::File {
430            primary,
431            acquisition,
432        } = &*self.provider
433        else {
434            return Err(Error::new(
435                &crate::codes::REQUEST_SOURCE_INVALID_PATH,
436                "an acquisition root applies to a file source",
437            ));
438        };
439        let canonical = std::fs::canonicalize(&requested)
440            .map_err(|cause| open_error(&crate::codes::READ_IO_METADATA, &requested, cause))?;
441        let Ok(remainder) = acquisition.root_display.strip_prefix(&canonical) else {
442            return Err(Error::new(
443                &crate::codes::REQUEST_SOURCE_INVALID_PATH,
444                format!(
445                    "the case file directory {} is outside the requested acquisition root {}",
446                    acquisition.root_display.display(),
447                    canonical.display()
448                ),
449            ));
450        };
451        let mut directory = Vec::new();
452        for component in remainder.components() {
453            let Component::Normal(segment) = component else {
454                return Err(Error::new(
455                    &crate::codes::REQUEST_SOURCE_INVALID_PATH,
456                    "the acquisition root does not resolve to a plain prefix of the case directory",
457                ));
458            };
459            let Some(segment) = segment.to_str().filter(|text| plain_segment(text)) else {
460                return Err(Error::new(
461                    &crate::codes::REQUEST_SOURCE_INVALID_PATH,
462                    "the acquisition root does not resolve to a plain prefix of the case directory",
463                ));
464            };
465            directory.push(segment.to_owned());
466        }
467        let primary = SourceBuffer::new(
468            primary.id().clone(),
469            primary.name().to_owned(),
470            primary.shared_bytes(),
471            directory,
472        );
473        Ok(Self {
474            name: self.name,
475            provider: Arc::new(SourceProvider::File {
476                primary,
477                acquisition: FileAcquisition {
478                    root_display: canonical,
479                    selected: true,
480                    state: Mutex::new(AcquisitionState::default()),
481                },
482            }),
483            declared_format: self.declared_format,
484        })
485    }
486
487    /// Select one parser explicitly while retaining the same source owner.
488    #[must_use]
489    pub fn with_format(mut self, format: FormatId) -> Self {
490        self.declared_format = Some(format);
491        self
492    }
493
494    #[must_use]
495    pub fn name(&self) -> &str {
496        &self.name
497    }
498
499    #[must_use]
500    pub const fn format(&self) -> Option<&FormatId> {
501        self.declared_format.as_ref()
502    }
503
504    #[must_use]
505    pub fn is_directory(&self) -> bool {
506        matches!(&*self.provider, SourceProvider::Directory { .. })
507    }
508
509    /// Borrow the sole primary buffer of a file or memory source.
510    pub fn primary_buffer(&self) -> Result<SourceBuffer, Error> {
511        match &*self.provider {
512            SourceProvider::Memory { primary, .. } | SourceProvider::File { primary, .. } => {
513                Ok(primary.clone())
514            }
515            SourceProvider::Directory { .. } => Err(Error::new(
516                &crate::codes::REQUEST_SOURCE_DIRECTORY_REQUIRED,
517                "a directory source has no implicit primary buffer",
518            )
519            .with_source(self.clone())),
520        }
521    }
522
523    /// Acquire and retain one file of a directory source by its root relative
524    /// name.
525    pub fn buffer(&self, name: &crate::ArtifactPath) -> Result<SourceBuffer, Error> {
526        let SourceProvider::Directory { acquisition } = &*self.provider else {
527            return Err(Error::new(
528                &crate::codes::REQUEST_SOURCE_DIRECTORY_REQUIRED,
529                "named child buffers require a directory source",
530            )
531            .with_source(self.clone()));
532        };
533        let segments = resolve_segments(&[], name.as_str())
534            .map_err(|error| error.with_source(self.clone()))?;
535        acquisition
536            .acquire(&segments)
537            .map_err(|error| error.with_source(self.clone()))
538    }
539
540    /// Acquire and retain one file beneath the acquisition root by its root
541    /// relative name, for a parser that resolves referenced names itself and
542    /// hands over the resolved result. An in-memory source consults the
543    /// buffers the caller supplied.
544    pub fn root_buffer(&self, name: &str) -> Result<SourceBuffer, Error> {
545        match &*self.provider {
546            SourceProvider::Memory { named, .. } => {
547                let segments = resolve_segments(&[], name)?;
548                let key = segments.join("/");
549                named.get(&key).cloned().ok_or_else(|| {
550                    Error::new(
551                        &crate::codes::REQUEST_SOURCE_UNKNOWN_BUFFER,
552                        format!(
553                            "referenced buffer `{key}` was not supplied to this in-memory source"
554                        ),
555                    )
556                    .with_source(self.clone())
557                })
558            }
559            SourceProvider::File { acquisition, .. }
560            | SourceProvider::Directory { acquisition } => {
561                let segments = resolve_segments(&[], name)?;
562                acquisition
563                    .acquire(&segments)
564                    .map_err(|error| error.with_source(self.clone()))
565            }
566        }
567    }
568
569    /// The canonical root selected with [`Source::with_acquisition_root`],
570    /// `None` when the root defaulted to the containing directory or the
571    /// source is not file backed. Read-only context for a parser's own
572    /// resolution and refusal wording; acquisition itself always goes
573    /// through this source.
574    #[must_use]
575    pub fn selected_acquisition_root(&self) -> Option<&Path> {
576        match &*self.provider {
577            SourceProvider::Memory { .. } | SourceProvider::Directory { .. } => None,
578            SourceProvider::File { acquisition, .. } => acquisition
579                .selected
580                .then_some(acquisition.root_display.as_path()),
581        }
582    }
583
584    /// Acquire and retain one file referenced by `referrer`, resolved against
585    /// the referring file's directory and confined beneath the acquisition
586    /// root. An in-memory source resolves the same name against the buffers
587    /// the caller supplied and never touches the filesystem.
588    pub fn referenced_buffer(
589        &self,
590        referrer: &SourceBuffer,
591        name: &str,
592    ) -> Result<SourceBuffer, Error> {
593        let referrer_directory: Vec<&str> = referrer.directory_segments().collect();
594        match &*self.provider {
595            SourceProvider::Memory { named, .. } => {
596                let segments = resolve_segments(&referrer_directory, name)?;
597                let key = segments.join("/");
598                named.get(&key).cloned().ok_or_else(|| {
599                    Error::new(
600                        &crate::codes::REQUEST_SOURCE_UNKNOWN_BUFFER,
601                        format!(
602                            "referenced buffer `{key}` was not supplied to this in-memory source"
603                        ),
604                    )
605                    .with_source(self.clone())
606                })
607            }
608            SourceProvider::File { acquisition, .. }
609            | SourceProvider::Directory { acquisition } => {
610                let segments = match absolute_to_root_relative(&acquisition.root_display, name) {
611                    Some(root_relative) => root_relative?,
612                    None => resolve_segments(&referrer_directory, name)?,
613                };
614                acquisition
615                    .acquire(&segments)
616                    .map_err(|error| error.with_source(self.clone()))
617            }
618        }
619    }
620
621    /// The root relative file names of a directory source, in sorted order,
622    /// so a directory format can report files outside its profile without
623    /// touching the filesystem itself. Symbolic links are listed by name and
624    /// refused if acquired. The listing is bounded by the referenced file
625    /// budget; a directory holding more entries is refused.
626    #[allow(clippy::too_many_lines)] // one bounded walk, framed and budgeted in place
627    pub fn entry_names(&self) -> Result<Vec<crate::ArtifactPath>, Error> {
628        match &*self.provider {
629            SourceProvider::Memory { named, .. } => named
630                .keys()
631                .map(|name| crate::ArtifactPath::new(name.clone()))
632                .collect(),
633            SourceProvider::File { .. } => Err(Error::new(
634                &crate::codes::REQUEST_SOURCE_DIRECTORY_REQUIRED,
635                "entry listing requires a directory source",
636            )
637            .with_source(self.clone())),
638            SourceProvider::Directory { acquisition } => {
639                // The walk runs against the pinned root handle so a directory
640                // component swapped for a symbolic link mid-listing fails the
641                // descriptor walk rather than redirecting the listing outside
642                // the root. The walk is depth first over frames, one open
643                // handle per level: a child is opened from its parent's live
644                // handle, and a frame's handle closes when its subdirectories
645                // are exhausted, so the descriptors held at any moment are
646                // bounded by the depth bound, never by the entry budget or a
647                // directory's fan-out. Every budget bounds the work before it
648                // is incurred: entries stop being read the moment the
649                // remaining allowance is exhausted, and a prefix at the depth
650                // bound is refused before it is walked. The lock is held
651                // across the walk, like acquisition.
652                struct Frame<Handle> {
653                    prefix: Vec<String>,
654                    directory: Handle,
655                    /// Subdirectory names discovered and not yet descended.
656                    subdirectories: Vec<String>,
657                }
658
659                let mut state = acquisition.lock();
660                if let Some(listed) = &state.listed {
661                    return Ok(listed.clone());
662                }
663                let root = state.pinned_root(&acquisition.root_display)?;
664                let budget_refusal = || {
665                    Error::new(
666                        &crate::codes::READ_IO_REFERENCE_BUDGET,
667                        format!(
668                            "the source directory holds more than {MAX_REFERENCED_FILES} entries"
669                        ),
670                    )
671                };
672                let root_handle = root.duplicate_handle().map_err(|cause| {
673                    Error::new(
674                        &crate::codes::READ_IO_METADATA,
675                        "cannot list the source directory root",
676                    )
677                    .with_cause(cause)
678                })?;
679                let mut names = Vec::new();
680                // Subdirectory names discovered across every frame and not
681                // yet listed; each still charges the entry budget.
682                let mut undescended = 0usize;
683                let mut frames = Vec::new();
684                let mut arriving = Some((Vec::<String>::new(), root_handle));
685                while let Some((prefix, directory)) = arriving.take() {
686                    let allowance = MAX_REFERENCED_FILES
687                        .checked_sub(names.len() + undescended)
688                        .filter(|allowance| *allowance > 0)
689                        .ok_or_else(budget_refusal)?;
690                    let entries =
691                        platform::list_entries(&directory, allowance).map_err(|cause| {
692                            if platform::is_entry_budget(&cause) {
693                                budget_refusal()
694                            } else {
695                                listing_error(&prefix.join("/"), cause)
696                            }
697                        })?;
698                    let mut subdirectories = Vec::new();
699                    for (name, is_directory) in entries {
700                        if names.len() + undescended + subdirectories.len() >= MAX_REFERENCED_FILES
701                        {
702                            return Err(budget_refusal());
703                        }
704                        if is_directory {
705                            if prefix.len() + 1 >= MAX_REFERENCED_DEPTH {
706                                return Err(Error::new(
707                                    &crate::codes::READ_IO_REFERENCE_BUDGET,
708                                    format!(
709                                        "the source directory nests more than {MAX_REFERENCED_DEPTH} levels deep"
710                                    ),
711                                ));
712                            }
713                            subdirectories.push(name);
714                        } else {
715                            let mut child = prefix.clone();
716                            child.push(name);
717                            names.push(crate::ArtifactPath::new(child.join("/"))?);
718                        }
719                    }
720                    undescended += subdirectories.len();
721                    frames.push(Frame {
722                        prefix,
723                        directory,
724                        subdirectories,
725                    });
726
727                    // Descend into the deepest frame's next subdirectory;
728                    // pop and drop a frame — closing its handle — as soon as
729                    // its subdirectories are exhausted, before any sibling
730                    // opens.
731                    while let Some(frame) = frames.last_mut() {
732                        let Some(name) = frame.subdirectories.pop() else {
733                            frames.pop();
734                            continue;
735                        };
736                        undescended -= 1;
737                        let mut child = frame.prefix.clone();
738                        let handle = platform::open_child_directory(&frame.directory, &name)
739                            .map_err(|cause| {
740                                child.push(name.clone());
741                                listing_error(&child.join("/"), cause)
742                            })?;
743                        child.push(name);
744                        arriving = Some((child, handle));
745                        break;
746                    }
747                }
748                names.sort();
749                state.listed = Some(names.clone());
750                Ok(names)
751            }
752        }
753    }
754
755    /// Buffers already retained by this source, in deterministic name order.
756    #[must_use]
757    pub fn acquired_buffers(&self) -> Vec<SourceBuffer> {
758        match &*self.provider {
759            SourceProvider::Memory { primary, named } => {
760                let mut buffers = vec![primary.clone()];
761                buffers.extend(named.values().cloned());
762                buffers
763            }
764            SourceProvider::File {
765                primary,
766                acquisition,
767            } => {
768                let mut buffers = vec![primary.clone()];
769                buffers.extend(acquisition.lock().cache.values().cloned());
770                buffers
771            }
772            SourceProvider::Directory { acquisition } => {
773                acquisition.lock().cache.values().cloned().collect()
774            }
775        }
776    }
777}
778
779impl fmt::Debug for Source {
780    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
781        formatter
782            .debug_struct("Source")
783            .field("name", &self.name)
784            .field("is_directory", &self.is_directory())
785            .field("declared_format", &self.declared_format)
786            .field("acquired_buffer_count", &self.acquired_buffers().len())
787            .finish_non_exhaustive()
788    }
789}
790
791impl FileAcquisition {
792    fn lock(&self) -> MutexGuard<'_, AcquisitionState> {
793        self.state
794            .lock()
795            .unwrap_or_else(std::sync::PoisonError::into_inner)
796    }
797
798    /// Read and retain one file beneath the root. The lock is held across the
799    /// read so a file is read once, retained once, and charged once even under
800    /// concurrent requests. The remaining byte budget bounds the allocation
801    /// itself: it is computed before the read and handed to the reader, so a
802    /// file that would overrun the budget is refused before its bytes are
803    /// reserved.
804    fn acquire(&self, segments: &[String]) -> Result<SourceBuffer, Error> {
805        let key = segments.join("/");
806        let mut state = self.lock();
807        if let Some(buffer) = state.cache.get(&key) {
808            return Ok(buffer.clone());
809        }
810        if state.files >= MAX_REFERENCED_FILES {
811            return Err(Error::new(
812                &crate::codes::READ_IO_REFERENCE_BUDGET,
813                format!("this source already acquired {MAX_REFERENCED_FILES} referenced files"),
814            ));
815        }
816        let remaining = MAX_REFERENCED_BYTES.saturating_sub(state.bytes);
817        let root = state.pinned_root(&self.root_display)?;
818        let file = root.open_beneath(segments).map_err(|error| {
819            if platform::is_symlink_refusal(&error) {
820                Error::new(
821                    &crate::codes::REQUEST_SOURCE_SYMLINK_REFUSED,
822                    format!("referenced file `{key}` crosses a symbolic link"),
823                )
824            } else {
825                Error::new(
826                    &crate::codes::READ_IO_OPEN,
827                    format!("cannot open referenced file `{key}`"),
828                )
829                .with_cause(error)
830            }
831        })?;
832        let bytes = read_open_file(file, &key, ReadBudget::Referenced(remaining))?;
833        let directory = segments[..segments.len() - 1].to_vec();
834        let buffer = SourceBuffer::new(SourceId::new(&key)?, key.clone(), bytes, directory);
835        state.files += 1;
836        state.bytes += buffer.bytes().len() as u64;
837        state.cache.insert(key, buffer.clone());
838        Ok(buffer)
839    }
840}
841
842/// True when `segment` is a single plain file name on every supported
843/// platform: it reduces to exactly one normal path component equal to itself,
844/// so it carries no separator of any platform, no drive or root prefix, no
845/// NUL, and is neither `.` nor `..` nor empty. Joining such segments one at a
846/// time onto a directory cannot reach outside that directory.
847fn plain_segment(segment: &str) -> bool {
848    if segment.is_empty()
849        || segment == "."
850        || segment == ".."
851        || segment.contains(['/', '\\', '\0', ':'])
852    {
853        return false;
854    }
855    let mut components = Path::new(segment).components();
856    matches!(components.next(), Some(Component::Normal(text)) if text == std::ffi::OsStr::new(segment))
857        && components.next().is_none()
858}
859
860/// Resolve a referenced name against the referring directory, lexically and
861/// before any filesystem access. The name is split on both separators, `.` is
862/// dropped, `..` pops within the root and is refused past it, a leading
863/// separator or drive spelling is refused, and every remaining segment must be
864/// a single plain file name on the platform under test. The result is the
865/// exact component list the platform walk opens one at a time.
866fn resolve_segments(referrer_directory: &[&str], name: &str) -> Result<Vec<String>, Error> {
867    if name.is_empty()
868        || name.len() > crate::validation::MAX_ARTIFACT_PATH_BYTES
869        || name.contains('\0')
870        || name.starts_with(['/', '\\'])
871    {
872        return Err(Error::new(
873            &crate::codes::REQUEST_SOURCE_INVALID_PATH,
874            "a referenced name must be a nonempty bounded relative path",
875        ));
876    }
877    let mut segments: Vec<String> = referrer_directory
878        .iter()
879        .map(|segment| (*segment).to_owned())
880        .collect();
881    for raw in name.split(['/', '\\']) {
882        match raw {
883            "" | "." => {}
884            ".." => {
885                if segments.pop().is_none() {
886                    return Err(Error::new(
887                        &crate::codes::REQUEST_SOURCE_ESCAPES_ROOT,
888                        format!("referenced name `{name}` resolves outside the acquisition root"),
889                    ));
890                }
891            }
892            segment => {
893                if segment.len() > crate::validation::MAX_ARTIFACT_PATH_BYTES
894                    || !plain_segment(segment)
895                {
896                    return Err(Error::new(
897                        &crate::codes::REQUEST_SOURCE_INVALID_PATH,
898                        format!("referenced name `{name}` is not a portable relative path"),
899                    ));
900                }
901                segments.push(segment.to_owned());
902            }
903        }
904    }
905    if segments.is_empty() {
906        return Err(Error::new(
907            &crate::codes::REQUEST_SOURCE_INVALID_PATH,
908            format!("referenced name `{name}` does not name a file"),
909        ));
910    }
911    if segments.len() > MAX_REFERENCED_DEPTH {
912        return Err(Error::new(
913            &crate::codes::READ_IO_REFERENCE_BUDGET,
914            format!("referenced name `{name}` nests more than {MAX_REFERENCED_DEPTH} levels deep"),
915        ));
916    }
917    Ok(segments)
918}
919
920/// An absolute referenced name is accepted only when it sits lexically beneath
921/// the canonical root; the walk then reopens it component by component from
922/// the pinned root handle. Returns `None` for a relative name. The segment
923/// list is built from the platform's own path components, one segment per
924/// directory component, each required to be a plain file name.
925fn absolute_to_root_relative(root: &Path, name: &str) -> Option<Result<Vec<String>, Error>> {
926    let path = Path::new(name);
927    if !path.is_absolute() {
928        return None;
929    }
930    let Ok(remainder) = path.strip_prefix(root) else {
931        return Some(Err(Error::new(
932            &crate::codes::REQUEST_SOURCE_ESCAPES_ROOT,
933            format!("referenced name `{name}` resolves outside the acquisition root"),
934        )));
935    };
936    let mut segments = Vec::new();
937    for component in remainder.components() {
938        let text = match component {
939            Component::Normal(text) => text.to_str(),
940            _ => None,
941        };
942        let Some(text) = text.filter(|text| plain_segment(text)) else {
943            return Some(Err(Error::new(
944                &crate::codes::REQUEST_SOURCE_INVALID_PATH,
945                format!("referenced name `{name}` is not a portable path beneath the root"),
946            )));
947        };
948        segments.push(text.to_owned());
949    }
950    if segments.is_empty() {
951        return Some(Err(Error::new(
952            &crate::codes::REQUEST_SOURCE_INVALID_PATH,
953            format!("referenced name `{name}` does not name a file"),
954        )));
955    }
956    if segments.len() > MAX_REFERENCED_DEPTH {
957        return Some(Err(Error::new(
958            &crate::codes::READ_IO_REFERENCE_BUDGET,
959            format!("referenced name `{name}` nests more than {MAX_REFERENCED_DEPTH} levels deep"),
960        )));
961    }
962    Some(Ok(segments))
963}
964
965fn canonical_parent(path: &Path) -> Result<PathBuf, Error> {
966    let parent = match path.parent() {
967        Some(parent) if !parent.as_os_str().is_empty() => parent,
968        _ => Path::new("."),
969    };
970    std::fs::canonicalize(parent)
971        .map_err(|cause| open_error(&crate::codes::READ_IO_METADATA, parent, cause))
972}
973
974fn open_error(info: &'static crate::DiagnosticInfo, path: &Path, cause: std::io::Error) -> Error {
975    Error::new(info, format!("cannot open source `{}`", path.display())).with_cause(cause)
976}
977
978/// A listing walk failure at one root relative directory: a symbolic link is
979/// the acquisition refusal; anything else is an I/O failure.
980fn listing_error(display: &str, cause: std::io::Error) -> Error {
981    if platform::is_symlink_refusal(&cause) {
982        Error::new(
983            &crate::codes::REQUEST_SOURCE_SYMLINK_REFUSED,
984            format!("source directory `{display}` crosses a symbolic link"),
985        )
986    } else {
987        Error::new(
988            &crate::codes::READ_IO_METADATA,
989            format!("cannot list source directory `{display}`"),
990        )
991        .with_cause(cause)
992    }
993}
994
995/// Read an already opened regular file completely. The handle was opened with
996/// symbolic links refused, and the regular file check runs on the open
997/// descriptor, so no path is consulted twice. The budget bounds the
998/// allocation itself: a file whose declared length exceeds it is refused
999/// before any bytes are reserved, and the reader is capped so a file that
1000/// grows past the bound during the read is refused rather than read.
1001fn read_open_file(file: std::fs::File, name: &str, budget: ReadBudget) -> Result<Arc<[u8]>, Error> {
1002    let max_bytes = budget.bytes();
1003    let metadata = file.metadata().map_err(|cause| {
1004        Error::new(
1005            &crate::codes::READ_IO_METADATA,
1006            format!("cannot inspect source buffer `{name}`"),
1007        )
1008        .with_cause(cause)
1009    })?;
1010    if !metadata.is_file() {
1011        return Err(Error::new(
1012            &crate::codes::REQUEST_SOURCE_NOT_A_FILE,
1013            format!("source buffer `{name}` is not a regular file"),
1014        ));
1015    }
1016    let declared_length = metadata.len();
1017    if declared_length > max_bytes {
1018        return Err(budget.exceeded(name));
1019    }
1020    let capacity = usize::try_from(declared_length).map_err(|cause| {
1021        Error::new(
1022            &crate::codes::READ_IO_ALLOCATION_REFUSED,
1023            format!("source buffer `{name}` is too large for this platform"),
1024        )
1025        .with_cause(cause)
1026    })?;
1027    let mut bytes = Vec::new();
1028    bytes.try_reserve_exact(capacity).map_err(|cause| {
1029        Error::new(
1030            &crate::codes::READ_IO_ALLOCATION_REFUSED,
1031            format!("cannot reserve {declared_length} bytes for source buffer `{name}`"),
1032        )
1033        .with_cause(cause)
1034    })?;
1035    let read_limit = declared_length
1036        .checked_add(1)
1037        .ok_or_else(|| {
1038            Error::new(
1039                &crate::codes::READ_IO_ALLOCATION_REFUSED,
1040                format!("source buffer `{name}` is too large to read safely"),
1041            )
1042        })?
1043        .min(max_bytes.saturating_add(1));
1044    let mut file = file;
1045    file.by_ref()
1046        .take(read_limit)
1047        .read_to_end(&mut bytes)
1048        .map_err(|cause| {
1049            Error::new(
1050                &crate::codes::READ_IO_READ,
1051                format!("cannot read source buffer `{name}`"),
1052            )
1053            .with_cause(cause)
1054        })?;
1055    if bytes.len() != capacity {
1056        return Err(Error::new(
1057            &crate::codes::READ_IO_SOURCE_CHANGED,
1058            format!("source buffer `{name}` changed length while it was read"),
1059        ));
1060    }
1061    Ok(bytes.into())
1062}
1063
1064#[cfg(unix)]
1065mod platform {
1066    //! Descriptor-relative acquisition: the root directory is pinned by an
1067    //! open descriptor, and every referenced component is opened relative to
1068    //! it with `O_NOFOLLOW`, so replacing a component with a symbolic link
1069    //! during acquisition fails instead of redirecting the read outside the
1070    //! root. Every open also carries `O_NONBLOCK`, so the open call itself
1071    //! never waits on another process (a FIFO with no writer opens
1072    //! immediately and is then refused by the regular file check on the
1073    //! descriptor); the flag is cleared before any read.
1074
1075    use std::ffi::CString;
1076    use std::fs::File;
1077    use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
1078    use std::os::unix::ffi::OsStrExt;
1079    use std::path::Path;
1080
1081    #[derive(Debug)]
1082    pub(super) struct RootHandle(OwnedFd);
1083
1084    /// Open the named path with symbolic links at the final component refused
1085    /// by the kernel and without the open itself blocking on another process.
1086    pub(super) fn open_no_follow(path: &Path) -> std::io::Result<File> {
1087        let path = c_string(path.as_os_str().as_bytes())?;
1088        // SAFETY: the pointer references a NUL-terminated buffer owned by
1089        // `path`, which outlives the call; the returned descriptor is owned
1090        // exclusively by the `File` constructed below.
1091        let fd = unsafe {
1092            libc::open(
1093                path.as_ptr(),
1094                libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NONBLOCK,
1095            )
1096        };
1097        if fd < 0 {
1098            return Err(std::io::Error::last_os_error());
1099        }
1100        // SAFETY: `fd` is a freshly opened descriptor this function owns.
1101        let file = unsafe { File::from_raw_fd(fd) };
1102        clear_nonblock(&file)?;
1103        Ok(file)
1104    }
1105
1106    /// Open a root directory with a symbolic link at the final component
1107    /// refused, and the directory confirmed on the opened descriptor. Not
1108    /// `O_DIRECTORY | O_NOFOLLOW`: Darwin reports that combination on a
1109    /// symbolic link as `ENOTDIR`, hiding the refusal reason.
1110    pub(super) fn open_root(path: &Path) -> std::io::Result<RootHandle> {
1111        let path = c_string(path.as_os_str().as_bytes())?;
1112        // SAFETY: as in `open_no_follow`.
1113        let fd = unsafe {
1114            libc::open(
1115                path.as_ptr(),
1116                libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NONBLOCK,
1117            )
1118        };
1119        if fd < 0 {
1120            return Err(std::io::Error::last_os_error());
1121        }
1122        // SAFETY: `fd` is a freshly opened descriptor this function owns.
1123        let file = unsafe { File::from_raw_fd(fd) };
1124        if !file.metadata()?.is_dir() {
1125            return Err(std::io::Error::from(std::io::ErrorKind::NotADirectory));
1126        }
1127        clear_nonblock(&file)?;
1128        Ok(RootHandle(file.into()))
1129    }
1130
1131    impl RootHandle {
1132        pub(super) fn open_beneath(&self, segments: &[String]) -> std::io::Result<File> {
1133            let mut directory: Option<OwnedFd> = None;
1134            let (file_segment, directories) =
1135                segments.split_last().expect("resolution yields a file");
1136            for segment in directories {
1137                // `O_NOFOLLOW` alone, then a directory check on the opened
1138                // descriptor: Darwin reports `O_NOFOLLOW | O_DIRECTORY` on a
1139                // symbolic link as `ENOTDIR`, which would hide the refusal
1140                // reason, and checking the descriptor cannot race.
1141                let next = self.open_at(
1142                    directory.as_ref(),
1143                    segment,
1144                    libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NONBLOCK,
1145                )?;
1146                let next = File::from(next);
1147                if !next.metadata()?.is_dir() {
1148                    return Err(std::io::Error::from(std::io::ErrorKind::NotADirectory));
1149                }
1150                directory = Some(next.into());
1151            }
1152            let fd = self.open_at(
1153                directory.as_ref(),
1154                file_segment,
1155                libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NONBLOCK,
1156            )?;
1157            let file = File::from(fd);
1158            clear_nonblock(&file)?;
1159            Ok(file)
1160        }
1161
1162        /// A fresh open file description of the root, for a listing walk
1163        /// that opens each child directory relative to its parent. `dup`
1164        /// would share the directory stream position with the pinned root
1165        /// (and with every other walk), so a walk that stops early would
1166        /// leave the next one a shorter listing; `openat(fd, ".")` yields an
1167        /// independent description of the same directory with no path in
1168        /// between.
1169        pub(super) fn duplicate_handle(&self) -> std::io::Result<DirectoryHandle> {
1170            let segment = c_string(b".")?;
1171            // SAFETY: the root descriptor is live for the call and the
1172            // pointer references a NUL-terminated buffer owned by `segment`.
1173            let fd = unsafe {
1174                libc::openat(
1175                    self.0.as_raw_fd(),
1176                    segment.as_ptr(),
1177                    libc::O_RDONLY | libc::O_CLOEXEC | libc::O_DIRECTORY,
1178                )
1179            };
1180            if fd < 0 {
1181                return Err(std::io::Error::last_os_error());
1182            }
1183            // SAFETY: `fd` is a freshly opened descriptor this function owns.
1184            Ok(unsafe { OwnedFd::from_raw_fd(fd) })
1185        }
1186
1187        fn open_at(
1188            &self,
1189            directory: Option<&OwnedFd>,
1190            segment: &str,
1191            flags: libc::c_int,
1192        ) -> std::io::Result<OwnedFd> {
1193            if !super::plain_segment(segment) {
1194                return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
1195            }
1196            let at = directory.map_or_else(|| self.0.as_raw_fd(), AsRawFd::as_raw_fd);
1197            let segment = c_string(segment.as_bytes())?;
1198            // SAFETY: `at` is a live descriptor owned by `self` or by the
1199            // caller's `directory` for the duration of the call, and the
1200            // pointer references a NUL-terminated buffer owned by `segment`.
1201            let fd = unsafe { libc::openat(at, segment.as_ptr(), flags) };
1202            if fd < 0 {
1203                return Err(std::io::Error::last_os_error());
1204            }
1205            // SAFETY: `fd` is a freshly opened descriptor this function owns.
1206            Ok(unsafe { OwnedFd::from_raw_fd(fd) })
1207        }
1208    }
1209
1210    /// An open directory the listing walk can read entries from and open
1211    /// children relative to.
1212    pub(super) type DirectoryHandle = OwnedFd;
1213
1214    /// Open one child directory of an already opened directory, with a
1215    /// symbolic link at the child refused and the directory confirmed on the
1216    /// opened descriptor.
1217    pub(super) fn open_child_directory(
1218        parent: &DirectoryHandle,
1219        name: &str,
1220    ) -> std::io::Result<DirectoryHandle> {
1221        if !super::plain_segment(name) {
1222            return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
1223        }
1224        let segment = c_string(name.as_bytes())?;
1225        // SAFETY: `parent` is a live descriptor for the duration of the call,
1226        // and the pointer references the NUL-terminated buffer owned by
1227        // `segment`.
1228        let fd = unsafe {
1229            libc::openat(
1230                parent.as_raw_fd(),
1231                segment.as_ptr(),
1232                libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NONBLOCK,
1233            )
1234        };
1235        if fd < 0 {
1236            return Err(std::io::Error::last_os_error());
1237        }
1238        // SAFETY: `fd` is a freshly opened descriptor this function owns.
1239        let file = unsafe { File::from_raw_fd(fd) };
1240        if !file.metadata()?.is_dir() {
1241            return Err(std::io::Error::from(std::io::ErrorKind::NotADirectory));
1242        }
1243        Ok(file.into())
1244    }
1245
1246    const ENTRY_BUDGET_MARKER: &str = "directory entry allowance exhausted";
1247
1248    fn entry_budget_error() -> std::io::Error {
1249        std::io::Error::other(ENTRY_BUDGET_MARKER)
1250    }
1251
1252    /// True when a listing failed because it reached the caller's entry
1253    /// allowance rather than a real I/O failure.
1254    pub(super) fn is_entry_budget(error: &std::io::Error) -> bool {
1255        error.kind() == std::io::ErrorKind::Other && error.to_string().contains(ENTRY_BUDGET_MARKER)
1256    }
1257
1258    /// Read the entries of an open directory, at most `max` of them: the
1259    /// bound is enforced inside the read loop, before the entry that would
1260    /// cross it is accepted, so the work and the memory of a listing are
1261    /// bounded by the allowance rather than by the directory's true entry
1262    /// count. Returns each UTF-8 entry name with whether it is a directory;
1263    /// symbolic links are listed by name and refused when acquired.
1264    pub(super) fn list_entries(
1265        directory: &DirectoryHandle,
1266        max: usize,
1267    ) -> std::io::Result<Vec<(String, bool)>> {
1268        use std::os::fd::IntoRawFd;
1269
1270        // The stream takes ownership of a duplicate, so the caller's handle
1271        // stays usable for opening children.
1272        let raw = directory.try_clone()?.into_raw_fd();
1273        // SAFETY: `raw` is a live directory descriptor whose ownership
1274        // transfers to the returned stream; on failure it is closed here.
1275        let stream = unsafe { libc::fdopendir(raw) };
1276        if stream.is_null() {
1277            let error = std::io::Error::last_os_error();
1278            // SAFETY: `raw` is still owned by this function when `fdopendir`
1279            // fails.
1280            unsafe { libc::close(raw) };
1281            return Err(error);
1282        }
1283        let mut entries = Vec::new();
1284        loop {
1285            // SAFETY: `stream` is the live directory stream opened above.
1286            errno_clear();
1287            let entry = unsafe { libc::readdir(stream) };
1288            if entry.is_null() {
1289                let error = std::io::Error::last_os_error();
1290                // SAFETY: `stream` is the live directory stream opened above;
1291                // it is closed exactly once.
1292                unsafe { libc::closedir(stream) };
1293                if error.raw_os_error().is_some_and(|code| code != 0) {
1294                    return Err(error);
1295                }
1296                break;
1297            }
1298            // SAFETY: `entry` is valid until the next `readdir` on this
1299            // stream, and only the NUL-terminated name within the entry is
1300            // read: the raw pointer to the array's first element is followed
1301            // to its terminator, never the whole declared array.
1302            let name_bytes = unsafe {
1303                std::ffi::CStr::from_ptr((&raw const (*entry).d_name).cast::<libc::c_char>())
1304            };
1305            let Ok(name) = name_bytes.to_str() else {
1306                continue;
1307            };
1308            if name == "." || name == ".." {
1309                continue;
1310            }
1311            if entries.len() == max {
1312                // SAFETY: as above; the stream is closed exactly once.
1313                unsafe { libc::closedir(stream) };
1314                return Err(entry_budget_error());
1315            }
1316            // SAFETY: as above; `d_type` is a plain byte field read by copy.
1317            let kind = unsafe { (*entry).d_type };
1318            let is_directory = match kind {
1319                libc::DT_DIR => true,
1320                libc::DT_UNKNOWN => {
1321                    // A filesystem without `d_type` support: ask the
1322                    // descriptor, without following a symbolic link.
1323                    let mut stat: libc::stat = unsafe { std::mem::zeroed() };
1324                    let segment =
1325                        c_string(name.as_bytes()).expect("directory entry names carry no NUL");
1326                    // SAFETY: `stream` is live, so `dirfd` is a live
1327                    // descriptor; the name pointer references the
1328                    // NUL-terminated buffer owned by `segment`.
1329                    let status = unsafe {
1330                        libc::fstatat(
1331                            libc::dirfd(stream),
1332                            segment.as_ptr(),
1333                            &raw mut stat,
1334                            libc::AT_SYMLINK_NOFOLLOW,
1335                        )
1336                    };
1337                    status == 0 && stat.st_mode & libc::S_IFMT == libc::S_IFDIR
1338                }
1339                _ => false,
1340            };
1341            entries.push((name.to_owned(), is_directory));
1342        }
1343        Ok(entries)
1344    }
1345
1346    fn errno_clear() {
1347        errno::set_errno(errno::Errno(0));
1348    }
1349
1350    /// Clear `O_NONBLOCK` on an opened descriptor before it is read.
1351    fn clear_nonblock(file: &File) -> std::io::Result<()> {
1352        let fd = file.as_raw_fd();
1353        // SAFETY: `fd` is a live descriptor owned by `file` for the duration
1354        // of both calls.
1355        let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
1356        if flags < 0 {
1357            return Err(std::io::Error::last_os_error());
1358        }
1359        // SAFETY: as above.
1360        let status = unsafe { libc::fcntl(fd, libc::F_SETFL, flags & !libc::O_NONBLOCK) };
1361        if status < 0 {
1362            return Err(std::io::Error::last_os_error());
1363        }
1364        Ok(())
1365    }
1366
1367    pub(super) fn is_symlink_refusal(error: &std::io::Error) -> bool {
1368        matches!(error.raw_os_error(), Some(libc::ELOOP | libc::EMLINK))
1369    }
1370
1371    pub(super) fn is_directory_open_failure(error: &std::io::Error, path: &Path) -> bool {
1372        // Opening a directory read-only succeeds on Linux and macOS, so the
1373        // usual route is the metadata check on the opened handle; this covers
1374        // a platform whose open refuses directories outright.
1375        let _ = path;
1376        error.raw_os_error() == Some(libc::EISDIR)
1377    }
1378
1379    fn c_string(bytes: &[u8]) -> std::io::Result<CString> {
1380        CString::new(bytes).map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidInput))
1381    }
1382
1383    #[cfg(test)]
1384    mod tests {
1385        use super::*;
1386
1387        #[test]
1388        fn open_root_refuses_a_symbolic_link_to_a_directory() {
1389            let base = std::env::temp_dir().join(format!(
1390                "powerio-core-open-root-{}-{}",
1391                std::process::id(),
1392                std::time::SystemTime::now()
1393                    .duration_since(std::time::UNIX_EPOCH)
1394                    .unwrap()
1395                    .as_nanos()
1396            ));
1397            std::fs::create_dir_all(&base).unwrap();
1398            assert!(open_root(&base).is_ok());
1399            let link = base.join("link");
1400            std::os::unix::fs::symlink(&base, &link).unwrap();
1401            let error = open_root(&link).unwrap_err();
1402            assert!(
1403                is_symlink_refusal(&error) || error.kind() == std::io::ErrorKind::NotADirectory,
1404                "{error:?}"
1405            );
1406            std::fs::remove_dir_all(&base).unwrap();
1407        }
1408    }
1409}
1410
1411#[cfg(not(unix))]
1412mod platform {
1413    //! Windows and other platforms have no `openat`. The walk opens every
1414    //! intermediate directory into a handle held for the remainder of the
1415    //! walk, with reparse points refused on the opened handle and the share
1416    //! mode excluding delete, so a held component can be neither replaced by
1417    //! a link nor renamed away while a child is opened beneath it — the same
1418    //! invariant the Unix descriptor walk enforces.
1419
1420    use std::fs::File;
1421    use std::path::{Path, PathBuf};
1422
1423    #[derive(Debug)]
1424    pub(super) struct RootHandle {
1425        root: PathBuf,
1426    }
1427
1428    pub(super) fn open_no_follow(path: &Path) -> std::io::Result<File> {
1429        let file = open_reparse_refused(path)?;
1430        Ok(file)
1431    }
1432
1433    pub(super) fn open_root(path: &Path) -> std::io::Result<RootHandle> {
1434        // Verified at open; each walk re-pins the root for its own duration,
1435        // so the source's lifetime holds no lock that would block legitimate
1436        // tree changes between walks.
1437        drop(open_directory_pinned(path)?);
1438        Ok(RootHandle {
1439            root: path.to_path_buf(),
1440        })
1441    }
1442
1443    impl RootHandle {
1444        pub(super) fn open_beneath(&self, segments: &[String]) -> std::io::Result<File> {
1445            let mut path = self.root.clone();
1446            let (file_segment, directories) =
1447                segments.split_last().expect("resolution yields a file");
1448            // Every handle from the root down stays alive until the final
1449            // open: the next component is opened only while every ancestor
1450            // is still held, and all release when the walk returns.
1451            let mut held = Vec::with_capacity(directories.len() + 1);
1452            held.push(open_directory_pinned(&self.root)?);
1453            for segment in directories {
1454                push_plain_segment(&mut path, segment)?;
1455                held.push(open_directory_pinned(&path)?);
1456            }
1457            push_plain_segment(&mut path, file_segment)?;
1458            let file = open_reparse_refused(&path)?;
1459            drop(held);
1460            Ok(file)
1461        }
1462
1463        /// The root as a listing handle: the pinned handle plus the verified
1464        /// path each child extends.
1465        pub(super) fn duplicate_handle(&self) -> std::io::Result<DirectoryHandle> {
1466            let handle = open_directory_pinned(&self.root)?;
1467            Ok(DirectoryHandle {
1468                path: self.root.clone(),
1469                _handle: handle,
1470            })
1471        }
1472    }
1473
1474    /// A verified directory the listing walk extends one plain component at
1475    /// a time, holding its own pinned handle; the walk keeps a frame per
1476    /// level, so every ancestor of an open frame stays held.
1477    #[derive(Debug)]
1478    pub(super) struct DirectoryHandle {
1479        path: PathBuf,
1480        _handle: File,
1481    }
1482
1483    /// Extend the walk by one child directory, opened while the parent's
1484    /// handle is held, with a reparse point at the child refused on the
1485    /// opened handle.
1486    pub(super) fn open_child_directory(
1487        parent: &DirectoryHandle,
1488        name: &str,
1489    ) -> std::io::Result<DirectoryHandle> {
1490        let mut path = parent.path.clone();
1491        push_plain_segment(&mut path, name)?;
1492        let handle = open_directory_pinned(&path)?;
1493        Ok(DirectoryHandle {
1494            path,
1495            _handle: handle,
1496        })
1497    }
1498
1499    const ENTRY_BUDGET_MARKER: &str = "directory entry allowance exhausted";
1500
1501    fn entry_budget_error() -> std::io::Error {
1502        std::io::Error::other(ENTRY_BUDGET_MARKER)
1503    }
1504
1505    /// True when a listing failed because it reached the caller's entry
1506    /// allowance rather than a real I/O failure.
1507    pub(super) fn is_entry_budget(error: &std::io::Error) -> bool {
1508        error.kind() == std::io::ErrorKind::Other && error.to_string().contains(ENTRY_BUDGET_MARKER)
1509    }
1510
1511    /// Read the entries of one held directory, at most `max` of them: the
1512    /// bound is enforced inside the read loop, before the entry that would
1513    /// cross it is accepted. The held handle keeps the listed path the
1514    /// verified directory for the duration.
1515    pub(super) fn list_entries(
1516        directory: &DirectoryHandle,
1517        max: usize,
1518    ) -> std::io::Result<Vec<(String, bool)>> {
1519        let mut entries = Vec::new();
1520        for entry in std::fs::read_dir(&directory.path)? {
1521            let entry = entry?;
1522            let Ok(name) = entry.file_name().into_string() else {
1523                continue;
1524            };
1525            if entries.len() == max {
1526                return Err(entry_budget_error());
1527            }
1528            let is_directory = entry.file_type()?.is_dir();
1529            entries.push((name, is_directory));
1530        }
1531        Ok(entries)
1532    }
1533
1534    /// Refuse a segment that is not a single plain file name before it is
1535    /// joined onto the walked path.
1536    fn push_plain_segment(path: &mut PathBuf, segment: &str) -> std::io::Result<()> {
1537        if !super::plain_segment(segment) {
1538            return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput));
1539        }
1540        path.push(segment);
1541        Ok(())
1542    }
1543
1544    /// Open one directory into a held handle with the reparse point refused
1545    /// on the handle itself. On Windows the open uses backup semantics (a
1546    /// directory needs it), keeps `FILE_SHARE_DELETE` out of the share mode
1547    /// so the held component cannot be renamed or deleted, and refuses a
1548    /// handle whose attributes carry the reparse flag.
1549    #[cfg(windows)]
1550    fn open_directory_pinned(path: &Path) -> std::io::Result<File> {
1551        use std::os::windows::fs::{MetadataExt, OpenOptionsExt};
1552
1553        const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
1554        const FILE_FLAG_BACKUP_SEMANTICS: u32 = 0x0200_0000;
1555        const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;
1556        const FILE_ATTRIBUTE_DIRECTORY: u32 = 0x0000_0010;
1557        const FILE_SHARE_READ: u32 = 0x0000_0001;
1558        const FILE_SHARE_WRITE: u32 = 0x0000_0002;
1559
1560        let file = std::fs::OpenOptions::new()
1561            .read(true)
1562            .share_mode(FILE_SHARE_READ | FILE_SHARE_WRITE)
1563            .custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS)
1564            .open(path)?;
1565        let attributes = file.metadata()?.file_attributes();
1566        if attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
1567            return Err(symlink_error());
1568        }
1569        if attributes & FILE_ATTRIBUTE_DIRECTORY == 0 {
1570            return Err(std::io::Error::from(std::io::ErrorKind::NotADirectory));
1571        }
1572        Ok(file)
1573    }
1574
1575    #[cfg(not(windows))]
1576    fn open_directory_pinned(path: &Path) -> std::io::Result<File> {
1577        let metadata = std::fs::symlink_metadata(path)?;
1578        if metadata.file_type().is_symlink() {
1579            return Err(symlink_error());
1580        }
1581        if !metadata.is_dir() {
1582            return Err(std::io::Error::from(std::io::ErrorKind::NotADirectory));
1583        }
1584        File::open(path)
1585    }
1586
1587    #[cfg(windows)]
1588    fn open_reparse_refused(path: &Path) -> std::io::Result<File> {
1589        use std::os::windows::fs::{MetadataExt, OpenOptionsExt};
1590
1591        const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
1592        const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;
1593
1594        let file = std::fs::OpenOptions::new()
1595            .read(true)
1596            .custom_flags(FILE_FLAG_OPEN_REPARSE_POINT)
1597            .open(path)?;
1598        let attributes = file.metadata()?.file_attributes();
1599        if attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
1600            return Err(symlink_error());
1601        }
1602        Ok(file)
1603    }
1604
1605    #[cfg(not(windows))]
1606    fn open_reparse_refused(path: &Path) -> std::io::Result<File> {
1607        let metadata = std::fs::symlink_metadata(path)?;
1608        if metadata.file_type().is_symlink() {
1609            return Err(symlink_error());
1610        }
1611        File::open(path)
1612    }
1613
1614    fn symlink_error() -> std::io::Error {
1615        std::io::Error::new(std::io::ErrorKind::InvalidData, "symbolic link refused")
1616    }
1617
1618    pub(super) fn is_symlink_refusal(error: &std::io::Error) -> bool {
1619        error.kind() == std::io::ErrorKind::InvalidData
1620            && error.to_string().contains("symbolic link refused")
1621    }
1622
1623    pub(super) fn is_directory_open_failure(error: &std::io::Error, path: &Path) -> bool {
1624        let _ = error;
1625        std::fs::symlink_metadata(path).is_ok_and(|metadata| metadata.is_dir())
1626    }
1627}
1628
1629/// The name a source built from content in memory carries when the caller
1630/// supplies none. Format detection and diagnostics read it, so content whose
1631/// name carries meaning goes through [`Source::from_memory`] instead.
1632pub const MEMORY_SOURCE_NAME: &str = "<memory>";
1633
1634/// One input a read operation can acquire.
1635///
1636/// A string or path names a file or directory to open. A byte buffer is
1637/// content already in memory, named [`MEMORY_SOURCE_NAME`]. A [`Source`]
1638/// passes through, so a source carrying named buffers or a widened
1639/// acquisition root reaches the same operations as a file name.
1640///
1641/// A caller's own input type reaches the same operations by implementing it.
1642/// It carries this one method and gains no further required method: options
1643/// belong to the operation, not to the input.
1644pub trait IntoSource {
1645    /// Acquire the input.
1646    ///
1647    /// # Errors
1648    /// The file or directory cannot be acquired, or the in-memory name is
1649    /// invalid.
1650    fn into_source(self) -> Result<Source, Error>;
1651}
1652
1653impl IntoSource for Source {
1654    fn into_source(self) -> Result<Source, Error> {
1655        Ok(self)
1656    }
1657}
1658
1659impl IntoSource for &Source {
1660    fn into_source(self) -> Result<Source, Error> {
1661        Ok(self.clone())
1662    }
1663}
1664
1665/// Names a file or directory to open.
1666macro_rules! into_source_by_name {
1667    ($($input:ty),* $(,)?) => {
1668        $(
1669            impl IntoSource for $input {
1670                fn into_source(self) -> Result<Source, Error> {
1671                    Source::open(PathBuf::from(self))
1672                }
1673            }
1674        )*
1675    };
1676}
1677
1678into_source_by_name!(&str, &String, String, &Path, &PathBuf, PathBuf);
1679
1680/// Content already in memory.
1681macro_rules! into_source_by_content {
1682    ($($input:ty => $bytes:expr),* $(,)?) => {
1683        $(
1684            impl IntoSource for $input {
1685                fn into_source(self) -> Result<Source, Error> {
1686                    let bytes: Arc<[u8]> = $bytes(self);
1687                    Source::from_memory(MEMORY_SOURCE_NAME, bytes)
1688                }
1689            }
1690        )*
1691    };
1692}
1693
1694into_source_by_content!(
1695    &[u8] => Arc::<[u8]>::from,
1696    Vec<u8> => Arc::<[u8]>::from,
1697    Arc<[u8]> => std::convert::identity,
1698);
1699
1700impl<const N: usize> IntoSource for &[u8; N] {
1701    fn into_source(self) -> Result<Source, Error> {
1702        Source::from_memory(MEMORY_SOURCE_NAME, Arc::<[u8]>::from(self.as_slice()))
1703    }
1704}
1705
1706#[cfg(test)]
1707mod tests {
1708    use std::time::{SystemTime, UNIX_EPOCH};
1709
1710    use super::*;
1711    use crate::ArtifactPath;
1712
1713    /// Serializes the tests that measure process wide resources (open
1714    /// descriptor counts, descriptor limits), so a measurement compares
1715    /// against a baseline taken under the same guard rather than a free
1716    /// running count other tests move.
1717    static PROCESS_RESOURCE_TESTS: Mutex<()> = Mutex::new(());
1718
1719    fn process_resource_guard() -> MutexGuard<'static, ()> {
1720        PROCESS_RESOURCE_TESTS
1721            .lock()
1722            .unwrap_or_else(std::sync::PoisonError::into_inner)
1723    }
1724
1725    /// Byte counting global allocator, scoped to the one measuring thread so
1726    /// parallel tests never pollute a measurement.
1727    struct CountingAllocator;
1728
1729    static ALLOCATED_BYTES: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
1730
1731    thread_local! {
1732        static MEASURING: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
1733    }
1734
1735    fn measured_bytes<T>(work: impl FnOnce() -> T) -> (T, usize) {
1736        let _ = MEASURING.try_with(|flag| flag.set(true));
1737        let before = ALLOCATED_BYTES.load(std::sync::atomic::Ordering::Relaxed);
1738        let value = work();
1739        let after = ALLOCATED_BYTES.load(std::sync::atomic::Ordering::Relaxed);
1740        let _ = MEASURING.try_with(|flag| flag.set(false));
1741        (value, after.saturating_sub(before))
1742    }
1743
1744    // SAFETY: delegates every operation to the system allocator; the counter
1745    // is a side effect on the measuring thread only.
1746    unsafe impl std::alloc::GlobalAlloc for CountingAllocator {
1747        unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 {
1748            if MEASURING.try_with(std::cell::Cell::get).unwrap_or(false) {
1749                ALLOCATED_BYTES.fetch_add(layout.size(), std::sync::atomic::Ordering::Relaxed);
1750            }
1751            unsafe { std::alloc::System.alloc(layout) }
1752        }
1753
1754        unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) {
1755            unsafe { std::alloc::System.dealloc(ptr, layout) }
1756        }
1757
1758        unsafe fn realloc(
1759            &self,
1760            ptr: *mut u8,
1761            layout: std::alloc::Layout,
1762            new_size: usize,
1763        ) -> *mut u8 {
1764            if MEASURING.try_with(std::cell::Cell::get).unwrap_or(false) {
1765                ALLOCATED_BYTES.fetch_add(new_size, std::sync::atomic::Ordering::Relaxed);
1766            }
1767            unsafe { std::alloc::System.realloc(ptr, layout, new_size) }
1768        }
1769    }
1770
1771    #[global_allocator]
1772    static COUNTING: CountingAllocator = CountingAllocator;
1773
1774    fn test_root(name: &str) -> PathBuf {
1775        let nonce = SystemTime::now()
1776            .duration_since(UNIX_EPOCH)
1777            .unwrap()
1778            .as_nanos();
1779        std::env::temp_dir().join(format!(
1780            "powerio-core-{name}-{}-{nonce}",
1781            std::process::id()
1782        ))
1783    }
1784
1785    #[test]
1786    fn format_ids_use_the_exact_open_grammar() {
1787        for id in ["matpower", "psse-raw", "doe-go-3", "x1"] {
1788            assert_eq!(FormatId::new(id).unwrap().as_str(), id);
1789        }
1790        for id in ["", "1matpower", "MATPOWER", "psse--raw", "psse-", "p_sse"] {
1791            assert!(FormatId::new(id).is_err(), "{id}");
1792        }
1793        assert!(FormatId::new("a".repeat(MAX_FORMAT_ID_BYTES + 1)).is_err());
1794    }
1795
1796    #[test]
1797    fn memory_sources_retain_arbitrary_binary_bytes_without_copying() {
1798        let bytes: Arc<[u8]> = vec![0, 255, 0, 128].into();
1799        let pointer = bytes.as_ptr();
1800        let source = Source::from_memory("input.bin", Arc::clone(&bytes))
1801            .unwrap()
1802            .with_format(FormatId::new("pwb").unwrap());
1803        let buffer = source.primary_buffer().unwrap();
1804        assert_eq!(buffer.bytes(), [0, 255, 0, 128]);
1805        assert_eq!(buffer.bytes().as_ptr(), pointer);
1806        assert_eq!(source.format().unwrap().as_str(), "pwb");
1807        assert!(Source::from_memory("", Vec::new()).is_err());
1808        assert!(Source::from_memory("x\0y", Vec::new()).is_err());
1809    }
1810
1811    #[test]
1812    fn a_bom_is_retained_and_skipped_for_the_parser_without_a_second_buffer() {
1813        let bytes: Vec<u8> = [0xEF, 0xBB, 0xBF, b'm', b'p', b'c'].to_vec();
1814        let source = Source::from_memory("case.m", bytes).unwrap();
1815        let buffer = source.primary_buffer().unwrap();
1816        assert!(buffer.has_utf8_bom());
1817        assert_eq!(buffer.bytes().len(), 6);
1818        assert_eq!(buffer.content_bytes(), b"mpc");
1819        // The parser slice points into the one retained buffer.
1820        assert_eq!(
1821            buffer.content_bytes().as_ptr(),
1822            buffer.bytes()[3..].as_ptr()
1823        );
1824
1825        let plain = Source::from_memory("case.m", b"mpc".to_vec()).unwrap();
1826        let plain = plain.primary_buffer().unwrap();
1827        assert!(!plain.has_utf8_bom());
1828        assert_eq!(plain.content_bytes(), plain.bytes());
1829    }
1830
1831    #[test]
1832    fn a_memory_source_resolves_named_buffers_and_never_the_filesystem() {
1833        let source = Source::from_memory("master.dss", b"redirect sub/feeder.dss".to_vec())
1834            .unwrap()
1835            .with_named_buffer("sub/feeder.dss", b"feeder".to_vec())
1836            .unwrap();
1837        let primary = source.primary_buffer().unwrap();
1838        let feeder = source
1839            .referenced_buffer(&primary, "sub/feeder.dss")
1840            .unwrap();
1841        assert_eq!(feeder.bytes(), b"feeder");
1842
1843        // Referrer-relative resolution: from inside `sub/`, a sibling name
1844        // resolves beneath `sub/` and `..` climbs within the supplied names.
1845        let sibling = source
1846            .referenced_buffer(&feeder, "../sub/feeder.dss")
1847            .unwrap();
1848        assert_eq!(sibling.bytes(), b"feeder");
1849
1850        assert!(source.referenced_buffer(&primary, "missing.dss").is_err());
1851        let escape = source.referenced_buffer(&primary, "../outside.dss");
1852        assert_eq!(
1853            escape.unwrap_err().category(),
1854            crate::ErrorCategory::Request
1855        );
1856    }
1857
1858    #[test]
1859    fn a_file_source_acquires_referenced_files_beneath_its_containing_directory() {
1860        let root = test_root("file-refs");
1861        std::fs::create_dir_all(root.join("sub")).unwrap();
1862        std::fs::write(root.join("master.dss"), b"master").unwrap();
1863        std::fs::write(root.join("sub/feeder.dss"), b"feeder").unwrap();
1864        std::fs::write(root.join("sub/coords.csv"), b"coords").unwrap();
1865
1866        let source = Source::open(root.join("master.dss")).unwrap();
1867        let primary = source.primary_buffer().unwrap();
1868        assert_eq!(primary.bytes(), b"master");
1869
1870        let feeder = source
1871            .referenced_buffer(&primary, "sub/feeder.dss")
1872            .unwrap();
1873        assert_eq!(feeder.bytes(), b"feeder");
1874        // Names resolve against the referring file: from the feeder, a bare
1875        // sibling name lands in `sub/`.
1876        let coords = source.referenced_buffer(&feeder, "coords.csv").unwrap();
1877        assert_eq!(coords.bytes(), b"coords");
1878        // And `..` climbs back toward the root but never past it.
1879        let master_again = source.referenced_buffer(&feeder, "../master.dss").unwrap();
1880        assert_eq!(master_again.bytes(), b"master");
1881        let escape = source.referenced_buffer(&primary, "../escape.dss");
1882        assert_eq!(
1883            escape.unwrap_err().category(),
1884            crate::ErrorCategory::Request
1885        );
1886
1887        // The same file is retained once.
1888        let again = source
1889            .referenced_buffer(&primary, "sub/feeder.dss")
1890            .unwrap();
1891        assert_eq!(again.bytes().as_ptr(), feeder.bytes().as_ptr());
1892        assert_eq!(source.acquired_buffers().len(), 4);
1893        std::fs::remove_dir_all(root).unwrap();
1894    }
1895
1896    #[test]
1897    fn an_explicitly_wider_root_admits_shared_files_and_still_confines() {
1898        let root = test_root("wider-root");
1899        std::fs::create_dir_all(root.join("cases")).unwrap();
1900        std::fs::create_dir_all(root.join("shared")).unwrap();
1901        std::fs::write(root.join("cases/master.dss"), b"master").unwrap();
1902        std::fs::write(root.join("shared/wires.dss"), b"wires").unwrap();
1903
1904        // Confined by default: the sibling directory is outside.
1905        let narrow = Source::open(root.join("cases/master.dss")).unwrap();
1906        let primary = narrow.primary_buffer().unwrap();
1907        assert!(
1908            narrow
1909                .referenced_buffer(&primary, "../shared/wires.dss")
1910                .is_err()
1911        );
1912
1913        // The wider root is selected while constructing the source.
1914        let wide = Source::open(root.join("cases/master.dss"))
1915            .unwrap()
1916            .with_acquisition_root(&root)
1917            .unwrap();
1918        let primary = wide.primary_buffer().unwrap();
1919        let wires = wide
1920            .referenced_buffer(&primary, "../shared/wires.dss")
1921            .unwrap();
1922        assert_eq!(wires.bytes(), b"wires");
1923        assert!(
1924            wide.referenced_buffer(&primary, "../../etc/passwd")
1925                .is_err()
1926        );
1927
1928        // A root that does not contain the case file is refused.
1929        let outside = test_root("wider-root-outside");
1930        std::fs::create_dir_all(&outside).unwrap();
1931        assert!(
1932            Source::open(root.join("cases/master.dss"))
1933                .unwrap()
1934                .with_acquisition_root(&outside)
1935                .is_err()
1936        );
1937        std::fs::remove_dir_all(outside).ok();
1938        std::fs::remove_dir_all(root).unwrap();
1939    }
1940
1941    #[test]
1942    fn directory_buffers_are_lazy_cached_and_binary_safe() {
1943        let root = test_root("directory");
1944        std::fs::create_dir_all(root.join("nested")).unwrap();
1945        std::fs::write(root.join("nested/data.bin"), [0, 255, 7]).unwrap();
1946        let source = Source::open(&root).unwrap();
1947        assert!(source.is_directory());
1948        assert!(source.acquired_buffers().is_empty());
1949        let name = ArtifactPath::new("nested/data.bin").unwrap();
1950        let first = source.buffer(&name).unwrap();
1951        let second = source.buffer(&name).unwrap();
1952        assert_eq!(first.bytes(), [0, 255, 7]);
1953        assert_eq!(first.bytes().as_ptr(), second.bytes().as_ptr());
1954        assert_eq!(source.acquired_buffers().len(), 1);
1955        std::fs::remove_dir_all(root).unwrap();
1956    }
1957
1958    #[cfg(unix)]
1959    #[test]
1960    fn source_acquisition_refuses_root_and_child_symlinks() {
1961        use std::os::unix::fs::symlink;
1962
1963        let root = test_root("symlink");
1964        std::fs::create_dir_all(&root).unwrap();
1965        std::fs::write(root.join("real.bin"), b"real").unwrap();
1966        symlink(root.join("real.bin"), root.join("link.bin")).unwrap();
1967        let source = Source::open(&root).unwrap();
1968        let error = source
1969            .buffer(&ArtifactPath::new("link.bin").unwrap())
1970            .unwrap_err();
1971        assert_eq!(error.category(), crate::ErrorCategory::Request);
1972
1973        // A symlinked intermediate directory is refused by the component walk.
1974        std::fs::create_dir_all(root.join("real-dir")).unwrap();
1975        std::fs::write(root.join("real-dir/inner.bin"), b"inner").unwrap();
1976        symlink(root.join("real-dir"), root.join("link-dir")).unwrap();
1977        let error = source
1978            .buffer(&ArtifactPath::new("link-dir/inner.bin").unwrap())
1979            .unwrap_err();
1980        assert_eq!(error.category(), crate::ErrorCategory::Request);
1981
1982        let root_link = root.with_extension("link");
1983        symlink(&root, &root_link).unwrap();
1984        assert!(Source::open(&root_link).is_err());
1985        std::fs::remove_file(root_link).unwrap();
1986        std::fs::remove_dir_all(root).unwrap();
1987    }
1988
1989    #[cfg(unix)]
1990    #[test]
1991    fn a_named_pipe_is_refused_promptly_and_siblings_still_acquire() {
1992        use std::os::unix::ffi::OsStrExt;
1993
1994        let root = test_root("fifo");
1995        std::fs::create_dir_all(&root).unwrap();
1996        std::fs::write(root.join("real.csv"), b"real").unwrap();
1997        let fifo = root.join("pipe.dat");
1998        let c_path = std::ffi::CString::new(fifo.as_os_str().as_bytes()).unwrap();
1999        // SAFETY: the pointer references the NUL-terminated buffer owned by
2000        // `c_path`, which outlives the call.
2001        assert_eq!(unsafe { libc::mkfifo(c_path.as_ptr(), 0o644) }, 0);
2002
2003        // No process is attached to the pipe, so a blocking open would hang;
2004        // each operation is driven from a worker with a bounded wait so a
2005        // regression fails the test rather than hanging it.
2006        let (sender, receiver) = std::sync::mpsc::channel();
2007        let opened_root = root.clone();
2008        let worker = std::thread::spawn(move || {
2009            let open_error = Source::open(opened_root.join("pipe.dat")).map(|_| ());
2010            let directory = Source::open(&opened_root).unwrap();
2011            let buffer_error = directory
2012                .buffer(&ArtifactPath::new("pipe.dat").unwrap())
2013                .map(|_| ());
2014            let sibling = directory
2015                .buffer(&ArtifactPath::new("real.csv").unwrap())
2016                .map(|buffer| buffer.bytes().to_vec());
2017            sender.send((open_error, buffer_error, sibling)).unwrap();
2018        });
2019        let (open_error, buffer_error, sibling) = receiver
2020            .recv_timeout(std::time::Duration::from_secs(10))
2021            .expect("acquisition on a writerless pipe completes promptly");
2022        worker.join().unwrap();
2023        assert_eq!(
2024            open_error.unwrap_err().category(),
2025            crate::ErrorCategory::Request
2026        );
2027        assert_eq!(
2028            buffer_error.unwrap_err().category(),
2029            crate::ErrorCategory::Request
2030        );
2031        assert_eq!(sibling.unwrap(), b"real");
2032        std::fs::remove_dir_all(root).unwrap();
2033    }
2034
2035    #[test]
2036    fn primary_limits_validate_configuration_and_bound_allocation() {
2037        use std::ffi::OsStr;
2038        assert_eq!(parse_primary_limit(None).unwrap(), DEFAULT_PRIMARY_BYTES);
2039        assert_eq!(parse_primary_limit(Some(OsStr::new("32"))).unwrap(), 32);
2040        for value in ["", "0", "-1", "+1", " 32", "3KiB", "18446744073709551615"] {
2041            assert!(parse_primary_limit(Some(OsStr::new(value))).is_err());
2042        }
2043        let root = test_root("primary-budget");
2044        std::fs::create_dir_all(&root).unwrap();
2045        let path = root.join("input");
2046        std::fs::write(&path, b"abcd").unwrap();
2047        for limit in [4, 5] {
2048            let bytes = read_open_file(
2049                std::fs::File::open(&path).unwrap(),
2050                "input",
2051                ReadBudget::Primary(limit),
2052            )
2053            .unwrap();
2054            assert_eq!(&*bytes, b"abcd");
2055        }
2056        assert!(
2057            read_open_file(
2058                std::fs::File::open(&path).unwrap(),
2059                "input",
2060                ReadBudget::Primary(3)
2061            )
2062            .is_err()
2063        );
2064        std::fs::File::create(&path)
2065            .unwrap()
2066            .set_len(DEFAULT_PRIMARY_BYTES * 4)
2067            .unwrap();
2068        let (error, allocated) = measured_bytes(|| {
2069            read_open_file(
2070                std::fs::File::open(&path).unwrap(),
2071                "input",
2072                ReadBudget::Primary(DEFAULT_PRIMARY_BYTES),
2073            )
2074            .unwrap_err()
2075        });
2076        assert!(error.to_string().contains("primary source"));
2077        assert!(allocated < 4096);
2078        std::fs::remove_dir_all(root).unwrap();
2079    }
2080
2081    #[test]
2082    fn an_over_budget_referenced_file_is_refused_before_allocation() {
2083        let root = test_root("budget");
2084        std::fs::create_dir_all(&root).unwrap();
2085        std::fs::write(root.join("master.dss"), b"master").unwrap();
2086        // A sparse file whose declared length far exceeds the acquisition
2087        // budget; no bytes are written, so the refusal must come from the
2088        // declared length, before any reservation.
2089        let big = std::fs::File::create(root.join("big.dat")).unwrap();
2090        big.set_len(MAX_REFERENCED_BYTES * 4).unwrap();
2091        drop(big);
2092
2093        let source = Source::open(root.join("master.dss")).unwrap();
2094        let primary = source.primary_buffer().unwrap();
2095        // The refusal happens before the reserve: the bytes allocated on this
2096        // thread during the refused acquisition are a tiny fraction of the
2097        // declared length. The measurement is thread scoped, so it fails when
2098        // the pre-reserve refusal is removed and never passes by accident.
2099        let (error, allocated) =
2100            measured_bytes(|| source.referenced_buffer(&primary, "big.dat").unwrap_err());
2101        assert!(error.to_string().contains("acquisition budget"), "{error}");
2102        assert!(
2103            (allocated as u64) < MAX_REFERENCED_BYTES / 16,
2104            "the refused acquisition allocated {allocated} bytes"
2105        );
2106        std::fs::remove_dir_all(root).unwrap();
2107    }
2108
2109    #[cfg(unix)]
2110    #[test]
2111    fn racing_entry_listing_never_names_files_outside_the_root() {
2112        use std::os::unix::fs::symlink;
2113
2114        let root = test_root("race-list");
2115        std::fs::create_dir_all(root.join("sub")).unwrap();
2116        std::fs::write(root.join("sub/inside.txt"), b"inside").unwrap();
2117        let outside = test_root("race-list-outside");
2118        std::fs::create_dir_all(&outside).unwrap();
2119        std::fs::write(outside.join("outside-only.txt"), b"outside").unwrap();
2120
2121        let source = Source::open(&root).unwrap();
2122        let stop = std::sync::atomic::AtomicBool::new(false);
2123        std::thread::scope(|scope| {
2124            let flipper = scope.spawn(|| {
2125                while !stop.load(std::sync::atomic::Ordering::Relaxed) {
2126                    let _ = std::fs::remove_dir_all(root.join("sub"));
2127                    let _ = symlink(&outside, root.join("sub"));
2128                    let _ = std::fs::remove_file(root.join("sub"));
2129                    let _ = std::fs::create_dir(root.join("sub"));
2130                    let _ = std::fs::write(root.join("sub/inside.txt"), b"inside");
2131                }
2132            });
2133            for _ in 0..50 {
2134                // Either the real subtree lists, or the walk fails; a name
2135                // that exists only outside the root never appears.
2136                if let Ok(names) = source.entry_names() {
2137                    assert!(
2138                        names
2139                            .iter()
2140                            .all(|name| !name.as_str().contains("outside-only")),
2141                        "{names:?}"
2142                    );
2143                }
2144            }
2145            stop.store(true, std::sync::atomic::Ordering::Relaxed);
2146            flipper.join().unwrap();
2147        });
2148        std::fs::remove_dir_all(&root).ok();
2149        std::fs::remove_dir_all(&outside).ok();
2150    }
2151
2152    #[test]
2153    fn referenced_names_must_be_portable_relative_paths() {
2154        let root = test_root("portable-names");
2155        std::fs::create_dir_all(root.join("sub")).unwrap();
2156        std::fs::write(root.join("master.dss"), b"master").unwrap();
2157        std::fs::write(root.join("sub/feeder.dss"), b"feeder").unwrap();
2158        let source = Source::open(root.join("master.dss")).unwrap();
2159        let primary = source.primary_buffer().unwrap();
2160
2161        // A climb spelled with the platform's alternate separator is the same
2162        // climb; a leading separator and a drive spelling are refused.
2163        for name in ["..\\escape.dss", "\\escape.dss", "C:\\escape.dss", "C:x"] {
2164            let error = source.referenced_buffer(&primary, name).unwrap_err();
2165            assert_eq!(
2166                error.category(),
2167                crate::ErrorCategory::Request,
2168                "{name}: {error}"
2169            );
2170        }
2171        assert!(source.root_buffer("..\\master.dss").is_err());
2172        assert!(source.root_buffer("\\master.dss").is_err());
2173
2174        // Ordinary relative names keep resolving.
2175        let feeder = source
2176            .referenced_buffer(&primary, "sub/feeder.dss")
2177            .unwrap();
2178        assert_eq!(feeder.bytes(), b"feeder");
2179
2180        // An absolute in-root name resolves to one segment per directory
2181        // component: it lands on the same cached buffer the relative name
2182        // produced, proving the per component walk ran on the same key. The
2183        // acquisition root is the canonical containing directory, so the
2184        // absolute spelling is canonical too.
2185        let absolute = root.canonicalize().unwrap().join("sub").join("feeder.dss");
2186        let again = source
2187            .referenced_buffer(&primary, absolute.to_str().unwrap())
2188            .unwrap();
2189        assert_eq!(again.bytes().as_ptr(), feeder.bytes().as_ptr());
2190        // No acquisition returned bytes from outside the root.
2191        assert!(
2192            source
2193                .acquired_buffers()
2194                .iter()
2195                .all(|buffer| !buffer.name().contains("escape"))
2196        );
2197        std::fs::remove_dir_all(root).unwrap();
2198    }
2199
2200    #[cfg(unix)]
2201    #[test]
2202    fn live_sources_hold_no_directory_descriptors_before_acquisition() {
2203        fn open_descriptor_count() -> usize {
2204            let table = if cfg!(target_os = "macos") {
2205                "/dev/fd"
2206            } else {
2207                "/proc/self/fd"
2208            };
2209            std::fs::read_dir(table).unwrap().count()
2210        }
2211
2212        let _guard = process_resource_guard();
2213        let root = test_root("fd-count");
2214        std::fs::create_dir_all(&root).unwrap();
2215        std::fs::write(root.join("case.m"), b"case").unwrap();
2216        std::fs::write(root.join("ref.csv"), b"ref").unwrap();
2217
2218        let before = open_descriptor_count();
2219        let sources: Vec<Source> = (0..300)
2220            .map(|_| Source::open(root.join("case.m")).unwrap())
2221            .collect();
2222        let held = open_descriptor_count();
2223        assert!(
2224            held <= before + 4,
2225            "{} sources hold {} descriptors over the baseline {}",
2226            sources.len(),
2227            held - before,
2228            before
2229        );
2230
2231        // Sources still acquire afterwards, and one source's two acquisitions
2232        // resolve through the same pinned root to one buffer. Acquisition
2233        // pins one descriptor per source, so this runs on a subset that stays
2234        // under the default descriptor limit.
2235        for source in sources.iter().take(32) {
2236            let primary = source.primary_buffer().unwrap();
2237            let first = source.referenced_buffer(&primary, "ref.csv").unwrap();
2238            let second = source.referenced_buffer(&primary, "ref.csv").unwrap();
2239            assert_eq!(first.bytes().as_ptr(), second.bytes().as_ptr());
2240        }
2241        drop(sources);
2242        std::fs::remove_dir_all(root).unwrap();
2243    }
2244
2245    #[test]
2246    fn entry_listing_returns_names_of_every_length_exactly() {
2247        // Guard on the entry-name read: names are read to their terminator,
2248        // whatever length the entry actually occupies.
2249        let root = test_root("name-lengths");
2250        std::fs::create_dir_all(&root).unwrap();
2251        let long = "n".repeat(200);
2252        for name in ["a", "medium-name.csv", long.as_str()] {
2253            std::fs::write(root.join(name), b"x").unwrap();
2254        }
2255        let source = Source::open(&root).unwrap();
2256        let mut names: Vec<String> = source
2257            .entry_names()
2258            .unwrap()
2259            .iter()
2260            .map(|name| name.as_str().to_owned())
2261            .collect();
2262        names.sort();
2263        let mut expected = vec!["a".to_owned(), "medium-name.csv".to_owned(), long];
2264        expected.sort();
2265        assert_eq!(names, expected);
2266        std::fs::remove_dir_all(root).unwrap();
2267    }
2268
2269    #[cfg(unix)]
2270    #[test]
2271    fn a_directory_nested_past_the_depth_bound_is_refused_promptly() {
2272        use std::os::fd::{AsRawFd, FromRawFd};
2273
2274        // The unwind below holds one descriptor per level; serialize with the
2275        // other descriptor sensitive tests so their measurements stay exact.
2276        let _guard = process_resource_guard();
2277
2278        let root = test_root("deep-chain");
2279        std::fs::create_dir_all(&root).unwrap();
2280        // Build the chain by relative creation from inside each level, so the
2281        // tree reaches past any absolute path length limit.
2282        let name = std::ffi::CString::new("d").unwrap();
2283        let mut level = std::fs::File::open(&root).unwrap();
2284        for _ in 0..(MAX_REFERENCED_DEPTH + 40) {
2285            // SAFETY: `level` owns a live directory descriptor for both
2286            // calls, and the pointer references the NUL-terminated buffer
2287            // owned by `name`.
2288            unsafe {
2289                assert_eq!(libc::mkdirat(level.as_raw_fd(), name.as_ptr(), 0o755), 0);
2290                let fd = libc::openat(
2291                    level.as_raw_fd(),
2292                    name.as_ptr(),
2293                    libc::O_RDONLY | libc::O_CLOEXEC,
2294                );
2295                assert!(fd >= 0);
2296                level = std::fs::File::from_raw_fd(fd);
2297            }
2298        }
2299        drop(level);
2300
2301        let (sender, receiver) = std::sync::mpsc::channel();
2302        let listed_root = root.clone();
2303        let worker = std::thread::spawn(move || {
2304            let source = Source::open(&listed_root).unwrap();
2305            sender.send(source.entry_names().map(|_| ())).unwrap();
2306        });
2307        let outcome = receiver
2308            .recv_timeout(std::time::Duration::from_secs(10))
2309            .expect("the depth refusal returns promptly");
2310        worker.join().unwrap();
2311        let error = outcome.expect_err("a chain past the depth bound refuses");
2312        assert!(error.to_string().contains("levels deep"), "{error}");
2313
2314        // The chain is deeper than remove_dir_all's own recursion budget on
2315        // some platforms; unwind it level by level with the same descriptors.
2316        let mut fds = vec![std::fs::File::open(&root).unwrap()];
2317        loop {
2318            let last = fds.last().unwrap();
2319            // SAFETY: as above.
2320            let fd = unsafe {
2321                libc::openat(
2322                    last.as_raw_fd(),
2323                    name.as_ptr(),
2324                    libc::O_RDONLY | libc::O_CLOEXEC,
2325                )
2326            };
2327            if fd < 0 {
2328                break;
2329            }
2330            // SAFETY: `fd` is a freshly opened descriptor.
2331            fds.push(unsafe { std::fs::File::from_raw_fd(fd) });
2332        }
2333        while fds.len() > 1 {
2334            let parent = &fds[fds.len() - 2];
2335            // SAFETY: as above; AT_REMOVEDIR removes the empty directory.
2336            unsafe {
2337                libc::unlinkat(parent.as_raw_fd(), name.as_ptr(), libc::AT_REMOVEDIR);
2338            }
2339            fds.pop();
2340        }
2341        drop(fds);
2342        std::fs::remove_dir_all(&root).unwrap();
2343    }
2344
2345    #[test]
2346    fn a_directory_past_the_entry_budget_is_refused_with_bounded_memory() {
2347        let root = test_root("entry-budget");
2348        std::fs::create_dir_all(&root).unwrap();
2349        // Four times the budget: an unbounded read would materialize four
2350        // times the names the allowance admits, which the thread scoped
2351        // allocation measurement separates decisively from the bounded read.
2352        let excess = MAX_REFERENCED_FILES * 4;
2353        for index in 0..excess {
2354            std::fs::write(root.join(format!("f{index:05}.csv")), b"").unwrap();
2355        }
2356        let source = Source::open(&root).unwrap();
2357        let (error, allocated) = measured_bytes(|| source.entry_names().unwrap_err());
2358        assert!(error.to_string().contains("entries"), "{error}");
2359        // The listing stopped reading at the allowance: the bytes allocated
2360        // on this thread are bounded by the entry budget, never by the
2361        // directory's true entry count. Removing the in-loop bound reads all
2362        // `excess` names and fails this assertion.
2363        assert!(
2364            allocated < MAX_REFERENCED_FILES * 192,
2365            "the refused listing allocated {allocated} bytes"
2366        );
2367        std::fs::remove_dir_all(root).unwrap();
2368    }
2369
2370    #[cfg(unix)]
2371    #[test]
2372    fn listing_breadth_never_scales_open_descriptors() {
2373        fn open_descriptor_count() -> usize {
2374            let table = if cfg!(target_os = "macos") {
2375                "/dev/fd"
2376            } else {
2377                "/proc/self/fd"
2378            };
2379            std::fs::read_dir(table).unwrap().count()
2380        }
2381
2382        let _guard = process_resource_guard();
2383        let root = test_root("breadth");
2384        // Far more immediate subdirectories than the lowered descriptor
2385        // limit, each holding one file, plus one nested chain, so the walk
2386        // proves its held descriptors follow depth rather than breadth.
2387        let breadth = 400usize;
2388        for index in 0..breadth {
2389            let sub = root.join(format!("s{index:03}"));
2390            std::fs::create_dir_all(&sub).unwrap();
2391            std::fs::write(sub.join("data.csv"), b"x").unwrap();
2392        }
2393        std::fs::create_dir_all(root.join("nested/a/b/c")).unwrap();
2394        std::fs::write(root.join("nested/a/b/c/deep.csv"), b"x").unwrap();
2395
2396        // Lower the descriptor soft limit for the duration, so a walk whose
2397        // descriptor use scales with breadth fails here rather than passing
2398        // on a machine with a raised limit.
2399        // SAFETY: `getrlimit` fills the zeroed out-parameter; the lowered
2400        // limit is restored below.
2401        let mut original: libc::rlimit = unsafe { std::mem::zeroed() };
2402        assert_eq!(
2403            // SAFETY: as above.
2404            unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &raw mut original) },
2405            0
2406        );
2407        let lowered = libc::rlimit {
2408            rlim_cur: 256,
2409            rlim_max: original.rlim_max,
2410        };
2411        // SAFETY: lowering the soft limit for this process; restored below.
2412        assert_eq!(
2413            unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &raw const lowered) },
2414            0
2415        );
2416
2417        let baseline = open_descriptor_count();
2418        let peak = std::sync::atomic::AtomicUsize::new(0);
2419        let stop = std::sync::atomic::AtomicBool::new(false);
2420        let names = std::thread::scope(|scope| {
2421            let sampler = scope.spawn(|| {
2422                while !stop.load(std::sync::atomic::Ordering::Relaxed) {
2423                    let count = open_descriptor_count();
2424                    peak.fetch_max(count, std::sync::atomic::Ordering::Relaxed);
2425                }
2426            });
2427            let source = Source::open(&root).unwrap();
2428            let names = source.entry_names().unwrap();
2429            stop.store(true, std::sync::atomic::Ordering::Relaxed);
2430            sampler.join().unwrap();
2431            drop(source);
2432            names
2433        });
2434        // SAFETY: restoring the limit read above.
2435        assert_eq!(
2436            unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &raw const original) },
2437            0
2438        );
2439
2440        assert_eq!(names.len(), breadth + 1, "every file listed");
2441        let sampled_peak = peak.load(std::sync::atomic::Ordering::Relaxed);
2442        assert!(
2443            sampled_peak <= baseline + MAX_REFERENCED_DEPTH + 16,
2444            "the walk held {sampled_peak} descriptors over a baseline of {baseline}"
2445        );
2446        std::fs::remove_dir_all(root).unwrap();
2447    }
2448
2449    #[test]
2450    fn a_directory_listing_still_names_windows_reserved_spellings() {
2451        // The output predicate refusing reserved device stems must not narrow
2452        // what a source directory can list.
2453        let root = test_root("reserved-listing");
2454        std::fs::create_dir_all(&root).unwrap();
2455        std::fs::write(root.join("aux.dss"), b"content").unwrap();
2456        let source = Source::open(&root).unwrap();
2457        let names = source.entry_names().unwrap();
2458        assert_eq!(names.len(), 1);
2459        assert_eq!(names[0].as_str(), "aux.dss");
2460        std::fs::remove_dir_all(root).unwrap();
2461    }
2462
2463    #[test]
2464    fn a_directory_listing_repeats_and_survives_acquisition() {
2465        // The walk runs once and its result is the source's one immutable
2466        // listing: a second call between or after buffer acquisitions
2467        // returns the same names rather than an empty second walk from a
2468        // directory stream a platform shares across duplicated descriptors.
2469        let root = test_root("repeat-listing");
2470        std::fs::create_dir_all(&root).unwrap();
2471        std::fs::write(root.join("network.csv"), b"name\nseq\n").unwrap();
2472        std::fs::write(root.join("buses.csv"), b"name\nB1\n").unwrap();
2473        let source = Source::open(&root).unwrap();
2474        let first = source.entry_names().unwrap();
2475        assert_eq!(first.len(), 2);
2476        let name = ArtifactPath::new("network.csv").unwrap();
2477        source.buffer(&name).unwrap();
2478        let second = source.entry_names().unwrap();
2479        assert_eq!(first, second);
2480        std::fs::remove_dir_all(root).unwrap();
2481    }
2482
2483    #[test]
2484    fn concurrent_acquisition_retains_one_buffer_for_one_name() {
2485        let root = test_root("concurrent");
2486        std::fs::create_dir_all(&root).unwrap();
2487        std::fs::write(root.join("shared.csv"), b"shared").unwrap();
2488        let source = Source::open(&root).unwrap();
2489        let name = ArtifactPath::new("shared.csv").unwrap();
2490        let buffers: Vec<_> = std::thread::scope(|scope| {
2491            (0..8)
2492                .map(|_| {
2493                    let source = source.clone();
2494                    let name = name.clone();
2495                    scope.spawn(move || source.buffer(&name).unwrap())
2496                })
2497                .collect::<Vec<_>>()
2498                .into_iter()
2499                .map(|handle| handle.join().unwrap())
2500                .collect()
2501        });
2502        let pointer = buffers[0].bytes().as_ptr();
2503        assert!(
2504            buffers
2505                .iter()
2506                .all(|buffer| buffer.bytes().as_ptr() == pointer)
2507        );
2508        assert_eq!(source.acquired_buffers().len(), 1);
2509        std::fs::remove_dir_all(root).unwrap();
2510    }
2511    #[cfg(unix)]
2512    #[test]
2513    fn a_refused_listing_never_shortens_the_next_one() {
2514        // A walk that stops early (the entry budget) must not leave the next
2515        // walk a partially consumed directory stream: every retry reads the
2516        // whole directory again and refuses the same way.
2517        let root = test_root("refused-listing");
2518        std::fs::create_dir_all(&root).unwrap();
2519        for index in 0..(MAX_REFERENCED_FILES + 5) {
2520            std::fs::write(root.join(format!("f{index}.txt")), b"x").unwrap();
2521        }
2522        let source = Source::open(&root).unwrap();
2523        let first = source.entry_names().unwrap_err();
2524        let second = source.entry_names().unwrap_err();
2525        assert_eq!(
2526            first.diagnostics().first().map(|d| d.code().to_owned()),
2527            second.diagnostics().first().map(|d| d.code().to_owned()),
2528            "the refusal repeats rather than shrinking into a partial listing"
2529        );
2530        drop(source);
2531        let _ = std::fs::remove_dir_all(&root);
2532    }
2533}