33
44//! [`Backend`] for filesystems supported by [`super::resolver`]
55
6+ use alloc:: boxed:: Box ;
67use alloc:: vec:: Vec ;
8+ use core:: any:: { Any , TypeId } ;
9+ use core:: marker:: PhantomData ;
10+
11+ use crate :: utilities:: anymap:: AnyCloneSendSync ;
712
813use super :: errors:: {
914 ChmodError , ChownError , FileStatusError , MkdirError , OpenError , ReadDirError , ReadError ,
@@ -38,18 +43,9 @@ pub(super) mod private {
3843}
3944
4045/// A backend that can be used to support a (full or subset of) a LiteBox filesystem.
41- pub trait Backend : private:: Sealed + Send + Sync + ' static {
42- /// Supporting walk through the backend
43- type WalkingDirHandle < ' a > : ' a ;
44-
45- /// An owned handle to an open file
46- type FileHandle : Clone + Send + Sync + ' static ;
47-
48- /// An owned handle to an open directory
49- type DirHandle : Clone + Send + Sync + ' static ;
50-
46+ pub trait Backend : private:: Sealed + Send + Sync + Any {
5147 /// Obtain access to the root directory of the backend.
52- fn root ( & self ) -> Self :: WalkingDirHandle < ' _ > ;
48+ fn root ( & self ) -> WalkingDirHandle < ' _ > ;
5349
5450 /// Walk one or more `components` starting from the `from` handle.
5551 ///
@@ -60,12 +56,12 @@ pub trait Backend: private::Sealed + Send + Sync + 'static {
6056 /// `WalkStopReason::StoppedAtNonDirectory`.
6157 fn walk_directories < ' a > (
6258 & ' a self ,
63- from : Self :: WalkingDirHandle < ' a > ,
59+ from : WalkingDirHandle < ' a > ,
6460 components : & [ & str ] ,
65- ) -> Result < WalkOutcome < Self :: WalkingDirHandle < ' a > > , WalkError > ;
61+ ) -> Result < WalkOutcome < WalkingDirHandle < ' a > > , WalkError > ;
6662
6763 /// Take an owned handle to a `dir` found via a walk.
68- fn owned_dir_at ( & self , dir : Self :: WalkingDirHandle < ' _ > ) -> Self :: DirHandle ;
64+ fn owned_dir_at ( & self , dir : WalkingDirHandle < ' _ > ) -> DirHandle ;
6965
7066 /// Obtain a walking handle to an existing owned dir.
7167 ///
@@ -74,28 +70,27 @@ pub trait Backend: private::Sealed + Send + Sync + 'static {
7470 ///
7571 /// XXX(jayb): We will likely migrate away from `Option` here when we do a bit of an overhaul of
7672 /// the `errors` module in order to more consistently support stale errors everywhere.
77- fn walking_dir_at < ' a > ( & ' a self , dir : & Self :: DirHandle ) -> Option < Self :: WalkingDirHandle < ' a > > ;
73+ fn walking_dir_at < ' a > ( & ' a self , dir : & DirHandle ) -> Option < WalkingDirHandle < ' a > > ;
7874
7975 /// Open an (existing) file at `dir`.
8076 ///
8177 /// To create a file, you need [`Self::create_file_at`].
8278 fn open_file_at (
8379 & self ,
84- dir : Self :: WalkingDirHandle < ' _ > ,
80+ dir : WalkingDirHandle < ' _ > ,
8581 name : & str ,
8682 flags : OFlags ,
87- ) -> Result < Permissioned < Self :: FileHandle > , OpenError > ;
83+ ) -> Result < Permissioned < FileHandle > , OpenError > ;
8884
8985 /// Read directory entries at `dir`.
90- fn list_dir_at ( & self , handle : Self :: DirHandle ) -> Result < Vec < DirEntry > , ReadDirError > ;
86+ fn list_dir_at ( & self , handle : DirHandle ) -> Result < Vec < DirEntry > , ReadDirError > ;
9187
9288 /// Read at `offset` into `buf`, returning the number of bytes read.
9389 ///
9490 /// Backends do not have an internal notion of offsets; instead the resolver maintains offsets
9591 /// as needed. For files with non-position-based [`SeekBehavior`], such as `stdin`, the resolver
9692 /// passes zero and the backend should ignore the offset.
97- fn read ( & self , h : & Self :: FileHandle , buf : & mut [ u8 ] , offset : usize )
98- -> Result < usize , ReadError > ;
93+ fn read ( & self , h : & FileHandle , buf : & mut [ u8 ] , offset : usize ) -> Result < usize , ReadError > ;
9994
10095 /// Optional performance hook: get static backing data for a file, if available and supported.
10196 ///
@@ -104,7 +99,7 @@ pub trait Backend: private::Sealed + Send + Sync + 'static {
10499 ///
105100 /// Returns `None` if indicating no static backing data is available/supported.
106101 #[ expect( unused_variables, reason = "default body, non-underscored param names" ) ]
107- fn get_static_backing_data ( & self , h : & Self :: FileHandle ) -> Option < & ' static [ u8 ] > {
102+ fn get_static_backing_data ( & self , h : & FileHandle ) -> Option < & ' static [ u8 ] > {
108103 None
109104 }
110105
@@ -116,59 +111,156 @@ pub trait Backend: private::Sealed + Send + Sync + 'static {
116111 // it ugly on the interface side here. It would be very ugly for us to pass in extra flags, or
117112 // indeed even need to maintain/handle seeking on every backend; mostly we need some sort of
118113 // nicer locking discipline, but I don't want to block the MVP for this just yet.
119- fn write ( & self , h : & Self :: FileHandle , buf : & [ u8 ] , offset : usize ) -> Result < usize , WriteError > ;
114+ fn write ( & self , h : & FileHandle , buf : & [ u8 ] , offset : usize ) -> Result < usize , WriteError > ;
120115
121116 /// Truncate the file to the specified length.
122117 ///
123118 /// If shorter than existing size, extra data is lost. If longer than existing size, resize by
124119 /// adding `\0`s.
125- fn truncate ( & self , h : & Self :: FileHandle , length : usize ) -> Result < ( ) , TruncateError > ;
120+ fn truncate ( & self , h : & FileHandle , length : usize ) -> Result < ( ) , TruncateError > ;
126121
127122 /// Describe seek behavior for an open file handle.
128- fn seek_behavior ( & self , h : & Self :: FileHandle ) -> SeekBehavior ;
123+ fn seek_behavior ( & self , h : & FileHandle ) -> SeekBehavior ;
129124
130125 /// Status of an open file handle.
131- fn file_status ( & self , h : & Self :: FileHandle ) -> Result < FileStatus , FileStatusError > ;
126+ fn file_status ( & self , h : & FileHandle ) -> Result < FileStatus , FileStatusError > ;
132127
133128 /// Status of an open directory handle.
134- fn dir_status ( & self , h : & Self :: DirHandle ) -> Result < FileStatus , FileStatusError > ;
129+ fn dir_status ( & self , h : & DirHandle ) -> Result < FileStatus , FileStatusError > ;
135130
136131 /// Create a new file at `parent` with the given `name` and `mode`.
137132 fn create_file_at (
138133 & self ,
139- dir : Self :: DirHandle ,
134+ dir : DirHandle ,
140135 name : & str ,
141136 mode : Mode ,
142- ) -> Result < Self :: FileHandle , OpenError > ;
137+ ) -> Result < FileHandle , OpenError > ;
143138
144139 /// Create a new directory at `parent` with the given `name` and `mode`.
145- fn mkdir_at (
146- & self ,
147- dir : Self :: DirHandle ,
148- name : & str ,
149- mode : Mode ,
150- ) -> Result < Self :: DirHandle , MkdirError > ;
140+ fn mkdir_at ( & self , dir : DirHandle , name : & str , mode : Mode ) -> Result < DirHandle , MkdirError > ;
151141
152142 /// Remove the file `name` at `parent`.
153- fn unlink_at ( & self , dir : Self :: DirHandle , name : & str ) -> Result < ( ) , UnlinkError > ;
143+ fn unlink_at ( & self , dir : DirHandle , name : & str ) -> Result < ( ) , UnlinkError > ;
154144
155145 /// Remove the directory `name` at `parent`.
156146 // XXX(jayb): I don't like that unlink and rmdir exist separately, we should probably merge them.
157- fn rmdir_at ( & self , dir : Self :: DirHandle , name : & str ) -> Result < ( ) , RmdirError > ;
147+ fn rmdir_at ( & self , dir : DirHandle , name : & str ) -> Result < ( ) , RmdirError > ;
158148
159149 /// Update the permissions for the file/dir `name` at `parent`.
160- fn chmod_at ( & self , dir : Self :: DirHandle , name : & str , mode : Mode ) -> Result < ( ) , ChmodError > ;
150+ fn chmod_at ( & self , dir : DirHandle , name : & str , mode : Mode ) -> Result < ( ) , ChmodError > ;
161151
162152 /// Update the owner/group for the file/dir `name` at `parent`.
163153 fn chown_at (
164154 & self ,
165- dir : Self :: DirHandle ,
155+ dir : DirHandle ,
166156 name : & str ,
167157 user : Option < u16 > ,
168158 group : Option < u16 > ,
169159 ) -> Result < ( ) , ChownError > ;
170160}
171161
162+ /// Concrete handle types used by a backend.
163+ ///
164+ /// This trait is intentionally separate from [`Backend`]: the dyn-safe [`Backend`] interface keeps
165+ /// using erased handle wrappers, while concrete backend implementations can use these associated
166+ /// types at their own boundaries instead of spelling out manual erased-handle downcasts.
167+ pub ( crate ) trait BackendHandles {
168+ /// Supporting walk through the backend
169+ type WalkingDirHandle < ' a > : ' a ;
170+ /// An owned handle to an open file
171+ type FileHandle : Clone + Send + Sync + ' static ;
172+ /// An owned handle to an open directory
173+ type DirHandle : Clone + Send + Sync + ' static ;
174+ }
175+
176+ /// Supporting walk through the backend
177+ pub struct WalkingDirHandle < ' a > {
178+ backend_type : TypeId ,
179+ raw : Box < dyn ErasedWalkingDirHandle + ' a > ,
180+ _invariant : PhantomData < fn ( & ' a ( ) ) -> & ' a ( ) > ,
181+ }
182+
183+ /// An owned handle to an open file
184+ #[ derive( Clone ) ]
185+ pub struct FileHandle {
186+ raw : Box < dyn AnyCloneSendSync > ,
187+ }
188+
189+ /// An owned handle to an open directory
190+ #[ derive( Clone ) ]
191+ pub struct DirHandle {
192+ raw : Box < dyn AnyCloneSendSync > ,
193+ }
194+
195+ trait ErasedWalkingDirHandle {
196+ fn into_raw ( self : Box < Self > ) -> * mut ( ) ;
197+ }
198+
199+ impl < H > ErasedWalkingDirHandle for H {
200+ fn into_raw ( self : Box < Self > ) -> * mut ( ) {
201+ Box :: into_raw ( self ) . cast ( )
202+ }
203+ }
204+
205+ impl < ' a > WalkingDirHandle < ' a > {
206+ pub ( super ) fn from_typed < B : BackendHandles + ' static > ( handle : B :: WalkingDirHandle < ' a > ) -> Self {
207+ Self {
208+ backend_type : TypeId :: of :: < B > ( ) ,
209+ raw : Box :: new ( handle) ,
210+ _invariant : PhantomData ,
211+ }
212+ }
213+
214+ /// Recover the concrete walking handle stored in this erased handle for `B`.
215+ pub ( super ) fn into_typed < B : BackendHandles + ' static > ( self ) -> B :: WalkingDirHandle < ' a > {
216+ assert_eq ! (
217+ self . backend_type,
218+ TypeId :: of:: <B >( ) ,
219+ "backend walking directory handle type mismatch"
220+ ) ;
221+ // SAFETY: `from_typed::<B>` records `TypeId::of::<B>()` and stores a
222+ // `B::WalkingDirHandle<'a>`. `WalkingDirHandle<'a>` is invariant in `'a`, so the lifetime
223+ // parameter cannot have been changed since construction. The assertion above confirms that
224+ // this handle is being recovered for the same backend type, and together these guarantee
225+ // that the allocation has the expected concrete type.
226+ unsafe { * Box :: from_raw ( self . raw . into_raw ( ) . cast :: < B :: WalkingDirHandle < ' a > > ( ) ) }
227+ }
228+ }
229+
230+ impl FileHandle {
231+ pub ( super ) fn from_typed < B : BackendHandles > ( handle : B :: FileHandle ) -> Self {
232+ Self {
233+ raw : Box :: new ( handle) ,
234+ }
235+ }
236+
237+ pub ( super ) fn get_typed < B : BackendHandles > ( & self ) -> & B :: FileHandle {
238+ ( & * self . raw as & dyn Any )
239+ . downcast_ref :: < B :: FileHandle > ( )
240+ . expect ( "backend file handle type mismatch" )
241+ }
242+ }
243+
244+ impl DirHandle {
245+ pub ( super ) fn from_typed < B : BackendHandles > ( handle : B :: DirHandle ) -> Self {
246+ Self {
247+ raw : Box :: new ( handle) ,
248+ }
249+ }
250+
251+ pub ( super ) fn get_typed < B : BackendHandles > ( & self ) -> & B :: DirHandle {
252+ ( & * self . raw as & dyn Any )
253+ . downcast_ref :: < B :: DirHandle > ( )
254+ . expect ( "backend directory handle type mismatch" )
255+ }
256+
257+ pub ( super ) fn into_typed < B : BackendHandles > ( self ) -> B :: DirHandle {
258+ let raw: Box < dyn Any > = self . raw ;
259+ * raw. downcast :: < B :: DirHandle > ( )
260+ . expect ( "backend directory handle type mismatch" )
261+ }
262+ }
263+
172264/// A successful walk of directories through the backend
173265pub struct WalkOutcome < Walking > {
174266 /// A component per walked element.
@@ -232,3 +324,15 @@ pub(super) struct PermissionInfo {
232324 pub ( super ) mode : Mode ,
233325 pub ( super ) owner : UserInfo ,
234326}
327+
328+ #[ cfg( test) ]
329+ mod tests {
330+ use super :: Backend ;
331+
332+ #[ test]
333+ fn backend_is_dyn_safe ( ) {
334+ fn assert_dyn_safe ( _: Option < & dyn Backend > ) { }
335+
336+ assert_dyn_safe ( None ) ;
337+ }
338+ }
0 commit comments