use crate::*;
use core::mem;
use ntapi::ntioapi::*;
use winapi::shared::minwindef::MAX_PATH;
use winapi::shared::ntdef::{PVOID, TRUE};
#[derive(Clone)]
#[cfg_attr(any(feature = "std", test), derive(Debug))]
pub struct File(Handle);
impl From<Handle> for File {
fn from(handle: Handle) -> Self {
Self(handle)
}
}
impl File {
pub fn create_new(name: &NtString) -> Result<Self> {
let handle = NewHandle::create_new(name)?;
Ok(Self(handle))
}
pub fn create_preallocated(name: &NtString, size: u64) -> Result<Self> {
let handle = NewHandle::create_new(name)?;
let file = Self(handle);
file.set_end_of_file(size)?;
Ok(file)
}
pub fn open(name: &NtString) -> Result<Self> {
let handle = NewHandle::open(name)?;
Ok(Self(handle))
}
pub fn open_readonly(name: &NtString) -> Result<Self> {
let handle = NewHandle::open_readonly(name)?;
Ok(Self(handle))
}
pub fn open_or_create(name: &NtString) -> Result<(Self, bool)> {
let (handle, already_exists) = NewHandle::open_or_create(name)?;
Ok((Self(handle), already_exists))
}
pub fn owerwrite(name: &NtString) -> Result<Self> {
let handle = NewHandle::owerwrite(name)?;
Ok(Self(handle))
}
pub fn owerwrite_or_create(name: &NtString) -> Result<(Self, bool)> {
let (handle, already_exists) = NewHandle::owerwrite_or_create(name)?;
Ok((Self(handle), already_exists))
}
}
impl File {
pub fn is_valid(&self) -> bool {
self.0.is_valid()
}
pub fn close(&mut self) -> Result<()> {
self.0.close()
}
pub fn object_name(&self) -> Result<NtString> {
self.0.object_name()
}
}
impl File {
unsafe fn query_info<T: AsByteSliceMut>(&self, class: FILE_INFORMATION_CLASS, buffer: &mut T) -> Result<()> {
let mut iosb: IO_STATUS_BLOCK = mem::zeroed();
let bytes_buffer = buffer.as_byte_slice_mut();
let bytes_len = bytes_buffer.len();
let status = NtQueryInformationFile(
self.0.as_raw(),
&mut iosb,
bytes_buffer.as_mut_ptr() as PVOID,
bytes_len as u32,
class,
);
nt_result!(status, ())
}
unsafe fn set_info<T: AsByteSlice>(&self, class: FILE_INFORMATION_CLASS, info: &T) -> Result<()> {
let mut iosb: IO_STATUS_BLOCK = mem::zeroed();
let bytes_buffer = info.as_byte_slice();
let bytes_len = bytes_buffer.len();
let status = NtSetInformationFile(self.0.as_raw(), &mut iosb, bytes_buffer.as_ptr() as PVOID, bytes_len as u32, class);
nt_result!(status, ())
}
pub fn pos(&self) -> Result<u64> {
unsafe {
let mut info = StructBuffer::<FILE_POSITION_INFORMATION>::new();
self.query_info(FilePositionInformation, &mut info)?;
Ok(*info.CurrentByteOffset.QuadPart() as u64)
}
}
pub fn set_pos(&self, pos: u64) -> Result<u64> {
unsafe {
let mut info = StructBuffer::<FILE_POSITION_INFORMATION>::zeroed();
*info.CurrentByteOffset.QuadPart_mut() = pos as i64;
self.set_info(FilePositionInformation, &info)?;
}
Ok(pos as u64)
}
pub fn size(&self) -> Result<u64> {
unsafe {
let mut info = StructBuffer::<FILE_STANDARD_INFORMATION>::new();
self.query_info(FileStandardInformation, &mut info)?;
Ok(*info.EndOfFile.QuadPart() as u64)
}
}
pub fn size_on_disk(&self) -> Result<u64> {
unsafe {
let mut info = StructBuffer::<FILE_STANDARD_INFORMATION>::new();
self.query_info(FileStandardInformation, &mut info)?;
Ok(*info.AllocationSize.QuadPart() as u64)
}
}
pub fn access_mask(&self) -> Result<Access> {
unsafe {
let mut info = StructBuffer::<FILE_ACCESS_INFORMATION>::new();
self.query_info(FileAccessInformation, &mut info)?;
Ok(Access::from_bits_unchecked(info.AccessFlags))
}
}
pub fn alignment(&self) -> Result<usize> {
unsafe {
let mut info = StructBuffer::<FILE_ALIGNMENT_INFORMATION>::new();
self.query_info(FileAlignmentInformation, &mut info)?;
Ok(info.AlignmentRequirement as usize)
}
}
pub fn mode(&self) -> Result<Options> {
unsafe {
let mut info = StructBuffer::<FILE_MODE_INFORMATION>::new();
self.query_info(FileModeInformation, &mut info)?;
Ok(Options::from_bits_unchecked(info.Mode))
}
}
pub fn is_remote(&self) -> Result<bool> {
unsafe {
let mut info = StructBuffer::<FILE_IS_REMOTE_DEVICE_INFORMATION>::new();
self.query_info(FileIsRemoteDeviceInformation, &mut info)?;
Ok(info.IsRemote == TRUE)
}
}
pub fn path_name(&self) -> Result<NtString> {
unsafe {
let mut info = StructBuffer::<FILE_NAME_INFORMATION>::with_ext(MAX_PATH * U16_SIZE);
self.query_info(FileNameInformation, &mut info)?;
Ok(NtString::from_raw_bytes(info.FileName.as_ptr(), info.FileNameLength))
}
}
pub fn set_end_of_file(&self, end_of_file: u64) -> Result<()> {
unsafe {
let mut info = StructBuffer::<FILE_END_OF_FILE_INFORMATION>::zeroed();
*info.EndOfFile.QuadPart_mut() = end_of_file as i64;
self.set_info(FileEndOfFileInformation, &info)
}
}
}
impl Read for File {
fn read(&self, buffer: &mut [u8]) -> Result<usize> {
self.0.read(buffer, None)
}
}
impl ReadAt for File {
fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<usize> {
self.0.read(buffer, Some(offset))
}
}
impl Flush for File {
fn flush(&self) -> Result<()> {
self.0.flush()
}
}
impl Size for File {
fn size(&self) -> Result<u64> {
File::size(self)
}
}
impl Write for File {
fn write(&self, data: &[u8]) -> Result<usize> {
self.0.write(data, None)
}
}
impl WriteAt for File {
fn write_at(&self, offset: u64, data: &[u8]) -> Result<usize> {
self.0.write(data, Some(offset))
}
}
impl Seek for File {
fn seek(&self, to: SeekFrom) -> Result<u64> {
let (mut pos, offset) = match to {
SeekFrom::Start(s) => (s as i64, 0),
SeekFrom::End(e) => (self.size()? as i64, e),
SeekFrom::Current(c) => (self.pos()? as i64, c),
};
pos += offset;
self.set_pos(pos as u64)
}
fn stream_position(&self) -> Result<u64> {
self.pos()
}
fn stream_len(&self) -> Result<u64> {
self.size()
}
}
#[cfg(feature = "std")]
mod std_impl {
use super::*;
use std::io;
use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
impl FromRawHandle for File {
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
Self::from(Handle::from_raw_handle(handle))
}
}
impl AsRawHandle for File {
fn as_raw_handle(&self) -> RawHandle {
self.0.as_raw_handle()
}
}
impl IntoRawHandle for File {
fn into_raw_handle(self) -> RawHandle {
self.0.into_raw_handle()
}
}
impl io::Read for File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(self as &dyn crate::Read).read(buf).map_err(Into::into)
}
}
impl io::Write for File {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(self as &dyn crate::Write).write(buf).map_err(Into::into)
}
fn flush(&mut self) -> io::Result<()> {
(self as &dyn crate::Write).flush().map_err(Into::into)
}
}
impl io::Seek for File {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
(self as &dyn crate::Seek).seek(pos.into()).map_err(Into::into)
}
}
}
#[cfg(feature = "std")]
pub use std_impl::*;