Skip to main content

hdiff_update_core/
fs_ops.rs

1use std::path::Path;
2
3#[cfg(not(windows))]
4use std::fs;
5
6use crate::{error::io_path, Error, Result};
7
8#[cfg(windows)]
9use std::{ffi::OsStr, os::windows::ffi::OsStrExt};
10
11#[cfg(windows)]
12use windows_sys::Win32::Storage::FileSystem::{
13    MoveFileExW, MOVEFILE_REPLACE_EXISTING, MOVEFILE_WRITE_THROUGH,
14};
15
16pub(crate) fn replace_file(source: &Path, destination: &Path) -> Result<()> {
17    #[cfg(windows)]
18    {
19        let source_wide = wide_null(source.as_os_str()).map_err(|error| io_path(source, error))?;
20        let destination_wide =
21            wide_null(destination.as_os_str()).map_err(|error| io_path(destination, error))?;
22        let result = unsafe {
23            MoveFileExW(
24                source_wide.as_ptr(),
25                destination_wide.as_ptr(),
26                MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH,
27            )
28        };
29        if result == 0 {
30            return Err(io_path(destination, std::io::Error::last_os_error()));
31        }
32        Ok(())
33    }
34
35    #[cfg(not(windows))]
36    {
37        fs::rename(source, destination).map_err(|error| io_path(destination, error))
38    }
39}
40
41pub fn ensure_available_space(path: &Path, required: u64) -> Result<u64> {
42    let available = fs2::available_space(path).map_err(|error| io_path(path, error))?;
43    if available < required {
44        return Err(Error::InsufficientDiskSpace {
45            path: path.to_path_buf(),
46            required,
47            available,
48        });
49    }
50    Ok(available)
51}
52
53#[cfg(windows)]
54fn wide_null(value: &OsStr) -> std::io::Result<Vec<u16>> {
55    let mut wide = Vec::new();
56    for unit in value.encode_wide() {
57        if unit == 0 {
58            return Err(std::io::Error::new(
59                std::io::ErrorKind::InvalidInput,
60                "path contains an embedded null character",
61            ));
62        }
63        wide.push(unit);
64    }
65    wide.push(0);
66    Ok(wide)
67}
68
69#[cfg(test)]
70mod tests {
71    use tempfile::tempdir;
72
73    use super::ensure_available_space;
74    use crate::Error;
75
76    #[test]
77    fn disk_preflight_rejects_impossible_requirements() {
78        let dir = tempdir().unwrap();
79        let error = ensure_available_space(dir.path(), u64::MAX).unwrap_err();
80        assert!(matches!(error, Error::InsufficientDiskSpace { .. }));
81    }
82}