diff --git a/src/devices/virtio_9p/file.rs b/src/devices/virtio_9p/file.rs index cab9676..b138e7e 100644 --- a/src/devices/virtio_9p/file.rs +++ b/src/devices/virtio_9p/file.rs @@ -1,8 +1,9 @@ -use std::cell::{RefCell, RefMut}; +use std::cell::{RefCell, RefMut, Cell}; use std::collections::BTreeMap; use std::{io, fmt}; use std::path::{Path, PathBuf, Component}; use std::fs::{Metadata, File}; +use std::os::unix::io::{RawFd,AsRawFd}; use std::os::linux::fs::MetadataExt; use std::os::unix::fs::FileExt; @@ -12,7 +13,6 @@ use crate::devices::virtio_9p::{ use std::io::{Cursor, SeekFrom, Seek, Read}; use std::sync::{RwLock, Arc}; - pub const P9_DOTL_RDONLY: u32 = 0o00000000; pub const P9_DOTL_WRONLY: u32 = 0o00000001; pub const P9_DOTL_RDWR: u32 = 0o00000002; @@ -37,6 +37,21 @@ pub const P9_QTFILE: u8 = 0x00; pub const P9_QTSYMLINK: u8 = 0x02; pub const P9_QTDIR: u8 = 0x80; +const P9_LOCK_SUCCESS: u8 = 0; +const P9_LOCK_BLOCKED: u8 =1; +const P9_LOCK_ERROR: u8 = 2; + +const P9_LOCK_TYPE_RDLCK: u8 = 0; +const P9_LOCK_TYPE_WRLCK: u8 = 1; +const P9_LOCK_TYPE_UNLCK: u8 = 2; + +#[derive(PartialEq,Copy,Clone)] +enum LockType { + LockUn, + LockSh, + LockEx, +} + #[derive(Clone)] pub struct Buffer>(Arc>>); impl > Buffer { @@ -61,21 +76,35 @@ enum FileObject { NotAFile, } +impl FileObject { + fn fd(&self) -> Option { + match self { + FileObject::File(file) => Some(file.as_raw_fd()), + _ => None, + } + } +} + pub struct P9File { file: FileObject, + lock: Cell, } impl P9File { + + fn new(file: FileObject) -> Self { + P9File { file, lock: Cell::new(LockType::LockUn) } + } pub fn new_not_a_file() -> Self { - P9File { file: FileObject::NotAFile } + Self::new(FileObject::NotAFile) } pub fn from_file(file: File) -> Self { - P9File { file: FileObject::File(file) } + Self::new(FileObject::File(file)) } pub fn from_buffer(buffer: Buffer<&'static [u8]>) -> Self { - P9File { file: FileObject::BufferFile(buffer) } + Self::new(FileObject::BufferFile(buffer)) } pub fn sync_all(&self) -> io::Result<()> { @@ -84,6 +113,7 @@ impl P9File { _ => Ok(()), } } + pub fn sync_data(&self) -> io::Result<()> { match self.file { FileObject::File(ref f) => f.sync_data(), @@ -106,6 +136,99 @@ impl P9File { FileObject::NotAFile => Ok(0), } } + + fn map_locktype(ltype: u8) -> LockType { + match ltype { + P9_LOCK_TYPE_UNLCK => LockType::LockUn, + P9_LOCK_TYPE_RDLCK => LockType::LockSh, + P9_LOCK_TYPE_WRLCK => LockType::LockEx, + _ => LockType::LockUn, + } + } + + fn errno() -> i32 { + unsafe { *libc::__errno_location() } + } + + fn raw_flock(fd: RawFd, op: i32) -> u8 { + unsafe { + if libc::flock(fd, op) == -1 { + if Self::errno() == libc::EWOULDBLOCK { + P9_LOCK_BLOCKED + } else { + P9_LOCK_ERROR + } + } else { + P9_LOCK_SUCCESS + } + } + } + + pub fn flock(&self, ltype: u8) -> io::Result { + + let fd = match self.file.fd() { + Some(fd) => fd, + None => { + self.lock.set(Self::map_locktype(ltype)); + return Ok(P9_LOCK_SUCCESS); + } + }; + + match ltype { + P9_LOCK_TYPE_UNLCK => { + let status = Self::raw_flock(fd, libc::LOCK_UN); + if status == P9_LOCK_SUCCESS { + self.lock.set(LockType::LockUn); + } + Ok(status) + } + P9_LOCK_TYPE_WRLCK => { + let status = Self::raw_flock(fd, libc::LOCK_EX|libc::LOCK_NB); + if status == P9_LOCK_SUCCESS { + self.lock.set(LockType::LockEx); + } + Ok(status) + } + P9_LOCK_TYPE_RDLCK => { + let status = Self::raw_flock(fd, libc::LOCK_SH|libc::LOCK_NB); + if status == P9_LOCK_SUCCESS { + self.lock.set(LockType::LockSh); + } + Ok(status) + } + _ => system_error(libc::EINVAL), + } + } + + pub fn getlock(&self, ltype: u8) -> io::Result { + let fd = match self.file.fd() { + Some(fd) => fd, + None => { + return Ok(P9_LOCK_TYPE_UNLCK); + } + }; + + match ltype { + P9_LOCK_TYPE_RDLCK => { + if self.lock.get() == LockType::LockUn { + match Self::raw_flock(fd, libc::LOCK_NB|libc::LOCK_SH) { + P9_LOCK_SUCCESS => { Self::raw_flock(fd, libc::LOCK_UN); } + _ => return Ok(P9_LOCK_TYPE_WRLCK), + } + } + } + P9_LOCK_TYPE_WRLCK => { + if self.lock.get() == LockType::LockUn { + match Self::raw_flock(fd, libc::LOCK_NB|libc::LOCK_EX) { + P9_LOCK_SUCCESS => { Self::raw_flock(fd, libc::LOCK_UN); }, + _ => return Ok(P9_LOCK_TYPE_WRLCK), + } + } + } + _ => {} + } + Ok(P9_LOCK_TYPE_UNLCK) + } } #[derive(Copy,Clone)] diff --git a/src/devices/virtio_9p/filesystem.rs b/src/devices/virtio_9p/filesystem.rs index d928597..d4813d3 100644 --- a/src/devices/virtio_9p/filesystem.rs +++ b/src/devices/virtio_9p/filesystem.rs @@ -89,7 +89,6 @@ impl FileSystem { } fn metadata(&self, path: &Path) -> io::Result { - let path = self.canonicalize(path)?; path.symlink_metadata() } @@ -246,7 +245,7 @@ impl FileSystemOps for FileSystem { } fn readlink(&self, path: &Path) -> io::Result { - let path = self.canonicalize(path)?; + let path = self.canonicalize_parent(path)?; fs::read_link(&path).map(|pbuf| pbuf.into_os_string()) } diff --git a/src/devices/virtio_9p/server.rs b/src/devices/virtio_9p/server.rs index 6bfe2ae..83ad0f3 100644 --- a/src/devices/virtio_9p/server.rs +++ b/src/devices/virtio_9p/server.rs @@ -35,6 +35,9 @@ const P9_TWRITE: u8 = 118; const P9_TCLUNK: u8 = 120; const P9_REMOVE: u8 = 122; + +const P9_LOCK_FLAGS_BLOCK: u32 = 1; + pub struct Server { root: PathBuf, debug: bool, @@ -110,8 +113,8 @@ impl Server { P9_TXATTRCREATE => self.p9_unsupported(pp)?, P9_TREADDIR => self.p9_readdir(pp)?, P9_TFSYNC => self.p9_fsync(pp)?, - P9_TLOCK => self.p9_unsupported(pp)?, - P9_TGETLOCK => self.p9_unsupported(pp)?, + P9_TLOCK => self.p9_lock(pp)?, + P9_TGETLOCK => self.p9_getlock(pp)?, P9_TUNLINKAT => self.p9_unlinkat(pp)?, P9_TLINK => self.p9_link(pp)?, P9_TMKDIR=> self.p9_mkdir(pp)?, @@ -401,6 +404,54 @@ impl Server { pp.write_done() } + fn p9_lock_args(&self, pp: &mut PduParser) -> io::Result<(&Fid,u8,u32)> { + let fid = self.read_fid(pp)?; + let ltype = pp.r8()?; + let flags = pp.r32()?; + let _ = pp.r64()?; + let _ = pp.r64()?; + let _ = pp.r32()?; + let _ = pp.read_string()?; + pp.read_done()?; + Ok((fid, ltype, flags)) + } + + fn p9_lock(&mut self, pp: &mut PduParser) -> io::Result<()> { + let (fid, ltype,flags)= self.p9_lock_args(pp)?; + if flags & !P9_LOCK_FLAGS_BLOCK != 0 { + return system_error(libc::EINVAL); + } + let file = fid.file()?; + let status = file.flock(ltype)?; + pp.w8(status)?; + pp.write_done() + } + + fn p9_getlock_args(&mut self, pp: &mut PduParser) -> io::Result<(&Fid, u8, u64, u64, u32, String)> { + let fid = self.read_fid(pp)?; + let ltype = pp.r8()?; + let start = pp.r64()?; + let length= pp.r64()?; + let pid = pp.r32()?; + let client_id = pp.read_string()?; + pp.read_done()?; + + Ok((fid, ltype, start, length, pid, client_id)) + } + + fn p9_getlock(&mut self, pp: &mut PduParser) -> io::Result<()> { + let (fid, ltype,start,length, pid, client_id) = self.p9_getlock_args(pp)?; + + let file = fid.file()?; + let rtype = file.getlock(ltype)?; + pp.w8(rtype)?; + pp.w64(start)?; + pp.w64(length)?; + pp.w32(pid)?; + pp.write_string(&client_id)?; + pp.write_done() + } + fn p9_unlinkat_args(&self, pp: &mut PduParser) -> io::Result<(PathBuf, u32)> { let path = self.read_new_path(pp)?; let flags = pp.r32()?;