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