devices: virtio: fs: passthrough: Acquire lock before fchdir

Since fchdir is a per-process operation, we need a mutex to prevent
multiple threads from calling it at the same time.

BUG=none
TEST=start ARCVM

Change-Id: I072f500f6aa5bb5afb4983cd092f4994d31fa9c4
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4194229
Reviewed-by: David Stevens <stevensd@chromium.org>
Commit-Queue: Keiichi Watanabe <keiichiw@chromium.org>
This commit is contained in:
Keiichi Watanabe 2023-01-25 21:45:58 +09:00 committed by crosvm LUCI
parent 4a4ed3cae2
commit 3eed4ae229

View file

@ -565,6 +565,9 @@ impl Default for Config {
/// directory ends up as the root of the file system process. One way to accomplish this is via a
/// combination of mount namespaces and the pivot_root system call.
pub struct PassthroughFs {
// Mutex that must be acquired before executing a process-wide operation such as fchdir.
process_lock: Mutex<()>,
// File descriptors for various points in the file system tree.
inodes: Mutex<MultikeyBTreeMap<Inode, InodeAltKey, Arc<InodeData>>>,
next_inode: AtomicU64,
@ -633,6 +636,7 @@ impl PassthroughFs {
let proc = unsafe { File::from_raw_descriptor(raw_descriptor) };
Ok(PassthroughFs {
process_lock: Mutex::new(()),
inodes: Mutex::new(MultikeyBTreeMap::new()),
next_inode: AtomicU64::new(ROOT_ID + 1),
@ -950,12 +954,17 @@ impl PassthroughFs {
// directory. This effectively emulates an *at syscall starting at /proc, which is useful when
// there is no *at syscall available. Panics if any of the fchdir calls fail or if there is no
// root inode.
//
// NOTE: this method acquires an `self`-wide lock. If any locks are acquired in `f`, care must
// be taken to avoid the risk of deadlocks.
fn with_proc_chdir<F, T>(&self, f: F) -> T
where
F: FnOnce() -> T,
{
let root = self.find_inode(ROOT_ID).expect("failed to find root inode");
// Acquire a lock for `fchdir`.
let _proc_lock = self.process_lock.lock();
// Safe because this doesn't modify any memory and we check the return value. Since the
// fchdir should never fail we just use debug_asserts.
let proc_cwd = unsafe { libc::fchdir(self.proc.as_raw_descriptor()) };