diff --git a/apps/locallibrary/changes/loc-255-disk-image-windows.md b/apps/locallibrary/changes/loc-255-disk-image-windows.md new file mode 100644 index 000000000..99b76631e --- /dev/null +++ b/apps/locallibrary/changes/loc-255-disk-image-windows.md @@ -0,0 +1,3 @@ +### Fixed + +- `format` to FAT32 on Windows no longer fails with "The device is not ready" after writing the new file system. diff --git a/crates/locallibrary/src/format/tests/disk_image.rs b/crates/locallibrary/src/format/tests/disk_image.rs index 3df81d498..d56fad4fd 100644 --- a/crates/locallibrary/src/format/tests/disk_image.rs +++ b/crates/locallibrary/src/format/tests/disk_image.rs @@ -235,7 +235,12 @@ mod os { format!(r"\\.\PhysicalDrive{}", n.trim()) } + /// Windows keeps an attached image's file open, so one that's gone isn't attached. Asked about + /// a file that's gone, `Get-DiskImage` fails even with `SilentlyContinue`. pub fn attached(file: &Path) -> Option { + if !file.exists() { + return None; + } let path = quote(&file.to_string_lossy()); let n = powershell(&format!( "$i = Get-DiskImage -ImagePath {path} -ErrorAction SilentlyContinue\nif ($i.Attached) {{ $i.Number }}" diff --git a/crates/locallibrary/src/format/windows.rs b/crates/locallibrary/src/format/windows.rs index e1fe71788..ac9da95ff 100644 --- a/crates/locallibrary/src/format/windows.rs +++ b/crates/locallibrary/src/format/windows.rs @@ -236,12 +236,17 @@ pub fn format(disk: &Disk, t: &Target, ctx: &Ctx<'_>) -> Result> #[cfg(windows)] fn write_fat32(letter: char, layout: &fat32::Layout, label: &str, ctx: &Ctx<'_>) -> Result<()> { + use std::os::windows::fs::OpenOptionsExt; use std::os::windows::io::AsRawHandle; let path = format!(r"\\.\{letter}:"); + // FILE_FLAG_WRITE_THROUGH: each write reaches the disk before it returns. The volume can't be + // flushed once it's dismounted (that fails as "The device is not ready"), and writes to a + // volume handle skip the cache already, so this is all the flushing they need. let mut f = std::fs::OpenOptions::new() .read(true) .write(true) + .custom_flags(0x8000_0000) .open(&path) .map_err(|e| Error::Io { path: path.clone().into(), source: e })?; // Explorer and indexers open new volumes briefly; retry the lock for a few seconds. @@ -260,7 +265,6 @@ fn write_fat32(letter: char, layout: &fat32::Layout, label: &str, ctx: &Ctx<'_>) .map_err(|e| Error::Io { path: path.clone().into(), source: e })?; let volume_id = u32::from_le_bytes(uuid::Uuid::new_v4().as_bytes()[..4].try_into().expect("4 bytes")); fat32::write(&mut f, 0, layout, label, volume_id, ctx)?; - f.sync_all().map_err(|e| Error::Io { path: path.into(), source: e })?; // Closing the handle unlocks the volume; Windows mounts it as FAT32 on the next access. Ok(()) }