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(unsafe { 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 { unsafe { 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(unsafe { 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                unsafe {
82                    self.reopen(fs, parent, name)?;
83                }
84            }
85            self.open_count += 1;
86            Ok(())
87        }
88    }
89
90    /// Close the FatfsDirRef, dropping the underlying Dir if the open count reaches zero.
91    pub fn close(&mut self, fs: &FatFilesystemInner) {
92        assert!(self.open_count > 0);
93        self.open_count -= 1;
94        if self.open_count == 0 {
95            self.take(&fs);
96        }
97    }
98
99    /// Extracts the wrapped value, restoring its lifetime to that of _fs, and invalidate
100    /// this FatfsDirRef. Any future calls to the borrow_*() functions will panic.
101    pub fn take<'a>(&mut self, _fs: &'a FatFilesystemInner) -> Option<Dir<'a>> {
102        unsafe { std::mem::transmute(self.inner.take()) }
103    }
104}
105
106impl<'a> Wrapper<'a> for FatfsDirRef {
107    type Target = Dir<'a>;
108
109    fn get(&self, _fs: &'a FatFilesystemInner) -> Option<&Dir<'a>> {
110        unsafe { std::mem::transmute(self.inner.as_ref()) }
111    }
112
113    fn get_mut(&mut self, _fs: &'a FatFilesystemInner) -> Option<&mut Dir<'a>> {
114        // We need to transmute() back to the right lifetime because otherwise rust forces us to
115        // return a &'static mut, because it thinks that any references within the file must be to
116        // objects with a static lifetime. This isn't the case (because the lifetime is determined
117        // by the lock on FatFilesystemInner, which we know is held), so this is safe.
118        unsafe { std::mem::transmute(self.inner.as_mut()) }
119    }
120}
121
122// Safe because whenever the `inner` is used, the filesystem lock is held.
123unsafe impl Sync for FatfsDirRef {}
124unsafe impl Send for FatfsDirRef {}
125
126impl Drop for FatfsDirRef {
127    fn drop(&mut self) {
128        assert_eq!(self.open_count, 0);
129        // Need to call take().
130        assert!(self.inner.is_none());
131    }
132}
133
134pub struct FatfsFileRef {
135    inner: Option<File<'static>>,
136    open_count: usize,
137}
138
139impl FatfsFileRef {
140    /// Wraps and erases the lifetime. The caller assumes responsibility for
141    /// ensuring the associated filesystem lives long enough and is pinned.
142    pub unsafe fn from(file: File<'_>) -> Self {
143        FatfsFileRef { inner: Some(unsafe { std::mem::transmute(file) }), open_count: 1 }
144    }
145
146    /// Reopen the FatfsDirRef if open count > 0.
147    pub unsafe fn maybe_reopen(
148        &mut self,
149        fs: &FatFilesystemInner,
150        parent: &FatDirectory,
151        name: &str,
152    ) -> Result<(), Status> {
153        if self.open_count == 0 { Ok(()) } else { unsafe { self.reopen(fs, parent, name) } }
154    }
155
156    unsafe fn reopen(
157        &mut self,
158        fs: &FatFilesystemInner,
159        parent: &FatDirectory,
160        name: &str,
161    ) -> Result<(), Status> {
162        let file = parent.find_child(fs, name)?.ok_or(Status::NOT_FOUND)?.to_file();
163        self.inner.replace(unsafe { std::mem::transmute(file) });
164        Ok(())
165    }
166
167    pub unsafe fn open(
168        &mut self,
169        fs: &FatFilesystemInner,
170        parent: Option<&FatDirectory>,
171        name: &str,
172    ) -> Result<(), Status> {
173        if self.open_count == std::usize::MAX {
174            Err(Status::UNAVAILABLE)
175        } else {
176            if self.open_count == 0 {
177                unsafe {
178                    self.reopen(fs, parent.ok_or(Status::BAD_HANDLE)?, name)?;
179                }
180            }
181            self.open_count += 1;
182            Ok(())
183        }
184    }
185
186    pub fn close(&mut self, fs: &FatFilesystemInner) {
187        assert!(self.open_count > 0);
188        self.open_count -= 1;
189        if self.open_count == 0 {
190            self.take(&fs);
191        }
192    }
193
194    /// Extracts the wrapped value, restoring its lifetime to that of _fs, and invalidate
195    /// this FatFsRef.
196    pub fn take<'a>(&mut self, _fs: &'a FatFilesystemInner) -> Option<File<'a>> {
197        self.inner.take()
198    }
199}
200
201impl<'a> Wrapper<'a> for FatfsFileRef {
202    type Target = File<'a>;
203
204    fn get(&self, _fs: &'a FatFilesystemInner) -> Option<&File<'a>> {
205        self.inner.as_ref()
206    }
207
208    fn get_mut(&mut self, _fs: &'a FatFilesystemInner) -> Option<&mut File<'a>> {
209        // We need to transmute() back to the right lifetime because otherwise rust forces us to
210        // return a &'static mut, because it thinks that any references within the file must be to
211        // objects with a static lifetime. This isn't the case (because the lifetime is determined
212        // by the lock on FatFilesystemInner, which we know is held), so this is safe.
213        unsafe { std::mem::transmute(self.inner.as_mut()) }
214    }
215}
216
217// Safe because whenever the `inner` is used, the filesystem lock is held.
218unsafe impl Sync for FatfsFileRef {}
219unsafe impl Send for FatfsFileRef {}
220
221impl Drop for FatfsFileRef {
222    fn drop(&mut self) {
223        // Need to call take().
224        assert_eq!(self.open_count, 0);
225        assert!(self.inner.is_none());
226    }
227}
228
229pub struct Guard<'a, T>(&'a FatFilesystemInner, Ref<'a, T>);
230
231impl<'a, T> Guard<'a, T> {
232    pub fn new(fs: &'a FatFilesystemInner, inner: Ref<'a, T>) -> Self {
233        Self(fs, inner)
234    }
235}
236
237impl<'a, T: Wrapper<'a>> Deref for Guard<'a, T> {
238    type Target = T::Target;
239    fn deref(&self) -> &T::Target {
240        self.1.get(self.0).unwrap()
241    }
242}
243
244pub(crate) struct GuardMut<'a, T>(&'a FatFilesystemInner, RefMut<'a, T>);
245
246impl<'a, T> GuardMut<'a, T> {
247    pub fn new(fs: &'a FatFilesystemInner, inner: RefMut<'a, T>) -> Self {
248        Self(fs, inner)
249    }
250}
251
252impl<'a, T: Wrapper<'a>> Deref for GuardMut<'a, T> {
253    type Target = T::Target;
254    fn deref(&self) -> &T::Target {
255        self.1.get(self.0).unwrap()
256    }
257}
258
259impl<'a, T: Wrapper<'a>> DerefMut for GuardMut<'a, T> {
260    fn deref_mut(&mut self) -> &mut T::Target {
261        self.1.get_mut(self.0).unwrap()
262    }
263}