Compare commits

...

2 commits

View file

@ -197,8 +197,20 @@ fn thread_fn(
} }
gop.value_ptr.* = ot; gop.value_ptr.* = ot;
} }
// When a directory is renamed, scan its children so that
// subsequent delete events for contents can resolve their type.
if (ot == .dir and info.Action == FILE_ACTION_RENAMED_NEW_NAME)
scan_path_types_into(allocator, path_types, full_path);
break :blk ot; break :blk ot;
}; };
// Suppress FILE_ACTION_MODIFIED on directories: these are
// parent-directory mtime side-effects of child operations
// (delete, rename) and are not emitted by any other backend.
if (event_type == .modified and object_type == .dir) {
if (info.NextEntryOffset == 0) break;
offset += info.NextEntryOffset;
continue;
}
// Capture next_entry_offset before releasing the mutex: after unlock, // Capture next_entry_offset before releasing the mutex: after unlock,
// the main thread may call remove_watch() which frees w.buf, making // the main thread may call remove_watch() which frees w.buf, making
// the `info` pointer (which points into w.buf) a dangling reference. // the `info` pointer (which points into w.buf) a dangling reference.
@ -260,9 +272,14 @@ pub fn add_watch(self: *@This(), allocator: std.mem.Allocator, path: []const u8)
} }
// Walk root recursively and seed path_types with the type of every entry. // Walk root recursively and seed path_types with the type of every entry.
// Called from add_watch (mutex already held) so pre-existing paths are // Called from add_watch and from thread_fn on directory renames so that
// known before any FILE_ACTION_REMOVED or FILE_ACTION_RENAMED_OLD_NAME fires. // FILE_ACTION_REMOVED events for children of a renamed directory can
// resolve their ObjectType from cache.
fn scan_path_types(self: *@This(), allocator: std.mem.Allocator, root: []const u8) void { fn scan_path_types(self: *@This(), allocator: std.mem.Allocator, root: []const u8) void {
scan_path_types_into(allocator, &self.path_types, root);
}
fn scan_path_types_into(allocator: std.mem.Allocator, path_types: *std.StringHashMapUnmanaged(ObjectType), root: []const u8) void {
var dir = std.fs.openDirAbsolute(root, .{ .iterate = true }) catch return; var dir = std.fs.openDirAbsolute(root, .{ .iterate = true }) catch return;
defer dir.close(); defer dir.close();
var iter = dir.iterate(); var iter = dir.iterate();
@ -274,15 +291,15 @@ fn scan_path_types(self: *@This(), allocator: std.mem.Allocator, root: []const u
}; };
var path_buf: [std.fs.max_path_bytes]u8 = undefined; var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const full_path = std.fmt.bufPrint(&path_buf, "{s}\\{s}", .{ root, entry.name }) catch continue; const full_path = std.fmt.bufPrint(&path_buf, "{s}\\{s}", .{ root, entry.name }) catch continue;
const gop = self.path_types.getOrPut(allocator, full_path) catch continue; const gop = path_types.getOrPut(allocator, full_path) catch continue;
if (!gop.found_existing) { if (!gop.found_existing) {
gop.key_ptr.* = allocator.dupe(u8, full_path) catch { gop.key_ptr.* = allocator.dupe(u8, full_path) catch {
_ = self.path_types.remove(full_path); _ = path_types.remove(full_path);
continue; continue;
}; };
} }
gop.value_ptr.* = ot; gop.value_ptr.* = ot;
if (ot == .dir) self.scan_path_types(allocator, gop.key_ptr.*); if (ot == .dir) scan_path_types_into(allocator, path_types, gop.key_ptr.*);
} }
} }