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.
45//! 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::sync::Arc;
13use zx::Status;
1415pub struct FatfsDirRef {
16 inner: Option<Dir<'static>>,
17 open_count: usize,
18}
1920impl FatfsDirRef {
21/// Wraps and erases the lifetime. The caller assumes responsibility for
22 /// ensuring the associated filesystem lives long enough and is pinned.
23pub unsafe fn from(dir: Dir<'_>) -> Self {
24 FatfsDirRef { inner: Some(std::mem::transmute(dir)), open_count: 1 }
25 }
2627pub fn empty() -> Self {
28 FatfsDirRef { inner: None, open_count: 0 }
29 }
3031/// Extracts a reference to the wrapped value. The lifetime is restored to
32 /// that of _fs.
33pub fn borrow<'a>(&'a self, _fs: &'a FatFilesystemInner) -> Option<&'a Dir<'a>> {
34unsafe { std::mem::transmute(self.inner.as_ref()) }
35 }
3637/// Extracts a mutable reference to the wrapped value. The lifetime is restored to
38 /// that of _fs.
39pub fn borrow_mut<'a>(&'a mut self, _fs: &'a FatFilesystemInner) -> Option<&'a mut Dir<'a>> {
40// We need to transmute() back to the right lifetime because otherwise rust forces us to
41 // return a &'static mut, because it thinks that any references within the file must be to
42 // objects with a static lifetime. This isn't the case (because the lifetime is determined
43 // by the lock on FatFilesystemInner, which we know is held), so this is safe.
44unsafe { std::mem::transmute(self.inner.as_mut()) }
45 }
4647/// Reopen the FatfsDirRef if open count > 0.
48pub unsafe fn maybe_reopen(
49&mut self,
50 fs: &FatFilesystemInner,
51 parent: Option<&Arc<FatDirectory>>,
52 name: &str,
53 ) -> Result<(), Status> {
54if self.open_count == 0 {
55Ok(())
56 } else {
57self.reopen(fs, parent, name)
58 }
59 }
6061unsafe fn reopen(
62&mut self,
63 fs: &FatFilesystemInner,
64 parent: Option<&Arc<FatDirectory>>,
65 name: &str,
66 ) -> Result<(), Status> {
67let dir = if let Some(parent) = parent {
68 parent.open_ref(fs)?;
69defer! { parent.close_ref(fs) }
70 parent.find_child(fs, name)?.ok_or(Status::NOT_FOUND)?.to_dir()
71 } else {
72 fs.root_dir()
73 };
74self.inner.replace(std::mem::transmute(dir));
75Ok(())
76 }
7778/// Open the FatfsDirRef, incrementing the open count.
79pub unsafe fn open(
80&mut self,
81 fs: &FatFilesystemInner,
82 parent: Option<&Arc<FatDirectory>>,
83 name: &str,
84 ) -> Result<(), Status> {
85if self.open_count == std::usize::MAX {
86Err(Status::UNAVAILABLE)
87 } else {
88if self.open_count == 0 {
89self.reopen(fs, parent, name)?;
90 }
91self.open_count += 1;
92Ok(())
93 }
94 }
9596/// Close the FatfsDirRef, dropping the underlying Dir if the open count reaches zero.
97pub fn close(&mut self, fs: &FatFilesystemInner) {
98assert!(self.open_count > 0);
99self.open_count -= 1;
100if self.open_count == 0 {
101self.take(&fs);
102 }
103 }
104105/// Extracts the wrapped value, restoring its lifetime to that of _fs, and invalidate
106 /// this FatfsDirRef. Any future calls to the borrow_*() functions will panic.
107pub fn take<'a>(&mut self, _fs: &'a FatFilesystemInner) -> Option<Dir<'a>> {
108unsafe { std::mem::transmute(self.inner.take()) }
109 }
110}
111112// Safe because whenever the `inner` is used, the filesystem lock is held.
113unsafe impl Sync for FatfsDirRef {}
114unsafe impl Send for FatfsDirRef {}
115116impl Drop for FatfsDirRef {
117fn drop(&mut self) {
118assert_eq!(self.open_count, 0);
119// Need to call take().
120assert!(self.inner.is_none());
121 }
122}
123124pub struct FatfsFileRef {
125 inner: Option<File<'static>>,
126 open_count: usize,
127}
128129impl FatfsFileRef {
130/// Wraps and erases the lifetime. The caller assumes responsibility for
131 /// ensuring the associated filesystem lives long enough and is pinned.
132pub unsafe fn from(file: File<'_>) -> Self {
133 FatfsFileRef { inner: Some(std::mem::transmute(file)), open_count: 1 }
134 }
135136/// Extracts a mutable reference to the wrapped value. The lifetime is restored to
137 /// that of _fs.
138pub fn borrow_mut<'a>(&'a mut self, _fs: &'a FatFilesystemInner) -> Option<&'a mut File<'a>> {
139// We need to transmute() back to the right lifetime because otherwise rust forces us to
140 // return a &'static mut, because it thinks that any references within the file must be to
141 // objects with a static lifetime. This isn't the case (because the lifetime is determined
142 // by the lock on FatFilesystemInner, which we know is held), so this is safe.
143unsafe { std::mem::transmute(self.inner.as_mut()) }
144 }
145146/// Extracts a reference to the wrapped value. The lifetime is restored to that
147 /// of _fs.
148pub fn borrow<'a>(&'a self, _fs: &'a FatFilesystemInner) -> Option<&'a File<'a>> {
149self.inner.as_ref()
150 }
151152/// Reopen the FatfsDirRef if open count > 0.
153pub unsafe fn maybe_reopen(
154&mut self,
155 fs: &FatFilesystemInner,
156 parent: &FatDirectory,
157 name: &str,
158 ) -> Result<(), Status> {
159if self.open_count == 0 {
160Ok(())
161 } else {
162self.reopen(fs, parent, name)
163 }
164 }
165166unsafe fn reopen(
167&mut self,
168 fs: &FatFilesystemInner,
169 parent: &FatDirectory,
170 name: &str,
171 ) -> Result<(), Status> {
172let file = parent.find_child(fs, name)?.ok_or(Status::NOT_FOUND)?.to_file();
173self.inner.replace(std::mem::transmute(file));
174Ok(())
175 }
176177pub unsafe fn open(
178&mut self,
179 fs: &FatFilesystemInner,
180 parent: Option<&FatDirectory>,
181 name: &str,
182 ) -> Result<(), Status> {
183if self.open_count == std::usize::MAX {
184Err(Status::UNAVAILABLE)
185 } else {
186if self.open_count == 0 {
187self.reopen(fs, parent.ok_or(Status::BAD_HANDLE)?, name)?;
188 }
189self.open_count += 1;
190Ok(())
191 }
192 }
193194pub fn close(&mut self, fs: &FatFilesystemInner) {
195assert!(self.open_count > 0);
196self.open_count -= 1;
197if self.open_count == 0 {
198self.take(&fs);
199 }
200 }
201202/// Extracts the wrapped value, restoring its lifetime to that of _fs, and invalidate
203 /// this FatFsRef. Any future calls to the borrow_*() functions will panic.
204pub fn take<'a>(&mut self, _fs: &'a FatFilesystemInner) -> Option<File<'a>> {
205self.inner.take()
206 }
207}
208209// Safe because whenever the `inner` is used, the filesystem lock is held.
210unsafe impl Sync for FatfsFileRef {}
211unsafe impl Send for FatfsFileRef {}
212213impl Drop for FatfsFileRef {
214fn drop(&mut self) {
215// Need to call take().
216assert_eq!(self.open_count, 0);
217assert!(self.inner.is_none());
218 }
219}