Skip to content

Commit

Permalink
Merge pull request #1208 from pmj/mac-kext-182-no-writes-on-offline-r…
Browse files Browse the repository at this point in the history
…oots

Mac ProjFS: Deny I/O on offline roots, with exceptions, part 1

This is the first stage of implementing #182 - the kext now blocks write I/O to empty or placeholder files on offline roots.

I/O needs to be blocked when:

1. A message failed to deliver to the provider.
2. A process attempts to write to an empty file in an offline root (this file would subsequently be overwritten by a hydration event)
3. A process attempts to write to a placeholder (hydrated unmodified) file when the provider is offline. The file would not show up in git status and could be overwritten by a subsequent checkout/rebase/merge.
4. A process attempts to rename a file in an offline root.
5. A process attempts to read/execute an empty file in an offline root. The result of the read would be bad data, so failing with denied authorisation is preferable to letting the bad data propagate.
6. A process attempts to create files or directories in an offline root.

This change implements cases 1-4. Cases 5-6 will be covered in a future change.

At first glance this seems like it would be a very simple change, but in practice, some processes must be allowed unfettered access to files in offline roots. So a large part of this patch set is dedicated to implementing a system for allowing exceptions - there is a new type of IOKit user client for processes which need unrestricted access to offline roots, and this needed to be implemented all the way through from kext, via native lib, to (managed) VFS4G code proper.

Next, the vnode handler, which is in the actual business of denying or allowing file access, has been expanded to allow returning different results based on whether the root's provider is offline.

Finally, "deny" results are actually returned for a number of different cases - so far, this only includes writes. Even this caused some test failures, so the test process itself needs to register as an exception with unrestricted offline root access.
  • Loading branch information
pmj authored Aug 6, 2019
2 parents e547846 + 975c1a3 commit 1d9b7ae
Show file tree
Hide file tree
Showing 24 changed files with 741 additions and 46 deletions.
1 change: 1 addition & 0 deletions GVFS/GVFS.Common/FileSystem/IKernelDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ public interface IKernelDriver
bool TryPrepareFolderForCallbacks(string folderPath, out string error, out Exception exception);
bool IsReady(JsonTracer tracer, string enlistmentRoot, TextWriter output, out string error);
bool IsGVFSUpgradeSupported();
bool RegisterForOfflineIO();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
Expand All @@ -12,6 +13,26 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
[Category(Categories.ExtraCoverage)]
public class RepairTests : TestsWithEnlistmentPerTestCase
{
private const string PrjFSLibPath = "libPrjFSLib.dylib";

[OneTimeSetUp]
public void TurnOfflineIOOn()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
RegisterForOfflineIO();
}
}

[OneTimeTearDown]
public void TurnOfflineIOOff()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
UnregisterForOfflineIO();
}
}

[TestCase]
public void NoFixesNeeded()
{
Expand Down Expand Up @@ -154,6 +175,12 @@ public void FixesCorruptGitConfig()
this.Enlistment.MountGVFS();
}

[DllImport(PrjFSLibPath, EntryPoint = "PrjFS_RegisterForOfflineIO")]
private static extern uint RegisterForOfflineIO();

[DllImport(PrjFSLibPath, EntryPoint = "PrjFS_UnregisterForOfflineIO")]
private static extern uint UnregisterForOfflineIO();

private void CreateCorruptIndexAndRename(string indexPath, Action<FileStream, FileStream> corruptionAction)
{
string tempIndexPath = indexPath + ".lock";
Expand Down
5 changes: 5 additions & 0 deletions GVFS/GVFS.Platform.Mac/ProjFSKext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ public bool TryPrepareFolderForCallbacks(string folderPath, out string error, ou
}

return true;
}

public bool RegisterForOfflineIO()
{
return PrjFSLib.Mac.Managed.OfflineIO.RegisterForOfflineIO();
}

private bool TryLoad(ITracer tracer, TextWriter output, out string errorMessage)
Expand Down
5 changes: 5 additions & 0 deletions GVFS/GVFS.Platform.Windows/ProjFSFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,11 @@ public bool IsReady(JsonTracer tracer, string enlistmentRoot, TextWriter output,
TryAttach(enlistmentRoot, out error);
}

public bool RegisterForOfflineIO()
{
return true;
}

private static bool IsInboxAndEnabled()
{
ProcessResult getOptionalFeatureResult = GetProjFSOptionalFeatureStatus();
Expand Down
1 change: 1 addition & 0 deletions GVFS/GVFS/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class Program
public static void Main(string[] args)
{
GVFSPlatformLoader.Initialize();
GVFSPlatform.Instance.KernelDriver.RegisterForOfflineIO();

Type[] verbTypes = new Type[]
{
Expand Down
8 changes: 8 additions & 0 deletions ProjFS.Mac/PrjFS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
4A558DBA22357AB000AFDE07 /* ProviderMessaging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A558DB822357AB000AFDE07 /* ProviderMessaging.cpp */; };
4A558DBC22357AB000AFDE07 /* ProviderMessaging.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4A558DB922357AB000AFDE07 /* ProviderMessaging.hpp */; };
4A558DBD22357AB000AFDE07 /* ProviderMessaging.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4A558DB922357AB000AFDE07 /* ProviderMessaging.hpp */; };
4A5EC302229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A5EC300229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp */; };
4A5EC303229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4A5EC301229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp */; };
4A781DA52220946000DB7733 /* VirtualizationRootsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A781DA42220946000DB7733 /* VirtualizationRootsTests.mm */; };
4A781DA72220971E00DB7733 /* VirtualizationRoots.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8A721E42AC50008103C /* VirtualizationRoots.cpp */; };
4A781DAB222330F700DB7733 /* KextMockUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A781DAA222330F700DB7733 /* KextMockUtilities.cpp */; };
Expand Down Expand Up @@ -290,6 +292,8 @@
4A2A69A12297375A00ACAAAF /* KextAssertIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KextAssertIntegration.h; sourceTree = "<group>"; };
4A558DB822357AB000AFDE07 /* ProviderMessaging.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProviderMessaging.cpp; sourceTree = "<group>"; };
4A558DB922357AB000AFDE07 /* ProviderMessaging.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ProviderMessaging.hpp; sourceTree = "<group>"; };
4A5EC300229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSOfflineIOUserClient.cpp; sourceTree = "<group>"; };
4A5EC301229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PrjFSOfflineIOUserClient.hpp; sourceTree = "<group>"; };
4A781DA42220946000DB7733 /* VirtualizationRootsTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = VirtualizationRootsTests.mm; sourceTree = "<group>"; };
4A781DA6222094C300DB7733 /* VirtualizationRootsPrivate.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = VirtualizationRootsPrivate.hpp; sourceTree = "<group>"; };
4A781DA82222C6EA00DB7733 /* PrjFSProviderUserClient.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PrjFSProviderUserClient.hpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -465,6 +469,8 @@
4391F89C21E42AC40008103C /* PrjFSKext.cpp */,
4391F8A021E42AC50008103C /* PrjFSLogUserClient.cpp */,
4391F89E21E42AC50008103C /* PrjFSLogUserClient.hpp */,
4A5EC300229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp */,
4A5EC301229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp */,
4391F89521E42AC40008103C /* PrjFSProviderUserClient.cpp */,
4391F89F21E42AC50008103C /* PrjFSProviderUserClientPrivate.hpp */,
4A781DA82222C6EA00DB7733 /* PrjFSProviderUserClient.hpp */,
Expand Down Expand Up @@ -561,6 +567,7 @@
files = (
4391F8A821E42AC50008103C /* Locks.hpp in Headers */,
4391F8B821E42AC50008103C /* PrjFSLogUserClient.hpp in Headers */,
4A5EC303229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp in Headers */,
4391F8B221E42AC50008103C /* Memory.hpp in Headers */,
4391F8AE21E42AC50008103C /* VirtualizationRoots.hpp in Headers */,
4391F8BE21E42AC50008103C /* KauthHandler.hpp in Headers */,
Expand Down Expand Up @@ -868,6 +875,7 @@
4391F8C121E42AC50008103C /* VirtualizationRoots.cpp in Sources */,
4391F8C021E42AC50008103C /* Locks.cpp in Sources */,
4391F8AD21E42AC50008103C /* KauthHandler.cpp in Sources */,
4A5EC302229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp in Sources */,
4391F8AF21E42AC50008103C /* PrjFSProviderUserClient.cpp in Sources */,
4391F8BA21E42AC50008103C /* PrjFSLogUserClient.cpp in Sources */,
4A558DBA22357AB000AFDE07 /* ProviderMessaging.cpp in Sources */,
Expand Down
5 changes: 5 additions & 0 deletions ProjFS.Mac/PrjFSKext/ArrayUtilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ template <typename T> void Array_DefaultInit(T* array, size_t count)
}
}

template <typename T, typename MIN_T, typename MAX_T>
auto clamp(const T& value, const MIN_T& min, const MAX_T& max)
{
return value < min ? min : (value > max ? max : value);
}
Loading

0 comments on commit 1d9b7ae

Please sign in to comment.