Hi all,
I’m trying to use std.fs.Dir.Walker through my Iterator interface but I run into a segfault every time on the second iteration. It segfaults in the walker and I don’t really know how to resolve it.
I’m developing this on Windows 11 with zig version 0.13.0, it might work on other operating systems but I haven’t tested that.
Any help here would be greatly appreciated!
Code
const std = @import("std");
pub fn main() !void {
var buffer: [1000000]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
const testPath = try std.process.getEnvVarOwned(allocator, "HOME");
var fsStore = try FileSystemStore.init(testPath);
var store = fsStore.store();
var iter = try store.iterate(allocator);
while (try iter.next()) |entry| {
std.debug.print("{s}\n", .{entry.basename});
}
}
const Store = struct {
ptr: *anyopaque,
iterateFn: *const fn (ptr: *anyopaque, allocator: std.mem.Allocator) anyerror!Iterator,
fn init(ptr: anytype) Store {
const T = @TypeOf(ptr);
const ptr_info = @typeInfo(T);
if (ptr_info != .Pointer) @compileError("ptr must be a pointer");
if (ptr_info.Pointer.size != .One) @compileError("ptr must be a single item pointer");
const gen = struct {
pub fn iterate(pointer: *anyopaque, allocator: std.mem.Allocator) anyerror!Iterator {
const self: T = @ptrCast(@alignCast(pointer));
return ptr_info.Pointer.child.iterate(self, allocator);
}
};
return .{
.ptr = ptr,
.iterateFn = gen.iterate,
};
}
fn iterate(self: Store, allocator: std.mem.Allocator) anyerror!Iterator {
return self.iterateFn(self.ptr, allocator);
}
pub const Iterator = struct {
ptr: *anyopaque,
nextFn: *const fn (ptr: *anyopaque) anyerror!?std.fs.Dir.Walker.Entry,
fn init(ptr: anytype) Iterator {
const T = @TypeOf(ptr);
const ptr_info = @typeInfo(T);
if (ptr_info != .Pointer) @compileError("ptr must be a pointer");
if (ptr_info.Pointer.size != .One) @compileError("ptr must be a single item pointer");
const gen = struct {
pub fn next(pointer: *anyopaque) anyerror!?std.fs.Dir.Walker.Entry {
const self: T = @ptrCast(@alignCast(pointer));
return ptr_info.Pointer.child.next(self);
}
};
return .{
.ptr = ptr,
.nextFn = gen.next,
};
}
fn next(self: Iterator) anyerror!?std.fs.Dir.Walker.Entry {
return self.nextFn(self.ptr);
}
};
};
const FileSystemStore = struct {
root: []const u8,
pub fn init(root: []const u8) anyerror!FileSystemStore {
std.fs.makeDirAbsolute(root) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => return err,
};
return FileSystemStore{
.root = root,
};
}
pub fn store(self: *FileSystemStore) Store {
return Store.init(self);
}
fn iterate(self: *FileSystemStore, allocator: std.mem.Allocator) !Store.Iterator {
return Iterator.init(allocator, self.root);
}
fn deinit(self: *FileSystemStore) void {
self.dir.close();
}
const Iterator = struct {
walker: std.fs.Dir.Walker,
dir: std.fs.Dir,
fn init(allocator: std.mem.Allocator, path: []const u8) !Store.Iterator {
var dir = try std.fs.openDirAbsolute(path, .{ .iterate = true });
var iter = Iterator{
.walker = try dir.walk(allocator),
.dir = dir,
};
return Store.Iterator.init(&iter);
}
fn iterate(self: *Iterator) !Store.Iterator {
return Store.Iterator.init(self);
}
fn next(self: *Iterator) !?std.fs.Dir.Walker.Entry {
return self.walker.next();
}
fn deinit(self: *Iterator) void {
self.walker.deinit();
self.dir.close();
}
};
};
Segfault when running on my machine
> zig build-exe .\main.zig
> ./main.exe
<file from first iteration>
Segmentation fault at address 0x5cf447d17
C:\...\main.zig:122:36: 0xca5760 in next (main.exe.obj)
return self.walker.next();
^
C:\...\main.zig:63:55: 0xc9cea4 in next (main.exe.obj)
return ptr_info.Pointer.child.next(self);
^
C:\...\main.zig:74:31: 0xc41907 in next (main.exe.obj)
return self.nextFn(self.ptr);
^
C:\...\main.zig:13:25: 0xc41271 in main (main.exe.obj)
while (try iter.next()) |entry| {
^
C:\...\zig.zig_Microsoft.Winget.Source_8wekyb3d8bbwe\zig-windows-x86_64-0.13.0\lib\std\start.zig:363:53: 0xc41bbc in WinStartup (main.exe.obj)
std.os.windows.ntdll.RtlExitUserProcess(callMain());
^
???:?:?: 0x7ffaf27153df in ??? (KERNEL32.DLL)
???:?:?: 0x7ffaf2a6485a in ??? (ntdll.dll)
When running the walker on the same hierarchy the walker completes without issue.
Working walker example
pub fn main() !void {
var buffer: [1000000]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
const testPath = try std.process.getEnvVarOwned(allocator, "HOME");
var dir = try std.fs.openDirAbsolute(testPath, .{ .iterate = true });
var walker = try dir.walk(allocator);
while (try walker.next()) |entry| {
std.debug.print("{s}\n", .{entry.basename});
}
}