blob: b292feb08d5da8ec5e89f740b880661fec5c8a72 [file] [log] [blame]
// Copyright 2017 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 zx;
use zx::{DurationNum, HandleBased};
use fdio;
use std;
use std::fs;
use std::os::unix::io::RawFd;
use std::os::unix::io::IntoRawFd;
use std::os::unix::io::FromRawFd;
use std::path::Path;
use std::os::unix::fs::OpenOptionsExt;
use fdio::fdio_sys::{O_ADMIN, O_NOREMOTE, O_DIRECTORY};
#[link(name = "fs-management")]
extern "C" {
fn vfs_unmount_handle(
srv: zx::sys::zx_handle_t,
deadline: zx::sys::zx_time_t,
) -> zx::sys::zx_status_t;
}
pub struct Mount {
mountfd: RawFd,
}
impl Drop for Mount {
fn drop(&mut self) {
let mut h: zx::sys::zx_handle_t = zx::sys::ZX_HANDLE_INVALID;
let sz = unsafe {
fdio::ioctl(
self.mountfd,
fdio::IOCTL_VFS_UNMOUNT_NODE,
std::ptr::null_mut(),
0,
&mut h as *mut _ as *mut std::os::raw::c_void,
std::mem::size_of::<zx::sys::zx_handle_t>(),
)
};
if sz < 0 {
// TODO(raggi): report errors somewhere/somehow more appropriate
eprintln!("fuchsia-vfs: failed to unmount node");
} else {
// TODO(raggi): what is a reasonable timeout value here?
let deadline = 1_000_000.nanos().after_now();
let status = unsafe { vfs_unmount_handle(h, deadline.nanos()) };
if status != zx::sys::ZX_OK {
eprintln!("fuchsia-vfs: failed to unmount handle: {:?}", status);
}
}
unsafe { zx::sys::zx_handle_close(h) };
std::mem::drop(unsafe { fs::File::from_raw_fd(self.mountfd) });
}
}
pub fn mount(path: &Path, chan: zx::Channel) -> Result<Mount, zx::Status> {
let dir = fs::OpenOptions::new()
.read(true)
.custom_flags(O_DIRECTORY | O_ADMIN | O_NOREMOTE)
.open(&path)
.unwrap();
let mount = Mount { mountfd: dir.into_raw_fd() };
let h = chan.into_handle().into_raw();
let sz = unsafe {
fdio::ioctl(
mount.mountfd,
fdio::IOCTL_VFS_MOUNT_FS,
&h as *const _ as *const std::os::raw::c_void,
std::mem::size_of::<zx::sys::zx_handle_t>(),
std::ptr::null_mut(),
0,
)
};
if sz != 0 {
unsafe { zx::sys::zx_handle_close(h) };
return Err(zx::Status::from_raw(sz as i32));
}
Ok(mount)
}
#[cfg(test)]
mod test {
use super::*;
use zx::AsHandleRef;
extern crate tempdir;
#[test]
fn test_mount_unmount() {
let (c1, c2) = zx::Channel::create().unwrap();
// TODO(raggi): where is the appropriate place to put this, it's part of the mount protocol?
c2.signal_handle(zx::Signals::NONE, zx::Signals::USER_0)
.unwrap();
let port = zx::Port::create().unwrap();
c2.wait_async_handle(
&port,
1,
zx::Signals::CHANNEL_PEER_CLOSED,
zx::WaitAsyncOpts::Once,
).unwrap();
let td = tempdir::TempDir::new("test_mount_unmount").unwrap();
let m = mount(&td.path(), c1).unwrap();
assert_eq!(
zx::Status::TIMED_OUT,
port.wait(2_000_000.nanos().after_now()).expect_err(
"timeout",
)
);
std::mem::drop(m);
let packet = port.wait(2_000_000.nanos().after_now()).unwrap();
match packet.contents() {
zx::PacketContents::SignalOne(sp) => {
assert_eq!(
zx::Signals::CHANNEL_PEER_CLOSED,
sp.observed() & zx::Signals::CHANNEL_PEER_CLOSED
);
}
_ => assert!(false, "expected signalone packet"),
}
}
}