blob: 2876c128df46f6c75f51970ca89ca4a9c4d55ec2 [file] [log] [blame]
// Copyright 2021 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.
use std::sync::Arc;
use crate::fs::*;
use crate::lock::RwLock;
use crate::types::*;
/// The mutable state for an FsContext.
///
/// This state is cloned in FsContext::fork.
#[derive(Clone)]
struct FsContextState {
/// The current working directory.
cwd: NamespaceNode,
// See <https://man7.org/linux/man-pages/man2/umask.2.html>
umask: FileMode,
}
/// The file system context associated with a task.
///
/// File system operations, such as opening a file or mounting a directory, are
/// performed using this context.
pub struct FsContext {
/// The namespace tree for this FsContext.
///
/// This field owns the mount table for this FsContext.
namespace: Arc<Namespace>,
/// The root of the namespace tree for this FsContext.
///
/// Operations on the file system are typically either relative to this
/// root or to the cwd().
pub root: NamespaceNode,
/// The mutable state for this FsContext.
state: RwLock<FsContextState>,
}
impl FsContext {
/// Create an FsContext for the given namespace.
///
/// The root and cwd of the FsContext are initialized to the root of the
/// namespace.
pub fn new(root: FileSystemHandle) -> Arc<FsContext> {
let namespace = Namespace::new(root);
let root = namespace.root();
Arc::new(FsContext {
namespace,
root: root.clone(),
state: RwLock::new(FsContextState { cwd: root, umask: FileMode::DEFAULT_UMASK }),
})
}
pub fn fork(&self) -> Arc<FsContext> {
// A child process created via fork(2) inherits its parent's umask.
// The umask is left unchanged by execve(2).
//
// See <https://man7.org/linux/man-pages/man2/umask.2.html>
Arc::new(FsContext {
namespace: self.namespace.clone(),
root: self.root.clone(),
state: RwLock::new(self.state.read().clone()),
})
}
/// Returns a reference to the current working directory.
pub fn cwd(&self) -> NamespaceNode {
let state = self.state.read();
state.cwd.clone()
}
/// Change the current working directory.
pub fn chdir(&self, name: NamespaceNode) {
let mut state = self.state.write();
state.cwd = name;
}
pub fn apply_umask(&self, mode: FileMode) -> FileMode {
let umask = self.state.read().umask;
mode & !umask
}
pub fn set_umask(&self, umask: FileMode) -> FileMode {
let mut state = self.state.write();
let old_umask = state.umask;
// umask() sets the calling process's file mode creation mask
// (umask) to mask & 0o777 (i.e., only the file permission bits of
// mask are used), and returns the previous value of the mask.
//
// See <https://man7.org/linux/man-pages/man2/umask.2.html>
state.umask = umask & FileMode::ALLOW_ALL;
old_umask
}
pub fn namespace_root(&self) -> NamespaceNode {
self.namespace.root()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::fs::tmpfs::TmpFs;
use crate::testing::*;
#[::fuchsia::test]
fn test_umask() {
let (kernel, _current_task) = create_kernel_and_task_with_pkgfs();
let fs = FsContext::new(TmpFs::new(&kernel));
assert_eq!(FileMode::from_bits(0o22), fs.set_umask(FileMode::from_bits(0o3020)));
assert_eq!(FileMode::from_bits(0o646), fs.apply_umask(FileMode::from_bits(0o666)));
assert_eq!(FileMode::from_bits(0o3646), fs.apply_umask(FileMode::from_bits(0o3666)));
assert_eq!(FileMode::from_bits(0o20), fs.set_umask(FileMode::from_bits(0o11)));
}
#[::fuchsia::test]
fn test_chdir() {
let (_kernel, current_task) = create_kernel_and_task_with_pkgfs();
assert_eq!(b"/".to_vec(), current_task.fs.cwd().path());
let bin = current_task.open_file(b"bin", OpenFlags::RDONLY).expect("missing bin directory");
current_task.fs.chdir(bin.name.clone());
assert_eq!(b"/bin".to_vec(), current_task.fs.cwd().path());
// Now that we have changed directories to bin, we're opening a file
// relative to that directory, which doesn't exist.
assert!(current_task.open_file(b"bin", OpenFlags::RDONLY).is_err());
// However, bin still exists in the root directory.
assert!(current_task.open_file(b"/bin", OpenFlags::RDONLY).is_ok());
current_task.fs.chdir(
current_task
.open_file(b"..", OpenFlags::RDONLY)
.expect("failed to open ..")
.name
.clone(),
);
assert_eq!(b"/".to_vec(), current_task.fs.cwd().path());
// Now bin exists again because we've gone back to the root.
assert!(current_task.open_file(b"bin", OpenFlags::RDONLY).is_ok());
// Repeating the .. doesn't do anything because we're already at the root.
current_task.fs.chdir(
current_task
.open_file(b"..", OpenFlags::RDONLY)
.expect("failed to open ..")
.name
.clone(),
);
assert_eq!(b"/".to_vec(), current_task.fs.cwd().path());
assert!(current_task.open_file(b"bin", OpenFlags::RDONLY).is_ok());
}
}