| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! This module provides abstractions over the fatfs Dir and File types, |
| //! erasing their lifetimes and allowing them to be kept without holding the filesystem lock. |
| use { |
| crate::{ |
| directory::FatDirectory, |
| filesystem::FatFilesystemInner, |
| node::Node, |
| types::{Dir, File}, |
| }, |
| fuchsia_zircon::Status, |
| scopeguard::defer, |
| std::sync::Arc, |
| }; |
| |
| pub struct FatfsDirRef { |
| inner: Option<Dir<'static>>, |
| open_count: usize, |
| } |
| |
| impl FatfsDirRef { |
| /// Wraps and erases the lifetime. The caller assumes responsibility for |
| /// ensuring the associated filesystem lives long enough and is pinned. |
| pub unsafe fn from(dir: Dir<'_>) -> Self { |
| FatfsDirRef { inner: Some(std::mem::transmute(dir)), open_count: 1 } |
| } |
| |
| pub fn empty() -> Self { |
| FatfsDirRef { inner: None, open_count: 0 } |
| } |
| |
| /// Extracts a reference to the wrapped value. The lifetime is restored to |
| /// that of _fs. |
| pub fn borrow<'a>(&'a self, _fs: &'a FatFilesystemInner) -> Option<&'a Dir<'a>> { |
| unsafe { std::mem::transmute(self.inner.as_ref()) } |
| } |
| |
| /// Extracts a mutable reference to the wrapped value. The lifetime is restored to |
| /// that of _fs. |
| pub fn borrow_mut<'a>(&'a mut self, _fs: &'a FatFilesystemInner) -> Option<&'a mut Dir<'a>> { |
| // We need to transmute() back to the right lifetime because otherwise rust forces us to |
| // return a &'static mut, because it thinks that any references within the file must be to |
| // objects with a static lifetime. This isn't the case (because the lifetime is determined |
| // by the lock on FatFilesystemInner, which we know is held), so this is safe. |
| unsafe { std::mem::transmute(self.inner.as_mut()) } |
| } |
| |
| /// Reopen the FatfsDirRef if open count > 0. |
| pub unsafe fn maybe_reopen( |
| &mut self, |
| fs: &FatFilesystemInner, |
| parent: Option<&Arc<FatDirectory>>, |
| name: &str, |
| ) -> Result<(), Status> { |
| if self.open_count == 0 { |
| Ok(()) |
| } else { |
| self.reopen(fs, parent, name) |
| } |
| } |
| |
| unsafe fn reopen( |
| &mut self, |
| fs: &FatFilesystemInner, |
| parent: Option<&Arc<FatDirectory>>, |
| name: &str, |
| ) -> Result<(), Status> { |
| let dir = if let Some(parent) = parent { |
| parent.open_ref(fs)?; |
| defer! { parent.close_ref(fs) } |
| parent.find_child(fs, name)?.ok_or(Status::NOT_FOUND)?.to_dir() |
| } else { |
| fs.root_dir() |
| }; |
| self.inner.replace(std::mem::transmute(dir)); |
| Ok(()) |
| } |
| |
| /// Open the FatfsDirRef, incrementing the open count. |
| pub unsafe fn open( |
| &mut self, |
| fs: &FatFilesystemInner, |
| parent: Option<&Arc<FatDirectory>>, |
| name: &str, |
| ) -> Result<(), Status> { |
| if self.open_count == std::usize::MAX { |
| Err(Status::UNAVAILABLE) |
| } else { |
| if self.open_count == 0 { |
| self.reopen(fs, parent, name)?; |
| } |
| self.open_count += 1; |
| Ok(()) |
| } |
| } |
| |
| /// Close the FatfsDirRef, dropping the underlying Dir if the open count reaches zero. |
| pub fn close(&mut self, fs: &FatFilesystemInner) { |
| assert!(self.open_count > 0); |
| self.open_count -= 1; |
| if self.open_count == 0 { |
| self.take(&fs); |
| } |
| } |
| |
| /// Extracts the wrapped value, restoring its lifetime to that of _fs, and invalidate |
| /// this FatfsDirRef. Any future calls to the borrow_*() functions will panic. |
| pub fn take<'a>(&mut self, _fs: &'a FatFilesystemInner) -> Option<Dir<'a>> { |
| unsafe { std::mem::transmute(self.inner.take()) } |
| } |
| } |
| |
| // Safe because whenever the `inner` is used, the filesystem lock is held. |
| unsafe impl Sync for FatfsDirRef {} |
| unsafe impl Send for FatfsDirRef {} |
| |
| impl Drop for FatfsDirRef { |
| fn drop(&mut self) { |
| assert_eq!(self.open_count, 0); |
| // Need to call take(). |
| assert!(self.inner.is_none()); |
| } |
| } |
| |
| pub struct FatfsFileRef { |
| inner: Option<File<'static>>, |
| open_count: usize, |
| } |
| |
| impl FatfsFileRef { |
| /// Wraps and erases the lifetime. The caller assumes responsibility for |
| /// ensuring the associated filesystem lives long enough and is pinned. |
| pub unsafe fn from(file: File<'_>) -> Self { |
| FatfsFileRef { inner: Some(std::mem::transmute(file)), open_count: 1 } |
| } |
| |
| /// Extracts a mutable reference to the wrapped value. The lifetime is restored to |
| /// that of _fs. |
| pub fn borrow_mut<'a>(&'a mut self, _fs: &'a FatFilesystemInner) -> Option<&'a mut File<'a>> { |
| // We need to transmute() back to the right lifetime because otherwise rust forces us to |
| // return a &'static mut, because it thinks that any references within the file must be to |
| // objects with a static lifetime. This isn't the case (because the lifetime is determined |
| // by the lock on FatFilesystemInner, which we know is held), so this is safe. |
| unsafe { std::mem::transmute(self.inner.as_mut()) } |
| } |
| |
| /// Extracts a reference to the wrapped value. The lifetime is restored to that |
| /// of _fs. |
| pub fn borrow<'a>(&'a self, _fs: &'a FatFilesystemInner) -> Option<&'a File<'a>> { |
| self.inner.as_ref() |
| } |
| |
| /// Reopen the FatfsDirRef if open count > 0. |
| pub unsafe fn maybe_reopen( |
| &mut self, |
| fs: &FatFilesystemInner, |
| parent: &FatDirectory, |
| name: &str, |
| ) -> Result<(), Status> { |
| if self.open_count == 0 { |
| Ok(()) |
| } else { |
| self.reopen(fs, parent, name) |
| } |
| } |
| |
| unsafe fn reopen( |
| &mut self, |
| fs: &FatFilesystemInner, |
| parent: &FatDirectory, |
| name: &str, |
| ) -> Result<(), Status> { |
| let file = parent.find_child(fs, name)?.ok_or(Status::NOT_FOUND)?.to_file(); |
| self.inner.replace(std::mem::transmute(file)); |
| Ok(()) |
| } |
| |
| pub unsafe fn open( |
| &mut self, |
| fs: &FatFilesystemInner, |
| parent: Option<&FatDirectory>, |
| name: &str, |
| ) -> Result<(), Status> { |
| if self.open_count == std::usize::MAX { |
| Err(Status::UNAVAILABLE) |
| } else { |
| if self.open_count == 0 { |
| self.reopen(fs, parent.ok_or(Status::BAD_HANDLE)?, name)?; |
| } |
| self.open_count += 1; |
| Ok(()) |
| } |
| } |
| |
| pub fn close(&mut self, fs: &FatFilesystemInner) { |
| assert!(self.open_count > 0); |
| self.open_count -= 1; |
| if self.open_count == 0 { |
| self.take(&fs); |
| } |
| } |
| |
| /// Extracts the wrapped value, restoring its lifetime to that of _fs, and invalidate |
| /// this FatFsRef. Any future calls to the borrow_*() functions will panic. |
| pub fn take<'a>(&mut self, _fs: &'a FatFilesystemInner) -> Option<File<'a>> { |
| self.inner.take() |
| } |
| } |
| |
| // Safe because whenever the `inner` is used, the filesystem lock is held. |
| unsafe impl Sync for FatfsFileRef {} |
| unsafe impl Send for FatfsFileRef {} |
| |
| impl Drop for FatfsFileRef { |
| fn drop(&mut self) { |
| // Need to call take(). |
| assert_eq!(self.open_count, 0); |
| assert!(self.inner.is_none()); |
| } |
| } |