Files
bun.sh/instructions.md
Zack Radisic 39cfea5e82 wip
2025-07-22 00:13:07 -07:00

1.8 KiB
Raw Permalink Blame History

Fixing symlinks (reparse point) with the shell rm built-in on Windows

On Posix systems for the rm shell built-in, if we don't know yet whether a file is a regular file or directory, we try to unlink it first.

If it is actually a directory, it will return EISDIR and we go and delete its children before calling rmdirat.

Conveniently, this handles symlinks to directories too.

However, this breaks on Windows if we have a symlink to a directory. Why? We don't have unlink on Windows. Instead we have a DeleteFileBun(...) function inside of src/windows.zig that tries to do the equivalent.

Unfortunately, it does not have the same behavior as posix for deleting symlinks. It will return with EISDIR (despite the FILE_OPEN_REPARSE_POINT being set, it seems to always try to delete the referenced directory).

So the solution is that we need to use the Windows API to delete the reparse point. We can do it like this:

HANDLE h = CreateFileW(L"linkDir",
                       GENERIC_WRITE,                        // must allow write
                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                       NULL,
                       OPEN_EXISTING,
                       FILE_FLAG_OPEN_REPARSE_POINT |        // dont follow
                       FILE_FLAG_BACKUP_SEMANTICS,
                       NULL);

// header only  tag identifies which reparse point to remove
REPARSE_GUID_DATA_BUFFER hdr = {0};
hdr.ReparseTag = IO_REPARSE_TAG_SYMLINK;   // or _MOUNT_POINT, _SOCK, etc.

DWORD bytes;
DeviceIoControl(h,
                FSCTL_DELETE_REPARSE_POINT,                 // core op
                &hdr, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, // in buf / len
                NULL, 0,                                    // no out
                &bytes,
                NULL);

I want you to implement this code in Zig.