mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(env): Windows watcher identity from file indexes
This commit is contained in:
parent
956e81504c
commit
97a600395e
3 changed files with 67 additions and 14 deletions
5
packages/env/daemon/src/sys/unix.rs
vendored
5
packages/env/daemon/src/sys/unix.rs
vendored
|
|
@ -316,3 +316,8 @@ pub fn drive_cwds() -> Map<String, Value> {
|
|||
pub fn read_dir(path: &str) -> io::Result<std::fs::ReadDir> {
|
||||
std::fs::read_dir(path)
|
||||
}
|
||||
|
||||
/// Device and inode of `path`; the metadata already has them.
|
||||
pub fn path_identity(_path: &str, metadata: &Metadata, _follow: bool) -> (u64, u64) {
|
||||
identity(metadata)
|
||||
}
|
||||
|
|
|
|||
51
packages/env/daemon/src/sys/windows.rs
vendored
51
packages/env/daemon/src/sys/windows.rs
vendored
|
|
@ -17,11 +17,12 @@ use std::process::{Child, Command, ExitStatus, Stdio};
|
|||
use std::sync::OnceLock;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::time::UNIX_EPOCH;
|
||||
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
|
||||
use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE};
|
||||
use windows_sys::Win32::Storage::FileSystem::{
|
||||
CREATE_ALWAYS, CreateFileW, FILE_APPEND_DATA, FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_SHARE_DELETE,
|
||||
FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_WRITE_DATA, MOVEFILE_REPLACE_EXISTING, MoveFileExW,
|
||||
BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CreateFileW, FILE_APPEND_DATA,
|
||||
FILE_ATTRIBUTE_NORMAL, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
|
||||
FILE_WRITE_DATA, GetFileInformationByHandle, MOVEFILE_REPLACE_EXISTING, MoveFileExW,
|
||||
OPEN_ALWAYS, OPEN_EXISTING,
|
||||
};
|
||||
use windows_sys::Win32::System::IO::CancelSynchronousIo;
|
||||
|
|
@ -602,6 +603,48 @@ pub fn read_dir(path: &str) -> io::Result<std::fs::ReadDir> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Volume serial number and file index of `path`, as libuv's `stat` reports `dev` and `ino`: the creation time is not
|
||||
/// enough, because NTFS gives a file created under a recently removed name the old one's creation time ("tunneling").
|
||||
/// Opens a handle, like libuv; falls back to the metadata when the path cannot be opened.
|
||||
pub fn path_identity(path: &str, metadata: &Metadata, follow: bool) -> (u64, u64) {
|
||||
let Ok(name) = wide(path) else {
|
||||
return identity(metadata);
|
||||
};
|
||||
let flags = FILE_FLAG_BACKUP_SEMANTICS
|
||||
| if follow {
|
||||
0
|
||||
} else {
|
||||
FILE_FLAG_OPEN_REPARSE_POINT
|
||||
};
|
||||
// SAFETY: a NUL-terminated wide path; the handle is closed below.
|
||||
let handle = unsafe {
|
||||
CreateFileW(
|
||||
name.as_ptr(),
|
||||
0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
std::ptr::null(),
|
||||
OPEN_EXISTING,
|
||||
flags,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if handle == INVALID_HANDLE_VALUE {
|
||||
return identity(metadata);
|
||||
}
|
||||
// SAFETY: a valid handle and a zeroed buffer for the call to fill.
|
||||
let mut information: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() };
|
||||
let found = unsafe { GetFileInformationByHandle(handle, &mut information) } != 0;
|
||||
// SAFETY: the handle opened above.
|
||||
unsafe { CloseHandle(handle) };
|
||||
if !found {
|
||||
return identity(metadata);
|
||||
}
|
||||
(
|
||||
u64::from(information.dwVolumeSerialNumber),
|
||||
(u64::from(information.nFileIndexHigh) << 32) | u64::from(information.nFileIndexLow),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::quote_argument;
|
||||
|
|
|
|||
25
packages/env/daemon/src/watch.rs
vendored
25
packages/env/daemon/src/watch.rs
vendored
|
|
@ -119,9 +119,9 @@ fn denied(error: &io::Error) -> bool {
|
|||
error.kind() == io::ErrorKind::PermissionDenied
|
||||
}
|
||||
|
||||
fn entry_of(metadata: &Metadata, hash: Option<u64>) -> Entry {
|
||||
fn kind_of(metadata: &Metadata) -> Kind {
|
||||
let file_type = metadata.file_type();
|
||||
let kind = if file_type.is_file() {
|
||||
if file_type.is_file() {
|
||||
Kind::File
|
||||
} else if file_type.is_dir() {
|
||||
Kind::Directory
|
||||
|
|
@ -129,8 +129,13 @@ fn entry_of(metadata: &Metadata, hash: Option<u64>) -> Entry {
|
|||
Kind::Symlink
|
||||
} else {
|
||||
Kind::Other
|
||||
};
|
||||
let (dev, ino) = sys::identity(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/// What a snapshot remembers of `path`, whose `metadata` came from `stat` (`follow`) or `lstat`.
|
||||
fn entry_of(path: &str, metadata: &Metadata, follow: bool, hash: Option<u64>) -> Entry {
|
||||
let kind = kind_of(metadata);
|
||||
let (dev, ino) = sys::path_identity(path, metadata, follow);
|
||||
let directory = kind == Kind::Directory;
|
||||
Entry {
|
||||
kind,
|
||||
|
|
@ -250,7 +255,7 @@ impl FileWatcher<'_> {
|
|||
self.control.aborted.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
fn record(&self, scan: &mut Scan, path: &str, metadata: &Metadata) {
|
||||
fn record(&self, scan: &mut Scan, path: &str, metadata: &Metadata, follow: bool) {
|
||||
let mut hash = None;
|
||||
let recent = metadata
|
||||
.modified()
|
||||
|
|
@ -272,7 +277,7 @@ impl FileWatcher<'_> {
|
|||
});
|
||||
}
|
||||
scan.snapshot
|
||||
.insert(path.to_string(), entry_of(metadata, hash));
|
||||
.insert(path.to_string(), entry_of(path, metadata, follow, hash));
|
||||
}
|
||||
|
||||
fn count(&self, scan: &mut Scan, path: &str) -> Result<(), ScanError> {
|
||||
|
|
@ -329,11 +334,11 @@ impl FileWatcher<'_> {
|
|||
let Ok(metadata) = fs::symlink_metadata(&path) else {
|
||||
continue;
|
||||
};
|
||||
let kind = entry_of(&metadata, None).kind;
|
||||
let kind = kind_of(&metadata);
|
||||
scan.listed.insert(path.clone(), kind);
|
||||
// A target's own entry (following links) wins over its listing by another target.
|
||||
if !scan.snapshot.contains_key(&path) {
|
||||
self.record(scan, &path, &metadata);
|
||||
self.record(scan, &path, &metadata, false);
|
||||
}
|
||||
kind
|
||||
}
|
||||
|
|
@ -364,7 +369,7 @@ impl FileWatcher<'_> {
|
|||
let entry = Entry {
|
||||
size: 0,
|
||||
mtime: (0, 0),
|
||||
..entry_of(&metadata, None)
|
||||
..entry_of(&ancestor, &metadata, false, None)
|
||||
};
|
||||
scan.snapshot.insert(ancestor, entry);
|
||||
}
|
||||
|
|
@ -378,7 +383,7 @@ impl FileWatcher<'_> {
|
|||
}
|
||||
Err(_) => continue,
|
||||
};
|
||||
self.record(&mut scan, &target.path, &metadata);
|
||||
self.record(&mut scan, &target.path, &metadata, true);
|
||||
if metadata.is_file()
|
||||
&& fs::symlink_metadata(&target.path)
|
||||
.is_ok_and(|link| link.file_type().is_symlink())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue