fuchsia_fatfs/
refs.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! This module provides abstractions over the fatfs Dir and File types,
6//! erasing their lifetimes and allowing them to be kept without holding the filesystem lock.
7use crate::directory::FatDirectory;
8use crate::filesystem::FatFilesystemInner;
9use crate::node::Node;
10use crate::types::{Dir, File};
11use scopeguard::defer;
12use std::cell::{Ref, RefMut};
13use std::ops::{Deref, DerefMut};
14use std::sync::Arc;
15use zx::Status;
16
17pub trait Wrapper<'a> {
18    type Target: 'a;
19
20    /// Extracts a reference to the wrapped value. The lifetime is restored to that of `fs`.
21    fn get(&self, fs: &'a FatFilesystemInner) -> Option<&Self::Target>;
22
23    /// Extracts a mutable reference to the wrapped value. The lifetime is restored to that of `fs`.
24    fn get_mut(&mut self, fs: &'a FatFilesystemInner) -> Option<&mut Self::Target>;
25}
26
27pub struct FatfsDirRef {
28    inner: Option<Dir<'static>>,
29    open_count: usize,
30}
31
32impl FatfsDirRef {
33    /// Wraps and erases the lifetime. The caller assumes responsibility for
34    /// ensuring the associated filesystem lives long enough and is pinned.
35    pub unsafe fn from(dir: Dir<'_>) -> Self {
36        FatfsDirRef { inner: Some(std::mem::transmute(dir)), open_count: 1 }
37    }
38
39    pub fn empty() -> Self {
40        FatfsDirRef { inner: None, open_count: 0 }
41    }
42
43    /// Reopen the FatfsDirRef if open count > 0.
44    pub unsafe fn maybe_reopen(
45        &mut self,
46        fs: &FatFilesystemInner,
47        parent: Option<&Arc<FatDirectory>>,
48        name: &str,
49    ) -> Result<(), Status> {
50        if self.open_count == 0 { Ok(()) } else { self.reopen(fs, parent, name) }
51    }
52
53    unsafe fn reopen(
54        &mut self,
55        fs: &FatFilesystemInner,
56        parent: Option<&Arc<FatDirectory>>,
57        name: &str,
58    ) -> Result<(), Status> {
59        let dir = if let Some(parent) = parent {
60            parent.open_ref(fs)?;
61            defer! { parent.close_ref(fs) }
62            parent.find_child(fs, name)?.ok_or(Status::NOT_FOUND)?.to_dir()
63        } else {
64            fs.root_dir()
65        };
66        self.inner.replace(std::mem::transmute(dir));
67        Ok(())
68    }
69
70    /// Open the FatfsDirRef, incrementing the open count.
71    pub unsafe fn open(
72        &mut self,
73        fs: &FatFilesystemInner,
74        parent: Option<&Arc<FatDirectory>>,
75        name: &str,
76    ) -> Result<(), Status> {
77        if self.open_count == std::usize::MAX {
78            Err(Status::UNAVAILABLE)
79        } else {
80            if self.open_count == 0 {
81                self.reopen(fs, parent, name)?;
82            }
83            self.open_count += 1;
84            Ok(())
85        }
86    }
87
88    /// Close the FatfsDirRef, dropping the underlying Dir if the open count reaches zero.
89    pub fn close(&mut self, fs: &FatFilesystemInner) {
90        assert!(self.open_count > 0);
91        self.open_count -= 1;
92        if self.open_count == 0 {
93            self.take(&fs);
94        }
95    }
96
97    /// Extracts the wrapped value, restoring its lifetime to that of _fs, and invalidate
98    /// this FatfsDirRef. Any future calls to the borrow_*() functions will panic.
99    pub fn take<'a>(&mut self, _fs: &'a FatFilesystemInner) -> Option<Dir<'a>> {
100        unsafe { std::mem::transmute(self.inner.take()) }
101    }
102}
103
104impl<'a> Wrapper<'a> for FatfsDirRef {
105    type Target = Dir<'a>;
106
107    fn get(&self, _fs: &'a FatFilesystemInner) -> Option<&Dir<'a>> {
108        unsafe { std::mem::transmute(self.inner.as_ref()) }
109    }
110
111    fn get_mut(&mut self, _fs: &'a FatFilesystemInner) -> Option<&mut Dir<'a>> {
112        // We need to transmute() back to the right lifetime because otherwise rust forces us to
113        // return a &'static mut, because it thinks that any references within the file must be to
114        // objects with a static lifetime. This isn't the case (because the lifetime is determined
115        // by the lock on FatFilesystemInner, which we know is held), so this is safe.
116        unsafe { std::mem::transmute(self.inner.as_mut()) }
117    }
118}
119
120// Safe because whenever the `inner` is used, the filesystem lock is held.
121unsafe impl Sync for FatfsDirRef {}
122unsafe impl Send for FatfsDirRef {}
123
124impl Drop for FatfsDirRef {
125    fn drop(&mut self) {
126        assert_eq!(self.open_count, 0);
127        // Need to call take().
128        assert!(self.inner.is_none());
129    }
130}
131
132pub struct FatfsFileRef {
133    inner: Option<File<'static>>,
134    open_count: usize,
135}
136
137impl FatfsFileRef {
138    /// Wraps and erases the lifetime. The caller assumes responsibility for
139    /// ensuring the associated filesystem lives long enough and is pinned.
140    pub unsafe fn from(file: File<'_>) -> Self {
141        FatfsFileRef { inner: Some(std::mem::transmute(file)), open_count: 1 }
142    }
143
144    /// Reopen the FatfsDirRef if open count > 0.
145    pub unsafe fn maybe_reopen(
146        &mut self,
147        fs: &FatFilesystemInner,
148        parent: &FatDirectory,
149        name: &str,
150    ) -> Result<(), Status> {
151        if self.open_count == 0 { Ok(()) } else { self.reopen(fs, parent, name) }
152    }
153
154    unsafe fn reopen(
155        &mut self,
156        fs: &FatFilesystemInner,
157        parent: &FatDirectory,
158        name: &str,
159    ) -> Result<(), Status> {
160        let file = parent.find_child(fs, name)?.ok_or(Status::NOT_FOUND)?.to_file();
161        self.inner.replace(std::mem::transmute(file));
162        Ok(())
163    }
164
165    pub unsafe fn open(
166        &mut self,
167        fs: &FatFilesystemInner,
168        parent: Option<&FatDirectory>,
169        name: &str,
170    ) -> Result<(), Status> {
171        if self.open_count == std::usize::MAX {
172            Err(Status::UNAVAILABLE)
173        } else {
174            if self.open_count == 0 {
175                self.reopen(fs, parent.ok_or(Status::BAD_HANDLE)?, name)?;
176            }
177            self.open_count += 1;
178            Ok(())
179        }
180    }
181
182    pub fn close(&mut self, fs: &FatFilesystemInner) {
183        assert!(self.open_count > 0);
184        self.open_count -= 1;
185        if self.open_count == 0 {
186            self.take(&fs);
187        }
188    }
189
190    /// Extracts the wrapped value, restoring its lifetime to that of _fs, and invalidate
191    /// this FatFsRef.
192    pub fn take<'a>(&mut self, _fs: &'a FatFilesystemInner) -> Option<File<'a>> {
193        self.inner.take()
194    }
195}
196
197impl<'a> Wrapper<'a> for FatfsFileRef {
198    type Target = File<'a>;
199
200    fn get(&self, _fs: &'a FatFilesystemInner) -> Option<&File<'a>> {
201        self.inner.as_ref()
202    }
203
204    fn get_mut(&mut self, _fs: &'a FatFilesystemInner) -> Option<&mut File<'a>> {
205        // We need to transmute() back to the right lifetime because otherwise rust forces us to
206        // return a &'static mut, because it thinks that any references within the file must be to
207        // objects with a static lifetime. This isn't the case (because the lifetime is determined
208        // by the lock on FatFilesystemInner, which we know is held), so this is safe.
209        unsafe { std::mem::transmute(self.inner.as_mut()) }
210    }
211}
212
213// Safe because whenever the `inner` is used, the filesystem lock is held.
214unsafe impl Sync for FatfsFileRef {}
215unsafe impl Send for FatfsFileRef {}
216
217impl Drop for FatfsFileRef {
218    fn drop(&mut self) {
219        // Need to call take().
220        assert_eq!(self.open_count, 0);
221        assert!(self.inner.is_none());
222    }
223}
224
225pub struct Guard<'a, T>(&'a FatFilesystemInner, Ref<'a, T>);
226
227impl<'a, T> Guard<'a, T> {
228    pub fn new(fs: &'a FatFilesystemInner, inner: Ref<'a, T>) -> Self {
229        Self(fs, inner)
230    }
231}
232
233impl<'a, T: Wrapper<'a>> Deref for Guard<'a, T> {
234    type Target = T::Target;
235    fn deref(&self) -> &T::Target {
236        self.1.get(self.0).unwrap()
237    }
238}
239
240pub(crate) struct GuardMut<'a, T>(&'a FatFilesystemInner, RefMut<'a, T>);
241
242impl<'a, T> GuardMut<'a, T> {
243    pub fn new(fs: &'a FatFilesystemInner, inner: RefMut<'a, T>) -> Self {
244        Self(fs, inner)
245    }
246}
247
248impl<'a, T: Wrapper<'a>> Deref for GuardMut<'a, T> {
249    type Target = T::Target;
250    fn deref(&self) -> &T::Target {
251        self.1.get(self.0).unwrap()
252    }
253}
254
255impl<'a, T: Wrapper<'a>> DerefMut for GuardMut<'a, T> {
256    fn deref_mut(&mut self) -> &mut T::Target {
257        self.1.get_mut(self.0).unwrap()
258    }
259}