blob: 87eb66832f6afe9332e88c574d905cae83c71792 [file] [log] [blame]
// 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());
}
}