Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Get Unix file stats with LStat (instead of Stat)
Browse files Browse the repository at this point in the history
Stat was ignoring symbolic links (and getting stats for the target
instead) which caused checks for whether or not a path was a sym link
to fail on Unix.

Fixes dotnet/corefx#5015
  • Loading branch information
mjrousos committed Jan 14, 2016
1 parent 5f8081a commit d7b709c
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 1 deletion.
13 changes: 13 additions & 0 deletions src/Common/tests/System/IO/FileCleanupTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ protected string GetTestFilePath(int? index = null, [CallerMemberName] string me
return Path.Combine(TestDirectory, GetTestFileName(index, memberName, lineNumber));
}

/// <summary>
/// Creates a non-empty file
/// </summary>
/// <param name="path">Path to create the file at</param>
protected void CreateNonEmptyFile(string path, int fileSize)
{
using (var fs = File.Create(path))
{
var testData = new byte[fileSize];
fs.Write(testData, 0, testData.Length);
}
}

/// <summary>Gets a test file name that is associated with the call site.</summary>
/// <param name="index">An optional index value to use as a suffix on the file name. Typically a loop index.</param>
/// <param name="memberName">The member name of the function calling this method.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,9 @@ void IFileSystemObject.Refresh()
{
// This should not throw, instead we store the result so that we can throw it
// when someone actually accesses a property.
int result = Interop.Sys.Stat(FullPath, out _fileStatus);
// Use LStat (rather than Stat) so that information about a symbolic link will be retrieved
// (rather than information about the target) if FullPath points to a symbolic link.
int result = Interop.Sys.LStat(FullPath, out _fileStatus);
if (result >= 0)
{
_fileStatusInitialized = 0;
Expand Down
1 change: 1 addition & 0 deletions src/System.IO.FileSystem/src/System/IO/UnixFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public override void CopyFile(string sourceFullPath, string destFullPath, bool o

// Now copy over relevant read/write/execute permissions from the source to the destination
Interop.Sys.FileStatus status;
// Use Stat (not LStat) since permissions for symbolic links are meaninless and defer to permissions on the target
Interop.CheckIo(Interop.Sys.Stat(sourceFullPath, out status), sourceFullPath);
int newMode = status.Mode & (int)Interop.Sys.Permissions.Mask;
Interop.CheckIo(Interop.Sys.ChMod(destFullPath, newMode), destFullPath);
Expand Down
29 changes: 29 additions & 0 deletions src/System.IO.FileSystem/tests/File/GetSetAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,34 @@ public void WindowsInvalidAttributes(FileAttributes attr)
Set(path, attr);
Assert.Equal(FileAttributes.Normal, Get(path));
}

[Fact]
public void SymLinksAreReparsePoints()
{
var path = GetTestFilePath();
var linkPath = GetTestFilePath();
File.Create(path).Dispose();
MountHelper.CreateSymbolicLink(linkPath, path);

Assert.Equal(FileAttributes.ReparsePoint, FileAttributes.ReparsePoint & Get(linkPath));
}

[Fact]
public void SymLinksDoNotReflectTargetAttributes()
{
var path = GetTestFilePath();
var linkPath = GetTestFilePath();
File.Create(path).Dispose();
MountHelper.CreateSymbolicLink(linkPath, path);

Set(path, FileAttributes.ReadOnly);

Assert.Equal(FileAttributes.ReadOnly, Get(path));

// Can't assume that ReparsePoint is the only attribute because Windows will add Archive automatically
// Instead, just make sure that ReparsePoint is present and ReadOnly is not
Assert.Equal(FileAttributes.ReparsePoint, FileAttributes.ReparsePoint & Get(linkPath));
Assert.Equal((FileAttributes)0, FileAttributes.ReadOnly & Get(linkPath));
}
}
}
15 changes: 15 additions & 0 deletions src/System.IO.FileSystem/tests/FileInfo/Exists.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,20 @@ public void FalseForDirectory()
FileInfo di = new FileInfo(fileName);
Assert.False(di.Exists);
}

[Fact]
public void SymLinksExistIndependentlyOfTarget()
{
var path = GetTestFilePath();
var linkPath = GetTestFilePath();
File.Create(path).Dispose();
MountHelper.CreateSymbolicLink(linkPath, path);
File.Delete(path);

var info = new FileInfo(path);
var linkInfo = new FileInfo(linkPath);
Assert.True(linkInfo.Exists);
Assert.False(info.Exists);
}
}
}
17 changes: 17 additions & 0 deletions src/System.IO.FileSystem/tests/FileInfo/Length.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,22 @@ public void Length_Of_Directory_Throws_FileNotFoundException()
FileInfo info = new FileInfo(path);
Assert.Throws<FileNotFoundException>(() => info.Length);
}

[Fact]
public void SymLinkLength()
{
var path = GetTestFilePath();
var linkPath = GetTestFilePath();
CreateNonEmptyFile(path, 500);
MountHelper.CreateSymbolicLink(linkPath, path);

var info = new FileInfo(path);
var linkInfo = new FileInfo(linkPath);
Assert.Equal(500, info.Length);
// On Windows, sym links report 0 size; on Linux, their size is the length of the path they point to
// Confirm that the size we get is not the size of the target file (and that it's less, since our temporary
// sym links should never exceed 500 bytes.
Assert.True(500 > linkInfo.Length);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@ public static class MountHelper
[DllImport("api-ms-win-core-file-l1-1-0.dll", EntryPoint = "DeleteVolumeMountPointW", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
private static extern bool DeleteVolumeMountPoint(String mountPoint);

/// <summary>Creates a symbolic link using command line tools</summary>
/// <param name="linkPath">The existing file</param>
/// <param name="targetPath"></param>
public static int CreateSymbolicLink(string linkPath, string targetPath)
{
Process symLinkProcess = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
symLinkProcess = Process.Start("cmd", string.Format("/c mklink \"{0}\" \"{1}\"", linkPath, targetPath));
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
symLinkProcess = Process.Start("ln", string.Format("-s \"{0}\" \"{1}\"", targetPath, linkPath));
}

if (symLinkProcess != null)
{
symLinkProcess.WaitForExit();
return symLinkProcess.ExitCode;
}
else
{
return -1;
}
}

public static void Mount(String volumeName, String mountPoint)
{

Expand Down
1 change: 1 addition & 0 deletions src/System.IO.FileSystem/tests/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"System.Collections": "4.0.10",
"System.Console": "4.0.0-rc2-23610",
"System.Diagnostics.Debug": "4.0.10",
"System.Diagnostics.Process": "4.0.0",
"System.Globalization": "4.0.10",
"System.IO": "4.0.10",
"System.IO.FileSystem.Primitives": "4.0.0",
Expand Down

0 comments on commit d7b709c

Please sign in to comment.