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
Merge pull request #5020 from mjrousos/UnixGetAttributesFix
Browse files Browse the repository at this point in the history
Get Unix file stats with LStat (instead of Stat)
  • Loading branch information
stephentoub committed Jan 14, 2016
2 parents a7dd46b + 967cde5 commit 984cb7d
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 1 deletion.
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 @@ -43,6 +43,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
// Use Stat (not LStat) since permissions for symbolic links are meaninless and defer to permissions on the target
Interop.Sys.FileStatus status;
Interop.CheckIo(Interop.Sys.Stat(sourceFullPath, out status), sourceFullPath);
int newMode = status.Mode & (int)Interop.Sys.Permissions.Mask;
Expand Down
47 changes: 47 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,52 @@ public void WindowsInvalidAttributes(FileAttributes attr)
Set(path, attr);
Assert.Equal(FileAttributes.Normal, Get(path));
}

// In some cases (such as when running without elevated privileges,
// the symbolic link may fail to create. Only run this test if it creates
// links successfully.
[ConditionalFact("CanCreateSymbolicLinks")]
public void SymLinksAreReparsePoints()
{
var path = GetTestFilePath();
var linkPath = GetTestFilePath();
File.Create(path).Dispose();
Assert.True(MountHelper.CreateSymbolicLink(linkPath, path));
Assert.Equal(FileAttributes.ReparsePoint, FileAttributes.ReparsePoint & Get(linkPath));
}

// In some cases (such as when running without elevated privileges,
// the symbolic link may fail to create. Only run this test if it creates
// links successfully.
[ConditionalFact("CanCreateSymbolicLinks")]
public void SymLinksDoNotReflectTargetAttributes()
{
var path = GetTestFilePath();
var linkPath = GetTestFilePath();
File.Create(path).Dispose();
Assert.True(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));
}

private static bool CanCreateSymbolicLinks
{
get
{
var path = Path.GetTempFileName();
var linkPath = path + ".link";
var ret = MountHelper.CreateSymbolicLink(linkPath, path);
try { File.Delete(path); } catch { }
try { File.Delete(linkPath); } catch { }
return ret;
}
}
}
}
31 changes: 31 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,36 @@ public void FalseForDirectory()
FileInfo di = new FileInfo(fileName);
Assert.False(di.Exists);
}

// In some cases (such as when running without elevated privileges,
// the symbolic link may fail to create. Only run this test if it creates
// links successfully.
[ConditionalFact("CanCreateSymbolicLinks")]
public void SymLinksExistIndependentlyOfTarget()
{
var path = GetTestFilePath();
var linkPath = GetTestFilePath();
File.Create(path).Dispose();
Assert.True(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);
}

private static bool CanCreateSymbolicLinks
{
get
{
var path = Path.GetTempFileName();
var linkPath = path + ".link";
var ret = MountHelper.CreateSymbolicLink(linkPath, path);
try { File.Delete(path); } catch { }
try { File.Delete(linkPath); } catch { }
return ret;
}
}
}
}
35 changes: 35 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,40 @@ public void Length_Of_Directory_Throws_FileNotFoundException()
FileInfo info = new FileInfo(path);
Assert.Throws<FileNotFoundException>(() => info.Length);
}

// In some cases (such as when running without elevated privileges,
// the symbolic link may fail to create. Only run this test if it creates
// links successfully.
[ConditionalFact("CanCreateSymbolicLinks")]
public void SymLinkLength()
{
var path = GetTestFilePath();
var linkPath = GetTestFilePath();
using (var tempFile = new TempFile(path, 2000))
{
Assert.True(MountHelper.CreateSymbolicLink(linkPath, path));

var info = new FileInfo(path);
var linkInfo = new FileInfo(linkPath);
Assert.Equal(2000, 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(2000 > linkInfo.Length);
}
}

private static bool CanCreateSymbolicLinks
{
get
{
var path = Path.GetTempFileName();
var linkPath = path + ".link";
var ret = MountHelper.CreateSymbolicLink(linkPath, path);
try { File.Delete(path); } catch { }
try { File.Delete(linkPath); } catch { }
return ret;
}
}
}
}
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 bool 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 (0 == symLinkProcess.ExitCode);
}
else
{
return false;
}
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@
<Compile Include="$(CommonTestPath)\System\IO\FileCleanupTestBase.cs">
<Link>Common\System\IO\FileCleanupTestBase.cs</Link>
</Compile>
<Compile Include="$(CommonTestPath)\System\IO\TempFile.cs">
<Link>Common\System\IO\TempFile.cs</Link>
</Compile>
<!-- Performance Tests -->
<Compile Include="Performance\Perf.Directory.cs" />
<Compile Include="Performance\Perf.File.cs" />
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-23712",
"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 984cb7d

Please sign in to comment.