зеркало из https://github.com/microsoft/scalar.git
Changed included in this update:
Handle IOExceptions when setting Console.InputEncoding Only add to the projection if the index entry has the skipworktree bit and is Update Git for Windows for status cache fixes Stand up script to enable Mac functional tests to run as a build definition Native hooks on Mac Update the readme to indicate the rename is in progress Remove GC.Collect, update allocation strategy, and add logging for index parsing Update to ProjFS version 2018.719.1 Remove the string split from upgrading to modified paths Update git for windows with version that uses new msys2 runtime add utility method to convert from Windows to Unix paths Run prefetch in the background Update to ProjFS version 2018.706.1 Tweak logic for determining the current test directory Update GVFS.Hooks to work on Mac, install GVFS.Hooks on Mac, and fix clone+mount with GVFS aware Git Fixes incremental builds when changing props files in GVFS.Build
This commit is contained in:
Родитель
cb68bfee31
Коммит
c7e37417bd
17
GVFS.sln
17
GVFS.sln
|
@ -45,7 +45,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.NativeTests", "GVFS\GV
|
|||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Hooks", "GVFS\GVFS.Hooks\GVFS.Hooks.csproj", "{BDA91EE5-C684-4FC5-A90A-B7D677421917}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Hooks.Windows", "GVFS\GVFS.Hooks\GVFS.Hooks.Windows.csproj", "{BDA91EE5-C684-4FC5-A90A-B7D677421917}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
|
@ -57,7 +57,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Service", "GVFS\GVFS.S
|
|||
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.ReadObjectHook", "GVFS\GVFS.ReadObjectHook\GVFS.ReadObjectHook.vcxproj", "{5A6656D5-81C7-472C-9DC8-32D071CB2258}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.ReadObjectHook", "GVFS\GVFS.ReadObjectHook\GVFS.ReadObjectHook.Windows.vcxproj", "{5A6656D5-81C7-472C-9DC8-32D071CB2258}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
|
@ -106,7 +106,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.SignFiles", "GVFS\GVFS
|
|||
{93B403FD-DAFB-46C5-9636-B122792A548A} = {93B403FD-DAFB-46C5-9636-B122792A548A}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.VirtualFileSystemHook", "GVFS\GVFS.VirtualFileSystemHook\GVFS.VirtualFileSystemHook.vcxproj", "{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.VirtualFileSystemHook", "GVFS\GVFS.VirtualFileSystemHook\GVFS.VirtualFileSystemHook.Windows.vcxproj", "{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
|
@ -165,6 +165,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.FunctionalTests", "GVF
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.FunctionalTests.LockHolder", "GVFS\GVFS.FunctionalTests.LockHolder\GVFS.FunctionalTests.LockHolder.csproj", "{FA273F69-5762-43D8-AEA1-B4F08090D624}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Hooks.Mac", "GVFS\GVFS.Hooks\GVFS.Hooks.Mac.csproj", "{4CC2A90D-D240-4382-B4BF-5E175515E492}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug.Mac|x64 = Debug.Mac|x64
|
||||
|
@ -359,6 +361,14 @@ Global
|
|||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Release.Windows|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -392,6 +402,7 @@ Global
|
|||
{0F0A008E-AB12-40EC-A671-37A541B08C7F} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A025908B-DAB1-46CB-83A3-56F3B863D8FA}
|
||||
|
|
|
@ -12,12 +12,12 @@ using System.Linq;
|
|||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class CheckoutFetchHelper : PrefetchHelper
|
||||
public class CheckoutPrefetcher : BlobPrefetcher
|
||||
{
|
||||
private readonly bool allowIndexMetadataUpdateFromWorkingTree;
|
||||
private readonly int checkoutThreadCount;
|
||||
|
||||
public CheckoutFetchHelper(
|
||||
public CheckoutPrefetcher(
|
||||
ITracer tracer,
|
||||
Enlistment enlistment,
|
||||
GitObjectsHttpRequestor objectRequestor,
|
||||
|
@ -64,7 +64,7 @@ namespace FastFetch
|
|||
this.DownloadMissingCommit(commitToFetch, this.GitObjects);
|
||||
|
||||
// Configure pipeline
|
||||
// Checkout uses DiffHelper when running checkout.Start(), which we use instead of LsTreeHelper like in FetchHelper.cs
|
||||
// Checkout uses DiffHelper when running checkout.Start(), which we use instead of LsTreeHelper
|
||||
// Checkout diff output => FindMissingBlobs => BatchDownload => IndexPack => Checkout available blobs
|
||||
CheckoutJob checkout = new CheckoutJob(this.checkoutThreadCount, this.FolderList, commitToFetch, this.Tracer, this.Enlistment);
|
||||
FindMissingBlobsJob blobFinder = new FindMissingBlobsJob(this.SearchThreadCount, checkout.RequiredBlobs, checkout.AvailableBlobShas, this.Tracer, this.Enlistment);
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\LibGit2Sharp.NativeBinaries.props" Condition="Exists('..\LibGit2Sharp.NativeBinaries.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
|
@ -56,7 +56,7 @@
|
|||
<Compile Include="$(BuildOutputDir)\CommonAssemblyVersion.cs">
|
||||
<Link>CommonAssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="CheckoutFetchHelper.cs" />
|
||||
<Compile Include="CheckoutPrefetcher.cs" />
|
||||
<Compile Include="FastFetchVerb.cs" />
|
||||
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
|
|
|
@ -218,9 +218,9 @@ namespace FastFetch
|
|||
});
|
||||
|
||||
RetryConfig retryConfig = new RetryConfig(this.MaxAttempts, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes));
|
||||
PrefetchHelper fetchHelper = this.GetFetchHelper(tracer, enlistment, cacheServer, retryConfig);
|
||||
BlobPrefetcher prefetcher = this.GetFolderPrefetcher(tracer, enlistment, cacheServer, retryConfig);
|
||||
string error;
|
||||
if (!PrefetchHelper.TryLoadFolderList(enlistment, this.FolderList, this.FolderListFile, fetchHelper.FolderList, out error))
|
||||
if (!BlobPrefetcher.TryLoadFolderList(enlistment, this.FolderList, this.FolderListFile, prefetcher.FolderList, out error))
|
||||
{
|
||||
tracer.RelatedError(error);
|
||||
Console.WriteLine(error);
|
||||
|
@ -237,10 +237,10 @@ namespace FastFetch
|
|||
try
|
||||
{
|
||||
bool isBranch = this.Commit == null;
|
||||
fetchHelper.Prefetch(commitish, isBranch);
|
||||
return !fetchHelper.HasFailures;
|
||||
prefetcher.Prefetch(commitish, isBranch);
|
||||
return !prefetcher.HasFailures;
|
||||
}
|
||||
catch (PrefetchHelper.FetchException e)
|
||||
catch (BlobPrefetcher.FetchException e)
|
||||
{
|
||||
tracer.RelatedError(e.Message);
|
||||
return false;
|
||||
|
@ -263,7 +263,7 @@ namespace FastFetch
|
|||
Console.WriteLine("See the full log at " + fastfetchLogFile);
|
||||
}
|
||||
|
||||
isSuccess &= !fetchHelper.HasFailures;
|
||||
isSuccess &= !prefetcher.HasFailures;
|
||||
}
|
||||
catch (AggregateException e)
|
||||
{
|
||||
|
@ -303,13 +303,13 @@ namespace FastFetch
|
|||
return enlistment.RepoUrl;
|
||||
}
|
||||
|
||||
private PrefetchHelper GetFetchHelper(ITracer tracer, Enlistment enlistment, CacheServerInfo cacheServer, RetryConfig retryConfig)
|
||||
private BlobPrefetcher GetFolderPrefetcher(ITracer tracer, Enlistment enlistment, CacheServerInfo cacheServer, RetryConfig retryConfig)
|
||||
{
|
||||
GitObjectsHttpRequestor objectRequestor = new GitObjectsHttpRequestor(tracer, enlistment, cacheServer, retryConfig);
|
||||
|
||||
if (this.Checkout)
|
||||
{
|
||||
return new CheckoutFetchHelper(
|
||||
return new CheckoutPrefetcher(
|
||||
tracer,
|
||||
enlistment,
|
||||
objectRequestor,
|
||||
|
@ -322,7 +322,7 @@ namespace FastFetch
|
|||
}
|
||||
else
|
||||
{
|
||||
return new PrefetchHelper(
|
||||
return new BlobPrefetcher(
|
||||
tracer,
|
||||
enlistment,
|
||||
objectRequestor,
|
||||
|
|
|
@ -40,19 +40,19 @@
|
|||
<Compile Include="GenerateVersionInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="GVFS.props">
|
||||
<BuildProps Include="GVFS.props">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="GVFS.cs.props">
|
||||
</BuildProps>
|
||||
<BuildProps Include="GVFS.cs.props">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="GVFS.cpp.props">
|
||||
</BuildProps>
|
||||
<BuildProps Include="GVFS.cpp.props">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
<None Include="ProjFS.props">
|
||||
</BuildProps>
|
||||
<GeneratedPackageConfig Include="packages.config" />
|
||||
<BuildProps Include="ProjFS.props">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</BuildProps>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="GetTargetFrameworkProperties" />
|
||||
|
@ -129,7 +129,6 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<GVFSSetupPath>$(BuildOutputDir)\GVFS.Installer\bin\x64\$(Configuration)\SetupGVFS.$(GVFSVersion).exe</GVFSSetupPath>
|
||||
<GitPackagesConfigPath>$(MSBuildThisFileDirectory)packages.config</GitPackagesConfigPath>
|
||||
<OutDir>$(BuildOutputDir)\GVFS.Build\</OutDir>
|
||||
<GitVersionConstantsPath>$(OutDir)GVFSConstants.GitVersion.cs</GitVersionConstantsPath>
|
||||
<G4WInstallBatPath>$(OutDir)InstallG4W.bat</G4WInstallBatPath>
|
||||
|
@ -160,13 +159,13 @@
|
|||
</ItemGroup>
|
||||
|
||||
<Target Name="GVFSPreBuild"
|
||||
Inputs="$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateG4WNugetReference.cs"
|
||||
Outputs="$(GitPackagesConfigPath)">
|
||||
Inputs="$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateG4WNugetReference.cs;@(BuildProps)"
|
||||
Outputs="@(GeneratedPackageConfig)">
|
||||
<GenerateG4WNugetReference GitPackageVersion="$(GitPackageVersion)" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
</Target>
|
||||
|
||||
<Target Name="GVFSPackageRestore"
|
||||
Inputs="$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(GitPackagesConfigPath)"
|
||||
Inputs="$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);@(GeneratedPackageConfig)"
|
||||
Outputs="$(RestoreTimestampPath)"
|
||||
DependsOnTargets="GVFSPreBuild"
|
||||
Condition="'$(OS)' == 'Windows_NT'">
|
||||
|
@ -177,7 +176,7 @@
|
|||
|
||||
<Target Name="GenerateInstallScripts"
|
||||
DependsOnTargets="GVFSPackageRestore;$(GenerateInstallScriptsDependsOn)"
|
||||
Inputs="$(RestoreTimestampPath);$(GitPackagesConfigPath);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateGitVersionConstants.cs;$(MSBuildThisFileDirectory)GenerateInstallScripts.cs;$(MSBuildThisFileDirectory)GenerateGVFSInstallersNuspec.cs"
|
||||
Inputs="$(RestoreTimestampPath);@(GeneratedPackageConfig);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateGitVersionConstants.cs;$(MSBuildThisFileDirectory)GenerateInstallScripts.cs;$(MSBuildThisFileDirectory)GenerateGVFSInstallersNuspec.cs"
|
||||
Outputs="$(GitVersionConstantsPath);$(G4WInstallBatPath);$(GVFSInstallBatPath);$(GVFSInstallerNuspecPath)"
|
||||
Condition="'$(OS)' == 'Windows_NT'">
|
||||
<GenerateGitVersionConstants GitPackageVersion="$(GitPackageVersion)" PackagesPath="$(PackagesDir)" OutputFile="$(GitVersionConstantsPath)">
|
||||
|
@ -189,7 +188,7 @@
|
|||
|
||||
<Target Name="GenerateShared"
|
||||
DependsOnTargets="GenerateInstallScripts;$(GenerateSharedDependsOn)"
|
||||
Inputs="$(RestoreTimestampPath);$(GitPackagesConfigPath);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateVersionInfo.cs;$(MSBuildThisFileDirectory)GenerateApplicationManifests.cs"
|
||||
Inputs="$(RestoreTimestampPath);@(GeneratedPackageConfig);$(MSBuildThisFileFullPath);$(MSBuildProjectFullPath);$(MSBuildThisFileDirectory)GenerateVersionInfo.cs;$(MSBuildThisFileDirectory)GenerateApplicationManifests.cs"
|
||||
Outputs="$(AssemblyVersionPath);$(VersionHeaderPath);@(ApplicationNeedsManifest->'%(OutputFile)')">
|
||||
<GenerateVersionInfo Version="$(GVFSVersion)" AssemblyVersion="$(AssemblyVersionPath)" VersionHeader="$(VersionHeaderPath)" />
|
||||
<GenerateApplicationManifests Version="$(GVFSVersion)" ApplicationName="@(ApplicationNeedsManifest)" ManifestPath="@(ApplicationNeedsManifest->'%(OutputFile)')" />
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<PropertyGroup Label="Parameters">
|
||||
<GVFSVersion>0.2.173.2</GVFSVersion>
|
||||
<GitPackageVersion>2.6479675</GitPackageVersion>
|
||||
<GitPackageVersion>2.20180730.3</GitPackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="DefaultSettings">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjFSPackage>GVFS.ProjFS.2018.628.1</ProjFSPackage>
|
||||
<ProjFSPackage>GVFS.ProjFS.2018.719.1</ProjFSPackage>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
|
@ -14,22 +14,6 @@ namespace GVFS.Common
|
|||
Failure,
|
||||
}
|
||||
|
||||
private enum StdHandle
|
||||
{
|
||||
Stdin = -10,
|
||||
Stdout = -11,
|
||||
Stderr = -12
|
||||
}
|
||||
|
||||
private enum FileType : uint
|
||||
{
|
||||
Unknown = 0x0000,
|
||||
Disk = 0x0001,
|
||||
Char = 0x0002,
|
||||
Pipe = 0x0003,
|
||||
Remote = 0x8000,
|
||||
}
|
||||
|
||||
public static bool ShowStatusWhileRunning(
|
||||
Func<bool> action,
|
||||
string message,
|
||||
|
@ -156,20 +140,9 @@ namespace GVFS.Common
|
|||
return result;
|
||||
}
|
||||
|
||||
public static bool IsConsoleOutputRedirectedToFile()
|
||||
{
|
||||
return FileType.Disk == GetFileType(GetStdHandle(StdHandle.Stdout));
|
||||
}
|
||||
|
||||
public static string GetGVFSLogMessage(string enlistmentRoot)
|
||||
{
|
||||
return "Run 'gvfs log " + enlistmentRoot + "' for more info.";
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr GetStdHandle(StdHandle std);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern FileType GetFileType(IntPtr hdl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace GVFS.Common
|
||||
{
|
||||
public static class EpochConverter
|
||||
{
|
||||
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public static long ToUnixEpochSeconds(DateTime datetime)
|
||||
{
|
||||
return Convert.ToInt64(Math.Truncate((datetime - UnixEpoch).TotalSeconds));
|
||||
}
|
||||
|
||||
public static DateTime FromUnixEpochSeconds(long secondsSinceEpoch)
|
||||
{
|
||||
return UnixEpoch.AddSeconds(secondsSinceEpoch);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,12 +12,6 @@ namespace GVFS.Common.FileSystem
|
|||
{
|
||||
public static class HooksInstaller
|
||||
{
|
||||
private const string HooksConfigContentTemplate =
|
||||
@"########################################################################
|
||||
# Automatically generated file, do not modify.
|
||||
# See {0} config setting
|
||||
########################################################################
|
||||
{1}";
|
||||
private static readonly string ExecutingDirectory;
|
||||
|
||||
static HooksInstaller()
|
||||
|
@ -29,23 +23,23 @@ namespace GVFS.Common.FileSystem
|
|||
{
|
||||
IEnumerable<string> valuableHooksLines = defaultHooksLines.Where(line => !string.IsNullOrEmpty(line.Trim()));
|
||||
|
||||
if (valuableHooksLines.Contains(GVFSConstants.GVFSHooksExecutableName, StringComparer.OrdinalIgnoreCase))
|
||||
if (valuableHooksLines.Contains(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new HooksConfigurationException(
|
||||
"GVFS.Hooks.exe should not be specified in the configuration for "
|
||||
$"{GVFSPlatform.Instance.Constants.GVFSHooksExecutableName} should not be specified in the configuration for "
|
||||
+ GVFSConstants.DotGit.Hooks.PostCommandHookName + " hooks (" + filename + ").");
|
||||
}
|
||||
else if (!valuableHooksLines.Any())
|
||||
{
|
||||
return GVFSConstants.GVFSHooksExecutableName;
|
||||
return GVFSPlatform.Instance.Constants.GVFSHooksExecutableName;
|
||||
}
|
||||
else if (hookName.Equals(GVFSConstants.DotGit.Hooks.PostCommandHookName))
|
||||
{
|
||||
return string.Join("\n", new string[] { GVFSConstants.GVFSHooksExecutableName }.Concat(valuableHooksLines));
|
||||
return string.Join("\n", new string[] { GVFSPlatform.Instance.Constants.GVFSHooksExecutableName }.Concat(valuableHooksLines));
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Join("\n", valuableHooksLines.Concat(new string[] { GVFSConstants.GVFSHooksExecutableName }));
|
||||
return string.Join("\n", valuableHooksLines.Concat(new string[] { GVFSPlatform.Instance.Constants.GVFSHooksExecutableName }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,30 +48,30 @@ namespace GVFS.Common.FileSystem
|
|||
error = string.Empty;
|
||||
try
|
||||
{
|
||||
string installedReadObjectHookPath = Path.Combine(ExecutingDirectory, GVFSConstants.GVFSReadObjectHookExecutableName);
|
||||
string targetReadObjectHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.ReadObjectPath + GVFSConstants.ExecutableExtension);
|
||||
if (!TryAction(() => CopyHook(context, installedReadObjectHookPath, targetReadObjectHookPath), out error))
|
||||
string installedReadObjectHookPath = Path.Combine(ExecutingDirectory, GVFSPlatform.Instance.Constants.GVFSReadObjectHookExecutableName);
|
||||
string targetReadObjectHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.ReadObjectPath + GVFSPlatform.Instance.Constants.ExecutableExtension);
|
||||
if (!TryHooksInstallationAction(() => CopyHook(context, installedReadObjectHookPath, targetReadObjectHookPath), out error))
|
||||
{
|
||||
error = "Failed to copy " + installedReadObjectHookPath + "\n" + error;
|
||||
return false;
|
||||
}
|
||||
|
||||
string installedVirtualFileSystemHookPath = Path.Combine(ExecutingDirectory, GVFSConstants.GVFSVirtualFileSystemHookExecutableName);
|
||||
string targetVirtualFileSystemHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.VirtualFileSystemPath + GVFSConstants.ExecutableExtension);
|
||||
if (!TryAction(() => CopyHook(context, installedVirtualFileSystemHookPath, targetVirtualFileSystemHookPath), out error))
|
||||
string installedVirtualFileSystemHookPath = Path.Combine(ExecutingDirectory, GVFSPlatform.Instance.Constants.GVFSVirtualFileSystemHookExecutableName);
|
||||
string targetVirtualFileSystemHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.VirtualFileSystemPath + GVFSPlatform.Instance.Constants.ExecutableExtension);
|
||||
if (!TryHooksInstallationAction(() => CopyHook(context, installedVirtualFileSystemHookPath, targetVirtualFileSystemHookPath), out error))
|
||||
{
|
||||
error = "Failed to copy " + installedVirtualFileSystemHookPath + "\n" + error;
|
||||
return false;
|
||||
}
|
||||
|
||||
string precommandHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.PreCommandPath);
|
||||
if (!TryInstallGitCommandHooks(context, GVFSConstants.DotGit.Hooks.PreCommandHookName, precommandHookPath, out error))
|
||||
if (!GVFSPlatform.Instance.TryInstallGitCommandHooks(context, ExecutingDirectory, GVFSConstants.DotGit.Hooks.PreCommandHookName, precommandHookPath, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string postcommandHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Hooks.PostCommandPath);
|
||||
if (!TryInstallGitCommandHooks(context, GVFSConstants.DotGit.Hooks.PostCommandHookName, postcommandHookPath, out error))
|
||||
if (!GVFSPlatform.Instance.TryInstallGitCommandHooks(context, ExecutingDirectory, GVFSConstants.DotGit.Hooks.PostCommandHookName, postcommandHookPath, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -97,7 +91,7 @@ namespace GVFS.Common.FileSystem
|
|||
context,
|
||||
GVFSConstants.DotGit.Hooks.ReadObjectName,
|
||||
GVFSConstants.DotGit.Hooks.ReadObjectPath,
|
||||
GVFSConstants.GVFSReadObjectHookExecutableName,
|
||||
GVFSPlatform.Instance.Constants.GVFSReadObjectHookExecutableName,
|
||||
out errorMessage))
|
||||
{
|
||||
return false;
|
||||
|
@ -107,7 +101,7 @@ namespace GVFS.Common.FileSystem
|
|||
context,
|
||||
GVFSConstants.DotGit.Hooks.VirtualFileSystemName,
|
||||
GVFSConstants.DotGit.Hooks.VirtualFileSystemPath,
|
||||
GVFSConstants.GVFSVirtualFileSystemHookExecutableName,
|
||||
GVFSPlatform.Instance.Constants.GVFSVirtualFileSystemHookExecutableName,
|
||||
out errorMessage))
|
||||
{
|
||||
return false;
|
||||
|
@ -116,7 +110,24 @@ namespace GVFS.Common.FileSystem
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool TryAction(Action action, out string errorMessage)
|
||||
public static void CopyHook(GVFSContext context, string sourcePath, string destinationPath)
|
||||
{
|
||||
Exception ex;
|
||||
if (!context.FileSystem.TryCopyToTempFileAndRename(sourcePath, destinationPath, out ex))
|
||||
{
|
||||
throw new RetryableException($"Error installing {sourcePath} to {destinationPath}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to perform the specified action. The action will be retried (with backoff) up to 3 times.
|
||||
/// </summary>
|
||||
/// <param name="action">Action to perform</param>
|
||||
/// <param name="errorMessage">Error message</param>
|
||||
/// <returns>True if the action succeeded and false otherwise</returns>
|
||||
/// <remarks>This method is optimized for the hooks installation process and should not be used
|
||||
/// as a generic retry mechanism. See RetryWrapper for a general purpose retry mechanism</remarks>
|
||||
public static bool TryHooksInstallationAction(Action action, out string errorMessage)
|
||||
{
|
||||
int retriesLeft = 3;
|
||||
int retryWaitMillis = 500; // Will grow exponentially on each retry attempt
|
||||
|
@ -157,7 +168,7 @@ namespace GVFS.Common.FileSystem
|
|||
out string errorMessage)
|
||||
{
|
||||
bool copyHook = false;
|
||||
string enlistmentHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, hookPath + GVFSConstants.ExecutableExtension);
|
||||
string enlistmentHookPath = Path.Combine(context.Enlistment.WorkingDirectoryRoot, hookPath + GVFSPlatform.Instance.Constants.ExecutableExtension);
|
||||
string installedHookPath = Path.Combine(ExecutingDirectory, hookExecutableName);
|
||||
|
||||
if (!context.FileSystem.FileExists(installedHookPath))
|
||||
|
@ -221,74 +232,6 @@ namespace GVFS.Common.FileSystem
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool TryInstallGitCommandHooks(GVFSContext context, string hookName, string commandHookPath, out string errorMessage)
|
||||
{
|
||||
// The GitHooksLoader requires the following setup to invoke a hook:
|
||||
// Copy GithooksLoader.exe to hook-name.exe
|
||||
// Create a text file named hook-name.hooks that lists the applications to execute for the hook, one application per line
|
||||
|
||||
string gitHooksloaderPath = Path.Combine(ExecutingDirectory, GVFSConstants.DotGit.Hooks.LoaderExecutable);
|
||||
if (!TryAction(() => CopyHook(context, gitHooksloaderPath, commandHookPath + GVFSConstants.ExecutableExtension), out errorMessage))
|
||||
{
|
||||
errorMessage = "Failed to copy " + GVFSConstants.DotGit.Hooks.LoaderExecutable + " to " + commandHookPath + GVFSConstants.ExecutableExtension + "\n" + errorMessage;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryAction(() => CreateHookCommandConfig(context, hookName, commandHookPath), out errorMessage))
|
||||
{
|
||||
errorMessage = "Failed to create " + commandHookPath + GVFSConstants.GitConfig.HooksExtension + "\n" + errorMessage;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void CopyHook(GVFSContext context, string sourcePath, string destinationPath)
|
||||
{
|
||||
Exception ex;
|
||||
if (!context.FileSystem.TryCopyToTempFileAndRename(sourcePath, destinationPath, out ex))
|
||||
{
|
||||
throw new RetryableException($"Error installing {sourcePath} to {destinationPath}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateHookCommandConfig(GVFSContext context, string hookName, string commandHookPath)
|
||||
{
|
||||
string targetPath = commandHookPath + GVFSConstants.GitConfig.HooksExtension;
|
||||
|
||||
try
|
||||
{
|
||||
string configSetting = GVFSConstants.GitConfig.HooksPrefix + hookName;
|
||||
string mergedHooks = MergeHooks(context, configSetting, hookName);
|
||||
|
||||
string contents = string.Format(HooksConfigContentTemplate, configSetting, mergedHooks);
|
||||
Exception ex;
|
||||
if (!context.FileSystem.TryWriteTempFileAndRename(targetPath, contents, out ex))
|
||||
{
|
||||
throw new RetryableException("Error installing " + targetPath, ex);
|
||||
}
|
||||
}
|
||||
catch (IOException io)
|
||||
{
|
||||
throw new RetryableException("Error installing " + targetPath, io);
|
||||
}
|
||||
}
|
||||
|
||||
private static string MergeHooks(GVFSContext context, string configSettingName, string hookName)
|
||||
{
|
||||
GitProcess configProcess = new GitProcess(context.Enlistment);
|
||||
string filename;
|
||||
string[] defaultHooksLines = { };
|
||||
|
||||
if (configProcess.TryGetFromConfig(configSettingName, forceOutsideEnlistment: true, value: out filename))
|
||||
{
|
||||
filename = filename.Trim(' ', '\n');
|
||||
defaultHooksLines = File.ReadAllLines(filename);
|
||||
}
|
||||
|
||||
return MergeHooksData(defaultHooksLines, filename, hookName);
|
||||
}
|
||||
|
||||
public class HooksConfigurationException : Exception
|
||||
{
|
||||
public HooksConfigurationException(string message)
|
||||
|
|
|
@ -7,5 +7,6 @@
|
|||
void MoveAndOverwriteFile(string sourceFileName, string destinationFilename);
|
||||
void CreateHardLink(string newFileName, string existingFileName);
|
||||
bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage);
|
||||
void ChangeMode(string path, int mode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp2.1;netstandard2.0</TargetFrameworks>
|
||||
<Platforms>x64</Platforms>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -18,12 +18,6 @@ namespace GVFS.Common
|
|||
public const string WorkingDirectoryRootName = "src";
|
||||
public const string UnattendedEnvironmentVariable = "GVFS_UNATTENDED";
|
||||
|
||||
public const string GVFSExecutableName = "GVFS.exe";
|
||||
public const string GVFSHooksExecutableName = "GVFS.Hooks.exe";
|
||||
public const string GVFSReadObjectHookExecutableName = "GVFS.ReadObjectHook.exe";
|
||||
public const string GVFSVirtualFileSystemHookExecutableName = "GVFS.VirtualFileSystemHook.exe";
|
||||
public const string MountExecutableName = "GVFS.Mount.exe";
|
||||
public const string ExecutableExtension = ".exe";
|
||||
public const string GitIsNotInstalledError = "Could not find git.exe. Ensure that Git is installed.";
|
||||
|
||||
public static class GitConfig
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace GVFS.Common
|
|||
gvfsHooksRoot,
|
||||
flushFileBuffersForPacks: true)
|
||||
{
|
||||
this.NamedPipeName = Paths.GetNamedPipeName(this.EnlistmentRoot);
|
||||
this.NamedPipeName = GVFSPlatform.Instance.GetNamedPipeName(this.EnlistmentRoot);
|
||||
this.DotGVFSRoot = Path.Combine(this.EnlistmentRoot, GVFSConstants.DotGVFS.Root);
|
||||
this.GVFSLogsRoot = Path.Combine(this.EnlistmentRoot, GVFSConstants.DotGVFS.LogPath);
|
||||
this.LocalObjectsRoot = Path.Combine(this.WorkingDirectoryRoot, GVFSConstants.DotGit.Objects.Root);
|
||||
|
@ -119,7 +119,7 @@ namespace GVFS.Common
|
|||
public static bool WaitUntilMounted(string enlistmentRoot, bool unattended, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
using (NamedPipeClient pipeClient = new NamedPipeClient(Paths.GetNamedPipeName(enlistmentRoot)))
|
||||
using (NamedPipeClient pipeClient = new NamedPipeClient(GVFSPlatform.Instance.GetNamedPipeName(enlistmentRoot)))
|
||||
{
|
||||
int timeout = unattended ? 300000 : 60000;
|
||||
if (!pipeClient.Connect(timeout))
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace GVFS.Common
|
|||
string fullCommand,
|
||||
int pid,
|
||||
bool isElevated,
|
||||
bool isConsoleOutputRedirectedToFile,
|
||||
bool checkAvailabilityOnly,
|
||||
string gvfsEnlistmentRoot,
|
||||
out string result)
|
||||
|
@ -90,7 +91,7 @@ namespace GVFS.Common
|
|||
waitForLock,
|
||||
message,
|
||||
output: Console.Out,
|
||||
showSpinner: !ConsoleHelper.IsConsoleOutputRedirectedToFile(),
|
||||
showSpinner: !isConsoleOutputRedirectedToFile,
|
||||
gvfsLogEnlistmentRoot: gvfsEnlistmentRoot);
|
||||
}
|
||||
|
||||
|
@ -104,6 +105,7 @@ namespace GVFS.Common
|
|||
string fullCommand,
|
||||
int pid,
|
||||
bool isElevated,
|
||||
bool isConsoleOutputRedirectedToFile,
|
||||
Action<NamedPipeMessages.ReleaseLock.Response> responseHandler,
|
||||
string gvfsEnlistmentRoot,
|
||||
string waitingMessage = "",
|
||||
|
@ -124,7 +126,7 @@ namespace GVFS.Common
|
|||
return ConsoleHelper.ActionResult.Success;
|
||||
};
|
||||
|
||||
if (unattended || ConsoleHelper.IsConsoleOutputRedirectedToFile())
|
||||
if (unattended || isConsoleOutputRedirectedToFile)
|
||||
{
|
||||
releaseLock();
|
||||
}
|
||||
|
|
|
@ -396,7 +396,7 @@ namespace GVFS.Common
|
|||
if (this.externalLockHolder != null)
|
||||
{
|
||||
int pid = this.externalLockHolder.PID;
|
||||
externalHolderTerminatedWithoutReleasingLock = !ProcessHelper.IsProcessActive(pid);
|
||||
externalHolderTerminatedWithoutReleasingLock = !GVFSPlatform.Instance.IsProcessActive(pid);
|
||||
}
|
||||
|
||||
return this.externalLockHolder;
|
||||
|
|
|
@ -10,6 +10,11 @@ namespace GVFS.Common
|
|||
{
|
||||
public abstract class GVFSPlatform
|
||||
{
|
||||
public GVFSPlatform(string executableExtension)
|
||||
{
|
||||
this.Constants = new GVFSPlatformConstants(executableExtension);
|
||||
}
|
||||
|
||||
public static GVFSPlatform Instance { get; private set; }
|
||||
|
||||
public abstract IKernelDriver KernelDriver { get; }
|
||||
|
@ -18,6 +23,8 @@ namespace GVFS.Common
|
|||
public abstract IPlatformFileSystem FileSystem { get; }
|
||||
public virtual bool IsUnderConstruction { get; } = false;
|
||||
public virtual bool SupportsGVFSService { get; } = true;
|
||||
public GVFSPlatformConstants Constants { get; }
|
||||
|
||||
public static void Register(GVFSPlatform platform)
|
||||
{
|
||||
if (GVFSPlatform.Instance != null)
|
||||
|
@ -29,13 +36,19 @@ namespace GVFS.Common
|
|||
}
|
||||
|
||||
public abstract void StartBackgroundProcess(string programName, string[] args);
|
||||
public abstract bool IsProcessActive(int processId);
|
||||
|
||||
public abstract string GetNamedPipeName(string enlistmentRoot);
|
||||
public abstract NamedPipeServerStream CreatePipeByName(string pipeName);
|
||||
|
||||
public abstract string GetOSVersionInformation();
|
||||
public abstract void InitializeEnlistmentACLs(string enlistmentPath);
|
||||
public abstract bool IsElevated();
|
||||
public abstract string GetCurrentUser();
|
||||
public abstract void ConfigureVisualStudio(string gitBinPath, ITracer tracer);
|
||||
public abstract bool TryGetGVFSHooksPathAndVersion(out string hooksPaths, out string hooksVersion, out string error);
|
||||
|
||||
public abstract bool TryGetGVFSHooksPathAndVersion(out string hooksPaths, out string hooksVersion, out string error);
|
||||
public abstract bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage);
|
||||
|
||||
public abstract InProcEventListener CreateTelemetryListenerIfEnabled(string providerName);
|
||||
|
||||
|
@ -58,5 +71,39 @@ namespace GVFS.Common
|
|||
pathRoot = Path.GetPathRoot(normalizedPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
public class GVFSPlatformConstants
|
||||
{
|
||||
public GVFSPlatformConstants(string executableExtension)
|
||||
{
|
||||
this.ExecutableExtension = executableExtension;
|
||||
}
|
||||
|
||||
public string ExecutableExtension { get; }
|
||||
public string GVFSExecutableName
|
||||
{
|
||||
get { return "GVFS" + this.ExecutableExtension; }
|
||||
}
|
||||
|
||||
public string GVFSHooksExecutableName
|
||||
{
|
||||
get { return "GVFS.Hooks" + this.ExecutableExtension; }
|
||||
}
|
||||
|
||||
public string GVFSReadObjectHookExecutableName
|
||||
{
|
||||
get { return "GVFS.ReadObjectHook" + this.ExecutableExtension; }
|
||||
}
|
||||
|
||||
public string GVFSVirtualFileSystemHookExecutableName
|
||||
{
|
||||
get { return "GVFS.VirtualFileSystemHook" + this.ExecutableExtension; }
|
||||
}
|
||||
|
||||
public string MountExecutableName
|
||||
{
|
||||
get { return "GVFS.Mount" + this.ExecutableExtension; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace GVFS.Common.Git
|
|||
return output.Succeeded && output.Result.Success;
|
||||
}
|
||||
|
||||
public void DeleteStaleTempPrefetchPackAndIdxs()
|
||||
public virtual void DeleteStaleTempPrefetchPackAndIdxs()
|
||||
{
|
||||
string[] staleTempPacks = this.ReadPackFileNames(Path.Combine(this.Enlistment.GitPackRoot, GitObjects.TempPackFolder), GVFSConstants.PrefetchPackPrefix);
|
||||
foreach (string stalePackPath in staleTempPacks)
|
||||
|
@ -92,7 +92,7 @@ namespace GVFS.Common.Git
|
|||
}
|
||||
}
|
||||
|
||||
public bool TryDownloadPrefetchPacks(long latestTimestamp, out List<string> packIndexes)
|
||||
public virtual bool TryDownloadPrefetchPacks(long latestTimestamp, out List<string> packIndexes)
|
||||
{
|
||||
EventMetadata metadata = CreateEventMetadata();
|
||||
metadata.Add("latestTimestamp", latestTimestamp);
|
||||
|
|
|
@ -13,7 +13,10 @@ namespace GVFS.Common.Git
|
|||
{
|
||||
public class GitProcess
|
||||
{
|
||||
private const int HResultEHANDLE = -2147024890; // 0x80070006 E_HANDLE
|
||||
|
||||
private static readonly Encoding UTF8NoBOM = new UTF8Encoding(false);
|
||||
private static bool failedToSetEncoding = false;
|
||||
|
||||
private object executionLock = new object();
|
||||
|
||||
|
@ -28,7 +31,22 @@ namespace GVFS.Common.Git
|
|||
// We need to use the BOM-less encoding because Git doesn't understand it
|
||||
if (Console.InputEncoding.CodePage == UTF8NoBOM.CodePage)
|
||||
{
|
||||
Console.InputEncoding = UTF8NoBOM;
|
||||
try
|
||||
{
|
||||
Console.InputEncoding = UTF8NoBOM;
|
||||
}
|
||||
catch (IOException ex) when (ex.HResult == HResultEHANDLE)
|
||||
{
|
||||
// If the standard input for a console is redirected / not available,
|
||||
// then we might not be able to set the InputEncoding here.
|
||||
// In practice, this can happen if we attempt to run a GitProcess from within a Service,
|
||||
// such as GVFS.Service.
|
||||
// Record that we failed to set the encoding, but do not quite the process.
|
||||
// This means that git commands that use stdin will not work, but
|
||||
// for our scenarios, we do not expect these calls at this this time.
|
||||
// We will check and fail if we attempt to write to stdin in in a git call below.
|
||||
GitProcess.failedToSetEncoding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,6 +435,11 @@ namespace GVFS.Common.Git
|
|||
Action<string> parseStdOutLine,
|
||||
int timeoutMs)
|
||||
{
|
||||
if (failedToSetEncoding && writeStdIn != null)
|
||||
{
|
||||
return new Result(string.Empty, "Attempting to use to stdin, but the process does not have the right input encodings set.", Result.GenericFailureCode);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// From https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace GVFS.Common.NamedPipes
|
|||
|
||||
try
|
||||
{
|
||||
this.writer.WriteLine(message);
|
||||
this.writer.WritePlatformIndependentLine(message);
|
||||
this.writer.Flush();
|
||||
}
|
||||
catch (IOException e)
|
||||
|
|
|
@ -216,7 +216,7 @@ namespace GVFS.Common.NamedPipes
|
|||
{
|
||||
try
|
||||
{
|
||||
this.writer.WriteLine(message);
|
||||
this.writer.WritePlatformIndependentLine(message);
|
||||
this.writer.Flush();
|
||||
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
using System.IO;
|
||||
|
||||
namespace GVFS.Common.NamedPipes
|
||||
{
|
||||
public static class NamedPipeStreamWriterExtensions
|
||||
{
|
||||
public const int Foo = 0;
|
||||
|
||||
public static void WritePlatformIndependentLine(this StreamWriter writer, string value)
|
||||
{
|
||||
// WriteLine is not platform independent as on some platforms it terminates lines with \r\n
|
||||
// and on others it uses \n
|
||||
writer.Write(value + "\n");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,11 +11,6 @@ namespace GVFS.Common
|
|||
return GetRoot(directory, GVFSConstants.DotGit.Root);
|
||||
}
|
||||
|
||||
public static string GetNamedPipeName(string enlistmentRoot)
|
||||
{
|
||||
return "GVFS_" + enlistmentRoot.ToUpper().Replace(':', '_').Replace('/', '_');
|
||||
}
|
||||
|
||||
public static string GetServiceDataRoot(string serviceName)
|
||||
{
|
||||
return Path.Combine(
|
||||
|
@ -68,5 +63,10 @@ namespace GVFS.Common
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string ConvertPathToGitFormat(string path)
|
||||
{
|
||||
return path.Replace(Path.DirectorySeparatorChar, GVFSConstants.GitPathSeparator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.Common.Prefetch
|
||||
{
|
||||
public class BackgroundPrefetcher : IDisposable
|
||||
{
|
||||
private const string TelemetryKey = nameof(BackgroundPrefetcher);
|
||||
private readonly TimeSpan timerPeriod = TimeSpan.FromMinutes(15);
|
||||
private readonly TimeSpan timeBetweenPrefetches = TimeSpan.FromMinutes(70);
|
||||
|
||||
private ITracer tracer;
|
||||
private GVFSEnlistment enlistment;
|
||||
private PhysicalFileSystem fileSystem;
|
||||
private GitObjects gitObjects;
|
||||
private Timer prefetchJobTimer;
|
||||
private Thread prefetchJobThread;
|
||||
|
||||
public BackgroundPrefetcher(ITracer tracer, GVFSEnlistment enlistment, PhysicalFileSystem fileSystem, GitObjects gitObjects)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
this.enlistment = enlistment;
|
||||
this.fileSystem = fileSystem;
|
||||
this.gitObjects = gitObjects;
|
||||
|
||||
this.prefetchJobThread = null;
|
||||
|
||||
this.prefetchJobTimer = new Timer((state) => this.LaunchPrefetchJobIfIdle(), null, this.timerPeriod, this.timerPeriod);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.prefetchJobTimer?.Dispose();
|
||||
this.prefetchJobTimer = null;
|
||||
}
|
||||
|
||||
public bool LaunchPrefetchJobIfIdle()
|
||||
{
|
||||
if (this.prefetchJobThread?.IsAlive == true)
|
||||
{
|
||||
this.tracer.RelatedInfo(nameof(BackgroundPrefetcher) + ": background thread not idle, skipping timed start");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.prefetchJobThread = new Thread(() => this.BackgroundPrefetch());
|
||||
this.prefetchJobThread.IsBackground = true;
|
||||
this.prefetchJobThread.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is used for test purposes only.
|
||||
/// </summary>
|
||||
public void WaitForPrefetchToFinish()
|
||||
{
|
||||
this.prefetchJobThread?.Join();
|
||||
}
|
||||
|
||||
private void BackgroundPrefetch()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.BackgroundPrefetch), EventLevel.Informational))
|
||||
{
|
||||
long last;
|
||||
string error;
|
||||
|
||||
if (!CommitPrefetcher.TryGetMaxGoodPrefetchTimestamp(activity, this.enlistment, this.fileSystem, this.gitObjects, out last, out error))
|
||||
{
|
||||
activity.RelatedError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime lastDateTime = EpochConverter.FromUnixEpochSeconds(last);
|
||||
DateTime now = DateTime.UtcNow;
|
||||
|
||||
if (now <= lastDateTime + this.timeBetweenPrefetches)
|
||||
{
|
||||
activity.RelatedInfo(TelemetryKey + ": Skipping prefetch since most-recent prefetch ({0}) is too close to now ({1})", lastDateTime, now);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CommitPrefetcher.TryPrefetchCommitsAndTrees(activity, this.enlistment, this.fileSystem, this.gitObjects, out error))
|
||||
{
|
||||
activity.RelatedError($"{TelemetryKey}: {nameof(CommitPrefetcher.TryPrefetchCommitsAndTrees)} failed with error '{error}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
this.tracer.RelatedInfo(TelemetryKey + ": Aborting prefetch background thread due to ThreadAbortException");
|
||||
return;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Method", nameof(this.BackgroundPrefetch));
|
||||
metadata.Add("ExceptionMessage", e.Message);
|
||||
metadata.Add("StackTrace", e.StackTrace);
|
||||
this.tracer.RelatedWarning(
|
||||
metadata: metadata,
|
||||
message: TelemetryKey + ": IOException while running prefetch background thread (non-fatal): " + e.Message,
|
||||
keywords: Keywords.Telemetry);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Method", nameof(this.BackgroundPrefetch));
|
||||
metadata.Add("ExceptionMessage", e.Message);
|
||||
metadata.Add("StackTrace", e.StackTrace);
|
||||
this.tracer.RelatedError(
|
||||
metadata: metadata,
|
||||
message: TelemetryKey + ": Unexpected Exception while running prefetch background thread (fatal): " + e.Message,
|
||||
keywords: Keywords.Telemetry);
|
||||
Environment.Exit((int)ReturnCode.GenericError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ using System.Threading;
|
|||
|
||||
namespace GVFS.Common.Prefetch
|
||||
{
|
||||
public class PrefetchHelper
|
||||
public class BlobPrefetcher
|
||||
{
|
||||
protected const string RefsHeadsGitPath = "refs/heads/";
|
||||
|
||||
|
@ -29,10 +29,10 @@ namespace GVFS.Common.Prefetch
|
|||
|
||||
protected readonly bool SkipConfigUpdate;
|
||||
|
||||
private const string AreaPath = nameof(PrefetchHelper);
|
||||
private const string AreaPath = nameof(BlobPrefetcher);
|
||||
private static string pathSeparatorString = Path.DirectorySeparatorChar.ToString();
|
||||
|
||||
public PrefetchHelper(
|
||||
public BlobPrefetcher(
|
||||
ITracer tracer,
|
||||
Enlistment enlistment,
|
||||
GitObjectsHttpRequestor objectRequestor,
|
||||
|
@ -67,7 +67,7 @@ namespace GVFS.Common.Prefetch
|
|||
{
|
||||
folderListOutput.AddRange(
|
||||
foldersInput.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(path => PrefetchHelper.ToAbsolutePath(enlistment, path, isFolder: true)));
|
||||
.Select(path => BlobPrefetcher.ToAbsolutePath(enlistment, path, isFolder: true)));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(folderListFile))
|
||||
{
|
||||
|
@ -77,7 +77,7 @@ namespace GVFS.Common.Prefetch
|
|||
.Select(line => line.Trim())
|
||||
.Where(line => !string.IsNullOrEmpty(line))
|
||||
.Where(line => !line.StartsWith(GVFSConstants.GitCommentSign.ToString()))
|
||||
.Select(path => PrefetchHelper.ToAbsolutePath(enlistment, path, isFolder: true));
|
||||
.Select(path => BlobPrefetcher.ToAbsolutePath(enlistment, path, isFolder: true));
|
||||
|
||||
folderListOutput.AddRange(allLines);
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ namespace GVFS.Common.Prefetch
|
|||
{
|
||||
fileListOutput.AddRange(
|
||||
filesInput.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(path => PrefetchHelper.ToAbsolutePath(enlistment, path, isFolder: false)));
|
||||
.Select(path => BlobPrefetcher.ToAbsolutePath(enlistment, path, isFolder: false)));
|
||||
|
||||
foreach (string file in fileListOutput)
|
||||
{
|
|
@ -0,0 +1,235 @@
|
|||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.NamedPipes;
|
||||
using GVFS.Common.Tracing;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.Common.Prefetch
|
||||
{
|
||||
public static class CommitPrefetcher
|
||||
{
|
||||
private const int IoFailureRetryDelayMS = 50;
|
||||
private const int LockWaitTimeMs = 100;
|
||||
private const int WaitingOnLockLogThreshold = 50;
|
||||
private const string PrefetchCommitsAndTreesLock = "prefetch-commits-trees.lock";
|
||||
|
||||
public static bool TryPrefetchCommitsAndTrees(
|
||||
ITracer tracer,
|
||||
GVFSEnlistment enlistment,
|
||||
PhysicalFileSystem fileSystem,
|
||||
GitObjects gitObjects,
|
||||
out string error)
|
||||
{
|
||||
List<string> packIndexes;
|
||||
using (FileBasedLock prefetchLock = new FileBasedLock(
|
||||
fileSystem,
|
||||
tracer,
|
||||
Path.Combine(enlistment.GitPackRoot, PrefetchCommitsAndTreesLock),
|
||||
enlistment.EnlistmentRoot,
|
||||
overwriteExistingLock: true))
|
||||
{
|
||||
WaitUntilLockIsAcquired(tracer, prefetchLock);
|
||||
long maxGoodTimeStamp;
|
||||
|
||||
gitObjects.DeleteStaleTempPrefetchPackAndIdxs();
|
||||
|
||||
if (!TryGetMaxGoodPrefetchTimestamp(tracer, enlistment, fileSystem, gitObjects, out maxGoodTimeStamp, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gitObjects.TryDownloadPrefetchPacks(maxGoodTimeStamp, out packIndexes))
|
||||
{
|
||||
error = "Failed to download prefetch packs";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (packIndexes == null || packIndexes.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// We make a best-effort request to run MIDX and commit-graph writes
|
||||
using (NamedPipeClient pipeClient = new NamedPipeClient(enlistment.NamedPipeName))
|
||||
{
|
||||
if (!pipeClient.Connect())
|
||||
{
|
||||
tracer.RelatedWarning(
|
||||
metadata: null,
|
||||
message: "Failed to connect to GVFS. Skipping post-fetch job request.",
|
||||
keywords: Keywords.Telemetry);
|
||||
return true;
|
||||
}
|
||||
|
||||
NamedPipeMessages.RunPostFetchJob.Request request = new NamedPipeMessages.RunPostFetchJob.Request(packIndexes);
|
||||
if (pipeClient.TrySendRequest(request.CreateMessage()))
|
||||
{
|
||||
NamedPipeMessages.Message response;
|
||||
|
||||
if (pipeClient.TryReadResponse(out response))
|
||||
{
|
||||
tracer.RelatedInfo("Requested post-fetch job with resonse '{0}'", response.Header);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tracer.RelatedWarning(
|
||||
metadata: null,
|
||||
message: "Requested post-fetch job failed to respond",
|
||||
keywords: Keywords.Telemetry);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tracer.RelatedWarning(
|
||||
metadata: null,
|
||||
message: "Message to named pipe failed to send, skipping post-fetch job request.",
|
||||
keywords: Keywords.Telemetry);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetMaxGoodPrefetchTimestamp(
|
||||
ITracer tracer,
|
||||
GVFSEnlistment enlistment,
|
||||
PhysicalFileSystem fileSystem,
|
||||
GitObjects gitObjects,
|
||||
out long maxGoodTimestamp,
|
||||
out string error)
|
||||
{
|
||||
fileSystem.CreateDirectory(enlistment.GitPackRoot);
|
||||
|
||||
string[] packs = gitObjects.ReadPackFileNames(enlistment.GitPackRoot, GVFSConstants.PrefetchPackPrefix);
|
||||
List<PrefetchPackInfo> orderedPacks = packs
|
||||
.Where(pack => GetTimestamp(pack).HasValue)
|
||||
.Select(pack => new PrefetchPackInfo(GetTimestamp(pack).Value, pack))
|
||||
.OrderBy(packInfo => packInfo.Timestamp)
|
||||
.ToList();
|
||||
|
||||
maxGoodTimestamp = -1;
|
||||
|
||||
int firstBadPack = -1;
|
||||
for (int i = 0; i < orderedPacks.Count; ++i)
|
||||
{
|
||||
long timestamp = orderedPacks[i].Timestamp;
|
||||
string packPath = orderedPacks[i].Path;
|
||||
string idxPath = Path.ChangeExtension(packPath, ".idx");
|
||||
if (!fileSystem.FileExists(idxPath))
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("pack", packPath);
|
||||
metadata.Add("idxPath", idxPath);
|
||||
metadata.Add("timestamp", timestamp);
|
||||
GitProcess.Result indexResult = gitObjects.IndexPackFile(packPath);
|
||||
if (indexResult.HasErrors)
|
||||
{
|
||||
firstBadPack = i;
|
||||
|
||||
metadata.Add("Errors", indexResult.Errors);
|
||||
tracer.RelatedWarning(metadata, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}: Found pack file that's missing idx file, and failed to regenerate idx");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxGoodTimestamp = timestamp;
|
||||
|
||||
metadata.Add(TracingConstants.MessageKey.InfoMessage, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}: Found pack file that's missing idx file, and regenerated idx");
|
||||
tracer.RelatedEvent(EventLevel.Informational, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}_RebuildIdx", metadata);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
maxGoodTimestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstBadPack != -1)
|
||||
{
|
||||
const int MaxDeleteRetries = 200; // 200 * IoFailureRetryDelayMS (50ms) = 10 seconds
|
||||
const int RetryLoggingThreshold = 40; // 40 * IoFailureRetryDelayMS (50ms) = 2 seconds
|
||||
|
||||
// Delete packs and indexes in reverse order so that if prefetch is killed, subseqeuent prefetch commands will
|
||||
// find the right starting spot.
|
||||
for (int i = orderedPacks.Count - 1; i >= firstBadPack; --i)
|
||||
{
|
||||
string packPath = orderedPacks[i].Path;
|
||||
string idxPath = Path.ChangeExtension(packPath, ".idx");
|
||||
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("path", idxPath);
|
||||
metadata.Add(TracingConstants.MessageKey.InfoMessage, $"{nameof(TryGetMaxGoodPrefetchTimestamp)} deleting bad idx file");
|
||||
tracer.RelatedEvent(EventLevel.Informational, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}_DeleteBadIdx", metadata);
|
||||
if (!fileSystem.TryWaitForDelete(tracer, idxPath, IoFailureRetryDelayMS, MaxDeleteRetries, RetryLoggingThreshold))
|
||||
{
|
||||
error = $"Unable to delete {idxPath}";
|
||||
return false;
|
||||
}
|
||||
|
||||
metadata = new EventMetadata();
|
||||
metadata.Add("path", packPath);
|
||||
metadata.Add(TracingConstants.MessageKey.InfoMessage, $"{nameof(TryGetMaxGoodPrefetchTimestamp)} deleting bad pack file");
|
||||
tracer.RelatedEvent(EventLevel.Informational, $"{nameof(TryGetMaxGoodPrefetchTimestamp)}_DeleteBadPack", metadata);
|
||||
if (!fileSystem.TryWaitForDelete(tracer, packPath, IoFailureRetryDelayMS, MaxDeleteRetries, RetryLoggingThreshold))
|
||||
{
|
||||
error = $"Unable to delete {packPath}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static long? GetTimestamp(string packName)
|
||||
{
|
||||
string filename = Path.GetFileName(packName);
|
||||
if (!filename.StartsWith(GVFSConstants.PrefetchPackPrefix))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string[] parts = filename.Split('-');
|
||||
long parsed;
|
||||
if (parts.Length > 1 && long.TryParse(parts[1], out parsed))
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void WaitUntilLockIsAcquired(ITracer tracer, FileBasedLock fileBasedLock)
|
||||
{
|
||||
int attempt = 0;
|
||||
while (!fileBasedLock.TryAcquireLockAndDeleteOnClose())
|
||||
{
|
||||
Thread.Sleep(LockWaitTimeMs);
|
||||
++attempt;
|
||||
if (attempt == WaitingOnLockLogThreshold)
|
||||
{
|
||||
attempt = 0;
|
||||
tracer.RelatedInfo("WaitUntilLockIsAcquired: Waiting to acquire prefetch lock");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PrefetchPackInfo
|
||||
{
|
||||
public PrefetchPackInfo(long timestamp, string path)
|
||||
{
|
||||
this.Timestamp = timestamp;
|
||||
this.Path = path;
|
||||
}
|
||||
|
||||
public long Timestamp { get; }
|
||||
public string Path { get; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,25 +9,6 @@ namespace GVFS.Common
|
|||
{
|
||||
public static class ProcessHelper
|
||||
{
|
||||
private const int StillActive = 259; /* from Win32 STILL_ACTIVE */
|
||||
|
||||
public static bool IsProcessActive(int processId)
|
||||
{
|
||||
using (SafeFileHandle process = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.QueryLimitedInformation, false, processId))
|
||||
{
|
||||
if (!process.IsInvalid)
|
||||
{
|
||||
uint exitCode;
|
||||
if (NativeMethods.GetExitCodeProcess(process, out exitCode) && exitCode == StillActive)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static ProcessResult Run(string programName, string args, bool redirectOutput = true)
|
||||
{
|
||||
ProcessStartInfo processInfo = new ProcessStartInfo(programName);
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace GVFS.FunctionalTests.LockHolder
|
|||
throw new Exception("Unable to get GVFS Enlistment root: " + errorMessage);
|
||||
}
|
||||
|
||||
string enlistmentPipename = Paths.GetNamedPipeName(enlistmentRoot);
|
||||
string enlistmentPipename = GVFSPlatform.Instance.GetNamedPipeName(enlistmentRoot);
|
||||
|
||||
AcquireLock(enlistmentPipename);
|
||||
|
||||
|
@ -63,6 +63,7 @@ namespace GVFS.FunctionalTests.LockHolder
|
|||
fullCommand: AcquireGVFSLockVerb.fullCommand,
|
||||
pid: pid,
|
||||
isElevated: false,
|
||||
isConsoleOutputRedirectedToFile: false,
|
||||
checkAvailabilityOnly: false,
|
||||
gvfsEnlistmentRoot: null,
|
||||
result: out result))
|
||||
|
|
|
@ -57,6 +57,9 @@
|
|||
<Compile Include="AcquireGVFSLock.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs">
|
||||
<Link>PlatformLoader.Windows.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
|
@ -71,6 +74,10 @@
|
|||
<Project>{4CE404E7-D3FC-471C-993C-64615861EA63}</Project>
|
||||
<Name>GVFS.Platform.Windows</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GVFS.Virtualization\GVFS.Virtualization.csproj">
|
||||
<Project>{f468b05a-95e5-46bc-8c67-b80a78527b7d}</Project>
|
||||
<Name>GVFS.Virtualization</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets" Condition="Exists('..\..\..\packages\StyleCop.MSBuild.5.0.0\build\StyleCop.MSBuild.targets')" />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using CommandLine;
|
||||
using GVFS.PlatformLoader;
|
||||
|
||||
namespace GVFS.FunctionalTests.LockHolder
|
||||
{
|
||||
|
@ -6,6 +7,8 @@ namespace GVFS.FunctionalTests.LockHolder
|
|||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GVFSPlatformLoader.Initialize();
|
||||
|
||||
Parser.Default.ParseArguments<AcquireGVFSLockVerb>(args)
|
||||
.WithParsed(acquireGVFSLock => acquireGVFSLock.Execute());
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@
|
|||
xcopy /Y $(BuildOutputDir)\GVFS.Mount.Windows\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\FastFetch\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.NativeTests\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Hooks\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.FunctionalTests.LockHolder\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
|
||||
REM If there is an inbox version of the ProjFS dll, we must use that one to guarantee compat with the sys, so delete whatever is in our TargetDir
|
||||
|
|
|
@ -264,6 +264,7 @@ namespace GVFS.FunctionalTests.Windows.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountCreatesModifiedPathsDatabase()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.FunctionalTests.FileSystemRunners
|
||||
{
|
||||
|
@ -53,6 +55,31 @@ namespace GVFS.FunctionalTests.FileSystemRunners
|
|||
}
|
||||
}
|
||||
|
||||
public static void DeleteDirectoryWithUnlimitedRetries(string path)
|
||||
{
|
||||
BashRunner runner = new BashRunner();
|
||||
bool pathExists = Directory.Exists(path);
|
||||
int retryCount = 0;
|
||||
while (pathExists)
|
||||
{
|
||||
string output = runner.DeleteDirectory(path);
|
||||
pathExists = Directory.Exists(path);
|
||||
if (pathExists)
|
||||
{
|
||||
++retryCount;
|
||||
Thread.Sleep(500);
|
||||
if (retryCount > 10)
|
||||
{
|
||||
retryCount = 0;
|
||||
if (Debugger.IsAttached)
|
||||
{
|
||||
Debugger.Break();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool FileExists(string path)
|
||||
{
|
||||
string bashPath = this.ConvertWinPathToBashPath(path);
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace GVFS.FunctionalTests.FileSystemRunners
|
|||
}
|
||||
}
|
||||
|
||||
public static void DeleteDirectoryWithRetry(string path)
|
||||
public static void DeleteDirectoryWithUnlimitedRetries(string path)
|
||||
{
|
||||
CmdRunner runner = new CmdRunner();
|
||||
bool pathExists = Directory.Exists(path);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
|||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Mount.Windows\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\FastFetch\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.NativeTests\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Hooks\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.FunctionalTests.LockHolder\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
@ -35,6 +36,9 @@ namespace GVFS.FunctionalTests
|
|||
|
||||
GVFSTestConfig.LocalCacheRoot = runner.GetCustomArgWithParam("--shared-gvfs-cache-root");
|
||||
|
||||
List<string> includeCategories = new List<string>();
|
||||
List<string> excludeCategories = new List<string>();
|
||||
|
||||
if (runner.HasCustomArg("--full-suite"))
|
||||
{
|
||||
Console.WriteLine("Running the full suite of tests");
|
||||
|
@ -50,22 +54,22 @@ namespace GVFS.FunctionalTests
|
|||
}
|
||||
else
|
||||
{
|
||||
runner.ExcludeCategory(Categories.FullSuiteOnly);
|
||||
excludeCategories.Add(Categories.FullSuiteOnly);
|
||||
GVFSTestConfig.FileSystemRunners = FileSystemRunners.FileSystemRunner.DefaultRunners;
|
||||
}
|
||||
|
||||
if (runner.HasCustomArg("--windows-only"))
|
||||
{
|
||||
runner.IncludeCategory(Categories.Windows);
|
||||
includeCategories.Add(Categories.Windows);
|
||||
}
|
||||
|
||||
if (runner.HasCustomArg("--mac-only"))
|
||||
{
|
||||
runner.IncludeCategory(Categories.Mac.M1);
|
||||
runner.ExcludeCategory(Categories.Mac.M2);
|
||||
runner.ExcludeCategory(Categories.Mac.M3);
|
||||
runner.ExcludeCategory(Categories.Mac.M4);
|
||||
runner.ExcludeCategory(Categories.Windows);
|
||||
includeCategories.Add(Categories.Mac.M1);
|
||||
includeCategories.Add(Categories.Mac.M2);
|
||||
excludeCategories.Add(Categories.Mac.M3);
|
||||
excludeCategories.Add(Categories.Mac.M4);
|
||||
excludeCategories.Add(Categories.Windows);
|
||||
}
|
||||
|
||||
GVFSTestConfig.RepoToClone =
|
||||
|
@ -73,7 +77,7 @@ namespace GVFS.FunctionalTests
|
|||
?? Properties.Settings.Default.RepoToClone;
|
||||
|
||||
RunBeforeAnyTests();
|
||||
Environment.ExitCode = runner.RunTests();
|
||||
Environment.ExitCode = runner.RunTests(includeCategories, excludeCategories);
|
||||
RunAfterAllTests();
|
||||
|
||||
if (Debugger.IsAttached)
|
||||
|
|
|
@ -20,10 +20,11 @@ namespace GVFS.FunctionalTests.Properties
|
|||
public static string FastFetchControl { get; set; }
|
||||
public static string PathToGit { get; set; }
|
||||
public static string PathToGVFSService { get; set; }
|
||||
public static string BinaryFileNameExtension { get; set; }
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
CurrentDirectory = Path.GetFullPath(Path.GetDirectoryName(Environment.CommandLine.Trim('"', ' ')));
|
||||
CurrentDirectory = Path.GetFullPath(Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]));
|
||||
|
||||
RepoToClone = @"https://gvfs.visualstudio.com/ci/_git/ForTests";
|
||||
Commitish = @"FunctionalTests/20180214";
|
||||
|
@ -39,14 +40,17 @@ namespace GVFS.FunctionalTests.Properties
|
|||
FastFetchRoot = @"C:\Repos\GVFSFunctionalTests\FastFetch\Test";
|
||||
FastFetchControl = @"C:\Repos\GVFSFunctionalTests\FastFetch\Control";
|
||||
PathToGVFSService = @"GVFS.Service.exe";
|
||||
BinaryFileNameExtension = ".exe";
|
||||
}
|
||||
else
|
||||
{
|
||||
string root = "/GVFS.FT";
|
||||
EnlistmentRoot = Path.Combine(root, "test");
|
||||
ControlGitRepoRoot = Path.Combine(root, "control");
|
||||
PathToGVFS = "gvfs";
|
||||
PathToGit = "/usr/local/bin/git";
|
||||
PathToBash = "/bin/bash";
|
||||
BinaryFileNameExtension = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.FullSuiteOnly)]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public class CacheServerTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string CustomUrl = "https://myCache";
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
public void DehydrateShouldSucceedEvenIfObjectCacheIsDeleted()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
CmdRunner.DeleteDirectoryWithRetry(this.Enlistment.GetObjectRoot(this.fileSystem));
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.Enlistment.GetObjectRoot(this.fileSystem));
|
||||
this.DehydrateShouldSucceed("The repo was successfully dehydrated and remounted", confirm: true, noStatus: true);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
}
|
||||
|
||||
[TestCase, Order(1)]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void GitStatus()
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
|
@ -32,12 +33,14 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void GitLog()
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "log -n1", "commit", "Author:", "Date:");
|
||||
}
|
||||
|
||||
[TestCase, Order(3)]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void GitBranch()
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Properties;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
|
@ -45,7 +46,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
string readObjectPath = this.Enlistment.GetVirtualPathTo(".git", "hooks", "read-object.exe");
|
||||
string readObjectPath = this.Enlistment.GetVirtualPathTo(".git", "hooks", "read-object" + Settings.Default.BinaryFileNameExtension);
|
||||
readObjectPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(readObjectPath);
|
||||
readObjectPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
|
|
@ -14,6 +14,7 @@ using System.Text;
|
|||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixtureSource(typeof(FileSystemRunner), FileSystemRunner.TestRunners)]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public class WorkingDirectoryTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const int CurrentPlaceholderVersion = 1;
|
||||
|
@ -53,10 +54,13 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
[TestCase, Order(1)]
|
||||
public void ProjectedFileHasExpectedContents()
|
||||
{
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\ProjectedFileHasExpectedContents.cpp").ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "ProjectedFileHasExpectedContents.cpp")
|
||||
.ShouldBeAFile(this.fileSystem)
|
||||
.WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
[Category(Categories.Mac.M3)]
|
||||
public void StreamAccessReadWriteMemoryMappedProjectedFile()
|
||||
{
|
||||
string filename = @"Test_EPF_WorkingDirectoryTests\StreamAccessReadWriteMemoryMappedProjectedFile.cs";
|
||||
|
@ -120,6 +124,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
}
|
||||
|
||||
[TestCase, Order(3)]
|
||||
[Category(Categories.Mac.M3)]
|
||||
public void RandomAccessReadWriteMemoryMappedProjectedFile()
|
||||
{
|
||||
string filename = @"Test_EPF_WorkingDirectoryTests\RandomAccessReadWriteMemoryMappedProjectedFile.cs";
|
||||
|
@ -180,6 +185,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
}
|
||||
|
||||
[TestCase, Order(4)]
|
||||
[Category(Categories.Mac.M3)]
|
||||
public void StreamAndRandomAccessReadWriteMemoryMappedProjectedFile()
|
||||
{
|
||||
string filename = @"Test_EPF_WorkingDirectoryTests\StreamAndRandomAccessReadWriteMemoryMappedProjectedFile.cs";
|
||||
|
@ -274,6 +280,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
}
|
||||
|
||||
[TestCase, Order(5)]
|
||||
[Category(Categories.Mac.M3)]
|
||||
public void MoveProjectedFileToInvalidFolder()
|
||||
{
|
||||
string targetFolderName = "test_folder";
|
||||
|
@ -299,7 +306,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
public void EnumerateAndReadDoesNotChangeEnumerationOrder()
|
||||
{
|
||||
string folderVirtualPath = this.Enlistment.GetVirtualPathTo("EnumerateAndReadTestFiles");
|
||||
NativeTests.EnumerateAndReadDoesNotChangeEnumerationOrder(folderVirtualPath).ShouldEqual(true);
|
||||
this.EnumerateAndReadShouldNotChangeEnumerationOrder(folderVirtualPath);
|
||||
folderVirtualPath.ShouldBeADirectory(this.fileSystem);
|
||||
folderVirtualPath.ShouldBeADirectory(this.fileSystem).WithItems();
|
||||
}
|
||||
|
@ -322,7 +329,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
[TestCase, Order(8)]
|
||||
public void HydratingNestedFileUsesNameCaseFromRepo()
|
||||
{
|
||||
string filePath = "GVFS\\FastFetch\\Properties\\AssemblyInfo.cs";
|
||||
string filePath = Path.Combine("GVFS", "FastFetch", "Properties", "AssemblyInfo.cs");
|
||||
string filePathAllCaps = filePath.ToUpper();
|
||||
string parentFolderVirtualPathAllCaps = this.Enlistment.GetVirtualPathTo(Path.GetDirectoryName(filePathAllCaps));
|
||||
parentFolderVirtualPathAllCaps.ShouldBeADirectory(this.fileSystem).WithItems().ShouldContainSingle(info => info.Name.Equals(Path.GetFileName(filePath), StringComparison.Ordinal));
|
||||
|
@ -346,6 +353,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
}
|
||||
|
||||
[TestCase, Order(9)]
|
||||
[Category(Categories.Mac.M3)]
|
||||
public void WriteToHydratedFileAfterRemount()
|
||||
{
|
||||
string fileName = "Test_EPF_WorkingDirectoryTests\\WriteToHydratedFileAfterRemount.cpp";
|
||||
|
@ -363,8 +371,8 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
|
||||
[TestCase, Order(10)]
|
||||
public void ReadDeepProjectedFile()
|
||||
{
|
||||
string testFilePath = "Test_EPF_WorkingDirectoryTests\\1\\2\\3\\4\\ReadDeepProjectedFile.cpp";
|
||||
{
|
||||
string testFilePath = Path.Combine("Test_EPF_WorkingDirectoryTests", "1", "2", "3", "4", "ReadDeepProjectedFile.cpp");
|
||||
this.Enlistment.GetVirtualPathTo(testFilePath).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
|
@ -372,28 +380,29 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
public void FilePlaceHolderHasVersionInfo()
|
||||
{
|
||||
string sha = "BB1C8B9ADA90D6B8F6C88F12C6DDB07C186155BD";
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest\\GVFlt_ModifyFileInScratchAndDir\\ModifyFileInScratchAndDir.txt");
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest", "GVFlt_ModifyFileInScratchAndDir", "ModifyFileInScratchAndDir.txt");
|
||||
virtualFilePath.ShouldBeAFile(this.fileSystem).WithContents();
|
||||
|
||||
ProcessResult revParseHeadResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse HEAD");
|
||||
string commitID = revParseHeadResult.Output.Trim();
|
||||
|
||||
NativeTests.PlaceHolderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, sha).ShouldEqual(true);
|
||||
this.PlaceholderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, sha).ShouldEqual(true);
|
||||
}
|
||||
|
||||
[TestCase, Order(12), Ignore("Results in an access violation in the functional test on the build server")]
|
||||
public void FolderPlaceHolderHasVersionInfo()
|
||||
{
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest\\GVFlt_ModifyFileInScratchAndDir");
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest", "GVFlt_ModifyFileInScratchAndDir");
|
||||
|
||||
ProcessResult revParseHeadResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse HEAD");
|
||||
string commitID = revParseHeadResult.Output.Trim();
|
||||
|
||||
NativeTests.PlaceHolderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, string.Empty).ShouldEqual(true);
|
||||
this.PlaceholderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, string.Empty).ShouldEqual(true);
|
||||
}
|
||||
|
||||
[TestCase, Order(13)]
|
||||
[Category(Categories.GitCommands)]
|
||||
[Category(Categories.Mac.M3)]
|
||||
public void FolderContentsProjectedAfterFolderCreateAndCheckout()
|
||||
{
|
||||
string folderName = "GVFlt_MultiThreadTest";
|
||||
|
@ -419,6 +428,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
|
||||
[TestCase, Order(14)]
|
||||
[Category(Categories.GitCommands)]
|
||||
[Category(Categories.Mac.M3)]
|
||||
public void FolderContentsCorrectAfterCreateNewFolderRenameAndCheckoutCommitWithSameFolder()
|
||||
{
|
||||
// 1ca414ced40f64bf94fc6c7f885974708bc600be is the commit prior to adding Test_EPF_MoveRenameFileTests
|
||||
|
@ -478,7 +488,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
|
||||
// Read a copy of AllNullObjectRedownloaded.txt to force the object to be downloaded
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/AllNullObjectRedownloaded_copy.txt").Output.Trim().ShouldEqual(sha);
|
||||
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\AllNullObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
|
||||
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "AllNullObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
|
||||
objectPath.ShouldBeAFile(this.fileSystem);
|
||||
|
||||
// Set the contents of objectPath to all NULL
|
||||
|
@ -486,7 +496,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
File.WriteAllBytes(objectPath, Enumerable.Repeat<byte>(0, (int)objectFileInfo.Length).ToArray());
|
||||
|
||||
// Read the original path and verify its contents are correct
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\AllNullObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "AllNullObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Confirm there's a new item in the corrupt objects folder
|
||||
corruptObjectFolderPath.ShouldBeADirectory(this.fileSystem);
|
||||
|
@ -495,6 +505,8 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
}
|
||||
|
||||
[TestCase, Order(17)]
|
||||
//// TODO(Mac): Figure out why git for Mac is not requesting a redownload of the truncated object
|
||||
[Category(Categories.Mac.M3)]
|
||||
public void TruncatedObjectRedownloaded()
|
||||
{
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + this.Enlistment.Commitish);
|
||||
|
@ -513,18 +525,30 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
|
||||
// Read a copy of TruncatedObjectRedownloaded.txt to force the object to be downloaded
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded_copy.txt").Output.Trim().ShouldEqual(sha);
|
||||
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\TruncatedObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
|
||||
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
|
||||
objectPath.ShouldBeAFile(this.fileSystem);
|
||||
|
||||
// Truncate the contents of objectPath
|
||||
string tempTruncatedObjectPath = objectPath + "truncated";
|
||||
FileInfo objectFileInfo = new FileInfo(objectPath);
|
||||
long objectLength = objectFileInfo.Length;
|
||||
using (FileStream objectStream = new FileStream(objectPath, FileMode.Open))
|
||||
using (FileStream truncatedObjectStream = new FileStream(tempTruncatedObjectPath, FileMode.CreateNew))
|
||||
{
|
||||
objectStream.SetLength(objectFileInfo.Length - 8);
|
||||
for (int i = 0; i < (objectStream.Length - 16); ++i)
|
||||
{
|
||||
truncatedObjectStream.WriteByte((byte)objectStream.ReadByte());
|
||||
}
|
||||
}
|
||||
|
||||
this.fileSystem.DeleteFile(objectPath);
|
||||
this.fileSystem.MoveFile(tempTruncatedObjectPath, objectPath);
|
||||
tempTruncatedObjectPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
objectPath.ShouldBeAFile(this.fileSystem);
|
||||
new FileInfo(objectPath).Length.ShouldEqual(objectLength - 16);
|
||||
|
||||
// Read the original path and verify its contents are correct
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\TruncatedObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Confirm there's a new item in the corrupt objects folder
|
||||
corruptObjectFolderPath.ShouldBeADirectory(this.fileSystem).WithItems().Count().ShouldEqual(initialCorruptObjectCount + 1);
|
||||
|
@ -533,7 +557,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
[TestCase, Order(18)]
|
||||
public void CreateFileAfterTryOpenNonExistentFile()
|
||||
{
|
||||
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\CreateFileAfterTryOpenNonExistentFile_NotProjected.txt");
|
||||
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "CreateFileAfterTryOpenNonExistentFile_NotProjected.txt");
|
||||
string fileContents = "CreateFileAfterTryOpenNonExistentFile file contents";
|
||||
filePath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.WriteAllText(filePath, fileContents);
|
||||
|
@ -543,11 +567,11 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
[TestCase, Order(19)]
|
||||
public void RenameFileAfterTryOpenNonExistentFile()
|
||||
{
|
||||
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\RenameFileAfterTryOpenNonExistentFile_NotProjected.txt");
|
||||
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "RenameFileAfterTryOpenNonExistentFile_NotProjected.txt");
|
||||
string fileContents = "CreateFileAfterTryOpenNonExistentFile file contents";
|
||||
filePath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
string newFilePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests\\RenameFileAfterTryOpenNonExistentFile_NewFile.txt");
|
||||
string newFilePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "RenameFileAfterTryOpenNonExistentFile_NewFile.txt");
|
||||
this.fileSystem.WriteAllText(newFilePath, fileContents);
|
||||
newFilePath.ShouldBeAFile(this.fileSystem).WithContents(fileContents);
|
||||
|
||||
|
@ -571,6 +595,48 @@ BOOL APIENTRY DllMain( HMODULE hModule,
|
|||
folderEntries.ShouldContain(file => file.Name.Equals(expectedEntryName));
|
||||
}
|
||||
|
||||
private void EnumerateAndReadShouldNotChangeEnumerationOrder(string folderRelativePath)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
NativeTests.EnumerateAndReadDoesNotChangeEnumerationOrder(folderRelativePath).ShouldEqual(true);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
string[] entries = Directory.GetFileSystemEntries(folderRelativePath);
|
||||
foreach (string entry in entries)
|
||||
{
|
||||
File.ReadAllText(entry);
|
||||
}
|
||||
|
||||
string[] postReadEntries = Directory.GetFileSystemEntries(folderRelativePath);
|
||||
Enumerable.SequenceEqual(entries, postReadEntries)
|
||||
.ShouldBeTrue($"Entries are not the same after reading. Orignial list:\n{string.Join(",", entries)}\n\nAfter read:\n{string.Join(",", postReadEntries)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail("Unsupported platform");
|
||||
}
|
||||
}
|
||||
|
||||
private bool PlaceholderHasVersionInfo(string relativePath, int version, string sha)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return NativeTests.PlaceHolderHasVersionInfo(relativePath, version, sha);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
//// TODO(Mac): Add a version of PlaceHolderHasVersionInfo that works on Mac
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail("Unsupported platform");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class NativeTests
|
||||
{
|
||||
[DllImport("GVFS.NativeTests.dll")]
|
||||
|
|
|
@ -10,7 +10,8 @@ using System.Linq;
|
|||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.Mac.M2)]
|
||||
//// TODO(Mac): Enable for M2 once notifications are working
|
||||
//// [Category(Categories.Mac.M2)]
|
||||
public class PersistedModifiedPathsTests : TestsWithEnlistmentPerTestCase
|
||||
{
|
||||
private static readonly string FileToAdd = Path.Combine("GVFS", "TestAddFile.txt");
|
||||
|
|
|
@ -45,13 +45,13 @@ namespace GVFS.FunctionalTests.Tests
|
|||
[TearDown]
|
||||
public void TearDownTests()
|
||||
{
|
||||
CmdRunner.DeleteDirectoryWithRetry(this.fastFetchRepoRoot);
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.fastFetchRepoRoot);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void DeleteControlRepo()
|
||||
{
|
||||
CmdRunner.DeleteDirectoryWithRetry(this.fastFetchControlRoot);
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.fastFetchControlRoot);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
|
|
|
@ -15,19 +15,21 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
|
|||
private const string EncodingFileFolder = "FilenameEncoding";
|
||||
private const string EncodingFilename = "ريلٌأكتوبرûمارسأغسطسºٰٰۂْٗ۵ريلٌأك.txt";
|
||||
private const string ContentWhenEditingFile = "// Adding a comment to the file";
|
||||
private const string EditFilePath = @"GVFS\GVFS.Common\GVFSContext.cs";
|
||||
private const string DeleteFilePath = @"GVFS\GVFS\Program.cs";
|
||||
private const string RenameFilePathFrom = @"GVFS\GVFS.Common\Physical\FileSystem\FileProperties.cs";
|
||||
private const string RenameFilePathTo = @"GVFS\GVFS.Common\Physical\FileSystem\FileProperties2.cs";
|
||||
private const string RenameFolderPathFrom = @"GVFS\GVFS.Common\PrefetchPacks";
|
||||
private const string RenameFolderPathTo = @"GVFS\GVFS.Common\PrefetchPacksRenamed";
|
||||
private const string UnknownTestName = "Unknown";
|
||||
|
||||
private static readonly string EditFilePath = Path.Combine("GVFS", "GVFS.Common", "GVFSContext.cs");
|
||||
private static readonly string DeleteFilePath = Path.Combine("GVFS", "GVFS", "Program.cs");
|
||||
private static readonly string RenameFilePathFrom = Path.Combine("GVFS", "GVFS.Common", "Physical", "FileSystem", "FileProperties.cs");
|
||||
private static readonly string RenameFilePathTo = Path.Combine("GVFS", "GVFS.Common", "Physical", "FileSystem", "FileProperties2.cs");
|
||||
private static readonly string RenameFolderPathFrom = Path.Combine("GVFS", "GVFS.Common", "PrefetchPacks");
|
||||
private static readonly string RenameFolderPathTo = Path.Combine("GVFS", "GVFS.Common", "PrefetchPacksRenamed");
|
||||
|
||||
public GitCommandsTests() : base(enlistmentPerTest: false)
|
||||
{
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void VerifyTestFilesExist()
|
||||
{
|
||||
// Sanity checks to ensure that the test files we expect to be in our test repo are present
|
||||
|
@ -39,24 +41,28 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
|
|||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void StatusTest()
|
||||
{
|
||||
this.ValidateGitCommand("status");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void StatusShortTest()
|
||||
{
|
||||
this.ValidateGitCommand("status -s");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void BranchTest()
|
||||
{
|
||||
this.ValidateGitCommand("branch");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void NewBranchTest()
|
||||
{
|
||||
this.ValidateGitCommand("branch tests/functional/NewBranchTest");
|
||||
|
@ -64,6 +70,7 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
|
|||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.Mac.M2)]
|
||||
public void DeleteBranchTest()
|
||||
{
|
||||
this.ValidateGitCommand("branch tests/functional/DeleteBranchTest");
|
||||
|
|
|
@ -46,6 +46,14 @@ namespace GVFS.FunctionalTests.Tests.GitCommands
|
|||
this.FilesShouldMatchCheckoutOfSourceBranch();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void RebaseConflict_RemoveDeletedTheirsFile()
|
||||
{
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
this.RunGitCommand("rebase " + GitRepoTests.ConflictSourceBranch);
|
||||
this.ValidateGitCommand("rm Test_ConflictTests/ModifiedFiles/ChangeInSourceDeleteInTarget.txt");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void RebaseConflict_AddThenContinue()
|
||||
{
|
||||
|
|
|
@ -132,7 +132,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
|
|||
|
||||
string objectsRoot = GVFSHelpers.GetPersistedGitObjectsRoot(enlistment1.DotGVFSRoot).ShouldNotBeNull();
|
||||
objectsRoot.ShouldBeADirectory(this.fileSystem);
|
||||
CmdRunner.DeleteDirectoryWithRetry(objectsRoot);
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(objectsRoot);
|
||||
|
||||
string metadataPath = Path.Combine(this.localCachePath, "mapping.dat");
|
||||
metadataPath.ShouldBeAFile(this.fileSystem);
|
||||
|
@ -172,7 +172,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
|
|||
try
|
||||
{
|
||||
// Delete objectsRoot rather than this.localCachePath as the blob sizes database cannot be deleted while GVFS is mounted
|
||||
CmdRunner.DeleteDirectoryWithRetry(objectsRoot);
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(objectsRoot);
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
catch (IOException)
|
||||
|
@ -212,7 +212,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
|
|||
mappingFileContents.Length.ShouldNotEqual(0, "mapping.dat should not be empty");
|
||||
|
||||
// Delete the git objects root folder, mount should re-create it and the mapping.dat file should not change
|
||||
CmdRunner.DeleteDirectoryWithRetry(objectsRoot);
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(objectsRoot);
|
||||
|
||||
enlistment.MountGVFS();
|
||||
|
||||
|
@ -239,7 +239,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
|
|||
mappingFileContents.Length.ShouldNotEqual(0, "mapping.dat should not be empty");
|
||||
|
||||
// Delete the local cache folder, mount should re-create it and generate a new mapping file and local cache key
|
||||
CmdRunner.DeleteDirectoryWithRetry(enlistment.LocalCacheRoot);
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(enlistment.LocalCacheRoot);
|
||||
|
||||
enlistment.MountGVFS();
|
||||
|
||||
|
@ -267,7 +267,7 @@ namespace GVFS.FunctionalTests.Tests.MultiEnlistmentTests
|
|||
// localCacheParentPath can be deleted (as the SQLite blob sizes database cannot be deleted while GVFS is mounted)
|
||||
protected override void OnTearDownEnlistmentsDeleted()
|
||||
{
|
||||
CmdRunner.DeleteDirectoryWithRetry(this.localCacheParentPath);
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.localCacheParentPath);
|
||||
}
|
||||
|
||||
private GVFSFunctionalTestEnlistment CloneAndMountEnlistment(string branch = null)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tools
|
||||
{
|
||||
|
@ -68,7 +69,15 @@ namespace GVFS.FunctionalTests.Tools
|
|||
|
||||
public void Delete()
|
||||
{
|
||||
CmdRunner.DeleteDirectoryWithRetry(this.RootPath);
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.RootPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(Mac): See if we can use BashRunner.DeleteDirectoryWithRetry on Windows as well
|
||||
BashRunner.DeleteDirectoryWithUnlimitedRetries(this.RootPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,11 +133,12 @@ namespace GVFS.FunctionalTests.Tools
|
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// Use cmd.exe to delete the enlistment as it properly handles tombstones and reparse points
|
||||
CmdRunner.DeleteDirectoryWithRetry(this.EnlistmentRoot);
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.EnlistmentRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(Mac): BashRunner.DeleteDirectory(this.EnlistmentRoot);
|
||||
// TODO(Mac): Figure out why the call to DeleteDirectoryWithRetry is not returning
|
||||
// BashRunner.DeleteDirectoryWithRetry(this.EnlistmentRoot);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AssemblyName>GVFS.Hooks</AssemblyName>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<RunTimeIdentifiers>osx-x64</RunTimeIdentifiers>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>GVFS.Hooks</RootNamespace>
|
||||
<AssemblyName>GVFS.Hooks</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Version>$(GVFSVersion)</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Version>$(GVFSVersion)</Version>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="HooksPlatform\GVFSHooksPlatform.Windows.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--
|
||||
Files from GVFS.Common included as links here to prevent adding
|
||||
project reference. The project reference leads to performance degradation
|
||||
due to the other dependencies that come along with GVFS.Common.
|
||||
-->
|
||||
<Compile Include="..\GVFS.Common\ConsoleHelper.cs">
|
||||
<Link>Common\ConsoleHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Git\GitConfigHelper.cs">
|
||||
<Link>Common\Git\GitConfigHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Git\GitConfigSetting.cs">
|
||||
<Link>Common\Git\GitConfigSetting.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Git\GitVersion.cs">
|
||||
<Link>Common\Git\GitVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\GVFSConstants.cs">
|
||||
<Link>Common\GVFSConstants.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\GVFSEnlistment.Shared.cs">
|
||||
<Link>Common\GVFSEnlistment.Shared.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\GVFSLock.Shared.cs">
|
||||
<Link>Common\GVFSLock.Shared.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\NamedPipes\BrokenPipeException.cs">
|
||||
<Link>Common\NamedPipes\BrokenPipeException.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\NamedPipes\LockNamedPipeMessages.cs">
|
||||
<Link>Common\NamedPipes\LockNamedPipeMessages.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\NamedPipes\NamedPipeClient.cs">
|
||||
<Link>Common\NamedPipes\NamedPipeClient.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\NamedPipes\NamedPipeStreamWriterExtensions.cs">
|
||||
<Link>Common\NamedPipes\NamedPipeStreamWriterExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\NativeMethods.Shared.cs">
|
||||
<Link>Common\NativeMethods.Shared.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Paths.Shared.cs">
|
||||
<Link>Common\Paths.Shared.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\ProcessHelper.cs">
|
||||
<Link>Common\ProcessHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\ProcessResult.cs">
|
||||
<Link>Common\ProcessResult.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Tracing\EventLevel.cs">
|
||||
<Link>Common\Tracing\EventLevel.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Tracing\EventMetadata.cs">
|
||||
<Link>Common\Tracing\EventMetadata.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Tracing\EventOpcode.cs">
|
||||
<Link>Common\Tracing\EventOpcode.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Tracing\ITracer.cs">
|
||||
<Link>Common\Tracing\ITracer.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Tracing\Keywords.cs">
|
||||
<Link>Common\Tracing\Keywords.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Platform.Mac\MacFileSystem.Shared.cs">
|
||||
<Link>Mac\MacFileSystem.Shared.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Platform.Mac\MacPlatform.Shared.cs">
|
||||
<Link>Mac\MacPlatform.Shared.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="StyleCop.Error.MSBuild" Version="1.0.0" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
<PackageReference Include="StyleCop.MSBuild" Version="5.0.0" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -83,6 +83,9 @@
|
|||
<Compile Include="..\GVFS.Common\NamedPipes\NamedPipeClient.cs">
|
||||
<Link>Common\NamedPipes\NamedPipeClient.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\NamedPipes\NamedPipeStreamWriterExtensions.cs">
|
||||
<Link>Common\NamedPipes\NamedPipeStreamWriterExtensions.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\NativeMethods.Shared.cs">
|
||||
<Link>Common\NativeMethods.Shared.cs</Link>
|
||||
</Compile>
|
||||
|
@ -116,6 +119,7 @@
|
|||
<Compile Include="..\GVFS.Platform.Windows\WindowsPlatform.Shared.cs">
|
||||
<Link>Windows\WindowsPlatform.Shared.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="HooksPlatform\GVFSHooksPlatform.Windows.cs" />
|
||||
<Compile Include="KnownGitCommands.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
@ -0,0 +1,37 @@
|
|||
using GVFS.Platform.Mac;
|
||||
|
||||
namespace GVFS.Hooks.HooksPlatform
|
||||
{
|
||||
public static class GVFSHooksPlatform
|
||||
{
|
||||
public static bool IsElevated()
|
||||
{
|
||||
return MacPlatform.IsElevatedImplementation();
|
||||
}
|
||||
|
||||
public static bool IsProcessActive(int processId)
|
||||
{
|
||||
return MacPlatform.IsProcessActiveImplementation(processId);
|
||||
}
|
||||
|
||||
public static string GetNamedPipeName(string enlistmentRoot)
|
||||
{
|
||||
return MacPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
|
||||
}
|
||||
|
||||
public static bool IsConsoleOutputRedirectedToFile()
|
||||
{
|
||||
return MacPlatform.IsConsoleOutputRedirectedToFileImplementation();
|
||||
}
|
||||
|
||||
public static bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
{
|
||||
return MacPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
|
||||
}
|
||||
|
||||
public static bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage)
|
||||
{
|
||||
return MacFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using GVFS.Platform.Windows;
|
||||
|
||||
namespace GVFS.Hooks.HooksPlatform
|
||||
{
|
||||
public static class GVFSHooksPlatform
|
||||
{
|
||||
public static bool IsElevated()
|
||||
{
|
||||
return WindowsPlatform.IsElevatedImplementation();
|
||||
}
|
||||
|
||||
public static bool IsProcessActive(int processId)
|
||||
{
|
||||
return WindowsPlatform.IsProcessActiveImplementation(processId);
|
||||
}
|
||||
|
||||
public static string GetNamedPipeName(string enlistmentRoot)
|
||||
{
|
||||
return WindowsPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
|
||||
}
|
||||
|
||||
public static bool IsConsoleOutputRedirectedToFile()
|
||||
{
|
||||
return WindowsPlatform.IsConsoleOutputRedirectedToFileImplementation();
|
||||
}
|
||||
|
||||
public static bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
{
|
||||
return WindowsPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
|
||||
}
|
||||
|
||||
public static bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage)
|
||||
{
|
||||
return WindowsFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,11 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.NamedPipes;
|
||||
using GVFS.Platform.Windows;
|
||||
using GVFS.Hooks.HooksPlatform;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace GVFS.Hooks
|
||||
{
|
||||
|
@ -40,19 +38,19 @@ namespace GVFS.Hooks
|
|||
|
||||
string errorMessage;
|
||||
string normalizedCurrentDirectory;
|
||||
if (!WindowsFileSystem.TryGetNormalizedPathImplementation(Environment.CurrentDirectory, out normalizedCurrentDirectory, out errorMessage))
|
||||
if (!GVFSHooksPlatform.TryGetNormalizedPath(Environment.CurrentDirectory, out normalizedCurrentDirectory, out errorMessage))
|
||||
{
|
||||
ExitWithError($"Failed to determine final path for current directory {Environment.CurrentDirectory}. Error: {errorMessage}");
|
||||
}
|
||||
|
||||
if (!WindowsPlatform.TryGetGVFSEnlistmentRootImplementation(Environment.CurrentDirectory, out enlistmentRoot, out errorMessage))
|
||||
if (!GVFSHooksPlatform.TryGetGVFSEnlistmentRoot(Environment.CurrentDirectory, out enlistmentRoot, out errorMessage))
|
||||
{
|
||||
// Nothing to hook when being run outside of a GVFS repo.
|
||||
// This is also the path when run with --git-dir outside of a GVFS directory, see Story #949665
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
enlistmentPipename = Paths.GetNamedPipeName(enlistmentRoot);
|
||||
enlistmentPipename = GVFSHooksPlatform.GetNamedPipeName(enlistmentRoot);
|
||||
|
||||
switch (GetHookType(args))
|
||||
{
|
||||
|
@ -205,7 +203,7 @@ namespace GVFS.Hooks
|
|||
|
||||
int pid = GetParentPid(args);
|
||||
if (pid == Program.InvalidProcessId ||
|
||||
!ProcessHelper.IsProcessActive(pid))
|
||||
!GVFSHooksPlatform.IsProcessActive(pid))
|
||||
{
|
||||
ExitWithError("GVFS.Hooks: Unable to find parent git.exe process " + "(PID: " + pid + ").");
|
||||
}
|
||||
|
@ -251,7 +249,7 @@ namespace GVFS.Hooks
|
|||
private static void AcquireGVFSLockForProcess(bool unattended, string[] args, int pid, NamedPipeClient pipeClient)
|
||||
{
|
||||
string result;
|
||||
bool checkGvfsLockAvailabilitOnly = CheckGVFSLockAvailabilityOnly(args);
|
||||
bool checkGvfsLockAvailabilityOnly = CheckGVFSLockAvailabilityOnly(args);
|
||||
string fullCommand = GenerateFullCommand(args);
|
||||
|
||||
if (!GVFSLock.TryAcquireGVFSLockForProcess(
|
||||
|
@ -259,10 +257,11 @@ namespace GVFS.Hooks
|
|||
pipeClient,
|
||||
fullCommand,
|
||||
pid,
|
||||
WindowsPlatform.IsElevatedImplementation(),
|
||||
checkGvfsLockAvailabilitOnly,
|
||||
null, // gvfsEnlistmentRoot
|
||||
out result))
|
||||
GVFSHooksPlatform.IsElevated(),
|
||||
isConsoleOutputRedirectedToFile: GVFSHooksPlatform.IsConsoleOutputRedirectedToFile(),
|
||||
checkAvailabilityOnly: checkGvfsLockAvailabilityOnly,
|
||||
gvfsEnlistmentRoot: null,
|
||||
result: out result))
|
||||
{
|
||||
ExitWithError(result);
|
||||
}
|
||||
|
@ -277,7 +276,8 @@ namespace GVFS.Hooks
|
|||
pipeClient,
|
||||
fullCommand,
|
||||
pid,
|
||||
WindowsPlatform.IsElevatedImplementation(),
|
||||
GVFSHooksPlatform.IsElevated(),
|
||||
GVFSHooksPlatform.IsConsoleOutputRedirectedToFile(),
|
||||
response =>
|
||||
{
|
||||
if (response == null || response.ResponseData == null)
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
#define VCRuntimeDir PackagesDir + "\GVFS.VCRuntime.0.1.0-build\lib\x64"
|
||||
#define GVFSDir BuildOutputDir + "\GVFS.Windows\bin\" + PlatformAndConfiguration
|
||||
#define GVFSCommonDir BuildOutputDir + "\GVFS.Common\bin\" + PlatformAndConfiguration + "\netstandard2.0"
|
||||
#define HooksDir BuildOutputDir + "\GVFS.Hooks\bin\" + PlatformAndConfiguration
|
||||
#define HooksDir BuildOutputDir + "\GVFS.Hooks.Windows\bin\" + PlatformAndConfiguration
|
||||
#define HooksLoaderDir BuildOutputDir + "\GitHooksLoader\bin\" + PlatformAndConfiguration
|
||||
#define ServiceDir BuildOutputDir + "\GVFS.Service\bin\" + PlatformAndConfiguration
|
||||
#define ServiceUIDir BuildOutputDir + "\GVFS.Service.UI\bin\" + PlatformAndConfiguration
|
||||
#define GVFSMountDir BuildOutputDir + "\GVFS.Mount.Windows\bin\" + PlatformAndConfiguration
|
||||
#define ReadObjectDir BuildOutputDir + "\GVFS.ReadObjectHook\bin\" + PlatformAndConfiguration
|
||||
#define VirtualFileSystemDir BuildOutputDir + "\GVFS.VirtualFileSystemHook\bin\" + PlatformAndConfiguration
|
||||
#define ReadObjectDir BuildOutputDir + "\GVFS.ReadObjectHook.Windows\bin\" + PlatformAndConfiguration
|
||||
#define VirtualFileSystemDir BuildOutputDir + "\GVFS.VirtualFileSystemHook.Windows\bin\" + PlatformAndConfiguration
|
||||
|
||||
#define MyAppName "GVFS"
|
||||
#define MyAppInstallerVersion GetFileVersion(GVFSDir + "\GVFS.exe")
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AssemblyName>gvfs.mount</AssemblyName>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<RunTimeIdentifiers>osx-x64</RunTimeIdentifiers>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="$(SolutionDir)\GVFS\GVFS.Build\GVFS.cs.props" />
|
||||
|
@ -78,7 +78,7 @@
|
|||
<Link>CommonAssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs">
|
||||
<Link>PlatformLoader.Windows.cs</Link>
|
||||
<Link>PlatformLoader.Windows.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="InProcessMount.cs" />
|
||||
<Compile Include="MountAbortedException.cs" />
|
||||
|
@ -122,9 +122,9 @@
|
|||
</Target>
|
||||
<Import Project="..\..\..\packages\StyleCop.Error.MSBuild.1.0.0\build\StyleCop.Error.MSBuild.Targets" Condition="Exists('..\..\..\packages\StyleCop.Error.MSBuild.1.0.0\build\StyleCop.Error.MSBuild.Targets')" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>xcopy /Y $(BuildOutputDir)\GVFS.ReadObjectHook\bin\$(Platform)\$(Configuration)\GVFS.ReadObjectHook.* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.VirtualFileSystemHook\bin\$(Platform)\$(Configuration)\GVFS.VirtualFileSystemHook.* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Hooks\bin\$(Platform)\$(Configuration)\GVFS.Hooks.* $(TargetDir)</PostBuildEvent>
|
||||
<PostBuildEvent>xcopy /Y $(BuildOutputDir)\GVFS.ReadObjectHook.Windows\bin\$(Platform)\$(Configuration)\GVFS.ReadObjectHook.* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.VirtualFileSystemHook.Windows\bin\$(Platform)\$(Configuration)\GVFS.VirtualFileSystemHook.* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\GVFS.Hooks.* $(TargetDir)</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.7\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.7\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.7\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.7\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
|
|
|
@ -3,6 +3,7 @@ using GVFS.Common.FileSystem;
|
|||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Http;
|
||||
using GVFS.Common.NamedPipes;
|
||||
using GVFS.Common.Prefetch;
|
||||
using GVFS.Common.Tracing;
|
||||
using GVFS.PlatformLoader;
|
||||
using GVFS.Virtualization;
|
||||
|
@ -28,6 +29,7 @@ namespace GVFS.Mount
|
|||
private FileSystemCallbacks fileSystemCallbacks;
|
||||
private GVFSEnlistment enlistment;
|
||||
private ITracer tracer;
|
||||
private BackgroundPrefetcher prefetcher;
|
||||
|
||||
private CacheServerInfo cacheServer;
|
||||
private RetryConfig retryConfig;
|
||||
|
@ -117,13 +119,10 @@ namespace GVFS.Mount
|
|||
|
||||
this.ValidateMountPoints();
|
||||
|
||||
if (!GVFSPlatform.Instance.IsUnderConstruction)
|
||||
string errorMessage;
|
||||
if (!HooksInstaller.TryUpdateHooks(this.context, out errorMessage))
|
||||
{
|
||||
string errorMessage;
|
||||
if (!HooksInstaller.TryUpdateHooks(this.context, out errorMessage))
|
||||
{
|
||||
this.FailMountAndExit(errorMessage);
|
||||
}
|
||||
this.FailMountAndExit(errorMessage);
|
||||
}
|
||||
|
||||
GVFSPlatform.Instance.ConfigureVisualStudio(this.enlistment.GitBinPath, this.tracer);
|
||||
|
@ -509,6 +508,11 @@ namespace GVFS.Mount
|
|||
FileSystemVirtualizer virtualizer = this.CreateOrReportAndExit(() => GVFSPlatformLoader.CreateFileSystemVirtualizer(this.context, this.gitObjects), "Failed to create src folder virtualizer");
|
||||
this.fileSystemCallbacks = this.CreateOrReportAndExit(() => new FileSystemCallbacks(this.context, this.gitObjects, RepoMetadata.Instance, virtualizer), "Failed to create src folder callback listener");
|
||||
|
||||
if (!this.context.Unattended)
|
||||
{
|
||||
this.prefetcher = this.CreateOrReportAndExit(() => new BackgroundPrefetcher(this.tracer, this.enlistment, this.context.FileSystem, this.gitObjects), "Failed to start background prefetcher");
|
||||
}
|
||||
|
||||
int majorVersion;
|
||||
int minorVersion;
|
||||
if (!RepoMetadata.Instance.TryGetOnDiskLayoutVersion(out majorVersion, out minorVersion, out error))
|
||||
|
@ -542,6 +546,12 @@ namespace GVFS.Mount
|
|||
|
||||
private void UnmountAndStopWorkingDirectoryCallbacks()
|
||||
{
|
||||
if (this.prefetcher != null)
|
||||
{
|
||||
this.prefetcher.Dispose();
|
||||
this.prefetcher = null;
|
||||
}
|
||||
|
||||
if (this.heartbeat != null)
|
||||
{
|
||||
this.heartbeat.Stop();
|
||||
|
|
10
GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/contents.xcworkspacedata
сгенерированный
Normal file
10
GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/contents.xcworkspacedata
сгенерированный
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "container:../GVFS.ReadObjectHook/GVFS.ReadObjectHook.Mac.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0940"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "267A836720EE9F27005E6B60"
|
||||
BuildableName = "GVFS.VirtualFileSystemHook"
|
||||
BlueprintName = "GVFS.VirtualFileSystemHook"
|
||||
ReferencedContainer = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "2673FD9120EBDAA900B64B7F"
|
||||
BuildableName = "GVFS.ReadObjectHook"
|
||||
BlueprintName = "GVFS.ReadObjectHook"
|
||||
ReferencedContainer = "container:../GVFS.ReadObjectHook/GVFS.ReadObjectHook.Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "267A836720EE9F27005E6B60"
|
||||
BuildableName = "GVFS.VirtualFileSystemHook"
|
||||
BlueprintName = "GVFS.VirtualFileSystemHook"
|
||||
ReferencedContainer = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<PathRunnable
|
||||
runnableDebuggingMode = "0"
|
||||
FilePath = "/<placeholder>">
|
||||
</PathRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "267A836720EE9F27005E6B60"
|
||||
BuildableName = "GVFS.VirtualFileSystemHook"
|
||||
BlueprintName = "GVFS.VirtualFileSystemHook"
|
||||
ReferencedContainer = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "267A836720EE9F27005E6B60"
|
||||
BuildableName = "GVFS.VirtualFileSystemHook"
|
||||
BlueprintName = "GVFS.VirtualFileSystemHook"
|
||||
ReferencedContainer = "container:../GVFS.VirtualFileSystemHook/GVFS.VirtualFileSystemHook.Mac.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -1,4 +1,14 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifdef __APPLE__
|
||||
typedef std::string PATH_STRING;
|
||||
typedef int PIPE_HANDLE;
|
||||
#elif _WIN32
|
||||
typedef std::wstring PATH_STRING;
|
||||
typedef HANDLE PIPE_HANDLE;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
||||
enum ReturnCode
|
||||
{
|
||||
|
@ -24,153 +34,23 @@ inline void die(int err, const char *fmt, ...)
|
|||
vfprintf(stderr, fmt, params);
|
||||
va_end(params);
|
||||
exit(err);
|
||||
}
|
||||
|
||||
inline std::wstring GetFinalPathName(const std::wstring& path)
|
||||
{
|
||||
HANDLE fileHandle;
|
||||
|
||||
// Using FILE_FLAG_BACKUP_SEMANTICS as it works with file as well as folder path
|
||||
// According to MSDN, https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx,
|
||||
// we must set this flag to obtain a handle to a directory
|
||||
fileHandle = CreateFileW(
|
||||
path.c_str(),
|
||||
FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
die(ReturnCode::PathNameError, "Could not open oppen handle to %ls to determine final path name, Error: %d\n", path.c_str(), GetLastError());
|
||||
}
|
||||
|
||||
wchar_t finalPathByHandle[MAX_PATH] = { 0 };
|
||||
DWORD finalPathSize = GetFinalPathNameByHandleW(fileHandle, finalPathByHandle, MAX_PATH, FILE_NAME_NORMALIZED);
|
||||
if (finalPathSize == 0)
|
||||
{
|
||||
die(ReturnCode::PathNameError, "Could not get final path name by handle for %ls, Error: %d\n", path.c_str(), GetLastError());
|
||||
}
|
||||
|
||||
std::wstring finalPath(finalPathByHandle);
|
||||
|
||||
// The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\" or "\\?\UNC\"
|
||||
// More information the prefixes is here http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
|
||||
std::wstring PathPrefix(L"\\\\?\\");
|
||||
std::wstring UncPrefix(L"\\\\?\\UNC\\");
|
||||
|
||||
if (finalPath.compare(0, UncPrefix.length(), UncPrefix) == 0)
|
||||
{
|
||||
finalPath = L"\\\\" + finalPath.substr(UncPrefix.length());
|
||||
}
|
||||
else if (finalPath.compare(0, PathPrefix.length(), PathPrefix) == 0)
|
||||
{
|
||||
finalPath = finalPath.substr(PathPrefix.length());
|
||||
}
|
||||
|
||||
return finalPath;
|
||||
}
|
||||
|
||||
inline std::wstring GetGVFSPipeName(const char *appName)
|
||||
{
|
||||
// The pipe name is build using the path of the GVFS enlistment root.
|
||||
// Start in the current directory and walk up the directory tree
|
||||
// until we find a folder that contains the ".gvfs" folder
|
||||
|
||||
const size_t dotGVFSRelativePathLength = sizeof(L"\\.gvfs") / sizeof(wchar_t);
|
||||
|
||||
// TODO 640838: Support paths longer than MAX_PATH
|
||||
wchar_t enlistmentRoot[MAX_PATH];
|
||||
DWORD currentDirResult = GetCurrentDirectoryW(MAX_PATH - dotGVFSRelativePathLength, enlistmentRoot);
|
||||
if (currentDirResult == 0 || currentDirResult > MAX_PATH - dotGVFSRelativePathLength)
|
||||
{
|
||||
die(ReturnCode::GetCurrentDirectoryFailure, "GetCurrentDirectory failed (%d)\n", GetLastError());
|
||||
}
|
||||
|
||||
std::wstring finalRootPath(GetFinalPathName(enlistmentRoot));
|
||||
errno_t copyResult = wcscpy_s(enlistmentRoot, finalRootPath.c_str());
|
||||
if (copyResult != 0)
|
||||
{
|
||||
die(ReturnCode::PipeConnectError, "Could not copy finalRootPath: %ls. Error: %d\n", finalRootPath.c_str(), copyResult);
|
||||
}
|
||||
|
||||
size_t enlistmentRootLength = wcslen(enlistmentRoot);
|
||||
if ('\\' != enlistmentRoot[enlistmentRootLength - 1])
|
||||
{
|
||||
wcscat_s(enlistmentRoot, L"\\");
|
||||
enlistmentRootLength++;
|
||||
}
|
||||
|
||||
// Walk up enlistmentRoot looking for a folder named .gvfs
|
||||
wchar_t* lastslash = enlistmentRoot + enlistmentRootLength - 1;
|
||||
WIN32_FIND_DATAW findFileData;
|
||||
HANDLE dotGVFSHandle;
|
||||
while (1)
|
||||
{
|
||||
wcscat_s(lastslash, MAX_PATH - (lastslash - enlistmentRoot), L".gvfs");
|
||||
dotGVFSHandle = FindFirstFileW(enlistmentRoot, &findFileData);
|
||||
if (dotGVFSHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose(dotGVFSHandle);
|
||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lastslash--;
|
||||
while ((enlistmentRoot != lastslash) && (*lastslash != '\\'))
|
||||
{
|
||||
lastslash--;
|
||||
}
|
||||
|
||||
if (enlistmentRoot == lastslash)
|
||||
{
|
||||
die(ReturnCode::NotInGVFSEnlistment, "%s must be run from inside a GVFS enlistment\n", appName);
|
||||
}
|
||||
|
||||
*(lastslash + 1) = 0;
|
||||
};
|
||||
|
||||
*(lastslash) = 0;
|
||||
|
||||
std::wstring namedPipe(CharUpperW(enlistmentRoot));
|
||||
std::replace(namedPipe.begin(), namedPipe.end(), L':', L'_');
|
||||
return L"\\\\.\\pipe\\GVFS_" + namedPipe;
|
||||
}
|
||||
|
||||
inline HANDLE CreatePipeToGVFS(const std::wstring& pipeName)
|
||||
{
|
||||
HANDLE pipeHandle;
|
||||
while (1)
|
||||
{
|
||||
pipeHandle = CreateFileW(
|
||||
pipeName.c_str(), // pipe name
|
||||
GENERIC_READ | // read and write access
|
||||
GENERIC_WRITE,
|
||||
0, // no sharing
|
||||
NULL, // default security attributes
|
||||
OPEN_EXISTING, // opens existing pipe
|
||||
0, // default attributes
|
||||
NULL); // no template file
|
||||
|
||||
if (pipeHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetLastError() != ERROR_PIPE_BUSY)
|
||||
{
|
||||
die(ReturnCode::PipeConnectError, "Could not open pipe: %ls, Error: %d\n", pipeName.c_str(), GetLastError());
|
||||
}
|
||||
|
||||
if (!WaitNamedPipeW(pipeName.c_str(), 3000))
|
||||
{
|
||||
die(ReturnCode::PipeConnectTimeout, "Could not open pipe: %ls, Timed out.", pipeName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return pipeHandle;
|
||||
}
|
||||
}
|
||||
|
||||
PATH_STRING GetFinalPathName(const PATH_STRING& path);
|
||||
PATH_STRING GetGVFSPipeName(const char *appName);
|
||||
PIPE_HANDLE CreatePipeToGVFS(const PATH_STRING& pipeName);
|
||||
void DisableCRLFTranslationOnStdPipes();
|
||||
|
||||
bool WriteToPipe(
|
||||
PIPE_HANDLE pipe,
|
||||
const char* message,
|
||||
unsigned long messageLength,
|
||||
/* out */ unsigned long* bytesWritten,
|
||||
/* out */ int* error);
|
||||
|
||||
bool ReadFromPipe(
|
||||
PIPE_HANDLE pipe,
|
||||
char* buffer,
|
||||
unsigned long bufferLength,
|
||||
/* out */ unsigned long* bytesRead,
|
||||
/* out */ int* error);
|
||||
|
|
|
@ -0,0 +1,170 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define MAX_PATH 260
|
||||
|
||||
PATH_STRING GetFinalPathName(const PATH_STRING& path)
|
||||
{
|
||||
// TODO(Mac): Implement
|
||||
return path;
|
||||
}
|
||||
|
||||
PATH_STRING GetGVFSPipeName(const char *appName)
|
||||
{
|
||||
// The pipe name is built using the path of the GVFS enlistment root.
|
||||
// Start in the current directory and walk up the directory tree
|
||||
// until we find a folder that contains the ".gvfs" folder
|
||||
|
||||
// TODO 640838: Support paths longer than MAX_PATH
|
||||
char enlistmentRoot[MAX_PATH];
|
||||
if (getcwd(enlistmentRoot, MAX_PATH) == nullptr)
|
||||
{
|
||||
die(ReturnCode::GetCurrentDirectoryFailure, "getcwd failed (%d)\n", errno);
|
||||
}
|
||||
|
||||
PATH_STRING finalRootPath(GetFinalPathName(enlistmentRoot));
|
||||
size_t resultLength = strlcpy(enlistmentRoot, finalRootPath.c_str(), sizeof(enlistmentRoot));
|
||||
if (resultLength >= sizeof(enlistmentRoot))
|
||||
{
|
||||
die(ReturnCode::PipeConnectError,
|
||||
"Could not copy finalRootPath: %ls, insufficient buffer. resultLength: %d, sizeof(enlistmentRoot): %d\n",
|
||||
finalRootPath.c_str(),
|
||||
resultLength,
|
||||
sizeof(enlistmentRoot));
|
||||
}
|
||||
|
||||
size_t enlistmentRootLength = strlen(enlistmentRoot);
|
||||
if ('/' != enlistmentRoot[enlistmentRootLength - 1])
|
||||
{
|
||||
strlcat(enlistmentRoot, "/", sizeof(enlistmentRoot));
|
||||
enlistmentRootLength++;
|
||||
}
|
||||
|
||||
// Walk up enlistmentRoot looking for a folder named .gvfs
|
||||
char* lastslash = enlistmentRoot + enlistmentRootLength - 1;
|
||||
bool gvfsFound = false;
|
||||
while (1)
|
||||
{
|
||||
*lastslash = '\0';
|
||||
DIR* directory = opendir(enlistmentRoot);
|
||||
if (directory == nullptr)
|
||||
{
|
||||
die(ReturnCode::NotInGVFSEnlistment, "Failed to open directory: %s, error: %d\n", enlistmentRoot, errno);
|
||||
}
|
||||
|
||||
dirent* dirEntry = readdir(directory);
|
||||
while (!gvfsFound && dirEntry != nullptr)
|
||||
{
|
||||
if (dirEntry->d_type == DT_DIR && strcmp(dirEntry->d_name, ".gvfs") == 0)
|
||||
{
|
||||
gvfsFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
dirEntry = readdir(directory);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(directory);
|
||||
|
||||
if (gvfsFound)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (errno != 0)
|
||||
{
|
||||
die(ReturnCode::NotInGVFSEnlistment, "readdir failed in directory: %s, error: %i\n", enlistmentRoot, errno);
|
||||
}
|
||||
|
||||
lastslash--;
|
||||
while ((enlistmentRoot != lastslash) && (*lastslash != '/'))
|
||||
{
|
||||
lastslash--;
|
||||
}
|
||||
|
||||
if (enlistmentRoot == lastslash)
|
||||
{
|
||||
die(ReturnCode::NotInGVFSEnlistment, "%s must be run from inside a GVFS enlistment\n", appName);
|
||||
}
|
||||
|
||||
*(lastslash + 1) = 0;
|
||||
};
|
||||
|
||||
*(lastslash) = 0;
|
||||
|
||||
return PATH_STRING(enlistmentRoot) + "/.gvfs/GVFS_NetCorePipe";
|
||||
}
|
||||
|
||||
PIPE_HANDLE CreatePipeToGVFS(const PATH_STRING& pipeName)
|
||||
{
|
||||
PIPE_HANDLE socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (socket_fd < 0)
|
||||
{
|
||||
die(ReturnCode::PipeConnectError, "Failed to create a new socket, pipeName: %s, error: %d\n", pipeName.c_str(), errno);
|
||||
}
|
||||
|
||||
struct sockaddr_un socket_address;
|
||||
memset(&socket_address, 0, sizeof(struct sockaddr_un));
|
||||
|
||||
socket_address.sun_family = AF_UNIX;
|
||||
size_t resultLength = strlcpy(socket_address.sun_path, pipeName.c_str(), sizeof(socket_address.sun_path));
|
||||
|
||||
if (resultLength >= sizeof(socket_address.sun_path))
|
||||
{
|
||||
die(ReturnCode::PipeConnectError,
|
||||
"Could not copy pipeName: %ls, insufficient buffer. resultLength: %d, sizeof(socket_address.sun_path): %d\n",
|
||||
pipeName.c_str(),
|
||||
resultLength,
|
||||
sizeof(socket_address.sun_path));
|
||||
}
|
||||
|
||||
if(connect(socket_fd, (struct sockaddr *) &socket_address, sizeof(struct sockaddr_un)) != 0)
|
||||
{
|
||||
die(ReturnCode::PipeConnectError, "Failed to connect socket, pipeName: %s, error: %d\n", pipeName.c_str(), errno);
|
||||
}
|
||||
|
||||
return socket_fd;
|
||||
}
|
||||
|
||||
void DisableCRLFTranslationOnStdPipes()
|
||||
{
|
||||
// not required on Mac
|
||||
}
|
||||
|
||||
bool WriteToPipe(PIPE_HANDLE pipe, const char* message, size_t messageLength, /* out */ size_t* bytesWritten, /* out */ int* error)
|
||||
{
|
||||
*bytesWritten = write(pipe, message, messageLength);
|
||||
bool success = *bytesWritten == messageLength;
|
||||
*error = success ? 0 : errno;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ReadFromPipe(PIPE_HANDLE pipe, char* buffer, size_t bufferLength, /* out */ size_t* bytesRead, /* out */ int* error)
|
||||
{
|
||||
*error = 0;
|
||||
*bytesRead = 0;
|
||||
ssize_t readByteCount;
|
||||
|
||||
do
|
||||
{
|
||||
readByteCount = recv(pipe, buffer, bufferLength, 0);
|
||||
} while (readByteCount == -1 && errno == EINTR);
|
||||
|
||||
if (readByteCount < 0)
|
||||
{
|
||||
*error = errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
*bytesRead = readByteCount;
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <algorithm>
|
||||
#include "common.h"
|
||||
|
||||
PATH_STRING GetFinalPathName(const PATH_STRING& path)
|
||||
{
|
||||
HANDLE fileHandle;
|
||||
|
||||
// Using FILE_FLAG_BACKUP_SEMANTICS as it works with file as well as folder path
|
||||
// According to MSDN, https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx,
|
||||
// we must set this flag to obtain a handle to a directory
|
||||
fileHandle = CreateFileW(
|
||||
path.c_str(),
|
||||
FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
die(ReturnCode::PathNameError, "Could not open oppen handle to %ls to determine final path name, Error: %d\n", path.c_str(), GetLastError());
|
||||
}
|
||||
|
||||
wchar_t finalPathByHandle[MAX_PATH] = { 0 };
|
||||
DWORD finalPathSize = GetFinalPathNameByHandleW(fileHandle, finalPathByHandle, MAX_PATH, FILE_NAME_NORMALIZED);
|
||||
if (finalPathSize == 0)
|
||||
{
|
||||
die(ReturnCode::PathNameError, "Could not get final path name by handle for %ls, Error: %d\n", path.c_str(), GetLastError());
|
||||
}
|
||||
|
||||
std::wstring finalPath(finalPathByHandle);
|
||||
|
||||
// The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\" or "\\?\UNC\"
|
||||
// More information the prefixes is here http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
|
||||
std::wstring PathPrefix(L"\\\\?\\");
|
||||
std::wstring UncPrefix(L"\\\\?\\UNC\\");
|
||||
|
||||
if (finalPath.compare(0, UncPrefix.length(), UncPrefix) == 0)
|
||||
{
|
||||
finalPath = L"\\\\" + finalPath.substr(UncPrefix.length());
|
||||
}
|
||||
else if (finalPath.compare(0, PathPrefix.length(), PathPrefix) == 0)
|
||||
{
|
||||
finalPath = finalPath.substr(PathPrefix.length());
|
||||
}
|
||||
|
||||
return finalPath;
|
||||
}
|
||||
|
||||
PATH_STRING GetGVFSPipeName(const char *appName)
|
||||
{
|
||||
// The pipe name is built using the path of the GVFS enlistment root.
|
||||
// Start in the current directory and walk up the directory tree
|
||||
// until we find a folder that contains the ".gvfs" folder
|
||||
|
||||
const size_t dotGVFSRelativePathLength = sizeof(L"\\.gvfs") / sizeof(wchar_t);
|
||||
|
||||
// TODO 640838: Support paths longer than MAX_PATH
|
||||
wchar_t enlistmentRoot[MAX_PATH];
|
||||
DWORD currentDirResult = GetCurrentDirectoryW(MAX_PATH - dotGVFSRelativePathLength, enlistmentRoot);
|
||||
if (currentDirResult == 0 || currentDirResult > MAX_PATH - dotGVFSRelativePathLength)
|
||||
{
|
||||
die(ReturnCode::GetCurrentDirectoryFailure, "GetCurrentDirectory failed (%d)\n", GetLastError());
|
||||
}
|
||||
|
||||
PATH_STRING finalRootPath(GetFinalPathName(enlistmentRoot));
|
||||
errno_t copyResult = wcscpy_s(enlistmentRoot, finalRootPath.c_str());
|
||||
if (copyResult != 0)
|
||||
{
|
||||
die(ReturnCode::PipeConnectError, "Could not copy finalRootPath: %ls. Error: %d\n", finalRootPath.c_str(), copyResult);
|
||||
}
|
||||
|
||||
size_t enlistmentRootLength = wcslen(enlistmentRoot);
|
||||
if ('\\' != enlistmentRoot[enlistmentRootLength - 1])
|
||||
{
|
||||
wcscat_s(enlistmentRoot, L"\\");
|
||||
enlistmentRootLength++;
|
||||
}
|
||||
|
||||
// Walk up enlistmentRoot looking for a folder named .gvfs
|
||||
wchar_t* lastslash = enlistmentRoot + enlistmentRootLength - 1;
|
||||
WIN32_FIND_DATAW findFileData;
|
||||
HANDLE dotGVFSHandle;
|
||||
while (1)
|
||||
{
|
||||
wcscat_s(lastslash, MAX_PATH - (lastslash - enlistmentRoot), L".gvfs");
|
||||
dotGVFSHandle = FindFirstFileW(enlistmentRoot, &findFileData);
|
||||
if (dotGVFSHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose(dotGVFSHandle);
|
||||
if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lastslash--;
|
||||
while ((enlistmentRoot != lastslash) && (*lastslash != '\\'))
|
||||
{
|
||||
lastslash--;
|
||||
}
|
||||
|
||||
if (enlistmentRoot == lastslash)
|
||||
{
|
||||
die(ReturnCode::NotInGVFSEnlistment, "%s must be run from inside a GVFS enlistment\n", appName);
|
||||
}
|
||||
|
||||
*(lastslash + 1) = 0;
|
||||
};
|
||||
|
||||
*(lastslash) = 0;
|
||||
|
||||
PATH_STRING namedPipe(CharUpperW(enlistmentRoot));
|
||||
std::replace(namedPipe.begin(), namedPipe.end(), L':', L'_');
|
||||
return L"\\\\.\\pipe\\GVFS_" + namedPipe;
|
||||
}
|
||||
|
||||
PIPE_HANDLE CreatePipeToGVFS(const PATH_STRING& pipeName)
|
||||
{
|
||||
PIPE_HANDLE pipeHandle;
|
||||
while (1)
|
||||
{
|
||||
pipeHandle = CreateFileW(
|
||||
pipeName.c_str(), // pipe name
|
||||
GENERIC_READ | // read and write access
|
||||
GENERIC_WRITE,
|
||||
0, // no sharing
|
||||
NULL, // default security attributes
|
||||
OPEN_EXISTING, // opens existing pipe
|
||||
0, // default attributes
|
||||
NULL); // no template file
|
||||
|
||||
if (pipeHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetLastError() != ERROR_PIPE_BUSY)
|
||||
{
|
||||
die(ReturnCode::PipeConnectError, "Could not open pipe: %ls, Error: %d\n", pipeName.c_str(), GetLastError());
|
||||
}
|
||||
|
||||
if (!WaitNamedPipeW(pipeName.c_str(), 3000))
|
||||
{
|
||||
die(ReturnCode::PipeConnectTimeout, "Could not open pipe: %ls, Timed out.", pipeName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return pipeHandle;
|
||||
}
|
||||
|
||||
void DisableCRLFTranslationOnStdPipes()
|
||||
{
|
||||
// set the mode to binary so we don't get CRLF translation
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
}
|
||||
|
||||
bool WriteToPipe(PIPE_HANDLE pipe, const char* message, unsigned long messageLength, /* out */ unsigned long* bytesWritten, /* out */ int* error)
|
||||
{
|
||||
BOOL success = WriteFile(
|
||||
pipe, // pipe handle
|
||||
message, // message
|
||||
messageLength, // message length
|
||||
bytesWritten, // bytes written
|
||||
NULL); // not overlapped
|
||||
|
||||
*error = success ? 0 : GetLastError();
|
||||
|
||||
return success != FALSE;
|
||||
}
|
||||
|
||||
bool ReadFromPipe(PIPE_HANDLE pipe, char* buffer, unsigned long bufferLength, /* out */ unsigned long* bytesRead, /* out */ int* error)
|
||||
{
|
||||
*error = 0;
|
||||
*bytesRead = 0;
|
||||
BOOL success = ReadFile(
|
||||
pipe, // pipe handle
|
||||
buffer, // buffer to receive reply
|
||||
bufferLength, // size of buffer
|
||||
bytesRead, // number of bytes read
|
||||
NULL); // not overlapped
|
||||
|
||||
if (!success)
|
||||
{
|
||||
*error = GetLastError();
|
||||
}
|
||||
|
||||
return success || (*error == ERROR_MORE_DATA);
|
||||
}
|
|
@ -75,7 +75,7 @@ bool QueryDirectoryFileRestartScanResetsFilter(const char* folderPath)
|
|||
&nonExistentFileFilter, // FileName
|
||||
TRUE); // RestartScan
|
||||
|
||||
SHOULD_EQUAL(status, STATUS_NO_SUCH_FILE);
|
||||
SHOULD_EQUAL(status, STATUS_NO_MORE_FILES);
|
||||
}
|
||||
catch (TestException&)
|
||||
{
|
||||
|
|
|
@ -260,7 +260,7 @@ bool ProjFS_EnumTestNoMoreNoSuchReturnCodes(const char* virtualRootPath)
|
|||
restartScan);
|
||||
|
||||
// Check expected status code for a repeat query on a given handle for a non-existent name.
|
||||
VERIFY_ARE_EQUAL(STATUS_NO_SUCH_FILE, repeatStatus);
|
||||
VERIFY_ARE_EQUAL(STATUS_NO_MORE_FILES, repeatStatus);
|
||||
|
||||
// Once more, this time without SL_RESTART_SCAN.
|
||||
restartScan = false;
|
||||
|
|
|
@ -62,6 +62,9 @@
|
|||
<Compile Include="ProfilingEnvironment.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs">
|
||||
<Link>PlatformLoader.Windows.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace GVFS.PerfProfiling
|
|||
{
|
||||
GVFSPlatform.Register(new WindowsPlatform());
|
||||
string gitBinPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
|
||||
string hooksPath = ProcessHelper.WhereDirectory(GVFSConstants.GVFSHooksExecutableName);
|
||||
string hooksPath = ProcessHelper.WhereDirectory(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
|
||||
|
||||
return GVFSEnlistment.CreateFromDirectory(enlistmentRootPath, gitBinPath, hooksPath);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.PlatformLoader;
|
||||
using GVFS.Virtualization.Projection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -21,6 +22,7 @@ namespace GVFS.PerfProfiling
|
|||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
GVFSPlatformLoader.Initialize();
|
||||
string enlistmentRootPath = @"M:\OS";
|
||||
if (args.Length > 0 && !string.IsNullOrWhiteSpace(args[0]))
|
||||
{
|
||||
|
@ -41,9 +43,9 @@ namespace GVFS.PerfProfiling
|
|||
|
||||
Dictionary<TestsToRun, Action> allTests = new Dictionary<TestsToRun, Action>
|
||||
{
|
||||
{ TestsToRun.ValidateIndex, () => GitIndexProjection.ReadIndex(Path.Combine(environment.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Index)) },
|
||||
{ TestsToRun.ValidateIndex, () => GitIndexProjection.ReadIndex(environment.Context.Tracer, Path.Combine(environment.Enlistment.WorkingDirectoryRoot, GVFSConstants.DotGit.Index)) },
|
||||
{ TestsToRun.RebuildProjection, () => environment.FileSystemCallbacks.GitIndexProjectionProfiler.ForceRebuildProjection() },
|
||||
{ TestsToRun.ValidateModifiedPaths, () => environment.FileSystemCallbacks.GitIndexProjectionProfiler.ForceAddMissingModifiedPaths() },
|
||||
{ TestsToRun.ValidateModifiedPaths, () => environment.FileSystemCallbacks.GitIndexProjectionProfiler.ForceAddMissingModifiedPaths(environment.Context.Tracer) },
|
||||
};
|
||||
|
||||
long before = GetMemoryUsage();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp2.1;netstandard2.0</TargetFrameworks>
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace GVFS.Platform.Mac
|
||||
{
|
||||
public partial class MacFileSystem
|
||||
{
|
||||
public static bool TryGetNormalizedPathImplementation(string path, out string normalizedPath, out string errorMessage)
|
||||
{
|
||||
// TODO(Mac): Properly determine normalized paths (e.g. across links)
|
||||
errorMessage = null;
|
||||
normalizedPath = path;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GVFS.Platform.Mac
|
||||
{
|
||||
public class MacFileSystem : IPlatformFileSystem
|
||||
public partial class MacFileSystem : IPlatformFileSystem
|
||||
{
|
||||
public bool SupportsFileMode { get; } = true;
|
||||
|
||||
|
@ -30,12 +31,17 @@ namespace GVFS.Platform.Mac
|
|||
File.Copy(existingFileName, newFileName);
|
||||
}
|
||||
|
||||
public void ChangeMode(string path, int mode)
|
||||
{
|
||||
Chmod(path, mode);
|
||||
}
|
||||
|
||||
public bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage)
|
||||
{
|
||||
// TODO(Mac): Properly determine normalized paths (e.g. across links)
|
||||
errorMessage = null;
|
||||
normalizedPath = path;
|
||||
return true;
|
||||
return MacFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);
|
||||
}
|
||||
|
||||
[DllImport("libc", EntryPoint = "chmod", SetLastError = true)]
|
||||
private static extern int Chmod(string pathname, int mode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
using GVFS.Common;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Platform.Mac
|
||||
{
|
||||
public partial class MacPlatform
|
||||
{
|
||||
public static bool IsElevatedImplementation()
|
||||
{
|
||||
// TODO(Mac): Implement proper check
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsProcessActiveImplementation(int processId)
|
||||
{
|
||||
// TODO(Mac): Implement proper check
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetNamedPipeNameImplementation(string enlistmentRoot)
|
||||
{
|
||||
// Pipes are stored as files on OSX, use a rooted pipe name to keep full control of the location of the file
|
||||
return Path.Combine(enlistmentRoot, GVFSConstants.DotGVFS.Root, "GVFS_NetCorePipe");
|
||||
}
|
||||
|
||||
public static bool IsConsoleOutputRedirectedToFileImplementation()
|
||||
{
|
||||
// TODO(Mac): Implement proper check
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
{
|
||||
// TODO(Mac): Merge this code with the implementation in WindowsPlatform
|
||||
|
||||
enlistmentRoot = null;
|
||||
|
||||
string finalDirectory;
|
||||
if (!MacFileSystem.TryGetNormalizedPathImplementation(directory, out finalDirectory, out errorMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
enlistmentRoot = Paths.GetRoot(finalDirectory, GVFSConstants.DotGVFS.Root);
|
||||
if (enlistmentRoot == null)
|
||||
{
|
||||
errorMessage = $"Failed to find the root directory for {GVFSConstants.DotGVFS.Root} in {finalDirectory}";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,12 +4,18 @@ using GVFS.Common.Git;
|
|||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
|
||||
namespace GVFS.Platform.Mac
|
||||
{
|
||||
public class MacPlatform : GVFSPlatform
|
||||
public partial class MacPlatform : GVFSPlatform
|
||||
{
|
||||
public MacPlatform()
|
||||
: base(executableExtension: string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public override IKernelDriver KernelDriver { get; } = new ProjFSKext();
|
||||
public override IGitInstallation GitInstallation { get; } = new MacGitInstallation();
|
||||
public override IDiskLayoutUpgradeData DiskLayoutUpgrade { get; } = new MacDiskLayoutUpgradeData();
|
||||
|
@ -31,6 +37,25 @@ namespace GVFS.Platform.Mac
|
|||
return true;
|
||||
}
|
||||
|
||||
public override bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
|
||||
string gvfsHooksPath = Path.Combine(executingDirectory, GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
|
||||
|
||||
File.WriteAllText(
|
||||
commandHookPath,
|
||||
$"#!/bin/sh\n{gvfsHooksPath} {hookName} \"$@\"");
|
||||
GVFSPlatform.Instance.FileSystem.ChangeMode(commandHookPath, Convert.ToInt32("755", 8));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool IsProcessActive(int processId)
|
||||
{
|
||||
return MacPlatform.IsProcessActiveImplementation(processId);
|
||||
}
|
||||
|
||||
public override void StartBackgroundProcess(string programName, string[] args)
|
||||
{
|
||||
ProcessLauncher.StartBackgroundProcess(programName, args);
|
||||
|
@ -77,38 +102,24 @@ namespace GVFS.Platform.Mac
|
|||
{
|
||||
}
|
||||
|
||||
public override string GetNamedPipeName(string enlistmentRoot)
|
||||
{
|
||||
return MacPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
|
||||
}
|
||||
|
||||
public override bool IsConsoleOutputRedirectedToFile()
|
||||
{
|
||||
// TODO(Mac): Implement proper check
|
||||
return false;
|
||||
return MacPlatform.IsConsoleOutputRedirectedToFileImplementation();
|
||||
}
|
||||
|
||||
public override bool IsElevated()
|
||||
{
|
||||
// TODO(Mac): Implement proper check
|
||||
return false;
|
||||
return MacPlatform.IsElevatedImplementation();
|
||||
}
|
||||
|
||||
public override bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
{
|
||||
// TODO(Mac): Merge this code with the implementation in WindowsPlatform
|
||||
|
||||
enlistmentRoot = null;
|
||||
|
||||
string finalDirectory;
|
||||
if (!this.FileSystem.TryGetNormalizedPath(directory, out finalDirectory, out errorMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
enlistmentRoot = Paths.GetRoot(finalDirectory, GVFSConstants.DotGVFS.Root);
|
||||
if (enlistmentRoot == null)
|
||||
{
|
||||
errorMessage = $"Failed to find the root directory for {GVFSConstants.DotGVFS.Root} in {finalDirectory}";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return MacPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,25 +28,45 @@ namespace GVFS.Platform.Windows.DiskLayoutUpgrades
|
|||
}
|
||||
|
||||
string sparseCheckoutPath = Path.Combine(enlistmentRoot, GVFSConstants.WorkingDirectoryRootName, GVFSConstants.DotGit.Info.SparseCheckoutPath);
|
||||
IEnumerable<string> sparseCheckoutLines = fileSystem.ReadAllText(sparseCheckoutPath).Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
bool isRetryable;
|
||||
foreach (string entry in sparseCheckoutLines)
|
||||
using (FileStream fs = File.OpenRead(sparseCheckoutPath))
|
||||
using (StreamReader reader = new StreamReader(fs))
|
||||
{
|
||||
bool isFolder = entry.EndsWith(GVFSConstants.GitPathSeparatorString);
|
||||
if (!modifiedPaths.TryAdd(entry.Trim(GVFSConstants.GitPathSeparator), isFolder, out isRetryable))
|
||||
string entry = reader.ReadLine();
|
||||
while (entry != null)
|
||||
{
|
||||
tracer.RelatedError("Unable to add to the modified paths database.");
|
||||
return false;
|
||||
entry = entry.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(entry))
|
||||
{
|
||||
bool isFolder = entry.EndsWith(GVFSConstants.GitPathSeparatorString);
|
||||
if (!modifiedPaths.TryAdd(entry.Trim(GVFSConstants.GitPathSeparator), isFolder, out isRetryable))
|
||||
{
|
||||
tracer.RelatedError("Unable to add to the modified paths database.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
entry = reader.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
string alwaysExcludePath = Path.Combine(enlistmentRoot, GVFSConstants.WorkingDirectoryRootName, GVFSConstants.DotGit.Info.AlwaysExcludePath);
|
||||
if (fileSystem.FileExists(alwaysExcludePath))
|
||||
{
|
||||
string[] alwaysExcludeLines = fileSystem.ReadAllText(alwaysExcludePath).Split(new string[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
for (int i = alwaysExcludeLines.Length - 1; i >= 0; i--)
|
||||
string alwaysExcludeData = fileSystem.ReadAllText(alwaysExcludePath);
|
||||
|
||||
char[] carriageReturnOrLineFeed = new[] { '\r', '\n' };
|
||||
int endPosition = alwaysExcludeData.Length;
|
||||
while (endPosition > 0)
|
||||
{
|
||||
string entry = alwaysExcludeLines[i];
|
||||
int startPosition = alwaysExcludeData.LastIndexOfAny(carriageReturnOrLineFeed, endPosition - 1);
|
||||
if (startPosition < 0)
|
||||
{
|
||||
startPosition = 0;
|
||||
}
|
||||
|
||||
string entry = alwaysExcludeData.Substring(startPosition, endPosition - startPosition).Trim();
|
||||
|
||||
if (entry.EndsWith("*"))
|
||||
{
|
||||
// This is the first entry using the old format and we don't want to process old entries
|
||||
|
@ -55,16 +75,23 @@ namespace GVFS.Platform.Windows.DiskLayoutUpgrades
|
|||
break;
|
||||
}
|
||||
|
||||
entry = entry.TrimStart('!');
|
||||
bool isFolder = entry.EndsWith(GVFSConstants.GitPathSeparatorString);
|
||||
if (!isFolder)
|
||||
// Substring will not return a null and the Trim will get rid of all the whitespace
|
||||
// if there is a length it will be a valid path that we need to process
|
||||
if (entry.Length > 0)
|
||||
{
|
||||
if (!modifiedPaths.TryAdd(entry.Trim(GVFSConstants.GitPathSeparator), isFolder, out isRetryable))
|
||||
entry = entry.TrimStart('!');
|
||||
bool isFolder = entry.EndsWith(GVFSConstants.GitPathSeparatorString);
|
||||
if (!isFolder)
|
||||
{
|
||||
tracer.RelatedError("Unable to add to the modified paths database.");
|
||||
return false;
|
||||
if (!modifiedPaths.TryAdd(entry.Trim(GVFSConstants.GitPathSeparator), isFolder, out isRetryable))
|
||||
{
|
||||
tracer.RelatedError("Unable to add to the modified paths database.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endPosition = startPosition;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="$(SolutionDir)\GVFS\GVFS.Build\ProjFS.props" />
|
||||
|
@ -92,6 +92,7 @@
|
|||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="WindowsFileSystem.Shared.cs" />
|
||||
<Compile Include="WindowsFileSystemVirtualizer.cs" />
|
||||
<Compile Include="WindowsGitHooksInstaller.cs" />
|
||||
<Compile Include="WindowsGitInstallation.cs" />
|
||||
<Compile Include="WindowsPhysicalDiskInfo.cs" />
|
||||
<Compile Include="WindowsPlatform.cs" />
|
||||
|
|
|
@ -25,6 +25,10 @@ namespace GVFS.Platform.Windows
|
|||
NativeMethods.CreateHardLink(newFileName, existingFileName);
|
||||
}
|
||||
|
||||
public void ChangeMode(string path, int mode)
|
||||
{
|
||||
}
|
||||
|
||||
public bool TryGetNormalizedPath(string path, out string normalizedPath, out string errorMessage)
|
||||
{
|
||||
return WindowsFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Platform.Windows
|
||||
{
|
||||
internal static class WindowsGitHooksInstaller
|
||||
{
|
||||
private const string HooksConfigContentTemplate =
|
||||
@"########################################################################
|
||||
# Automatically generated file, do not modify.
|
||||
# See {0} config setting
|
||||
########################################################################
|
||||
{1}";
|
||||
|
||||
public static void CreateHookCommandConfig(GVFSContext context, string hookName, string commandHookPath)
|
||||
{
|
||||
string targetPath = commandHookPath + GVFSConstants.GitConfig.HooksExtension;
|
||||
|
||||
try
|
||||
{
|
||||
string configSetting = GVFSConstants.GitConfig.HooksPrefix + hookName;
|
||||
string mergedHooks = MergeHooks(context, configSetting, hookName);
|
||||
|
||||
string contents = string.Format(HooksConfigContentTemplate, configSetting, mergedHooks);
|
||||
Exception ex;
|
||||
if (!context.FileSystem.TryWriteTempFileAndRename(targetPath, contents, out ex))
|
||||
{
|
||||
throw new RetryableException("Error installing " + targetPath, ex);
|
||||
}
|
||||
}
|
||||
catch (IOException io)
|
||||
{
|
||||
throw new RetryableException("Error installing " + targetPath, io);
|
||||
}
|
||||
}
|
||||
|
||||
private static string MergeHooks(GVFSContext context, string configSettingName, string hookName)
|
||||
{
|
||||
GitProcess configProcess = new GitProcess(context.Enlistment);
|
||||
string filename;
|
||||
string[] defaultHooksLines = { };
|
||||
|
||||
if (configProcess.TryGetFromConfig(configSettingName, forceOutsideEnlistment: true, value: out filename))
|
||||
{
|
||||
filename = filename.Trim(' ', '\n');
|
||||
defaultHooksLines = File.ReadAllLines(filename);
|
||||
}
|
||||
|
||||
return HooksInstaller.MergeHooksData(defaultHooksLines, filename, hookName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,31 @@
|
|||
using GVFS.Common;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
|
||||
namespace GVFS.Platform.Windows
|
||||
{
|
||||
public partial class WindowsPlatform
|
||||
{
|
||||
private const int StillActive = 259; /* from Win32 STILL_ACTIVE */
|
||||
|
||||
private enum StdHandle
|
||||
{
|
||||
Stdin = -10,
|
||||
Stdout = -11,
|
||||
Stderr = -12
|
||||
}
|
||||
|
||||
private enum FileType : uint
|
||||
{
|
||||
Unknown = 0x0000,
|
||||
Disk = 0x0001,
|
||||
Char = 0x0002,
|
||||
Pipe = 0x0003,
|
||||
Remote = 0x8000,
|
||||
}
|
||||
|
||||
public static bool IsElevatedImplementation()
|
||||
{
|
||||
using (WindowsIdentity id = WindowsIdentity.GetCurrent())
|
||||
|
@ -13,6 +34,33 @@ namespace GVFS.Platform.Windows
|
|||
}
|
||||
}
|
||||
|
||||
public static bool IsProcessActiveImplementation(int processId)
|
||||
{
|
||||
using (SafeFileHandle process = NativeMethods.OpenProcess(NativeMethods.ProcessAccessFlags.QueryLimitedInformation, false, processId))
|
||||
{
|
||||
if (!process.IsInvalid)
|
||||
{
|
||||
uint exitCode;
|
||||
if (NativeMethods.GetExitCodeProcess(process, out exitCode) && exitCode == StillActive)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetNamedPipeNameImplementation(string enlistmentRoot)
|
||||
{
|
||||
return "GVFS_" + enlistmentRoot.ToUpper().Replace(':', '_');
|
||||
}
|
||||
|
||||
public static bool IsConsoleOutputRedirectedToFileImplementation()
|
||||
{
|
||||
return FileType.Disk == GetFileType(GetStdHandle(StdHandle.Stdout));
|
||||
}
|
||||
|
||||
public static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
{
|
||||
enlistmentRoot = null;
|
||||
|
@ -32,5 +80,11 @@ namespace GVFS.Platform.Windows
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr GetStdHandle(StdHandle std);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern FileType GetFileType(IntPtr hdl);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,11 @@ namespace GVFS.Platform.Windows
|
|||
private const string BuildLabRegistryValue = "BuildLab";
|
||||
private const string BuildLabExRegistryValue = "BuildLabEx";
|
||||
|
||||
public WindowsPlatform()
|
||||
: base(executableExtension: ".exe")
|
||||
{
|
||||
}
|
||||
|
||||
public override IKernelDriver KernelDriver { get; } = new ProjFSFilter();
|
||||
public override IGitInstallation GitInstallation { get; } = new WindowsGitInstallation();
|
||||
public override IDiskLayoutUpgradeData DiskLayoutUpgrade { get; } = new WindowsDiskLayoutUpgradeData();
|
||||
|
@ -158,6 +163,16 @@ namespace GVFS.Platform.Windows
|
|||
return WindowsPlatform.IsElevatedImplementation();
|
||||
}
|
||||
|
||||
public override bool IsProcessActive(int processId)
|
||||
{
|
||||
return WindowsPlatform.IsProcessActiveImplementation(processId);
|
||||
}
|
||||
|
||||
public override string GetNamedPipeName(string enlistmentRoot)
|
||||
{
|
||||
return WindowsPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
|
||||
}
|
||||
|
||||
public override void ConfigureVisualStudio(string gitBinPath, ITracer tracer)
|
||||
{
|
||||
const string GitBinPathEnd = "\\cmd\\git.exe";
|
||||
|
@ -181,18 +196,44 @@ namespace GVFS.Platform.Windows
|
|||
{
|
||||
error = null;
|
||||
hooksVersion = null;
|
||||
hooksPath = ProcessHelper.WhereDirectory(GVFSConstants.GVFSHooksExecutableName);
|
||||
hooksPath = ProcessHelper.WhereDirectory(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
|
||||
if (hooksPath == null)
|
||||
{
|
||||
error = "Could not find " + GVFSConstants.GVFSHooksExecutableName;
|
||||
error = "Could not find " + GVFSPlatform.Instance.Constants.GVFSHooksExecutableName;
|
||||
return false;
|
||||
}
|
||||
|
||||
FileVersionInfo hooksFileVersionInfo = FileVersionInfo.GetVersionInfo(hooksPath + "\\" + GVFSConstants.GVFSHooksExecutableName);
|
||||
FileVersionInfo hooksFileVersionInfo = FileVersionInfo.GetVersionInfo(Path.Combine(hooksPath, GVFSPlatform.Instance.Constants.GVFSHooksExecutableName));
|
||||
hooksVersion = hooksFileVersionInfo.ProductVersion;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage)
|
||||
{
|
||||
// The GitHooksLoader requires the following setup to invoke a hook:
|
||||
// Copy GithooksLoader.exe to hook-name.exe
|
||||
// Create a text file named hook-name.hooks that lists the applications to execute for the hook, one application per line
|
||||
|
||||
string gitHooksloaderPath = Path.Combine(executingDirectory, GVFSConstants.DotGit.Hooks.LoaderExecutable);
|
||||
if (!HooksInstaller.TryHooksInstallationAction(
|
||||
() => HooksInstaller.CopyHook(context, gitHooksloaderPath, commandHookPath + GVFSPlatform.Instance.Constants.ExecutableExtension),
|
||||
out errorMessage))
|
||||
{
|
||||
errorMessage = "Failed to copy " + GVFSConstants.DotGit.Hooks.LoaderExecutable + " to " + commandHookPath + GVFSPlatform.Instance.Constants.ExecutableExtension + "\n" + errorMessage;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HooksInstaller.TryHooksInstallationAction(
|
||||
() => WindowsGitHooksInstaller.CreateHookCommandConfig(context, hookName, commandHookPath),
|
||||
out errorMessage))
|
||||
{
|
||||
errorMessage = "Failed to create " + commandHookPath + GVFSConstants.GitConfig.HooksExtension + "\n" + errorMessage;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string GetCurrentUser()
|
||||
{
|
||||
WindowsIdentity identity = WindowsIdentity.GetCurrent();
|
||||
|
@ -204,7 +245,7 @@ namespace GVFS.Platform.Windows
|
|||
|
||||
public override bool IsConsoleOutputRedirectedToFile()
|
||||
{
|
||||
return ConsoleHelper.IsConsoleOutputRedirectedToFile();
|
||||
return WindowsPlatform.IsConsoleOutputRedirectedToFileImplementation();
|
||||
}
|
||||
|
||||
public override bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="GVFS.ProjFS" version="2018.628.1" targetFramework="net461" />
|
||||
<package id="GVFS.ProjFS" version="2018.719.1" targetFramework="net461" />
|
||||
<package id="ManagedEsent" version="1.9.4" targetFramework="net461" />
|
||||
<package id="Microsoft.Database.Collections.Generic" version="1.9.4" targetFramework="net461" />
|
||||
<package id="Microsoft.Database.Isam" version="1.9.4" targetFramework="net461" />
|
||||
|
|
|
@ -0,0 +1,284 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
2673FD9620EBDAA900B64B7F /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2673FD9520EBDAA900B64B7F /* main.cpp */; };
|
||||
2673FD9C20EBDEA500B64B7F /* packet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2673FD9B20EBDEA500B64B7F /* packet.cpp */; };
|
||||
26E839D820FD387D004E53CE /* common.mac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E839D720FD29EC004E53CE /* common.mac.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
2673FD9020EBDAA900B64B7F /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2673FD9220EBDAA900B64B7F /* GVFS.ReadObjectHook */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = GVFS.ReadObjectHook; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2673FD9520EBDAA900B64B7F /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = SOURCE_ROOT; };
|
||||
2673FD9A20EBDEA500B64B7F /* packet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = packet.h; sourceTree = SOURCE_ROOT; };
|
||||
2673FD9B20EBDEA500B64B7F /* packet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = packet.cpp; sourceTree = SOURCE_ROOT; };
|
||||
2673FD9D20EBDEAA00B64B7F /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = common.h; path = ../GVFS.NativeHooks.Common/common.h; sourceTree = SOURCE_ROOT; };
|
||||
26E839D720FD29EC004E53CE /* common.mac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = common.mac.cpp; path = ../GVFS.NativeHooks.Common/common.mac.cpp; sourceTree = SOURCE_ROOT; };
|
||||
26E839DA20FD58B8004E53CE /* stdafx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stdafx.h; sourceTree = SOURCE_ROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
2673FD8F20EBDAA900B64B7F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
2673FD7F20EBD81D00B64B7F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
26E839DA20FD58B8004E53CE /* stdafx.h */,
|
||||
26E839D720FD29EC004E53CE /* common.mac.cpp */,
|
||||
2673FD9D20EBDEAA00B64B7F /* common.h */,
|
||||
2673FD9B20EBDEA500B64B7F /* packet.cpp */,
|
||||
2673FD9A20EBDEA500B64B7F /* packet.h */,
|
||||
2673FD9520EBDAA900B64B7F /* main.cpp */,
|
||||
2673FD9320EBDAA900B64B7F /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2673FD9320EBDAA900B64B7F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2673FD9220EBDAA900B64B7F /* GVFS.ReadObjectHook */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
2673FD9120EBDAA900B64B7F /* GVFS.ReadObjectHook */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 2673FD9720EBDAA900B64B7F /* Build configuration list for PBXNativeTarget "GVFS.ReadObjectHook" */;
|
||||
buildPhases = (
|
||||
2673FD8E20EBDAA900B64B7F /* Sources */,
|
||||
2673FD8F20EBDAA900B64B7F /* Frameworks */,
|
||||
2673FD9020EBDAA900B64B7F /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = GVFS.ReadObjectHook;
|
||||
productName = GVFS.ReadObjectHook;
|
||||
productReference = 2673FD9220EBDAA900B64B7F /* GVFS.ReadObjectHook */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
2673FD8020EBD81D00B64B7F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0940;
|
||||
TargetAttributes = {
|
||||
2673FD9120EBDAA900B64B7F = {
|
||||
CreatedOnToolsVersion = 9.4.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 2673FD8320EBD81D00B64B7F /* Build configuration list for PBXProject "GVFS.ReadObjectHook.Mac" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 2673FD7F20EBD81D00B64B7F;
|
||||
productRefGroup = 2673FD9320EBDAA900B64B7F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
2673FD9120EBDAA900B64B7F /* GVFS.ReadObjectHook */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
2673FD8E20EBDAA900B64B7F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
26E839D820FD387D004E53CE /* common.mac.cpp in Sources */,
|
||||
2673FD9620EBDAA900B64B7F /* main.cpp in Sources */,
|
||||
2673FD9C20EBDEA500B64B7F /* packet.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
2673FD8420EBD81D00B64B7F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2673FD8520EBD81D00B64B7F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
2673FD9820EBDAA900B64B7F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = UBF8T346G9;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
2673FD9920EBDAA900B64B7F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = UBF8T346G9;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
2673FD8320EBD81D00B64B7F /* Build configuration list for PBXProject "GVFS.ReadObjectHook.Mac" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2673FD8420EBD81D00B64B7F /* Debug */,
|
||||
2673FD8520EBD81D00B64B7F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
2673FD9720EBDAA900B64B7F /* Build configuration list for PBXNativeTarget "GVFS.ReadObjectHook" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
2673FD9820EBDAA900B64B7F /* Debug */,
|
||||
2673FD9920EBDAA900B64B7F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 2673FD8020EBD81D00B64B7F /* Project object */;
|
||||
}
|
7
GVFS/GVFS.ReadObjectHook/GVFS.ReadObjectHook.Mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
сгенерированный
Normal file
7
GVFS/GVFS.ReadObjectHook/GVFS.ReadObjectHook.Mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
сгенерированный
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:GVFS.ReadObjectHook.Mac.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -15,6 +15,8 @@
|
|||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>readobject</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.10240.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>GVFS.ReadObjectHook.Windows</ProjectName>
|
||||
<TargetName>GVFS.ReadObjectHook</TargetName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="$(SolutionDir)\GVFS\GVFS.Build\GVFS.cpp.props" />
|
||||
|
@ -109,6 +111,7 @@
|
|||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\GVFS.NativeHooks.Common\common.windows.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="packet.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
|
@ -16,6 +16,9 @@
|
|||
<Filter Include="Shared Header Files">
|
||||
<UniqueIdentifier>{c3243239-d853-4df9-bdbb-9a4efa72a827}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Shared Source Files">
|
||||
<UniqueIdentifier>{e6c30bd2-e246-47d9-ad10-137165f4628c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
|
@ -44,6 +47,9 @@
|
|||
<ClCompile Include="packet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\GVFS.NativeHooks.Common\common.windows.cpp">
|
||||
<Filter>Shared Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Version.rc">
|
|
@ -1,147 +1,150 @@
|
|||
// GVFS.ReadObjectHook
|
||||
//
|
||||
// When GVFS installs GVFS.ReadObjectHook.exe, it copies the file to
|
||||
// the .git\hooks folder, and renames the executable to read-object.exe
|
||||
// read-object.exe is called by git.exe when it fails to find the object it's looking for on disk.
|
||||
// When GVFS installs GVFS.ReadObjectHook, it copies the file to
|
||||
// the .git\hooks folder, and renames the executable to read-object
|
||||
// read-object is called by git when it fails to find the object it's looking for on disk.
|
||||
//
|
||||
// Git and read-object.exe negoiate an interface and capabilities then git issues a "get" command for the missing SHA.
|
||||
// Git and read-object negotiate an interface and capabilities then git issues a "get" command for the missing SHA.
|
||||
// See Git Documentation/Technical/read-object-protocol.txt for details.
|
||||
// GVFS.ReadObjectHook decides which GVFS instance to connect to based on it's path.
|
||||
// GVFS.ReadObjectHook decides which GVFS instance to connect to based on its path.
|
||||
// It then connects to GVFS and asks GVFS to download the requested object (to the .git\objects folder).
|
||||
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "packet.h"
|
||||
#include "packet.h"
|
||||
#include "common.h"
|
||||
|
||||
#define MAX_PACKET_LENGTH 512
|
||||
#define SHA1_LENGTH 40
|
||||
#define MESSAGE_LENGTH (4 + SHA1_LENGTH + 1)
|
||||
#define DLO_REQUEST_LENGTH (4 + SHA1_LENGTH + 1)
|
||||
|
||||
// Expected response:
|
||||
// "S\n" -> Success
|
||||
// "F\n" -> Failure
|
||||
#define DLO_RESPONSE_LENGTH 2
|
||||
|
||||
enum ReadObjectHookErrorReturnCode
|
||||
{
|
||||
ErrorReadObjectProtocol = ReturnCode::LastError + 1,
|
||||
ErrorReadObjectProtocol = ReturnCode::LastError + 1,
|
||||
};
|
||||
|
||||
int DownloadSHA(HANDLE pipeHandle, const char *sha1)
|
||||
int DownloadSHA(PIPE_HANDLE pipeHandle, const char *sha1)
|
||||
{
|
||||
// Construct download request message
|
||||
// Format: "DLO|<40 character SHA>"
|
||||
// Example: "DLO|920C34DCDDFC8F07AC4704C8C0D087D6F2095729"
|
||||
char message[MESSAGE_LENGTH+1];
|
||||
if (_snprintf_s(message, _TRUNCATE, "DLO|%s\n", sha1) < 0)
|
||||
{
|
||||
die(ReturnCode::InvalidSHA, "First argument must be a 40 character SHA, actual value: %s\n", sha1);
|
||||
}
|
||||
// Construct download request message
|
||||
// Format: "DLO|<40 character SHA>"
|
||||
// Example: "DLO|920C34DCDDFC8F07AC4704C8C0D087D6F2095729"
|
||||
char request[DLO_REQUEST_LENGTH+1];
|
||||
if (snprintf(request, DLO_REQUEST_LENGTH+1, "DLO|%s\n", sha1) != DLO_REQUEST_LENGTH)
|
||||
{
|
||||
die(ReturnCode::InvalidSHA, "First argument must be a 40 character SHA, actual value: %s\n", sha1);
|
||||
}
|
||||
|
||||
DWORD bytesWritten;
|
||||
BOOL success = WriteFile(
|
||||
pipeHandle, // pipe handle
|
||||
message, // message
|
||||
MESSAGE_LENGTH, // message length
|
||||
&bytesWritten, // bytes written
|
||||
NULL); // not overlapped
|
||||
unsigned long bytesWritten;
|
||||
int error = 0;
|
||||
bool success = WriteToPipe(
|
||||
pipeHandle,
|
||||
request,
|
||||
DLO_REQUEST_LENGTH,
|
||||
&bytesWritten,
|
||||
&error);
|
||||
|
||||
if (!success || bytesWritten != MESSAGE_LENGTH)
|
||||
{
|
||||
die(ReturnCode::PipeWriteFailed, "Failed to write to pipe (%d)\n", GetLastError());
|
||||
}
|
||||
if (!success || bytesWritten != DLO_REQUEST_LENGTH)
|
||||
{
|
||||
die(ReturnCode::PipeWriteFailed, "Failed to write to pipe (%d)\n", error);
|
||||
}
|
||||
|
||||
char response[DLO_RESPONSE_LENGTH];
|
||||
unsigned long totalBytesRead = 0;
|
||||
error = 0;
|
||||
do
|
||||
{
|
||||
unsigned long bytesRead = 0;
|
||||
success = ReadFromPipe(
|
||||
pipeHandle,
|
||||
response + totalBytesRead,
|
||||
sizeof(response) - (sizeof(char) * totalBytesRead),
|
||||
&bytesRead,
|
||||
&error);
|
||||
totalBytesRead += bytesRead;
|
||||
} while (success && totalBytesRead < DLO_RESPONSE_LENGTH);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
die(ReturnCode::PipeReadFailed, "Read response from pipe failed (%d)\n", error);
|
||||
}
|
||||
|
||||
DWORD bytesRead;
|
||||
do
|
||||
{
|
||||
// Read from the pipe.
|
||||
success = ReadFile(
|
||||
pipeHandle, // pipe handle
|
||||
message, // buffer to receive reply
|
||||
sizeof(message), // size of buffer
|
||||
&bytesRead, // number of bytes read
|
||||
NULL); // not overlapped
|
||||
|
||||
if (!success && GetLastError() != ERROR_MORE_DATA)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (!success); // repeat loop if ERROR_MORE_DATA
|
||||
if (!success)
|
||||
{
|
||||
die(ReturnCode::PipeReadFailed, "Read response from pipe failed (%d)\n", GetLastError());
|
||||
}
|
||||
|
||||
return *message == 'S' ? ReturnCode::Success : ReturnCode::FailureToDownload;
|
||||
return *response == 'S' ? ReturnCode::Success : ReturnCode::FailureToDownload;
|
||||
}
|
||||
|
||||
int main(int, char *argv[])
|
||||
{
|
||||
char packet_buffer[MAX_PACKET_LENGTH];
|
||||
size_t len;
|
||||
int err;
|
||||
char packet_buffer[MAX_PACKET_LENGTH];
|
||||
size_t len;
|
||||
int err;
|
||||
|
||||
// set the mode to binary so we don't get CRLF translation
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
DisableCRLFTranslationOnStdPipes();
|
||||
|
||||
packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if (strcmp(packet_buffer, "git-read-object-client"))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad welcome message\n");
|
||||
}
|
||||
packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if (strcmp(packet_buffer, "git-read-object-client"))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad welcome message\n");
|
||||
}
|
||||
|
||||
packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if (strcmp(packet_buffer, "version=1"))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad version\n");
|
||||
}
|
||||
packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if (strcmp(packet_buffer, "version=1"))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad version\n");
|
||||
}
|
||||
|
||||
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad version end\n");
|
||||
}
|
||||
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad version end\n");
|
||||
}
|
||||
|
||||
packet_txt_write("git-read-object-server");
|
||||
packet_txt_write("version=1");
|
||||
packet_flush();
|
||||
packet_txt_write("git-read-object-server");
|
||||
packet_txt_write("version=1");
|
||||
packet_flush();
|
||||
|
||||
packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if (strcmp(packet_buffer, "capability=get"))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad capability\n");
|
||||
}
|
||||
packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if (strcmp(packet_buffer, "capability=get"))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad capability\n");
|
||||
}
|
||||
|
||||
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad capability end\n");
|
||||
}
|
||||
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad capability end\n");
|
||||
}
|
||||
|
||||
packet_txt_write("capability=get");
|
||||
packet_flush();
|
||||
packet_txt_write("capability=get");
|
||||
packet_flush();
|
||||
|
||||
std::wstring pipeName(GetGVFSPipeName(argv[0]));
|
||||
PATH_STRING pipeName(GetGVFSPipeName(argv[0]));
|
||||
|
||||
HANDLE pipeHandle = CreatePipeToGVFS(pipeName);
|
||||
PIPE_HANDLE pipeHandle = CreatePipeToGVFS(pipeName);
|
||||
|
||||
while (1)
|
||||
{
|
||||
packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if (strcmp(packet_buffer, "command=get"))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad command\n");
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if (strcmp(packet_buffer, "command=get"))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad command\n");
|
||||
}
|
||||
|
||||
len = packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if ((len != SHA1_LENGTH + 5) || strncmp(packet_buffer, "sha1=", 5))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad sha1 in get command\n");
|
||||
}
|
||||
len = packet_txt_read(packet_buffer, sizeof(packet_buffer));
|
||||
if ((len != SHA1_LENGTH + 5) || strncmp(packet_buffer, "sha1=", 5))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad sha1 in get command\n");
|
||||
}
|
||||
|
||||
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad command end\n");
|
||||
}
|
||||
if (packet_txt_read(packet_buffer, sizeof(packet_buffer)))
|
||||
{
|
||||
die(ReadObjectHookErrorReturnCode::ErrorReadObjectProtocol, "Bad command end\n");
|
||||
}
|
||||
|
||||
err = DownloadSHA(pipeHandle, packet_buffer + 5);
|
||||
packet_txt_write(err ? "status=error" : "status=success");
|
||||
packet_flush();
|
||||
}
|
||||
err = DownloadSHA(pipeHandle, packet_buffer + 5);
|
||||
packet_txt_write(err ? "status=error" : "status=success");
|
||||
packet_flush();
|
||||
}
|
||||
|
||||
// we'll never reach here as the signal to exit is having stdin closed which is handled in packet_bin_read
|
||||
// we'll never reach here as the signal to exit is having stdin closed which is handled in packet_bin_read
|
||||
}
|
||||
|
|
|
@ -4,14 +4,11 @@
|
|||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "targetver.h"
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
|
|
|
@ -10,8 +10,8 @@ namespace GVFS.Service
|
|||
|
||||
private Configuration()
|
||||
{
|
||||
this.GVFSLocation = Path.Combine(AssemblyPath, GVFSConstants.GVFSExecutableName);
|
||||
this.GVFSServiceUILocation = Path.Combine(AssemblyPath, GVFSConstants.Service.UIName + GVFSConstants.ExecutableExtension);
|
||||
this.GVFSLocation = Path.Combine(AssemblyPath, GVFSPlatform.Instance.Constants.GVFSExecutableName);
|
||||
this.GVFSServiceUILocation = Path.Combine(AssemblyPath, GVFSConstants.Service.UIName + GVFSPlatform.Instance.Constants.ExecutableExtension);
|
||||
}
|
||||
|
||||
public static Configuration Instance
|
||||
|
|
|
@ -71,7 +71,7 @@ namespace GVFS.Service.Handlers
|
|||
{
|
||||
WindowsGitInstallation windowsGitInstallation = new WindowsGitInstallation();
|
||||
string gitBinPath = windowsGitInstallation.GetInstalledGitBinPath();
|
||||
string hooksPath = ProcessHelper.WhereDirectory(GVFSConstants.GVFSHooksExecutableName);
|
||||
string hooksPath = ProcessHelper.WhereDirectory(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
|
||||
GVFSEnlistment enlistment = null;
|
||||
|
||||
try
|
||||
|
|
|
@ -37,18 +37,16 @@
|
|||
<FilesToSign Include="
|
||||
$(BuildOutputDir)\GitHooksLoader\bin\$(Platform)\$(Configuration)\GitHooksLoader.exe;
|
||||
$(BuildOutputDir)\FastFetch\bin\$(Platform)\$(Configuration)\FastFetch.exe;
|
||||
$(BuildOutputDir)\GVFS.Common\bin\$(Platform)\$(Configuration)\netcoreapp2.0\GVFS.Common.dll;
|
||||
$(BuildOutputDir)\GVFS.Common\bin\$(Platform)\$(Configuration)\netstandard2.0\GVFS.Common.dll;
|
||||
$(BuildOutputDir)\GVFS.GVFlt\bin\$(Platform)\$(Configuration)\GVFS.GVFlt.dll;
|
||||
$(BuildOutputDir)\GVFS.Hooks\bin\$(Platform)\$(Configuration)\GVFS.Hooks.exe;
|
||||
$(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\GVFS.Hooks.exe;
|
||||
$(BuildOutputDir)\GVFS.Mount.Windows\bin\$(Platform)\$(Configuration)\GVFS.Mount.exe;
|
||||
$(BuildOutputDir)\GVFS.Platform.Windows\bin\$(Platform)\$(Configuration)\GVFS.Platform.Windows.dll;
|
||||
$(BuildOutputDir)\GVFS.ReadObjectHook\bin\$(Platform)\$(Configuration)\GVFS.ReadObjectHook.exe;
|
||||
$(BuildOutputDir)\GVFS.ReadObjectHook.Windows\bin\$(Platform)\$(Configuration)\GVFS.ReadObjectHook.exe;
|
||||
$(BuildOutputDir)\GVFS.Service\bin\$(Platform)\$(Configuration)\GVFS.Service.exe;
|
||||
$(BuildOutputDir)\GVFS.Service.UI\bin\$(Platform)\$(Configuration)\GVFS.Service.UI.exe;
|
||||
$(BuildOutputDir)\GVFS.Virtualization\bin\$(Platform)\$(Configuration)\netcoreapp2.0\GVFS.Virtualization.dll;
|
||||
$(BuildOutputDir)\GVFS.Virtualization\bin\$(Platform)\$(Configuration)\netstandard2.0\GVFS.Virtualization.dll;
|
||||
$(BuildOutputDir)\GVFS.VirtualFileSystemHook\bin\$(Platform)\$(Configuration)\GVFS.VirtualFileSystemHook.exe;
|
||||
$(BuildOutputDir)\GVFS.VirtualFileSystemHook.Windows\bin\$(Platform)\$(Configuration)\GVFS.VirtualFileSystemHook.exe;
|
||||
$(BuildOutputDir)\GVFS.Windows\bin\$(Platform)\$(Configuration)\GVFS.exe;">
|
||||
<Authenticode>Microsoft</Authenticode>
|
||||
<InProject>false</InProject>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.0;netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp2.1;netstandard2.0</TargetFrameworks>
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
|
|
|
@ -9,12 +9,10 @@ namespace GVFS.Tests
|
|||
public class NUnitRunner
|
||||
{
|
||||
private List<string> args;
|
||||
private List<string> filters;
|
||||
|
||||
public NUnitRunner(string[] args)
|
||||
{
|
||||
this.args = new List<string>(args);
|
||||
this.filters = new List<string>();
|
||||
}
|
||||
|
||||
public string GetCustomArgWithParam(string arg)
|
||||
|
@ -35,22 +33,13 @@ namespace GVFS.Tests
|
|||
return this.args.Remove(arg);
|
||||
}
|
||||
|
||||
public void ExcludeCategory(string category)
|
||||
public int RunTests(ICollection<string> includeCategories, ICollection<string> excludeCategories)
|
||||
{
|
||||
this.filters.Add("cat!=" + category);
|
||||
}
|
||||
|
||||
public void IncludeCategory(string category)
|
||||
{
|
||||
this.filters.Add("cat==" + category);
|
||||
}
|
||||
|
||||
public int RunTests()
|
||||
{
|
||||
if (this.filters.Count > 0)
|
||||
string filters = GetFiltersArgument(includeCategories, excludeCategories);
|
||||
if (filters.Length > 0)
|
||||
{
|
||||
this.args.Add("--where");
|
||||
this.args.Add(string.Join("&&", this.filters));
|
||||
this.args.Add(filters);
|
||||
}
|
||||
|
||||
DateTime now = DateTime.Now;
|
||||
|
@ -61,5 +50,21 @@ namespace GVFS.Tests
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string GetFiltersArgument(ICollection<string> includeCategories, ICollection<string> excludeCategories)
|
||||
{
|
||||
string filters = string.Empty;
|
||||
if (includeCategories != null && includeCategories.Any())
|
||||
{
|
||||
filters = "(" + string.Join("||", includeCategories.Select(x => $"cat=={x}")) + ")";
|
||||
}
|
||||
|
||||
if (excludeCategories != null && excludeCategories.Any())
|
||||
{
|
||||
filters += (filters.Length > 0 ? "&&" : string.Empty) + string.Join("&&", excludeCategories.Select(x => $"cat!={x}"));
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="GVFS.ProjFS" version="2018.628.1" targetFramework="net461" />
|
||||
<package id="GVFS.ProjFS" version="2018.719.1" targetFramework="net461" />
|
||||
<package id="Microsoft.Data.Sqlite" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Data.Sqlite.Core" version="2.0.0" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
|
||||
|
|
|
@ -19,7 +19,10 @@ namespace GVFS.UnitTests.CommandLine
|
|||
public void MergeHooksDataThrowsOnFoundGVFSHooks()
|
||||
{
|
||||
Assert.Throws<HooksInstaller.HooksConfigurationException>(
|
||||
() => HooksInstaller.MergeHooksData(new string[] { "first", "gvfs.hooks.exe" }, Filename, GVFSConstants.DotGit.Hooks.PreCommandHookName));
|
||||
() => HooksInstaller.MergeHooksData(
|
||||
new string[] { "first", GVFSPlatform.Instance.Constants.GVFSHooksExecutableName },
|
||||
Filename,
|
||||
GVFSConstants.DotGit.Hooks.PreCommandHookName));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
|
@ -30,7 +33,7 @@ namespace GVFS.UnitTests.CommandLine
|
|||
.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(line => !line.StartsWith("#"));
|
||||
|
||||
resultLines.Single().ShouldEqual(GVFSConstants.GVFSHooksExecutableName);
|
||||
resultLines.Single().ShouldEqual(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
|
@ -44,7 +47,7 @@ namespace GVFS.UnitTests.CommandLine
|
|||
resultLines.Count().ShouldEqual(3);
|
||||
resultLines.ElementAt(0).ShouldEqual("first");
|
||||
resultLines.ElementAt(1).ShouldEqual("second");
|
||||
resultLines.ElementAt(2).ShouldEqual(GVFSConstants.GVFSHooksExecutableName);
|
||||
resultLines.ElementAt(2).ShouldEqual(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
|
@ -56,7 +59,7 @@ namespace GVFS.UnitTests.CommandLine
|
|||
.Where(line => !line.StartsWith("#"));
|
||||
|
||||
resultLines.Count().ShouldEqual(3);
|
||||
resultLines.ElementAt(0).ShouldEqual(GVFSConstants.GVFSHooksExecutableName);
|
||||
resultLines.ElementAt(0).ShouldEqual(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
|
||||
resultLines.ElementAt(1).ShouldEqual("first");
|
||||
resultLines.ElementAt(2).ShouldEqual("second");
|
||||
}
|
||||
|
@ -72,7 +75,7 @@ namespace GVFS.UnitTests.CommandLine
|
|||
resultLines.Count().ShouldEqual(3);
|
||||
resultLines.ElementAt(0).ShouldEqual("first");
|
||||
resultLines.ElementAt(1).ShouldEqual("second");
|
||||
resultLines.ElementAt(2).ShouldEqual(GVFSConstants.GVFSHooksExecutableName);
|
||||
resultLines.ElementAt(2).ShouldEqual(GVFSPlatform.Instance.Constants.GVFSHooksExecutableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GVFS.UnitTests.Common
|
||||
{
|
||||
[TestFixture]
|
||||
public class PathsTests
|
||||
{
|
||||
[TestCase]
|
||||
public void CanConvertOSPathToGitFormat()
|
||||
{
|
||||
string systemPath;
|
||||
string expectedGitPath;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
systemPath = @"C:\This\is\a\path";
|
||||
expectedGitPath = @"C:/This/is/a/path";
|
||||
}
|
||||
else
|
||||
{
|
||||
systemPath = @"/This/is/a/path";
|
||||
expectedGitPath = systemPath;
|
||||
}
|
||||
|
||||
string actualTransformedPath = Paths.ConvertPathToGitFormat(systemPath);
|
||||
actualTransformedPath.ShouldEqual(expectedGitPath);
|
||||
|
||||
string doubleTransformedPath = Paths.ConvertPathToGitFormat(actualTransformedPath);
|
||||
doubleTransformedPath.ShouldEqual(expectedGitPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -12,6 +12,11 @@ namespace GVFS.UnitTests.Mock.Common
|
|||
{
|
||||
public class MockPlatform : GVFSPlatform
|
||||
{
|
||||
public MockPlatform()
|
||||
: base(executableExtension: ".mockexe")
|
||||
{
|
||||
}
|
||||
|
||||
public override IKernelDriver KernelDriver => throw new NotSupportedException();
|
||||
|
||||
public override IGitInstallation GitInstallation => throw new NotSupportedException();
|
||||
|
@ -30,6 +35,16 @@ namespace GVFS.UnitTests.Mock.Common
|
|||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override bool TryInstallGitCommandHooks(GVFSContext context, string executingDirectory, string hookName, string commandHookPath, out string errorMessage)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override string GetNamedPipeName(string enlistmentRoot)
|
||||
{
|
||||
return "GVFS_Mock_PipeName";
|
||||
}
|
||||
|
||||
public override NamedPipeServerStream CreatePipeByName(string pipeName)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
@ -70,6 +85,11 @@ namespace GVFS.UnitTests.Mock.Common
|
|||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override bool IsProcessActive(int processId)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
|
|
@ -173,6 +173,12 @@ namespace GVFS.UnitTests.Mock.FileSystem
|
|||
public MockDirectory CreateDirectory(string path)
|
||||
{
|
||||
int lastSlashIdx = path.LastIndexOf(Path.DirectorySeparatorChar);
|
||||
|
||||
if (lastSlashIdx <= 0)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
string parentPath = path.Substring(0, lastSlashIdx);
|
||||
MockDirectory parentDirectory = this.FindDirectory(parentPath);
|
||||
if (parentDirectory == null)
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче