@@ -307,26 +307,82 @@ const Os = switch (builtin.os.tag) {
307
307
return self .overlapped .hEvent .? ;
308
308
}
309
309
310
- fn init (gpa : Allocator , handle : windows.HANDLE , id : FileId ) ! * @This () {
311
- const event = try windows .CreateEventExW (
310
+ fn init (gpa : Allocator , path : Cache.Path ) ! * @This () {
311
+ // The following code is a drawn out NtCreateFile call. (mostly adapted from std.fs.Dir.makeOpenDirAccessMaskW)
312
+ // It's necessary in order to get the flags are required when calling ReadDirectoryChangesW.
313
+ var dir_handle : windows.HANDLE = undefined ;
314
+ {
315
+ const root_fd = path .root_dir .handle .fd ;
316
+ const sub_path = path .subPathOrDot ();
317
+ const sub_path_w = try windows .sliceToPrefixedFileW (root_fd , sub_path );
318
+ const path_len_bytes = std .math .cast (u16 , sub_path_w .len * 2 ) orelse return error .NameTooLong ;
319
+
320
+ var nt_name = windows.UNICODE_STRING {
321
+ .Length = @intCast (path_len_bytes ),
322
+ .MaximumLength = @intCast (path_len_bytes ),
323
+ .Buffer = @constCast (sub_path_w .span ().ptr ),
324
+ };
325
+ var attr = windows.OBJECT_ATTRIBUTES {
326
+ .Length = @sizeOf (windows .OBJECT_ATTRIBUTES ),
327
+ .RootDirectory = if (std .fs .path .isAbsoluteWindowsW (sub_path_w .span ())) null else root_fd ,
328
+ .Attributes = 0 , // Note we do not use OBJ_CASE_INSENSITIVE here.
329
+ .ObjectName = & nt_name ,
330
+ .SecurityDescriptor = null ,
331
+ .SecurityQualityOfService = null ,
332
+ };
333
+ var io : windows.IO_STATUS_BLOCK = undefined ;
334
+ const rc = windows .ntdll .NtCreateFile (
335
+ & dir_handle ,
336
+ windows .SYNCHRONIZE | windows .GENERIC_READ | windows .FILE_LIST_DIRECTORY ,
337
+ & attr ,
338
+ & io ,
339
+ null ,
340
+ 0 ,
341
+ windows .FILE_SHARE_READ | windows .FILE_SHARE_WRITE | windows .FILE_SHARE_DELETE ,
342
+ windows .FILE_OPEN ,
343
+ windows .FILE_DIRECTORY_FILE | windows .FILE_OPEN_FOR_BACKUP_INTENT ,
344
+ null ,
345
+ 0 ,
346
+ );
347
+
348
+ switch (rc ) {
349
+ .SUCCESS = > {},
350
+ .OBJECT_NAME_INVALID = > return error .BadPathName ,
351
+ .OBJECT_NAME_NOT_FOUND = > return error .FileNotFound ,
352
+ .OBJECT_NAME_COLLISION = > return error .PathAlreadyExists ,
353
+ .OBJECT_PATH_NOT_FOUND = > return error .FileNotFound ,
354
+ .NOT_A_DIRECTORY = > return error .NotDir ,
355
+ // This can happen if the directory has 'List folder contents' permission set to 'Deny'
356
+ .ACCESS_DENIED = > return error .AccessDenied ,
357
+ .INVALID_PARAMETER = > unreachable ,
358
+ else = > return windows .unexpectedStatus (rc ),
359
+ }
360
+ }
361
+ assert (dir_handle != windows .INVALID_HANDLE_VALUE );
362
+ errdefer windows .CloseHandle (dir_handle );
363
+
364
+ const dir_id = try getFileId (dir_handle );
365
+
366
+ const wait_handle = try windows .CreateEventExW (
312
367
null ,
313
368
null ,
314
369
windows .CREATE_EVENT_MANUAL_RESET ,
315
370
windows .EVENT_ALL_ACCESS ,
316
371
);
372
+ errdefer windows .CloseHandle (wait_handle );
317
373
318
- const result = try gpa .create (@This ());
319
- result .* = .{
320
- .handle = handle ,
321
- .id = id ,
374
+ const dir_ptr = try gpa .create (@This ());
375
+ dir_ptr .* = .{
376
+ .handle = dir_handle ,
377
+ .id = dir_id ,
322
378
.overlapped = std .mem .zeroInit (
323
379
windows .OVERLAPPED ,
324
380
.{
325
- .hEvent = event ,
381
+ .hEvent = wait_handle ,
326
382
},
327
383
),
328
384
};
329
- return result ;
385
+ return dir_ptr ;
330
386
}
331
387
332
388
fn deinit (self : * @This (), gpa : Allocator ) void {
@@ -407,81 +463,21 @@ const Os = switch (builtin.os.tag) {
407
463
const reaction_set = rs : {
408
464
const gop = try w .dir_table .getOrPut (gpa , path );
409
465
if (! gop .found_existing ) {
410
- // The following code is a drawn out NtCreateFile call. (mostly adapted from std.fs.Dir.makeOpenDirAccessMaskW)
411
- // It's necessary in order to get the flags are required when calling ReadDirectoryChangesW.
412
- var dir_handle : windows.HANDLE = undefined ;
413
- {
414
- const root_fd = path .root_dir .handle .fd ;
415
- const sub_path = path .subPathOrDot ();
416
- const sub_path_w = try windows .sliceToPrefixedFileW (root_fd , sub_path );
417
- const path_len_bytes = std .math .cast (u16 , sub_path_w .len * 2 ) orelse return error .NameTooLong ;
418
-
419
- var nt_name = windows.UNICODE_STRING {
420
- .Length = @intCast (path_len_bytes ),
421
- .MaximumLength = @intCast (path_len_bytes ),
422
- .Buffer = @constCast (sub_path_w .span ().ptr ),
423
- };
424
- var attr = windows.OBJECT_ATTRIBUTES {
425
- .Length = @sizeOf (windows .OBJECT_ATTRIBUTES ),
426
- .RootDirectory = if (std .fs .path .isAbsoluteWindowsW (sub_path_w .span ())) null else root_fd ,
427
- .Attributes = 0 , // Note we do not use OBJ_CASE_INSENSITIVE here.
428
- .ObjectName = & nt_name ,
429
- .SecurityDescriptor = null ,
430
- .SecurityQualityOfService = null ,
431
- };
432
- var io : windows.IO_STATUS_BLOCK = undefined ;
433
- const rc = windows .ntdll .NtCreateFile (
434
- & dir_handle ,
435
- windows .SYNCHRONIZE | windows .GENERIC_READ | windows .FILE_LIST_DIRECTORY ,
436
- & attr ,
437
- & io ,
438
- null ,
439
- 0 ,
440
- windows .FILE_SHARE_READ | windows .FILE_SHARE_WRITE | windows .FILE_SHARE_DELETE ,
441
- windows .FILE_OPEN ,
442
- windows .FILE_DIRECTORY_FILE | windows .FILE_OPEN_FOR_BACKUP_INTENT ,
443
- null ,
444
- 0 ,
445
- );
446
-
447
- switch (rc ) {
448
- .SUCCESS = > {},
449
- .OBJECT_NAME_INVALID = > return error .BadPathName ,
450
- .OBJECT_NAME_NOT_FOUND = > return error .FileNotFound ,
451
- .OBJECT_NAME_COLLISION = > return error .PathAlreadyExists ,
452
- .OBJECT_PATH_NOT_FOUND = > return error .FileNotFound ,
453
- .NOT_A_DIRECTORY = > return error .NotDir ,
454
- // This can happen if the directory has 'List folder contents' permission set to 'Deny'
455
- .ACCESS_DENIED = > return error .AccessDenied ,
456
- .INVALID_PARAMETER = > unreachable ,
457
- else = > return windows .unexpectedStatus (rc ),
458
- }
459
- assert (dir_handle != windows .INVALID_HANDLE_VALUE );
460
- }
461
-
462
- const dir_id = getFileId (dir_handle ) catch | err | {
463
- windows .CloseHandle (dir_handle );
464
- return err ;
465
- };
466
-
467
- // `dir_id` may already be present in the table in
466
+ const dir = try Os .Directory .init (gpa , path );
467
+ errdefer dir .deinit (gpa );
468
+ // `dir.id` may already be present in the table in
468
469
// the case that we have multiple Cache.Path instances
469
470
// that compare inequal but ultimately point to the same
470
471
// directory on the file system.
471
472
// In such case, we must revert adding this directory, but keep
472
473
// the additions to the step set.
473
- const dh_gop = w .os .handle_table .getOrPut (gpa , dir_id ) catch | err | {
474
- windows .CloseHandle (dir_handle );
475
- return err ;
476
- };
474
+ const dh_gop = try w .os .handle_table .getOrPut (gpa , dir .id );
477
475
if (dh_gop .found_existing ) {
478
- windows . CloseHandle ( dir_handle );
476
+ dir . deinit ( gpa );
479
477
_ = w .dir_table .pop ();
480
478
} else {
481
479
assert (dh_gop .index == gop .index );
482
480
dh_gop .value_ptr .* = .{};
483
- const dir = try Os .Directory .init (gpa , dir_handle , dir_id );
484
- errdefer dir .deinit (gpa );
485
481
try dir .readChanges ();
486
482
try w .os .handle_extra .insert (gpa , dh_gop .index , .{
487
483
.dir = dir ,
0 commit comments