golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.66k stars 17.62k forks source link

os/types_windows: fileStat.Mode() should use the reparse tag surrogate bit to identify symlinks #23684

Closed mattico closed 6 years ago

mattico commented 6 years ago

Go Version: 1.9.3 (affects 1.4+ since cf521ce64f50c4f300294d6b649f34eca87bb8a3) Operating System: Windows 10 x64 build 16299 (Fall Creator's Update) What did you do: attempt to traverse into the OneDrive folder to list files using filepath.Walk What did you expect to see: a listing of files inside the OneDrive folder What did you see instead: traversal stops at the OneDrive folder

I noticed that the code for fileStat.Mode() on Windows uses the presence of FILE_ATTRIBUTE_REPARSE_POINT to determine if a file is a symlink (or rather, acts like a unix symlink). While this is a common method, it is actually incorrect. Since a reparse point is simply a hook for a filesystem minifilter driver, their semantics are mostly undefined. When Microsoft added the Files On-Demand feature to OneDrive in the Fall Creator's Update, they used a reparse point which does not behave like a symlink, and thus is not readable using Readlink(). This has caused problems for users who attempt to use readlink to traverse directories that report being symlinks.

The good news is that the surrogate bit in the reparse tag specifies if "the file or directory represents another named entity in the system". That sounds like a Microsoft definition of symlink-like behavior. I made a program which verified that the surrogate bit is set on reparse points that behave like symlinks:

PATH REPARSE TAG IsReparseTagNameSurrogate
D:\symlink 10100000000000000000000000001100 true
D:\symlink_dir 10100000000000000000000000001100 true
D:\junction 10100000000000000000000000000011 true
D:\mountpoint 10100000000000000000000000000011 true
D:\OneDrive 10010000000000000111000000011010 false
D:\hardlink hard links do not use reparse points and they don't act like symlinks

Additionally, Microsoft has indicated that the IsReparseTagNameSurrogate macro is the correct way to determine if a reparse point behaves like a symlink.

TL;DR: filestat.Mode() should read the reparse tag, and only set ModeSymlink if the surrogate bit is set in the reparse tag.

mattico commented 6 years ago

Moved information to comment on related issue #22579