I would love any and all feedback on updated versions of my "Reparse Point" patches, which are now available in staging: https://github.com/wine-staging/wine-staging/tree/master/patches/ntdll-Junct...
Major changes since the last RFC version are: 1) Deletion of symlinks is now atomic (where supported). 2) Regular Unix symlinks are now reported as WSL Unix symlinks. 3) FILE_OPEN_REPARSE_POINT is now properly supported for applications that wish to access reparse points directly (symlink operations by file descriptor instead of by path). 4) Wine's cmd "dir" command now shows reparse point types and targets. 5) Wine's cmd "mklink" command now supports creating junction points and NT symlinks. 6) Absolute reparse points that are outside of the prefix no longer contain the path to drive Z (or appropriate drive letter). 7) Absolute reparse points now contain a string identifying the end of the Wine prefix that is used to modify the target path on file access if a prefix has been relocated (if the target is within the prefix). 8) Many, many bug fixes.
Thank you in advance to those who look these patches over, and previous iterations, your feedback is greatly appreciated! I know that these patches have been a long time coming, but I believe that we're finally getting to a place where we've figured out all the gotchas. Hopefully other folks agree ;)
===
Context: For those of you that wonder "why do these patches exist?", I have been putting these together over the past few years for several reasons: 1) More modern Windows programs use NT symlinks to avoid duplicating files and taking up extra disk space (particularly .NET). 2) Wine currently has no mechanism to make symlinks on the "PE" side of the dividing line, these patches allow this for Windows file paths and a small additional patch (creating WSL Unix symlinks) allows the same behavior for Unix paths. 3) Wine currently takes up a lot of space with system DLLs that are duplicates of the system-wide files, a logical extension to these patches is a Wine-specific reparse tag that allows these system DLLs to be "copy-on-write" so that Wine prefixes don't take up unnecessary disk space. Please note that this feature is not a focus of the patchset (not included), as I would like to get the infrastructure in place before tackling that problem.
Not currently supported: 1) Reparse points of type other than junction points, NT symlinks, and WSL Unix symlinks. 2) Creating WSL Unix symlinks. 3) Changing ownership permissions (lrwxrwxrwx) on the symlink instead of the target (currently a Linux limitation).
Implementation overview: • Reparse points are implemented as Unix symlinks where relative symlinks always start with "./" and absolute symlinks start with "/". • Following the relative/absolute flag the 32-bit reparse point tag (junction point or NT symlink) is encoded as a relative path where each bit is encoded as "/" (0) or "./" (1). When creating WSL Unix symlinks (future feature) this tag encoding will be skipped. • For NT symlinks this is followed by a file/directory flag ("./" for directories, "/" for files) so that dangling symlinks can still be deleted properly with RemoveFile or RemoveDirectory (as appropriate). • After the reparse tag (and file/directory flag) the Unix path is then appended, relative paths are included unmodified and absolute paths have some minor modifications. • If the path is an absolute path and points outside the prefix then the portion of the path to drive Z (or appropriate drive letter) is removed. • If the path is an absolute path that points inside the prefix then after the prefix path an 8-bit 'P' is encoded the same way that the reparse point tag is ("/"=0, "./"=1). • If an absolute symlink is opened by the wineserver then the prefix path is checked (if appropriate) and if it doesn't match the path of the current Wine prefix then the symlink is modified to replace the old prefix path with the current prefix path. • Because reparse points involve a modification to an existing empty file/directory, symlink creation and "removal" is implemented by creating the symlink at a temporary location and then replacing the original with an atomic renameat2 call (on supported systems), equivalent call (BSD/Mac), or by unlinking the original and moving the symlink into place (for legacy systems that do not support atomic replacement).
Best, Erich