From bf90ffac5a411955948ee636e9fbb0368b4fbbc6 Mon Sep 17 00:00:00 2001 From: Phil Dennis-Jordan Date: Fri, 2 Aug 2019 17:16:44 +0200 Subject: [PATCH] Mac ProjFS kext: Blocks renaming files in offline roots at the hydration stage This causes the offline root check to block the file renaming operation at the hydration stage for empty files. The unit test is updated to check both the empty and hydrated scenarios. --- ProjFS.Mac/PrjFSKext/KauthHandler.cpp | 2 +- .../PrjFSKextTests/HandleOperationTests.mm | 102 +++++++++++------- 2 files changed, 62 insertions(+), 42 deletions(-) diff --git a/ProjFS.Mac/PrjFSKext/KauthHandler.cpp b/ProjFS.Mac/PrjFSKext/KauthHandler.cpp index f92c4f1f34..425e189a11 100644 --- a/ProjFS.Mac/PrjFSKext/KauthHandler.cpp +++ b/ProjFS.Mac/PrjFSKext/KauthHandler.cpp @@ -592,7 +592,7 @@ KEXT_STATIC int HandleVnodeOperation( // Prevent system services from hydrating files as this tends to cause deadlocks with the kauth listeners for Antivirus software CallbackPolicy_UserInitiatedOnly, // Prevent write access to empty files in offline roots. For now allow reads, and always allow the user to delete files. - isWriteOperation, + isWriteOperation || (isRename && s_osSupportsRenameDetection), &root, &vnodeFsidInode, &kauthResult, diff --git a/ProjFS.Mac/PrjFSKextTests/HandleOperationTests.mm b/ProjFS.Mac/PrjFSKextTests/HandleOperationTests.mm index 5b7d32fbe6..ffd3554150 100644 --- a/ProjFS.Mac/PrjFSKextTests/HandleOperationTests.mm +++ b/ProjFS.Mac/PrjFSKextTests/HandleOperationTests.mm @@ -1335,50 +1335,70 @@ - (void) testOfflineRootDeniesRename // to let them happen as we can't distinguish them from file deletions // before it's already happened. - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - TestForAllSupportedDarwinVersions(^{ - InitPendingRenames(); - - if (version_major >= PrjFSDarwinMajorVersion::MacOS10_14_Mojave) - { - string renamedFilePath = self->filePath + "_renamed"; - HandleFileOpOperation( - nullptr, // credential - nullptr, /* idata, unused */ - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(self->testFileVnode.get()), - reinterpret_cast(self->filePath.c_str()), - reinterpret_cast(renamedFilePath.c_str()), - 0); // unused - } - - int deleteAuthResult = - HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(self->context), - reinterpret_cast(self->testFileVnode.get()), - 0, - 0); - - if (version_major <= PrjFSDarwinMajorVersion::MacOS10_13_HighSierra) - { - XCTAssertEqual(deleteAuthResult, KAUTH_RESULT_DEFER); - } - else + // Check the behaviour works for both empty and full files, and empty files are not hydrated. + vector vnode_flags { FileFlags_IsInVirtualizationRoot, FileFlags_IsInVirtualizationRoot | FileFlags_IsEmpty }; + + std::for_each(vnode_flags.begin(), vnode_flags.end(), + [self](uint32_t flags) { - // On Mojave+, renames should be blocked: - XCTAssertEqual(deleteAuthResult, KAUTH_RESULT_DENY); - } - - MockCalls::Clear(); - CleanupPendingRenames(); - }); - + testFileVnode->attrValues.va_flags = flags; + + TestForAllSupportedDarwinVersions(^{ + InitPendingRenames(); + + if (version_major >= PrjFSDarwinMajorVersion::MacOS10_14_Mojave) + { + string renamedFilePath = self->filePath + "_renamed"; + HandleFileOpOperation( + nullptr, // credential + nullptr, /* idata, unused */ + KAUTH_FILEOP_WILL_RENAME, + reinterpret_cast(self->testFileVnode.get()), + reinterpret_cast(self->filePath.c_str()), + reinterpret_cast(renamedFilePath.c_str()), + 0); // unused + } + + int deleteAuthResult = + HandleVnodeOperation( + nullptr, + nullptr, + KAUTH_VNODE_DELETE, + reinterpret_cast(self->context), + reinterpret_cast(self->testFileVnode.get()), + 0, + 0); + + if (version_major <= PrjFSDarwinMajorVersion::MacOS10_13_HighSierra) + { + XCTAssertEqual(deleteAuthResult, KAUTH_RESULT_DEFER); + } + else + { + // On Mojave+, renames should be blocked: + XCTAssertEqual(deleteAuthResult, KAUTH_RESULT_DENY, "flags = 0x%x", flags); + } + + XCTAssertFalse( + MockCalls::DidCallFunction( + ProviderMessaging_TrySendRequestAndWaitForResponse, + _, + MessageType_KtoU_HydrateFile, + self->testFileVnode.get(), + _, + _, + _, + _, + _, + _, + nullptr)); + + MockCalls::Clear(); + CleanupPendingRenames(); + }); + }); } - (void) testOfflineRootAllowsRegisteredProcessAccessToEmptyFile