OpenDAL Read Validation (CompleteLayer / CompleteReader)#
Overview#
OpenDAL enforces exact-size read guarantees on bounded range reads through CompleteLayer and its inner CompleteReader wrapper. When a caller requests a specific byte range (e.g., bytes=1024-2047), OpenDAL validates that the underlying storage backend returned exactly the requested number of bytes β no more, no less. Short reads that stop before reaching the requested size (e.g., due to a truncated file) are treated as errors, not silent data loss.
CompleteLayer is an internal, mandatory layer β not a user-configurable option. Every Operator automatically has it applied once and only once during construction.
How It's Applied#
OperatorBuilder::new() wires up three mandatory layers in order: ErrorContextLayer, CompleteLayer, and CorrectnessCheckLayer. CompleteLayer is never exposed to external users and cannot be bypassed.
When the Operator calls read(), CompleteAccessor extracts the size from OpRead's BytesRange (the Option<u64> second field), then wraps the backend reader in a CompleteReader initialized with that size.
BytesRange and Bounded vs. Unbounded Reads#
BytesRange is a (offset: u64, size: Option<u64>) tuple:
- Bounded β
size = Some(n): read exactlynbytes starting atoffset. Maps to HTTPRange: bytes=<offset>-<offset+n-1>. - Unbounded β
size = None: read to end of content. Maps toRange: bytes=<offset>-.
CompleteReader only enforces size validation when size is Some. An unbounded read (size = None) has no byte-count check.
CompleteReader: The Validation Mechanism#
CompleteReader<R> wraps the inner reader and carries three fields: the inner reader R, an optional size: Option<u64> (the expected byte count), and a running read: u64 counter.
The read loop accumulates bytes into self.read. When the inner reader returns an empty buffer (EOF signal), check() is called immediately:
self.read vs size | Outcome |
|---|---|
| Equal | Ok(()) β success |
Less than size | Err(ErrorKind::Unexpected, "reader got too little data") |
Greater than size | Err(ErrorKind::Unexpected, "reader got too much data") |
Both "too little" and "too much" errors include expect and actual context fields for diagnostics.
CompleteWriter: Analogous Write Validation#
CompleteWriter applies the same pattern on writes: it tracks bytes written via self.size, then on close() calls check(content_length) against the content_length reported by the underlying writer. Append-mode and zero-length writes skip this check. In debug builds, dropping an unclosed writer emits a log::warn!.
Other Completions in CompleteLayer#
Read/write size validation is one responsibility of CompleteLayer. The same layer also:
- Stat Completion β simulates
staton directories vialistwhen the backend lacks native dir-stat support. - List Completion β wraps listers with
FlatListerorPrefixListerto emulatelist_with_recursiveand prefix filtering for backends that don't support them natively. create_dirCompletion β synthesizescreate_dirvia an empty write when the backend supportswrite_can_emptyandlist.
Key Source Files#
| File | Purpose |
|---|---|
core/src/layers/complete.rs | CompleteLayer, CompleteAccessor, CompleteReader, CompleteWriter |
core/src/types/operator/builder.rs | OperatorBuilder::new() β where CompleteLayer is applied |
core/src/raw/http_util/bytes_range.rs | BytesRange β bounded/unbounded range representation |
core/src/raw/ops.rs | OpRead β carries BytesRange into the read call |