Skip to content
5 changes: 0 additions & 5 deletions GVFS/GVFS.FunctionalTests/FileSystemRunners/BashRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ public BashRunner()
}
}

public override bool SupportsHardlinkCreation
{
get { return true; }
}

protected override string FileName
{
get
Expand Down
5 changes: 0 additions & 5 deletions GVFS/GVFS.FunctionalTests/FileSystemRunners/CmdRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@ public class CmdRunner : ShellRunner
"The process cannot access the file because it is being used by another process"
};

public override bool SupportsHardlinkCreation
{
get { return true; }
}

protected override string FileName
{
get
Expand Down
11 changes: 1 addition & 10 deletions GVFS/GVFS.FunctionalTests/FileSystemRunners/FileSystemRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ public static FileSystemRunner DefaultRunner
get { return defaultRunner; }
}

public virtual bool SupportsHardlinkCreation
{
get { return false; }
}

// File methods
public abstract bool FileExists(string path);
public abstract string MoveFile(string sourcePath, string targetPath);
Expand All @@ -74,11 +69,7 @@ public virtual bool SupportsHardlinkCreation
public abstract void ReadAllText_FileShouldNotBeFound(string path);

public abstract void CreateEmptyFile(string path);

public virtual void CreateHardLink(string newLinkFilePath, string existingFilePath)
{
Assert.Fail($"This runner does not support {nameof(this.CreateHardLink)}");
}
public abstract void CreateHardLink(string newLinkFilePath, string existingFilePath);

/// <summary>
/// Write the specified contents to the specified file. By calling this method the caller is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ public override void CreateEmptyFile(string path)
this.RunProcess(string.Format("-Command \"&{{ New-Item -ItemType file {0}}}\"", path));
}

public override void CreateHardLink(string newLinkFilePath, string existingFilePath)
{
this.RunProcess(string.Format("-Command \"&{{ New-Item -ItemType HardLink -Path {0} -Value {1}}}\"", newLinkFilePath, existingFilePath));
}

public override void WriteAllText(string path, string contents)
{
this.RunProcess(string.Format("-Command \"&{{ Out-File -FilePath {0} -InputObject '{1}' -Encoding ascii -NoNewline}}\"", path, contents));
Expand Down
21 changes: 21 additions & 0 deletions GVFS/GVFS.FunctionalTests/FileSystemRunners/SystemIORunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ public override void CreateEmptyFile(string path)
}
}

public override void CreateHardLink(string newLinkFilePath, string existingFilePath)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
WindowsCreateHardLink(newLinkFilePath, existingFilePath, IntPtr.Zero).ShouldBeTrue($"Failed to create hard link: {Marshal.GetLastWin32Error()}");
}
else
{
MacCreateHardLink(existingFilePath, newLinkFilePath).ShouldEqual(0, $"Failed to create hard link: {Marshal.GetLastWin32Error()}");
}
}

public override void WriteAllText(string path, string contents)
{
File.WriteAllText(path, contents);
Expand Down Expand Up @@ -180,6 +192,15 @@ public override void ReadAllText_FileShouldNotBeFound(string path)
[DllImport("kernel32", SetLastError = true)]
private static extern bool MoveFileEx(string existingFileName, string newFileName, int flags);

[DllImport("libc", EntryPoint = "link", SetLastError = true)]
private static extern int MacCreateHardLink(string oldPath, string newPath);

[DllImport("kernel32.dll", EntryPoint = "CreateHardLink", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool WindowsCreateHardLink(
string newLinkFileName,
string existingFileName,
IntPtr securityAttributes);

private static void RetryOnException(Action action)
{
for (int i = 0; i < 10; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,6 @@ public void CreateFileTest()
[TestCase, Order(2)]
public void CreateHardLinkTest()
{
if (!this.fileSystem.SupportsHardlinkCreation)
{
return;
}

string existingFileName = "fileToLinkTo.txt";
string existingFilePath = this.Enlistment.GetVirtualPathTo(existingFileName);
GVFSHelpers.ModifiedPathsShouldNotContain(this.fileSystem, this.Enlistment.DotGVFSRoot, existingFileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using GVFS.Tests.Should;
using NUnit.Framework;
using System.IO;
using System.Runtime.InteropServices;

namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
{
Expand Down Expand Up @@ -119,7 +118,7 @@ public void ModifiedPathsSavedAfterRemount(FileSystemRunner fileSystem)
}
}

[TestCaseSource(typeof(HardLinkRunners), HardLinkRunners.TestRunners)]
[TestCaseSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)]
public void ModifiedPathsCorrectAfterHardLinking(FileSystemRunner fileSystem)
{
const string ExpectedModifiedFilesContentsAfterHardlinks =
Expand Down Expand Up @@ -161,27 +160,5 @@ A LinkToFileOutsideSrc.txt
reader.ReadToEnd().ShouldEqual(ExpectedModifiedFilesContentsAfterHardlinks);
}
}

private class HardLinkRunners
{
public const string TestRunners = "Runners";

public static object[] Runners
{
get
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return new[]
{
new object[] { new CmdRunner() },
new object[] { new BashRunner() },
};
}

return new[] { new object[] { new BashRunner() } };
}
}
}
}
}
7 changes: 1 addition & 6 deletions GVFS/GVFS.FunctionalTests/Tests/GitCommands/AddStageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ public void StageBasicTest()
[TestCase, Order(3)]
public void AddAndStageHardLinksTest()
{
if (!this.FileSystem.SupportsHardlinkCreation)
{
return;
}

this.CreateHardLink("ReadmeLink.md", "Readme.md");
this.ValidateGitCommand("add ReadmeLink.md");
this.RunGitCommand("commit -m \"Created ReadmeLink.md\"");
Expand All @@ -61,7 +56,7 @@ public void StageAllowsPlaceholderCreation()
private void CommandAllowsPlaceholderCreation(string command, params string[] fileToReadPathParts)
{
string fileToRead = Path.Combine(fileToReadPathParts);
this.EditFile($"Some new content for {command}.", "Readme.md");
this.EditFile($"Some new content for {command}.", "Protocol.md");
ManualResetEventSlim resetEvent = GitHelpers.RunGitCommandWithWaitAndStdIn(this.Enlistment, resetTimeout: 3000, command: $"{command} -p", stdinToQuit: "q", processId: out _);
this.FileContentsShouldMatch(fileToRead);
this.ValidateGitCommand("--no-optional-locks status");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,6 @@ public void CheckoutBranchAfterReadingAllFilesAndVerifyContentsCorrect()
}

[TestCase]
[Category(Categories.MacTODO.M3)]
public void CheckoutBranchThatHasFolderShouldGetDeleted()
{
// this.ControlGitRepo.Commitish should not have the folder Test_ConflictTests\AddedFiles
Expand Down Expand Up @@ -803,6 +802,7 @@ public void SuccessfullyChecksOutDirectoryToFileToDirectory()
this.ShouldNotExistOnDisk("d", "c");
}

// TODO(Mac): This test needs the fix for issue #264
[TestCase]
[Category(Categories.MacTODO.M3)]
public void DeleteFileThenCheckout()
Expand Down
10 changes: 2 additions & 8 deletions GVFS/GVFS.FunctionalTests/Tests/GitCommands/GitRepoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,8 @@ protected void CreateHardLink(string newLinkFileName, string existingFileName)
string virtualNewLinkFile = Path.Combine(this.Enlistment.RepoRoot, newLinkFileName);
string controlNewLinkFile = Path.Combine(this.ControlGitRepo.RootPath, newLinkFileName);

// GitRepoTests are only run with SystemIORunner (which does not support hardlink
// creation) so use a BashRunner instead.
this.FileSystem.SupportsHardlinkCreation.ShouldBeFalse(
"If this.FileSystem.SupportsHardlinkCreation is true, CreateHardLink no longer needs to create a BashRunner");
FileSystemRunner runner = new BashRunner();

runner.CreateHardLink(virtualNewLinkFile, virtualExistingFile);
runner.CreateHardLink(controlNewLinkFile, controlExistingFile);
this.FileSystem.CreateHardLink(virtualNewLinkFile, virtualExistingFile);
this.FileSystem.CreateHardLink(controlNewLinkFile, controlExistingFile);
}

protected void SetFileAsReadOnly(string filePath)
Expand Down
1 change: 0 additions & 1 deletion GVFS/GVFS.FunctionalTests/Tests/GitCommands/RebaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
{
[TestFixture]
[Category(Categories.GitCommands)]
[Category(Categories.MacTODO.M3)]
public class RebaseTests : GitRepoTests
{
public RebaseTests() : base(enlistmentPerTest: true)
Expand Down
33 changes: 26 additions & 7 deletions ProjFS.Mac/PrjFSKext/PrjFSKext/KauthHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ static bool TrySendRequestAndWaitForResponse(
const VirtualizationRoot* root,
MessageType messageType,
const vnode_t vnode,
const char* vnodePath,
int pid,
const char* procname,
int* kauthResult,
Expand Down Expand Up @@ -253,14 +254,25 @@ static int HandleVnodeOperation(
vnode_t currentVnode = reinterpret_cast<vnode_t>(arg1);
// arg2 is the (vnode_t) parent vnode
int* kauthError = reinterpret_cast<int*>(arg3);
int kauthResult = KAUTH_RESULT_DEFER;

const char* vnodePath = nullptr;
char vnodePathBuffer[PrjFSMaxPath];
int vnodePathLength = PrjFSMaxPath;

VirtualizationRoot* root = nullptr;
vtype vnodeType;
uint32_t currentVnodeFileFlags;
int pid;
char procname[MAXCOMLEN + 1];

int kauthResult = KAUTH_RESULT_DEFER;

// TODO(Mac): Issue #271 - Reduce reliance on vn_getpath
// Call vn_getpath first when the cache is hottest to increase the chances
// of successfully getting the path
if (0 == vn_getpath(currentVnode, vnodePathBuffer, &vnodePathLength))
{
vnodePath = vnodePathBuffer;
}

if (!ShouldHandleVnodeOpEvent(
context,
Expand Down Expand Up @@ -292,6 +304,7 @@ static int HandleVnodeOperation(
root,
MessageType_KtoU_EnumerateDirectory,
currentVnode,
vnodePath,
pid,
procname,
&kauthResult,
Expand All @@ -308,6 +321,7 @@ static int HandleVnodeOperation(
root,
MessageType_KtoU_NotifyDirectoryPreDelete,
currentVnode,
vnodePath,
pid,
procname,
&kauthResult,
Expand All @@ -325,6 +339,7 @@ static int HandleVnodeOperation(
root,
MessageType_KtoU_NotifyFilePreDelete,
currentVnode,
vnodePath,
pid,
procname,
&kauthResult,
Expand All @@ -351,6 +366,7 @@ static int HandleVnodeOperation(
root,
MessageType_KtoU_HydrateFile,
currentVnode,
vnodePath,
pid,
procname,
&kauthResult,
Expand Down Expand Up @@ -428,6 +444,7 @@ static int HandleFileOpOperation(
root,
messageType,
currentVnodeFromPath,
newPath,
pid,
procname,
&kauthResult,
Expand All @@ -439,7 +456,7 @@ static int HandleFileOpOperation(
else if (KAUTH_FILEOP_CLOSE == action)
{
vnode_t currentVnode = reinterpret_cast<vnode_t>(arg0);
// arg1 is the (const char *) path
const char* path = (const char*)arg1;
int closeFlags = static_cast<int>(arg2);

if (vnode_isdir(currentVnode))
Expand Down Expand Up @@ -476,6 +493,7 @@ static int HandleFileOpOperation(
root,
MessageType_KtoU_NotifyFileModified,
currentVnode,
path,
pid,
procname,
&kauthResult,
Expand All @@ -492,6 +510,7 @@ static int HandleFileOpOperation(
root,
MessageType_KtoU_NotifyFileCreated,
currentVnode,
path,
pid,
procname,
&kauthResult,
Expand Down Expand Up @@ -647,6 +666,7 @@ static bool TrySendRequestAndWaitForResponse(
const VirtualizationRoot* root,
MessageType messageType,
const vnode_t vnode,
const char* vnodePath,
int pid,
const char* procname,
int* kauthResult,
Expand All @@ -657,11 +677,10 @@ static bool TrySendRequestAndWaitForResponse(
OutstandingMessage message;
message.receivedResponse = false;

char vnodePath[PrjFSMaxPath];
int vnodePathLength = PrjFSMaxPath;
if (vn_getpath(vnode, vnodePath, &vnodePathLength))
if (nullptr == vnodePath)
{
KextLog_Error("Unable to resolve a vnode to its path");
// Default error code is EACCES. See errno.h for more codes.
*kauthError = EAGAIN;
*kauthResult = KAUTH_RESULT_DENY;
return false;
}
Expand Down