You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Request: a way to tell if two filepaths point to the same file #17873
On Windows, the recommended way is to use the win32 API GetFileInformationByHandle and compare nFileIndexLow, nFileIndexHigh, dwVolumeSerialNumber. You have to do this via p/invoke because there's no .NET wrapper for it.
P/invoke was tolerable to me because it was all self-contained and easy. But as I port my code over to corefx, I honestly can't be bothered to go the whole nuget platform-specific native binary route. It's far too much work.
I'd love it you could add an API to corefx to judge whether two filenames point to the same file. Judging by the huge number of requests for this on stackoverflow, it seems like a common bread-and-butter scenario.
Please consider both APIs: checking the equality based on real-path and getting the real-path for given path:
publicstaticclassDirectory{// resolves symlink etc.publicstaticstringGetRealPath(stringpath);// resolves symlink and performs equality checkpublicstaticboolHaveSameTarget(stringpath1,stringpath2);}publicstaticclassFile{// resolves symlink etc.publicstaticstringGetRealPath(stringpath);// resolves symlink and performs equality checkpublicstaticboolHaveSameTarget(stringpath1,stringpath2);}
HaveSameTarget is proposed in Directory and File types because existing System.IO.Path APIs are mostly doing canonical operations (no real FS / IO intervention). Therefore, Directory, File, DirectoryInfo and FileInfo seem to be good candidates for this method. However, static methods are conventionally added to Directory and File types.
GetRealPath will help us keeping track of recursive symlink dotnet/corefx#16094
What exactly is the definition of "Real Path"? You wrote "resolves symlinks etc" but there's a lot buried in that...
On unix if you have two hard links then there's no unique real path. Likewise in Windows if you have junction points.
On Windows if you use "subst" on your hard drive then for instance x:\file.txt and c:\directory\file.txt might both be the same file, but I don't know which of them is the more "real".
On Windows \?\C:\directory\file.txt and c:\directory\file.txt both point to the same file but I don't know which name is more "real"
On Windows you might have a network-path with \?\ or a subst path like x:\file.txt, both pointing to the same file, but I don't know which is real.
If you have a recursive symlink, what should GetRealPath do?
I think the concept of a "real path" is far too woolly. It certainly can't be used as the implementation technique for HaveSameTarget. The best you could do is have a function called "ResolveSymlinks" (but I think that API is questionable -- the reason users use symlinks is because they want applications to see a particular directory structure, and they don't want applications second-guessing them).
Before we mark this as ready for review we should update the issue with a more concrete API proposal including a finalized API, some code examples, and justification for real-world usage. I think Jeremy's post is on the right track for desired behavior, we just need something more formal before we should move forward.
Agree on that. I am not an expert but I think the API can define reasonable constraints. I thought HaveSameTarget can make use of GetRealPath under the hood.
I think that API is questionable
On the contrary, I think there really is a need of parsing the real path in .NET (especially Core) as it is provided by almost all other language frameworks.
"I thought HaveSameTarget can make use of GetRealPath under the hood." -- no it can't! I gave examples where the correct solution (inode &c.) will correctly claim that two files are the same, but GetRealPath (as defined in PHP and Python and nodejs) will claim they're different.
I'll chime in more later when I have more time. I'll point out #14321 again as it discusses overlapping issues.
One initial thought is that we have to think about multiple drivers (such as mounted network shares) in how we define anything. I don't know that it is possible to say that two files definitively aren't the same if they don't share the same actual device (e.g. \Device\Harddisk0\ on Windows).
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
I would add something like File.GetFileIdentity(), returning unique opaque object representing the file and implementing IEquatable<>, for the sake of feeding into GroupBy. The real content might be e. g. triple of (nFileIndexLow, nFileIndexHigh, dwVolumeSerialNumber) on Windows. [One can achieve the same with just HaveSameTarget but in quadratic time.]
Activity
karelz commentedon Oct 11, 2016
Related to #14321
We need API proposal
JeremyKuhne commentedon Oct 12, 2016
Perhaps
Behavior:
ArgumentNullExceptionfornullor empty pathsWe may also want to consider exposing this for existing
SafeFileHandles. Perhapsbool SafeFileHandle.HasSameTarget(SafeFileHandle fileHandle).danmoseley commentedon Dec 27, 2016
@JeremyKuhne, @ianhays do you consider this ready for API review?
am11 commentedon Feb 14, 2017
Please consider both APIs: checking the equality based on real-path and getting the real-path for given path:
HaveSameTargetis proposed inDirectoryandFiletypes because existingSystem.IO.PathAPIs are mostly doing canonical operations (no real FS / IO intervention). Therefore,Directory,File,DirectoryInfoandFileInfoseem to be good candidates for this method. However,staticmethods are conventionally added toDirectoryandFiletypes.GetRealPathwill help us keeping track of recursive symlink dotnet/corefx#16094GetRealPathwill also fold the use-case of Add API to get actual file casing in path #14321, if method return real casing on case-insensitive filesystems.ljw1004 commentedon Feb 14, 2017
What exactly is the definition of "Real Path"? You wrote "resolves symlinks etc" but there's a lot buried in that...
I think the concept of a "real path" is far too woolly. It certainly can't be used as the implementation technique for HaveSameTarget. The best you could do is have a function called "ResolveSymlinks" (but I think that API is questionable -- the reason users use symlinks is because they want applications to see a particular directory structure, and they don't want applications second-guessing them).
ianhays commentedon Feb 14, 2017
Before we mark this as ready for review we should update the issue with a more concrete API proposal including a finalized API, some code examples, and justification for real-world usage. I think Jeremy's post is on the right track for desired behavior, we just need something more formal before we should move forward.
am11 commentedon Feb 14, 2017
Agree on that. I am not an expert but I think the API can define reasonable constraints. I thought
HaveSameTargetcan make use ofGetRealPathunder the hood.On the contrary, I think there really is a need of parsing the real path in .NET (especially Core) as it is provided by almost all other language frameworks.
PHP http://php.net/manual/en/function.realpath.php
Perl http://perldoc.perl.org/Cwd.html
Python https://docs.python.org/2/library/os.path.html#os.path.realpath
Ruby http://apidock.com/ruby/Pathname/realpath
node.js https://nodejs.org/api/fs.html#fs_fs_realpath_path_options_callback
Symlinks are used more commonly on Unix systems and hence more pain/complaints from that side of the house: dotnet/coreclr#2128.
On .NET, even the Windows-only P/Invoke based solutions such as this one are quite complicated to implement IMO.
ljw1004 commentedon Feb 14, 2017
"I thought HaveSameTarget can make use of GetRealPath under the hood." -- no it can't! I gave examples where the correct solution (inode &c.) will correctly claim that two files are the same, but GetRealPath (as defined in PHP and Python and nodejs) will claim they're different.
JeremyKuhne commentedon Feb 14, 2017
I'll chime in more later when I have more time. I'll point out #14321 again as it discusses overlapping issues.
One initial thought is that we have to think about multiple drivers (such as mounted network shares) in how we define anything. I don't know that it is possible to say that two files definitively aren't the same if they don't share the same actual device (e.g.
\Device\Harddisk0\on Windows).JeremyKuhne commentedon Jan 15, 2020
This issue has come up in other forums such as Twitter/StackOverflow
hamarb123 commentedon Nov 30, 2021
I would like this api also.
dotnet-policy-service commentedon Oct 19, 2026
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
This process is part of our issue cleanup automation.
dotnet-policy-service commentedon Oct 19, 2026
Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process.
This process is part of our issue cleanup automation.
hamarb123 commentedon Oct 19, 2026
I would still like to see this
RikkiGibson commentedon Oct 20, 2026
I think this relates to #52666. I believe this API would make it much easier to detect symlink cycles during recursive directory enumeration.
It looks like a similar library https://github.com/BurntSushi/same-file is used to implement symlink cycle detection in ripgrep.
vladd commentedon Oct 20, 2026
I would add something like
File.GetFileIdentity(), returning unique opaque object representing the file and implementingIEquatable<>, for the sake of feeding intoGroupBy. The real content might be e. g. triple of(nFileIndexLow, nFileIndexHigh, dwVolumeSerialNumber)on Windows. [One can achieve the same with justHaveSameTargetbut in quadratic time.]