use super::*;
#[cfg(not(feature = "async"))]
use std::io::prelude::*;
use std::ops::{Deref, DerefMut};
pub struct File {
handle: ResourceHandle,
#[cfg(not(feature = "async"))]
pos: u64,
#[cfg(not(feature = "async"))]
dirty: bool,
access: FileAccessMask,
end_of_file: u64,
}
impl File {
pub fn new(handle: ResourceHandle, access: FileAccessMask, end_of_file: u64) -> Self {
File {
handle,
access,
end_of_file,
#[cfg(not(feature = "async"))]
pos: 0,
#[cfg(not(feature = "async"))]
dirty: false,
}
}
pub fn end_of_file(&self) -> u64 {
self.end_of_file
}
pub fn access(&self) -> FileAccessMask {
self.access
}
#[maybe_async]
pub async fn read_block(
&self,
buf: &mut [u8],
pos: u64,
unbuffered: bool,
) -> std::io::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
if !self.access.file_read_data() {
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"No read permission",
));
}
if pos >= self.end_of_file {
return Ok(0);
}
log::debug!(
"Reading up to {} bytes at offset {} from {}",
buf.len(),
pos,
self.handle.name()
);
let mut flags = ReadFlags::new();
if self.handle.conn_info.config.compression_enabled
&& self.handle.conn_info.dialect.supports_compression()
{
flags.set_read_compressed(true);
}
if unbuffered && self.handle.conn_info.negotiation.dialect_rev >= Dialect::Smb0302 {
flags.set_read_unbuffered(true);
}
let response = self
.handle
.send_receive(Content::ReadRequest(ReadRequest {
padding: 0,
flags,
length: buf.len() as u32,
offset: pos,
file_id: self.handle.file_id,
minimum_count: 1,
}))
.await
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
let content = response
.message
.content
.to_readresponse()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
let actual_read_length = content.buffer.len();
log::debug!(
"Read {} bytes from {}.",
actual_read_length,
self.handle.name()
);
buf[..actual_read_length].copy_from_slice(&content.buffer);
Ok(actual_read_length)
}
#[maybe_async]
pub async fn write_block(&self, buf: &[u8], pos: u64) -> std::io::Result<usize> {
if buf.is_empty() {
return Ok(0);
}
if !self.access.file_write_data() {
return Err(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"No write permission",
));
}
log::debug!(
"Writing {} bytes at offset {} to {}",
buf.len(),
pos,
self.handle.name()
);
let response = self
.handle
.send_receive(Content::WriteRequest(WriteRequest {
offset: pos,
file_id: self.handle.file_id,
flags: WriteFlags::new(),
buffer: buf.to_vec(),
}))
.await
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
let content = response
.message
.content
.to_writeresponse()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
let actual_written_length = content.count as usize;
log::debug!(
"Wrote {} bytes to {}.",
actual_written_length,
self.handle.name()
);
Ok(actual_written_length)
}
#[maybe_async]
pub async fn flush(&self) -> std::io::Result<()> {
let _response = self
.handle
.send_receive(Content::FlushRequest(FlushRequest {
file_id: self.handle.file_id,
}))
.await
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
log::debug!("Flushed {}.", self.handle.name());
Ok(())
}
}
#[cfg(not(feature = "async"))]
impl Seek for File {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
let next_pos = match pos {
std::io::SeekFrom::Start(pos) => pos,
std::io::SeekFrom::End(pos) => {
let pos = self.end_of_file as i64 + pos;
if pos < 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Invalid seek position",
));
}
pos.try_into().map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid seek position")
})?
}
std::io::SeekFrom::Current(pos) => {
let pos = self.pos as i64 + pos;
if pos < 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Invalid seek position",
));
}
pos.try_into().map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "Invalid seek position")
})?
}
};
if next_pos > self.end_of_file {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Invalid seek position",
));
}
Ok(self.pos)
}
}
#[cfg(not(feature = "async"))]
impl Read for File {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let read_length = File::read_block(self, buf, self.pos, false)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
self.pos += read_length as u64;
Ok(read_length)
}
}
#[cfg(not(feature = "async"))]
impl Write for File {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let written_length = File::write_block(self, buf, self.pos)?;
self.pos += written_length as u64;
self.dirty = true;
Ok(written_length)
}
fn flush(&mut self) -> std::io::Result<()> {
if !self.dirty {
return Ok(());
}
File::flush(self)
}
}
impl Deref for File {
type Target = ResourceHandle;
fn deref(&self) -> &Self::Target {
&self.handle
}
}
impl DerefMut for File {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.handle
}
}