pub struct TaildropStore { /* private fields */ }Expand description
A Taildrop file store rooted at a fixed directory. All operations are confined to this root by
joining only validate_base_name-validated names.
Implementations§
Source§impl TaildropStore
impl TaildropStore
Sourcepub fn new(root: impl Into<PathBuf>) -> Result<Self, TaildropError>
pub fn new(root: impl Into<PathBuf>) -> Result<Self, TaildropError>
Create a store rooted at root, creating the directory (and parents) if needed.
Sourcepub fn reap_abandoned_partials(
&self,
now: SystemTime,
delete_delay: Duration,
) -> usize
pub fn reap_abandoned_partials( &self, now: SystemTime, delete_delay: Duration, ) -> usize
Reap abandoned .partial files: delete every <base>.partial in the store root whose last
modification is older than delete_delay relative to now AND whose base name has no
in-flight transfer. Returns the number deleted. Mirrors Go feature/taildrop/delete.go’s
fileDeleter, which GCs a partial deleteDelay (1h) after it was last touched, sparing one
that an active put is still writing.
This fork has no per-file timer queue (the store is a passive Arc, not an actor); instead a
periodic background sweep — see spawn_partial_reaper — calls this. The two Go cancellation
signals are both honored: an active transfer’s base name is in in_flight (skipped here,
the analog of Go’s “no active put” check), and a resumed transfer advances the partial’s
mtime on every write (so a partial resumed within the window looks recent and is spared). A
permanently-abandoned partial is neither, so it ages out and is deleted, reclaiming the disk
and clearing the stale-partial 409 that would otherwise block an offset == 0 re-send of
the same name forever.
now and delete_delay are parameters (not read from the clock here) so the reap logic is
deterministically testable. A partial whose mtime is unreadable or in the future is treated as
fresh (kept) — fail-safe toward never deleting a file that might still be live.
Sourcepub async fn put_file<R>(
&self,
name: &str,
reader: R,
offset: u64,
expected_len: u64,
) -> Result<u64, TaildropError>
pub async fn put_file<R>( &self, name: &str, reader: R, offset: u64, expected_len: u64, ) -> Result<u64, TaildropError>
Receive a file named name from reader, writing to <name>.partial then atomically
renaming to a non-clobbering final name on success. Mirrors Go manager.PutFile.
offset lets a resumed transfer append past already-written bytes (the partial is opened, the
write starts at offset, and any bytes already on disk past offset are truncated away).
expected_len is the declared total length of the completed file (the request’s
Content-Length plus offset); the transfer is finalized only if exactly that many bytes are
present. Returns the total number of bytes in the completed file.
Fail-closed: an invalid name is rejected before any path is built; an in-progress partial for
the same name yields TaildropError::FileExists; an out-of-range resume offset (past the
current partial length) is rejected; an I/O error mid-transfer — or a body that ends before
expected_len (a short/interrupted stream) — leaves the .partial on disk and the final name
is never created. This matches Go feature/taildrop/send.go, which errors when the copied
length does not equal the declared length rather than publishing a truncated file.
The retained .partial is resumable only by a peer that issues a ranged retry (an offset > 0
PUT); a sender that always restarts at offset == 0 will instead hit the in-progress-conflict
path (TaildropError::FileExists) until the stale partial is cleared. A permanently-abandoned
partial is reclaimed by the background reaper after DELETE_DELAY (Go’s fileDeleter
equivalent — see reap_abandoned_partials /
spawn_partial_reaper), which also clears that offset == 0 conflict once the stale partial
ages out.
Sourcepub fn waiting_files(&self) -> Result<Vec<WaitingFile>, TaildropError>
pub fn waiting_files(&self) -> Result<Vec<WaitingFile>, TaildropError>
List fully-received (non-partial) files, sorted by name (Go WaitingFiles).
Sourcepub fn delete_file(&self, name: &str) -> Result<(), TaildropError>
pub fn delete_file(&self, name: &str) -> Result<(), TaildropError>
Delete a fully-received file by base name (Go DeleteFile). The name is validated first, so a
traversal attempt can never escape the store root, and a symlink at the target is refused (Go
O_NOFOLLOW intent) rather than followed — a planted root/foo.txt -> /etc/passwd must not
let a delete foo.txt remove the link’s target.
Sourcepub fn open_file(&self, name: &str) -> Result<(File, u64), TaildropError>
pub fn open_file(&self, name: &str) -> Result<(File, u64), TaildropError>
Open a fully-received file by base name for reading, returning the handle and its size (Go
OpenFile). The name is validated first, and a symlink at the target is refused (Go
O_NOFOLLOW intent) so a planted symlink cannot redirect the read to an arbitrary file.
Trait Implementations§
Source§impl Clone for TaildropStore
impl Clone for TaildropStore
Source§fn clone(&self) -> TaildropStore
fn clone(&self) -> TaildropStore
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for TaildropStore
impl RefUnwindSafe for TaildropStore
impl Send for TaildropStore
impl Sync for TaildropStore
impl Unpin for TaildropStore
impl UnsafeUnpin for TaildropStore
impl UnwindSafe for TaildropStore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<A, T> DynMessage<A> for T
impl<A, T> DynMessage<A> for T
Source§fn handle_dyn<'a>(
self: Box<T>,
state: &'a mut A,
actor_ref: ActorRef<A>,
tx: Option<Sender<Result<Box<dyn Any + Send>, SendError<Box<dyn Any + Send>, Box<dyn Any + Send>>>>>,
stop: &'a mut bool,
) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn ReplyError>>> + Send + 'a>>
fn handle_dyn<'a>( self: Box<T>, state: &'a mut A, actor_ref: ActorRef<A>, tx: Option<Sender<Result<Box<dyn Any + Send>, SendError<Box<dyn Any + Send>, Box<dyn Any + Send>>>>>, stop: &'a mut bool, ) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn ReplyError>>> + Send + 'a>>
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more