Skip to content

Commit 1d9b7ae

Browse files
authored
Merge pull request #1208 from pmj/mac-kext-182-no-writes-on-offline-roots
Mac ProjFS: Deny I/O on offline roots, with exceptions, part 1 This is the first stage of implementing microsoft#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.
2 parents e547846 + 975c1a3 commit 1d9b7ae

24 files changed

Lines changed: 741 additions & 46 deletions

File tree

GVFS/GVFS.Common/FileSystem/IKernelDriver.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ public interface IKernelDriver
1919
bool TryPrepareFolderForCallbacks(string folderPath, out string error, out Exception exception);
2020
bool IsReady(JsonTracer tracer, string enlistmentRoot, TextWriter output, out string error);
2121
bool IsGVFSUpgradeSupported();
22+
bool RegisterForOfflineIO();
2223
}
2324
}

GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/RepairTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.IO;
66
using System.Linq;
7+
using System.Runtime.InteropServices;
78
using System.Text;
89

910
namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
@@ -12,6 +13,26 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
1213
[Category(Categories.ExtraCoverage)]
1314
public class RepairTests : TestsWithEnlistmentPerTestCase
1415
{
16+
private const string PrjFSLibPath = "libPrjFSLib.dylib";
17+
18+
[OneTimeSetUp]
19+
public void TurnOfflineIOOn()
20+
{
21+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
22+
{
23+
RegisterForOfflineIO();
24+
}
25+
}
26+
27+
[OneTimeTearDown]
28+
public void TurnOfflineIOOff()
29+
{
30+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
31+
{
32+
UnregisterForOfflineIO();
33+
}
34+
}
35+
1536
[TestCase]
1637
public void NoFixesNeeded()
1738
{
@@ -154,6 +175,12 @@ public void FixesCorruptGitConfig()
154175
this.Enlistment.MountGVFS();
155176
}
156177

178+
[DllImport(PrjFSLibPath, EntryPoint = "PrjFS_RegisterForOfflineIO")]
179+
private static extern uint RegisterForOfflineIO();
180+
181+
[DllImport(PrjFSLibPath, EntryPoint = "PrjFS_UnregisterForOfflineIO")]
182+
private static extern uint UnregisterForOfflineIO();
183+
157184
private void CreateCorruptIndexAndRename(string indexPath, Action<FileStream, FileStream> corruptionAction)
158185
{
159186
string tempIndexPath = indexPath + ".lock";

GVFS/GVFS.Platform.Mac/ProjFSKext.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ public bool TryPrepareFolderForCallbacks(string folderPath, out string error, ou
8484
}
8585

8686
return true;
87+
}
88+
89+
public bool RegisterForOfflineIO()
90+
{
91+
return PrjFSLib.Mac.Managed.OfflineIO.RegisterForOfflineIO();
8792
}
8893

8994
private bool TryLoad(ITracer tracer, TextWriter output, out string errorMessage)

GVFS/GVFS.Platform.Windows/ProjFSFilter.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,11 @@ public bool IsReady(JsonTracer tracer, string enlistmentRoot, TextWriter output,
466466
TryAttach(enlistmentRoot, out error);
467467
}
468468

469+
public bool RegisterForOfflineIO()
470+
{
471+
return true;
472+
}
473+
469474
private static bool IsInboxAndEnabled()
470475
{
471476
ProcessResult getOptionalFeatureResult = GetProjFSOptionalFeatureStatus();

GVFS/GVFS/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public class Program
1313
public static void Main(string[] args)
1414
{
1515
GVFSPlatformLoader.Initialize();
16+
GVFSPlatform.Instance.KernelDriver.RegisterForOfflineIO();
1617

1718
Type[] verbTypes = new Type[]
1819
{

ProjFS.Mac/PrjFS.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@
8989
4A558DBA22357AB000AFDE07 /* ProviderMessaging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A558DB822357AB000AFDE07 /* ProviderMessaging.cpp */; };
9090
4A558DBC22357AB000AFDE07 /* ProviderMessaging.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4A558DB922357AB000AFDE07 /* ProviderMessaging.hpp */; };
9191
4A558DBD22357AB000AFDE07 /* ProviderMessaging.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4A558DB922357AB000AFDE07 /* ProviderMessaging.hpp */; };
92+
4A5EC302229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A5EC300229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp */; };
93+
4A5EC303229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4A5EC301229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp */; };
9294
4A781DA52220946000DB7733 /* VirtualizationRootsTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A781DA42220946000DB7733 /* VirtualizationRootsTests.mm */; };
9395
4A781DA72220971E00DB7733 /* VirtualizationRoots.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8A721E42AC50008103C /* VirtualizationRoots.cpp */; };
9496
4A781DAB222330F700DB7733 /* KextMockUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A781DAA222330F700DB7733 /* KextMockUtilities.cpp */; };
@@ -290,6 +292,8 @@
290292
4A2A69A12297375A00ACAAAF /* KextAssertIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KextAssertIntegration.h; sourceTree = "<group>"; };
291293
4A558DB822357AB000AFDE07 /* ProviderMessaging.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProviderMessaging.cpp; sourceTree = "<group>"; };
292294
4A558DB922357AB000AFDE07 /* ProviderMessaging.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ProviderMessaging.hpp; sourceTree = "<group>"; };
295+
4A5EC300229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSOfflineIOUserClient.cpp; sourceTree = "<group>"; };
296+
4A5EC301229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PrjFSOfflineIOUserClient.hpp; sourceTree = "<group>"; };
293297
4A781DA42220946000DB7733 /* VirtualizationRootsTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = VirtualizationRootsTests.mm; sourceTree = "<group>"; };
294298
4A781DA6222094C300DB7733 /* VirtualizationRootsPrivate.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = VirtualizationRootsPrivate.hpp; sourceTree = "<group>"; };
295299
4A781DA82222C6EA00DB7733 /* PrjFSProviderUserClient.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PrjFSProviderUserClient.hpp; sourceTree = "<group>"; };
@@ -465,6 +469,8 @@
465469
4391F89C21E42AC40008103C /* PrjFSKext.cpp */,
466470
4391F8A021E42AC50008103C /* PrjFSLogUserClient.cpp */,
467471
4391F89E21E42AC50008103C /* PrjFSLogUserClient.hpp */,
472+
4A5EC300229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp */,
473+
4A5EC301229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp */,
468474
4391F89521E42AC40008103C /* PrjFSProviderUserClient.cpp */,
469475
4391F89F21E42AC50008103C /* PrjFSProviderUserClientPrivate.hpp */,
470476
4A781DA82222C6EA00DB7733 /* PrjFSProviderUserClient.hpp */,
@@ -561,6 +567,7 @@
561567
files = (
562568
4391F8A821E42AC50008103C /* Locks.hpp in Headers */,
563569
4391F8B821E42AC50008103C /* PrjFSLogUserClient.hpp in Headers */,
570+
4A5EC303229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp in Headers */,
564571
4391F8B221E42AC50008103C /* Memory.hpp in Headers */,
565572
4391F8AE21E42AC50008103C /* VirtualizationRoots.hpp in Headers */,
566573
4391F8BE21E42AC50008103C /* KauthHandler.hpp in Headers */,
@@ -868,6 +875,7 @@
868875
4391F8C121E42AC50008103C /* VirtualizationRoots.cpp in Sources */,
869876
4391F8C021E42AC50008103C /* Locks.cpp in Sources */,
870877
4391F8AD21E42AC50008103C /* KauthHandler.cpp in Sources */,
878+
4A5EC302229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp in Sources */,
871879
4391F8AF21E42AC50008103C /* PrjFSProviderUserClient.cpp in Sources */,
872880
4391F8BA21E42AC50008103C /* PrjFSLogUserClient.cpp in Sources */,
873881
4A558DBA22357AB000AFDE07 /* ProviderMessaging.cpp in Sources */,

ProjFS.Mac/PrjFSKext/ArrayUtilities.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,8 @@ template <typename T> void Array_DefaultInit(T* array, size_t count)
2222
}
2323
}
2424

25+
template <typename T, typename MIN_T, typename MAX_T>
26+
auto clamp(const T& value, const MIN_T& min, const MAX_T& max)
27+
{
28+
return value < min ? min : (value > max ? max : value);
29+
}

0 commit comments

Comments
 (0)