Skip to content

Commit 54df206

Browse files
committed
Replace ReadDirectoryChangesW with ReadDirectoryChangesExW, out of which the FileAttributes can tell if a file or folder is being created/modified/deleted.
This raises the minimum required Windows version to Windows 10, as this is where ReadDirectoryChangesExW was introduced.
1 parent 539bc95 commit 54df206

File tree

1 file changed

+47
-40
lines changed

1 file changed

+47
-40
lines changed

notify/src/windows.rs

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ use windows_sys::Win32::Foundation::{
2424
INVALID_HANDLE_VALUE, WAIT_OBJECT_0,
2525
};
2626
use windows_sys::Win32::Storage::FileSystem::{
27-
CreateFileW, ReadDirectoryChangesW, FILE_ACTION_ADDED, FILE_ACTION_MODIFIED,
28-
FILE_ACTION_REMOVED, FILE_ACTION_RENAMED_NEW_NAME, FILE_ACTION_RENAMED_OLD_NAME,
29-
FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OVERLAPPED, FILE_LIST_DIRECTORY,
30-
FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_NOTIFY_CHANGE_CREATION, FILE_NOTIFY_CHANGE_DIR_NAME,
31-
FILE_NOTIFY_CHANGE_FILE_NAME, FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_SECURITY,
32-
FILE_NOTIFY_CHANGE_SIZE, FILE_NOTIFY_INFORMATION, FILE_SHARE_DELETE, FILE_SHARE_READ,
27+
CreateFileW, ReadDirectoryChangesExW, ReadDirectoryNotifyExtendedInformation,
28+
FILE_ACTION_ADDED, FILE_ACTION_MODIFIED, FILE_ACTION_REMOVED, FILE_ACTION_RENAMED_NEW_NAME,
29+
FILE_ACTION_RENAMED_OLD_NAME, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
30+
FILE_FLAG_OVERLAPPED, FILE_LIST_DIRECTORY, FILE_NOTIFY_CHANGE_ATTRIBUTES,
31+
FILE_NOTIFY_CHANGE_CREATION, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_NOTIFY_CHANGE_FILE_NAME,
32+
FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_NOTIFY_CHANGE_SECURITY, FILE_NOTIFY_CHANGE_SIZE,
33+
FILE_NOTIFY_EXTENDED_INFORMATION, FILE_NOTIFY_INFORMATION, FILE_SHARE_DELETE, FILE_SHARE_READ,
3334
FILE_SHARE_WRITE, OPEN_EXISTING,
3435
};
3536
use windows_sys::Win32::System::Threading::{
@@ -305,7 +306,7 @@ fn start_read(
305306

306307
// This is using an asynchronous call with a completion routine for receiving notifications
307308
// An I/O completion port would probably be more performant
308-
let ret = ReadDirectoryChangesW(
309+
let ret = ReadDirectoryChangesExW(
309310
handle,
310311
request.buffer.as_mut_ptr() as *mut c_void,
311312
BUF_SIZE,
@@ -314,6 +315,7 @@ fn start_read(
314315
&mut 0u32 as *mut u32, // not used for async reqs
315316
overlapped,
316317
Some(handle_event),
318+
ReadDirectoryNotifyExtendedInformation,
317319
);
318320

319321
if ret == 0 {
@@ -382,7 +384,7 @@ unsafe extern "system" fn handle_event(
382384
// In Wine, FILE_NOTIFY_INFORMATION structs are packed placed in the buffer;
383385
// they are aligned to 16bit (WCHAR) boundary instead of 32bit required by FILE_NOTIFY_INFORMATION.
384386
// Hence, we need to use `read_unaligned` here to avoid UB.
385-
let mut cur_entry = ptr::read_unaligned(cur_offset as *const FILE_NOTIFY_INFORMATION);
387+
let mut cur_entry = ptr::read_unaligned(cur_offset as *const FILE_NOTIFY_EXTENDED_INFORMATION);
386388
loop {
387389
// filename length is size in bytes, so / 2
388390
let len = cur_entry.FileNameLength as usize / 2;
@@ -422,44 +424,49 @@ unsafe extern "system" fn handle_event(
422424

423425
let event_handler = |res| emit_event(&request.event_handler, res);
424426

425-
if cur_entry.Action == FILE_ACTION_RENAMED_OLD_NAME {
426-
let mode = RenameMode::From;
427-
let kind = ModifyKind::Name(mode);
428-
let kind = EventKind::Modify(kind);
429-
let ev = newe.set_kind(kind);
430-
event_handler(Ok(ev))
431-
} else {
432-
match cur_entry.Action {
433-
FILE_ACTION_RENAMED_NEW_NAME => {
434-
let kind = EventKind::Modify(ModifyKind::Name(RenameMode::To));
435-
let ev = newe.set_kind(kind);
436-
event_handler(Ok(ev));
437-
}
438-
FILE_ACTION_ADDED => {
439-
let kind = EventKind::Create(CreateKind::Any);
440-
let ev = newe.set_kind(kind);
441-
event_handler(Ok(ev));
442-
}
443-
FILE_ACTION_REMOVED => {
444-
let kind = EventKind::Remove(RemoveKind::Any);
445-
let ev = newe.set_kind(kind);
446-
event_handler(Ok(ev));
447-
}
448-
FILE_ACTION_MODIFIED => {
449-
let kind = EventKind::Modify(ModifyKind::Any);
450-
let ev = newe.set_kind(kind);
451-
event_handler(Ok(ev));
452-
}
453-
_ => (),
454-
};
455-
}
427+
match cur_entry.Action {
428+
FILE_ACTION_RENAMED_OLD_NAME => {
429+
let kind = EventKind::Modify(ModifyKind::Name(RenameMode::From));
430+
let ev = newe.set_kind(kind);
431+
event_handler(Ok(ev))
432+
}
433+
FILE_ACTION_RENAMED_NEW_NAME => {
434+
let kind = EventKind::Modify(ModifyKind::Name(RenameMode::To));
435+
let ev = newe.set_kind(kind);
436+
event_handler(Ok(ev));
437+
}
438+
FILE_ACTION_ADDED => {
439+
let kind = if (cur_entry.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 {
440+
EventKind::Create(CreateKind::Folder)
441+
} else {
442+
EventKind::Create(CreateKind::File)
443+
};
444+
let ev = newe.set_kind(kind);
445+
event_handler(Ok(ev));
446+
}
447+
FILE_ACTION_REMOVED => {
448+
let kind = if (cur_entry.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 {
449+
EventKind::Remove(RemoveKind::Folder)
450+
} else {
451+
EventKind::Remove(RemoveKind::File)
452+
};
453+
let ev = newe.set_kind(kind);
454+
event_handler(Ok(ev));
455+
}
456+
FILE_ACTION_MODIFIED => {
457+
let kind = EventKind::Modify(ModifyKind::Any);
458+
let ev = newe.set_kind(kind);
459+
event_handler(Ok(ev));
460+
}
461+
_ => (),
462+
};
456463
}
457464

458465
if cur_entry.NextEntryOffset == 0 {
459466
break;
460467
}
461468
cur_offset = cur_offset.offset(cur_entry.NextEntryOffset as isize);
462-
cur_entry = ptr::read_unaligned(cur_offset as *const FILE_NOTIFY_INFORMATION);
469+
cur_entry = ptr::read_unaligned(cur_offset as *const FILE_NOTIFY_EXTENDED_INFORMATION);
463470
}
464471
}
465472

0 commit comments

Comments
 (0)