use std::io::{self, Read, Seek, SeekFrom, Write};
use libc::{c_int, c_void, mode_t, off_t};
use pavao_sys::{
smbc_getFunctionClose, smbc_getFunctionLseek, smbc_getFunctionRead, smbc_getFunctionWrite,
SMBCFILE,
};
use crate::{utils, SmbClient};
pub struct SmbFile<'a> {
smbc: &'a SmbClient,
fd: *mut SMBCFILE,
}
impl<'a> SmbFile<'a> {
pub(crate) fn new(smbc: &'a SmbClient, fd: *mut SMBCFILE) -> Self {
Self { smbc, fd }
}
}
impl Read for SmbFile<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
trace!("reading file to buf [{:?};{}]", buf.as_ptr(), buf.len());
let ctx = self
.smbc
.ctx()
.map_err(|_| std::io::Error::other("smbc context is not initialized, cannot read"))?;
let read_fn = self.smbc.get_fn(ctx, smbc_getFunctionRead)?;
let bytes_read = utils::to_result_with_le(read_fn(
ctx,
self.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len() as _,
))?;
Ok(bytes_read as usize)
}
}
impl Write for SmbFile<'_> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
trace!("writing buf [{:?};{}] to file", buf.as_ptr(), buf.len());
let ctx = self
.smbc
.ctx()
.map_err(|_| std::io::Error::other("smbc context is not initialized, cannot read"))?;
let write_fn = self.smbc.get_fn(ctx, smbc_getFunctionWrite)?;
let bytes_wrote = utils::to_result_with_le(write_fn(
ctx,
self.fd,
buf.as_ptr() as *const c_void,
buf.len() as _,
))?;
Ok(bytes_wrote as usize)
}
fn flush(&mut self) -> io::Result<()> {
trace!("flush is not supported on SmbFile");
Ok(())
}
}
impl Seek for SmbFile<'_> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
trace!("seeking file at {:?}", pos);
let ctx = self
.smbc
.ctx()
.map_err(|_| std::io::Error::other("smbc context is not initialized, cannot read"))?;
let lseek_fn = self.smbc.get_fn(ctx, smbc_getFunctionLseek)?;
let (whence, off) = match pos {
SeekFrom::Start(p) => (libc::SEEK_SET, p as off_t),
SeekFrom::End(p) => (libc::SEEK_END, p as off_t),
SeekFrom::Current(p) => (libc::SEEK_CUR, p as off_t),
};
let res = lseek_fn(ctx, self.fd, off, whence);
let res = utils::to_result_with_errno(res, libc::EINVAL)?;
Ok(res as u64)
}
}
impl Drop for SmbFile<'_> {
fn drop(&mut self) {
trace!("closing file");
if let Ok(ctx) = self.smbc.ctx() {
if let Ok(close_fn) = self.smbc.get_fn(ctx, smbc_getFunctionClose) {
close_fn(ctx, self.fd);
}
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct SmbOpenOptions {
flags: c_int,
read: bool,
write: bool,
pub(crate) mode: mode_t,
}
impl Default for SmbOpenOptions {
fn default() -> Self {
Self {
flags: 0,
read: false,
write: false,
mode: 0o644,
}
}
}
impl SmbOpenOptions {
pub fn read(mut self, read: bool) -> Self {
self.read = read;
self
}
pub fn write(mut self, write: bool) -> Self {
self.write = write;
self
}
pub fn append(mut self, append: bool) -> Self {
self.flag(libc::O_APPEND, append);
self
}
pub fn create(mut self, create: bool) -> Self {
self.flag(libc::O_CREAT, create);
self
}
pub fn truncate(mut self, truncate: bool) -> Self {
self.flag(libc::O_TRUNC, truncate);
self
}
pub fn exclusive(mut self, exclusive: bool) -> Self {
self.flag(libc::O_EXCL, exclusive);
self
}
pub fn mode(mut self, mode: mode_t) -> Self {
self.mode = mode;
self
}
pub(crate) fn to_flags(self) -> c_int {
let base_mode = match (self.read, self.write) {
(false, false) | (true, false) => libc::O_RDONLY,
(false, true) => libc::O_WRONLY,
(true, true) => libc::O_RDWR,
};
base_mode | self.flags
}
fn flag(&mut self, flag: c_int, on: bool) {
if on {
self.flags |= flag;
} else {
self.flags &= !flag;
}
}
}
#[cfg(test)]
mod test {
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn should_initialize_open_options() {
let open_opts = SmbOpenOptions::default();
assert_eq!(open_opts.read, false);
assert_eq!(open_opts.write, false);
assert_eq!(open_opts.mode, 0o644);
assert_eq!(open_opts.to_flags(), 0);
}
#[test]
fn should_set_open_options() {
let open_opts = SmbOpenOptions::default()
.read(true)
.write(true)
.append(true)
.exclusive(true)
.create(true)
.truncate(true)
.mode(0o755);
assert_eq!(open_opts.read, true);
assert_eq!(open_opts.write, true);
assert_eq!(open_opts.mode, 0o755);
assert_eq!(
open_opts.to_flags(),
libc::O_RDWR | libc::O_TRUNC | libc::O_APPEND | libc::O_EXCL | libc::O_CREAT
);
}
}