#![cfg(target_os = "windows")]
#![allow(unsafe_code)]
use std::ffi::OsStr;
use std::io::{self, Read, Write};
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use windows::core::PCWSTR;
use windows::Win32::Foundation::{CloseHandle, GENERIC_READ, GENERIC_WRITE, HANDLE, LUID};
use windows::Win32::Security::{
AdjustTokenPrivileges, LookupPrivilegeValueW, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED,
TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,
};
use windows::Win32::Storage::FileSystem::{
BackupRead, BackupWrite, CreateFileW, FILE_ATTRIBUTE_NORMAL, FILE_FLAG_BACKUP_SEMANTICS,
FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
};
use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
pub fn enable_backup_restore_privileges() -> io::Result<()> {
unsafe {
let mut token = HANDLE::default();
OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token,
)
.map_err(|e| io::Error::other(format!("OpenProcessToken: {e}")))?;
let backup_res = enable_named_privilege(token, "SeBackupPrivilege");
let restore_res = enable_named_privilege(token, "SeRestorePrivilege");
if let Err(e) = CloseHandle(token) {
tracing::debug!("CloseHandle(token) failed: {e}");
}
backup_res?;
restore_res?;
}
Ok(())
}
unsafe fn enable_named_privilege(token: HANDLE, name: &str) -> io::Result<()> {
let name_w: Vec<u16> = OsStr::new(name)
.encode_wide()
.chain(std::iter::once(0))
.collect();
let mut luid = LUID::default();
unsafe {
LookupPrivilegeValueW(PCWSTR::null(), PCWSTR::from_raw(name_w.as_ptr()), &mut luid)
.map_err(|e| io::Error::other(format!("LookupPrivilegeValueW({name}): {e}")))?;
let privs = TOKEN_PRIVILEGES {
PrivilegeCount: 1,
Privileges: [LUID_AND_ATTRIBUTES {
Luid: luid,
Attributes: SE_PRIVILEGE_ENABLED,
}],
};
AdjustTokenPrivileges(token, false, Some(&raw const privs), 0, None, None)
.map_err(|e| io::Error::other(format!("AdjustTokenPrivileges({name}): {e}")))?;
}
Ok(())
}
pub struct BackupStreamWriter {
handle: HANDLE,
context: *mut core::ffi::c_void,
}
unsafe impl Send for BackupStreamWriter {}
impl BackupStreamWriter {
pub fn create(path: &Path) -> io::Result<Self> {
let wide: Vec<u16> = path
.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect();
let handle = unsafe {
CreateFileW(
PCWSTR::from_raw(wide.as_ptr()),
GENERIC_WRITE.0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
None,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
None,
)
}
.map_err(|e| io::Error::other(format!("CreateFileW: {e}")))?;
Ok(Self {
handle,
context: std::ptr::null_mut(),
})
}
pub fn create_new(path: &Path) -> io::Result<Self> {
drop(std::fs::File::create(path)?);
Self::create(path)
}
}
impl Write for BackupStreamWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut bytes_written: u32 = 0;
unsafe {
BackupWrite(
self.handle,
buf,
&mut bytes_written,
false,
false,
&mut self.context,
)
}
.map_err(|e| io::Error::other(format!("BackupWrite: {e}")))?;
Ok(bytes_written as usize)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Drop for BackupStreamWriter {
fn drop(&mut self) {
let mut bytes_written: u32 = 0;
unsafe {
let _ = BackupWrite(
self.handle,
&[],
&mut bytes_written,
true,
false,
&mut self.context,
);
let _ = CloseHandle(self.handle);
}
}
}
pub struct BackupStreamReader {
handle: HANDLE,
context: *mut core::ffi::c_void,
}
unsafe impl Send for BackupStreamReader {}
impl BackupStreamReader {
pub fn open(path: &Path) -> io::Result<Self> {
let wide: Vec<u16> = path
.as_os_str()
.encode_wide()
.chain(std::iter::once(0))
.collect();
let handle = unsafe {
CreateFileW(
PCWSTR::from_raw(wide.as_ptr()),
GENERIC_READ.0,
FILE_SHARE_READ,
None,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
None,
)
}
.map_err(|e| io::Error::other(format!("CreateFileW: {e}")))?;
Ok(Self {
handle,
context: std::ptr::null_mut(),
})
}
}
impl Read for BackupStreamReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let mut bytes_read: u32 = 0;
unsafe {
BackupRead(
self.handle,
buf,
&mut bytes_read,
false,
false,
&mut self.context,
)
}
.map_err(|e| io::Error::other(format!("BackupRead: {e}")))?;
Ok(bytes_read as usize)
}
}
impl Drop for BackupStreamReader {
fn drop(&mut self) {
let mut bytes_read: u32 = 0;
let mut scratch: [u8; 1] = [0];
unsafe {
let _ = BackupRead(
self.handle,
&mut scratch[..0],
&mut bytes_read,
true,
false,
&mut self.context,
);
let _ = CloseHandle(self.handle);
}
}
}