1use crate::directory::FatDirectory;
8use crate::filesystem::FatFilesystem;
9use crate::node::Node;
10use crate::types::{Dir, File};
11use scopeguard::defer;
12use std::cell::{Cell, Ref, RefCell, RefMut};
13use std::rc::Rc;
14use std::sync::Arc;
15use zx::Status;
16
17mod opaque {
18 #[repr(transparent)]
19 pub struct Opaque<T>(T);
20}
21pub use opaque::Opaque;
22
23pub unsafe trait ErasedLifetime<'a>: Sized {
30 type Target: 'a;
31
32 unsafe fn erase(val: Self::Target) -> Opaque<Self> {
39 unsafe {
42 let res = std::mem::transmute_copy(&val);
43 std::mem::forget(val);
44 res
45 }
46 }
47
48 unsafe fn restore(val: Opaque<Self>) -> Self::Target {
54 unsafe {
57 let res = std::mem::transmute_copy(&val);
58 std::mem::forget(val);
59 res
60 }
61 }
62
63 unsafe fn restore_ref(val: &Opaque<Self>) -> &Self::Target {
69 unsafe { &*(val as *const Opaque<Self> as *const Self::Target) }
72 }
73
74 unsafe fn restore_mut(val: &mut Opaque<Self>) -> &mut Self::Target {
80 unsafe { &mut *(val as *mut Opaque<Self> as *mut Self::Target) }
83 }
84}
85
86unsafe impl<'a> ErasedLifetime<'a> for Dir<'static> {
88 type Target = Dir<'a>;
89}
90
91unsafe impl<'a> ErasedLifetime<'a> for File<'static> {
93 type Target = File<'a>;
94}
95
96pub struct FsRef<T> {
97 inner: RefCell<Option<Opaque<T>>>,
98 open_count: Cell<usize>,
99 filesystem: Rc<FatFilesystem>,
100}
101
102impl<T> FsRef<T> {
103 pub unsafe fn from<'a>(
110 val: <T as ErasedLifetime<'a>>::Target,
111 filesystem: Rc<FatFilesystem>,
112 ) -> Self
113 where
114 T: ErasedLifetime<'a>,
115 {
116 FsRef {
117 inner: RefCell::new(Some(unsafe { T::erase(val) })),
120 open_count: Cell::new(1),
121 filesystem,
122 }
123 }
124
125 pub fn empty(filesystem: Rc<FatFilesystem>) -> Self {
126 FsRef { inner: RefCell::new(None), open_count: Cell::new(0), filesystem }
127 }
128
129 pub fn filesystem(&self) -> &Rc<FatFilesystem> {
130 &self.filesystem
131 }
132
133 pub fn close<'a>(&self)
134 where
135 T: ErasedLifetime<'a>,
136 {
137 let open_count = self.open_count.get();
138 assert!(open_count > 0);
139 self.open_count.set(open_count - 1);
140 if open_count == 1 {
141 self.clear();
142 }
143 }
144
145 pub fn clear<'a>(&self)
146 where
147 T: ErasedLifetime<'a>,
148 {
149 if let Some(f) = self.inner.borrow_mut().take() {
150 unsafe {
153 let restored = <T as ErasedLifetime<'a>>::restore(f);
154 drop(restored);
155 }
156 }
157 }
158
159 pub fn get<'a>(&'a self) -> Option<Ref<'a, <T as ErasedLifetime<'a>>::Target>>
160 where
161 T: ErasedLifetime<'a>,
162 {
163 let borrow = self.inner.borrow();
164 if borrow.is_none() {
165 None
166 } else {
167 Some(Ref::map(borrow, |val| {
168 unsafe { T::restore_ref(val.as_ref().unwrap()) }
171 }))
172 }
173 }
174
175 pub fn get_mut<'a>(&'a self) -> Option<RefMut<'a, <T as ErasedLifetime<'a>>::Target>>
176 where
177 T: ErasedLifetime<'a>,
178 {
179 let borrow = self.inner.borrow_mut();
180 if borrow.is_none() {
181 None
182 } else {
183 Some(RefMut::map(borrow, |val| {
184 unsafe { T::restore_mut(val.as_mut().unwrap()) }
187 }))
188 }
189 }
190}
191
192impl<T> Drop for FsRef<T> {
193 fn drop(&mut self) {
194 assert_eq!(self.open_count.get(), 0);
195 assert!(self.inner.borrow().is_none());
196 }
197}
198
199impl FsRef<Dir<'static>> {
200 pub fn maybe_reopen(
201 &self,
202 parent: Option<&Arc<FatDirectory>>,
203 name: &str,
204 ) -> Result<(), Status> {
205 if self.open_count.get() == 0 { Ok(()) } else { self.reopen(parent, name) }
206 }
207
208 fn reopen(&self, parent: Option<&Arc<FatDirectory>>, name: &str) -> Result<(), Status> {
209 let dir = if let Some(parent) = parent {
210 parent.open_ref()?;
211 defer! { parent.close_ref() }
212 parent.find_child(name)?.ok_or(Status::NOT_FOUND)?.to_dir()
213 } else {
214 self.filesystem.fatfs_root_dir()
215 };
216 self.inner.replace(Some(unsafe { <Dir<'static> as ErasedLifetime>::erase(dir) }));
219 Ok(())
220 }
221
222 pub fn open(&self, parent: Option<&Arc<FatDirectory>>, name: &str) -> Result<(), Status> {
223 let open_count = self.open_count.get();
224 if open_count == usize::MAX {
225 Err(Status::UNAVAILABLE)
226 } else {
227 if open_count == 0 {
228 self.reopen(parent, name)?;
229 }
230 self.open_count.set(open_count + 1);
231 Ok(())
232 }
233 }
234}
235
236impl FsRef<File<'static>> {
237 pub fn maybe_reopen(&self, parent: &FatDirectory, name: &str) -> Result<(), Status> {
238 if self.open_count.get() == 0 { Ok(()) } else { self.reopen(parent, name) }
239 }
240
241 fn reopen(&self, parent: &FatDirectory, name: &str) -> Result<(), Status> {
242 let file = parent.find_child(name)?.ok_or(Status::NOT_FOUND)?.to_file();
243 self.inner.replace(Some(unsafe { <File<'static> as ErasedLifetime>::erase(file) }));
246 Ok(())
247 }
248
249 pub fn open(&self, parent: Option<&FatDirectory>, name: &str) -> Result<(), Status> {
250 let open_count = self.open_count.get();
251 if open_count == usize::MAX {
252 Err(Status::UNAVAILABLE)
253 } else {
254 if open_count == 0 {
255 self.reopen(parent.ok_or(Status::BAD_HANDLE)?, name)?;
256 }
257 self.open_count.set(open_count + 1);
258 Ok(())
259 }
260 }
261}
262
263pub type FatfsDirRef = FsRef<Dir<'static>>;
264pub type FatfsFileRef = FsRef<File<'static>>;