diff --git a/.azure-pipelines/templates/macos-build-and-unit-test.yml b/.azure-pipelines/templates/macos-build-and-unit-test.yml deleted file mode 100644 index 5a335578d0..0000000000 --- a/.azure-pipelines/templates/macos-build-and-unit-test.yml +++ /dev/null @@ -1,42 +0,0 @@ -steps: - - - task: DotNetCoreInstaller@0 - displayName: Use .NET Core SDK 2.1.301 - inputs: - packageType: sdk - version: '2.1.301' - - - task: InstallAppleCertificate@2 - displayName: Install kext certificate - inputs: - certSecureFile: PrjFSKextAug272018.p12 - certPwd: $(PrjFSKextCertPassword) - keychain: temp - - - script: Scripts/Mac/BuildGVFSForMac.sh $(configuration) - displayName: Build VFSForGit $(configuration) - - - script: MirrorProvider/Scripts/Mac/Build.sh $(configuration) - displayName: Build MirrorProvider $(configuration) - - - task: PublishTestResults@2 - displayName: Publish test results - inputs: - testRunner: NUnit - testResultsFiles: '**/TestResult.xml' - searchFolder: $(System.DefaultWorkingDirectory) - testRunTitle: Mac $(configuration) Unit Tests - publishRunAttachments: true - condition: succeededOrFailed() - - - script: Scripts/Mac/CI/CreateBuildDrop.sh $(configuration) $(Build.ArtifactStagingDirectory)/Tests - displayName: Create functional test drop. - - - task: PublishBuildArtifacts@1 - displayName: Publish functional test drop artifact. - inputs: - pathtoPublish: $(Build.ArtifactStagingDirectory)/Tests - artifactName: "FunctionalTests_$(platformFriendlyName)_$(configuration)" - parallel: true - parallelCount: 8 - condition: succeededOrFailed() diff --git a/.azure-pipelines/templates/macos-functional-test.yml b/.azure-pipelines/templates/macos-functional-test.yml deleted file mode 100644 index 6c2da8f63c..0000000000 --- a/.azure-pipelines/templates/macos-functional-test.yml +++ /dev/null @@ -1,71 +0,0 @@ -steps: - - - task: DotNetCoreInstaller@0 - displayName: Use .NET Core SDK 2.1.301 - inputs: - packageType: sdk - version: '2.1.301' - - - task: InstallAppleCertificate@2 - displayName: Install kext certificate - inputs: - certSecureFile: PrjFSKextAug272018.p12 - certPwd: $(PrjFSKextCertPassword) - keychain: temp - - - bash: rm -rf $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/BuildOutput/Git/* - displayName: Clean previous Git installations - - - task: DownloadBuildArtifacts@0 - displayName: Download functional test drop - inputs: - buildType: current - downloadType: specific - artifactName: FunctionalTests_$(platformFriendlyName)_$(configuration) - downloadPath: $(Build.BinariesDirectory) - - - bash: | - chmod +x $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/*.sh - chmod +x $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/ProjFS.Mac/Scripts/*.sh - chmod +x $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/Publish/* - chmod +x $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/BuildOutput/ProjFS.Mac/Native/$(configuration)/prjfs-log - displayName: Ensure tests assets are executable - - - bash: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/CleanupFunctionalTests.sh - displayName: Clean environment - - - bash: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/PrepFunctionalTests.sh - displayName: Prep functional tests - - - bash: | - $BUILD_BINARIESDIRECTORY/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/ProjFS.Mac/Scripts/LoadPrjFSKext.sh $(configuration) - $BUILD_BINARIESDIRECTORY/FunctionalTests_$(platformFriendlyName)_$(configuration)/BuildOutput/ProjFS.Mac/Native/$(configuration)/prjfs-log > $BUILD_ARTIFACTSTAGINGDIRECTORY/kext.log 2>&1 & - displayName: Enable kext logging - - - bash: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/RunFunctionalTests.sh $(configuration) - displayName: Run functional tests - - - task: PublishTestResults@2 - displayName: Publish functional test results - inputs: - testRunner: NUnit - testResultsFiles: "**\\TestResult*.xml" - searchFolder: $(System.DefaultWorkingDirectory) - testRunTitle: macOS $(configuration) Functional Tests - publishRunAttachments: true - condition: succeededOrFailed() - - - bash: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/CleanupFunctionalTests.sh - displayName: Cleanup - condition: always() - - - task: PublishBuildArtifacts@1 - displayName: Publish kext logs - inputs: - pathtoPublish: $(Build.ArtifactStagingDirectory)/kext.log - artifactName: kext.log - - - bash: sudo rm -rf $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration) - displayName: Cleanup phase 2 - condition: always() - diff --git a/GVFS.sln b/GVFS.sln index 2cee10493b..b79f30d251 100644 --- a/GVFS.sln +++ b/GVFS.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27428.2015 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30509.20 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DCE11095-DA5F-4878-B58D-2702765560F5}" ProjectSection(SolutionItems) = preProject @@ -58,8 +58,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Service.Windows", "GVF {24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {24D161E9-D1F0-4299-BBD3-5D940BEDD535} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Service.Mac", "GVFS\GVFS.Service\GVFS.Service.Mac.csproj", "{03769A07-F216-456B-886B-E07CAF6C5E81}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.ReadObjectHook.Windows", "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} @@ -92,23 +90,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Installer.Windows", "G {2F63B22B-EE26-4266-BF17-28A9146483A1} = {2F63B22B-EE26-4266-BF17-28A9146483A1} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Installer.Mac", "GVFS\GVFS.Installer.Mac\GVFS.Installer.Mac.csproj", "{25229A04-6554-49B1-A95A-3F3B76C5B0C8}" - ProjectSection(ProjectDependencies) = postProject - {03769A07-F216-456B-886B-E07CAF6C5E81} = {03769A07-F216-456B-886B-E07CAF6C5E81} - {4CC2A90D-D240-4382-B4BF-5E175515E492} = {4CC2A90D-D240-4382-B4BF-5E175515E492} - {28939122-7263-41E7-A7E2-CBFB01AD6A04} = {28939122-7263-41E7-A7E2-CBFB01AD6A04} - {A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574} - {F468B05A-95E5-46BC-8C67-B80A78527B7D} = {F468B05A-95E5-46BC-8C67-B80A78527B7D} - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A} = {1DAC3DA6-3D21-4917-B9A8-D60C8712252A} - {FAC6EFC5-A890-4CB2-8C80-6358E358C637} = {FAC6EFC5-A890-4CB2-8C80-6358E358C637} - {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09} = {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09} - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0} = {35CA4DFB-1320-4055-B8F6-F12E0F252FF0} - {AECEC217-2499-403D-B0BB-2962B9BE5970} = {AECEC217-2499-403D-B0BB-2962B9BE5970} - EndProjectSection -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.SignFiles", "GVFS\GVFS.SignFiles\GVFS.SignFiles.csproj", "{2F63B22B-EE26-4266-BF17-28A9146483A1}" ProjectSection(ProjectDependencies) = postProject {17498502-AEFF-4E70-90CC-1D0B56A8ADF5} = {17498502-AEFF-4E70-90CC-1D0B56A8ADF5} + {AECEC217-2499-403D-B0BB-2962B9BE5970} = {AECEC217-2499-403D-B0BB-2962B9BE5970} {1118B427-7063-422F-83B9-5023C8EC5A7A} = {1118B427-7063-422F-83B9-5023C8EC5A7A} {2D23AB54-541F-4ABC-8DCA-08C199E97ABB} = {2D23AB54-541F-4ABC-8DCA-08C199E97ABB} {F468B05A-95E5-46BC-8C67-B80A78527B7D} = {F468B05A-95E5-46BC-8C67-B80A78527B7D} @@ -121,7 +106,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.SignFiles", "GVFS\GVFS {4CE404E7-D3FC-471C-993C-64615861EA63} = {4CE404E7-D3FC-471C-993C-64615861EA63} {24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {24D161E9-D1F0-4299-BBD3-5D940BEDD535} {93B403FD-DAFB-46C5-9636-B122792A548A} = {93B403FD-DAFB-46C5-9636-B122792A548A} - {AECEC217-2499-403D-B0BB-2962B9BE5970} = {AECEC217-2499-403D-B0BB-2962B9BE5970} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.VirtualFileSystemHook.Windows", "GVFS\GVFS.VirtualFileSystemHook\GVFS.VirtualFileSystemHook.Windows.vcxproj", "{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}" @@ -131,18 +115,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.VirtualFileSystemHook. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Virtualization", "GVFS\GVFS.Virtualization\GVFS.Virtualization.csproj", "{F468B05A-95E5-46BC-8C67-B80A78527B7D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrjFSLib.Mac.Managed", "ProjFS.Mac\PrjFSLib.Mac.Managed\PrjFSLib.Mac.Managed.csproj", "{FAC6EFC5-A890-4CB2-8C80-6358E358C637}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Platform.Mac", "GVFS\GVFS.Platform.Mac\GVFS.Platform.Mac.csproj", "{1DAC3DA6-3D21-4917-B9A8-D60C8712252A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Platform.POSIX", "GVFS\GVFS.Platform.POSIX\GVFS.Platform.POSIX.csproj", "{15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.UnitTests", "GVFS\GVFS.UnitTests\GVFS.UnitTests.csproj", "{0D434FA7-6D8C-481E-B0CE-779B59EAEF53}" + ProjectSection(ProjectDependencies) = postProject + {93B403FD-DAFB-46C5-9636-B122792A548A} = {93B403FD-DAFB-46C5-9636-B122792A548A} + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Platform.Windows", "GVFS\GVFS.Platform.Windows\GVFS.Platform.Windows.csproj", "{4CE404E7-D3FC-471C-993C-64615861EA63}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Mac", "GVFS\GVFS\GVFS.Mac.csproj", "{28939122-7263-41E7-A7E2-CBFB01AD6A04}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Windows", "GVFS\GVFS\GVFS.Windows.csproj", "{32220664-594C-4425-B9A0-88E0BE2F3D2A}" ProjectSection(ProjectDependencies) = postProject {2D23AB54-541F-4ABC-8DCA-08C199E97ABB} = {2D23AB54-541F-4ABC-8DCA-08C199E97ABB} @@ -160,8 +141,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Mount.Windows", "GVFS\ {24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {24D161E9-D1F0-4299-BBD3-5D940BEDD535} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Mount.Mac", "GVFS\GVFS.Mount\GVFS.Mount.Mac.csproj", "{35CA4DFB-1320-4055-B8F6-F12E0F252FF0}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.FunctionalTests.Windows", "GVFS\GVFS.FunctionalTests.Windows\GVFS.FunctionalTests.Windows.csproj", "{0F0A008E-AB12-40EC-A671-37A541B08C7F}" ProjectSection(ProjectDependencies) = postProject {17498502-AEFF-4E70-90CC-1D0B56A8ADF5} = {17498502-AEFF-4E70-90CC-1D0B56A8ADF5} @@ -176,22 +155,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.FunctionalTests", "GVF ProjectSection(ProjectDependencies) = postProject {17498502-AEFF-4E70-90CC-1D0B56A8ADF5} = {17498502-AEFF-4E70-90CC-1D0B56A8ADF5} {07F2A520-2AB7-46DD-97C0-75D8E988D55B} = {07F2A520-2AB7-46DD-97C0-75D8E988D55B} - {28939122-7263-41E7-A7E2-CBFB01AD6A04} = {28939122-7263-41E7-A7E2-CBFB01AD6A04} {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5} = {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5} {32220664-594C-4425-B9A0-88E0BE2F3D2A} = {32220664-594C-4425-B9A0-88E0BE2F3D2A} {FA273F69-5762-43D8-AEA1-B4F08090D624} = {FA273F69-5762-43D8-AEA1-B4F08090D624} {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B} = {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B} {BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917} - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0} = {35CA4DFB-1320-4055-B8F6-F12E0F252FF0} EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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}" - ProjectSection(ProjectDependencies) = postProject - {A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574} - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Upgrader", "GVFS\GVFS.Upgrader\GVFS.Upgrader.csproj", "{AECEC217-2499-403D-B0BB-2962B9BE5970}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.PostIndexChangedHook.Windows", "GVFS\GVFS.PostIndexChangedHook\GVFS.PostIndexChangedHook.Windows.vcxproj", "{24D161E9-D1F0-4299-BBD3-5D940BEDD535}" @@ -261,14 +233,6 @@ Global {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}.Release.Mac|x64.ActiveCfg = Release|x64 {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}.Release.Windows|x64.ActiveCfg = Release|x64 {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}.Release.Windows|x64.Build.0 = Release|x64 - {03769A07-F216-456B-886B-E07CAF6C5E81}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {03769A07-F216-456B-886B-E07CAF6C5E81}.Debug.Mac|x64.Build.0 = Debug|x64 - {03769A07-F216-456B-886B-E07CAF6C5E81}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {03769A07-F216-456B-886B-E07CAF6C5E81}.Debug.Windows|x64.Build.0 = Debug|x64 - {03769A07-F216-456B-886B-E07CAF6C5E81}.Release.Mac|x64.ActiveCfg = Release|x64 - {03769A07-F216-456B-886B-E07CAF6C5E81}.Release.Mac|x64.Build.0 = Release|x64 - {03769A07-F216-456B-886B-E07CAF6C5E81}.Release.Windows|x64.ActiveCfg = Release|x64 - {03769A07-F216-456B-886B-E07CAF6C5E81}.Release.Windows|x64.Build.0 = Release|x64 {5A6656D5-81C7-472C-9DC8-32D071CB2258}.Debug.Mac|x64.ActiveCfg = Debug|x64 {5A6656D5-81C7-472C-9DC8-32D071CB2258}.Debug.Windows|x64.ActiveCfg = Debug|x64 {5A6656D5-81C7-472C-9DC8-32D071CB2258}.Debug.Windows|x64.Build.0 = Debug|x64 @@ -305,12 +269,6 @@ Global {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893}.Release.Mac|x64.ActiveCfg = Release|x64 {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893}.Release.Windows|x64.ActiveCfg = Release|x64 {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893}.Release.Windows|x64.Build.0 = Release|x64 - {25229A04-6554-49B1-A95A-3F3B76C5B0C8}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {25229A04-6554-49B1-A95A-3F3B76C5B0C8}.Debug.Mac|x64.Build.0 = Debug|x64 - {25229A04-6554-49B1-A95A-3F3B76C5B0C8}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {25229A04-6554-49B1-A95A-3F3B76C5B0C8}.Release.Mac|x64.ActiveCfg = Release|x64 - {25229A04-6554-49B1-A95A-3F3B76C5B0C8}.Release.Mac|x64.Build.0 = Release|x64 - {25229A04-6554-49B1-A95A-3F3B76C5B0C8}.Release.Windows|x64.ActiveCfg = Release|x64 {2F63B22B-EE26-4266-BF17-28A9146483A1}.Debug.Mac|x64.ActiveCfg = Debug|x64 {2F63B22B-EE26-4266-BF17-28A9146483A1}.Debug.Windows|x64.ActiveCfg = Debug|x64 {2F63B22B-EE26-4266-BF17-28A9146483A1}.Debug.Windows|x64.Build.0 = Debug|x64 @@ -331,22 +289,6 @@ Global {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Mac|x64.Build.0 = Release|x64 {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Windows|x64.ActiveCfg = Release|x64 {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Windows|x64.Build.0 = Release|x64 - {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Mac|x64.Build.0 = Debug|x64 - {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Windows|x64.Build.0 = Debug|x64 - {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Mac|x64.ActiveCfg = Release|x64 - {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Mac|x64.Build.0 = Release|x64 - {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Windows|x64.ActiveCfg = Release|x64 - {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Windows|x64.Build.0 = Release|x64 - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Debug.Mac|x64.Build.0 = Debug|x64 - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Debug.Windows|x64.Build.0 = Debug|x64 - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Release.Mac|x64.ActiveCfg = Release|x64 - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Release.Mac|x64.Build.0 = Release|x64 - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Release.Windows|x64.ActiveCfg = Release|x64 - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Release.Windows|x64.Build.0 = Release|x64 {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Debug.Mac|x64.ActiveCfg = Debug|x64 {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Debug.Mac|x64.Build.0 = Debug|x64 {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Debug.Windows|x64.ActiveCfg = Debug|x64 @@ -369,14 +311,6 @@ Global {4CE404E7-D3FC-471C-993C-64615861EA63}.Release.Mac|x64.ActiveCfg = Release|x64 {4CE404E7-D3FC-471C-993C-64615861EA63}.Release.Windows|x64.ActiveCfg = Release|x64 {4CE404E7-D3FC-471C-993C-64615861EA63}.Release.Windows|x64.Build.0 = Release|x64 - {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Debug.Mac|x64.Build.0 = Debug|x64 - {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Debug.Windows|x64.Build.0 = Debug|x64 - {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Release.Mac|x64.ActiveCfg = Release|x64 - {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Release.Mac|x64.Build.0 = Release|x64 - {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Release.Windows|x64.ActiveCfg = Release|x64 - {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Release.Windows|x64.Build.0 = Release|x64 {32220664-594C-4425-B9A0-88E0BE2F3D2A}.Debug.Mac|x64.ActiveCfg = Debug|x64 {32220664-594C-4425-B9A0-88E0BE2F3D2A}.Debug.Windows|x64.ActiveCfg = Debug|x64 {32220664-594C-4425-B9A0-88E0BE2F3D2A}.Debug.Windows|x64.Build.0 = Debug|x64 @@ -389,14 +323,6 @@ Global {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}.Release.Mac|x64.ActiveCfg = Release|x64 {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}.Release.Windows|x64.ActiveCfg = Release|x64 {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}.Release.Windows|x64.Build.0 = Release|x64 - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Debug.Mac|x64.Build.0 = Debug|x64 - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Debug.Windows|x64.Build.0 = Debug|x64 - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Release.Mac|x64.ActiveCfg = Release|x64 - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Release.Mac|x64.Build.0 = Release|x64 - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Release.Windows|x64.ActiveCfg = Release|x64 - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Release.Windows|x64.Build.0 = Release|x64 {0F0A008E-AB12-40EC-A671-37A541B08C7F}.Debug.Mac|x64.ActiveCfg = Debug|x64 {0F0A008E-AB12-40EC-A671-37A541B08C7F}.Debug.Windows|x64.ActiveCfg = Debug|x64 {0F0A008E-AB12-40EC-A671-37A541B08C7F}.Debug.Windows|x64.Build.0 = Debug|x64 @@ -419,22 +345,14 @@ Global {FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Mac|x64.Build.0 = 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 {AECEC217-2499-403D-B0BB-2962B9BE5970}.Debug.Mac|x64.ActiveCfg = Debug|x64 + {AECEC217-2499-403D-B0BB-2962B9BE5970}.Debug.Mac|x64.Build.0 = Debug|x64 {AECEC217-2499-403D-B0BB-2962B9BE5970}.Debug.Windows|x64.ActiveCfg = Debug|x64 {AECEC217-2499-403D-B0BB-2962B9BE5970}.Debug.Windows|x64.Build.0 = Debug|x64 {AECEC217-2499-403D-B0BB-2962B9BE5970}.Release.Mac|x64.ActiveCfg = Release|x64 + {AECEC217-2499-403D-B0BB-2962B9BE5970}.Release.Mac|x64.Build.0 = Release|x64 {AECEC217-2499-403D-B0BB-2962B9BE5970}.Release.Windows|x64.ActiveCfg = Release|x64 {AECEC217-2499-403D-B0BB-2962B9BE5970}.Release.Windows|x64.Build.0 = Release|x64 - {AECEC217-2499-403D-B0BB-2962B9BE5970}.Debug.Mac|x64.Build.0 = Debug|x64 - {AECEC217-2499-403D-B0BB-2962B9BE5970}.Release.Mac|x64.Build.0 = Release|x64 {24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Debug.Mac|x64.ActiveCfg = Debug|x64 {24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Debug.Windows|x64.ActiveCfg = Debug|x64 {24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Debug.Windows|x64.Build.0 = Debug|x64 @@ -454,30 +372,23 @@ Global {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA} {BDA91EE5-C684-4FC5-A90A-B7D677421917} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} - {03769A07-F216-456B-886B-E07CAF6C5E81} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {5A6656D5-81C7-472C-9DC8-32D071CB2258} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {28674A4B-1223-4633-A460-C8CC39B09318} = {DCE11095-DA5F-4878-B58D-2702765560F5} {C5D3CA26-562F-4CA4-A378-B93E97A730E3} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA} {93B403FD-DAFB-46C5-9636-B122792A548A} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {A4984251-840E-4622-AD0C-66DFCE2B2574} = {AB0D9230-3893-4486-8899-F9C871FB5D5F} {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} - {25229A04-6554-49B1-A95A-3F3B76C5B0C8} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {2F63B22B-EE26-4266-BF17-28A9146483A1} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {2D23AB54-541F-4ABC-8DCA-08C199E97ABB} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {F468B05A-95E5-46BC-8C67-B80A78527B7D} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} - {FAC6EFC5-A890-4CB2-8C80-6358E358C637} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} - {1DAC3DA6-3D21-4917-B9A8-D60C8712252A} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {0D434FA7-6D8C-481E-B0CE-779B59EAEF53} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA} {4CE404E7-D3FC-471C-993C-64615861EA63} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} - {28939122-7263-41E7-A7E2-CBFB01AD6A04} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {32220664-594C-4425-B9A0-88E0BE2F3D2A} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {17498502-AEFF-4E70-90CC-1D0B56A8ADF5} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} - {35CA4DFB-1320-4055-B8F6-F12E0F252FF0} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {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} {AECEC217-2499-403D-B0BB-2962B9BE5970} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} EndGlobalSection diff --git a/GVFS/GVFS.FunctionalTests.LockHolder/AcquireGVFSLock.cs b/GVFS/GVFS.FunctionalTests.LockHolder/AcquireGVFSLock.cs index a1e47f35d5..2a64fdeadc 100644 --- a/GVFS/GVFS.FunctionalTests.LockHolder/AcquireGVFSLock.cs +++ b/GVFS/GVFS.FunctionalTests.LockHolder/AcquireGVFSLock.cs @@ -1,7 +1,6 @@ using CommandLine; using GVFS.Common; using GVFS.Common.NamedPipes; -using GVFS.Platform.Mac; using GVFS.Platform.Windows; using System; using System.Diagnostics; @@ -43,11 +42,6 @@ public void Execute() private static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return MacPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage); - } - // Not able to use WindowsPlatform here - because of its dependency on WindowsIdentity (and also kernel32.dll). enlistmentRoot = null; @@ -70,11 +64,6 @@ private static bool TryGetGVFSEnlistmentRootImplementation(string directory, out private static string GetNamedPipeNameImplementation(string enlistmentRoot) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return MacPlatform.GetNamedPipeNameImplementation(enlistmentRoot); - } - // Not able to use WindowsPlatform here - because of its dependency on WindowsIdentity (and also kernel32.dll). return "GVFS_" + enlistmentRoot.ToUpper().Replace(':', '_'); } diff --git a/GVFS/GVFS.FunctionalTests.LockHolder/GVFS.FunctionalTests.LockHolder.csproj b/GVFS/GVFS.FunctionalTests.LockHolder/GVFS.FunctionalTests.LockHolder.csproj index 82a673437f..837fd98698 100644 --- a/GVFS/GVFS.FunctionalTests.LockHolder/GVFS.FunctionalTests.LockHolder.csproj +++ b/GVFS/GVFS.FunctionalTests.LockHolder/GVFS.FunctionalTests.LockHolder.csproj @@ -25,7 +25,6 @@ - diff --git a/GVFS/GVFS.Hooks/GVFS.Hooks.Mac.csproj b/GVFS/GVFS.Hooks/GVFS.Hooks.Mac.csproj deleted file mode 100644 index 9cf4b76519..0000000000 --- a/GVFS/GVFS.Hooks/GVFS.Hooks.Mac.csproj +++ /dev/null @@ -1,112 +0,0 @@ - - - - Exe - GVFS.Hooks - netcoreapp2.1 - x64 - osx-x64 - false - - - - GVFS.Hooks - GVFS.Hooks - - - $(GVFSVersion) - - - $(GVFSVersion) - - - - - - - - - Common\ConsoleHelper.cs - - - Common\Git\GitConfigHelper.cs - - - Common\Git\GitConfigSetting.cs - - - Common\Git\GitVersion.cs - - - Common\GVFSConstants.cs - - - Common\GVFSEnlistment.Shared.cs - - - Common\GVFSLock.Shared.cs - - - Common\NamedPipes\BrokenPipeException.cs - - - Common\NamedPipes\LockNamedPipeMessages.cs - - - Common\NamedPipes\NamedPipeClient.cs - - - Common\NamedPipes\NamedPipeStreamReader.cs - - - Common\NamedPipes\NamedPipeStreamWriter.cs - - - Common\NativeMethods.Shared.cs - - - Common\Paths.Shared.cs - - - Common\ProcessHelper.cs - - - Common\ProcessResult.cs - - - - Common\Tracing\EventLevel.cs - - - Common\Tracing\EventMetadata.cs - - - Common\Tracing\EventOpcode.cs - - - Common\Tracing\ITracer.cs - - - Common\Tracing\Keywords.cs - - - POSIX\POSIXFileSystem.Shared.cs - - - POSIX\POSIXPlatform.Shared.cs - - - Mac\MacPlatform.Shared.cs - - - - - - all - - - diff --git a/GVFS/GVFS.Installer.Mac/CreateMacInstaller.sh b/GVFS/GVFS.Installer.Mac/CreateMacInstaller.sh deleted file mode 100755 index 50bb22e42b..0000000000 --- a/GVFS/GVFS.Installer.Mac/CreateMacInstaller.sh +++ /dev/null @@ -1,223 +0,0 @@ -#!/bin/bash - -SOURCEDIRECTORY=$1 -if [ -z $SOURCEDIRECTORY ]; then - echo "Error: Source directory not specified" - exit 1 -fi - -CONFIGURATION=$2 -if [ -z $CONFIGURATION ]; then - echo "Error: Build configuration not specified" - exit 1 -fi - -PACKAGEVERSION=$3 -if [ -z $PACKAGEVERSION ]; then - echo "Error: Installer package version not specified" - exit 1 -fi - -BUILDOUTPUTDIR=${4%/} -if [ -z $BUILDOUTPUTDIR ]; then - echo "Error: Build output directory not specified" - exit 1 -fi - -if [ -z $VFS_OUTPUTDIR ]; then - echo "Error: Missing environment variable. VFS_OUTPUTDIR is not set" - exit 1 -fi - -if [ -z $VFS_PUBLISHDIR ]; then - echo "Error: Missing environment variable. VFS_PUBLISHDIR is not set" - exit 1 -fi - -STAGINGDIR="${BUILDOUTPUTDIR}/Staging" -PACKAGESTAGINGDIR="${BUILDOUTPUTDIR}/Packages" -VFSFORGITDESTINATION="usr/local/vfsforgit" -DAEMONPLISTDESTINATION="Library/LaunchDaemons" -AGENTPLISTDESTINATION="Library/LaunchAgents" -LIBRARYEXTENSIONSDESTINATION="Library/Extensions" -LIBRARYAPPSUPPORTDESTINATION="Library/Application Support/VFS For Git" -INSTALLERPACKAGENAME="VFSForGit.$PACKAGEVERSION" -INSTALLERPACKAGEID="com.vfsforgit.pkg" -UNINSTALLERPATH="${SOURCEDIRECTORY}/uninstall_vfsforgit.sh" -SCRIPTSPATH="${SOURCEDIRECTORY}/scripts" -COMPONENTSPLISTPATH="${SOURCEDIRECTORY}/vfsforgit_components.plist" -DIST_FILE_NAME="Distribution.updated.xml" - -function CheckBuildIsAvailable() -{ - if [ ! -d "$VFS_OUTPUTDIR" ] || [ ! -d "$VFS_PUBLISHDIR" ]; then - echo "Error: Could not find VFSForGit Build to package." - exit 1 - fi -} - -function SetPermissions() -{ - chmodCommand="chmod -R 755 \"${STAGINGDIR}\"" - eval $chmodCommand || exit 1 -} - -function CreateInstallerRoot() -{ - mkdirVfsForGit="mkdir -p \"${STAGINGDIR}/$VFSFORGITDESTINATION\"" - eval $mkdirVfsForGit || exit 1 - - mkdirPkgStaging="mkdir -p \"${PACKAGESTAGINGDIR}\"" - eval $mkdirPkgStaging || exit 1 - - mkdirBin="mkdir -p \"${STAGINGDIR}/usr/local/bin\"" - eval $mkdirBin || exit 1 - - mkdirBin="mkdir -p \"${STAGINGDIR}/$LIBRARYEXTENSIONSDESTINATION\"" - eval $mkdirBin || exit 1 - - mkdirBin="mkdir -p \"${STAGINGDIR}/$LIBRARYAPPSUPPORTDESTINATION\"" - eval $mkdirBin || exit 1 - - mkdirBin="mkdir -p \"${STAGINGDIR}/$DAEMONPLISTDESTINATION\"" - eval $mkdirBin || exit 1 - - mkdirBin="mkdir -p \"${STAGINGDIR}/$AGENTPLISTDESTINATION\"" - eval $mkdirBin || exit 1 -} - -function CopyBinariesToInstall() -{ - copyPublishDirectory="cp -Rf \"${VFS_PUBLISHDIR}\"/* \"${STAGINGDIR}/${VFSFORGITDESTINATION}/.\"" - eval $copyPublishDirectory || exit 1 - - removeTestAssemblies="find \"${STAGINGDIR}/${VFSFORGITDESTINATION}\" -name \"*GVFS.*Tests*\" -exec rm -f \"{}\" \";\"" - eval $removeTestAssemblies || exit 1 - - removeDataDirectory="rm -Rf \"${STAGINGDIR}/${VFSFORGITDESTINATION}/Data\"" - eval $removeDataDirectory || exit 1 - - copyPrjFS="cp -Rf \"${VFS_OUTPUTDIR}/ProjFS.Mac/Native/$CONFIGURATION\"/*.dylib \"${STAGINGDIR}/${VFSFORGITDESTINATION}/.\"" - eval $copyPrjFS || exit 1 - - copyPrjFS="cp -Rf \"${VFS_OUTPUTDIR}/ProjFS.Mac/Native/$CONFIGURATION\"/prjfs-log \"${STAGINGDIR}/${VFSFORGITDESTINATION}/.\"" - eval $copyPrjFS || exit 1 - - copyPrjFS="cp -Rf \"${VFS_OUTPUTDIR}/ProjFS.Mac/Native/$CONFIGURATION\"/PrjFSKextLogDaemon \"${STAGINGDIR}/${VFSFORGITDESTINATION}/.\"" - eval $copyPrjFS || exit 1 - - copyUnInstaller="cp -f \"${UNINSTALLERPATH}\" \"${STAGINGDIR}/${VFSFORGITDESTINATION}/.\"" - eval $copyUnInstaller || exit 1 - - copyPrjFS="cp -Rf \"${VFS_OUTPUTDIR}/ProjFS.Mac/Native/$CONFIGURATION\"/PrjFSKext.kext \"${STAGINGDIR}/${LIBRARYEXTENSIONSDESTINATION}/.\"" - eval $copyPrjFS || exit 1 - - copyPrjFS="cp -Rf \"${VFS_OUTPUTDIR}/ProjFS.Mac/Native/$CONFIGURATION/org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist\" \"${STAGINGDIR}/${DAEMONPLISTDESTINATION}/.\"" - eval $copyPrjFS || exit 1 - - copyNotificationApp="cp -Rf \"${VFS_OUTPUTDIR}/GVFS.Notifications/VFSForGit.Mac/Build/Products/$CONFIGURATION/VFS For Git.app\" \"${STAGINGDIR}/${LIBRARYAPPSUPPORTDESTINATION}/.\"" - eval $copyNotificationApp || exit 1 - - copyNotificationPlist="cp -Rf \"${SOURCEDIRECTORY}/../GVFS.Notifications/VFSForGit.Mac/org.vfsforgit.usernotification.plist\" \"${STAGINGDIR}/${AGENTPLISTDESTINATION}/.\"" - eval $copyNotificationPlist || exit 1 - - copyServicePlist="cp -Rf \"${SOURCEDIRECTORY}/../GVFS.Service/Mac/org.vfsforgit.service.plist\" \"${STAGINGDIR}/${AGENTPLISTDESTINATION}/.\"" - eval $copyServicePlist || exit 1 - - currentDirectory=`pwd` - cd "${STAGINGDIR}/usr/local/bin" - linkCommand="ln -sf ../vfsforgit/gvfs gvfs" - eval $linkCommand - cd $currentDirectory -} - -function CreateVFSForGitInstaller() -{ - pkgBuildCommand="/usr/bin/pkgbuild --identifier $INSTALLERPACKAGEID --component-plist \"${COMPONENTSPLISTPATH}\" --scripts \"${SCRIPTSPATH}\" --root \"${STAGINGDIR}\" \"${PACKAGESTAGINGDIR}/$INSTALLERPACKAGENAME.pkg\"" - eval $pkgBuildCommand || exit 1 -} - -function UpdateDistributionFile() -{ - VFSFORGIT_PKG_VERSION=$PACKAGEVERSION - VFSFORGIT_PKG_NAME="$INSTALLERPACKAGENAME.pkg" - GIT_PKG_NAME=$1 - GIT_PKG_VERSION=$2 - - /usr/bin/sed -e "s|VFSFORGIT_VERSION_PLACHOLDER|$VFSFORGIT_PKG_VERSION|g" "$SCRIPTSPATH/Distribution.xml" > "${BUILDOUTPUTDIR}/$DIST_FILE_NAME" - /usr/bin/sed -i.bak "s|VFSFORGIT_PKG_NAME_PLACEHOLDER|$VFSFORGIT_PKG_NAME|g" "${BUILDOUTPUTDIR}/$DIST_FILE_NAME" - - if [ ! -z "$GIT_PKG_NAME" ] && [ ! -z "$GIT_PKG_VERSION" ]; then - GIT_CHOICE_OUTLINE_ELEMENT_TEXT="" - GIT_CHOICE_ID_ELEMENT_TEXT=" " - GIT_PKG_REF_ELEMENT_TEXT="$GIT_PKG_NAME" - else - GIT_CHOICE_OUTLINE_ELEMENT_TEXT="" - GIT_CHOICE_ID_ELEMENT_TEXT="" - GIT_PKG_REF_ELEMENT_TEXT="" - fi - - /usr/bin/sed -i.bak "s|GIT_CHOICE_OUTLINE_PLACEHOLDER|$GIT_CHOICE_OUTLINE_ELEMENT_TEXT|g" "${BUILDOUTPUTDIR}/$DIST_FILE_NAME" - /usr/bin/sed -i.bak "s|GIT_CHOICE_ID_PLACEHOLDER|$GIT_CHOICE_ID_ELEMENT_TEXT|g" "${BUILDOUTPUTDIR}/$DIST_FILE_NAME" - /usr/bin/sed -i.bak "s|GIT_PKG_REF_PLACEHOLDER|$GIT_PKG_REF_ELEMENT_TEXT|g" "${BUILDOUTPUTDIR}/$DIST_FILE_NAME" - - /bin/rm -f "${BUILDOUTPUTDIR}/$DIST_FILE_NAME.bak" -} - -function CreateVFSForGitDistribution() -{ - # Update distribution file(removes Git info from template.) - UpdateDistributionFile "" "" - - buildVFSForGitDistCmd="/usr/bin/productbuild --distribution \"${BUILDOUTPUTDIR}/Distribution.updated.xml\" --package-path \"$PACKAGESTAGINGDIR\" \"${BUILDOUTPUTDIR}/$INSTALLERPACKAGENAME.pkg\"" - echo $buildVFSForGitDistCmd - eval $buildVFSForGitDistCmd || exit 1 - - /bin/rm -f "${BUILDOUTPUTDIR}/$DIST_FILE_NAME" -} - -function CreateMetaDistribution() -{ - GITVERSION="$($VFS_SCRIPTDIR/GetGitVersionNumber.sh)" - GITINSTALLERPKGPATH="$(find $VFS_PACKAGESDIR/gitformac.gvfs.installer/$GITVERSION -type f -name *.pkg)" || exit 1 - - GITPKGNAME="${GITINSTALLERPKGPATH##*/}" - GITINSTALLERPKGNAME="${GITPKGNAME%.pkg}" - GITVERSIONSTRING=`echo $GITINSTALLERPKGNAME | cut -d"-" -f2` - - if [[ -z "$GITVERSION" || -z "$GITVERSIONSTRING" ]]; then - echo "Error creating metapackage: could not determine Git package version." - exit 1 - fi - - if [ ! -f "$GITINSTALLERPKGPATH" ]; then - echo "Error creating metapackage: could not find Git installer package." - exit 1 - fi - - copyGitInstallerPkgToStgCmd="/bin/cp -Rf \"${GITINSTALLERPKGPATH}\" \"${PACKAGESTAGINGDIR}/.\"" - echo $copyGitInstallerPkgToStgCmd - eval $copyGitInstallerPkgToStgCmd || exit 1 - - UpdateDistributionFile "$GITPKGNAME" "$GITVERSIONSTRING" - - METAPACKAGENAME="$INSTALLERPACKAGENAME-Git.$GITVERSION.pkg" - buildMetapkgCmd="/usr/bin/productbuild --distribution \"${BUILDOUTPUTDIR}/Distribution.updated.xml\" --package-path \"$PACKAGESTAGINGDIR\" \"${BUILDOUTPUTDIR}/$METAPACKAGENAME\"" - echo $buildMetapkgCmd - eval $buildMetapkgCmd || exit 1 - - /bin/rm -f "${BUILDOUTPUTDIR}/$DIST_FILE_NAME" -} - -function Run() -{ - CheckBuildIsAvailable - CreateInstallerRoot - CopyBinariesToInstall - SetPermissions - CreateVFSForGitInstaller - CreateVFSForGitDistribution - CreateMetaDistribution -} - -Run diff --git a/GVFS/GVFS.Installer.Mac/GVFS.Installer.Mac.csproj b/GVFS/GVFS.Installer.Mac/GVFS.Installer.Mac.csproj deleted file mode 100644 index 5c05fceb38..0000000000 --- a/GVFS/GVFS.Installer.Mac/GVFS.Installer.Mac.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - GVFS.Installer.Mac - GVFS.Installer.Mac - netcoreapp2.1 - x64 - osx-x64 - - - - $(GVFSVersion) - - - - $(GVFSVersion) - - - - - - - - - - - - - - - - - - - diff --git a/GVFS/GVFS.Installer.Mac/scripts/Distribution.xml b/GVFS/GVFS.Installer.Mac/scripts/Distribution.xml deleted file mode 100644 index 26853166d5..0000000000 --- a/GVFS/GVFS.Installer.Mac/scripts/Distribution.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - VFS For Git - - - - - - - GIT_CHOICE_OUTLINE_PLACEHOLDER - - - - - - - VFSFORGIT_PKG_NAME_PLACEHOLDER - GIT_CHOICE_ID_PLACEHOLDER - GIT_PKG_REF_PLACEHOLDER - - - diff --git a/GVFS/GVFS.Installer.Mac/scripts/postinstall b/GVFS/GVFS.Installer.Mac/scripts/postinstall deleted file mode 100755 index 6b10d368a3..0000000000 --- a/GVFS/GVFS.Installer.Mac/scripts/postinstall +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/bash - -function startOrRestartService() -{ - domain=$1 - service=$2 - if [[ $domain == system* ]]; then - plistPath="/Library/LaunchDaemons" - elif [[ $domain == gui/* ]]; then - plistPath="/Library/LaunchAgents" - fi - startCmd="/bin/launchctl bootstrap $domain $plistPath/$service.plist" - restartCmd="/bin/launchctl kickstart -k $domain/$service" - isLoaded=`/bin/launchctl print $domain/$service | wc -l` - if [ $isLoaded -gt "0" ]; then - echo "Restarting Service: '$restartCmd'" - eval $restartCmd || exit 1 - else - echo "Starting Service: '$startCmd'" - eval $startCmd || exit 1 - fi -} - -# Load PrjFSKext if it is not loaded already -# PrjFSKext is an IOKit kext and should get automatically loaded -# by macOS after install. But the system does not seem to auto-load it -# consistently after every install. The code below checks if it has -# been auto-loaded. If not, it will attempt to load it. -# The BundleID of the Kext(ProjFS.Mac/PrjFS.xcodeproj) is defined in -# ProjFS.Mac/PrjFS.xcodeproj project. -KEXTBUNDLEID="org.vfsforgit.PrjFSKext" -KEXTPATH="/Library/Extensions/PrjFSKext.kext" -kextstatCmd="/usr/sbin/kextstat -l -b $KEXTBUNDLEID" -kextstatOutput=$(eval $kextstatCmd) -if [[ -z "${kextstatOutput// }" ]]; then -# load the kext using kextload command. Installer is run as the -# admin user, so we already have required privileges. In case load -# still fails, then exit 1. This will cause the installer to display -# an installation failed error message. - loadCmd="/sbin/kextload \"$KEXTPATH\"" - echo $loadCmd - eval $loadCmd || exit 1 -else - echo "$kextstatCmd returned non-zero output. This might possibly indicate an error." - echo "$kextstatOutput" -fi - -startOrRestartService "system" "org.vfsforgit.prjfs.PrjFSKextLogDaemon" - -# Load Launch Agents in all active User sessions -# There will be one loginwindow instance for each logged in user, -# get its uid (this will correspond to the logged in user's id.) -# Then use launchctl bootstrap gui/uid to auto load the Service -# for each user. -declare -a launchAgents=( - "org.vfsforgit.usernotification" - "org.vfsforgit.service" -) -for uid in $(ps -Ac -o uid,command | grep -iw "Finder" | awk '{print $1}'); do - for nextLaunchAgent in "${launchAgents[@]}"; do - startOrRestartService "gui/$uid" $nextLaunchAgent - done -done diff --git a/GVFS/GVFS.Installer.Mac/scripts/preinstall b/GVFS/GVFS.Installer.Mac/scripts/preinstall deleted file mode 100755 index 14495d22e1..0000000000 --- a/GVFS/GVFS.Installer.Mac/scripts/preinstall +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Unmount all registered repositories before unloading -# the Kext. -GVFSBINPATH="/usr/local/vfsforgit/gvfs" -if [ -f "${GVFSBINPATH}" ]; then - unmountCmd="${GVFSBINPATH} service --unmount-all" - echo $unmountCmd - eval $unmountCmd || exit 1 -fi - -KEXTBUNDLEID="org.vfsforgit.PrjFSKext" -isKextLoadedCmd="/usr/sbin/kextstat -l -b $KEXTBUNDLEID | wc -l" -isKextLoaded=$(eval $isKextLoadedCmd) -if [ "$isKextLoaded" -gt 0 ]; then - unloadCmd="/sbin/kextunload -b $KEXTBUNDLEID" - echo $unloadCmd - eval $unloadCmd || exit 1 -fi - -LEGACYKEXTBUNDLEID="io.gvfs.PrjFSKext" -isKextLoadedCmd="/usr/sbin/kextstat -l -b $LEGACYKEXTBUNDLEID | wc -l" -isKextLoaded=$(eval $isKextLoadedCmd) -if [ "$isKextLoaded" -gt 0 ]; then - unloadCmd="/sbin/kextunload -b $LEGACYKEXTBUNDLEID" - echo $unloadCmd - eval $unloadCmd || exit 1 -fi diff --git a/GVFS/GVFS.Installer.Mac/uninstall_vfsforgit.sh b/GVFS/GVFS.Installer.Mac/uninstall_vfsforgit.sh deleted file mode 100755 index 43327cbfcf..0000000000 --- a/GVFS/GVFS.Installer.Mac/uninstall_vfsforgit.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash - -KEXTFILENAME="PrjFSKext.kext" -VFSFORDIRECTORY="/usr/local/vfsforgit" -PRJFSKEXTDIRECTORY="/Library/Extensions" -LAUNCHDAEMONDIRECTORY="/Library/LaunchDaemons" -LAUNCHAGENTDIRECTORY="/Library/LaunchAgents" -LIBRARYAPPSUPPORTDIRECTORY="/Library/Application Support/VFS For Git" -SERVICEAPPDIRECTORY="${HOME}/Library/Application Support/GVFS" -LOGDAEMONLAUNCHDFILENAME="org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist" -SERVICEAGENTLAUNCHDFILENAME="org.vfsforgit.service.plist" -GVFSCOMMANDPATH="/usr/local/bin/gvfs" -UNINSTALLERCOMMANDPATH="/usr/local/bin/uninstall_vfsforgit.sh" -INSTALLERPACKAGEID="com.vfsforgit.pkg" -KEXTID="org.vfsforgit.PrjFSKext" - -function UnloadKext() -{ - kextLoaded=`/usr/sbin/kextstat -b "$KEXTID" | wc -l` - if [ $kextLoaded -eq "2" ]; then - unloadCmd="sudo /sbin/kextunload -b $KEXTID" - echo "$unloadCmd..." - eval $unloadCmd || exit 1 - fi -} - -function UnInstallVFSForGit() -{ - if [ -d "${PRJFSKEXTDIRECTORY}/$KEXTFILENAME" ]; then - rmCmd="sudo /bin/rm -Rf ${PRJFSKEXTDIRECTORY}/$KEXTFILENAME" - echo "$rmCmd..." - eval $rmCmd || { echo "Error: Could not delete ${PRJFSKEXTDIRECTORY}/$KEXTFILENAME. Delete it manually."; exit 1; } - fi - - # Check if the daemon is loaded. Unload only if necessary. - isLoadedCmd="sudo launchctl kill SIGCONT system/org.vfsforgit.prjfs.PrjFSKextLogDaemon" - echo "$isLoadedCmd" - if $isLoadedCmd; then - unloadCmd="sudo launchctl unload ${LAUNCHDAEMONDIRECTORY}/$LOGDAEMONLAUNCHDFILENAME" - echo "$unloadCmd..." - eval $unloadCmd || { echo "Error: Could not unload ${LAUNCHDAEMONDIRECTORY}/$LOGDAEMONLAUNCHDFILENAME. Unload it manually (\"$unloadCmd\")."; exit 1; } - fi - - if [ -f "${LAUNCHDAEMONDIRECTORY}/$LOGDAEMONLAUNCHDFILENAME" ]; then - rmCmd="sudo /bin/rm -Rf ${LAUNCHDAEMONDIRECTORY}/$LOGDAEMONLAUNCHDFILENAME" - echo "$rmCmd..." - eval $rmCmd || { echo "Error: Could not delete ${LAUNCHDAEMONDIRECTORY}/$LOGDAEMONLAUNCHDFILENAME. Delete it manually."; exit 1; } - fi - - # Unloading Service LaunchAgent for each user - # There will be one loginwindow instance for each logged in user, - # get its uid (this will correspond to the logged in user's id.) - # Then use launchctl bootout gui/uid to unload the Service - # for each user. - declare -a launchAgents=( - "org.vfsforgit.usernotification" - "org.vfsforgit.service" - ) - for nextLaunchAgent in "${launchAgents[@]}"; do - for uid in $(ps -Ac -o uid,command | grep -iw "loginwindow" | awk '{print $1}'); do - isLoadedCmd="sudo launchctl kill SIGCONT gui/$uid/$nextLaunchAgent" - echo "$isLoadedCmd" - if $isLoadedCmd; then - unloadCmd="launchctl bootout gui/$uid /Library/LaunchAgents/$nextLaunchAgent.plist" - echo "Unloading Service: '$unloadCmd'..." - eval $unloadCmd || exit 1 - fi - done - - rmCmd="sudo /bin/rm -Rf ${LAUNCHAGENTDIRECTORY}/$nextLaunchAgent.plist" - echo "$rmCmd..." - eval $rmCmd || { echo "Error: Could not delete ${LAUNCHAGENTDIRECTORY}/$nextLaunchAgent.plist. Delete it manually."; exit 1; } - done - - if [ -s "${GVFSCOMMANDPATH}" ]; then - rmCmd="sudo /bin/rm -Rf ${GVFSCOMMANDPATH}" - echo "$rmCmd..." - eval $rmCmd || { echo "Error: Could not delete ${GVFSCOMMANDPATH}. Delete it manually."; exit 1; } - fi - - if [ -d "${LIBRARYAPPSUPPORTDIRECTORY}" ]; then - rmCmd="sudo /bin/rm -Rf \"${LIBRARYAPPSUPPORTDIRECTORY}\"" - echo "$rmCmd..." - eval $rmCmd || { echo "Error: Could not delete ${LIBRARYAPPSUPPORTDIRECTORY}. Delete it manually."; exit 1; } - fi - - if [ -d "${SERVICEAPPDIRECTORY}" ]; then - rmCmd="sudo /bin/rm -Rf \"${SERVICEAPPDIRECTORY}\"" - echo "$rmCmd..." - eval $rmCmd || { echo "Error: Could not delete ${SERVICEAPPDIRECTORY}. Delete it manually."; exit 1; } - fi - - if [ -d "${VFSFORDIRECTORY}" ]; then - rmCmd="sudo /bin/rm -Rf ${VFSFORDIRECTORY}" - echo "$rmCmd..." - eval $rmCmd || { echo "Error: Could not delete ${VFSFORDIRECTORY}. Delete it manually."; exit 1; } - fi -} - -function ForgetPackage() -{ - if [ -f "/usr/sbin/pkgutil" ]; then - forgetCmd="sudo /usr/sbin/pkgutil --forget $INSTALLERPACKAGEID" - echo "$forgetCmd..." - eval $forgetCmd - fi -} - -function Run() -{ - UnloadKext - UnInstallVFSForGit - ForgetPackage - echo "Successfully uninstalled VFSForGit" -} - -Run diff --git a/GVFS/GVFS.Installer.Mac/vfsforgit_components.plist b/GVFS/GVFS.Installer.Mac/vfsforgit_components.plist deleted file mode 100644 index 9e7ca98507..0000000000 --- a/GVFS/GVFS.Installer.Mac/vfsforgit_components.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - BundleHasStrictIdentifier - - BundleIsRelocatable - - BundleIsVersionChecked - - BundleOverwriteAction - upgrade - RootRelativeBundlePath - Library/Application Support/VFS For Git/VFS For Git.app - - - BundleIsVersionChecked - - BundleOverwriteAction - upgrade - RootRelativeBundlePath - Library/Extensions/PrjFSKext.kext - - - diff --git a/GVFS/GVFS.Mount/GVFS.Mount.Mac.csproj b/GVFS/GVFS.Mount/GVFS.Mount.Mac.csproj deleted file mode 100644 index 11d456863f..0000000000 --- a/GVFS/GVFS.Mount/GVFS.Mount.Mac.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - Exe - gvfs.mount - netcoreapp2.1 - x64 - osx-x64 - - - - $(GVFSVersion) - - - $(GVFSVersion) - - - - - PlatformLoader.Mac.cs - - - - - - - - - - - all - - - - diff --git a/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/contents.xcworkspacedata b/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 790b6419be..0000000000 --- a/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 08de0be8d3..0000000000 --- a/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded - - - diff --git a/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/xcschemes/GVFS.Native.Mac.xcscheme b/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/xcschemes/GVFS.Native.Mac.xcscheme deleted file mode 100644 index c6476e6593..0000000000 --- a/GVFS/GVFS.Native.Mac/GVFS.Native.Mac.xcworkspace/xcshareddata/xcschemes/GVFS.Native.Mac.xcscheme +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/GVFS/GVFS.Platform.Mac/DiskLayoutUpgrades/DiskLayout18to19Upgrade_SqlitePlacholders.cs b/GVFS/GVFS.Platform.Mac/DiskLayoutUpgrades/DiskLayout18to19Upgrade_SqlitePlacholders.cs deleted file mode 100644 index f844b235b8..0000000000 --- a/GVFS/GVFS.Platform.Mac/DiskLayoutUpgrades/DiskLayout18to19Upgrade_SqlitePlacholders.cs +++ /dev/null @@ -1,9 +0,0 @@ -using GVFS.Common.DiskLayoutUpgrades; - -namespace GVFS.Platform.Mac.DiskLayoutUpgrades -{ - public class DiskLayout18to19Upgrade_SqlitePlacholders : DiskLayoutUpgrade_SqlitePlaceholders - { - protected override int SourceMajorVersion => 18; - } -} diff --git a/GVFS/GVFS.Platform.Mac/DiskLayoutUpgrades/MacDiskLayoutUpgradeData.cs b/GVFS/GVFS.Platform.Mac/DiskLayoutUpgrades/MacDiskLayoutUpgradeData.cs deleted file mode 100644 index bd257b313c..0000000000 --- a/GVFS/GVFS.Platform.Mac/DiskLayoutUpgrades/MacDiskLayoutUpgradeData.cs +++ /dev/null @@ -1,31 +0,0 @@ -using GVFS.Common; -using GVFS.DiskLayoutUpgrades; -using GVFS.Platform.Mac.DiskLayoutUpgrades; - -namespace GVFS.Platform.Mac -{ - public class MacDiskLayoutUpgradeData : IDiskLayoutUpgradeData - { - public DiskLayoutUpgrade[] Upgrades - { - get - { - return new DiskLayoutUpgrade[] - { - new DiskLayout18to19Upgrade_SqlitePlacholders(), - }; - } - } - - public DiskLayoutVersion Version => new DiskLayoutVersion( - currentMajorVersion: 19, - currentMinorVersion: 0, - minimumSupportedMajorVersion: 18); - - public bool TryParseLegacyDiskLayoutVersion(string dotGVFSPath, out int majorVersion) - { - majorVersion = 0; - return false; - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/GVFS.Platform.Mac.csproj b/GVFS/GVFS.Platform.Mac/GVFS.Platform.Mac.csproj deleted file mode 100644 index d702269054..0000000000 --- a/GVFS/GVFS.Platform.Mac/GVFS.Platform.Mac.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - netcoreapp2.1;netstandard2.0 - x64 - true - true - - - - $(GVFSVersion) - - - $(GVFSVersion) - - - - $(GVFSVersion) - - - - - - - - - - - - - all - - - - - - - diff --git a/GVFS/GVFS.Platform.Mac/MacDaemonController.cs b/GVFS/GVFS.Platform.Mac/MacDaemonController.cs deleted file mode 100644 index a54e9a38dc..0000000000 --- a/GVFS/GVFS.Platform.Mac/MacDaemonController.cs +++ /dev/null @@ -1,74 +0,0 @@ -using GVFS.Common; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace GVFS.Platform.Mac -{ - /// - /// Class to query the configured services on macOS - /// - public class MacDaemonController - { - private const string LaunchCtlPath = @"/bin/launchctl"; - private const string LaunchCtlArg = @"list"; - - private IProcessRunner processRunner; - - public MacDaemonController(IProcessRunner processRunner) - { - this.processRunner = processRunner; - } - - public bool TryGetDaemons(string currentUser, out List daemons, out string error) - { - // Consider for future improvement: - // Use Launchtl to run Launchctl as the "real" user, so we can get the process list from the user. - ProcessResult result = this.processRunner.Run(LaunchCtlPath, "asuser " + currentUser + " " + LaunchCtlPath + " " + LaunchCtlArg, true); - - if (result.ExitCode != 0) - { - error = result.Output; - daemons = null; - return false; - } - - return this.TryParseOutput(result.Output, out daemons, out error); - } - - private bool TryParseOutput(string output, out List daemonInfos, out string error) - { - daemonInfos = new List(); - - // 1st line is the header, skip it - foreach (string line in output.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Skip(1)) - { - // The expected output is a list of tab delimited entried: - // PID\tSTATUS\tLABEL - string[] tokens = line.Split('\t'); - - if (tokens.Length != 3) - { - daemonInfos = null; - error = $"Unexpected number of tokens in line: {line}"; - return false; - } - - string label = tokens[2]; - bool isRunning = int.TryParse(tokens[0], out _); - - daemonInfos.Add(new DaemonInfo() { Name = label, IsRunning = isRunning }); - } - - error = null; - return true; - } - - public class DaemonInfo - { - public string Name { get; set; } - public bool IsRunning { get; set; } - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/MacFileBasedLock.cs b/GVFS/GVFS.Platform.Mac/MacFileBasedLock.cs deleted file mode 100644 index 10451482fe..0000000000 --- a/GVFS/GVFS.Platform.Mac/MacFileBasedLock.cs +++ /dev/null @@ -1,133 +0,0 @@ -using GVFS.Common; -using GVFS.Common.FileSystem; -using GVFS.Common.Tracing; -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace GVFS.Platform.Mac -{ - public class MacFileBasedLock : FileBasedLock - { - private int lockFileDescriptor; - - public MacFileBasedLock( - PhysicalFileSystem fileSystem, - ITracer tracer, - string lockPath) - : base(fileSystem, tracer, lockPath) - { - this.lockFileDescriptor = NativeMethods.InvalidFileDescriptor; - } - - public override bool TryAcquireLock() - { - if (this.lockFileDescriptor == NativeMethods.InvalidFileDescriptor) - { - this.FileSystem.CreateDirectory(Path.GetDirectoryName(this.LockPath)); - - this.lockFileDescriptor = NativeMethods.Open( - this.LockPath, - NativeMethods.OpenCreate | NativeMethods.OpenWriteOnly, - NativeMethods.FileMode644); - - if (this.lockFileDescriptor == NativeMethods.InvalidFileDescriptor) - { - int errno = Marshal.GetLastWin32Error(); - EventMetadata metadata = this.CreateEventMetadata(errno); - this.Tracer.RelatedWarning( - metadata, - $"{nameof(MacFileBasedLock)}.{nameof(this.TryAcquireLock)}: Failed to open lock file"); - - return false; - } - } - - if (NativeMethods.FLock(this.lockFileDescriptor, NativeMethods.LockEx | NativeMethods.LockNb) != 0) - { - int errno = Marshal.GetLastWin32Error(); - if (errno != NativeMethods.EIntr && errno != NativeMethods.EWouldBlock) - { - EventMetadata metadata = this.CreateEventMetadata(errno); - this.Tracer.RelatedWarning( - metadata, - $"{nameof(MacFileBasedLock)}.{nameof(this.TryAcquireLock)}: Unexpected error when locking file"); - } - - return false; - } - - return true; - } - - public override void Dispose() - { - if (this.lockFileDescriptor != NativeMethods.InvalidFileDescriptor) - { - if (NativeMethods.Close(this.lockFileDescriptor) != 0) - { - // Failures of close() are logged for diagnostic purposes only. - // It's possible that errors from a previous operation (e.g. write(2)) - // are only reported in close(). We should *not* retry the close() if - // it fails since it may cause a re-used file descriptor from another - // thread to be closed. - - int errno = Marshal.GetLastWin32Error(); - EventMetadata metadata = this.CreateEventMetadata(errno); - this.Tracer.RelatedWarning( - metadata, - $"{nameof(MacFileBasedLock)}.{nameof(this.Dispose)}: Error when closing lock fd"); - } - - this.lockFileDescriptor = NativeMethods.InvalidFileDescriptor; - } - } - - private EventMetadata CreateEventMetadata(int errno = 0) - { - EventMetadata metadata = new EventMetadata(); - metadata.Add("Area", nameof(MacFileBasedLock)); - metadata.Add(nameof(this.LockPath), this.LockPath); - if (errno != 0) - { - metadata.Add(nameof(errno), errno); - } - - return metadata; - } - - private static class NativeMethods - { - // #define O_WRONLY 0x0001 /* open for writing only */ - public const int OpenWriteOnly = 0x0001; - - // #define O_CREAT 0x0200 /* create if nonexistant */ - public const int OpenCreate = 0x0200; - - // #define EINTR 4 /* Interrupted system call */ - public const int EIntr = 4; - - // #define EAGAIN 35 /* Resource temporarily unavailable */ - // #define EWOULDBLOCK EAGAIN /* Operation would block */ - public const int EWouldBlock = 35; - - public const int LockSh = 1; // #define LOCK_SH 1 /* shared lock */ - public const int LockEx = 2; // #define LOCK_EX 2 /* exclusive lock */ - public const int LockNb = 4; // #define LOCK_NB 4 /* don't block when locking */ - public const int LockUn = 8; // #define LOCK_UN 8 /* unlock */ - - public const int InvalidFileDescriptor = -1; - - public static readonly ushort FileMode644 = Convert.ToUInt16("644", 8); - - [DllImport("libc", EntryPoint = "open", SetLastError = true)] - public static extern int Open(string pathname, int flags, ushort mode); - - [DllImport("libc", EntryPoint = "close", SetLastError = true)] - public static extern int Close(int fd); - - [DllImport("libc", EntryPoint = "flock", SetLastError = true)] - public static extern int FLock(int fd, int operation); - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/MacFileSystem.cs b/GVFS/GVFS.Platform.Mac/MacFileSystem.cs deleted file mode 100644 index 39119e30e5..0000000000 --- a/GVFS/GVFS.Platform.Mac/MacFileSystem.cs +++ /dev/null @@ -1,186 +0,0 @@ -using GVFS.Common; -using GVFS.Platform.POSIX; -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace GVFS.Platform.Mac -{ - public class MacFileSystem : POSIXFileSystem - { - public override void ChangeMode(string path, ushort mode) - { - Chmod(path, mode); - } - - public override bool HydrateFile(string fileName, byte[] buffer) - { - return NativeFileReader.TryReadFirstByteOfFile(fileName, buffer); - } - - public override bool IsExecutable(string fileName) - { - NativeStat.StatBuffer statBuffer = this.StatFile(fileName); - return NativeStat.IsExecutable(statBuffer.Mode); - } - - public override bool IsSocket(string fileName) - { - NativeStat.StatBuffer statBuffer = this.StatFile(fileName); - return NativeStat.IsSock(statBuffer.Mode); - } - - public override bool IsFileSystemSupported(string path, out string error) - { - error = null; - - try - { - string lowerCaseFilePath = Path.Combine(path, $"casetest{Guid.NewGuid().ToString()}"); - string upperCaseFilePath = lowerCaseFilePath.ToUpper(); - - File.Create(lowerCaseFilePath); - if (File.Exists(upperCaseFilePath)) - { - File.Delete(lowerCaseFilePath); - return true; - } - - File.Delete(lowerCaseFilePath); - error = "VFS for Git does not support case sensitive filesystems"; - return false; - } - catch (Exception ex) - { - error = $"Exception when performing {nameof(MacFileSystem)}.{nameof(this.IsFileSystemSupported)}: {ex.ToString()}"; - return false; - } - } - - [DllImport("libc", EntryPoint = "chmod", SetLastError = true)] - private static extern int Chmod(string pathname, ushort mode); - - private NativeStat.StatBuffer StatFile(string fileName) - { - if (NativeStat.Stat(fileName, out NativeStat.StatBuffer statBuffer) != 0) - { - NativeMethods.ThrowLastWin32Exception($"Failed to stat {fileName}"); - } - - return statBuffer; - } - - private static class NativeStat - { - // #define S_IFMT 0170000 /* [XSI] type of file mask */ - private static readonly ushort IFMT = Convert.ToUInt16("170000", 8); - - // #define S_IFSOCK 0140000 /* [XSI] socket */ - private static readonly ushort IFSOCK = Convert.ToUInt16("0140000", 8); - - // #define S_IXUSR 0000100 /* [XSI] X for owner */ - private static readonly ushort IXUSR = Convert.ToUInt16("100", 8); - - // #define S_IXGRP 0000010 /* [XSI] X for group */ - private static readonly ushort IXGRP = Convert.ToUInt16("10", 8); - - // #define S_IXOTH 0000001 /* [XSI] X for other */ - private static readonly ushort IXOTH = Convert.ToUInt16("1", 8); - - public static bool IsSock(ushort mode) - { - // #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* socket */ - return (mode & IFMT) == IFSOCK; - } - - public static bool IsExecutable(ushort mode) - { - return (mode & (IXUSR | IXGRP | IXOTH)) != 0; - } - - [DllImport("libc", EntryPoint = "stat$INODE64", SetLastError = true)] - public static extern int Stat(string path, [Out] out StatBuffer statBuffer); - - [StructLayout(LayoutKind.Sequential)] - public struct TimeSpec - { - public long Sec; - public long Nsec; - } - - [StructLayout(LayoutKind.Sequential)] - public struct StatBuffer - { - public int Dev; /* ID of device containing file */ - public ushort Mode; /* Mode of file (see below) */ - public ushort NLink; /* Number of hard links */ - public ulong Ino; /* File serial number */ - public uint UID; /* User ID of the file */ - public uint GID; /* Group ID of the file */ - public int RDev; /* Device ID */ - - public TimeSpec ATimespec; /* time of last access */ - public TimeSpec MTimespec; /* time of last data modification */ - public TimeSpec CTimespec; /* time of last status change */ - public TimeSpec BirthTimespec; /* time of file creation(birth) */ - - public long Size; /* file size, in bytes */ - public long Blocks; /* blocks allocated for file */ - public int BlkSize; /* optimal blocksize for I/O */ - public uint Glags; /* user defined flags for file */ - public uint Gen; /* file generation number */ - public int LSpare; /* RESERVED: DO NOT USE! */ - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public long[] QSpare; /* RESERVED: DO NOT USE! */ - } - } - - private static class NativeFileReader - { - // #define O_RDONLY 0x0000 /* open for reading only */ - private const int ReadOnly = 0x0000; - - internal static bool TryReadFirstByteOfFile(string fileName, byte[] buffer) - { - int fileDescriptor = -1; - bool readStatus = false; - try - { - fileDescriptor = Open(fileName, ReadOnly); - if (fileDescriptor != -1) - { - readStatus = TryReadOneByte(fileDescriptor, buffer); - } - } - finally - { - Close(fileDescriptor); - } - - return readStatus; - } - - [DllImport("libc", EntryPoint = "open", SetLastError = true)] - private static extern int Open(string path, int flag); - - [DllImport("libc", EntryPoint = "close", SetLastError = true)] - private static extern int Close(int fd); - - [DllImport("libc", EntryPoint = "read", SetLastError = true)] - private static extern int Read(int fd, [Out] byte[] buf, int count); - - private static bool TryReadOneByte(int fileDescriptor, byte[] buffer) - { - int numBytes = Read(fileDescriptor, buffer, 1); - - if (numBytes == -1) - { - return false; - } - - return true; - } - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/MacFileSystemVirtualizer.cs b/GVFS/GVFS.Platform.Mac/MacFileSystemVirtualizer.cs deleted file mode 100644 index 2efb84f2e3..0000000000 --- a/GVFS/GVFS.Platform.Mac/MacFileSystemVirtualizer.cs +++ /dev/null @@ -1,734 +0,0 @@ -using GVFS.Common; -using GVFS.Common.Git; -using GVFS.Common.Tracing; -using GVFS.Virtualization.BlobSize; -using GVFS.Virtualization.FileSystem; -using GVFS.Virtualization.Projection; -using PrjFSLib.Mac; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Threading; - -namespace GVFS.Platform.Mac -{ - public class MacFileSystemVirtualizer : FileSystemVirtualizer - { - public static readonly byte[] PlaceholderVersionId = ToVersionIdByteArray(new byte[] { PlaceholderVersion }); - - private const int SymLinkTargetBufferSize = 4096; - private const long DummyFileSize = -1; - - private const string ClassName = nameof(MacFileSystemVirtualizer); - - private VirtualizationInstance virtualizationInstance; - - public MacFileSystemVirtualizer(GVFSContext context, GVFSGitObjects gitObjects) - : this(context, gitObjects, virtualizationInstance: null) - { - } - - public MacFileSystemVirtualizer( - GVFSContext context, - GVFSGitObjects gitObjects, - VirtualizationInstance virtualizationInstance) - : base(context, gitObjects) - { - this.virtualizationInstance = virtualizationInstance ?? new VirtualizationInstance(); - } - - protected override string EtwArea => ClassName; - - public static FSResult ResultToFSResult(Result result) - { - switch (result) - { - case Result.Invalid: - return FSResult.IOError; - - case Result.Success: - return FSResult.Ok; - - case Result.EFileNotFound: - case Result.EPathNotFound: - return FSResult.FileOrPathNotFound; - - case Result.EDirectoryNotEmpty: - return FSResult.DirectoryNotEmpty; - - case Result.EVirtualizationInvalidOperation: - return FSResult.VirtualizationInvalidOperation; - - default: - return FSResult.IOError; - } - } - - public override FileSystemResult ClearNegativePathCache(out uint totalEntryCount) - { - totalEntryCount = 0; - return new FileSystemResult(FSResult.Ok, rawResult: unchecked((int)Result.Success)); - } - - public override FileSystemResult DeleteFile(string relativePath, UpdatePlaceholderType updateFlags, out UpdateFailureReason failureReason) - { - UpdateFailureCause failureCause; - Result result = this.virtualizationInstance.DeleteFile(relativePath, (UpdateType)updateFlags, out failureCause); - failureReason = (UpdateFailureReason)failureCause; - return new FileSystemResult(ResultToFSResult(result), unchecked((int)result)); - } - - public override void Stop() - { - this.Context.Tracer.RelatedEvent(EventLevel.Informational, $"{nameof(this.Stop)}_StopRequested", metadata: null); - } - - /// - /// Writes a placeholder file. - /// - /// Placeholder's path relative to the root of the repo - /// Length of the file (ignored on this platform) - /// The SHA of the placeholder's contents, stored as the content ID in the placeholder - public override FileSystemResult WritePlaceholderFile( - string relativePath, - long endOfFile, - string sha) - { - // TODO(#223): Add functional tests that validate file mode is set correctly - GitIndexProjection.FileType fileType; - ushort fileMode; - this.FileSystemCallbacks.GitIndexProjection.GetFileTypeAndMode(relativePath, out fileType, out fileMode); - - if (fileType == GitIndexProjection.FileType.Regular) - { - Result result = this.virtualizationInstance.WritePlaceholderFile( - relativePath, - PlaceholderVersionId, - ToVersionIdByteArray(FileSystemVirtualizer.ConvertShaToContentId(sha)), - fileMode); - - return new FileSystemResult(ResultToFSResult(result), unchecked((int)result)); - } - else if (fileType == GitIndexProjection.FileType.SymLink) - { - string symLinkTarget; - if (this.TryGetSymLinkTarget(sha, out symLinkTarget)) - { - Result result = this.virtualizationInstance.WriteSymLink(relativePath, symLinkTarget); - - this.FileSystemCallbacks.OnFileSymLinkCreated(relativePath); - - return new FileSystemResult(ResultToFSResult(result), unchecked((int)result)); - } - - EventMetadata metadata = this.CreateEventMetadata(relativePath); - metadata.Add(nameof(sha), sha); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.WritePlaceholderFile)}: Failed to read contents of symlink object"); - return new FileSystemResult(FSResult.IOError, 0); - } - else - { - EventMetadata metadata = this.CreateEventMetadata(relativePath); - metadata.Add(nameof(fileType), fileType); - metadata.Add(nameof(fileMode), fileMode); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.WritePlaceholderFile)}: Unsupported fileType"); - return new FileSystemResult(FSResult.IOError, 0); - } - } - - public override FileSystemResult WritePlaceholderDirectory(string relativePath) - { - Result result = this.virtualizationInstance.WritePlaceholderDirectory(relativePath); - return new FileSystemResult(ResultToFSResult(result), unchecked((int)result)); - } - - /// Length of the file, not required on the Mac platform - public override FileSystemResult UpdatePlaceholderIfNeeded( - string relativePath, - DateTime creationTime, - DateTime lastAccessTime, - DateTime lastWriteTime, - DateTime changeTime, - FileAttributes fileAttributes, - long endOfFile, - string shaContentId, - UpdatePlaceholderType updateFlags, - out UpdateFailureReason failureReason) - { - UpdateFailureCause failureCause = UpdateFailureCause.NoFailure; - - // TODO(#223): Add functional tests that include: - // - Mode + content changes between commits - // - Mode only changes (without any change to content, see issue #223) - GitIndexProjection.FileType fileType; - ushort fileMode; - this.FileSystemCallbacks.GitIndexProjection.GetFileTypeAndMode(relativePath, out fileType, out fileMode); - - if (fileType == GitIndexProjection.FileType.Regular) - { - Result result = this.virtualizationInstance.UpdatePlaceholderIfNeeded( - relativePath, - PlaceholderVersionId, - ToVersionIdByteArray(ConvertShaToContentId(shaContentId)), - fileMode, - (UpdateType)updateFlags, - out failureCause); - - failureReason = (UpdateFailureReason)failureCause; - return new FileSystemResult(ResultToFSResult(result), unchecked((int)result)); - } - else if (fileType == GitIndexProjection.FileType.SymLink) - { - string symLinkTarget; - if (this.TryGetSymLinkTarget(shaContentId, out symLinkTarget)) - { - Result result = this.virtualizationInstance.ReplacePlaceholderFileWithSymLink( - relativePath, - symLinkTarget, - (UpdateType)updateFlags, - out failureCause); - - this.FileSystemCallbacks.OnFileSymLinkCreated(relativePath); - - failureReason = (UpdateFailureReason)failureCause; - return new FileSystemResult(ResultToFSResult(result), unchecked((int)result)); - } - - EventMetadata metadata = this.CreateEventMetadata(relativePath); - metadata.Add(nameof(shaContentId), shaContentId); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.UpdatePlaceholderIfNeeded)}: Failed to read contents of symlink object"); - failureReason = UpdateFailureReason.NoFailure; - return new FileSystemResult(FSResult.IOError, 0); - } - else - { - EventMetadata metadata = this.CreateEventMetadata(relativePath); - metadata.Add(nameof(fileType), fileType); - metadata.Add(nameof(fileMode), fileMode); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.UpdatePlaceholderIfNeeded)}: Unsupported fileType"); - failureReason = UpdateFailureReason.NoFailure; - return new FileSystemResult(FSResult.IOError, 0); - } - } - - public override FileSystemResult DehydrateFolder(string relativePath) - { - FileSystemResult result = new FileSystemResult(FSResult.Ok, 0); - GitIndexProjection.PathSparseState sparseState = this.FileSystemCallbacks.GitIndexProjection.GetFolderPathSparseState(relativePath); - - if (sparseState == GitIndexProjection.PathSparseState.Included) - { - // When the folder is included we need to create the placeholder to make sure it is on disk for enumeration - result = this.WritePlaceholderDirectory(relativePath); - if (result.Result == FSResult.Ok) - { - this.FileSystemCallbacks.OnPlaceholderFolderCreated(relativePath, string.Empty); - } - else if (result.Result == FSResult.FileOrPathNotFound) - { - // This will happen when the parent folder is also in the dehydrate list and is no longer on disk. - result = new FileSystemResult(FSResult.Ok, 0); - } - else - { - EventMetadata metadata = this.CreateEventMetadata(relativePath); - metadata.Add(nameof(result.Result), result.Result); - metadata.Add(nameof(result.RawResult), result.RawResult); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.DehydrateFolder)}: Write placeholder failed"); - } - } - - return result; - } - - public override bool TryStart(out string error) - { - error = string.Empty; - - // Callbacks - this.virtualizationInstance.OnEnumerateDirectory = this.OnEnumerateDirectory; - this.virtualizationInstance.OnGetFileStream = this.OnGetFileStream; - this.virtualizationInstance.OnLogError = this.OnLogError; - this.virtualizationInstance.OnLogWarning = this.OnLogWarning; - this.virtualizationInstance.OnLogInfo = this.OnLogInfo; - this.virtualizationInstance.OnFileModified = this.OnFileModified; - this.virtualizationInstance.OnPreDelete = this.OnPreDelete; - this.virtualizationInstance.OnNewFileCreated = this.OnNewFileCreated; - this.virtualizationInstance.OnFileRenamed = this.OnFileRenamed; - this.virtualizationInstance.OnHardLinkCreated = this.OnHardLinkCreated; - this.virtualizationInstance.OnFilePreConvertToFull = this.NotifyFilePreConvertToFull; - - uint threadCount = (uint)Environment.ProcessorCount * 2; - - Result result = this.virtualizationInstance.StartVirtualizationInstance( - this.Context.Enlistment.WorkingDirectoryRoot, - threadCount); - - if (result != Result.Success) - { - this.Context.Tracer.RelatedError($"{nameof(this.virtualizationInstance.StartVirtualizationInstance)} failed: " + result.ToString("X") + "(" + result.ToString("G") + ")"); - error = "Failed to start virtualization instance (" + result.ToString() + ")"; - return false; - } - - this.Context.Tracer.RelatedEvent(EventLevel.Informational, $"{nameof(this.TryStart)}_StartedVirtualization", metadata: null); - return true; - } - - private static byte[] ToVersionIdByteArray(byte[] version) - { - byte[] bytes = new byte[VirtualizationInstance.PlaceholderIdLength]; - Buffer.BlockCopy(version, 0, bytes, 0, version.Length); - return bytes; - } - - /// - /// Gets the target of the symbolic link. - /// - /// SHA of the loose object containing the target path of the symbolic link - /// Target path of the symbolic link - private bool TryGetSymLinkTarget(string sha, out string symLinkTarget) - { - symLinkTarget = null; - - string symLinkBlobContents = null; - try - { - if (!this.GitObjects.TryCopyBlobContentStream( - sha, - CancellationToken.None, - GVFSGitObjects.RequestSource.SymLinkCreation, - (stream, blobLength) => - { - byte[] buffer = new byte[SymLinkTargetBufferSize]; - uint bufferIndex = 0; - - // TODO(#1361): Find a better solution than reading from the stream one byte at at time - int nextByte = stream.ReadByte(); - while (nextByte != -1) - { - while (bufferIndex < buffer.Length && nextByte != -1) - { - buffer[bufferIndex] = (byte)nextByte; - nextByte = stream.ReadByte(); - ++bufferIndex; - } - - if (bufferIndex < buffer.Length) - { - buffer[bufferIndex] = 0; - symLinkBlobContents = Encoding.UTF8.GetString(buffer); - } - else - { - buffer[bufferIndex - 1] = 0; - - EventMetadata metadata = this.CreateEventMetadata(); - metadata.Add(nameof(sha), sha); - metadata.Add("bufferContents", Encoding.UTF8.GetString(buffer)); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.TryGetSymLinkTarget)}: SymLink target exceeds buffer size"); - - throw new GetSymLinkTargetException("SymLink target exceeds buffer size"); - } - } - })) - { - EventMetadata metadata = this.CreateEventMetadata(); - metadata.Add(nameof(sha), sha); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.TryGetSymLinkTarget)}: TryCopyBlobContentStream failed"); - - return false; - } - } - catch (GetSymLinkTargetException e) - { - EventMetadata metadata = this.CreateEventMetadata(relativePath: null, exception: e); - metadata.Add(nameof(sha), sha); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.TryGetSymLinkTarget)}: TryCopyBlobContentStream caught GetSymLinkTargetException"); - - return false; - } - catch (DecoderFallbackException e) - { - EventMetadata metadata = this.CreateEventMetadata(relativePath: null, exception: e); - metadata.Add(nameof(sha), sha); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.TryGetSymLinkTarget)}: TryCopyBlobContentStream caught DecoderFallbackException"); - - return false; - } - - symLinkTarget = symLinkBlobContents; - - return true; - } - - private Result OnGetFileStream( - ulong commandId, - string relativePath, - byte[] providerId, - byte[] contentId, - int triggeringProcessId, - string triggeringProcessName, - IntPtr fileHandle) - { - try - { - if (contentId == null) - { - this.Context.Tracer.RelatedError($"{nameof(this.OnGetFileStream)} called with null contentId, path: " + relativePath); - return Result.EInvalidOperation; - } - - if (providerId == null) - { - this.Context.Tracer.RelatedError($"{nameof(this.OnGetFileStream)} called with null epochId, path: " + relativePath); - return Result.EInvalidOperation; - } - - string sha = GetShaFromContentId(contentId); - byte placeholderVersion = GetPlaceholderVersionFromProviderId(providerId); - - EventMetadata metadata = this.CreateEventMetadata(relativePath); - metadata.Add(nameof(triggeringProcessId), triggeringProcessId); - metadata.Add(nameof(triggeringProcessName), triggeringProcessName); - metadata.Add(nameof(sha), sha); - metadata.Add(nameof(placeholderVersion), placeholderVersion); - metadata.Add(nameof(commandId), commandId); - ITracer activity = this.Context.Tracer.StartActivity("GetFileStream", EventLevel.Verbose, Keywords.Telemetry, metadata); - - if (placeholderVersion != FileSystemVirtualizer.PlaceholderVersion) - { - activity.RelatedError(metadata, nameof(this.OnGetFileStream) + ": Unexpected placeholder version"); - activity.Dispose(); - - // TODO(#1362): Is this the correct Result to return? - return Result.EIOError; - } - - try - { - if (!this.GitObjects.TryCopyBlobContentStream( - sha, - CancellationToken.None, - GVFSGitObjects.RequestSource.FileStreamCallback, - (stream, blobLength) => - { - // TODO(#1361): Find a better solution than reading from the stream one byte at at time - byte[] buffer = new byte[4096]; - uint bufferIndex = 0; - int nextByte = stream.ReadByte(); - int bytesWritten = 0; - while (nextByte != -1) - { - while (bufferIndex < buffer.Length && nextByte != -1) - { - buffer[bufferIndex] = (byte)nextByte; - nextByte = stream.ReadByte(); - ++bufferIndex; - } - - Result result = this.virtualizationInstance.WriteFileContents( - fileHandle, - buffer, - bufferIndex); - if (result != Result.Success) - { - activity.RelatedError(metadata, $"{nameof(this.virtualizationInstance.WriteFileContents)} failed, error: " + result.ToString("X") + "(" + result.ToString("G") + ")"); - throw new GetFileStreamException(result); - } - - if (bufferIndex == buffer.Length) - { - bufferIndex = 0; - bytesWritten += buffer.Length; - } - } - bytesWritten += Convert.ToInt32(bufferIndex); - - if (bytesWritten != blobLength) - { - // If the read size does not match the expected size print an error and add the file to ModifiedPaths.dat - // This allows the user to see that something went wrong with file hydration - // Unfortunitely we must do this check *after* the file is hydrated since the header isn't corrupt for trunctated objects on mac - this.Context.Tracer.RelatedError($"Read {relativePath} to {bytesWritten}, not expected size of {blobLength}"); - this.FileSystemCallbacks.OnFailedFileHydration(relativePath); - } - })) - { - activity.RelatedError(metadata, $"{nameof(this.OnGetFileStream)}: TryCopyBlobContentStream failed"); - - // TODO(#1362): Is this the correct Result to return? - return Result.EFileNotFound; - } - } - catch (GetFileStreamException e) - { - return e.Result; - } - - this.FileSystemCallbacks.OnPlaceholderFileHydrated(triggeringProcessName); - return Result.Success; - } - catch (Exception e) - { - EventMetadata metadata = this.CreateEventMetadata(relativePath, e); - metadata.Add(nameof(triggeringProcessId), triggeringProcessId); - metadata.Add(nameof(triggeringProcessName), triggeringProcessName); - metadata.Add(nameof(commandId), commandId); - this.LogUnhandledExceptionAndExit(nameof(this.OnGetFileStream), metadata); - } - - return Result.EIOError; - } - - private void OnLogError(string errorMessage) - { - this.Context.Tracer.RelatedError($"{nameof(MacFileSystemVirtualizer)}::{nameof(this.OnLogError)}: {errorMessage}"); - } - - private void OnLogWarning(string warningMessage) - { - this.Context.Tracer.RelatedWarning($"{nameof(MacFileSystemVirtualizer)}::{nameof(this.OnLogWarning)}: {warningMessage}"); - } - - private void OnLogInfo(string infoMessage) - { - this.Context.Tracer.RelatedInfo($"{nameof(MacFileSystemVirtualizer)}::{nameof(this.OnLogInfo)}: {infoMessage}"); - } - - private void OnFileModified(string relativePath) - { - try - { - if (Virtualization.FileSystemCallbacks.IsPathInsideDotGit(relativePath)) - { - this.OnDotGitFileOrFolderChanged(relativePath); - } - } - catch (Exception e) - { - this.LogUnhandledExceptionAndExit(nameof(this.OnFileModified), this.CreateEventMetadata(relativePath, e)); - } - } - - private Result NotifyFilePreConvertToFull(string relativePath) - { - this.OnFilePreConvertToFull(relativePath); - return Result.Success; - } - - private Result OnPreDelete(string relativePath, bool isDirectory) - { - try - { - bool pathInsideDotGit = Virtualization.FileSystemCallbacks.IsPathInsideDotGit(relativePath); - if (pathInsideDotGit) - { - if (relativePath.Equals(GVFSConstants.DotGit.Index, GVFSPlatform.Instance.Constants.PathComparison)) - { - string lockedGitCommand = this.Context.Repository.GVFSLock.GetLockedGitCommand(); - if (string.IsNullOrEmpty(lockedGitCommand)) - { - EventMetadata metadata = new EventMetadata(); - metadata.Add("Area", this.EtwArea); - metadata.Add(TracingConstants.MessageKey.WarningMessage, "Blocked index delete outside the lock"); - this.Context.Tracer.RelatedEvent(EventLevel.Warning, $"{nameof(this.OnPreDelete)}_BlockedIndexDelete", metadata); - - return Result.EAccessDenied; - } - } - - this.OnDotGitFileOrFolderDeleted(relativePath); - } - else - { - this.OnWorkingDirectoryFileOrFolderDeleteNotification(relativePath, isDirectory, isPreDelete: true); - } - } - catch (Exception e) - { - EventMetadata metadata = this.CreateEventMetadata(relativePath, e); - metadata.Add("isDirectory", isDirectory); - this.LogUnhandledExceptionAndExit(nameof(this.OnPreDelete), metadata); - } - - return Result.Success; - } - - private void OnNewFileCreated(string relativePath, bool isDirectory) - { - try - { - if (!Virtualization.FileSystemCallbacks.IsPathInsideDotGit(relativePath)) - { - if (isDirectory) - { - string lockedGitCommand = this.Context.Repository.GVFSLock.GetLockedGitCommand(); - GitCommandLineParser gitCommand = new GitCommandLineParser(lockedGitCommand); - if (gitCommand.IsValidGitCommand) - { - EventMetadata metadata = this.CreateEventMetadata(relativePath); - metadata.Add(nameof(lockedGitCommand), lockedGitCommand); - metadata.Add(TracingConstants.MessageKey.InfoMessage, "Git command created new folder"); - this.Context.Tracer.RelatedEvent(EventLevel.Informational, $"{nameof(this.OnNewFileCreated)}_GitCreatedFolder", metadata); - - // Record this folder as expanded so that GitIndexProjection will re-expand the folder - // when the projection change completes. - // - // Git creates new folders when there are files that it needs to create. - // However, git will only create files that are in ModifiedPaths.dat. There could - // be other files in the projection (that were not created by git) and so VFS must re-expand the - // newly created folder to ensure that all files are written to disk. - this.FileSystemCallbacks.OnPlaceholderFolderExpanded(relativePath); - } - else - { - this.FileSystemCallbacks.OnFolderCreated(relativePath, out bool sparseFoldersUpdated); - if (sparseFoldersUpdated) - { - // When sparseFoldersUpdated is true it means the folder was previously excluded from the projection and was - // included so it needs to enumerate the directory to get and create placeholders - // for all the directory items that are now included - this.OnEnumerateDirectory(0, relativePath, -1, $"{nameof(this.OnNewFileCreated)}_FolderIncluded"); - } - } - } - else - { - this.FileSystemCallbacks.OnFileCreated(relativePath); - } - } - } - catch (Exception e) - { - EventMetadata metadata = this.CreateEventMetadata(relativePath, e); - metadata.Add("isDirectory", isDirectory); - this.LogUnhandledExceptionAndExit(nameof(this.OnNewFileCreated), metadata); - } - } - - private void OnFileRenamed(string relativeDestinationPath, bool isDirectory) - { - // ProjFS for Mac *could* be updated to provide us with relativeSourcePath as well, - // but because VFSForGit doesn't need the source path on Mac for correct behavior - // the relativeSourcePath is left out of the notification to keep the kext simple - this.OnFileRenamed( - relativeSourcePath: string.Empty, - relativeDestinationPath: relativeDestinationPath, - isDirectory: isDirectory); - } - - private Result OnEnumerateDirectory( - ulong commandId, - string relativePath, - int triggeringProcessId, - string triggeringProcessName) - { - try - { - IEnumerable projectedItems = this.FileSystemCallbacks.GitIndexProjection.GetProjectedItems( - CancellationToken.None, - blobSizesConnection: null, - folderPath: relativePath); - - return this.CreatePlaceholders(relativePath, projectedItems, triggeringProcessName); - } - catch (Exception e) - { - EventMetadata metadata = this.CreateEventMetadata(relativePath, e); - metadata.Add("commandId", commandId); - this.LogUnhandledExceptionAndExit(nameof(this.OnEnumerateDirectory), metadata); - } - - return Result.EIOError; - } - - private Result CreatePlaceholders(string directoryRelativePath, IEnumerable projectedItems, string triggeringProcessName) - { - foreach (ProjectedFileInfo fileInfo in projectedItems) - { - string childRelativePath = Path.Combine(directoryRelativePath, fileInfo.Name); - - string sha; - FileSystemResult fileSystemResult; - if (fileInfo.IsFolder) - { - sha = string.Empty; - fileSystemResult = this.WritePlaceholderDirectory(childRelativePath); - } - else - { - sha = fileInfo.Sha.ToString(); - - // Writing placeholders on Mac does not require a file size - fileSystemResult = this.WritePlaceholderFile(childRelativePath, DummyFileSize, sha); - } - - Result result = (Result)fileSystemResult.RawResult; - if (result != Result.Success) - { - EventMetadata metadata = this.CreateEventMetadata(childRelativePath); - metadata.Add("fileInfo.Name", fileInfo.Name); - metadata.Add("fileInfo.Size", fileInfo.Size); - metadata.Add("fileInfo.IsFolder", fileInfo.IsFolder); - metadata.Add(nameof(result), result.ToString()); - metadata.Add(nameof(sha), sha); - this.Context.Tracer.RelatedError(metadata, $"{nameof(this.CreatePlaceholders)}: Write placeholder failed"); - - if (result == Result.EIOError) - { - // If there is an IO error writing the placeholder then the file might already exist and it needs to - // be added to the modified paths so that git will show any differences or errors when interacting with the file - // This will happen in the include mode when the user creates a file that is already in the files that - // should be projected but we are trying to create the placeholder after it has already been created - this.FileSystemCallbacks.OnFileConvertedToFull(childRelativePath); - } - else - { - return result; - } - } - else - { - if (fileInfo.IsFolder) - { - this.FileSystemCallbacks.OnPlaceholderFolderCreated(childRelativePath, triggeringProcessName); - } - else - { - this.FileSystemCallbacks.OnPlaceholderFileCreated(childRelativePath, sha, triggeringProcessName); - } - } - } - - this.FileSystemCallbacks.OnPlaceholderFolderExpanded(directoryRelativePath); - - return Result.Success; - } - - private class GetFileStreamException : Exception - { - public GetFileStreamException(Result errorCode) - : this("GetFileStreamException exception, error: " + errorCode.ToString(), errorCode) - { - } - - public GetFileStreamException(string message, Result result) - : base(message) - { - this.Result = result; - } - - public Result Result { get; } - } - - private class GetSymLinkTargetException : Exception - { - public GetSymLinkTargetException(string message) - : base(message) - { - } - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/MacPlatform.Shared.cs b/GVFS/GVFS.Platform.Mac/MacPlatform.Shared.cs deleted file mode 100644 index c2dfcf071f..0000000000 --- a/GVFS/GVFS.Platform.Mac/MacPlatform.Shared.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.IO; -using GVFS.Common; -using GVFS.Platform.POSIX; - -namespace GVFS.Platform.Mac -{ - public partial class MacPlatform - { - public const string DotGVFSRoot = ".gvfs"; - public const string UpgradeConfirmMessage = "`sudo gvfs upgrade --confirm --no-verify`"; - - public static string GetDataRootForGVFSImplementation() - { - return Path.Combine( - Environment.GetEnvironmentVariable("HOME"), - "Library", - "Application Support", - "GVFS"); - } - - public static string GetDataRootForGVFSComponentImplementation(string componentName) - { - return Path.Combine(GetDataRootForGVFSImplementation(), componentName); - } - - public static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage) - { - return POSIXPlatform.TryGetGVFSEnlistmentRootImplementation(directory, DotGVFSRoot, out enlistmentRoot, out errorMessage); - } - - public static string GetUpgradeHighestAvailableVersionDirectoryImplementation() - { - return GetUpgradeNonProtectedDirectoryImplementation(); - } - - public static string GetUpgradeNonProtectedDirectoryImplementation() - { - return Path.Combine(GetDataRootForGVFSImplementation(), ProductUpgraderInfo.UpgradeDirectoryName); - } - - public static string GetNamedPipeNameImplementation(string enlistmentRoot) - { - return POSIXPlatform.GetNamedPipeNameImplementation(enlistmentRoot, DotGVFSRoot); - } - - public static string GetUpgradeReminderNotificationImplementation() - { - return $"A new version of VFS for Git is available. Run {UpgradeConfirmMessage} to upgrade."; - } - - private string GetUpgradeNonProtectedDataDirectory() - { - return GetUpgradeNonProtectedDirectoryImplementation(); - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/MacPlatform.cs b/GVFS/GVFS.Platform.Mac/MacPlatform.cs deleted file mode 100644 index 64030cde98..0000000000 --- a/GVFS/GVFS.Platform.Mac/MacPlatform.cs +++ /dev/null @@ -1,273 +0,0 @@ -using GVFS.Common; -using GVFS.Common.FileSystem; -using GVFS.Common.Tracing; -using GVFS.Platform.POSIX; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -using System.Xml.XPath; - -namespace GVFS.Platform.Mac -{ - public partial class MacPlatform : POSIXPlatform - { - private const string UpgradeProtectedDataDirectory = "/usr/local/vfsforgit_upgrader"; - private const string DiagnosticReportsDirectory = "/Library/Logs/DiagnosticReports"; - private const string PanicFileNamePattern = "*panic"; - private const string SystemInstallerLogPath = "/var/log/install.log"; - - public MacPlatform() : base( - underConstruction: new UnderConstructionFlags( - supportsGVFSUpgrade: true, - supportsGVFSConfig: true, - supportsNuGetEncryption: false, - supportsNuGetVerification: false)) - { - } - - public override IDiskLayoutUpgradeData DiskLayoutUpgrade { get; } = new MacDiskLayoutUpgradeData(); - public override IKernelDriver KernelDriver { get; } = new ProjFSKext(); - public override string Name { get => "macOS"; } - public override GVFSPlatformConstants Constants { get; } = new MacPlatformConstants(); - public override IPlatformFileSystem FileSystem { get; } = new MacFileSystem(); - - public override string GVFSConfigPath - { - get - { - return Path.Combine(this.Constants.GVFSBinDirectoryPath, LocalGVFSConfig.FileName); - } - } - - /// - /// On the Mac VFSForGit installer messages get captured in the system - /// wide installer log file. There is no customized log file specific - /// to VFSForGit installer. - /// - public override bool SupportsSystemInstallLog - { - get - { - return true; - } - } - - public override string GetOSVersionInformation() - { - ProcessResult result = ProcessHelper.Run("sw_vers", args: string.Empty, redirectOutput: true); - return string.IsNullOrWhiteSpace(result.Output) ? result.Errors : result.Output; - } - - public override string GetSecureDataRootForGVFS() - { - // On the Mac, unlike Windows, there is no separate secure data root directory. - return MacPlatform.GetDataRootForGVFSImplementation(); - } - - public override string GetSecureDataRootForGVFSComponent(string componentName) - { - // On the Mac, unlike Windows, there is no separate secure data root directory. - return MacPlatform.GetDataRootForGVFSComponentImplementation(componentName); - } - - public override string GetCommonAppDataRootForGVFS() - { - return this.GetSecureDataRootForGVFS(); - } - - public override string GetLogsDirectoryForGVFSComponent(string componentName) - { - return Path.Combine(this.GetCommonAppDataRootForGVFS(), componentName); - } - - public override bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage) - { - return MacPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage); - } - - public override string GetNamedPipeName(string enlistmentRoot) - { - return MacPlatform.GetNamedPipeNameImplementation(enlistmentRoot); - } - - public override FileBasedLock CreateFileBasedLock( - PhysicalFileSystem fileSystem, - ITracer tracer, - string lockPath) - { - return new MacFileBasedLock(fileSystem, tracer, lockPath); - } - - public override string GetUpgradeProtectedDataDirectory() - { - return UpgradeProtectedDataDirectory; - } - - public override string GetUpgradeHighestAvailableVersionDirectory() - { - return GetUpgradeHighestAvailableVersionDirectoryImplementation(); - } - - /// - /// This is the directory in which the upgradelogs directory should go. - /// There can be multiple logs directories, so here we return the containing - /// directory. - /// - public override string GetUpgradeLogDirectoryParentDirectory() - { - return this.GetUpgradeNonProtectedDataDirectory(); - } - - public override string GetSystemInstallerLogPath() - { - return SystemInstallerLogPath; - } - - public override Dictionary GetPhysicalDiskInfo(string path, bool sizeStatsOnly) - { - // DiskUtil will return disk statistics in xml format - ProcessResult processResult = ProcessHelper.Run("diskutil", "info -plist /", true); - Dictionary result = new Dictionary(); - if (string.IsNullOrEmpty(processResult.Output)) - { - result.Add("DiskUtilError", processResult.Errors); - return result; - } - - try - { - // Parse the XML looking for FilesystemType - XDocument xmlDoc = XDocument.Parse(processResult.Output); - XElement filesystemTypeValue = xmlDoc.XPathSelectElement("plist/dict/key[text()=\"FilesystemType\"]")?.NextNode as XElement; - result.Add("FileSystemType", filesystemTypeValue != null ? filesystemTypeValue.Value: "Not Found"); - } - catch (XmlException ex) - { - result.Add("DiskUtilError", ex.ToString()); - } - - return result; - } - - public override ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformInteractions( - PhysicalFileSystem fileSystem, - ITracer tracer) - { - return new MacProductUpgraderPlatformStrategy(fileSystem, tracer); - } - - public override void IsServiceInstalledAndRunning(string name, out bool installed, out bool running) - { - string currentUser = this.GetCurrentUser(); - MacDaemonController macDaemonController = new MacDaemonController(new ProcessRunnerImpl()); - List daemons; - if (!macDaemonController.TryGetDaemons(currentUser, out daemons, out string error)) - { - installed = false; - running = false; - } - - MacDaemonController.DaemonInfo gvfsService = daemons.FirstOrDefault(sc => string.Equals(sc.Name, "org.vfsforgit.service")); - installed = gvfsService != null; - running = installed && gvfsService.IsRunning; - } - - public override bool TryCopyPanicLogs(string copyToDir, out string error) - { - error = null; - try - { - if (!Directory.Exists(DiagnosticReportsDirectory)) - { - return true; - } - - string copyToPanicDir = Path.Combine(copyToDir, ProjFSKext.DriverLogDirectory, "panic_logs"); - Directory.CreateDirectory(copyToPanicDir); - - foreach (string filePath in Directory.GetFiles(DiagnosticReportsDirectory, PanicFileNamePattern)) - { - try - { - // We only include panic logs caused by our kext - // Panics caused by our kext will be in the form DriverName(Version) - // We match the minimal requirement here - if (File.ReadAllText(filePath).Contains(ProjFSKext.DriverName + "(")) - { - File.Copy(filePath, Path.Combine(copyToPanicDir, Path.GetFileName(filePath))); - } - } - catch (Exception ex) - { - error = error == null ? string.Empty : error + "\n"; - error += $"{nameof(this.TryCopyPanicLogs)}: Failed to handle log {filePath}: {ex.ToString()}"; - } - } - } - catch (Exception ex) - { - error = error == null ? string.Empty : error + "\n"; - error += $"{nameof(this.TryCopyPanicLogs)}: Failed to copy panic logs: {ex.ToString()}"; - return false; - } - - return error == null; - } - - public class MacPlatformConstants : POSIXPlatformConstants - { - public override string InstallerExtension - { - get { return ".dmg"; } - } - - public override string WorkingDirectoryBackingRootPath - { - get { return GVFSConstants.WorkingDirectoryRootName; } - } - - public override string DotGVFSRoot - { - get { return MacPlatform.DotGVFSRoot; } - } - - public override string GVFSBinDirectoryPath - { - get { return Path.Combine("/usr", "local", this.GVFSBinDirectoryName); } - } - - public override string GVFSBinDirectoryName - { - get { return "vfsforgit"; } - } - - // Documented here (in the addressing section): https://www.unix.com/man-page/mojave/4/unix/ - public override int MaxPipePathLength => 104; - - public override string UpgradeInstallAdviceMessage - { - get { return $"When ready, run {this.UpgradeConfirmCommandMessage} to upgrade."; } - } - - public override string UpgradeConfirmCommandMessage - { - get { return UpgradeConfirmMessage; } - } - - public override string StartServiceCommandMessage - { - get { return "`launchctl load /Library/LaunchAgents/org.vfsforgit.service.plist`"; } - } - - public override string RunUpdateMessage - { - get { return $"Run {UpgradeConfirmMessage}."; } - } - - public override bool CaseSensitiveFileSystem => false; - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/MacProductUpgraderPlatformStrategy.cs b/GVFS/GVFS.Platform.Mac/MacProductUpgraderPlatformStrategy.cs deleted file mode 100644 index 3237caad82..0000000000 --- a/GVFS/GVFS.Platform.Mac/MacProductUpgraderPlatformStrategy.cs +++ /dev/null @@ -1,62 +0,0 @@ -using GVFS.Common; -using GVFS.Common.FileSystem; -using GVFS.Common.Tracing; -using System; -using System.IO; - -namespace GVFS.Platform.Mac -{ - public class MacProductUpgraderPlatformStrategy : ProductUpgraderPlatformStrategy - { - public MacProductUpgraderPlatformStrategy(PhysicalFileSystem fileSystem, ITracer tracer) - : base(fileSystem, tracer) - { - } - - public override bool TryPrepareLogDirectory(out string error) - { - error = null; - return true; - } - - public override bool TryPrepareApplicationDirectory(out string error) - { - string upgradeApplicationDirectory = ProductUpgraderInfo.GetUpgradeApplicationDirectory(); - - Exception deleteDirectoryException; - if (this.FileSystem.DirectoryExists(upgradeApplicationDirectory) && - !this.FileSystem.TryDeleteDirectory(upgradeApplicationDirectory, out deleteDirectoryException)) - { - error = $"Failed to delete {upgradeApplicationDirectory} - {deleteDirectoryException.Message}"; - - this.TraceException(deleteDirectoryException, nameof(this.TryPrepareApplicationDirectory), $"Error deleting {upgradeApplicationDirectory}."); - return false; - } - - this.FileSystem.CreateDirectory(upgradeApplicationDirectory); - - error = null; - return true; - } - - public override bool TryPrepareDownloadDirectory(out string error) - { - string directory = ProductUpgraderInfo.GetAssetDownloadsPath(); - - Exception deleteDirectoryException; - if (this.FileSystem.DirectoryExists(directory) && - !this.FileSystem.TryDeleteDirectory(directory, out deleteDirectoryException)) - { - error = $"Failed to delete {directory} - {deleteDirectoryException.Message}"; - - this.TraceException(deleteDirectoryException, nameof(this.TryPrepareDownloadDirectory), $"Error deleting {directory}."); - return false; - } - - this.FileSystem.CreateDirectory(directory); - - error = null; - return true; - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/ProjFSKext.cs b/GVFS/GVFS.Platform.Mac/ProjFSKext.cs deleted file mode 100644 index debaf35e6f..0000000000 --- a/GVFS/GVFS.Platform.Mac/ProjFSKext.cs +++ /dev/null @@ -1,137 +0,0 @@ -using GVFS.Common; -using GVFS.Common.FileSystem; -using GVFS.Common.Tracing; -using PrjFSLib.Mac; -using System; -using System.IO; -using System.Linq; - -namespace GVFS.Platform.Mac -{ - public class ProjFSKext : IKernelDriver - { - public const string DriverName = "org.vfsforgit.PrjFSKext"; - public const string DriverLogDirectory = "PrjFSKext"; - - private const int LoadKext_ExitCode_Success = 0; - - // This exit code was found in the following article - // https://developer.apple.com/library/archive/technotes/tn2459/_index.html - private const int LoadKext_ExitCode_NotApproved = 27; - - public bool EnumerationExpandsDirectories { get; } = true; - public bool EmptyPlaceholdersRequireFileSize { get; } = false; - - public string LogsFolderPath - { - get - { - return Path.Combine(System.IO.Path.GetTempPath(), DriverLogDirectory); - } - } - - public bool IsGVFSUpgradeSupported() - { - return true; - } - - public bool IsSupported(string normalizedEnlistmentRootPath, out string warning, out string error) - { - warning = null; - error = null; - - string pathRoot = Path.GetPathRoot(normalizedEnlistmentRootPath); - DriveInfo rootDriveInfo = DriveInfo.GetDrives().FirstOrDefault(x => x.Name == pathRoot); - if (rootDriveInfo == null) - { - warning = $"Unable to ensure that '{normalizedEnlistmentRootPath}' is an APFS or HFS+ volume."; - } - else if (!string.Equals(rootDriveInfo.DriveFormat, "APFS", StringComparison.OrdinalIgnoreCase) && - !string.Equals(rootDriveInfo.DriveFormat, "HFS", StringComparison.OrdinalIgnoreCase)) - { - error = $"Error: Currently only APFS and HFS+ volumes are supported. Ensure repo is located into an APFS or HFS+ volume."; - return false; - } - - return true; - } - - public bool TryFlushLogs(out string error) - { - Directory.CreateDirectory(this.LogsFolderPath); - ProcessResult logShowOutput = ProcessHelper.Run("log", args: "show --predicate \"subsystem contains \'org.vfsforgit\'\" --info", redirectOutput: true); - File.WriteAllText(Path.Combine(this.LogsFolderPath, "PrjFSKext.log"), logShowOutput.Output); - error = string.Empty; - - return true; - } - - public bool IsReady(JsonTracer tracer, string enlistmentRoot, TextWriter output, out string error) - { - error = null; - return - this.IsKextLoaded() || - this.TryLoad(tracer, output, out error); - } - - public bool TryPrepareFolderForCallbacks(string folderPath, out string error, out Exception exception) - { - exception = null; - error = string.Empty; - Result result = VirtualizationInstance.ConvertDirectoryToVirtualizationRoot(folderPath); - if (result != Result.Success) - { - error = "Failed to prepare \"" + folderPath + "\" for callbacks, error: " + result.ToString("F"); - return false; - } - - return true; - } - - public bool RegisterForOfflineIO() - { - return PrjFSLib.Mac.Managed.OfflineIO.RegisterForOfflineIO(); - } - - public bool UnregisterForOfflineIO() - { - return PrjFSLib.Mac.Managed.OfflineIO.UnregisterForOfflineIO(); - } - - private bool TryLoad(ITracer tracer, TextWriter output, out string errorMessage) - { - output?.WriteLine("Driver not loaded. Attempting to load. You may be prompted for sudo password..."); - EventMetadata metadata = new EventMetadata(); - ProcessResult loadKext = ProcessHelper.Run("sudo", "/sbin/kextload -b " + DriverName); - if (loadKext.ExitCode == LoadKext_ExitCode_Success) - { - tracer.RelatedWarning(metadata, $"{DriverName} was successfully loaded but should have been autoloaded.", Keywords.Telemetry); - errorMessage = null; - return true; - } - else if (loadKext.ExitCode == LoadKext_ExitCode_NotApproved) - { - tracer.RelatedError("Kext unable to load. Not approved by the user"); - errorMessage = DriverName + @" was unable to load. Please check and make sure you have allowed the extension in -System Preferences -> Security & Privacy"; - } - else - { - metadata.Add("ExitCode", loadKext.ExitCode); - metadata.Add("Output", loadKext.Output); - metadata.Add("Errors", loadKext.Errors); - tracer.RelatedError(metadata, "Failed to load kext"); - - errorMessage = DriverName + " is not loaded. Make sure the kext is loaded and try again."; - } - - return false; - } - - private bool IsKextLoaded() - { - ProcessResult loadedKexts = ProcessHelper.Run("kextstat", args: "-b " + DriverName, redirectOutput: true); - return loadedKexts.Output.Contains(DriverName); - } - } -} diff --git a/GVFS/GVFS.Platform.Mac/Properties/AssemblyInfo.cs b/GVFS/GVFS.Platform.Mac/Properties/AssemblyInfo.cs deleted file mode 100644 index a71456402b..0000000000 --- a/GVFS/GVFS.Platform.Mac/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("GVFS.Platform.Mac")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("GVFS.Platform.Mac")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2019")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4ce404e7-d3fc-471c-993c-64615861ea63")] diff --git a/GVFS/GVFS.Service/GVFS.Service.Mac.csproj b/GVFS/GVFS.Service/GVFS.Service.Mac.csproj deleted file mode 100644 index 4f34b2c450..0000000000 --- a/GVFS/GVFS.Service/GVFS.Service.Mac.csproj +++ /dev/null @@ -1,51 +0,0 @@ - - - - Exe - GVFS.Service - GVFS.Service - - netcoreapp2.1; netstandard2.0 - x64 - osx-x64 - false - true - - - - $(GVFSVersion) - - - $(GVFSVersion) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - all - - - diff --git a/GVFS/GVFS.UnitTests.Windows/GVFS.UnitTests.Windows.csproj b/GVFS/GVFS.UnitTests.Windows/GVFS.UnitTests.Windows.csproj index 361446da6f..2150248d73 100644 --- a/GVFS/GVFS.UnitTests.Windows/GVFS.UnitTests.Windows.csproj +++ b/GVFS/GVFS.UnitTests.Windows/GVFS.UnitTests.Windows.csproj @@ -141,10 +141,6 @@ - - {fac6efc5-a890-4cb2-8c80-6358e358c637} - PrjFSLib.Mac.Managed - {374bf1e5-0b2d-4d4a-bd5e-4212299def09} GVFS.Common @@ -153,10 +149,6 @@ {1118b427-7063-422f-83b9-5023c8ec5a7a} GVFS.GVFlt - - {1dac3da6-3d21-4917-b9a8-d60c8712252a} - GVFS.Platform.Mac - {15fae44c-0d21-4312-9fd3-28f05a5ab7a6} GVFS.Platform.POSIX @@ -169,9 +161,9 @@ {93b403fd-dafb-46c5-9636-b122792a548a} GVFS.Service.UI - - {03769a07-f216-456b-886b-e07caf6c5e81} - GVFS.Service.Mac + + {b8c1dfba-cafd-4f7e-a1a3-e11907b5467b} + GVFS.Service.Windows {72701bc3-5da9-4c7a-bf10-9e98c9fc8eac} @@ -194,8 +186,326 @@ - - NetCore\%(RecursiveDir)\%(Filename)%(Extension) + + NetCore\Category\\CategoryConstants.cs + + + NetCore\CommandLine\\HooksInstallerTests.cs + + + NetCore\Common\\AzDevOpsOrgFromNuGetFeedTests.cs + + + NetCore\Common\\BackgroundTaskQueueTests.cs + + + NetCore\Common\\CacheServerResolverTests.cs + + + NetCore\Common\Database\\GVFSDatabaseTests.cs + + + NetCore\Common\Database\\PlaceholderTableTests.cs + + + NetCore\Common\Database\\SparseTableTests.cs + + + NetCore\Common\Database\\TableTests.cs + + + NetCore\Common\\EpochConverterTests.cs + + + NetCore\Common\\FileBasedDictionaryTests.cs + + + NetCore\Common\\GitCommandLineParserTests.cs + + + NetCore\Common\\GitConfigHelperTests.cs + + + NetCore\Common\\GitObjectsTests.cs + + + NetCore\Common\\GitPathConverterTests.cs + + + NetCore\Common\\GitStatusCacheTests.cs + + + NetCore\Common\\GitVersionTests.cs + + + NetCore\Common\Git\\GitSslTests.cs + + + NetCore\Common\Git\\Sha1IdTests.cs + + + NetCore\Common\\GVFSEnlistmentHealthTests.cs + + + NetCore\Common\\GVFSEnlistmentTests.cs + + + NetCore\Common\\GVFSLockTests.cs + + + NetCore\Common\\InstallManifestTests.cs + + + NetCore\Common\\JsonTracerTests.cs + + + NetCore\Common\\LegacyPlaceholderDatabaseTests.cs + + + NetCore\Common\\LibGit2RepoInvokerTests.cs + + + NetCore\Common\\ModifiedPathsDatabaseTests.cs + + + NetCore\Common\\NamedPipeStreamReaderWriterTests.cs + + + NetCore\Common\\NamedPipeTests.cs + + + NetCore\Common\NuGetUpgrade\\NuGetUpgraderTests.cs + + + NetCore\Common\NuGetUpgrade\\OrgNuGetUpgraderTests.cs + + + NetCore\Common\\OrgInfoApiClientTests.cs + + + NetCore\Common\\PathsTests.cs + + + NetCore\Common\\PhysicalFileSystemDeleteTests.cs + + + NetCore\Common\\ProductUpgraderInfoTests.cs + + + NetCore\Common\\RefLogEntryTests.cs + + + NetCore\Common\\RetryBackoffTests.cs + + + NetCore\Common\\RetryConfigTests.cs + + + NetCore\Common\\RetryWrapperTests.cs + + + NetCore\Common\\SHA1UtilTests.cs + + + NetCore\Common\\TryCreateProductUpgraderTests.cs + + + NetCore\Git\\GitAuthenticationTests.cs + + + NetCore\Git\\GitObjectsTests.cs + + + NetCore\Git\\GitProcessTests.cs + + + NetCore\Git\\GVFSGitObjectsTests.cs + + + NetCore\Maintenance\\GitMaintenanceQueueTests.cs + + + NetCore\Maintenance\\GitMaintenanceStepTests.cs + + + NetCore\Maintenance\\LooseObjectStepTests.cs + + + NetCore\Maintenance\\PackfileMaintenanceStepTests.cs + + + NetCore\Maintenance\\PostFetchStepTests.cs + + + NetCore\Mock\Common\\MockFileBasedLock.cs + + + NetCore\Mock\Common\\MockGitStatusCache.cs + + + NetCore\Mock\Common\\MockGVFSEnlistment.cs + + + NetCore\Mock\Common\\MockLocalGVFSConfig.cs + + + NetCore\Mock\Common\\MockLocalGVFSConfigBuilder.cs + + + NetCore\Mock\Common\\MockPhysicalGitObjects.cs + + + NetCore\Mock\Common\\MockPlatform.cs + + + NetCore\Mock\Common\\MockProductUpgraderPlatformStrategy.cs + + + NetCore\Mock\Common\\MockTracer.cs + + + NetCore\Mock\Common\Tracing\\MockListener.cs + + + NetCore\Mock\FileSystem\\ConfigurableFileSystem.cs + + + NetCore\Mock\FileSystem\\MockDirectory.cs + + + NetCore\Mock\FileSystem\\MockFile.cs + + + NetCore\Mock\FileSystem\\MockFileSystem.cs + + + NetCore\Mock\FileSystem\\MockFileSystemCallbacks.cs + + + NetCore\Mock\FileSystem\\MockFileSystemWithCallbacks.cs + + + NetCore\Mock\FileSystem\\MockPlatformFileSystem.cs + + + NetCore\Mock\Git\\MockBatchHttpGitObjects.cs + + + NetCore\Mock\Git\\MockGitInstallation.cs + + + NetCore\Mock\Git\\MockGitProcess.cs + + + NetCore\Mock\Git\\MockGitRepo.cs + + + NetCore\Mock\Git\\MockGVFSGitObjects.cs + + + NetCore\Mock\Git\\MockHttpGitObjects.cs + + + NetCore\Mock\Git\\MockLibGit2Repo.cs + + + NetCore\Mock\\MockCacheServerInfo.cs + + + NetCore\Mock\\MockGitHubUpgrader.cs + + + NetCore\Mock\\MockInstallerPreRunChecker.cs + + + NetCore\Mock\\MockTextWriter.cs + + + NetCore\Mock\\ReusableMemoryStream.cs + + + NetCore\Mock\Virtualization\Background\\MockBackgroundTaskManager.cs + + + NetCore\Mock\Virtualization\BlobSize\\MockBlobSizesDatabase.cs + + + NetCore\Mock\Virtualization\FileSystem\\MockFileSystemVirtualizer.cs + + + NetCore\Mock\Virtualization\Projection\\MockGitIndexProjection.cs + + + NetCore\Prefetch\\BatchObjectDownloadStageTests.cs + + + NetCore\Prefetch\\BlobPrefetcherTests.cs + + + NetCore\Prefetch\\DiffHelperTests.cs + + + NetCore\Prefetch\\DiffTreeResultTests.cs + + + NetCore\Prefetch\\PrefetchPacksDeserializerTests.cs + + + NetCore\Prefetch\\PrefetchTracingTests.cs + + + NetCore\\Program.cs + + + NetCore\Service\\RepoRegistryTests.cs + + + NetCore\\Setup.cs + + + NetCore\Tracing\\EventListenerTests.cs + + + NetCore\Tracing\\QueuedPipeStringWriterTests.cs + + + NetCore\Tracing\\TelemetryDaemonEventListenerTests.cs + + + NetCore\Upgrader\\ProductUpgraderTests.cs + + + NetCore\Upgrader\\UpgradeOrchestratorTests.cs + + + NetCore\Upgrader\\UpgradeOrchestratorWithGitHubUpgraderTests.cs + + + NetCore\Upgrader\\UpgradeTests.cs + + + NetCore\Virtualization\\FileSystemCallbacksTests.cs + + + NetCore\Virtualization\Projection\\GitIndexEntryTests.cs + + + NetCore\Virtualization\Projection\\LazyUTF8StringTests.cs + + + NetCore\Virtualization\Projection\\ObjectPoolTests.cs + + + NetCore\Virtualization\Projection\\SortedFolderEntriesTests.cs + + + NetCore\Virtual\\CommonRepoSetup.cs + + + NetCore\Virtual\\FileSystemVirtualizerTester.cs + + + NetCore\Virtual\\TestsWithCommonRepo.cs Data\%(RecursiveDir)\%(Filename)%(Extension) @@ -230,4 +540,4 @@ --> - + \ No newline at end of file diff --git a/GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj b/GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj index 786009ac60..8013af848b 100644 --- a/GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj +++ b/GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj @@ -1,4 +1,4 @@ - + Exe @@ -51,10 +51,9 @@ - - - + + diff --git a/GVFS/GVFS.UnitTests/Mock/Mac/MacFileSystemVirtualizerTester.cs b/GVFS/GVFS.UnitTests/Mock/Mac/MacFileSystemVirtualizerTester.cs deleted file mode 100644 index 6877ae0dcd..0000000000 --- a/GVFS/GVFS.UnitTests/Mock/Mac/MacFileSystemVirtualizerTester.cs +++ /dev/null @@ -1,68 +0,0 @@ -using GVFS.Platform.Mac; -using GVFS.Tests.Should; -using GVFS.UnitTests.Virtual; -using GVFS.Virtualization.FileSystem; -using PrjFSLib.Mac; -using System; -using System.IO; - -namespace GVFS.UnitTests.Mock.Mac -{ - public class MacFileSystemVirtualizerTester : FileSystemVirtualizerTester - { - public MacFileSystemVirtualizerTester(CommonRepoSetup repo) - : base(repo) - { - } - - public MacFileSystemVirtualizerTester(CommonRepoSetup repo, string[] projectedFiles) - : base(repo, projectedFiles) - { - } - - public MockVirtualizationInstance MockVirtualization { get; private set; } - public MacFileSystemVirtualizer MacVirtualizer { get; private set; } - - public void InvokeOnGetFileStream(Result expectedResult = Result.Pending, byte[] providerId = null) - { - if (providerId == null) - { - providerId = MacFileSystemVirtualizer.PlaceholderVersionId; - } - - this.MockVirtualization.OnGetFileStream( - commandId: 1, - relativePath: "test.txt", - providerId: MacFileSystemVirtualizer.PlaceholderVersionId, - contentId: CommonRepoSetup.DefaultContentId, - triggeringProcessId: 2, - triggeringProcessName: "UnitTest", - fileHandle: IntPtr.Zero).ShouldEqual(expectedResult); - } - - public void InvokeUpdatePlaceholderIfNeeded(string fileName, FileSystemResult expectedResult, UpdateFailureCause expectedFailureCause) - { - UpdateFailureReason failureReason = UpdateFailureReason.NoFailure; - this.MacVirtualizer.UpdatePlaceholderIfNeeded( - fileName, - DateTime.Now, - DateTime.Now, - DateTime.Now, - DateTime.Now, - 0, - 15, - string.Empty, - UpdatePlaceholderType.AllowReadOnly, - out failureReason) - .ShouldEqual(expectedResult); - failureReason.ShouldEqual((UpdateFailureReason)expectedFailureCause); - } - - protected override FileSystemVirtualizer CreateVirtualizer(CommonRepoSetup repo) - { - this.MockVirtualization = new MockVirtualizationInstance(); - this.MacVirtualizer = new MacFileSystemVirtualizer(repo.Context, repo.GitObjects, this.MockVirtualization); - return this.MacVirtualizer; - } - } -} diff --git a/GVFS/GVFS.UnitTests/Mock/Mac/MockVirtualizationInstance.cs b/GVFS/GVFS.UnitTests/Mock/Mac/MockVirtualizationInstance.cs deleted file mode 100644 index f53aca9c74..0000000000 --- a/GVFS/GVFS.UnitTests/Mock/Mac/MockVirtualizationInstance.cs +++ /dev/null @@ -1,152 +0,0 @@ -using GVFS.Common; -using GVFS.Tests.Should; -using PrjFSLib.Mac; -using System; -using System.Collections.Concurrent; -using System.Threading; - -namespace GVFS.UnitTests.Mock.Mac -{ - public class MockVirtualizationInstance : VirtualizationInstance, IDisposable - { - private AutoResetEvent commandCompleted; - - public MockVirtualizationInstance() - { - this.commandCompleted = new AutoResetEvent(false); - this.CreatedPlaceholders = new ConcurrentDictionary(); - this.UpdatedPlaceholders = new ConcurrentDictionary(); - this.CreatedSymLinks = new ConcurrentHashSet(); - this.WriteFileReturnResult = Result.Success; - } - - public Result CompletionResult { get; set; } - public uint BytesWritten { get; private set; } - public Result WriteFileReturnResult { get; set; } - public Result UpdatePlaceholderIfNeededResult { get; set; } - public UpdateFailureCause UpdatePlaceholderIfNeededFailureCause { get; set; } - public Result DeleteFileResult { get; set; } - public UpdateFailureCause DeleteFileUpdateFailureCause { get; set; } - - public ConcurrentDictionary CreatedPlaceholders { get; private set; } - public ConcurrentDictionary UpdatedPlaceholders { get; private set; } - - public ConcurrentHashSet CreatedSymLinks { get; } - - public override EnumerateDirectoryCallback OnEnumerateDirectory { get; set; } - public override GetFileStreamCallback OnGetFileStream { get; set; } - - public override Result StartVirtualizationInstance( - string virtualizationRootFullPath, - uint poolThreadCount) - { - poolThreadCount.ShouldBeAtLeast(1U, "poolThreadCount must be greater than 0"); - return Result.Success; - } - - public override Result StopVirtualizationInstance() - { - return Result.Success; - } - - public override Result WriteFileContents( - IntPtr fileHandle, - byte[] bytes, - uint byteCount) - { - this.BytesWritten = byteCount; - return this.WriteFileReturnResult; - } - - public override Result DeleteFile( - string relativePath, - UpdateType updateFlags, - out UpdateFailureCause failureCause) - { - failureCause = this.DeleteFileUpdateFailureCause; - return this.DeleteFileResult; - } - - public override Result WritePlaceholderDirectory( - string relativePath) - { - throw new NotImplementedException(); - } - - public override Result WritePlaceholderFile( - string relativePath, - byte[] providerId, - byte[] contentId, - ushort fileMode) - { - this.CreatedPlaceholders.TryAdd(relativePath, fileMode); - return Result.Success; - } - - public override Result WriteSymLink( - string relativePath, - string symLinkTarget) - { - this.CreatedSymLinks.Add(relativePath); - return Result.Success; - } - - public override Result UpdatePlaceholderIfNeeded( - string relativePath, - byte[] providerId, - byte[] contentId, - ushort fileMode, - UpdateType updateFlags, - out UpdateFailureCause failureCause) - { - failureCause = this.UpdatePlaceholderIfNeededFailureCause; - if (failureCause == UpdateFailureCause.NoFailure) - { - this.UpdatedPlaceholders[relativePath] = fileMode; - } - - return this.UpdatePlaceholderIfNeededResult; - } - - public override Result ReplacePlaceholderFileWithSymLink( - string relativePath, - string symLinkTarget, - UpdateType updateFlags, - out UpdateFailureCause failureCause) - { - this.CreatedSymLinks.Add(relativePath); - failureCause = this.UpdatePlaceholderIfNeededFailureCause; - return this.UpdatePlaceholderIfNeededResult; - } - - public override Result CompleteCommand( - ulong commandId, - Result result) - { - this.CompletionResult = result; - this.commandCompleted.Set(); - return Result.Success; - } - - public Result WaitForCompletionStatus() - { - this.commandCompleted.WaitOne(); - return this.CompletionResult; - } - - public override Result ConvertDirectoryToPlaceholder( - string relativeDirectoryPath) - { - throw new NotImplementedException(); - } - - public void Dispose() - { - if (this.commandCompleted != null) - { - this.commandCompleted.Dispose(); - this.commandCompleted = null; - } - } - } -} diff --git a/GVFS/GVFS.UnitTests/Platform.Mac/MacDaemonControllerTests.cs b/GVFS/GVFS.UnitTests/Platform.Mac/MacDaemonControllerTests.cs deleted file mode 100644 index d8a43e2780..0000000000 --- a/GVFS/GVFS.UnitTests/Platform.Mac/MacDaemonControllerTests.cs +++ /dev/null @@ -1,41 +0,0 @@ -using GVFS.Common; -using GVFS.Platform.Mac; -using GVFS.Tests.Should; -using Moq; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace GVFS.UnitTests.Platform.Mac -{ - [TestFixture] - public class MacServiceProcessTests - { - [TestCase] - public void CanGetServices() - { - Mock processHelperMock = new Mock(MockBehavior.Strict); - - StringBuilder sb = new StringBuilder(); - sb.AppendLine("PID\tStatus\tLabel"); - sb.AppendLine("1\t0\tcom.apple.process1"); - sb.AppendLine("2\t0\tcom.apple.process2"); - sb.AppendLine("3\t0\tcom.apple.process3"); - sb.AppendLine("-\t0\tcom.apple.process4"); - - ProcessResult processResult = new ProcessResult(sb.ToString(), string.Empty, 0); - - processHelperMock.Setup(m => m.Run("/bin/launchctl", "asuser 521 /bin/launchctl list", true)).Returns(processResult); - - MacDaemonController daemonController = new MacDaemonController(processHelperMock.Object); - bool success = daemonController.TryGetDaemons("521", out List daemons, out string error); - - success.ShouldBeTrue(); - daemons.ShouldNotBeNull(); - daemons.Count.ShouldEqual(4); - processHelperMock.VerifyAll(); - } - } -} diff --git a/GVFS/GVFS.UnitTests/Platform.Mac/MacFileSystemVirtualizerTests.cs b/GVFS/GVFS.UnitTests/Platform.Mac/MacFileSystemVirtualizerTests.cs deleted file mode 100644 index d2bdcb8168..0000000000 --- a/GVFS/GVFS.UnitTests/Platform.Mac/MacFileSystemVirtualizerTests.cs +++ /dev/null @@ -1,390 +0,0 @@ -using GVFS.Common; -using GVFS.Platform.Mac; -using GVFS.Tests.Should; -using GVFS.UnitTests.Category; -using GVFS.UnitTests.Mock.Git; -using GVFS.UnitTests.Mock.Mac; -using GVFS.UnitTests.Virtual; -using GVFS.Virtualization.Background; -using GVFS.Virtualization.FileSystem; -using GVFS.Virtualization.Projection; -using NUnit.Framework; -using PrjFSLib.Mac; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace GVFS.UnitTests.Platform.Mac -{ - [TestFixture] - public class MacFileSystemVirtualizerTests : TestsWithCommonRepo - { - private static readonly Dictionary MappedResults = new Dictionary() - { - { Result.Success, FSResult.Ok }, - { Result.EFileNotFound, FSResult.FileOrPathNotFound }, - { Result.EPathNotFound, FSResult.FileOrPathNotFound }, - { Result.EDirectoryNotEmpty, FSResult.DirectoryNotEmpty }, - { Result.EVirtualizationInvalidOperation, FSResult.VirtualizationInvalidOperation }, - }; - - [TestCase] - public void ResultToFSResultMapsHResults() - { - foreach (Result result in Enum.GetValues(typeof(Result))) - { - if (MappedResults.ContainsKey(result)) - { - MacFileSystemVirtualizer.ResultToFSResult(result).ShouldEqual(MappedResults[result]); - } - else - { - MacFileSystemVirtualizer.ResultToFSResult(result).ShouldEqual(FSResult.IOError); - } - } - } - - [TestCase] - public void DeleteFile() - { - using (MockVirtualizationInstance mockVirtualization = new MockVirtualizationInstance()) - using (MacFileSystemVirtualizer virtualizer = new MacFileSystemVirtualizer(this.Repo.Context, this.Repo.GitObjects, mockVirtualization)) - { - const string DeleteTestFileName = "deleteMe.txt"; - UpdateFailureReason failureReason = UpdateFailureReason.NoFailure; - - mockVirtualization.DeleteFileResult = Result.Success; - mockVirtualization.DeleteFileUpdateFailureCause = UpdateFailureCause.NoFailure; - virtualizer - .DeleteFile(DeleteTestFileName, UpdatePlaceholderType.AllowReadOnly, out failureReason) - .ShouldEqual(new FileSystemResult(FSResult.Ok, (int)mockVirtualization.DeleteFileResult)); - failureReason.ShouldEqual((UpdateFailureReason)mockVirtualization.DeleteFileUpdateFailureCause); - - mockVirtualization.DeleteFileResult = Result.EFileNotFound; - mockVirtualization.DeleteFileUpdateFailureCause = UpdateFailureCause.NoFailure; - virtualizer - .DeleteFile(DeleteTestFileName, UpdatePlaceholderType.AllowReadOnly, out failureReason) - .ShouldEqual(new FileSystemResult(FSResult.FileOrPathNotFound, (int)mockVirtualization.DeleteFileResult)); - failureReason.ShouldEqual((UpdateFailureReason)mockVirtualization.DeleteFileUpdateFailureCause); - - // TODO: What will the result be when the UpdateFailureCause is DirtyData - mockVirtualization.DeleteFileResult = Result.EInvalidOperation; - - // TODO: The result should probably be VirtualizationInvalidOperation but for now it's IOError - mockVirtualization.DeleteFileUpdateFailureCause = UpdateFailureCause.DirtyData; - virtualizer - .DeleteFile(DeleteTestFileName, UpdatePlaceholderType.AllowReadOnly, out failureReason) - .ShouldEqual(new FileSystemResult(FSResult.IOError, (int)mockVirtualization.DeleteFileResult)); - failureReason.ShouldEqual((UpdateFailureReason)mockVirtualization.DeleteFileUpdateFailureCause); - } - } - - [TestCase] - public void UpdatePlaceholderIfNeeded() - { - const string UpdatePlaceholderFileName = "testUpdatePlaceholder.txt"; - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo, new[] { UpdatePlaceholderFileName })) - { - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - UpdatePlaceholderFileName, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.Regular, GitIndexProjection.FileMode644)); - - tester.MockVirtualization.UpdatePlaceholderIfNeededResult = Result.Success; - tester.MockVirtualization.UpdatePlaceholderIfNeededFailureCause = UpdateFailureCause.NoFailure; - tester.InvokeUpdatePlaceholderIfNeeded( - UpdatePlaceholderFileName, - expectedResult: new FileSystemResult(FSResult.Ok, (int)Result.Success), - expectedFailureCause: UpdateFailureCause.NoFailure); - - tester.MockVirtualization.UpdatedPlaceholders.ShouldContain(path => path.Key.Equals(UpdatePlaceholderFileName) && path.Value == GitIndexProjection.FileMode644); - tester.MockVirtualization.UpdatedPlaceholders.Clear(); - - tester.MockVirtualization.UpdatePlaceholderIfNeededResult = Result.EFileNotFound; - tester.MockVirtualization.UpdatePlaceholderIfNeededFailureCause = UpdateFailureCause.NoFailure; - tester.InvokeUpdatePlaceholderIfNeeded( - UpdatePlaceholderFileName, - expectedResult: new FileSystemResult(FSResult.FileOrPathNotFound, (int)Result.EFileNotFound), - expectedFailureCause: UpdateFailureCause.NoFailure); - - // TODO: What will the result be when the UpdateFailureCause is DirtyData - tester.MockVirtualization.UpdatePlaceholderIfNeededResult = Result.EInvalidOperation; - tester.MockVirtualization.UpdatePlaceholderIfNeededFailureCause = UpdateFailureCause.DirtyData; - - // TODO: The result should probably be VirtualizationInvalidOperation but for now it's IOError - tester.InvokeUpdatePlaceholderIfNeeded( - UpdatePlaceholderFileName, - expectedResult: new FileSystemResult(FSResult.IOError, (int)Result.EInvalidOperation), - expectedFailureCause: UpdateFailureCause.DirtyData); - } - } - - [TestCase] - public void WritePlaceholderForSymLink() - { - const string WriteSymLinkFileName = "testWriteSymLink.txt"; - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo, new[] { WriteSymLinkFileName })) - { - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - WriteSymLinkFileName, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.SymLink, fileMode: 0)); - - tester.Virtualizer.WritePlaceholderFile( - WriteSymLinkFileName, - endOfFile: 0, - sha: string.Empty).ShouldEqual(new FileSystemResult(FSResult.Ok, (int)Result.Success)); - - tester.MockVirtualization.CreatedPlaceholders.ShouldBeEmpty(); - tester.MockVirtualization.CreatedSymLinks.Count.ShouldEqual(1); - tester.MockVirtualization.CreatedSymLinks.ShouldContain(entry => entry.Equals(WriteSymLinkFileName)); - - tester.BackgroundTaskShouldBeScheduled(WriteSymLinkFileName, FileSystemTask.OperationType.OnFileSymLinkCreated); - } - } - - [TestCase] - public void UpdatePlaceholderToSymLink() - { - const string PlaceholderToLinkFileName = "testUpdatePlaceholderToLink.txt"; - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo, new[] { PlaceholderToLinkFileName })) - { - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - PlaceholderToLinkFileName, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.SymLink, fileMode: 0)); - - tester.MockVirtualization.UpdatePlaceholderIfNeededResult = Result.Success; - tester.MockVirtualization.UpdatePlaceholderIfNeededFailureCause = UpdateFailureCause.NoFailure; - tester.InvokeUpdatePlaceholderIfNeeded( - PlaceholderToLinkFileName, - expectedResult: new FileSystemResult(FSResult.Ok, (int)Result.Success), - expectedFailureCause: UpdateFailureCause.NoFailure); - - tester.MockVirtualization.UpdatedPlaceholders.Count.ShouldEqual(0, "UpdatePlaceholderIfNeeded should not be called when converting a placeholder to a link"); - tester.MockVirtualization.CreatedSymLinks.Count.ShouldEqual(1); - tester.MockVirtualization.CreatedSymLinks.ShouldContain(entry => entry.Equals(PlaceholderToLinkFileName)); - - tester.BackgroundTaskShouldBeScheduled(PlaceholderToLinkFileName, FileSystemTask.OperationType.OnFileSymLinkCreated); - } - } - - [TestCase] - public void ClearNegativePathCacheIsNoOp() - { - using (MockVirtualizationInstance mockVirtualization = new MockVirtualizationInstance()) - using (MacFileSystemVirtualizer virtualizer = new MacFileSystemVirtualizer(this.Repo.Context, this.Repo.GitObjects, mockVirtualization)) - { - uint totalEntryCount = 0; - virtualizer.ClearNegativePathCache(out totalEntryCount).ShouldEqual(new FileSystemResult(FSResult.Ok, (int)Result.Success)); - totalEntryCount.ShouldEqual(0U); - } - } - - [TestCase] - public void OnEnumerateDirectoryReturnsSuccessWhenResultsNotInMemory() - { - const string TestFileName = "test.txt"; - const string TestFolderName = "testFolder"; - string testFilePath = Path.Combine(TestFolderName, TestFileName); - - // Don't include TestFolderName as MockGitIndexProjection returns the same list of files regardless of what folder name - // it is passed - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - testFilePath, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.Regular, GitIndexProjection.FileMode644)); - - tester.GitIndexProjection.EnumerationInMemory = false; - tester.MockVirtualization.OnEnumerateDirectory(1, TestFolderName, triggeringProcessId: 1, triggeringProcessName: "UnitTests").ShouldEqual(Result.Success); - tester.MockVirtualization.CreatedPlaceholders.ShouldContain( - kvp => kvp.Key.Equals(testFilePath, GVFSPlatform.Instance.Constants.PathComparison) && kvp.Value == GitIndexProjection.FileMode644); - } - } - - [TestCase] - public void OnEnumerateDirectoryReturnsSuccessWhenResultsInMemory() - { - const string TestFileName = "test.txt"; - const string TestFolderName = "testFolder"; - string testFilePath = Path.Combine(TestFolderName, TestFileName); - - // Don't include TestFolderName as MockGitIndexProjection returns the same list of files regardless of what folder name - // it is passed - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - testFilePath, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.Regular, GitIndexProjection.FileMode644)); - - tester.GitIndexProjection.EnumerationInMemory = true; - tester.MockVirtualization.OnEnumerateDirectory(1, TestFolderName, triggeringProcessId: 1, triggeringProcessName: "UnitTests").ShouldEqual(Result.Success); - tester.MockVirtualization.CreatedPlaceholders.ShouldContain( - kvp => kvp.Key.Equals(testFilePath, GVFSPlatform.Instance.Constants.PathComparison) && kvp.Value == GitIndexProjection.FileMode644); - tester.GitIndexProjection.ExpandedFolders.ShouldMatchInOrder(TestFolderName); - } - } - - [TestCase] - public void OnEnumerateDirectorySetsFileModes() - { - const string TestFile644Name = "test644.txt"; - const string TestFile664Name = "test664.txt"; - const string TestFile755Name = "test755.txt"; - const string TestFolderName = "testFolder"; - string testFile644Path = Path.Combine(TestFolderName, TestFile644Name); - string testFile664Path = Path.Combine(TestFolderName, TestFile664Name); - string testFile755Path = Path.Combine(TestFolderName, TestFile755Name); - - // Don't include TestFolderName as MockGitIndexProjection returns the same list of files regardless of what folder name - // it is passed - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo, new[] { TestFile644Name, TestFile664Name, TestFile755Name })) - { - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - testFile644Path, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.Regular, GitIndexProjection.FileMode644)); - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - testFile664Path, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.Regular, GitIndexProjection.FileMode664)); - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - testFile755Path, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.Regular, GitIndexProjection.FileMode755)); - - tester.GitIndexProjection.EnumerationInMemory = true; - tester.MockVirtualization.OnEnumerateDirectory(1, TestFolderName, triggeringProcessId: 1, triggeringProcessName: "UnitTests").ShouldEqual(Result.Success); - tester.MockVirtualization.CreatedPlaceholders.ShouldContain( - kvp => kvp.Key.Equals(testFile644Path, GVFSPlatform.Instance.Constants.PathComparison) && kvp.Value == GitIndexProjection.FileMode644); - tester.MockVirtualization.CreatedPlaceholders.ShouldContain( - kvp => kvp.Key.Equals(testFile664Path, GVFSPlatform.Instance.Constants.PathComparison) && kvp.Value == GitIndexProjection.FileMode664); - tester.MockVirtualization.CreatedPlaceholders.ShouldContain( - kvp => kvp.Key.Equals(testFile755Path, GVFSPlatform.Instance.Constants.PathComparison) && kvp.Value == GitIndexProjection.FileMode755); - } - } - - [TestCase] - public void OnGetFileStreamReturnsSuccessWhenFileStreamAvailable() - { - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.MockVirtualization.WriteFileReturnResult = Result.Success; - tester.InvokeOnGetFileStream(expectedResult: Result.Success); - tester.MockVirtualization.BytesWritten.ShouldEqual(MockGVFSGitObjects.DefaultFileLength); - } - } - - [TestCase] - [Category(CategoryConstants.ExceptionExpected)] - public void OnGetFileStreamReturnsErrorWhenWriteFileContentsFails() - { - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.MockVirtualization.WriteFileReturnResult = Result.EIOError; - tester.InvokeOnGetFileStream(expectedResult: Result.EIOError); - } - } - - [TestCase] - public void OnNewFileCreatedInsideDotGitDirectoryShouldNotScheduleBackgroundTask() - { - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.MockVirtualization.OnNewFileCreated(Path.Combine(".git", "testing.txt"), isDirectory: false); - tester.BackgroundTaskRunner.Count.ShouldEqual(0); - } - } - - [TestCase] - public void OnNewFileCreatedFileShouldScheduleBackgroundTask() - { - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.MockVirtualization.OnNewFileCreated("testing.txt", isDirectory: false); - tester.BackgroundTaskShouldBeScheduled("testing.txt", FileSystemTask.OperationType.OnFileCreated); - tester.GitIndexProjection.SparseEntries.Count.ShouldEqual(0); - } - } - - [TestCase] - public void OnNewFileCreatedDirectoryIncludedShouldScheduleBackgroundTask() - { - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.GitIndexProjection.GetFolderPathSparseStateValue = GitIndexProjection.PathSparseState.Included; - tester.MockVirtualization.OnNewFileCreated("testing", isDirectory: true); - tester.BackgroundTaskShouldBeScheduled("testing", FileSystemTask.OperationType.OnFolderCreated); - tester.GitIndexProjection.SparseEntries.Count.ShouldEqual(0); - } - } - - [TestCase] - public void OnNewFileCreatedDirectoryExcludedShouldNotScheduleBackgroundTask() - { - const string TestFileName = "test.txt"; - const string TestFolderName = "testFolder"; - string testFilePath = Path.Combine(TestFolderName, TestFileName); - - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.GitIndexProjection.MockFileTypesAndModes.TryAdd( - testFilePath, - ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType.Regular, GitIndexProjection.FileMode644)); - - tester.GitIndexProjection.GetFolderPathSparseStateValue = GitIndexProjection.PathSparseState.Excluded; - tester.MockVirtualization.OnNewFileCreated(TestFolderName, isDirectory: true); - tester.BackgroundTaskRunner.Count.ShouldEqual(0); - tester.GitIndexProjection.SparseEntries.Count.ShouldEqual(1); - tester.GitIndexProjection.SparseEntries.First().ShouldEqual(TestFolderName); - tester.MockVirtualization.CreatedPlaceholders.ShouldContain( - kvp => kvp.Key.Equals(testFilePath, GVFSPlatform.Instance.Constants.PathComparison) && kvp.Value == GitIndexProjection.FileMode644); - } - } - - [TestCase] - public void OnNewFileCreatedDirectoryNotFoundShouldScheduleBackgroundTask() - { - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.GitIndexProjection.GetFolderPathSparseStateValue = GitIndexProjection.PathSparseState.NotFound; - tester.MockVirtualization.OnNewFileCreated("testing", isDirectory: true); - tester.BackgroundTaskShouldBeScheduled("testing", FileSystemTask.OperationType.OnFolderCreated); - tester.GitIndexProjection.SparseEntries.Count.ShouldEqual(0); - } - } - - [TestCase] - public void OnNewFileCreatedDirectoryExcludedTryAddSparseFolderFailureShouldScheduleBackgroundTask() - { - using (MacFileSystemVirtualizerTester tester = new MacFileSystemVirtualizerTester(this.Repo)) - { - tester.GitIndexProjection.GetFolderPathSparseStateValue = GitIndexProjection.PathSparseState.Excluded; - tester.GitIndexProjection.TryAddSparseFolderReturnValue = false; - tester.MockVirtualization.OnNewFileCreated("testing", isDirectory: true); - tester.BackgroundTaskShouldBeScheduled("testing", FileSystemTask.OperationType.OnFolderCreated); - tester.GitIndexProjection.SparseEntries.Count.ShouldEqual(0); - } - } - - private static ushort ConvertFileTypeAndModeToIndexFormat(GitIndexProjection.FileType fileType, ushort fileMode) - { - // Values used in the index file to indicate the type of the file - const ushort RegularFileIndexEntry = 0x8000; - const ushort SymLinkFileIndexEntry = 0xA000; - const ushort GitLinkFileIndexEntry = 0xE000; - - switch (fileType) - { - case GitIndexProjection.FileType.Regular: - return (ushort)(RegularFileIndexEntry | fileMode); - - case GitIndexProjection.FileType.SymLink: - return (ushort)(SymLinkFileIndexEntry | fileMode); - - case GitIndexProjection.FileType.GitLink: - return (ushort)(GitLinkFileIndexEntry | fileMode); - - default: - Assert.Fail($"Invalid fileType {fileType}"); - return 0; - } - } - } -} diff --git a/GVFS/GVFS.UnitTests/Service/Mac/MacServiceTests.cs b/GVFS/GVFS.UnitTests/Service/Mac/MacServiceTests.cs deleted file mode 100644 index 5dffa80a49..0000000000 --- a/GVFS/GVFS.UnitTests/Service/Mac/MacServiceTests.cs +++ /dev/null @@ -1,137 +0,0 @@ -using GVFS.Common; -using GVFS.Common.NamedPipes; -using GVFS.Service; -using GVFS.Service.Handlers; -using GVFS.UnitTests.Mock.Common; -using GVFS.UnitTests.Mock.FileSystem; -using Moq; -using NUnit.Framework; -using System.IO; - -namespace GVFS.UnitTests.Service.Mac -{ - [TestFixture] - public class MacServiceTests - { - private const string GVFSServiceName = "GVFS.Service"; - private const int ExpectedActiveUserId = 502; - private const int ExpectedSessionId = 502; - private static readonly string ExpectedActiveRepoPath = Path.Combine("mock:", "code", "repo2"); - private static readonly string ServiceDataLocation = Path.Combine("mock:", "registryDataFolder"); - - private MockFileSystem fileSystem; - private MockTracer tracer; - private MockPlatform gvfsPlatform; - - [SetUp] - public void SetUp() - { - this.tracer = new MockTracer(); - this.fileSystem = new MockFileSystem(new MockDirectory(ServiceDataLocation, null, null)); - this.gvfsPlatform = (MockPlatform)GVFSPlatform.Instance; - this.gvfsPlatform.MockCurrentUser = ExpectedActiveUserId.ToString(); - } - - [TestCase] - public void ServiceStartTriggersAutoMountForCurrentUser() - { - Mock repoRegistry = new Mock(MockBehavior.Strict); - repoRegistry.Setup(r => r.AutoMountRepos(ExpectedActiveUserId.ToString(), ExpectedSessionId, true)); - repoRegistry.Setup(r => r.TraceStatus()); - - GVFSService service = new GVFSService( - this.tracer, - serviceName: null, - repoRegistry: repoRegistry.Object); - - service.Run(); - - repoRegistry.VerifyAll(); - } - - [TestCase] - public void ServiceHandlesEnablePrjfsRequest() - { - string expectedServiceResponse = "TRequestResponse|{\"State\":1,\"ErrorMessage\":null}"; - Mock connectionMock = new Mock( - MockBehavior.Strict, - null, // serverStream - this.tracer, // tracer - null); // isStopping - connectionMock.Setup(mp => mp.TrySendResponse(expectedServiceResponse)).Returns(true); - - NamedPipeMessages.EnableAndAttachProjFSRequest request = new NamedPipeMessages.EnableAndAttachProjFSRequest(); - request.EnlistmentRoot = string.Empty; - - RequestHandler serviceRequestHandler = new RequestHandler(this.tracer, etwArea: string.Empty, repoRegistry: null); - serviceRequestHandler.HandleRequest(this.tracer, request.ToMessage().ToString(), connectionMock.Object); - - connectionMock.VerifyAll(); - } - - [TestCase] - public void RepoRegistryMountsOnlyRegisteredRepos() - { - Mock repoMounterMock = new Mock(MockBehavior.Strict); - repoMounterMock.Setup(mp => mp.MountRepository(ExpectedActiveRepoPath, ExpectedActiveUserId)).Returns(true); - - this.CreateTestRepos(ServiceDataLocation); - - RepoRegistry repoRegistry = new RepoRegistry( - this.tracer, - this.fileSystem, - ServiceDataLocation, - repoMounterMock.Object, - null); - - repoRegistry.AutoMountRepos(ExpectedActiveUserId.ToString(), ExpectedSessionId, checkDirectoryExists: false); - - repoMounterMock.VerifyAll(); - } - - [TestCase] - public void MountProcessLaunchedUsingCorrectArgs() - { - string executable = @"/bin/launchctl"; - string gvfsBinPath = Path.Combine(this.gvfsPlatform.Constants.GVFSBinDirectoryPath, this.gvfsPlatform.Constants.GVFSExecutableName); - string expectedArgs = $"asuser {ExpectedActiveUserId} {gvfsBinPath} mount {ExpectedActiveRepoPath}"; - - Mock mountLauncherMock = new Mock(MockBehavior.Strict, this.tracer); - mountLauncherMock.Setup(mp => mp.LaunchProcess( - executable, - expectedArgs, - ExpectedActiveRepoPath)) - .Returns(true); - - string errorString = null; - mountLauncherMock.Setup(mp => mp.WaitUntilMounted( - this.tracer, - ExpectedActiveRepoPath, - It.IsAny(), - out errorString)) - .Returns(true); - - GVFSMountProcess mountProcess = new GVFSMountProcess(this.tracer, mountLauncherMock.Object); - mountProcess.MountRepository(ExpectedActiveRepoPath, ExpectedActiveUserId); - - mountLauncherMock.VerifyAll(); - } - - private void CreateTestRepos(string dataLocation) - { - string repo1 = Path.Combine("mock:", "code", "repo1"); - string repo2 = ExpectedActiveRepoPath; - string repo3 = Path.Combine("mock:", "code", "repo3"); - string repo4 = Path.Combine("mock:", "code", "repo4"); - - this.fileSystem.WriteAllText( - Path.Combine(dataLocation, RepoRegistry.RegistryName), - $@"1 - {{""EnlistmentRoot"":""{repo1.Replace("\\", "\\\\")}"",""OwnerSID"":502,""IsActive"":false}} - {{""EnlistmentRoot"":""{repo2.Replace("\\", "\\\\")}"",""OwnerSID"":502,""IsActive"":true}} - {{""EnlistmentRoot"":""{repo3.Replace("\\", "\\\\")}"",""OwnerSID"":501,""IsActive"":false}} - {{""EnlistmentRoot"":""{repo4.Replace("\\", "\\\\")}"",""OwnerSID"":501,""IsActive"":true}} - "); - } - } -} diff --git a/GVFS/GVFS/GVFS.Mac.csproj b/GVFS/GVFS/GVFS.Mac.csproj deleted file mode 100644 index 50dd74a110..0000000000 --- a/GVFS/GVFS/GVFS.Mac.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Exe - gvfs - netcoreapp2.1 - x64 - osx-x64 - - - - $(GVFSVersion) - - - $(GVFSVersion) - - - - all - - - - - - - - - \ No newline at end of file diff --git a/MirrorProvider/MirrorProvider.Mac/MacFileSystemVirtualizer.cs b/MirrorProvider/MirrorProvider.Mac/MacFileSystemVirtualizer.cs deleted file mode 100644 index be40214ce5..0000000000 --- a/MirrorProvider/MirrorProvider.Mac/MacFileSystemVirtualizer.cs +++ /dev/null @@ -1,273 +0,0 @@ -using PrjFSLib.Mac; -using System; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; - -namespace MirrorProvider.Mac -{ - public class MacFileSystemVirtualizer : FileSystemVirtualizer - { - private VirtualizationInstance virtualizationInstance = new VirtualizationInstance(); - - protected override StringComparison PathComparison => StringComparison.OrdinalIgnoreCase; - protected override StringComparer PathComparer => StringComparer.OrdinalIgnoreCase; - - public override bool TryConvertVirtualizationRoot(string directory, out string error) - { - Result result = VirtualizationInstance.ConvertDirectoryToVirtualizationRoot(directory); - - error = result.ToString(); - return result == Result.Success; - } - - public override bool TryStartVirtualizationInstance(Enlistment enlistment, out string error) - { - this.virtualizationInstance.OnEnumerateDirectory = this.OnEnumerateDirectory; - this.virtualizationInstance.OnGetFileStream = this.OnGetFileStream; - this.virtualizationInstance.OnLogError = this.OnLogError; - this.virtualizationInstance.OnLogWarning = this.OnLogWarning; - this.virtualizationInstance.OnLogInfo = this.OnLogInfo; - this.virtualizationInstance.OnFileModified = this.OnFileModified; - this.virtualizationInstance.OnPreDelete = this.OnPreDelete; - this.virtualizationInstance.OnNewFileCreated = this.OnNewFileCreated; - this.virtualizationInstance.OnFileRenamed = this.OnFileRenamed; - this.virtualizationInstance.OnHardLinkCreated = this.OnHardLinkCreated; - this.virtualizationInstance.OnFilePreConvertToFull = this.OnFilePreConvertToFull; - - Result result = this.virtualizationInstance.StartVirtualizationInstance( - enlistment.SrcRoot, - poolThreadCount: (uint)Environment.ProcessorCount * 2); - - if (result == Result.Success) - { - return base.TryStartVirtualizationInstance(enlistment, out error); - } - else - { - error = result.ToString(); - return false; - } - } - - private Result OnEnumerateDirectory( - ulong commandId, - string relativePath, - int triggeringProcessId, - string triggeringProcessName) - { - Console.WriteLine($"OnEnumerateDirectory({commandId}, '{relativePath}', {triggeringProcessId}, {triggeringProcessName})"); - - try - { - if (!this.DirectoryExists(relativePath)) - { - return Result.EFileNotFound; - } - - foreach (ProjectedFileInfo child in this.GetChildItems(relativePath)) - { - if (child.Type == ProjectedFileInfo.FileType.Directory) - { - Result result = this.virtualizationInstance.WritePlaceholderDirectory( - Path.Combine(relativePath, child.Name)); - - if (result != Result.Success) - { - Console.WriteLine($"WritePlaceholderDirectory failed: {result}"); - return result; - } - } - else if (child.Type == ProjectedFileInfo.FileType.SymLink) - { - string childRelativePath = Path.Combine(relativePath, child.Name); - - string symLinkTarget; - if (this.TryGetSymLinkTarget(childRelativePath, out symLinkTarget)) - { - Result result = this.virtualizationInstance.WriteSymLink( - childRelativePath, - symLinkTarget); - - if (result != Result.Success) - { - Console.WriteLine($"WriteSymLink failed: {result}"); - return result; - } - } - else - { - return Result.EIOError; - } - } - else - { - // The MirrorProvider marks every file as executable (mode 755), but this is just a shortcut to avoid the pain of - // having to p/invoke to determine if the original file is exectuable or not. - // A real provider will have to get this information from its data source. For example, GVFS gets this info - // out of the git index along with all the other info for projecting files. - UInt16 fileMode = Convert.ToUInt16("755", 8); - - Result result = this.virtualizationInstance.WritePlaceholderFile( - Path.Combine(relativePath, child.Name), - providerId: ToVersionIdByteArray(1), - contentId: ToVersionIdByteArray(0), - fileMode: fileMode); - if (result != Result.Success) - { - Console.WriteLine($"WritePlaceholderFile failed: {result}"); - return result; - } - } - } - } - catch (IOException e) - { - Console.WriteLine($"IOException in OnEnumerateDirectory: {e.Message}"); - return Result.EIOError; - } - - return Result.Success; - } - - private Result OnGetFileStream( - ulong commandId, - string relativePath, - byte[] providerId, - byte[] contentId, - int triggeringProcessId, - string triggeringProcessName, - IntPtr fileHandle) - { - Console.WriteLine($"OnGetFileStream({commandId}, '{relativePath}', {contentId.Length}/{contentId[0]}:{contentId[1]}, {providerId.Length}/{providerId[0]}:{providerId[1]}, {triggeringProcessId}, {triggeringProcessName}, 0x{fileHandle.ToInt64():X})"); - - if (!this.FileExists(relativePath)) - { - return Result.EFileNotFound; - } - - try - { - const int bufferSize = 4096; - FileSystemResult hydrateFileResult = this.HydrateFile( - relativePath, - bufferSize, - (buffer, bytesToCopy) => - { - Result result = this.virtualizationInstance.WriteFileContents( - fileHandle, - buffer, - (uint)bytesToCopy); - if (result != Result.Success) - { - Console.WriteLine($"WriteFileContents failed: {result}"); - return false; - } - - return true; - }); - - if (hydrateFileResult != FileSystemResult.Success) - { - return Result.EIOError; - } - } - catch (IOException e) - { - Console.WriteLine($"IOException in OnGetFileStream: {e.Message}"); - return Result.EIOError; - } - - return Result.Success; - } - - private void OnLogError(string errorMessage) - { - Console.WriteLine($"OnLogError: {errorMessage}"); - } - - private void OnLogWarning(string warningMessage) - { - Console.WriteLine($"OnLogWarning: {warningMessage}"); - } - private void OnLogInfo(string infoMessage) - { - Console.WriteLine($"OnLogInfo: {infoMessage}"); - } - - private void OnFileModified(string relativePath) - { - Console.WriteLine($"OnFileModified: {relativePath}"); - } - - private Result OnPreDelete(string relativePath, bool isDirectory) - { - Console.WriteLine($"OnPreDelete (isDirectory: {isDirectory}): {relativePath}"); - return Result.Success; - } - - private void OnNewFileCreated(string relativePath, bool isDirectory) - { - Console.WriteLine($"OnNewFileCreated (isDirectory: {isDirectory}): {relativePath}"); - } - - private void OnFileRenamed(string relativeDestinationPath, bool isDirectory) - { - Console.WriteLine($"OnFileRenamed (isDirectory: {isDirectory}) destination: {relativeDestinationPath}"); - } - - private void OnHardLinkCreated(string existingRelativePath, string relativeNewLinkPath) - { - Console.WriteLine($"OnHardLinkCreated {relativeNewLinkPath} from {existingRelativePath}"); - } - - private Result OnFilePreConvertToFull(string relativePath) - { - Console.WriteLine($"OnFilePreConvertToFull: {relativePath}"); - return Result.Success; - } - - private bool TryGetSymLinkTarget(string relativePath, out string symLinkTarget) - { - symLinkTarget = null; - string fullPathInMirror = this.GetFullPathInMirror(relativePath); - - const ulong BufSize = 4096; - byte[] targetBuffer = new byte[BufSize]; - long bytesRead = ReadLink(fullPathInMirror, targetBuffer, BufSize); - if (bytesRead < 0) - { - Console.WriteLine($"GetSymLinkTarget failed: {Marshal.GetLastWin32Error()}"); - return false; - } - - targetBuffer[bytesRead] = 0; - symLinkTarget = Encoding.UTF8.GetString(targetBuffer); - - if (symLinkTarget.StartsWith(this.Enlistment.MirrorRoot, PathComparison)) - { - // Link target is an absolute path inside the MirrorRoot. - // The target needs to be adjusted to point inside the src root - symLinkTarget = Path.Combine( - this.Enlistment.SrcRoot.TrimEnd(Path.DirectorySeparatorChar), - symLinkTarget.Substring(this.Enlistment.MirrorRoot.Length).TrimStart(Path.DirectorySeparatorChar)); - } - - return true; - } - - private static byte[] ToVersionIdByteArray(byte version) - { - byte[] bytes = new byte[VirtualizationInstance.PlaceholderIdLength]; - bytes[0] = version; - - return bytes; - } - - [DllImport("libc", EntryPoint = "readlink", SetLastError = true)] - private static extern long ReadLink( - string path, - byte[] buf, - ulong bufsize); - } -} diff --git a/MirrorProvider/MirrorProvider.Mac/MirrorProvider.Mac.csproj b/MirrorProvider/MirrorProvider.Mac/MirrorProvider.Mac.csproj deleted file mode 100644 index 11e84c84e3..0000000000 --- a/MirrorProvider/MirrorProvider.Mac/MirrorProvider.Mac.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - Exe - netcoreapp2.1 - osx.10.12-x64 - x64 - Debug;Release - - - - ..\..\..\BuildOutput - - - - $(BuildOutputDir)\MirrorProvider.Mac\obj\$(Configuration)\$(Platform) - $(BuildOutputDir)\MirrorProvider.Mac\bin\$(Configuration)\$(Platform) - true - - - - true - full - false - TRACE;DEBUG - - - - true - TRACE;RELEASE - - - - - - - - - - - diff --git a/MirrorProvider/MirrorProvider.Mac/Program.cs b/MirrorProvider/MirrorProvider.Mac/Program.cs deleted file mode 100644 index 52483ddf16..0000000000 --- a/MirrorProvider/MirrorProvider.Mac/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace MirrorProvider.Mac -{ - class Program - { - static void Main(string[] args) - { - MirrorProviderCLI.Run(args, new MacFileSystemVirtualizer()); - } - } -} diff --git a/MirrorProvider/MirrorProvider.Windows/ActiveEnumeration.cs b/MirrorProvider/MirrorProvider.Windows/ActiveEnumeration.cs deleted file mode 100644 index 6116def251..0000000000 --- a/MirrorProvider/MirrorProvider.Windows/ActiveEnumeration.cs +++ /dev/null @@ -1,157 +0,0 @@ -using Microsoft.Windows.ProjFS; -using System.Collections.Generic; - -namespace MirrorProvider.Windows -{ - public class ActiveEnumeration - { - // Use our own enumerator to avoid having to dispose anything - private ProjectedFileInfoEnumerator fileInfoEnumerator; - private string filterString = null; - - public ActiveEnumeration(List fileInfos) - { - this.fileInfoEnumerator = new ProjectedFileInfoEnumerator(fileInfos); - this.ResetEnumerator(); - this.MoveNext(); - } - - /// - /// true if Current refers to an element in the enumeration, false if Current is past the end of the collection - /// - public bool IsCurrentValid { get; private set; } - - /// - /// Gets the element in the collection at the current position of the enumerator - /// - public ProjectedFileInfo Current - { - get { return this.fileInfoEnumerator.Current; } - } - - /// - /// Resets the enumerator and advances it to the first ProjectedFileInfo in the enumeration - /// - /// Filter string to save. Can be null. - public void RestartEnumeration(string filter) - { - this.ResetEnumerator(); - this.IsCurrentValid = this.fileInfoEnumerator.MoveNext(); - this.SaveFilter(filter); - } - - /// - /// Advances the enumerator to the next element of the collection (that is being projected). - /// If a filter string is set, MoveNext will advance to the next entry that matches the filter. - /// - /// - /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection - /// - public bool MoveNext() - { - this.IsCurrentValid = this.fileInfoEnumerator.MoveNext(); - while (this.IsCurrentValid && this.IsCurrentHidden()) - { - this.IsCurrentValid = this.fileInfoEnumerator.MoveNext(); - } - - return this.IsCurrentValid; - } - - /// - /// Attempts to save the filter string for this enumeration. When setting a filter string, if Current is valid - /// and does not match the specified filter, the enumerator will be advanced until an element is found that - /// matches the filter (or the end of the collection is reached). - /// - /// Filter string to save. Can be null. - /// True if the filter string was saved. False if the filter string was not saved (because a filter string - /// was previously saved). - /// - /// - /// Per MSDN (https://msdn.microsoft.com/en-us/library/windows/hardware/ff567047(v=vs.85).aspx, the filter string - /// specified in the first call to ZwQueryDirectoryFile will be used for all subsequent calls for the handle (and - /// the string specified in subsequent calls should be ignored) - /// - public bool TrySaveFilterString(string filter) - { - if (this.filterString == null) - { - this.SaveFilter(filter); - return true; - } - - return false; - } - - /// - /// Returns the current filter string or null if no filter string has been saved - /// - /// The current filter string or null if no filter string has been saved - public string GetFilterString() - { - return this.filterString; - } - - private void SaveFilter(string filter) - { - if (string.IsNullOrEmpty(filter)) - { - this.filterString = string.Empty; - } - else - { - this.filterString = filter; - if (this.IsCurrentValid && this.IsCurrentHidden()) - { - this.MoveNext(); - } - } - } - - private bool IsCurrentHidden() - { - return !Utils.IsFileNameMatch(this.Current.Name, this.GetFilterString()); - } - - private void ResetEnumerator() - { - this.fileInfoEnumerator.Reset(); - } - - private class ProjectedFileInfoEnumerator - { - private List list; - private int index; - - public ProjectedFileInfoEnumerator(List projectedFileInfos) - { - this.list = projectedFileInfos; - this.Reset(); - } - - public ProjectedFileInfo Current { get; private set; } - - // Combination of the logic in List.Enumerator MoveNext() and MoveNextRare() - // https://github.com/dotnet/corefx/blob/b492409b4a1952cda4b078f800499d382e1765fc/src/Common/src/CoreLib/System/Collections/Generic/List.cs#L1137 - public bool MoveNext() - { - if (this.index < this.list.Count) - { - this.Current = this.list[this.index]; - this.index++; - return true; - } - - this.index = this.list.Count + 1; - this.Current = null; - return false; - } - - public void Reset() - { - this.index = 0; - this.Current = null; - } - } - } -} diff --git a/MirrorProvider/MirrorProvider.Windows/App.config b/MirrorProvider/MirrorProvider.Windows/App.config deleted file mode 100644 index 00bfd114af..0000000000 --- a/MirrorProvider/MirrorProvider.Windows/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MirrorProvider/MirrorProvider.Windows/MirrorProvider.Windows.csproj b/MirrorProvider/MirrorProvider.Windows/MirrorProvider.Windows.csproj deleted file mode 100644 index 9abb7dca33..0000000000 --- a/MirrorProvider/MirrorProvider.Windows/MirrorProvider.Windows.csproj +++ /dev/null @@ -1,79 +0,0 @@ - - - - - Debug - x64 - {1ED51604-045C-4914-8054-15AAED90FF45} - Exe - MirrorProvider.Windows - MirrorProvider.Windows - v4.6.1 - 512 - true - - - ..\..\..\BuildOutput - - - $(BuildOutputDir)\MirrorProvider.Windows\bin\$(Platform)\$(Configuration)\ - $(BuildOutputDir)\MirrorProvider.Windows\obj\$(Platform)\$(Configuration)\ - true - x64 - - - true - DEBUG;TRACE - full - prompt - - - TRACE - true - pdbonly - prompt - - - - ..\..\..\packages\CommandLineParser.2.2.1\lib\net45\CommandLine.dll - - - ..\..\..\packages\Microsoft.Windows.ProjFS.1.1.19156.1\lib\net461\ProjectedFSLib.Managed.dll - - - - - ..\..\..\packages\System.Console.4.0.0\lib\net46\System.Console.dll - True - - - - ..\..\..\packages\System.Reflection.TypeExtensions.4.1.0\lib\net46\System.Reflection.TypeExtensions.dll - - - - - - - - - - - - - - - - - Designer - - - - - - {128451a2-7598-4142-ba2a-9b963d91ccbc} - MirrorProvider - - - - \ No newline at end of file diff --git a/MirrorProvider/MirrorProvider.Windows/Program.cs b/MirrorProvider/MirrorProvider.Windows/Program.cs deleted file mode 100644 index ad6878ba0c..0000000000 --- a/MirrorProvider/MirrorProvider.Windows/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace MirrorProvider.Windows -{ - class Program - { - static void Main(string[] args) - { - MirrorProviderCLI.Run(args, new WindowsFileSystemVirtualizer()); - } - } -} diff --git a/MirrorProvider/MirrorProvider.Windows/Properties/AssemblyInfo.cs b/MirrorProvider/MirrorProvider.Windows/Properties/AssemblyInfo.cs deleted file mode 100644 index 593a36852e..0000000000 --- a/MirrorProvider/MirrorProvider.Windows/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("MirrorProvider.Windows")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MirrorProvider.Windows")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2019")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("1ed51604-045c-4914-8054-15aaed90ff45")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MirrorProvider/MirrorProvider.Windows/WindowsFileSystemVirtualizer.cs b/MirrorProvider/MirrorProvider.Windows/WindowsFileSystemVirtualizer.cs deleted file mode 100644 index a2e51a2a16..0000000000 --- a/MirrorProvider/MirrorProvider.Windows/WindowsFileSystemVirtualizer.cs +++ /dev/null @@ -1,346 +0,0 @@ -using Microsoft.Windows.ProjFS; -using System; -using System.Collections.Concurrent; -using System.ComponentModel; -using System.IO; -using System.Linq; - -namespace MirrorProvider.Windows -{ - public class WindowsFileSystemVirtualizer : FileSystemVirtualizer, IRequiredCallbacks - { - private VirtualizationInstance virtualizationInstance; - private ConcurrentDictionary activeEnumerations = new ConcurrentDictionary(); - - protected override StringComparison PathComparison => StringComparison.OrdinalIgnoreCase; - protected override StringComparer PathComparer => StringComparer.OrdinalIgnoreCase; - - public override bool TryConvertVirtualizationRoot(string directory, out string error) - { - error = string.Empty; - HResult result = VirtualizationInstance.MarkDirectoryAsVirtualizationRoot(directory, Guid.NewGuid()); - if (result != HResult.Ok) - { - error = result.ToString("F"); - return false; - } - - return true; - } - - public override bool TryStartVirtualizationInstance(Enlistment enlistment, out string error) - { - this.virtualizationInstance.OnQueryFileName = this.QueryFileName; - this.virtualizationInstance.OnNotifyPreDelete = this.OnPreDelete; - this.virtualizationInstance.OnNotifyNewFileCreated = this.OnNewFileCreated; - this.virtualizationInstance.OnNotifyFileHandleClosedFileModifiedOrDeleted = this.OnFileModifiedOrDeleted; - this.virtualizationInstance.OnNotifyFileRenamed = this.OnFileRenamed; - this.virtualizationInstance.OnNotifyHardlinkCreated = this.OnHardlinkCreated; - this.virtualizationInstance.OnNotifyFilePreConvertToFull = this.OnFilePreConvertToFull; - - uint threadCount = (uint)Environment.ProcessorCount * 2; - - NotificationMapping[] notificationMappings = new NotificationMapping[] - { - new NotificationMapping( - NotificationType.NewFileCreated | - NotificationType.PreDelete | - NotificationType.FileRenamed | - NotificationType.HardlinkCreated | - NotificationType.FileHandleClosedFileModified, - string.Empty), - }; - - this.virtualizationInstance = new VirtualizationInstance( - enlistment.SrcRoot, - poolThreadCount: threadCount, - concurrentThreadCount: threadCount, - enableNegativePathCache: false, - notificationMappings: notificationMappings); - - HResult result = this.virtualizationInstance.StartVirtualizing(this); - - if (result == HResult.Ok) - { - return base.TryStartVirtualizationInstance(enlistment, out error); - } - - error = result.ToString("F"); - return false; - } - - public HResult StartDirectoryEnumerationCallback(int commandId, Guid enumerationId, string relativePath, uint triggeringProcessId, string triggeringProcessImageFileName) - { - Console.WriteLine($"StartDirectoryEnumeration: `{relativePath}`, {enumerationId}"); - - // On Windows, we have to sort the child items. The PrjFlt driver takes our list and merges it with - // what is on disk, and it assumes that both lists are already sorted. - ActiveEnumeration activeEnumeration = new ActiveEnumeration( - this.GetChildItems(relativePath) - .OrderBy(file => file.Name, PathComparer) - .ToList()); - - if (!this.activeEnumerations.TryAdd(enumerationId, activeEnumeration)) - { - return HResult.InternalError; - } - - return HResult.Ok; - } - - public HResult EndDirectoryEnumerationCallback(Guid enumerationId) - { - Console.WriteLine($"EndDirectioryEnumeration: {enumerationId}"); - - ActiveEnumeration activeEnumeration; - if (!this.activeEnumerations.TryRemove(enumerationId, out activeEnumeration)) - { - return HResult.InternalError; - } - - return HResult.Ok; - } - - public HResult GetDirectoryEnumerationCallback( - int commandId, - Guid enumerationId, - string filterFileName, - bool restartScan, - IDirectoryEnumerationResults results) - { - Console.WriteLine($"GetDiretoryEnumeration: {enumerationId}, {filterFileName}"); - - try - { - ActiveEnumeration activeEnumeration = null; - if (!this.activeEnumerations.TryGetValue(enumerationId, out activeEnumeration)) - { - return HResult.InternalError; - } - - if (restartScan) - { - activeEnumeration.RestartEnumeration(filterFileName); - } - else - { - activeEnumeration.TrySaveFilterString(filterFileName); - } - - bool entryAdded = false; - - HResult result = HResult.Ok; - while (activeEnumeration.IsCurrentValid) - { - ProjectedFileInfo fileInfo = activeEnumeration.Current; - - DateTime now = DateTime.UtcNow; - if (results.Add( - fileName: fileInfo.Name, - fileSize: fileInfo.IsDirectory ? 0 : fileInfo.Size, - isDirectory: fileInfo.IsDirectory, - fileAttributes: fileInfo.IsDirectory ? FileAttributes.Directory : FileAttributes.Archive, - creationTime: now, - lastAccessTime: now, - lastWriteTime: now, - changeTime: now)) - { - entryAdded = true; - activeEnumeration.MoveNext(); - } - else - { - result = entryAdded ? HResult.Ok : HResult.InsufficientBuffer; - break; - } - } - - return result; - } - catch (Win32Exception e) - { - return HResultFromWin32(e.NativeErrorCode); - } - catch (Exception) - { - return HResult.InternalError; - } - } - - public HResult GetPlaceholderInfoCallback( - int commandId, - string relativePath, - uint triggeringProcessId, - string triggeringProcessImageFileName) - { - Console.WriteLine($"GetPlaceholderInfoCallback: `{relativePath}`"); - - ProjectedFileInfo fileInfo = this.GetFileInfo(relativePath); - if (fileInfo == null) - { - return HResult.FileNotFound; - } - - DateTime now = DateTime.UtcNow; - HResult result = this.virtualizationInstance.WritePlaceholderInfo( - Path.Combine(Path.GetDirectoryName(relativePath), fileInfo.Name), - creationTime: now, - lastAccessTime: now, - lastWriteTime: now, - changeTime: now, - fileAttributes: fileInfo.IsDirectory ? FileAttributes.Directory : FileAttributes.Archive, - endOfFile: fileInfo.Size, - isDirectory: fileInfo.IsDirectory, - contentId: new byte[] { 0 }, - providerId: new byte[] { 1 }); - - if (result != HResult.Ok) - { - Console.WriteLine("WritePlaceholderInformation failed: " + result); - } - - return result; - } - - public HResult GetFileDataCallback( - int commandId, - string relativePath, - ulong byteOffset, - uint length, - Guid streamGuid, - byte[] contentId, - byte[] providerId, - uint triggeringProcessId, - string triggeringProcessImageFileName) - { - Console.WriteLine($"GetFileDataCallback: `{relativePath}`"); - - if (!this.FileExists(relativePath)) - { - return HResult.FileNotFound; - } - - try - { - const int bufferSize = 64 * 1024; - using (IWriteBuffer writeBuffer = this.virtualizationInstance.CreateWriteBuffer(bufferSize)) - { - ulong writeOffset = 0; - - FileSystemResult hydrateFileResult = this.HydrateFile( - relativePath, - bufferSize, - (readBuffer, bytesToCopy) => - { - writeBuffer.Stream.Seek(0, SeekOrigin.Begin); - writeBuffer.Stream.Write(readBuffer, 0, (int)bytesToCopy); - - HResult writeResult = this.virtualizationInstance.WriteFileData(streamGuid, writeBuffer, writeOffset, bytesToCopy); - if (writeResult != HResult.Ok) - { - Console.WriteLine("WriteFile faild: " + writeResult); - return false; - } - - writeOffset += bytesToCopy; - - return true; - }); - - if (hydrateFileResult != FileSystemResult.Success) - { - return HResult.InternalError; - } - } - } - catch (IOException e) - { - Console.WriteLine("IOException in GetFileDataCallback: " + e.Message); - return HResult.InternalError; - } - catch (UnauthorizedAccessException e) - { - Console.WriteLine("UnauthorizedAccessException in GetFileDataCallback: " + e.Message); - return HResult.InternalError; - } - - return HResult.Ok; - } - - private HResult QueryFileName(string relativePath) - { - Console.WriteLine($"QueryFileName: `{relativePath}`"); - - string parentDirectory = Path.GetDirectoryName(relativePath); - string childName = Path.GetFileName(relativePath); - if (this.GetChildItems(parentDirectory).Any(child => child.Name.Equals(childName, PathComparison))) - { - return HResult.Ok; - } - - return HResult.FileNotFound; - } - - private bool OnPreDelete(string relativePath, bool isDirectory, uint triggeringProcessId, string triggeringProcessImageFileName) - { - Console.WriteLine($"OnPreDelete (isDirectory: {isDirectory}): {relativePath}, triggeringProcessId: {triggeringProcessId}, triggeringProcessImageFileName: {triggeringProcessImageFileName}"); - return true; - } - - private void OnNewFileCreated( - string relativePath, - bool isDirectory, - uint triggeringProcessId, - string triggeringProcessImageFileName, - out NotificationType notificationMask) - { - notificationMask = NotificationType.UseExistingMask; - Console.WriteLine($"OnNewFileCreated (isDirectory: {isDirectory}): {relativePath}, triggeringProcessId: {triggeringProcessId}, triggeringProcessImageFileName: {triggeringProcessImageFileName}"); - } - - private void OnFileModifiedOrDeleted(string relativePath, bool isDirectory, bool isFileModified, bool isFileDeleted, uint triggeringProcessId, string triggeringProcessImageFileName) - { - // To keep WindowsFileSystemVirtualizer in sync with MacFileSystemVirtualizer we're only registering for - // NotificationType.FileHandleClosedFileModified and so this method will only be called for modifications. - // Once MacFileSystemVirtualizer supports delete notifications we'll register for - // NotificationType.FileHandleClosedFileDeleted and this method will be called for both modifications and deletions. - Console.WriteLine($"OnFileModifiedOrDeleted: `{relativePath}`, isDirectory: {isDirectory}, isModfied: {isFileModified}, isDeleted: {isFileDeleted}, triggeringProcessId: {triggeringProcessId}, triggeringProcessImageFileName: {triggeringProcessImageFileName}"); - } - - private void OnFileRenamed( - string relativeSourcePath, - string relativeDestinationPath, - bool isDirectory, - uint triggeringProcessId, - string triggeringProcessImageFileName, - out NotificationType notificationMask) - { - notificationMask = NotificationType.UseExistingMask; - Console.WriteLine($"OnFileRenamed (isDirectory: {isDirectory}), relativeSourcePath: {relativeSourcePath}, relativeDestinationPath: {relativeDestinationPath}, triggeringProcessId: {triggeringProcessId}, triggeringProcessImageFileName: {triggeringProcessImageFileName}"); - } - - private void OnHardlinkCreated( - string relativeExistingFilePath, - string relativeNewLinkFilePath, - uint triggeringProcessId, - string triggeringProcessImageFileName) - { - Console.WriteLine($"OnHardlinkCreated, relativeExistingFilePath: {relativeExistingFilePath}, relativeNewLinkFilePath: {relativeNewLinkFilePath}, triggeringProcessId: {triggeringProcessId}, triggeringProcessImageFileName: {triggeringProcessImageFileName}"); - } - - private bool OnFilePreConvertToFull(string virtualPath, uint triggeringProcessId, string triggeringProcessImageFileName) - { - Console.WriteLine($"OnPreConvertToFull (virtualPath: {virtualPath}), triggeringProcessId: {triggeringProcessId}, triggeringProcessImageFileName: {triggeringProcessImageFileName}"); - return true; - } - - // TODO: Add this to the ProjFS API - private static HResult HResultFromWin32(int win32error) - { - // HRESULT_FROM_WIN32(unsigned long x) { return (HRESULT)(x) <= 0 ? (HRESULT)(x) : (HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000);} - - const int FacilityWin32 = 7; - return win32error <= 0 ? (HResult)win32error : (HResult)unchecked((win32error & 0x0000FFFF) | (FacilityWin32 << 16) | 0x80000000); - } - } -} diff --git a/MirrorProvider/MirrorProvider.Windows/packages.config b/MirrorProvider/MirrorProvider.Windows/packages.config deleted file mode 100644 index 20f77b72b3..0000000000 --- a/MirrorProvider/MirrorProvider.Windows/packages.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MirrorProvider/MirrorProvider.sln b/MirrorProvider/MirrorProvider.sln deleted file mode 100644 index b0f715af2f..0000000000 --- a/MirrorProvider/MirrorProvider.sln +++ /dev/null @@ -1,54 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27428.2011 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrjFSLib.Mac.Managed", "..\ProjFS.Mac\PrjFSLib.Mac.Managed\PrjFSLib.Mac.Managed.csproj", "{064685CA-00F6-41AB-A2AB-3350AEC4419C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MirrorProvider.Windows", "MirrorProvider.Windows\MirrorProvider.Windows.csproj", "{1ED51604-045C-4914-8054-15AAED90FF45}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MirrorProvider.Mac", "MirrorProvider.Mac\MirrorProvider.Mac.csproj", "{4BD96573-BE04-421B-B8C4-207956970136}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MirrorProvider", "MirrorProvider\MirrorProvider.csproj", "{128451A2-7598-4142-BA2A-9B963D91CCBC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug.Mac|x64 = Debug.Mac|x64 - Debug.Windows|x64 = Debug.Windows|x64 - Release.Mac|x64 = Release.Mac|x64 - Release.Windows|x64 = Release.Windows|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {064685CA-00F6-41AB-A2AB-3350AEC4419C}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {064685CA-00F6-41AB-A2AB-3350AEC4419C}.Debug.Mac|x64.Build.0 = Debug|x64 - {064685CA-00F6-41AB-A2AB-3350AEC4419C}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {064685CA-00F6-41AB-A2AB-3350AEC4419C}.Release.Mac|x64.ActiveCfg = Release|x64 - {064685CA-00F6-41AB-A2AB-3350AEC4419C}.Release.Mac|x64.Build.0 = Release|x64 - {064685CA-00F6-41AB-A2AB-3350AEC4419C}.Release.Windows|x64.ActiveCfg = Release|x64 - {1ED51604-045C-4914-8054-15AAED90FF45}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {1ED51604-045C-4914-8054-15AAED90FF45}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {1ED51604-045C-4914-8054-15AAED90FF45}.Debug.Windows|x64.Build.0 = Debug|x64 - {1ED51604-045C-4914-8054-15AAED90FF45}.Release.Mac|x64.ActiveCfg = Release|x64 - {1ED51604-045C-4914-8054-15AAED90FF45}.Release.Windows|x64.ActiveCfg = Release|x64 - {1ED51604-045C-4914-8054-15AAED90FF45}.Release.Windows|x64.Build.0 = Release|x64 - {4BD96573-BE04-421B-B8C4-207956970136}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {4BD96573-BE04-421B-B8C4-207956970136}.Debug.Mac|x64.Build.0 = Debug|x64 - {4BD96573-BE04-421B-B8C4-207956970136}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {4BD96573-BE04-421B-B8C4-207956970136}.Release.Mac|x64.ActiveCfg = Release|x64 - {4BD96573-BE04-421B-B8C4-207956970136}.Release.Mac|x64.Build.0 = Release|x64 - {4BD96573-BE04-421B-B8C4-207956970136}.Release.Windows|x64.ActiveCfg = Release|x64 - {128451A2-7598-4142-BA2A-9B963D91CCBC}.Debug.Mac|x64.ActiveCfg = Debug|x64 - {128451A2-7598-4142-BA2A-9B963D91CCBC}.Debug.Mac|x64.Build.0 = Debug|x64 - {128451A2-7598-4142-BA2A-9B963D91CCBC}.Debug.Windows|x64.ActiveCfg = Debug|x64 - {128451A2-7598-4142-BA2A-9B963D91CCBC}.Debug.Windows|x64.Build.0 = Debug|x64 - {128451A2-7598-4142-BA2A-9B963D91CCBC}.Release.Mac|x64.ActiveCfg = Release|x64 - {128451A2-7598-4142-BA2A-9B963D91CCBC}.Release.Mac|x64.Build.0 = Release|x64 - {128451A2-7598-4142-BA2A-9B963D91CCBC}.Release.Windows|x64.ActiveCfg = Release|x64 - {128451A2-7598-4142-BA2A-9B963D91CCBC}.Release.Windows|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {D4A658E3-D34D-4C17-9F26-1CCDC2EF33C1} - EndGlobalSection -EndGlobal diff --git a/MirrorProvider/MirrorProvider/CloneVerb.cs b/MirrorProvider/MirrorProvider/CloneVerb.cs deleted file mode 100644 index 088fcbf004..0000000000 --- a/MirrorProvider/MirrorProvider/CloneVerb.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.IO; -using CommandLine; - -namespace MirrorProvider -{ - [Verb("clone")] - public class CloneVerb - { - [Value( - 0, - Required = true, - MetaName = "Path to mirror", - HelpText = "The local path to mirror from")] - public string PathToMirror { get; set; } - - [Value( - 1, - Required = true, - MetaName = "Enlistment root", - HelpText = "The path to create the mirrored enlistment in")] - public string EnlistmentRoot { get; set; } - - public void Execute(FileSystemVirtualizer fileSystemVirtualizer) - { - Console.WriteLine($"Cloning from {Path.GetFullPath(this.PathToMirror)} to {Path.GetFullPath(this.EnlistmentRoot)}"); - - if (Directory.Exists(this.EnlistmentRoot)) - { - Console.WriteLine($"Error: Directory {this.EnlistmentRoot} already exists"); - return; - } - - Enlistment enlistment = Enlistment.CreateNewEnlistment(this.EnlistmentRoot, this.PathToMirror); - if (enlistment == null) - { - Console.WriteLine("Error: Unable to create enlistment"); - return; - } - - if (fileSystemVirtualizer.TryConvertVirtualizationRoot(enlistment.SrcRoot, out string error)) - { - Console.WriteLine($"Virtualization root created successfully at {enlistment.SrcRoot}"); - } - else - { - Console.WriteLine("Error: Failed to create virtualization root: " + error); - } - } - } -} diff --git a/MirrorProvider/MirrorProvider/Enlistment.cs b/MirrorProvider/MirrorProvider/Enlistment.cs deleted file mode 100644 index e1544764fa..0000000000 --- a/MirrorProvider/MirrorProvider/Enlistment.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.IO; - -namespace MirrorProvider -{ - public class Enlistment - { - private Enlistment(string root, string mirrorRoot) - { - this.EnlistmentRoot = root; - this.DotMirrorRoot = Path.Combine(root, ".mirror"); - this.SrcRoot = Path.Combine(root, "src"); - - this.ConfigFile = Path.Combine(this.DotMirrorRoot, "config"); - this.MirrorRoot = mirrorRoot; - } - - public string EnlistmentRoot { get; private set; } - public string DotMirrorRoot { get; private set; } - public string SrcRoot { get; private set; } - - public string ConfigFile { get; private set; } - - public string MirrorRoot { get; private set; } - - public static Enlistment CreateNewEnlistment(string enlistmentRoot, string mirrorRoot) - { - if (!Directory.Exists(enlistmentRoot) && Directory.Exists(mirrorRoot)) - { - Enlistment enlistment = new Enlistment(enlistmentRoot, mirrorRoot); - - Directory.CreateDirectory(enlistment.EnlistmentRoot); - Directory.CreateDirectory(enlistment.DotMirrorRoot); - Directory.CreateDirectory(enlistment.SrcRoot); - - File.WriteAllText(enlistment.ConfigFile, mirrorRoot); - return enlistment; - } - - return null; - } - - public static Enlistment LoadExistingEnlistment(string enlistmentRoot) - { - if (Directory.Exists(enlistmentRoot)) - { - Enlistment enlistment = new Enlistment(enlistmentRoot, null); - enlistment.MirrorRoot = File.ReadAllText(enlistment.ConfigFile); - - if (Directory.Exists(enlistment.MirrorRoot)) - { - return enlistment; - } - } - - return null; - } - } -} diff --git a/MirrorProvider/MirrorProvider/FileSystemResult.cs b/MirrorProvider/MirrorProvider/FileSystemResult.cs deleted file mode 100644 index fd568d4936..0000000000 --- a/MirrorProvider/MirrorProvider/FileSystemResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MirrorProvider -{ - public enum FileSystemResult - { - Success, - EFileNotFound, - EIOError, - } -} diff --git a/MirrorProvider/MirrorProvider/FileSystemVirtualizer.cs b/MirrorProvider/MirrorProvider/FileSystemVirtualizer.cs deleted file mode 100644 index 6c2a875b00..0000000000 --- a/MirrorProvider/MirrorProvider/FileSystemVirtualizer.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace MirrorProvider -{ - public abstract class FileSystemVirtualizer - { - protected Enlistment Enlistment { get; private set; } - - protected abstract StringComparison PathComparison { get; } - protected abstract StringComparer PathComparer { get; } - - public abstract bool TryConvertVirtualizationRoot(string directory, out string error); - public virtual bool TryStartVirtualizationInstance(Enlistment enlistment, out string error) - { - this.Enlistment = enlistment; - error = null; - return true; - } - - protected string GetFullPathInMirror(string relativePath) - { - return Path.Combine(this.Enlistment.MirrorRoot, relativePath); - } - - protected bool DirectoryExists(string relativePath) - { - string fullPathInMirror = this.GetFullPathInMirror(relativePath); - DirectoryInfo dirInfo = new DirectoryInfo(fullPathInMirror); - - return dirInfo.Exists; - } - - protected bool FileExists(string relativePath) - { - string fullPathInMirror = this.GetFullPathInMirror(relativePath); - FileInfo fileInfo = new FileInfo(fullPathInMirror); - - return fileInfo.Exists; - } - - protected ProjectedFileInfo GetFileInfo(string relativePath) - { - string fullPathInMirror = this.GetFullPathInMirror(relativePath); - string fullParentPath = Path.GetDirectoryName(fullPathInMirror); - string fileName = Path.GetFileName(relativePath); - - string actualCaseName; - ProjectedFileInfo.FileType type; - if (this.FileOrDirectoryExists(fullParentPath, fileName, out actualCaseName, out type)) - { - return new ProjectedFileInfo( - actualCaseName, - size: (type == ProjectedFileInfo.FileType.File) ? new FileInfo(fullPathInMirror).Length : 0, - type: type); - } - - return null; - } - - protected IEnumerable GetChildItems(string relativePath) - { - string fullPathInMirror = this.GetFullPathInMirror(relativePath); - DirectoryInfo dirInfo = new DirectoryInfo(fullPathInMirror); - - if (!dirInfo.Exists) - { - yield break; - } - - foreach (FileSystemInfo fileSystemInfo in dirInfo.GetFileSystemInfos()) - { - if ((fileSystemInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) - { - // While not 100% accurate on all platforms, for simplicity assume that if the the file has reparse data it's a symlink - yield return new ProjectedFileInfo( - fileSystemInfo.Name, - size: 0, - type: ProjectedFileInfo.FileType.SymLink); - } - else if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory) - { - yield return new ProjectedFileInfo( - fileSystemInfo.Name, - size: 0, - type: ProjectedFileInfo.FileType.Directory); - } - else - { - FileInfo fileInfo = fileSystemInfo as FileInfo; - yield return new ProjectedFileInfo( - fileInfo.Name, - fileInfo.Length, - ProjectedFileInfo.FileType.File); - } - - } - } - - protected FileSystemResult HydrateFile(string relativePath, int bufferSize, Func tryWriteBytes) - { - string fullPathInMirror = this.GetFullPathInMirror(relativePath); - if (!File.Exists(fullPathInMirror)) - { - return FileSystemResult.EFileNotFound; - } - - using (FileStream fs = new FileStream(fullPathInMirror, FileMode.Open, FileAccess.Read)) - { - long remainingData = fs.Length; - byte[] buffer = new byte[bufferSize]; - - while (remainingData > 0) - { - int bytesToCopy = (int)Math.Min(remainingData, buffer.Length); - if (fs.Read(buffer, 0, bytesToCopy) != bytesToCopy) - { - return FileSystemResult.EIOError; - } - - if (!tryWriteBytes(buffer, (uint)bytesToCopy)) - { - return FileSystemResult.EIOError; - } - - remainingData -= bytesToCopy; - } - } - - return FileSystemResult.Success; - } - - private bool FileOrDirectoryExists( - string fullParentPath, - string fileName, - out string actualCaseName, - out ProjectedFileInfo.FileType type) - { - actualCaseName = null; - type = ProjectedFileInfo.FileType.Invalid; - - DirectoryInfo dirInfo = new DirectoryInfo(fullParentPath); - if (!dirInfo.Exists) - { - return false; - } - - FileSystemInfo fileSystemInfo = - dirInfo - .GetFileSystemInfos() - .FirstOrDefault(fsInfo => fsInfo.Name.Equals(fileName, PathComparison)); - - if (fileSystemInfo == null) - { - return false; - } - - actualCaseName = fileSystemInfo.Name; - - if ((fileSystemInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) - { - type = ProjectedFileInfo.FileType.SymLink; - } - else if ((fileSystemInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory) - { - type = ProjectedFileInfo.FileType.Directory; - } - else - { - type = ProjectedFileInfo.FileType.File; - } - - return true; - } - } -} diff --git a/MirrorProvider/MirrorProvider/MirrorProvider.csproj b/MirrorProvider/MirrorProvider/MirrorProvider.csproj deleted file mode 100644 index 6ae314baad..0000000000 --- a/MirrorProvider/MirrorProvider/MirrorProvider.csproj +++ /dev/null @@ -1,36 +0,0 @@ - - - - netstandard2.0 - x64 - Debug;Release - - - - ..\..\..\BuildOutput - - - - - $(BuildOutputDir)\MirrorProvider\obj\$(Configuration)\$(Platform) - $(BuildOutputDir)\MirrorProvider\bin\$(Configuration)\$(Platform) - true - - - - true - full - false - TRACE;DEBUG - - - - true - TRACE;RELEASE - - - - - - - diff --git a/MirrorProvider/MirrorProvider/MirrorProviderCLI.cs b/MirrorProvider/MirrorProvider/MirrorProviderCLI.cs deleted file mode 100644 index 3fc12e92b9..0000000000 --- a/MirrorProvider/MirrorProvider/MirrorProviderCLI.cs +++ /dev/null @@ -1,32 +0,0 @@ -using CommandLine; -using System; -using System.Linq; - -namespace MirrorProvider -{ - public static class MirrorProviderCLI - { - public static void Run(string[] args, FileSystemVirtualizer fileSystemVirtualizer) - { - new Parser( - settings => - { - settings.CaseSensitive = false; - settings.EnableDashDash = true; - settings.IgnoreUnknownArguments = false; - settings.HelpWriter = Console.Error; - }) - .ParseArguments(args, typeof(CloneVerb), typeof(MountVerb)) - .WithNotParsed( - errors => - { - if (errors.Any(error => error is TokenError)) - { - Environment.Exit(1); - } - }) - .WithParsed(clone => clone.Execute(fileSystemVirtualizer)) - .WithParsed(mount => mount.Execute(fileSystemVirtualizer)); - } - } -} diff --git a/MirrorProvider/MirrorProvider/MountVerb.cs b/MirrorProvider/MirrorProvider/MountVerb.cs deleted file mode 100644 index 4c271ddc8f..0000000000 --- a/MirrorProvider/MirrorProvider/MountVerb.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.IO; -using CommandLine; - -namespace MirrorProvider -{ - [Verb("mount")] - public class MountVerb - { - private Enlistment enlistment; - - [Value( - 0, - Required = true, - MetaName = "Enlistment root", - HelpText = "The path to create the mirrored enlistment in")] - public string EnlistmentRoot { get; set; } - - public void Execute(FileSystemVirtualizer fileSystemVirtualizer) - { - this.enlistment = Enlistment.LoadExistingEnlistment(this.EnlistmentRoot); - if (this.enlistment == null) - { - Console.WriteLine("Error: Unable to load enlistment"); - } - - Console.WriteLine(); - Console.WriteLine($"Mounting {Path.GetFullPath(this.enlistment.EnlistmentRoot)}"); - - if (fileSystemVirtualizer.TryStartVirtualizationInstance(this.enlistment, out string error)) - { - Console.WriteLine("Virtualization instance started successfully"); - - Console.WriteLine("Press Enter to end the instance"); - Console.ReadLine(); - } - else - { - Console.WriteLine("Virtualization instance failed to start: " + error); - } - } - } -} diff --git a/MirrorProvider/MirrorProvider/ProjectedFileInfo.cs b/MirrorProvider/MirrorProvider/ProjectedFileInfo.cs deleted file mode 100644 index 71269ad2eb..0000000000 --- a/MirrorProvider/MirrorProvider/ProjectedFileInfo.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace MirrorProvider -{ - public class ProjectedFileInfo - { - public ProjectedFileInfo(string name, long size, FileType type) - { - this.Name = name; - this.Size = size; - this.Type = type; - } - - public enum FileType - { - Invalid, - - File, - Directory, - SymLink - - } - - public string Name { get; } - public long Size { get; } - public FileType Type { get; } - public bool IsDirectory => this.Type == FileType.Directory; - } -} diff --git a/MirrorProvider/Scripts/Mac/Build.sh b/MirrorProvider/Scripts/Mac/Build.sh deleted file mode 100755 index 94617466d2..0000000000 --- a/MirrorProvider/Scripts/Mac/Build.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -CONFIGURATION=$1 -if [ -z $CONFIGURATION ]; then - CONFIGURATION=Debug -fi - -SCRIPTDIR=$(dirname ${BASH_SOURCE[0]}) - -SRCDIR=$SCRIPTDIR/../../.. -ROOTDIR=$SRCDIR/.. -SLN=$SRCDIR/MirrorProvider/MirrorProvider.sln - -# Build the ProjFS kext and libraries -$SRCDIR/ProjFS.Mac/Scripts/Build.sh $CONFIGURATION - -# If we're building the Profiling(Release) configuration, remove Profiling() for building .NET code -if [ "$CONFIGURATION" == "Profiling(Release)" ]; then - CONFIGURATION=Release -fi - -# Build the MirrorProvider -dotnet restore $SLN /p:Configuration="$CONFIGURATION.Mac" --packages $ROOTDIR/packages -dotnet build $SLN --configuration $CONFIGURATION.Mac diff --git a/MirrorProvider/Scripts/Mac/MirrorProvider_Clone.sh b/MirrorProvider/Scripts/Mac/MirrorProvider_Clone.sh deleted file mode 100755 index ad73a8ca90..0000000000 --- a/MirrorProvider/Scripts/Mac/MirrorProvider_Clone.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -CONFIGURATION=$1 -if [ -z $CONFIGURATION ]; then - CONFIGURATION=Debug -fi - -SCRIPTDIR=$(dirname ${BASH_SOURCE[0]}) -BUILDDIR=$SCRIPTDIR/../../../../BuildOutput/MirrorProvider.Mac/bin/$CONFIGURATION/x64/netcoreapp2.1 - -dotnet $BUILDDIR/MirrorProvider.Mac.dll clone ~/PathToMirror ~/TestRoot diff --git a/MirrorProvider/Scripts/Mac/MirrorProvider_Mount.sh b/MirrorProvider/Scripts/Mac/MirrorProvider_Mount.sh deleted file mode 100755 index 875a3e7ea1..0000000000 --- a/MirrorProvider/Scripts/Mac/MirrorProvider_Mount.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -CONFIGURATION=$1 -if [ -z $CONFIGURATION ]; then - CONFIGURATION=Debug -fi - -SCRIPTDIR=$(dirname ${BASH_SOURCE[0]}) -BUILDDIR=$SCRIPTDIR/../../../../BuildOutput/MirrorProvider.Mac/bin/$CONFIGURATION/x64/netcoreapp2.1 - -dotnet $BUILDDIR/MirrorProvider.Mac.dll mount ~/TestRoot diff --git a/MirrorProvider/Scripts/Windows/Build.bat b/MirrorProvider/Scripts/Windows/Build.bat deleted file mode 100644 index f012b577bc..0000000000 --- a/MirrorProvider/Scripts/Windows/Build.bat +++ /dev/null @@ -1,14 +0,0 @@ -IF "%1"=="" (SET "Configuration=Debug") ELSE (SET "Configuration=%1") - -SET SRC=%~dp0\..\..\.. -SET ROOT=%SRC%\.. -SET SLN=%SRC%\MirrorProvider\MirrorProvider.sln - -FOR /F "tokens=* USEBACKQ" %%F IN (`where nuget.exe`) DO ( - SET nuget=%%F - ECHO Found nuget.exe at '%%F' -) - -%nuget% restore %SLN% -dotnet restore %SLN% /p:Configuration="%Configuration%.Windows" --packages %ROOT%\packages -dotnet build %SLN% --configuration %Configuration%.Windows \ No newline at end of file diff --git a/MirrorProvider/Scripts/Windows/MirrorProvider_Clone.bat b/MirrorProvider/Scripts/Windows/MirrorProvider_Clone.bat deleted file mode 100644 index c579f0d0c0..0000000000 --- a/MirrorProvider/Scripts/Windows/MirrorProvider_Clone.bat +++ /dev/null @@ -1,4 +0,0 @@ -SET BUILDDIR=%~dp0\..\..\..\..\BuildOutput\MirrorProvider.Windows\bin\x64\Debug - -%BUILDDIR%\MirrorProvider.Windows.exe clone \PathToMirror \TestRoot - diff --git a/MirrorProvider/Scripts/Windows/MirrorProvider_Mount.bat b/MirrorProvider/Scripts/Windows/MirrorProvider_Mount.bat deleted file mode 100644 index e8c4f2350c..0000000000 --- a/MirrorProvider/Scripts/Windows/MirrorProvider_Mount.bat +++ /dev/null @@ -1,4 +0,0 @@ -SET BUILDDIR=%~dp0\..\..\..\..\BuildOutput\MirrorProvider.Windows\bin\x64\Debug - -%BUILDDIR%\MirrorProvider.Windows.exe mount \TestRoot - diff --git a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.pbxproj b/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.pbxproj deleted file mode 100644 index 7f8421550b..0000000000 --- a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.pbxproj +++ /dev/null @@ -1,310 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 4A6A654B22C227CA00E207E2 /* libEndpointSecurity.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6A654A22C227CA00E207E2 /* libEndpointSecurity.tbd */; }; - 4A6A654F22C4A8BE00E207E2 /* EndpointSecurityMirror.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A6A654E22C4A8BE00E207E2 /* EndpointSecurityMirror.cpp */; }; - 4A6A656922C4C33200E207E2 /* libbsm.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A6A656822C4C32B00E207E2 /* libbsm.tbd */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 4A6A653D22C227BA00E207E2 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 4A6A653F22C227BA00E207E2 /* EndpointSecurityMirror */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = EndpointSecurityMirror; sourceTree = BUILT_PRODUCTS_DIR; }; - 4A6A654A22C227CA00E207E2 /* libEndpointSecurity.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libEndpointSecurity.tbd; path = usr/lib/libEndpointSecurity.tbd; sourceTree = SDKROOT; }; - 4A6A654C22C22E9500E207E2 /* EndpointSecurityMirror.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = EndpointSecurityMirror.entitlements; sourceTree = ""; }; - 4A6A654E22C4A8BE00E207E2 /* EndpointSecurityMirror.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EndpointSecurityMirror.cpp; sourceTree = ""; }; - 4A6A655022C4A9E000E207E2 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 4A6A656822C4C32B00E207E2 /* libbsm.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbsm.tbd; path = usr/lib/libbsm.tbd; sourceTree = SDKROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 4A6A653C22C227BA00E207E2 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4A6A654B22C227CA00E207E2 /* libEndpointSecurity.tbd in Frameworks */, - 4A6A656922C4C33200E207E2 /* libbsm.tbd in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 4A6A653622C227BA00E207E2 = { - isa = PBXGroup; - children = ( - 4A6A654122C227BA00E207E2 /* EndpointSecurityMirror */, - 4A6A654022C227BA00E207E2 /* Products */, - 4A6A654922C227CA00E207E2 /* Frameworks */, - ); - sourceTree = ""; - }; - 4A6A654022C227BA00E207E2 /* Products */ = { - isa = PBXGroup; - children = ( - 4A6A653F22C227BA00E207E2 /* EndpointSecurityMirror */, - ); - name = Products; - sourceTree = ""; - }; - 4A6A654122C227BA00E207E2 /* EndpointSecurityMirror */ = { - isa = PBXGroup; - children = ( - 4A6A654C22C22E9500E207E2 /* EndpointSecurityMirror.entitlements */, - 4A6A654E22C4A8BE00E207E2 /* EndpointSecurityMirror.cpp */, - 4A6A655022C4A9E000E207E2 /* README.md */, - ); - path = EndpointSecurityMirror; - sourceTree = ""; - }; - 4A6A654922C227CA00E207E2 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 4A6A656822C4C32B00E207E2 /* libbsm.tbd */, - 4A6A654A22C227CA00E207E2 /* libEndpointSecurity.tbd */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 4A6A653E22C227BA00E207E2 /* EndpointSecurityMirror */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4A6A654622C227BA00E207E2 /* Build configuration list for PBXNativeTarget "EndpointSecurityMirror" */; - buildPhases = ( - 4A6A653B22C227BA00E207E2 /* Sources */, - 4A6A653C22C227BA00E207E2 /* Frameworks */, - 4A6A653D22C227BA00E207E2 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = EndpointSecurityMirror; - productName = EndpointSecurityMirror; - productReference = 4A6A653F22C227BA00E207E2 /* EndpointSecurityMirror */; - productType = "com.apple.product-type.tool"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 4A6A653722C227BA00E207E2 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1110; - ORGANIZATIONNAME = "Microsoft Corporation"; - TargetAttributes = { - 4A6A653E22C227BA00E207E2 = { - CreatedOnToolsVersion = 11.0; - }; - }; - }; - buildConfigurationList = 4A6A653A22C227BA00E207E2 /* Build configuration list for PBXProject "EndpointSecurityMirror" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 4A6A653622C227BA00E207E2; - productRefGroup = 4A6A654022C227BA00E207E2 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 4A6A653E22C227BA00E207E2 /* EndpointSecurityMirror */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 4A6A653B22C227BA00E207E2 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4A6A654F22C4A8BE00E207E2 /* EndpointSecurityMirror.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 4A6A654422C227BA00E207E2 /* 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; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - 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.14; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - }; - name = Debug; - }; - 4A6A654522C227BA00E207E2 /* 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; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - 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.14; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = macosx; - }; - name = Release; - }; - 4A6A654722C227BA00E207E2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = EndpointSecurityMirror/EndpointSecurityMirror.entitlements; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = PLT8TQHCAJ; - ENABLE_HARDENED_RUNTIME = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.mirrorprovider.endpointsecurityprototype; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 4A6A654822C227BA00E207E2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = EndpointSecurityMirror/EndpointSecurityMirror.entitlements; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = PLT8TQHCAJ; - ENABLE_HARDENED_RUNTIME = YES; - MACOSX_DEPLOYMENT_TARGET = 10.15; - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.mirrorprovider.endpointsecurityprototype; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 4A6A653A22C227BA00E207E2 /* Build configuration list for PBXProject "EndpointSecurityMirror" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4A6A654422C227BA00E207E2 /* Debug */, - 4A6A654522C227BA00E207E2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4A6A654622C227BA00E207E2 /* Build configuration list for PBXNativeTarget "EndpointSecurityMirror" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4A6A654722C227BA00E207E2 /* Debug */, - 4A6A654822C227BA00E207E2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 4A6A653722C227BA00E207E2 /* Project object */; -} diff --git a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index f116d98d8d..0000000000 --- a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/xcshareddata/xcschemes/EndpointSecurityMirror.xcscheme b/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/xcshareddata/xcschemes/EndpointSecurityMirror.xcscheme deleted file mode 100644 index 575fa5062c..0000000000 --- a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror.xcodeproj/xcshareddata/xcschemes/EndpointSecurityMirror.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/EndpointSecurityMirror.cpp b/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/EndpointSecurityMirror.cpp deleted file mode 100644 index b5fdbbaba2..0000000000 --- a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/EndpointSecurityMirror.cpp +++ /dev/null @@ -1,511 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using std::string; -using std::mutex; -using std::unordered_map; -using std::vector; -using std::atomic_uint; -using std::extent; - -typedef std::lock_guard Guard; - -// Local function declarations - -static void HandleSecurityEvent( - es_client_t* _Nonnull client, const es_message_t* _Nonnull message); -static int RecursiveEnumerationCopyfileStatusCallback( - int what, int stage, copyfile_state_t state, const char * src, const char * dst, void * ctx); -static void HydrateFileOrAwaitHydration(string eventPath, const es_message_t* message); -static void HydrateFile(string eventPath, es_message_t* message); -static const char* CopyfileWhatString(int what) __attribute__((unused)); -static const char* CopyfileStageString(int stage) __attribute__((unused)); - -// Global/static variable definitions - -static pid_t selfpid; -static constexpr const char* EmptyFileXattr = "org.vfsforgit.endpointsecuritymirror.emptyfile"; - -static es_client_t* client = nullptr; -static dispatch_queue_t s_hydrationQueue = nullptr; -static mutex s_hydrationMutex; -static unordered_map> s_waitingFileHydrationMessages; - -static string s_sourcePrefix, s_targetPrefix; - -static atomic_uint s_pendingAuthCount(0); -static mach_timebase_info_data_t s_machTimebase; - -// Helper functions - -static uint64_t usecFromMachDuration(uint64_t machDuration) -{ - // timebase gives ns, divide by 1000 for usec - return ((machDuration * s_machTimebase.numer) / s_machTimebase.denom) / 1000u; -} - -static int64_t usecFromMachDuration(int64_t machDuration) -{ - return ((machDuration * s_machTimebase.numer) / s_machTimebase.denom) / 1000; -} - -static const char* FilenameFromPath(const char* path) -{ - const char* lastSlash = std::strrchr(path, '/'); - if (lastSlash == nullptr) - { - return path; - } - else - { - return lastSlash + 1; - } -} - -static bool PathLiesWithinTarget(const char* path) -{ - return 0 == strncmp(s_targetPrefix.c_str(), path, s_targetPrefix.length()); -} - -static string SourcePathForTargetFile(const char* targetPath) -{ - return s_sourcePrefix + (targetPath + s_targetPrefix.length()); -} - - -// Helper function for making "what" argument to copyfile callback human-readable -static const char* CopyfileWhatString(int what) -{ - switch (what) - { - case COPYFILE_RECURSE_FILE: - return "COPYFILE_RECURSE_FILE"; - case COPYFILE_RECURSE_DIR: - return "COPYFILE_RECURSE_DIR"; - case COPYFILE_RECURSE_DIR_CLEANUP: - return "COPYFILE_RECURSE_DIR_CLEANUP"; - case COPYFILE_RECURSE_ERROR: - return "COPYFILE_RECURSE_ERROR"; - default: - return "???"; - } -} - -// Helper function for making "stage" argument to copyfile callback human-readable -static const char* CopyfileStageString(int stage) -{ - switch (stage) - { - case COPYFILE_START: - return "COPYFILE_START"; - case COPYFILE_FINISH: - return "COPYFILE_FINISH"; - case COPYFILE_ERR: - return "COPYFILE_ERR"; - default: - return "???"; - } -} - -// - -int main(int argc, const char* argv[]) -{ - selfpid = getpid(); - mach_timebase_info(&s_machTimebase); // required for subsequent mach time <-> nsec/usec conversions - - if (argc < 3) - { - fprintf(stderr, "Run as: %s \n", FilenameFromPath(argv[0])); - return 1; - } - - // The dispatch queue used for processing hydration requests. - // Note: concurrent, i.e. multithreaded. - s_hydrationQueue = dispatch_queue_create("org.vfsforgit.endpointsecuritymirror.hydrationqueue", DISPATCH_QUEUE_CONCURRENT); - - // Sanity check on the mirror source: must exist, must be a directory - const char* const sourceDir = argv[1]; - struct stat sourceDirStat = {}; - if (0 != stat(sourceDir, &sourceDirStat)) - { - perror("stat() on source directory failed"); - return 1; - } - - if (!S_ISDIR(sourceDirStat.st_mode)) - { - fprintf(stderr, "Source (%s) is not a directory.\n", sourceDir); - return 1; - } - - // If the target directory exists, assume it has already been populated with - // placeholders. If not, perform recursive enumeration. - const char* const targetDir = argv[2]; - int targetDirFD = open(targetDir, O_DIRECTORY | O_RDONLY); - if (targetDirFD < 0) - { - if (errno == ENOENT) - { - copyfile_state_t copyState = copyfile_state_alloc(); - // This callback function will be called multiple times on every directory - // and file encountered during recursive descent into the source directory - // Check copyfile manpage for details. - // We use the callback to set the EmptyFileXattr xattr and truncate - // the file to the correct length. - copyfile_state_set(copyState, COPYFILE_STATE_STATUS_CB, reinterpret_cast(&RecursiveEnumerationCopyfileStatusCallback)); - // Note that the COPYFILE_DATA flag is NOT set; this would copy the file - // contents, but we want empty placeholders. - int result = copyfile(sourceDir, targetDir, copyState, COPYFILE_METADATA | COPYFILE_RECURSIVE | COPYFILE_NOFOLLOW_SRC | COPYFILE_NOFOLLOW_DST); - if (result != 0) - { - perror("copyfile() for enumeration failed"); - } - } - else - { - perror("open() on target directory failed"); - return 1; - } - } - else - { - close(targetDirFD); - } - - // Ensure we have paths with trailing slashes for both source and target, so - // that simple string prefix tests will suffice from here on out. - char* absoluteSourceDir = realpath(sourceDir, nullptr); - s_sourcePrefix = absoluteSourceDir; - free(absoluteSourceDir); - if (s_sourcePrefix[s_sourcePrefix.length() - 1] != '/') - s_sourcePrefix.append("/"); - - char* absoluteTargetDir = realpath(targetDir, nullptr); - s_targetPrefix = absoluteTargetDir; - free(absoluteTargetDir); - if (s_targetPrefix[s_targetPrefix.length() - 1] != '/') - s_targetPrefix.append("/"); - - - printf("Starting up with source '%s', target '%s'\n", s_sourcePrefix.c_str(), s_targetPrefix.c_str()); - - // Perform the EndpointSecurity start-up: - // Create client object, clear cache, and subscribe to events we're interested in. - es_new_client_result_t result = es_new_client( - &client, - ^(es_client_t* _Nonnull client, const es_message_t* _Nonnull message) - { - std::atomic_fetch_add(&s_pendingAuthCount, 1u); - HandleSecurityEvent(client, message); - }); - if (result != ES_NEW_CLIENT_RESULT_SUCCESS) - { - fprintf(stderr, "es_new_client failed, error = %u\n", result); - return 1; - } - - es_clear_cache(client); // This may no longer be necessary; without it, early macOS 10.15 betas would drop events. - - es_event_type_t subscribe_events[] = { ES_EVENT_TYPE_AUTH_OPEN, ES_EVENT_TYPE_NOTIFY_LOOKUP }; - if (ES_RETURN_SUCCESS != es_subscribe(client, subscribe_events, extent::value)) - { - fprintf(stderr, "es_subscribe failed\n"); - return 1; - } - - // Handle events until process is killed - dispatch_main(); -} - -static void HandleSecurityEvent( - es_client_t* _Nonnull client, const es_message_t* _Nonnull message) -{ - if (message->action_type == ES_ACTION_TYPE_AUTH) - { - if (message->event_type == ES_EVENT_TYPE_AUTH_OPEN) - { - pid_t pid = audit_token_to_pid(message->process->audit_token); - if (pid == selfpid) - { - printf("Muting events from self (pid %d)\n", pid); - es_mute_process(client, &message->process->audit_token); - es_respond_result_t result = es_respond_flags_result(client, message, 0x7fffffff, false /* don't cache */); - assert(result == ES_RESPOND_RESULT_SUCCESS); - std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - return; - } - - const char* eventPath = message->event.open.file->path.data; - if (PathLiesWithinTarget(eventPath)) - { - char xattrBuffer[16]; - ssize_t xattrBytes = getxattr(eventPath, EmptyFileXattr, xattrBuffer, sizeof(xattrBuffer), 0 /* offset */, 0 /* options */); - if (xattrBytes >= 0) - { - // If we end up here, the event path lies within the mirror target, - // and the file is empty and thus needs hydrating before the open() - // call may proceed. - - const char* processFilename = - (message->process && message->process->executable && message->process->executable->path.data) - ? FilenameFromPath(message->process->executable->path.data) - : nullptr; - if (processFilename != nullptr && - (0 == strcmp("mdworker_shared", processFilename) - || 0 == strcmp("mds", processFilename))) - { - // Don't allow crawler processes to hydrate, so fail the open() call. - - //printf("Denying crawler process %u (%s) access to empty file '%s'\n", audit_token_to_pid(message->proc.audit_token), processFilename, eventPath); - es_respond_result_t result = es_respond_flags_result(client, message, 0x0, false /* don't cache */); - assert(result == ES_RESPOND_RESULT_SUCCESS); - unsigned count = std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - if (count != 1) - { - printf("In-flight authorisation requests pending: %u\n", count - 1); - } - } - else - { - // Request hydration - printf("Hydration event to '%s' caused by '%s'\n", eventPath, processFilename); - HydrateFileOrAwaitHydration(eventPath, message); - } - return; - } - else - { - // File already hydrated - es_respond_result_t result = es_respond_flags_result(client, message, 0x7fffffff, false /* don't cache */); - assert(result == ES_RESPOND_RESULT_SUCCESS); - unsigned count = std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - if (count != 1) - { - printf("In-flight authorisation requests pending: %u\n", count - 1); - } - return; - } - } - else if (errno != ENOATTR && PathLiesWithinTarget(eventPath)) - { - fprintf(stderr, "getxattr failed (%u, %s) on '%s' (within mirror target directory)\n", - errno, strerror(errno), eventPath); - } - - unsigned count = std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - if (count != 1) - { - printf("In-flight authorisation requests pending: %u\n", count - 1); - } - es_respond_result_t result = es_respond_flags_result(client, message, 0x7fffffff, false /* don't cache */); - assert(result == ES_RESPOND_RESULT_SUCCESS); - } - else - { - fprintf(stderr, "Unexpected event type: %u\n", message->event_type); - } - } - else if (message->action_type == ES_ACTION_TYPE_NOTIFY) - { - if (message->event_type == ES_EVENT_TYPE_NOTIFY_LOOKUP) - { - pid_t pid = audit_token_to_pid(message->process->audit_token); - if (pid != selfpid) - { - const char* eventPath = message->event.lookup.source_dir->path.data; - if (PathLiesWithinTarget(eventPath)) - { - printf("ES_EVENT_TYPE_NOTIFY_LOOKUP event for item '%s' in path '%s'\n", message->event.lookup.relative_target.data, eventPath); - } - } - - std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - return; - } - } - else - { - printf("Unexpected action type: %u, event type: %u\n", message->action_type, message->event_type); - unsigned count = std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - if (count != 1) - { - printf("In-flight authorisation requests pending: %u\n", count - 1); - } - } -} - -static void HydrateFileOrAwaitHydration(string eventPath, const es_message_t* message) -{ - es_message_t* messageCopy = es_copy_message(message); - Guard lock(s_hydrationMutex); - if (!s_waitingFileHydrationMessages.insert(make_pair(eventPath, vector())).second) - { - // already being hydrated, add to messages needing approval - printf("File '%s' already being hydrated by another thread\n", eventPath.c_str()); - s_waitingFileHydrationMessages[eventPath].push_back(messageCopy); - } - else - { - dispatch_async(s_hydrationQueue, ^{ - HydrateFile(eventPath, messageCopy); - }); - } -} - -static void HydrateFile(string eventPath, es_message_t* messageCopy) -{ - // Re-check xattr, file might already be hydrated. (defend against TOCTOU) - char xattrBuffer[16]; - ssize_t xattrBytes = getxattr(eventPath.c_str(), EmptyFileXattr, xattrBuffer, sizeof(xattrBuffer), 0 /* offset */, 0 /* options */); - if (xattrBytes < 0) - { - // Raced with other thread, hydration already done - unsigned count = std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - if (count != 1) - { - printf("In-flight authorisation requests pending: %u\n", count - 1); - } - es_respond_result_t result = es_respond_flags_result(client, messageCopy, 0x7fffffff, false /* don't cache */); - assert(result == ES_RESPOND_RESULT_SUCCESS); - return; - } - - string sourcePath = SourcePathForTargetFile(eventPath.c_str()); - - // This can be used for simulating slow hydrations: - // NOTE: if you increase this beyond 60000ms (1 minute) the process ends up being killed - unsigned delay_ms = 0;//random() % 60000u; - //printf("Hydrating '%s' -> '%s' for process %u (%s), with %u ms delay\n", sourcePath.c_str(), eventPath.c_str(), audit_token_to_pid(messageCopy->proc.audit_token), FilenameFromPath(messageCopy->proc.file.path.data), delay_ms); - usleep(delay_ms * 1000u); - - // Enumeration copied metadata, hydration copies data - int result = copyfile(sourcePath.c_str(), eventPath.c_str(), nullptr /* state */, COPYFILE_DATA); - - // This bitfield is required for responding to the open() authorisation. - // Not 100% sure what the bits stand for, but they are probably the - // FREAD/FWRITE flags, and the various O_* constants you can pass to open() - // Note that this value may be cached, so we need to authorise any option - // bits we might want to allow even if they are not requested for this - // specific open() call (in messageCopy->event.open.fflag) - uint32_t responseFlags = 0x7fffffff; - if (result == 0) - { - result = removexattr(eventPath.c_str(), EmptyFileXattr, 0 /* options */); - if (result != 0) - { - perror("removexattr failed"); - } - } - else - { - perror("hydration copyfile failed"); - responseFlags = 0x0; // This means deny the open() call - } - - // Sanity counter we use to ensure every auth callback is matched by a es_respond_flags_result() - unsigned count = std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - if (count != 1) - { - // This will occasionally print something when there are multiple callbacks outstanding. - // No indication of problems if the actual count keeps hovering around 1. - printf("In-flight authorisation requests pending: %u\n", count - 1); - } - - // Tell ES to allow the open() call where this hydration request originated. - es_respond_result_t response_result = es_respond_flags_result(client, messageCopy, responseFlags, false /* don't cache */); - assert(response_result == ES_RESPOND_RESULT_SUCCESS); - - // Calculation of remaining time to deadline (so we can see how close we are to having our process killed) - uint64_t responseMachTime = mach_absolute_time(); - uint64_t responseMachDuration = responseMachTime - messageCopy->mach_time; - int64_t responseMachDeadlineDelta = responseMachTime - messageCopy->deadline; - - printf("Hydrating '%s' done; response took %llu µs, %lld µs %s deadline; triggered by access from process %d ('%s')\n", - eventPath.c_str(), - usecFromMachDuration(responseMachDuration), - std::abs(usecFromMachDuration(responseMachDeadlineDelta)), - responseMachDeadlineDelta <= 0 ? "before" : "after", - audit_token_to_pid(messageCopy->process->audit_token), - messageCopy->process ? messageCopy->process->executable->path.data : "[NULL]"); - - es_free_message(messageCopy); - - - // Now respond in the same way to any other open() calls waiting for this file to be hydrated. - vector waitingMessages; - - { - // Atomically remove the list of waiting messages from the global map indexed by filename - Guard lock(s_hydrationMutex); - s_waitingFileHydrationMessages[eventPath].swap(waitingMessages); - s_waitingFileHydrationMessages.erase(eventPath); - } - - if (!waitingMessages.empty()) - printf("Responding to %zu other auth events for file '%s'\n", waitingMessages.size(), eventPath.c_str()); - - while (!waitingMessages.empty()) - { - es_message_t* waitingMessage = waitingMessages.back(); - waitingMessages.pop_back(); - - es_respond_result_t response_result = es_respond_flags_result(client, waitingMessage, responseFlags, false /* don't cache */); - assert(response_result == ES_RESPOND_RESULT_SUCCESS); - es_free_message(waitingMessage); - - unsigned count = std::atomic_fetch_sub(&s_pendingAuthCount, 1u); - if (count != 1) - { - printf("In-flight authorisation requests pending: %u\n", count - 1); - } - } -} - -static int RecursiveEnumerationCopyfileStatusCallback( - int what, int stage, copyfile_state_t state, const char* src, const char* dst, void* ctx) -{ - //printf("copyfile callback: what = %s, stage = %s, src = '%s', dst = '%s'\n", CopyfileWhatString(what), CopyfileStageString(stage), src, dst); - - if (what == COPYFILE_RECURSE_FILE && stage == COPYFILE_FINISH) - { - struct stat destStat = {}; - struct stat srcStat = {}; - if (0 != fstatat(AT_FDCWD, src, &srcStat, AT_SYMLINK_NOFOLLOW)) - { - fprintf(stderr, "stat() on source file '%s' failed: %d, %s\n", src, errno, strerror(errno)); - } - else if (0 != fstatat(AT_FDCWD, dst, &destStat, AT_SYMLINK_NOFOLLOW)) - { - fprintf(stderr, "stat() on destination file '%s' failed: %d, %s\n", dst, errno, strerror(errno)); - } - else if (S_ISREG(srcStat.st_mode)) - { - if (0 != setxattr(dst, EmptyFileXattr, "", 0 /* size */, 0 /* offset */, 0 /* options */)) - { - perror("setxattr failed: "); - } - //printf("truncate('%s', %llu)\n", dst, srcStat.st_size); - if (0 != truncate(dst, srcStat.st_size)) - { - fprintf(stderr, "truncate() on '%s', %llu bytes failed: %d, %s\n", src, srcStat.st_size, errno, strerror(errno)); - } - } - } - return COPYFILE_CONTINUE; -} - diff --git a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/EndpointSecurityMirror.entitlements b/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/EndpointSecurityMirror.entitlements deleted file mode 100644 index 46a73e7436..0000000000 --- a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/EndpointSecurityMirror.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.developer.endpoint-security.client - - - diff --git a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/README.md b/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/README.md deleted file mode 100644 index 887b0674da..0000000000 --- a/ProjFS.Mac/EndpointSecurityPrototype/EndpointSecurityMirror/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# EndpointSecurity Mirror Provider Prototype - -This uses the new EndpointSecurity API in macOS 10.15 Catalina to detect accesses to empty placeholder files and hydrates them on demand. - -Requires root privileges to run. I don't recommend running it in a debugger, as this can end up locking up the system. - -To run: - - sudo ./EndpointSecurityMirror path/to/source-directory path/to/mirror/target-directory - -Paths can be relative. If the target does not exist, its parent directory must exist; in this case the target directory will be recursively enumerated (filled with empty placeholders). - -There is very little error handling at the moment. Empty placeholders are marked using the `org.vfsforgit.endpointsecuritymirror.emptyfile` xattr. If such a file is opened by a process while this provider is running, the provider will aim to hydrate it before allowing the other process to continue. diff --git a/ProjFS.Mac/PrjFS.xcodeproj/project.pbxproj b/ProjFS.Mac/PrjFS.xcodeproj/project.pbxproj deleted file mode 100644 index 4d8e4462bc..0000000000 --- a/ProjFS.Mac/PrjFS.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1770 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXAggregateTarget section */ - 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 26A7DE95229363EA004F6252 /* Build configuration list for PBXAggregateTarget "GeneratePrjFSVersionFiles" */; - buildPhases = ( - 26A7DE96229363F2004F6252 /* ShellScript */, - ); - dependencies = ( - ); - name = GeneratePrjFSVersionFiles; - productName = GeneratePrjFSVersion; - }; - 4391F90021E436210008103C /* Build All */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 4391F90121E436210008103C /* Build configuration list for PBXAggregateTarget "Build All" */; - buildPhases = ( - ); - dependencies = ( - 4A08257521E77BEB00E21AFD /* PBXTargetDependency */, - 4391F90521E4362B0008103C /* PBXTargetDependency */, - 4391F90721E4362B0008103C /* PBXTargetDependency */, - 4391F90921E4362B0008103C /* PBXTargetDependency */, - ); - name = "Build All"; - productName = "Build All"; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 264758C921EFBA8B0095B9F8 /* VnodeCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264758C721EFBA8B0095B9F8 /* VnodeCache.cpp */; }; - 264758CA21EFBA8B0095B9F8 /* VnodeCache.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 264758C821EFBA8B0095B9F8 /* VnodeCache.hpp */; }; - 264758CC21FA709B0095B9F8 /* VnodeCacheTestable.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 264758CB21FA709B0095B9F8 /* VnodeCacheTestable.hpp */; }; - 264758CE21FA71140095B9F8 /* VnodeCacheTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 264758CD21FA71140095B9F8 /* VnodeCacheTests.mm */; }; - 264758CF21FA71E10095B9F8 /* VnodeCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 264758C721EFBA8B0095B9F8 /* VnodeCache.cpp */; }; - 264E723322930DA90059E150 /* JsonWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26786AE6228B816E00F53311 /* JsonWriter.cpp */; }; - 264E723422930E1E0059E150 /* JsonWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26786AE6228B816E00F53311 /* JsonWriter.cpp */; }; - 264E723E22930E660059E150 /* libPrjFSLib.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4391F8D521E430CF0008103C /* libPrjFSLib.dylib */; }; - 264E7245229318170059E150 /* JsonWriterTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 264E723122930AA30059E150 /* JsonWriterTests.mm */; }; - 264F8B642298455900B6EF84 /* ShouldHandleFileOpTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 264F8B632298455900B6EF84 /* ShouldHandleFileOpTests.mm */; }; - 265504D0224ADE11005FAD74 /* MockPerfTracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 265504CE224ADE11005FAD74 /* MockPerfTracing.cpp */; }; - 43057C5E21E439C700487681 /* prjfs-log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43057C5B21E439C700487681 /* prjfs-log.cpp */; }; - 43057C5F21E439C700487681 /* kext-perf-tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 43057C5C21E439C700487681 /* kext-perf-tracing.cpp */; }; - 4391F8A821E42AC50008103C /* Locks.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F88E21E42AC40008103C /* Locks.hpp */; }; - 4391F8AB21E42AC50008103C /* KextLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F89121E42AC40008103C /* KextLog.cpp */; }; - 4391F8AC21E42AC50008103C /* KextLog.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F89221E42AC40008103C /* KextLog.hpp */; }; - 4391F8AD21E42AC50008103C /* KauthHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F89321E42AC40008103C /* KauthHandler.cpp */; }; - 4391F8AE21E42AC50008103C /* VirtualizationRoots.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F89421E42AC40008103C /* VirtualizationRoots.hpp */; }; - 4391F8AF21E42AC50008103C /* PrjFSProviderUserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F89521E42AC40008103C /* PrjFSProviderUserClient.cpp */; }; - 4391F8B121E42AC50008103C /* Memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F89721E42AC40008103C /* Memory.cpp */; }; - 4391F8B221E42AC50008103C /* Memory.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F89821E42AC40008103C /* Memory.hpp */; }; - 4391F8B321E42AC50008103C /* PrjFSService.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F89921E42AC40008103C /* PrjFSService.hpp */; }; - 4391F8B421E42AC50008103C /* PrjFSService.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F89A21E42AC40008103C /* PrjFSService.cpp */; }; - 4391F8B521E42AC50008103C /* Message_Kernel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F89B21E42AC40008103C /* Message_Kernel.cpp */; }; - 4391F8B621E42AC50008103C /* PrjFSKext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F89C21E42AC40008103C /* PrjFSKext.cpp */; }; - 4391F8B721E42AC50008103C /* PrjFSClasses.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F89D21E42AC50008103C /* PrjFSClasses.hpp */; }; - 4391F8B821E42AC50008103C /* PrjFSLogUserClient.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F89E21E42AC50008103C /* PrjFSLogUserClient.hpp */; }; - 4391F8B921E42AC50008103C /* PrjFSProviderUserClientPrivate.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F89F21E42AC50008103C /* PrjFSProviderUserClientPrivate.hpp */; }; - 4391F8BA21E42AC50008103C /* PrjFSLogUserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8A021E42AC50008103C /* PrjFSLogUserClient.cpp */; }; - 4391F8BB21E42AC50008103C /* VnodeUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8A121E42AC50008103C /* VnodeUtilities.cpp */; }; - 4391F8BC21E42AC50008103C /* VnodeUtilities.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F8A221E42AC50008103C /* VnodeUtilities.hpp */; }; - 4391F8BD21E42AC50008103C /* PerformanceTracing.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F8A321E42AC50008103C /* PerformanceTracing.hpp */; }; - 4391F8BE21E42AC50008103C /* KauthHandler.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F8A421E42AC50008103C /* KauthHandler.hpp */; }; - 4391F8BF21E42AC50008103C /* PerformanceTracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8A521E42AC50008103C /* PerformanceTracing.cpp */; }; - 4391F8C021E42AC50008103C /* Locks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8A621E42AC50008103C /* Locks.cpp */; }; - 4391F8C121E42AC50008103C /* VirtualizationRoots.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8A721E42AC50008103C /* VirtualizationRoots.cpp */; }; - 4391F8DB21E430E20008103C /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4391F8DA21E430E20008103C /* CoreFoundation.framework */; }; - 4391F8DD21E430EB0008103C /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4391F8DC21E430EB0008103C /* IOKit.framework */; }; - 4391F8EA21E435230008103C /* PrjFSLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 4391F8E321E435230008103C /* PrjFSLib.h */; }; - 4391F8EB21E435230008103C /* PrjFSUser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8E421E435230008103C /* PrjFSUser.cpp */; }; - 4391F8EC21E435230008103C /* PrjFSLib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8E521E435230008103C /* PrjFSLib.cpp */; }; - 4391F8EE21E435230008103C /* PrjFSUser.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4391F8E721E435230008103C /* PrjFSUser.hpp */; }; - 4391F8FD21E435720008103C /* PrjFSUser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8E421E435230008103C /* PrjFSUser.cpp */; }; - 4391F8FE21E435810008103C /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4391F8DC21E430EB0008103C /* IOKit.framework */; }; - 4391F8FF21E435890008103C /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4391F8DA21E430E20008103C /* CoreFoundation.framework */; }; - 4A08257321E77BDD00E21AFD /* PrjFSKextLogDaemon.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A08257121E77BDD00E21AFD /* PrjFSKextLogDaemon.cpp */; }; - 4A08257621E77C4600E21AFD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4391F8DA21E430E20008103C /* CoreFoundation.framework */; }; - 4A08257721E77C4B00E21AFD /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4391F8DC21E430EB0008103C /* IOKit.framework */; }; - 4A08257821E77C5400E21AFD /* PrjFSUser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8E421E435230008103C /* PrjFSUser.cpp */; }; - 4A08257921E77C7000E21AFD /* org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4A08257021E77BDD00E21AFD /* org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist */; }; - 4A2A69A02297339A00ACAAAF /* KextAssertIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A2A699F2297339A00ACAAAF /* KextAssertIntegration.m */; }; - 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 */; }; - 4A781DAE2223374200DB7733 /* MockVnodeAndMount.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A781DAC2223374200DB7733 /* MockVnodeAndMount.cpp */; }; - 4A781DB02223391A00DB7733 /* KextLogMock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A781DAF2223391A00DB7733 /* KextLogMock.cpp */; }; - 4A781DB222233A1E00DB7733 /* TestLocks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A781DB122233A1E00DB7733 /* TestLocks.cpp */; }; - 4A781DB422233A4500DB7733 /* TestMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A781DB322233A4500DB7733 /* TestMemory.cpp */; }; - 4A82C45722807C6F00276002 /* Message_Shared.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A82C45622807C6F00276002 /* Message_Shared.cpp */; }; - 4A82C45822807C6F00276002 /* Message_Shared.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A82C45622807C6F00276002 /* Message_Shared.cpp */; }; - 4A82C45922807C6F00276002 /* Message_Shared.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4A82C45622807C6F00276002 /* Message_Shared.cpp */; }; - 4A82C45C228086F800276002 /* MessageTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4A82C45B228086F800276002 /* MessageTests.mm */; }; - 4A8C13A521F23F0200002878 /* KauthHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F89321E42AC40008103C /* KauthHandler.cpp */; }; - 4A8C13A621F2418400002878 /* libPrjFSKextTestable.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A8C13A021F23EE800002878 /* libPrjFSKextTestable.a */; }; - 4AAE3FCA22832340002673FA /* HandleFileOpOperationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4AAE3FC922832340002673FA /* HandleFileOpOperationTests.mm */; }; - 4AE873AE2310127D003B122B /* VnodeUtilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4391F8A121E42AC50008103C /* VnodeUtilities.cpp */; }; - 4AE873B0231017FB003B122B /* VnodeUtilitiesTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4AE873AF231017FB003B122B /* VnodeUtilitiesTests.mm */; }; - D9C087F222384670009C1110 /* MemoryTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = D9C087F122384670009C1110 /* MemoryTests.mm */; }; - F554202D224BB6E5008BAE95 /* ProviderMessagingMock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F554202C224BB6E5008BAE95 /* ProviderMessagingMock.cpp */; }; - F5554F422239883B00B31D19 /* MockProc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F5554F412239883B00B31D19 /* MockProc.cpp */; }; - F5E39C7A21F1118D006D65C2 /* KauthHandlerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5E39C7921F1118D006D65C2 /* KauthHandlerTests.mm */; }; - F5E39C8321F11556006D65C2 /* KauthHandlerTestable.hpp in Headers */ = {isa = PBXBuildFile; fileRef = F5E39C8121F11556006D65C2 /* KauthHandlerTestable.hpp */; }; - F5EACDC22242AB2D00EEA70E /* HandleOperationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5EACDC12242AB2D00EEA70E /* HandleOperationTests.mm */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 264E723F22930E660059E150 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4391F8D421E430CF0008103C; - remoteInfo = PrjFSLib; - }; - 26A7DE972293643F004F6252 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 26A7DE91229363EA004F6252; - remoteInfo = GeneratePrjFSVersion; - }; - 26A7DE992293644B004F6252 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 26A7DE91229363EA004F6252; - remoteInfo = GeneratePrjFSVersion; - }; - 26A7DE9B22936451004F6252 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 26A7DE91229363EA004F6252; - remoteInfo = GeneratePrjFSVersion; - }; - 26A7DE9D22936459004F6252 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 26A7DE91229363EA004F6252; - remoteInfo = GeneratePrjFSVersion; - }; - 26A7DEA122936473004F6252 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 26A7DE91229363EA004F6252; - remoteInfo = GeneratePrjFSVersion; - }; - 26A7DEA322936496004F6252 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 26A7DE91229363EA004F6252; - remoteInfo = GeneratePrjFSVersion; - }; - 26A7DEA52293649E004F6252 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 26A7DE91229363EA004F6252; - remoteInfo = GeneratePrjFSVersion; - }; - 4391F90421E4362B0008103C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4391F87E21E4278C0008103C; - remoteInfo = PrjFS; - }; - 4391F90621E4362B0008103C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4391F8D421E430CF0008103C; - remoteInfo = PrjFSLib; - }; - 4391F90821E4362B0008103C /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4391F8F321E4355C0008103C; - remoteInfo = "prjfs-log"; - }; - 4A08257421E77BEB00E21AFD /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4A08256721E77B7F00E21AFD; - remoteInfo = PrjFSKextLogDaemon; - }; - 4A8C13A721F241ED00002878 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4391F87621E4278C0008103C /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4A8C139F21F23EE800002878; - remoteInfo = PrjFSKextTestable; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 4391F8F221E4355C0008103C /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; - 4A08256621E77B7F00E21AFD /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 12; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - 4A08257921E77C7000E21AFD /* org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 262DFF982230798E005CC5DD /* VnodeCachePrivate.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = VnodeCachePrivate.hpp; sourceTree = ""; }; - 263DD5AD225D44C2005FEE9C /* VnodeCacheEntriesWrapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VnodeCacheEntriesWrapper.hpp; sourceTree = ""; }; - 264758C721EFBA8B0095B9F8 /* VnodeCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VnodeCache.cpp; sourceTree = ""; }; - 264758C821EFBA8B0095B9F8 /* VnodeCache.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VnodeCache.hpp; sourceTree = ""; }; - 264758CB21FA709B0095B9F8 /* VnodeCacheTestable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VnodeCacheTestable.hpp; sourceTree = ""; }; - 264758CD21FA71140095B9F8 /* VnodeCacheTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = VnodeCacheTests.mm; sourceTree = ""; }; - 264E723122930AA30059E150 /* JsonWriterTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = JsonWriterTests.mm; sourceTree = ""; }; - 264E723922930E660059E150 /* PrjFSLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrjFSLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 264E723D22930E660059E150 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 264F8B632298455900B6EF84 /* ShouldHandleFileOpTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ShouldHandleFileOpTests.mm; sourceTree = ""; }; - 265504CE224ADE11005FAD74 /* MockPerfTracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MockPerfTracing.cpp; sourceTree = ""; }; - 26786AE5228B815E00F53311 /* JsonWriter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = JsonWriter.hpp; sourceTree = ""; }; - 26786AE6228B816E00F53311 /* JsonWriter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JsonWriter.cpp; sourceTree = ""; }; - 26BBD24B22E282EA007273D9 /* PrjFSConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = PrjFSConfig.xcconfig; path = ../../BuildOutput/PrjFSConfig.xcconfig; sourceTree = ""; }; - 43057C5B21E439C700487681 /* prjfs-log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "prjfs-log.cpp"; sourceTree = ""; }; - 43057C5C21E439C700487681 /* kext-perf-tracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "kext-perf-tracing.cpp"; sourceTree = ""; }; - 43057C5D21E439C700487681 /* kext-perf-tracing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "kext-perf-tracing.hpp"; sourceTree = ""; }; - 4391F87F21E4278C0008103C /* PrjFSKext.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrjFSKext.kext; sourceTree = BUILT_PRODUCTS_DIR; }; - 4391F88E21E42AC40008103C /* Locks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Locks.hpp; sourceTree = ""; }; - 4391F88F21E42AC40008103C /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 4391F89021E42AC40008103C /* kernel-header-wrappers */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "kernel-header-wrappers"; sourceTree = ""; }; - 4391F89121E42AC40008103C /* KextLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KextLog.cpp; sourceTree = ""; }; - 4391F89221E42AC40008103C /* KextLog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KextLog.hpp; sourceTree = ""; }; - 4391F89321E42AC40008103C /* KauthHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KauthHandler.cpp; sourceTree = ""; }; - 4391F89421E42AC40008103C /* VirtualizationRoots.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VirtualizationRoots.hpp; sourceTree = ""; }; - 4391F89521E42AC40008103C /* PrjFSProviderUserClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSProviderUserClient.cpp; sourceTree = ""; }; - 4391F89621E42AC40008103C /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = ""; }; - 4391F89721E42AC40008103C /* Memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Memory.cpp; sourceTree = ""; }; - 4391F89821E42AC40008103C /* Memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Memory.hpp; sourceTree = ""; }; - 4391F89921E42AC40008103C /* PrjFSService.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PrjFSService.hpp; sourceTree = ""; }; - 4391F89A21E42AC40008103C /* PrjFSService.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSService.cpp; sourceTree = ""; }; - 4391F89B21E42AC40008103C /* Message_Kernel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Message_Kernel.cpp; sourceTree = ""; }; - 4391F89C21E42AC40008103C /* PrjFSKext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSKext.cpp; sourceTree = ""; }; - 4391F89D21E42AC50008103C /* PrjFSClasses.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PrjFSClasses.hpp; sourceTree = ""; }; - 4391F89E21E42AC50008103C /* PrjFSLogUserClient.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PrjFSLogUserClient.hpp; sourceTree = ""; }; - 4391F89F21E42AC50008103C /* PrjFSProviderUserClientPrivate.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PrjFSProviderUserClientPrivate.hpp; sourceTree = ""; }; - 4391F8A021E42AC50008103C /* PrjFSLogUserClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSLogUserClient.cpp; sourceTree = ""; }; - 4391F8A121E42AC50008103C /* VnodeUtilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VnodeUtilities.cpp; sourceTree = ""; }; - 4391F8A221E42AC50008103C /* VnodeUtilities.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = VnodeUtilities.hpp; sourceTree = ""; }; - 4391F8A321E42AC50008103C /* PerformanceTracing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PerformanceTracing.hpp; sourceTree = ""; }; - 4391F8A421E42AC50008103C /* KauthHandler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KauthHandler.hpp; sourceTree = ""; }; - 4391F8A521E42AC50008103C /* PerformanceTracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceTracing.cpp; sourceTree = ""; }; - 4391F8A621E42AC50008103C /* Locks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Locks.cpp; sourceTree = ""; }; - 4391F8A721E42AC50008103C /* VirtualizationRoots.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VirtualizationRoots.cpp; sourceTree = ""; }; - 4391F8D521E430CF0008103C /* libPrjFSLib.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libPrjFSLib.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; - 4391F8DA21E430E20008103C /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; - 4391F8DC21E430EB0008103C /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; - 4391F8E321E435230008103C /* PrjFSLib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrjFSLib.h; sourceTree = ""; }; - 4391F8E421E435230008103C /* PrjFSUser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSUser.cpp; sourceTree = ""; }; - 4391F8E521E435230008103C /* PrjFSLib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSLib.cpp; sourceTree = ""; }; - 4391F8E721E435230008103C /* PrjFSUser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PrjFSUser.hpp; sourceTree = ""; }; - 4391F8F421E4355C0008103C /* prjfs-log */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "prjfs-log"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4A08256821E77B7F00E21AFD /* PrjFSKextLogDaemon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PrjFSKextLogDaemon; sourceTree = BUILT_PRODUCTS_DIR; }; - 4A08257021E77BDD00E21AFD /* org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist; sourceTree = ""; }; - 4A08257121E77BDD00E21AFD /* PrjFSKextLogDaemon.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSKextLogDaemon.cpp; sourceTree = ""; }; - 4A08257221E77BDD00E21AFD /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 4A2A699B2295AA7800ACAAAF /* ArrayUtilities.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ArrayUtilities.hpp; sourceTree = ""; }; - 4A2A699D2296BACE00ACAAAF /* KauthHandlerPrivate.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KauthHandlerPrivate.hpp; sourceTree = ""; }; - 4A2A699F2297339A00ACAAAF /* KextAssertIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KextAssertIntegration.m; sourceTree = ""; }; - 4A2A69A12297375A00ACAAAF /* KextAssertIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KextAssertIntegration.h; sourceTree = ""; }; - 4A558DB822357AB000AFDE07 /* ProviderMessaging.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProviderMessaging.cpp; sourceTree = ""; }; - 4A558DB922357AB000AFDE07 /* ProviderMessaging.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ProviderMessaging.hpp; sourceTree = ""; }; - 4A5EC300229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PrjFSOfflineIOUserClient.cpp; sourceTree = ""; }; - 4A5EC301229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PrjFSOfflineIOUserClient.hpp; sourceTree = ""; }; - 4A781DA42220946000DB7733 /* VirtualizationRootsTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = VirtualizationRootsTests.mm; sourceTree = ""; }; - 4A781DA6222094C300DB7733 /* VirtualizationRootsPrivate.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = VirtualizationRootsPrivate.hpp; sourceTree = ""; }; - 4A781DA82222C6EA00DB7733 /* PrjFSProviderUserClient.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PrjFSProviderUserClient.hpp; sourceTree = ""; }; - 4A781DA92223305C00DB7733 /* KextMockUtilities.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KextMockUtilities.hpp; sourceTree = ""; }; - 4A781DAA222330F700DB7733 /* KextMockUtilities.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = KextMockUtilities.cpp; sourceTree = ""; }; - 4A781DAC2223374200DB7733 /* MockVnodeAndMount.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MockVnodeAndMount.cpp; sourceTree = ""; }; - 4A781DAD2223374200DB7733 /* MockVnodeAndMount.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MockVnodeAndMount.hpp; sourceTree = ""; }; - 4A781DAF2223391A00DB7733 /* KextLogMock.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = KextLogMock.cpp; sourceTree = ""; }; - 4A781DB122233A1E00DB7733 /* TestLocks.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TestLocks.cpp; sourceTree = ""; }; - 4A781DB322233A4500DB7733 /* TestMemory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TestMemory.cpp; sourceTree = ""; }; - 4A781DB722299C5300DB7733 /* VirtualizationRootsTestable.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = VirtualizationRootsTestable.hpp; sourceTree = ""; }; - 4A82C45622807C6F00276002 /* Message_Shared.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Message_Shared.cpp; sourceTree = ""; }; - 4A82C45A22807FD800276002 /* Message_Kernel.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Message_Kernel.hpp; sourceTree = ""; }; - 4A82C45B228086F800276002 /* MessageTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MessageTests.mm; sourceTree = ""; }; - 4A8C13A021F23EE800002878 /* libPrjFSKextTestable.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPrjFSKextTestable.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 4A8C13AA21F268FE00002878 /* PrjFSKextTests.exp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.exports; path = PrjFSKextTests.exp; sourceTree = ""; }; - 4AAE3FC922832340002673FA /* HandleFileOpOperationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HandleFileOpOperationTests.mm; sourceTree = ""; }; - 4AE873AF231017FB003B122B /* VnodeUtilitiesTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = VnodeUtilitiesTests.mm; sourceTree = ""; }; - D9C087F122384670009C1110 /* MemoryTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MemoryTests.mm; sourceTree = ""; }; - F5151F7F226F9083004B92C9 /* ProviderMessagingMock.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ProviderMessagingMock.hpp; sourceTree = ""; }; - F554202C224BB6E5008BAE95 /* ProviderMessagingMock.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ProviderMessagingMock.cpp; sourceTree = ""; }; - F5554F402239881800B31D19 /* MockProc.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MockProc.hpp; sourceTree = ""; }; - F5554F412239883B00B31D19 /* MockProc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MockProc.cpp; sourceTree = ""; }; - F557623E22009F0E005DE35E /* KextLogMock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KextLogMock.h; sourceTree = ""; }; - F5E39C7721F1118D006D65C2 /* PrjFSKextTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PrjFSKextTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - F5E39C7921F1118D006D65C2 /* KauthHandlerTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = KauthHandlerTests.mm; sourceTree = ""; }; - F5E39C7B21F1118D006D65C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - F5E39C8121F11556006D65C2 /* KauthHandlerTestable.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = KauthHandlerTestable.hpp; sourceTree = ""; }; - F5EACDC12242AB2D00EEA70E /* HandleOperationTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = HandleOperationTests.mm; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 264E723622930E660059E150 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 264E723E22930E660059E150 /* libPrjFSLib.dylib in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4391F87C21E4278C0008103C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4391F8D321E430CF0008103C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4391F8DD21E430EB0008103C /* IOKit.framework in Frameworks */, - 4391F8DB21E430E20008103C /* CoreFoundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4391F8F121E4355C0008103C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4391F8FF21E435890008103C /* CoreFoundation.framework in Frameworks */, - 4391F8FE21E435810008103C /* IOKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4A08256521E77B7F00E21AFD /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4A08257721E77C4B00E21AFD /* IOKit.framework in Frameworks */, - 4A08257621E77C4600E21AFD /* CoreFoundation.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4A8C139E21F23EE800002878 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F5E39C7421F1118D006D65C2 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4A8C13A621F2418400002878 /* libPrjFSKextTestable.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 264E723222930D8D0059E150 /* Json */ = { - isa = PBXGroup; - children = ( - 26786AE5228B815E00F53311 /* JsonWriter.hpp */, - 26786AE6228B816E00F53311 /* JsonWriter.cpp */, - ); - path = Json; - sourceTree = ""; - }; - 264E723A22930E660059E150 /* PrjFSLibTests */ = { - isa = PBXGroup; - children = ( - 264E723122930AA30059E150 /* JsonWriterTests.mm */, - 264E723D22930E660059E150 /* Info.plist */, - ); - path = PrjFSLibTests; - sourceTree = ""; - }; - 43057C5A21E439B200487681 /* prjfs-log */ = { - isa = PBXGroup; - children = ( - 43057C5C21E439C700487681 /* kext-perf-tracing.cpp */, - 43057C5D21E439C700487681 /* kext-perf-tracing.hpp */, - 43057C5B21E439C700487681 /* prjfs-log.cpp */, - ); - path = "prjfs-log"; - sourceTree = ""; - }; - 4391F87521E4278C0008103C = { - isa = PBXGroup; - children = ( - 26BBD24B22E282EA007273D9 /* PrjFSConfig.xcconfig */, - 4391F8C221E4306D0008103C /* PrjFSLib */, - 4391F88D21E42AA70008103C /* PrjFSKext */, - 4A08256921E77B7F00E21AFD /* PrjFSKextLogDaemon */, - F5E39C7821F1118D006D65C2 /* PrjFSKextTests */, - 264E723A22930E660059E150 /* PrjFSLibTests */, - 4391F88021E4278C0008103C /* Products */, - 4391F8D921E430E10008103C /* Frameworks */, - ); - indentWidth = 4; - sourceTree = ""; - tabWidth = 4; - usesTabs = 0; - }; - 4391F88021E4278C0008103C /* Products */ = { - isa = PBXGroup; - children = ( - 4391F87F21E4278C0008103C /* PrjFSKext.kext */, - 4391F8D521E430CF0008103C /* libPrjFSLib.dylib */, - 4391F8F421E4355C0008103C /* prjfs-log */, - 4A08256821E77B7F00E21AFD /* PrjFSKextLogDaemon */, - F5E39C7721F1118D006D65C2 /* PrjFSKextTests.xctest */, - 4A8C13A021F23EE800002878 /* libPrjFSKextTestable.a */, - 264E723922930E660059E150 /* PrjFSLibTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 4391F88D21E42AA70008103C /* PrjFSKext */ = { - isa = PBXGroup; - children = ( - 4A2A699B2295AA7800ACAAAF /* ArrayUtilities.hpp */, - 4391F88F21E42AC40008103C /* Info.plist */, - 4391F89321E42AC40008103C /* KauthHandler.cpp */, - 4391F8A421E42AC50008103C /* KauthHandler.hpp */, - 4A2A699D2296BACE00ACAAAF /* KauthHandlerPrivate.hpp */, - F5E39C8121F11556006D65C2 /* KauthHandlerTestable.hpp */, - 4391F89021E42AC40008103C /* kernel-header-wrappers */, - 4391F89121E42AC40008103C /* KextLog.cpp */, - 4391F89221E42AC40008103C /* KextLog.hpp */, - 4391F8A621E42AC50008103C /* Locks.cpp */, - 4391F88E21E42AC40008103C /* Locks.hpp */, - 4391F89721E42AC40008103C /* Memory.cpp */, - 4391F89821E42AC40008103C /* Memory.hpp */, - 4391F89B21E42AC40008103C /* Message_Kernel.cpp */, - 4A82C45A22807FD800276002 /* Message_Kernel.hpp */, - 4A82C45622807C6F00276002 /* Message_Shared.cpp */, - 4391F8A521E42AC50008103C /* PerformanceTracing.cpp */, - 4391F8A321E42AC50008103C /* PerformanceTracing.hpp */, - 4391F89D21E42AC50008103C /* PrjFSClasses.hpp */, - 4391F89C21E42AC40008103C /* PrjFSKext.cpp */, - 4391F8A021E42AC50008103C /* PrjFSLogUserClient.cpp */, - 4391F89E21E42AC50008103C /* PrjFSLogUserClient.hpp */, - 4A5EC300229D5F12005E8D8F /* PrjFSOfflineIOUserClient.cpp */, - 4A5EC301229D5F12005E8D8F /* PrjFSOfflineIOUserClient.hpp */, - 4391F89521E42AC40008103C /* PrjFSProviderUserClient.cpp */, - 4391F89F21E42AC50008103C /* PrjFSProviderUserClientPrivate.hpp */, - 4A781DA82222C6EA00DB7733 /* PrjFSProviderUserClient.hpp */, - 4391F89A21E42AC40008103C /* PrjFSService.cpp */, - 4391F89921E42AC40008103C /* PrjFSService.hpp */, - 4A558DB822357AB000AFDE07 /* ProviderMessaging.cpp */, - 4A558DB922357AB000AFDE07 /* ProviderMessaging.hpp */, - 4391F89621E42AC40008103C /* public */, - 4391F8A721E42AC50008103C /* VirtualizationRoots.cpp */, - 4391F89421E42AC40008103C /* VirtualizationRoots.hpp */, - 4A781DB722299C5300DB7733 /* VirtualizationRootsTestable.hpp */, - 4A781DA6222094C300DB7733 /* VirtualizationRootsPrivate.hpp */, - 264758C721EFBA8B0095B9F8 /* VnodeCache.cpp */, - 264758C821EFBA8B0095B9F8 /* VnodeCache.hpp */, - 262DFF982230798E005CC5DD /* VnodeCachePrivate.hpp */, - 264758CB21FA709B0095B9F8 /* VnodeCacheTestable.hpp */, - 4391F8A121E42AC50008103C /* VnodeUtilities.cpp */, - 4391F8A221E42AC50008103C /* VnodeUtilities.hpp */, - ); - path = PrjFSKext; - sourceTree = ""; - }; - 4391F8C221E4306D0008103C /* PrjFSLib */ = { - isa = PBXGroup; - children = ( - 264E723222930D8D0059E150 /* Json */, - 43057C5A21E439B200487681 /* prjfs-log */, - 4391F8E521E435230008103C /* PrjFSLib.cpp */, - 4391F8E321E435230008103C /* PrjFSLib.h */, - 4391F8E421E435230008103C /* PrjFSUser.cpp */, - 4391F8E721E435230008103C /* PrjFSUser.hpp */, - ); - path = PrjFSLib; - sourceTree = ""; - }; - 4391F8D921E430E10008103C /* Frameworks */ = { - isa = PBXGroup; - children = ( - 4391F8DC21E430EB0008103C /* IOKit.framework */, - 4391F8DA21E430E20008103C /* CoreFoundation.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - 4A08256921E77B7F00E21AFD /* PrjFSKextLogDaemon */ = { - isa = PBXGroup; - children = ( - 4A08257221E77BDD00E21AFD /* Info.plist */, - 4A08257021E77BDD00E21AFD /* org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist */, - 4A08257121E77BDD00E21AFD /* PrjFSKextLogDaemon.cpp */, - ); - path = PrjFSKextLogDaemon; - sourceTree = ""; - }; - F5E39C7821F1118D006D65C2 /* PrjFSKextTests */ = { - isa = PBXGroup; - children = ( - 4AAE3FC922832340002673FA /* HandleFileOpOperationTests.mm */, - F5EACDC12242AB2D00EEA70E /* HandleOperationTests.mm */, - F5E39C7B21F1118D006D65C2 /* Info.plist */, - F5E39C7921F1118D006D65C2 /* KauthHandlerTests.mm */, - 4A781DAF2223391A00DB7733 /* KextLogMock.cpp */, - 4A2A69A12297375A00ACAAAF /* KextAssertIntegration.h */, - 4A2A699F2297339A00ACAAAF /* KextAssertIntegration.m */, - F557623E22009F0E005DE35E /* KextLogMock.h */, - 4A781DAA222330F700DB7733 /* KextMockUtilities.cpp */, - 4A781DA92223305C00DB7733 /* KextMockUtilities.hpp */, - D9C087F122384670009C1110 /* MemoryTests.mm */, - 4A82C45B228086F800276002 /* MessageTests.mm */, - 265504CE224ADE11005FAD74 /* MockPerfTracing.cpp */, - F5554F412239883B00B31D19 /* MockProc.cpp */, - F5554F402239881800B31D19 /* MockProc.hpp */, - 4A781DAC2223374200DB7733 /* MockVnodeAndMount.cpp */, - 4A781DAD2223374200DB7733 /* MockVnodeAndMount.hpp */, - 4A8C13AA21F268FE00002878 /* PrjFSKextTests.exp */, - F554202C224BB6E5008BAE95 /* ProviderMessagingMock.cpp */, - F5151F7F226F9083004B92C9 /* ProviderMessagingMock.hpp */, - 264F8B632298455900B6EF84 /* ShouldHandleFileOpTests.mm */, - 4A781DB122233A1E00DB7733 /* TestLocks.cpp */, - 4A781DB322233A4500DB7733 /* TestMemory.cpp */, - 263DD5AD225D44C2005FEE9C /* VnodeCacheEntriesWrapper.hpp */, - 264758CD21FA71140095B9F8 /* VnodeCacheTests.mm */, - 4AE873AF231017FB003B122B /* VnodeUtilitiesTests.mm */, - 4A781DA42220946000DB7733 /* VirtualizationRootsTests.mm */, - ); - path = PrjFSKextTests; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 4391F87A21E4278C0008103C /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - 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 */, - 4391F8AC21E42AC50008103C /* KextLog.hpp in Headers */, - 4A558DBC22357AB000AFDE07 /* ProviderMessaging.hpp in Headers */, - 4391F8B321E42AC50008103C /* PrjFSService.hpp in Headers */, - 4391F8B721E42AC50008103C /* PrjFSClasses.hpp in Headers */, - 264758CC21FA709B0095B9F8 /* VnodeCacheTestable.hpp in Headers */, - 264758CA21EFBA8B0095B9F8 /* VnodeCache.hpp in Headers */, - 4391F8BC21E42AC50008103C /* VnodeUtilities.hpp in Headers */, - 4391F8BD21E42AC50008103C /* PerformanceTracing.hpp in Headers */, - 4391F8B921E42AC50008103C /* PrjFSProviderUserClientPrivate.hpp in Headers */, - F5E39C8321F11556006D65C2 /* KauthHandlerTestable.hpp in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4391F8D121E430CF0008103C /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4391F8EA21E435230008103C /* PrjFSLib.h in Headers */, - 4391F8EE21E435230008103C /* PrjFSUser.hpp in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4A8C139C21F23EE800002878 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 4A558DBD22357AB000AFDE07 /* ProviderMessaging.hpp in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 264E723822930E660059E150 /* PrjFSLibTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 264E724122930E660059E150 /* Build configuration list for PBXNativeTarget "PrjFSLibTests" */; - buildPhases = ( - 264E723522930E660059E150 /* Sources */, - 264E723622930E660059E150 /* Frameworks */, - 264E723722930E660059E150 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 26A7DEA62293649E004F6252 /* PBXTargetDependency */, - 264E724022930E660059E150 /* PBXTargetDependency */, - ); - name = PrjFSLibTests; - productName = PrjFSLibTests; - productReference = 264E723922930E660059E150 /* PrjFSLibTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 4391F87E21E4278C0008103C /* PrjFSKext */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4391F88721E4278C0008103C /* Build configuration list for PBXNativeTarget "PrjFSKext" */; - buildPhases = ( - 4391F87B21E4278C0008103C /* Sources */, - 4391F87C21E4278C0008103C /* Frameworks */, - 4391F87A21E4278C0008103C /* Headers */, - 4391F87D21E4278C0008103C /* Resources */, - 43057C6021E43CC100487681 /* Run Script - Check kext linkage */, - ); - buildRules = ( - ); - dependencies = ( - 26A7DE982293643F004F6252 /* PBXTargetDependency */, - ); - name = PrjFSKext; - productName = PrjFS; - productReference = 4391F87F21E4278C0008103C /* PrjFSKext.kext */; - productType = "com.apple.product-type.kernel-extension"; - }; - 4391F8D421E430CF0008103C /* PrjFSLib */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4391F8D621E430CF0008103C /* Build configuration list for PBXNativeTarget "PrjFSLib" */; - buildPhases = ( - 4391F8D121E430CF0008103C /* Headers */, - 4391F8D221E430CF0008103C /* Sources */, - 4391F8D321E430CF0008103C /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 26A7DE9A2293644B004F6252 /* PBXTargetDependency */, - ); - name = PrjFSLib; - productName = PrjFSLib; - productReference = 4391F8D521E430CF0008103C /* libPrjFSLib.dylib */; - productType = "com.apple.product-type.library.dynamic"; - }; - 4391F8F321E4355C0008103C /* prjfs-log */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4391F8F821E4355C0008103C /* Build configuration list for PBXNativeTarget "prjfs-log" */; - buildPhases = ( - 4391F8F021E4355C0008103C /* Sources */, - 4391F8F121E4355C0008103C /* Frameworks */, - 4391F8F221E4355C0008103C /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - 26A7DE9C22936451004F6252 /* PBXTargetDependency */, - ); - name = "prjfs-log"; - productName = "prjfs-log"; - productReference = 4391F8F421E4355C0008103C /* prjfs-log */; - productType = "com.apple.product-type.tool"; - }; - 4A08256721E77B7F00E21AFD /* PrjFSKextLogDaemon */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4A08256F21E77B7F00E21AFD /* Build configuration list for PBXNativeTarget "PrjFSKextLogDaemon" */; - buildPhases = ( - 4A08256421E77B7F00E21AFD /* Sources */, - 4A08256521E77B7F00E21AFD /* Frameworks */, - 4A08256621E77B7F00E21AFD /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - 26A7DE9E22936459004F6252 /* PBXTargetDependency */, - ); - name = PrjFSKextLogDaemon; - productName = PrjFSKextLogDaemon; - productReference = 4A08256821E77B7F00E21AFD /* PrjFSKextLogDaemon */; - productType = "com.apple.product-type.tool"; - }; - 4A8C139F21F23EE800002878 /* PrjFSKextTestable */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4A8C13A421F23EE800002878 /* Build configuration list for PBXNativeTarget "PrjFSKextTestable" */; - buildPhases = ( - 4A8C139C21F23EE800002878 /* Headers */, - 4A8C139D21F23EE800002878 /* Sources */, - 4A8C139E21F23EE800002878 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 26A7DEA222936473004F6252 /* PBXTargetDependency */, - ); - name = PrjFSKextTestable; - productName = PrjFSKextTestable; - productReference = 4A8C13A021F23EE800002878 /* libPrjFSKextTestable.a */; - productType = "com.apple.product-type.library.static"; - }; - F5E39C7621F1118D006D65C2 /* PrjFSKextTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = F5E39C7F21F1118D006D65C2 /* Build configuration list for PBXNativeTarget "PrjFSKextTests" */; - buildPhases = ( - F5E39C7321F1118D006D65C2 /* Sources */, - F5E39C7421F1118D006D65C2 /* Frameworks */, - F5E39C7521F1118D006D65C2 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 26A7DEA422936496004F6252 /* PBXTargetDependency */, - 4A8C13A821F241ED00002878 /* PBXTargetDependency */, - ); - name = PrjFSKextTests; - productName = PrjFSKextTests; - productReference = F5E39C7721F1118D006D65C2 /* PrjFSKextTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 4391F87621E4278C0008103C /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1010; - ORGANIZATIONNAME = "Microsoft Corporation"; - TargetAttributes = { - 264E723822930E660059E150 = { - CreatedOnToolsVersion = 9.4.1; - }; - 26A7DE91229363EA004F6252 = { - CreatedOnToolsVersion = 9.4.1; - }; - 4391F87E21E4278C0008103C = { - CreatedOnToolsVersion = 10.1; - }; - 4391F8D421E430CF0008103C = { - CreatedOnToolsVersion = 10.1; - }; - 4391F8F321E4355C0008103C = { - CreatedOnToolsVersion = 10.1; - }; - 4391F90021E436210008103C = { - CreatedOnToolsVersion = 10.1; - }; - 4A08256721E77B7F00E21AFD = { - CreatedOnToolsVersion = 9.4.1; - }; - 4A8C139F21F23EE800002878 = { - CreatedOnToolsVersion = 10.1; - }; - F5E39C7621F1118D006D65C2 = { - CreatedOnToolsVersion = 10.1; - }; - }; - }; - buildConfigurationList = 4391F87921E4278C0008103C /* Build configuration list for PBXProject "PrjFS" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 4391F87521E4278C0008103C; - productRefGroup = 4391F88021E4278C0008103C /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 4391F87E21E4278C0008103C /* PrjFSKext */, - 4391F8D421E430CF0008103C /* PrjFSLib */, - 4391F8F321E4355C0008103C /* prjfs-log */, - 4A08256721E77B7F00E21AFD /* PrjFSKextLogDaemon */, - 4391F90021E436210008103C /* Build All */, - F5E39C7621F1118D006D65C2 /* PrjFSKextTests */, - 4A8C139F21F23EE800002878 /* PrjFSKextTestable */, - 264E723822930E660059E150 /* PrjFSLibTests */, - 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 264E723722930E660059E150 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4391F87D21E4278C0008103C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F5E39C7521F1118D006D65C2 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 26A7DE96229363F2004F6252 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${BUILD_ROOT}/../../PrjFSVersion.h", - "${BUILD_ROOT}/../../PrjFSConfig.xcconfig", - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ ! -f \"${BUILD_ROOT}/../../PrjFSVersion.h\" ]; then\n \"${PROJECT_DIR}/Scripts/GeneratePrjFSVersionHeader.sh\"\nfi\nif [ ! -f \"${BUILD_ROOT}/../../PrjFSConfig.xcconfig\" ]; then\n \"${PROJECT_DIR}/Scripts/GeneratePrjFSXCConfig.sh\"\nfi"; - }; - 43057C6021E43CC100487681 /* Run Script - Check kext linkage */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script - Check kext linkage"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# This checks whether the kext is loadable in theory and whether we've missed any OSBundleLibraries\nkextlibs -c -multdef-symbols -undef-symbols -unsupported \"$BUILT_PRODUCTS_DIR/$WRAPPER_NAME\"\nkextutil -n \"$BUILT_PRODUCTS_DIR/$WRAPPER_NAME\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 264E723522930E660059E150 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 264E7245229318170059E150 /* JsonWriterTests.mm in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4391F87B21E4278C0008103C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4391F8BB21E42AC50008103C /* VnodeUtilities.cpp in Sources */, - 4391F8B521E42AC50008103C /* Message_Kernel.cpp in Sources */, - 4A82C45722807C6F00276002 /* Message_Shared.cpp in Sources */, - 4391F8B121E42AC50008103C /* Memory.cpp in Sources */, - 4391F8BF21E42AC50008103C /* PerformanceTracing.cpp in Sources */, - 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 */, - 4391F8B621E42AC50008103C /* PrjFSKext.cpp in Sources */, - 4391F8AB21E42AC50008103C /* KextLog.cpp in Sources */, - 264758C921EFBA8B0095B9F8 /* VnodeCache.cpp in Sources */, - 4391F8B421E42AC50008103C /* PrjFSService.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4391F8D221E430CF0008103C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4391F8EB21E435230008103C /* PrjFSUser.cpp in Sources */, - 4391F8EC21E435230008103C /* PrjFSLib.cpp in Sources */, - 4A82C45822807C6F00276002 /* Message_Shared.cpp in Sources */, - 264E723322930DA90059E150 /* JsonWriter.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4391F8F021E4355C0008103C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 43057C5E21E439C700487681 /* prjfs-log.cpp in Sources */, - 43057C5F21E439C700487681 /* kext-perf-tracing.cpp in Sources */, - 4391F8FD21E435720008103C /* PrjFSUser.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4A08256421E77B7F00E21AFD /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4A08257821E77C5400E21AFD /* PrjFSUser.cpp in Sources */, - 4A08257321E77BDD00E21AFD /* PrjFSKextLogDaemon.cpp in Sources */, - 264E723422930E1E0059E150 /* JsonWriter.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4A8C139D21F23EE800002878 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4A781DA72220971E00DB7733 /* VirtualizationRoots.cpp in Sources */, - 4A8C13A521F23F0200002878 /* KauthHandler.cpp in Sources */, - 4A82C45922807C6F00276002 /* Message_Shared.cpp in Sources */, - 264758CF21FA71E10095B9F8 /* VnodeCache.cpp in Sources */, - 4AE873AE2310127D003B122B /* VnodeUtilities.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F5E39C7321F1118D006D65C2 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4A781DAB222330F700DB7733 /* KextMockUtilities.cpp in Sources */, - 264F8B642298455900B6EF84 /* ShouldHandleFileOpTests.mm in Sources */, - 4A82C45C228086F800276002 /* MessageTests.mm in Sources */, - 4AE873B0231017FB003B122B /* VnodeUtilitiesTests.mm in Sources */, - 265504D0224ADE11005FAD74 /* MockPerfTracing.cpp in Sources */, - 4A781DA52220946000DB7733 /* VirtualizationRootsTests.mm in Sources */, - 4A2A69A02297339A00ACAAAF /* KextAssertIntegration.m in Sources */, - 4A781DAE2223374200DB7733 /* MockVnodeAndMount.cpp in Sources */, - 4AAE3FCA22832340002673FA /* HandleFileOpOperationTests.mm in Sources */, - F5554F422239883B00B31D19 /* MockProc.cpp in Sources */, - 4A781DB422233A4500DB7733 /* TestMemory.cpp in Sources */, - F5EACDC22242AB2D00EEA70E /* HandleOperationTests.mm in Sources */, - F5E39C7A21F1118D006D65C2 /* KauthHandlerTests.mm in Sources */, - D9C087F222384670009C1110 /* MemoryTests.mm in Sources */, - 4A781DB222233A1E00DB7733 /* TestLocks.cpp in Sources */, - 4A781DB02223391A00DB7733 /* KextLogMock.cpp in Sources */, - F554202D224BB6E5008BAE95 /* ProviderMessagingMock.cpp in Sources */, - 264758CE21FA71140095B9F8 /* VnodeCacheTests.mm in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 264E724022930E660059E150 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4391F8D421E430CF0008103C /* PrjFSLib */; - targetProxy = 264E723F22930E660059E150 /* PBXContainerItemProxy */; - }; - 26A7DE982293643F004F6252 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */; - targetProxy = 26A7DE972293643F004F6252 /* PBXContainerItemProxy */; - }; - 26A7DE9A2293644B004F6252 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */; - targetProxy = 26A7DE992293644B004F6252 /* PBXContainerItemProxy */; - }; - 26A7DE9C22936451004F6252 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */; - targetProxy = 26A7DE9B22936451004F6252 /* PBXContainerItemProxy */; - }; - 26A7DE9E22936459004F6252 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */; - targetProxy = 26A7DE9D22936459004F6252 /* PBXContainerItemProxy */; - }; - 26A7DEA222936473004F6252 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */; - targetProxy = 26A7DEA122936473004F6252 /* PBXContainerItemProxy */; - }; - 26A7DEA422936496004F6252 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */; - targetProxy = 26A7DEA322936496004F6252 /* PBXContainerItemProxy */; - }; - 26A7DEA62293649E004F6252 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 26A7DE91229363EA004F6252 /* GeneratePrjFSVersionFiles */; - targetProxy = 26A7DEA52293649E004F6252 /* PBXContainerItemProxy */; - }; - 4391F90521E4362B0008103C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4391F87E21E4278C0008103C /* PrjFSKext */; - targetProxy = 4391F90421E4362B0008103C /* PBXContainerItemProxy */; - }; - 4391F90721E4362B0008103C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4391F8D421E430CF0008103C /* PrjFSLib */; - targetProxy = 4391F90621E4362B0008103C /* PBXContainerItemProxy */; - }; - 4391F90921E4362B0008103C /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4391F8F321E4355C0008103C /* prjfs-log */; - targetProxy = 4391F90821E4362B0008103C /* PBXContainerItemProxy */; - }; - 4A08257521E77BEB00E21AFD /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4A08256721E77B7F00E21AFD /* PrjFSKextLogDaemon */; - targetProxy = 4A08257421E77BEB00E21AFD /* PBXContainerItemProxy */; - }; - 4A8C13A821F241ED00002878 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4A8C139F21F23EE800002878 /* PrjFSKextTestable */; - targetProxy = 4A8C13A721F241ED00002878 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 264E724222930E660059E150 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - INFOPLIST_FILE = PrjFSLibTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSLibTests.PrjFSLibTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 264E724322930E660059E150 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - INFOPLIST_FILE = PrjFSLibTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSLibTests.PrjFSLibTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - 264E724422930E660059E150 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - INFOPLIST_FILE = PrjFSLibTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSLibTests.PrjFSLibTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = "Profiling(Release)"; - }; - 26A7DE92229363EA004F6252 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 26A7DE93229363EA004F6252 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - 26A7DE94229363EA004F6252 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = "Profiling(Release)"; - }; - 43057C5221E437F300487681 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 26BBD24B22E282EA007273D9 /* PrjFSConfig.xcconfig */; - 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_CODE_COVERAGE = NO; - 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"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = "PRJFS_PERFORMANCE_TRACING_ENABLE=1"; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_SHADOW = YES; - 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; - MTL_FAST_MATH = YES; - SDKROOT = macosx; - SYMROOT = "$(SRCROOT)/../../BuildOutput/ProjFS.Mac/Native"; - USER_HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/../", - "$(BUILD_ROOT)/../../", - ); - WARNING_CFLAGS = ( - "-Werror=undefined-internal", - "-Werror", - "-Werror=format", - "-Werror=missing-prototypes", - ); - }; - name = "Profiling(Release)"; - }; - 43057C5321E437F300487681 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_STATIC_ANALYZER_MODE = deep; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - GCC_PREPROCESSOR_DEFINITIONS = ( - "PRJFS_PERFORMANCE_TRACING_ENABLE=1", - "MACH_ASSERT=1", - ); - INFOPLIST_FILE = PrjFSKext/Info.plist; - MODULE_NAME = org.vfsforgit.PrjFSKext; - MODULE_START = PrjFSKext_Start; - MODULE_STOP = PrjFSKext_Stop; - MODULE_VERSION = 1.0.0d1; - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSKext; - PRODUCT_NAME = "$(TARGET_NAME)"; - RUN_CLANG_STATIC_ANALYZER = YES; - WARNING_CFLAGS = ( - "-Werror=missing-prototypes", - "-Werror=undefined-internal", - ); - WRAPPER_EXTENSION = kext; - }; - name = "Profiling(Release)"; - }; - 43057C5421E437F300487681 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - EXECUTABLE_PREFIX = lib; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = "Profiling(Release)"; - }; - 43057C5521E437F300487681 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = "Profiling(Release)"; - }; - 43057C5621E437F300487681 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = "Profiling(Release)"; - }; - 4391F88521E4278C0008103C /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 26BBD24B22E282EA007273D9 /* PrjFSConfig.xcconfig */; - 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_CODE_COVERAGE = NO; - 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"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - 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_SHADOW = YES; - 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 = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SYMROOT = "$(SRCROOT)/../../BuildOutput/ProjFS.Mac/Native"; - USER_HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/../", - "$(BUILD_ROOT)/../../", - ); - WARNING_CFLAGS = ( - "-Werror=undefined-internal", - "-Werror", - "-Werror=format", - "-Werror=missing-prototypes", - ); - }; - name = Debug; - }; - 4391F88621E4278C0008103C /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 26BBD24B22E282EA007273D9 /* PrjFSConfig.xcconfig */; - 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_CODE_COVERAGE = NO; - 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"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - 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_SHADOW = YES; - 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; - MTL_FAST_MATH = YES; - SDKROOT = macosx; - SYMROOT = "$(SRCROOT)/../../BuildOutput/ProjFS.Mac/Native"; - USER_HEADER_SEARCH_PATHS = ( - "$(SRCROOT)/../", - "$(BUILD_ROOT)/../../", - ); - WARNING_CFLAGS = ( - "-Werror=undefined-internal", - "-Werror", - "-Werror=format", - "-Werror=missing-prototypes", - ); - }; - name = Release; - }; - 4391F88821E4278C0008103C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_STATIC_ANALYZER_MODE = deep; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - "MACH_ASSERT=1", - ); - INFOPLIST_FILE = PrjFSKext/Info.plist; - MODULE_NAME = org.vfsforgit.PrjFSKext; - MODULE_START = PrjFSKext_Start; - MODULE_STOP = PrjFSKext_Stop; - MODULE_VERSION = 1.0.0d1; - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSKext; - PRODUCT_NAME = "$(TARGET_NAME)"; - RUN_CLANG_STATIC_ANALYZER = YES; - WARNING_CFLAGS = ( - "-Werror=missing-prototypes", - "-Werror=undefined-internal", - ); - WRAPPER_EXTENSION = kext; - }; - name = Debug; - }; - 4391F88921E4278C0008103C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_STATIC_ANALYZER_MODE = deep; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - GCC_PREPROCESSOR_DEFINITIONS = "MACH_ASSERT=1"; - INFOPLIST_FILE = PrjFSKext/Info.plist; - MODULE_NAME = org.vfsforgit.PrjFSKext; - MODULE_START = PrjFSKext_Start; - MODULE_STOP = PrjFSKext_Stop; - MODULE_VERSION = 1.0.0d1; - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSKext; - PRODUCT_NAME = "$(TARGET_NAME)"; - RUN_CLANG_STATIC_ANALYZER = YES; - WARNING_CFLAGS = ( - "-Werror=missing-prototypes", - "-Werror=undefined-internal", - ); - WRAPPER_EXTENSION = kext; - }; - name = Release; - }; - 4391F8D721E430CF0008103C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - EXECUTABLE_PREFIX = lib; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Debug; - }; - 4391F8D821E430CF0008103C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - EXECUTABLE_PREFIX = lib; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Release; - }; - 4391F8F921E4355C0008103C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 4391F8FA21E4355C0008103C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - 4391F90221E436210008103C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 4391F90321E436210008103C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = UBF8T346G9; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - 4A08256C21E77B7F00E21AFD /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CREATE_INFOPLIST_SECTION_IN_BINARY = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - INFOPLIST_FILE = PrjFSKextLogDaemon/Info.plist; - MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 4A08256D21E77B7F00E21AFD /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CREATE_INFOPLIST_SECTION_IN_BINARY = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - INFOPLIST_FILE = PrjFSKextLogDaemon/Info.plist; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - 4A08256E21E77B7F00E21AFD /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CREATE_INFOPLIST_SECTION_IN_BINARY = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - INFOPLIST_FILE = PrjFSKextLogDaemon/Info.plist; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = "Profiling(Release)"; - }; - 4A8C13A121F23EE800002878 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CLANG_MODULES_AUTOLINK = NO; - DEAD_CODE_STRIPPING = YES; - EXECUTABLE_PREFIX = lib; - GCC_PREPROCESSOR_DEFINITIONS = ( - TESTABLE_KEXT_TARGET, - KEXT_UNIT_TESTING, - "MACH_ASSERT=1", - "$(inherited)", - ); - LINK_WITH_STANDARD_LIBRARIES = NO; - MACH_O_TYPE = staticlib; - OTHER_CFLAGS = "-ffreestanding"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - STRIP_INSTALLED_PRODUCT = NO; - SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/Kernel.framework/Headers/"; - }; - name = Debug; - }; - 4A8C13A221F23EE800002878 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CLANG_MODULES_AUTOLINK = NO; - DEAD_CODE_STRIPPING = YES; - EXECUTABLE_PREFIX = lib; - GCC_PREPROCESSOR_DEFINITIONS = ( - TESTABLE_KEXT_TARGET, - KEXT_UNIT_TESTING, - "MACH_ASSERT=1", - "$(inherited)", - ); - LINK_WITH_STANDARD_LIBRARIES = NO; - MACH_O_TYPE = staticlib; - OTHER_CFLAGS = "-ffreestanding"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - STRIP_INSTALLED_PRODUCT = NO; - SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/Kernel.framework/Headers/"; - }; - name = Release; - }; - 4A8C13A321F23EE800002878 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_CODE_COVERAGE = YES; - CLANG_MODULES_AUTOLINK = NO; - DEAD_CODE_STRIPPING = YES; - EXECUTABLE_PREFIX = lib; - GCC_PREPROCESSOR_DEFINITIONS = ( - TESTABLE_KEXT_TARGET, - KEXT_UNIT_TESTING, - "MACH_ASSERT=1", - "$(inherited)", - ); - LINK_WITH_STANDARD_LIBRARIES = NO; - MACH_O_TYPE = staticlib; - OTHER_CFLAGS = "-ffreestanding"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - STRIP_INSTALLED_PRODUCT = NO; - SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/Kernel.framework/Headers/"; - }; - name = "Profiling(Release)"; - }; - F5E39C7C21F1118D006D65C2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - EXPORTED_SYMBOLS_FILE = PrjFSKextTests/PrjFSKextTests.exp; - GCC_PREPROCESSOR_DEFINITIONS = ( - KEXT_UNIT_TESTING, - "DEBUG=1", - ); - INFOPLIST_FILE = PrjFSKextTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSKextTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - F5E39C7D21F1118D006D65C2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - EXPORTED_SYMBOLS_FILE = PrjFSKextTests/PrjFSKextTests.exp; - GCC_PREPROCESSOR_DEFINITIONS = KEXT_UNIT_TESTING; - INFOPLIST_FILE = PrjFSKextTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSKextTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; - F5E39C7E21F1118D006D65C2 /* Profiling(Release) */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_ENABLE_CODE_COVERAGE = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = UBF8T346G9; - EXPORTED_SYMBOLS_FILE = PrjFSKextTests/PrjFSKextTests.exp; - GCC_PREPROCESSOR_DEFINITIONS = ( - KEXT_UNIT_TESTING, - "PRJFS_PERFORMANCE_TRACING_ENABLE=1", - ); - INFOPLIST_FILE = PrjFSKextTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = org.vfsforgit.PrjFSKextTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = "Profiling(Release)"; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 264E724122930E660059E150 /* Build configuration list for PBXNativeTarget "PrjFSLibTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 264E724222930E660059E150 /* Debug */, - 264E724322930E660059E150 /* Release */, - 264E724422930E660059E150 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 26A7DE95229363EA004F6252 /* Build configuration list for PBXAggregateTarget "GeneratePrjFSVersionFiles" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 26A7DE92229363EA004F6252 /* Debug */, - 26A7DE93229363EA004F6252 /* Release */, - 26A7DE94229363EA004F6252 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4391F87921E4278C0008103C /* Build configuration list for PBXProject "PrjFS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4391F88521E4278C0008103C /* Debug */, - 4391F88621E4278C0008103C /* Release */, - 43057C5221E437F300487681 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4391F88721E4278C0008103C /* Build configuration list for PBXNativeTarget "PrjFSKext" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4391F88821E4278C0008103C /* Debug */, - 4391F88921E4278C0008103C /* Release */, - 43057C5321E437F300487681 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4391F8D621E430CF0008103C /* Build configuration list for PBXNativeTarget "PrjFSLib" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4391F8D721E430CF0008103C /* Debug */, - 4391F8D821E430CF0008103C /* Release */, - 43057C5421E437F300487681 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4391F8F821E4355C0008103C /* Build configuration list for PBXNativeTarget "prjfs-log" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4391F8F921E4355C0008103C /* Debug */, - 4391F8FA21E4355C0008103C /* Release */, - 43057C5521E437F300487681 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4391F90121E436210008103C /* Build configuration list for PBXAggregateTarget "Build All" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4391F90221E436210008103C /* Debug */, - 4391F90321E436210008103C /* Release */, - 43057C5621E437F300487681 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4A08256F21E77B7F00E21AFD /* Build configuration list for PBXNativeTarget "PrjFSKextLogDaemon" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4A08256C21E77B7F00E21AFD /* Debug */, - 4A08256D21E77B7F00E21AFD /* Release */, - 4A08256E21E77B7F00E21AFD /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4A8C13A421F23EE800002878 /* Build configuration list for PBXNativeTarget "PrjFSKextTestable" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4A8C13A121F23EE800002878 /* Debug */, - 4A8C13A221F23EE800002878 /* Release */, - 4A8C13A321F23EE800002878 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - F5E39C7F21F1118D006D65C2 /* Build configuration list for PBXNativeTarget "PrjFSKextTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - F5E39C7C21F1118D006D65C2 /* Debug */, - F5E39C7D21F1118D006D65C2 /* Release */, - F5E39C7E21F1118D006D65C2 /* Profiling(Release) */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 4391F87621E4278C0008103C /* Project object */; -} diff --git a/ProjFS.Mac/PrjFS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ProjFS.Mac/PrjFS.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index f5b0a7c99d..0000000000 --- a/ProjFS.Mac/PrjFS.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ProjFS.Mac/PrjFS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ProjFS.Mac/PrjFS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d..0000000000 --- a/ProjFS.Mac/PrjFS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ProjFS.Mac/PrjFS.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme b/ProjFS.Mac/PrjFS.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme deleted file mode 100644 index 68ef9416ad..0000000000 --- a/ProjFS.Mac/PrjFS.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ProjFS.Mac/PrjFSKext/ArrayUtilities.hpp b/ProjFS.Mac/PrjFSKext/ArrayUtilities.hpp deleted file mode 100644 index 2df2696117..0000000000 --- a/ProjFS.Mac/PrjFSKext/ArrayUtilities.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -template - constexpr size_t Array_Size(T (&array)[size]) -{ - return size; -} - -template void Array_CopyElements(T* destination, const T* source, size_t count) -{ - for (size_t i = 0; i < count; ++i) - { - destination[i] = source[i]; - } -} - -template void Array_DefaultInit(T* array, size_t count) -{ - for (size_t i = 0; i < count; ++i) - { - array[i] = T(); - } -} - -template - auto clamp(const T& value, const MIN_T& min, const MAX_T& max) -{ - return value < min ? min : (value > max ? max : value); -} diff --git a/ProjFS.Mac/PrjFSKext/Info.plist b/ProjFS.Mac/PrjFSKext/Info.plist deleted file mode 100644 index 3cbc627187..0000000000 --- a/ProjFS.Mac/PrjFSKext/Info.plist +++ /dev/null @@ -1,53 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - KEXT - CFBundleShortVersionString - $(PRJFS_BUNDLE_SHORT_VERSION_STRING) - CFBundleVersion - $(PRJFS_BUNDLE_VERSION) - NSHumanReadableCopyright - Copyright © Microsoft - IOKitPersonalities - - PrjFS - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - IOClass - org_vfsforgit_PrjFS - IOMatchCategory - org_vfsforgit_PrjFS - IOResourceMatch - IOBSD - IOProviderClass - IOResources - - - OSBundleLibraries - - com.apple.kpi.bsd - 16.7 - com.apple.kpi.libkern - 16.7 - com.apple.kpi.iokit - 16.7 - com.apple.kpi.mach - 16.7 - com.apple.kpi.dsep - 16.7 - - - diff --git a/ProjFS.Mac/PrjFSKext/KauthHandler.cpp b/ProjFS.Mac/PrjFSKext/KauthHandler.cpp deleted file mode 100644 index ec25918a5b..0000000000 --- a/ProjFS.Mac/PrjFSKext/KauthHandler.cpp +++ /dev/null @@ -1,1626 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "KauthHandlerPrivate.hpp" -#include "public/PrjFSCommon.h" -#include "public/PrjFSPerfCounter.h" -#include "public/PrjFSXattrs.h" -#include "VirtualizationRoots.hpp" -#include "VnodeUtilities.hpp" -#include "KauthHandler.hpp" -#include "public/Message.h" -#include "Locks.hpp" -#include "PrjFSProviderUserClient.hpp" -#include "PerformanceTracing.hpp" -#include "kernel-header-wrappers/mount.h" -#include "kernel-header-wrappers/stdatomic.h" -#include "KextLog.hpp" -#include "ProviderMessaging.hpp" -#include "VnodeCache.hpp" -#include "Memory.hpp" -#include "ArrayUtilities.hpp" - -#ifdef KEXT_UNIT_TESTING -#include "KauthHandlerTestable.hpp" -#endif - -enum ProviderCallbackPolicy -{ - CallbackPolicy_AllowAny, - CallbackPolicy_UserInitiatedOnly, -}; - -enum ProviderStatus -{ - Provider_StatusUnknown, - Provider_IsOffline, - Provider_IsOnline, -}; - -struct PendingRenameOperation -{ - vnode_t vnode; - thread_t thread; -}; - -// Function prototypes -KEXT_STATIC int HandleVnodeOperation( - kauth_cred_t credential, - void* idata, - kauth_action_t action, - uintptr_t arg0, - uintptr_t arg1, - uintptr_t arg2, - uintptr_t arg3); - -KEXT_STATIC int HandleFileOpOperation( - kauth_cred_t credential, - void* idata, - kauth_action_t action, - uintptr_t arg0, - uintptr_t arg1, - uintptr_t arg2, - uintptr_t arg3); - -static bool TryReadVNodeFileFlags(vnode_t vn, vfs_context_t _Nonnull context, uint32_t* flags); -KEXT_STATIC_INLINE bool FileFlagsBitIsSet(uint32_t fileFlags, uint32_t bit); -KEXT_STATIC_INLINE bool TryGetFileIsFlaggedAsInRoot(vnode_t vnode, vfs_context_t _Nonnull context, bool* flaggedInRoot); -KEXT_STATIC_INLINE bool ActionBitIsSet(kauth_action_t action, kauth_action_t mask); -KEXT_STATIC bool CurrentProcessIsAllowedToHydrate(); -KEXT_STATIC bool IsFileSystemCrawler(const char* procname); - -static void WaitForListenerCompletion(); -KEXT_STATIC bool ShouldIgnoreVnodeType(vtype vnodeType, vnode_t vnode); -static bool VnodeIsEligibleForEventHandling(vnode_t vnode); - -KEXT_STATIC bool ShouldHandleVnodeOpEvent( - // In params: - PerfTracer* perfTracer, - vfs_context_t _Nonnull context, - const vnode_t vnode, - kauth_action_t action, - - // Out params: - uint32_t* vnodeFileFlags, - int* pid, - char procname[MAXCOMLEN + 1], - int* kauthResult, - int* kauthError); - -static bool TryGetVirtualizationRoot( - // In params: - PerfTracer* perfTracer, - vfs_context_t _Nonnull context, - const vnode_t vnode, - pid_t pidMakingRequest, - ProviderCallbackPolicy callbackPolicy, - bool denyIfOffline, - - // Out params: - VirtualizationRootHandle* root, - FsidInode* vnodeFsidInode, - int* kauthResult, - int* kauthError, - ProviderStatus* _Nullable providerStatus = nullptr); - -KEXT_STATIC bool ShouldHandleFileOpEvent( - // In params: - PerfTracer* perfTracer, - vfs_context_t _Nonnull context, - const vnode_t vnode, - const char* path, - kauth_action_t action, - bool isDirectory, - - // Out params: - VirtualizationRootHandle* root, - int* pid); - -KEXT_STATIC bool InitPendingRenames(); -KEXT_STATIC void CleanupPendingRenames(); -KEXT_STATIC void ResizePendingRenames(uint32_t newMaxPendingRenames); - -// State -static kauth_listener_t s_vnodeListener = nullptr; -static kauth_listener_t s_fileopListener = nullptr; - -static atomic_int s_numActiveKauthEvents; -static SpinLock s_renameLock; -static PendingRenameOperation* s_pendingRenames = nullptr; -KEXT_STATIC uint32_t s_pendingRenameCount = 0; -KEXT_STATIC uint32_t s_maxPendingRenames = 0; -static bool s_osSupportsRenameDetection = false; - -// Public functions -kern_return_t KauthHandler_Init() -{ - if (nullptr != s_vnodeListener) - { - goto CleanupAndFail; - } - - if (!ProviderMessaging_Init()) - { - goto CleanupAndFail; - } - - if (VirtualizationRoots_Init()) - { - goto CleanupAndFail; - } - - if (VnodeCache_Init()) - { - goto CleanupAndFail; - } - - if (!InitPendingRenames()) - { - goto CleanupAndFail; - } - - s_vnodeListener = kauth_listen_scope(KAUTH_SCOPE_VNODE, HandleVnodeOperation, nullptr); - if (nullptr == s_vnodeListener) - { - goto CleanupAndFail; - } - - s_fileopListener = kauth_listen_scope(KAUTH_SCOPE_FILEOP, HandleFileOpOperation, nullptr); - if (nullptr == s_fileopListener) - { - goto CleanupAndFail; - } - - return KERN_SUCCESS; - -CleanupAndFail: - KauthHandler_Cleanup(); - return KERN_FAILURE; -} - -kern_return_t KauthHandler_Cleanup() -{ - kern_return_t result = KERN_SUCCESS; - - // First, stop new listener callback calls - if (nullptr != s_vnodeListener) - { - kauth_unlisten_scope(s_vnodeListener); - s_vnodeListener = nullptr; - } - else - { - result = KERN_FAILURE; - } - - if (nullptr != s_fileopListener) - { - kauth_unlisten_scope(s_fileopListener); - s_fileopListener = nullptr; - } - else - { - result = KERN_FAILURE; - } - - ProviderMessaging_AbortAllOutstandingEvents(); - - WaitForListenerCompletion(); - - CleanupPendingRenames(); - - if (VnodeCache_Cleanup()) - { - result = KERN_FAILURE; - } - - if (VirtualizationRoots_Cleanup()) - { - result = KERN_FAILURE; - } - - ProviderMessaging_Cleanup(); - - return result; -} - -KEXT_STATIC void UseMainForkIfNamedStream( - // In+out params: - vnode_t& vnode, - // Out params: - bool& putVnodeWhenDone) -{ - // A lot of our file checks such as attribute tests behave oddly if the vnode - // refers to a named fork/stream; apply the logic as if the vnode operation was - // occurring on the file itself. (/path/to/file/..namedfork/rsrc) - if (vnode_isnamedstream(vnode)) - { - vnode_t mainFileFork = vnode_getparent(vnode); - assert(NULLVP != mainFileFork); - - vnode = mainFileFork; - putVnodeWhenDone = true; - } - else - { - putVnodeWhenDone = false; - } -} - -KEXT_STATIC bool InitPendingRenames() -{ - // Only need to/can track renames on Mojave and newer - s_osSupportsRenameDetection = (version_major >= PrjFSDarwinMajorVersion::MacOS10_14_Mojave); - if (s_osSupportsRenameDetection) - { - s_renameLock = SpinLock_Alloc(); - s_maxPendingRenames = 8; // Arbitrary choice, should be maximum number of expected concurrent threads performing renames, but array will resize on demand - s_pendingRenameCount = 0; - s_pendingRenames = Memory_AllocArray(s_maxPendingRenames); - if (!SpinLock_IsValid(s_renameLock) || s_pendingRenames == nullptr) - { - return false; - } - - Array_DefaultInit(s_pendingRenames, s_maxPendingRenames); - } - - return true; -} - -KEXT_STATIC void CleanupPendingRenames() -{ - if (s_osSupportsRenameDetection) - { - if (SpinLock_IsValid(s_renameLock)) - { - SpinLock_FreeMemory(&s_renameLock); - } - - if (s_pendingRenames != nullptr) - { - assert(s_pendingRenameCount == 0); - Memory_FreeArray(s_pendingRenames, s_maxPendingRenames); - s_pendingRenames = nullptr; - s_maxPendingRenames = 0; - } - } -} - -KEXT_STATIC void ResizePendingRenames(uint32_t newMaxPendingRenames) -{ - PendingRenameOperation* newArray = Memory_AllocArray(newMaxPendingRenames); - assert(newArray != nullptr); - PendingRenameOperation* arrayToFree = nullptr; - uint32_t arrayToFreeLength = 0; - - SpinLock_Acquire(s_renameLock); - { - if (newMaxPendingRenames > s_maxPendingRenames) - { - Array_CopyElements(newArray, s_pendingRenames, s_maxPendingRenames); - Array_DefaultInit(newArray + s_maxPendingRenames, newMaxPendingRenames - s_maxPendingRenames); - - arrayToFree = s_pendingRenames; - arrayToFreeLength = s_maxPendingRenames; - - s_pendingRenames = newArray; - s_maxPendingRenames = newMaxPendingRenames; - } - else - { - arrayToFree = newArray; - arrayToFreeLength = newMaxPendingRenames; - } - } - SpinLock_Release(s_renameLock); - - if (arrayToFree != nullptr) - { - assert(arrayToFreeLength > 0); - Memory_FreeArray(arrayToFree, arrayToFreeLength); - } -} - -KEXT_STATIC void RecordPendingRenameOperation(vnode_t vnode) -{ - assertf(s_osSupportsRenameDetection, "This function should only be called from the KAUTH_FILEOP_WILL_RENAME handler, which is only supported by Darwin 18 (macOS 10.14 Mojave) and newer (version_major = %u)", version_major); - thread_t myThread = current_thread(); - - bool resizeTable; - do - { - resizeTable = false; - uint32_t resizeTableLength = 0; - - SpinLock_Acquire(s_renameLock); - { - if (s_pendingRenameCount < s_maxPendingRenames) - { - s_pendingRenames[s_pendingRenameCount].thread = myThread; - s_pendingRenames[s_pendingRenameCount].vnode = vnode; - ++s_pendingRenameCount; - } - else - { - for (uint32_t i = 0; i < s_pendingRenameCount; ++i) - { - assert(s_pendingRenames[i].thread != myThread); - } - - resizeTable = true; - - resizeTableLength = static_cast(clamp(s_maxPendingRenames * UINT64_C(2), 1u, UINT32_MAX)); - assert(resizeTableLength > s_maxPendingRenames); - } - } - SpinLock_Release(s_renameLock); - - if (resizeTable) - { - if (resizeTableLength > 16) - { - KextLog_Error("Warning: RecordPendingRenameOperation is causing pending rename array resize to %u items.", resizeTableLength); - } - ResizePendingRenames(resizeTableLength); - } - } while (resizeTable); -} - -KEXT_STATIC bool DeleteOpIsForRename(vnode_t vnode) -{ - if (!s_osSupportsRenameDetection) - { - // High Sierra and earlier do not support WILL_RENAME notification, so we have to assume any delete may be caused by a rename - assert(s_pendingRenameCount == 0); - assert(s_maxPendingRenames == 0); - return true; - } - - bool isRename = false; - - thread_t myThread = current_thread(); - - SpinLock_Acquire(s_renameLock); - { - for (uint32_t i = 0; i < s_pendingRenameCount; ++i) - { - if (s_pendingRenames[i].thread == myThread) - { - isRename = true; - assert(s_pendingRenames[i].vnode == vnode); - --s_pendingRenameCount; - if (i != s_pendingRenameCount) - { - s_pendingRenames[i] = s_pendingRenames[s_pendingRenameCount]; - } - - s_pendingRenames[s_pendingRenameCount] = PendingRenameOperation{}; - break; - } - } - } - SpinLock_Release(s_renameLock); - - return isRename; -} - -// Private functions -KEXT_STATIC int HandleVnodeOperation( - kauth_cred_t credential, - void* idata, - kauth_action_t action, - uintptr_t arg0, - uintptr_t arg1, - uintptr_t arg2, - uintptr_t arg3) -{ - atomic_fetch_add(&s_numActiveKauthEvents, 1); - - PerfTracer perfTracer; - PerfSample vnodeOpFunctionSample(&perfTracer, PrjFSPerfCounter_VnodeOp); - - vfs_context_t _Nonnull context = reinterpret_cast(arg0); - vnode_t currentVnode = reinterpret_cast(arg1); - // arg2 is the (vnode_t) parent vnode - int* kauthError = reinterpret_cast(arg3); - int kauthResult = KAUTH_RESULT_DEFER; - bool putVnodeWhenDone = false; - - UseMainForkIfNamedStream(currentVnode, putVnodeWhenDone); - - VirtualizationRootHandle root = RootHandle_None; - uint32_t currentVnodeFileFlags; - FsidInode vnodeFsidInode; - int pid = 0; - char procname[MAXCOMLEN + 1] = ""; - bool isDeleteAction = false; - bool isDirectory = false; - bool isRename = false; - - { - PerfSample considerVnodeSample(&perfTracer, PrjFSPerfCounter_VnodeOp_BasicVnodeChecks); - if (!VnodeIsEligibleForEventHandling(currentVnode)) - { - goto CleanupAndReturn; - } - } - - isDeleteAction = ActionBitIsSet(action, KAUTH_VNODE_DELETE); - if (isDeleteAction) - { - // This removes a matching entry from the array, so must run under the same - // conditions as the original RecordPendingRenameOperation call - hence early - // in the callback. - isRename = DeleteOpIsForRename(currentVnode); - } - - if (!ShouldHandleVnodeOpEvent( - &perfTracer, - context, - currentVnode, - action, - ¤tVnodeFileFlags, - &pid, - procname, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - - isDirectory = vnode_isdir(currentVnode); - - if (isDirectory) - { - if (isRename || - ActionBitIsSet( - action, - KAUTH_VNODE_LIST_DIRECTORY | - KAUTH_VNODE_SEARCH | - KAUTH_VNODE_READ_SECURITY | - KAUTH_VNODE_READ_ATTRIBUTES | - KAUTH_VNODE_READ_EXTATTRIBUTES)) - { - // Recursively expand directory on rename as user will expect the moved directory to have the same contents as in its original location - if (isRename) - { - if (!TryGetVirtualizationRoot( - &perfTracer, - context, - currentVnode, - pid, - // Prevent system services from expanding directories as part of enumeration as this tends to cause deadlocks with the kauth listeners for Antivirus software - CallbackPolicy_UserInitiatedOnly, - // We want to block directory renames when provider is offline, but we can only do this on - // newer OS versions as any delete operation could be a rename on High Sierra and older, - // so on those versions, isRename is true for all delete events. - s_osSupportsRenameDetection, - &root, - &vnodeFsidInode, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - - PerfSample recursivelyEnumerateSample(&perfTracer, PrjFSPerfCounter_VnodeOp_RecursivelyEnumerateDirectory); - - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - root, - MessageType_KtoU_RecursivelyEnumerateDirectory, - currentVnode, - vnodeFsidInode, - nullptr, // path not needed, use fsid/inode - nullptr, // source path N/A - pid, - procname, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - } - else if (FileFlagsBitIsSet(currentVnodeFileFlags, FileFlags_IsEmpty)) - { - if (!TryGetVirtualizationRoot( - &perfTracer, - context, - currentVnode, - pid, - // Prevent system services from expanding directories as part of enumeration as this tends to cause deadlocks with the kauth listeners for Antivirus software - CallbackPolicy_UserInitiatedOnly, - false, // allow reading offline directories even if not expanded - &root, - &vnodeFsidInode, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - - PerfSample enumerateDirectorySample(&perfTracer, PrjFSPerfCounter_VnodeOp_EnumerateDirectory); - - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - root, - MessageType_KtoU_EnumerateDirectory, - currentVnode, - vnodeFsidInode, - nullptr, // path not needed, use fsid/inode - nullptr, // source path N/A - pid, - procname, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - } - } - else if (ActionBitIsSet(action, KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY)) - { - if (!TryGetVirtualizationRoot( - &perfTracer, - context, - currentVnode, - pid, - CallbackPolicy_AllowAny, - // Block creating new files in offline roots. - true, - &root, - &vnodeFsidInode, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - } - } - else - { - if (isRename || // Hydrate before a file is moved as the user will not expect an empty file at the new location - ActionBitIsSet( - action, - KAUTH_VNODE_READ_ATTRIBUTES | - KAUTH_VNODE_WRITE_ATTRIBUTES | - KAUTH_VNODE_READ_EXTATTRIBUTES | - KAUTH_VNODE_WRITE_EXTATTRIBUTES | - KAUTH_VNODE_READ_DATA | - KAUTH_VNODE_WRITE_DATA | - KAUTH_VNODE_EXECUTE | - KAUTH_VNODE_APPEND_DATA)) - { - if (FileFlagsBitIsSet(currentVnodeFileFlags, FileFlags_IsEmpty)) - { - // Prevent access to empty files in offline roots, except always allow the user to delete files. - bool shouldBlockIfOffline = - (isRename && s_osSupportsRenameDetection) || - ActionBitIsSet( - action, - // Writes would get overwritten by subsequent hydration - KAUTH_VNODE_WRITE_ATTRIBUTES | - KAUTH_VNODE_WRITE_EXTATTRIBUTES | - KAUTH_VNODE_WRITE_DATA | - KAUTH_VNODE_APPEND_DATA | - // Reads would yield bad (null) data - KAUTH_VNODE_READ_DATA | - KAUTH_VNODE_READ_ATTRIBUTES | - KAUTH_VNODE_EXECUTE | - KAUTH_VNODE_READ_EXTATTRIBUTES); - if (!TryGetVirtualizationRoot( - &perfTracer, - context, - currentVnode, - pid, - // Prevent system services from hydrating files as this tends to cause deadlocks with the kauth listeners for Antivirus software - CallbackPolicy_UserInitiatedOnly, - shouldBlockIfOffline, - &root, - &vnodeFsidInode, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - - PerfSample enumerateDirectorySample(&perfTracer, PrjFSPerfCounter_VnodeOp_HydrateFile); - - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - root, - MessageType_KtoU_HydrateFile, - currentVnode, - vnodeFsidInode, - nullptr, // path not needed, use fsid/inode - nullptr, // source path N/A - pid, - procname, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - } - - if (ActionBitIsSet(action, KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA)) - { - /* At this stage we know the file is NOT empty, but it could be - * in either placeholder (hydrated) or full (modified/added to - * watchlist) state. - * - * If it's a placeholder, we need to notify the provider before - * allowing modifications so that it is tracked as a full file - * (and the placeholder xattr is removed). - * If the provider is offline in this situation, we want to - * prevent writes (by most processes) to the file so that the - * modifications don't go undetected. - * - * If it's already a full file on the other hand, nothing needs - * to be done, so we can allow writes even when the provider is - * offline. - */ - ProviderStatus providerStatus; - bool shouldMessageProvider = TryGetVirtualizationRoot( - &perfTracer, - context, - currentVnode, - pid, - CallbackPolicy_UserInitiatedOnly, - // At this stage, we don't yet know if the file is still a placeholder, so don't deny yet even if offline - false, // denyIfOffline - &root, - &vnodeFsidInode, - &kauthResult, - kauthError, - &providerStatus); - - if (!shouldMessageProvider) - { - // If we don't need to message the provider, that's normally - // the end of it, but in the case where the provider is - // offline we need to do further checks. - if (providerStatus != Provider_IsOffline) - { - goto CleanupAndReturn; - } - } - - PrjFSFileXAttrData rootXattr = {}; - SizeOrError xattrResult = Vnode_ReadXattr(currentVnode, PrjFSFileXAttrName, &rootXattr, sizeof(rootXattr)); - if (xattrResult.error == ENOATTR) - { - // If the file does not have the attribute, this means it's - // already in the "full" state, so no need to send a provider - // message or block the I/O if the provider is offline - goto CleanupAndReturn; - } - - if (providerStatus == Provider_IsOffline) - { - assert(!shouldMessageProvider); - // Deny write access to placeholders in an offline root - kauthResult = KAUTH_RESULT_DENY; - goto CleanupAndReturn; - } - - assert(shouldMessageProvider); - assert(providerStatus == Provider_IsOnline); - - PerfSample preConvertToFullSample(&perfTracer, PrjFSPerfCounter_VnodeOp_PreConvertToFull); - - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - root, - MessageType_KtoU_NotifyFilePreConvertToFull, - currentVnode, - vnodeFsidInode, - nullptr, // path not needed, use fsid/inode, - nullptr, // source path N/A - pid, - procname, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - } - } - } - - if (isDeleteAction) - { - if (!TryGetVirtualizationRoot( - &perfTracer, - context, - currentVnode, - pid, - // Allow any user to delete individual files, as this generally doesn't cause nested kauth callbacks. - CallbackPolicy_AllowAny, - // Allow deletes even if provider offline, except renames. Allow all deletes - // on OS versions where we can't distinguish renames & other deletes. - isRename && s_osSupportsRenameDetection, - &root, - &vnodeFsidInode, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - - PerfSample preDeleteSample(&perfTracer, PrjFSPerfCounter_VnodeOp_PreDelete); - - // Predeletes must be sent after hydration since they may convert the file to full - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - root, - isDirectory ? - MessageType_KtoU_NotifyDirectoryPreDelete : - isRename ? MessageType_KtoU_NotifyFilePreDeleteFromRename : MessageType_KtoU_NotifyFilePreDelete, - currentVnode, - vnodeFsidInode, - nullptr, // path not needed, use fsid/inode - nullptr, // source path N/A - pid, - procname, - &kauthResult, - kauthError)) - { - goto CleanupAndReturn; - } - } - - -CleanupAndReturn: - if (putVnodeWhenDone) - { - vnode_put(currentVnode); - } - - atomic_fetch_sub(&s_numActiveKauthEvents, 1); - return kauthResult; -} - -// Note: a fileop listener MUST NOT return an error, or it will result in a kernel panic. -// Fileop events are informational only. -KEXT_STATIC int HandleFileOpOperation( - kauth_cred_t credential, - void* idata, - kauth_action_t action, - uintptr_t arg0, - uintptr_t arg1, - uintptr_t arg2, - uintptr_t arg3) -{ - atomic_fetch_add(&s_numActiveKauthEvents, 1); - - PerfTracer perfTracer; - PerfSample fileOpSample(&perfTracer, PrjFSPerfCounter_FileOp); - - vfs_context_t _Nonnull context = vfs_context_create(NULL); - vnode_t currentVnode = NULLVP; - bool putCurrentVnode = false; - - if (KAUTH_FILEOP_RENAME == action) - { - // arg0 is the (const char *) fromPath (or the file being linked to) - const char* newPath = reinterpret_cast(arg1); - - // TODO(#1367): We need to handle failures to lookup the vnode. If we fail to lookup the vnode - // it's possible that we'll miss notifications - errno_t toErr = vnode_lookup(newPath, /* flags: */ VNODE_LOOKUP_NOFOLLOW, ¤tVnode, context); - if (0 != toErr) - { - VirtualizationRootHandle providerHandle = ActiveProvider_FindForPath(newPath); - // TODO(#1480): Drop log level to KEXTLOG_DEFAULT when providerHandle is 'none' once these failures become rare - KextLog_Error("HandleFileOpOperation (KAUTH_FILEOP_RENAME): vnode_lookup failed, errno %d for path '%s'; path lies within root %d%s", toErr, newPath, providerHandle, providerHandle == RootHandle_None ? "" : " with active provider"); - goto CleanupAndReturn; - } - - // Don't expect named stream here as they can't be directly hardlinked or renamed, only the main fork can - assert(!vnode_isnamedstream(currentVnode)); - - putCurrentVnode = true; - - bool isDirectory = (0 != vnode_isdir(currentVnode)); - - VirtualizationRootHandle root = RootHandle_None; - int pid; - if (!ShouldHandleFileOpEvent( - &perfTracer, - context, - currentVnode, - nullptr, // use vnode for lookup, not path - action, - isDirectory, - &root, - &pid)) - { - goto CleanupAndReturn; - } - - FsidInode vnodeFsidInode = Vnode_GetFsidAndInode(currentVnode, context, true /* the inode is used for getting the path in the provider, so use linkid */); - - char procname[MAXCOMLEN + 1]; - proc_name(pid, procname, MAXCOMLEN + 1); - - { - PerfSample renameSample(&perfTracer, PrjFSPerfCounter_FileOp_Renamed); - - MessageType messageType = - isDirectory - ? MessageType_KtoU_NotifyDirectoryRenamed - : MessageType_KtoU_NotifyFileRenamed; - - int kauthResult; - int kauthError; - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - root, - messageType, - currentVnode, - vnodeFsidInode, - newPath, - nullptr, // fromPath - pid, - procname, - &kauthResult, - &kauthError)) - { - goto CleanupAndReturn; - } - } - } - else if (KAUTH_FILEOP_LINK == action) - { - const char* fromPath = reinterpret_cast(arg0); - const char* newPath = reinterpret_cast(arg1); - - // TODO(#1367): We need to handle failures to lookup the vnode. If we fail to lookup the vnode - // it's possible that we'll miss notifications - errno_t toErr = vnode_lookup(newPath, /* flags: */ VNODE_LOOKUP_NOFOLLOW, ¤tVnode, context); - if (0 != toErr) - { - VirtualizationRootHandle providerHandle = ActiveProvider_FindForPath(newPath); - // TODO(#1480): Drop log level to KEXTLOG_DEFAULT when providerHandle is 'none' once these failures become rare - KextLog_Error("HandleFileOpOperation (KAUTH_FILEOP_LINK): vnode_lookup failed, errno %d for path '%s'; path lies within root %d%s", toErr, newPath, providerHandle, providerHandle == RootHandle_None ? "" : " with active provider"); - goto CleanupAndReturn; - } - - // Don't expect named stream here as they can't be directly hardlinked or renamed, only the main fork can - assert(!vnode_isnamedstream(currentVnode)); - - putCurrentVnode = true; - - bool isDirectory = (0 != vnode_isdir(currentVnode)); - if (isDirectory) - { - KextLog_Info("HandleFileOpOperation: KAUTH_FILEOP_LINK event for hardlinked directory currently not handled. ('%s' -> '%s')", fromPath, newPath); - goto CleanupAndReturn; - } - - VirtualizationRootHandle targetRoot, fromRoot; - pid_t pid; - bool messageTargetProvider = - ShouldHandleFileOpEvent( - &perfTracer, - context, - currentVnode, - nullptr, // don't pass path for target provider - action, - isDirectory, - &targetRoot, - &pid); - bool messageFromProvider = - ShouldHandleFileOpEvent( - &perfTracer, - context, - currentVnode, - fromPath, - action, - isDirectory, - &fromRoot, - &pid); - - if (!messageTargetProvider && !messageFromProvider) - { - goto CleanupAndReturn; - } - - FsidInode vnodeFsidInode = Vnode_GetFsidAndInode(currentVnode, context, true /* the inode is used for getting the path in the provider, so use linkid */); - - char procname[MAXCOMLEN + 1]; - proc_name(pid, procname, MAXCOMLEN + 1); - - { - PerfSample sample(&perfTracer, PrjFSPerfCounter_FileOp_HardLinkCreated); - - if (messageTargetProvider) - { - int kauthResult; - int kauthError = 0; - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - targetRoot, - MessageType_KtoU_NotifyFileHardLinkCreated, - currentVnode, - vnodeFsidInode, - newPath, - (messageFromProvider && targetRoot == fromRoot) ? fromPath : "", // Send "", to specify that the fromPath is not in the same root - pid, - procname, - &kauthResult, - &kauthError)) - { - KextLog_Error("HandleFileOpOperation: Request NotifyFileHardLinkCreated to destination provider %d failed, kauthResult = %u, kauthError = %u", - targetRoot, kauthResult, kauthError); - } - } - - if (messageFromProvider && (!messageTargetProvider || targetRoot != fromRoot)) // Don't send the same message to the same provider twice - { - int kauthResult; - int kauthError = 0; - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - fromRoot, - MessageType_KtoU_NotifyFileHardLinkCreated, - // vnode & target path are not in "fromRoot", so don't send them - nullptr, // vnode - FsidInode{}, - "", // Send target path as "" to signal the path is outside the Virtualization Root - fromPath, - pid, - procname, - &kauthResult, - &kauthError)) - { - KextLog_Error("HandleFileOpOperation: Request NotifyFileHardLinkCreated to source provider %d failed, kauthResult = %u, kauthError = %u", - fromRoot, kauthResult, kauthError); - } - } - } - } - else if (KAUTH_FILEOP_OPEN == action) - { - currentVnode = reinterpret_cast(arg0); - putCurrentVnode = false; - const char* path = reinterpret_cast(arg1); - - if (vnode_isdir(currentVnode)) - { - goto CleanupAndReturn; - } - - UseMainForkIfNamedStream(currentVnode, putCurrentVnode); - - bool fileFlaggedInRoot; - if (!TryGetFileIsFlaggedAsInRoot(currentVnode, context, &fileFlaggedInRoot)) - { - KextLog_ErrorVnodeProperties(currentVnode, "KAUTH_FILEOP_OPEN: checking file flags failed. Path = '%s'", path); - - goto CleanupAndReturn; - } - - if (fileFlaggedInRoot) - { - goto CleanupAndReturn; - } - - VirtualizationRootHandle root = RootHandle_None; - int pid; - if (!ShouldHandleFileOpEvent( - &perfTracer, - context, - currentVnode, - nullptr, // use vnode for lookup, not path - action, - false /* isDirectory */, - &root, - &pid)) - { - goto CleanupAndReturn; - } - - FsidInode vnodeFsidInode = Vnode_GetFsidAndInode(currentVnode, context, true /* the inode is used for getting the path in the provider, so use linkid */); - - char procname[MAXCOMLEN + 1]; - proc_name(pid, procname, MAXCOMLEN + 1); - PerfSample fileCreatedSample(&perfTracer, PrjFSPerfCounter_FileOp_FileCreated); - int kauthResult; - int kauthError; - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - root, - MessageType_KtoU_NotifyFileCreated, - currentVnode, - vnodeFsidInode, - path, - nullptr, // fromPath - pid, - procname, - &kauthResult, - &kauthError)) - { - goto CleanupAndReturn; - } - } - else if (KAUTH_FILEOP_CLOSE == action) - { - currentVnode = reinterpret_cast(arg0); - putCurrentVnode = false; - const char* path = reinterpret_cast(arg1); - int closeFlags = static_cast(arg2); - - if (vnode_isdir(currentVnode)) - { - goto CleanupAndReturn; - } - - UseMainForkIfNamedStream(currentVnode, putCurrentVnode); - - if (!FileFlagsBitIsSet(closeFlags, KAUTH_FILEOP_CLOSE_MODIFIED)) - { - goto CleanupAndReturn; - } - - VirtualizationRootHandle root = RootHandle_None; - int pid; - if (!ShouldHandleFileOpEvent( - &perfTracer, - context, - currentVnode, - nullptr, // use vnode for lookup, not path - action, - false /* isDirectory */, - &root, - &pid)) - { - goto CleanupAndReturn; - } - - FsidInode vnodeFsidInode = Vnode_GetFsidAndInode(currentVnode, context, true /* the inode is used for getting the path in the provider, so use linkid */); - - char procname[MAXCOMLEN + 1]; - proc_name(pid, procname, MAXCOMLEN + 1); - PerfSample fileModifiedSample(&perfTracer, PrjFSPerfCounter_FileOp_FileModified); - int kauthResult; - int kauthError; - if (!ProviderMessaging_TrySendRequestAndWaitForResponse( - root, - MessageType_KtoU_NotifyFileModified, - currentVnode, - vnodeFsidInode, - path, - nullptr, // fromPath - pid, - procname, - &kauthResult, - &kauthError)) - { - goto CleanupAndReturn; - } - } - else if (KAUTH_FILEOP_WILL_RENAME == action) - { - currentVnode = reinterpret_cast(arg0); - if (VnodeIsEligibleForEventHandling(currentVnode)) - { - // Records thread/vnode as rename() operation in progress, enabling optimisation in subsequent DELETE vnode listener handler - RecordPendingRenameOperation(currentVnode); - } - } - -CleanupAndReturn: - if (NULLVP != currentVnode && putCurrentVnode) - { - vnode_put(currentVnode); - } - - vfs_context_rele(context); - atomic_fetch_sub(&s_numActiveKauthEvents, 1); - - // We must always return DEFER from a fileop listener. The kernel does not allow any other - // result and will panic if we return anything else. - return KAUTH_RESULT_DEFER; -} - -static bool VnodeIsEligibleForEventHandling(vnode_t vnode) -{ - if (!VirtualizationRoot_VnodeIsOnAllowedFilesystem(vnode)) - { - return false; - } - - vtype vnodeType = vnode_vtype(vnode); - if (ShouldIgnoreVnodeType(vnodeType, vnode)) - { - return false; - } - - return true; -} - -KEXT_STATIC bool ShouldHandleVnodeOpEvent( - // In params: - PerfTracer* perfTracer, - vfs_context_t _Nonnull context, - const vnode_t vnode, - kauth_action_t action, - - // Out params: - uint32_t* vnodeFileFlags, - int* pid, - char procname[MAXCOMLEN + 1], - int* kauthResult, - int* kauthError) -{ - PerfSample handleVnodeSample(perfTracer, PrjFSPerfCounter_VnodeOp_ShouldHandle); - - *kauthResult = KAUTH_RESULT_DEFER; - - { - PerfSample isVnodeAccessSample(perfTracer, PrjFSPerfCounter_VnodeOp_ShouldHandle_IsVnodeAccessCheck); - - if (ActionBitIsSet(action, KAUTH_VNODE_ACCESS)) - { - perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_ShouldHandle_IgnoredVnodeAccessCheck); - - // From kauth.h: - // "The KAUTH_VNODE_ACCESS bit is passed to the callback if the authorisation - // request in progress is advisory, rather than authoritative. Listeners - // performing consequential work (i.e. not strictly checking authorisation) - // may test this flag to avoid performing unnecessary work." - *kauthResult = KAUTH_RESULT_DEFER; - return false; - } - } - - { - PerfSample readFlagsSample(perfTracer, PrjFSPerfCounter_VnodeOp_ShouldHandle_ReadFileFlags); - if (!TryReadVNodeFileFlags(vnode, context, vnodeFileFlags)) - { - *kauthError = EBADF; - *kauthResult = KAUTH_RESULT_DENY; - return false; - } - - if (!FileFlagsBitIsSet(*vnodeFileFlags, FileFlags_IsInVirtualizationRoot)) - { - // This vnode is not part of ANY virtualization root, so exit now before doing any more work. - // This gives us a cheap way to avoid adding overhead to IO outside of a virtualization root. - - perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_ShouldHandle_NotInAnyRoot); - - *kauthResult = KAUTH_RESULT_DEFER; - return false; - } - } - - *pid = vfs_context_pid(context); - proc_name(*pid, procname, MAXCOMLEN + 1); - - if (FileFlagsBitIsSet(*vnodeFileFlags, FileFlags_IsEmpty)) - { - // This vnode is not yet hydrated, so do not allow a file system crawler to force hydration. - // Once a vnode is hydrated, it's fine to allow crawlers to access those contents. - - PerfSample crawlerSample(perfTracer, PrjFSPerfCounter_VnodeOp_ShouldHandle_CheckFileSystemCrawler); - if (IsFileSystemCrawler(procname)) - { - // We must DENY file system crawlers rather than DEFER. - // If we allow the crawler's access to succeed without hydrating, the kauth result will be cached and we won't - // get called again, so we lose the opportunity to hydrate the file/directory and it will appear as though - // it is missing its contents. - - perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_ShouldHandle_DeniedFileSystemCrawler); - - *kauthResult = KAUTH_RESULT_DENY; - return false; - } - } - - return true; -} - -static bool TryGetVirtualizationRoot( - // In params: - PerfTracer* perfTracer, - vfs_context_t _Nonnull context, - const vnode_t vnode, - pid_t pidMakingRequest, - ProviderCallbackPolicy callbackPolicy, - bool denyIfOffline, - - // Out params: - VirtualizationRootHandle* root, - FsidInode* vnodeFsidInode, - int* kauthResult, - int* kauthError, - ProviderStatus* _Nullable providerStatus) -{ - PerfSample findRootSample(perfTracer, PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot); - - if (providerStatus != nullptr) - { - *providerStatus = Provider_StatusUnknown; - } - - *root = VnodeCache_FindRootForVnode( - perfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - vnode, - context); - - if (RootHandle_ProviderTemporaryDirectory == *root) - { - perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_TemporaryDirectory); - - *kauthResult = KAUTH_RESULT_DEFER; - return false; - } - else if (RootHandle_None == *root) - { - KextLog_File(vnode, "No virtualization root found for file with set flag."); - perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_NoRootFound); - - *kauthResult = KAUTH_RESULT_DEFER; - return false; - } - - ActiveProviderProperties provider = VirtualizationRoot_GetActiveProvider(*root); - if (providerStatus != nullptr) - { - *providerStatus = provider.isOnline ? Provider_IsOnline : Provider_IsOffline; - } - - if (!provider.isOnline) - { - perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_ProviderOffline); - - *kauthResult = KAUTH_RESULT_DEFER; - if (denyIfOffline && !VirtualizationRoots_ProcessMayAccessOfflineRoots(pidMakingRequest)) - { - *kauthResult = KAUTH_RESULT_DENY; - } - - return false; - } - - if (provider.pid == pidMakingRequest) - { - // If the calling process is the provider, we must exit right away to avoid deadlocks - perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_OriginatedByProvider); - - *kauthResult = KAUTH_RESULT_DEFER; - return false; - } - - if (callbackPolicy == CallbackPolicy_UserInitiatedOnly && !CurrentProcessIsAllowedToHydrate()) - { - // Prevent hydration etc. by system services - KextLog_Info("TryGetVirtualizationRoot: process %d is not allowed to hydrate.", pidMakingRequest); - perfTracer->IncrementCount(PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_UserRestriction); - - *kauthResult = KAUTH_RESULT_DENY; - return false; - } - - *vnodeFsidInode = Vnode_GetFsidAndInode(vnode, context, true /* the inode is used for getting the path in the provider, so use linkid */); - - return true; -} - -KEXT_STATIC bool CurrentProcessIsAllowedToHydrate() -{ - bool nonServiceUser = false; - - proc_t process = proc_self(); - - while (true) - { - kauth_cred_t credential = kauth_cred_proc_ref(process); - uid_t processUID = kauth_cred_getuid(credential); - kauth_cred_unref(&credential); - - if (processUID >= 500) - { - nonServiceUser = true; - break; - } - - pid_t parentPID = proc_ppid(process); - if (parentPID <= 1) - { - break; - } - - proc_t parentProcess = proc_find(parentPID); - proc_rele(process); - process = parentProcess; - if (parentProcess == nullptr) - { - KextLog_Error("CurrentProcessIsAllowedToHydrate: Failed to locate ancestor process %d for current process %d\n", parentPID, proc_selfpid()); - break; - } - } - - - if (process != nullptr) - { - proc_rele(process); - } - - if (!nonServiceUser) - { - // When amfid is invoked to check the code signature of a library which has not been hydrated, - // blocking hydration would cause the launch of an application which depends on the library to fail, - // so amfid should always be allowed to hydrate. - char buffer[MAXCOMLEN + 1] = ""; - proc_selfname(buffer, sizeof(buffer)); - if (0 == strcmp(buffer, "amfid")) - { - nonServiceUser = true; - } - } - - return nonServiceUser; -} - -static VirtualizationRootHandle FindRootForVnodeWithFileOpEvent( - PerfTracer* perfTracer, - vfs_context_t _Nonnull context, - const vnode_t vnode, - kauth_action_t action, - bool isDirectory) -{ - VirtualizationRootHandle root = RootHandle_None; - - if (isDirectory) - { - if (KAUTH_FILEOP_RENAME == action) - { - // Directory renames into (or out) of virtualization roots require invalidating all of the entries - // within the directory being renamed (because all of those entires now have a new virtualzation root). - // Rather than trying to find all vnodes in the cache that are children of the directory being renamed - // we simply invalidate the entire cache. - // - // Potential future optimizations include: - // - Only invalidate the cache if the rename moves a directory in or out of a directory - // - Keeping a per-root generation ID in the cache to allow invalidating a subset of the cache - VnodeCache_InvalidateCache(perfTracer); - } - - root = VnodeCache_FindRootForVnode( - perfTracer, - PrjFSPerfCounter_FileOp_Vnode_Cache_Hit, - PrjFSPerfCounter_FileOp_Vnode_Cache_Miss, - PrjFSPerfCounter_FileOp_FindRoot, - PrjFSPerfCounter_FileOp_FindRoot_Iteration, - vnode, - context); - } - else - { - // TODO(#1188): Once all hardlink paths are delivered to the appropriate root(s) - // check if the KAUTH_FILEOP_LINK case can be removed. For now the check is required to make - // sure we're looking up the most up-to-date parent information for the vnode on the next - // access to the vnode cache - switch(action) - { - case KAUTH_FILEOP_LINK: - root = VnodeCache_InvalidateVnodeRootAndGetLatestRoot( - perfTracer, - PrjFSPerfCounter_FileOp_Vnode_Cache_Hit, - PrjFSPerfCounter_FileOp_Vnode_Cache_Miss, - PrjFSPerfCounter_FileOp_FindRoot, - PrjFSPerfCounter_FileOp_FindRoot_Iteration, - vnode, - context); - break; - - case KAUTH_FILEOP_RENAME: - root = VnodeCache_RefreshRootForVnode( - perfTracer, - PrjFSPerfCounter_FileOp_Vnode_Cache_Hit, - PrjFSPerfCounter_FileOp_Vnode_Cache_Miss, - PrjFSPerfCounter_FileOp_FindRoot, - PrjFSPerfCounter_FileOp_FindRoot_Iteration, - vnode, - context); - break; - - default: - root = VnodeCache_FindRootForVnode( - perfTracer, - PrjFSPerfCounter_FileOp_Vnode_Cache_Hit, - PrjFSPerfCounter_FileOp_Vnode_Cache_Miss, - PrjFSPerfCounter_FileOp_FindRoot, - PrjFSPerfCounter_FileOp_FindRoot_Iteration, - vnode, - context); - break; - } - } - - return root; -} - -KEXT_STATIC bool ShouldHandleFileOpEvent( - // In params: - PerfTracer* perfTracer, - vfs_context_t _Nonnull context, - const vnode_t vnode, - const char* _Nullable path, // if non-null, path is used for finding provider, not vnode - kauth_action_t action, - bool isDirectory, - - // Out params: - VirtualizationRootHandle* root, - int* pid) -{ - PerfSample fileOpSample(perfTracer, PrjFSPerfCounter_FileOp_ShouldHandle); - - *root = RootHandle_None; - - if (!VnodeIsEligibleForEventHandling(vnode)) - { - return false; - } - - if (path != nullptr) - { - PerfSample findRootSample(perfTracer, PrjFSPerfCounter_FileOp_ShouldHandle_FindProviderPathBased); - - *root = ActiveProvider_FindForPath(path); - if (!VirtualizationRoot_IsValidRootHandle(*root)) - { - perfTracer->IncrementCount(PrjFSPerfCounter_FileOp_ShouldHandle_NoProviderFound); - return false; - } - } - else - { - PerfSample findRootSample(perfTracer, PrjFSPerfCounter_FileOp_ShouldHandle_FindVirtualizationRoot); - - *root = FindRootForVnodeWithFileOpEvent(perfTracer, context, vnode, action, isDirectory); - - if (!VirtualizationRoot_IsValidRootHandle(*root)) - { - perfTracer->IncrementCount(PrjFSPerfCounter_FileOp_ShouldHandle_NoRootFound); - return false; - } - } - - { - PerfSample checkRootOnlineSample(perfTracer, PrjFSPerfCounter_FileOp_ShouldHandle_CheckProvider); - - ActiveProviderProperties provider = VirtualizationRoot_GetActiveProvider(*root); - - if (!provider.isOnline) - { - perfTracer->IncrementCount(PrjFSPerfCounter_FileOp_ShouldHandle_OfflineRoot); - return false; - } - - // If the calling process is the provider, we must exit right away to avoid deadlocks - *pid = vfs_context_pid(context); - if (*pid == provider.pid) - { - perfTracer->IncrementCount(PrjFSPerfCounter_FileOp_ShouldHandle_OriginatedByProvider); - return false; - } - } - - return true; -} - -static void WaitForListenerCompletion() -{ - // Wait until all kauth events have noticed and returned. - // Always sleeping at least once reduces the likelihood of a race condition - // between kauth_unlisten_scope and the s_numActiveKauthEvents increment at - // the start of the callback. - // This race condition and the inability to work around it is a longstanding - // bug in the xnu kernel - see comment block in RemoveListener() function of - // the KauthORama sample code: - // https://developer.apple.com/library/archive/samplecode/KauthORama/Listings/KauthORama_c.html#//apple_ref/doc/uid/DTS10003633-KauthORama_c-DontLinkElementID_3 - do - { - Mutex_Sleep(1, nullptr, nullptr); - } while (atomic_load(&s_numActiveKauthEvents) > 0); -} - - -static errno_t GetVNodeAttributes(vnode_t vn, vfs_context_t _Nonnull context, struct vnode_attr* attrs) -{ - VATTR_INIT(attrs); - VATTR_WANTED(attrs, va_flags); - - return vnode_getattr(vn, attrs, context); -} - -static bool TryReadVNodeFileFlags(vnode_t vn, vfs_context_t _Nonnull context, uint32_t* flags) -{ - struct vnode_attr attributes = {}; - *flags = 0; - errno_t err = GetVNodeAttributes(vn, context, &attributes); - if (0 != err) - { - // May fail on some file system types? Perhaps we should early-out depending on mount point anyway. - // If we see failures we should also consider: - // - Falling back on vnode lookup (or custom cache) to determine if file is in the root - // - Assuming files are empty if we can't read the flags - KextLog_FileError(vn, "ReadVNodeFileFlags: GetVNodeAttributes failed with error %d; vnode type: %d, recycled: %s", err, vnode_vtype(vn), vnode_isrecycled(vn) ? "yes" : "no"); - return false; - } - - assert(VATTR_IS_SUPPORTED(&attributes, va_flags)); - *flags = attributes.va_flags; - return true; -} - -KEXT_STATIC_INLINE bool FileFlagsBitIsSet(uint32_t fileFlags, uint32_t bit) -{ - // Note: if multiple bits are set in 'bit', this will return true if ANY are set in fileFlags - return 0 != (fileFlags & bit); -} - -KEXT_STATIC_INLINE bool TryGetFileIsFlaggedAsInRoot(vnode_t vnode, vfs_context_t _Nonnull context, bool* flaggedInRoot) -{ - uint32_t vnodeFileFlags; - *flaggedInRoot = false; - if (!TryReadVNodeFileFlags(vnode, context, &vnodeFileFlags)) - { - return false; - } - - *flaggedInRoot = FileFlagsBitIsSet(vnodeFileFlags, FileFlags_IsInVirtualizationRoot); - return true; -} - -KEXT_STATIC_INLINE bool ActionBitIsSet(kauth_action_t action, kauth_action_t mask) -{ - return action & mask; -} - -KEXT_STATIC bool IsFileSystemCrawler(const char* procname) -{ - // These process will crawl the file system and force a full hydration - if (!strcmp(procname, "mds") || - !strcmp(procname, "mdworker") || - !strcmp(procname, "mdworker_shared") || - !strcmp(procname, "mds_stores") || - !strcmp(procname, "fseventsd") || - !strcmp(procname, "Spotlight")) - { - return true; - } - - return false; -} - -KEXT_STATIC bool ShouldIgnoreVnodeType(vtype vnodeType, vnode_t vnode) -{ - switch (vnodeType) - { - case VNON: - case VBLK: - case VCHR: - case VSOCK: - case VFIFO: - case VBAD: - return true; - case VREG: - case VDIR: - case VLNK: - break; - case VSTR: - case VCPLX: - KextLog_FileInfo(vnode, "vnode with type %s encountered", vnodeType == VSTR ? "VSTR" : "VCPLX"); - break; - default: - KextLog_FileInfo(vnode, "vnode with unknown type %d encountered", vnodeType); - } - - return false; -} diff --git a/ProjFS.Mac/PrjFSKext/KauthHandler.hpp b/ProjFS.Mac/PrjFSKext/KauthHandler.hpp deleted file mode 100644 index c1ea51d350..0000000000 --- a/ProjFS.Mac/PrjFSKext/KauthHandler.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef KauthHandler_h -#define KauthHandler_h - -#include "public/Message.h" -#include "VirtualizationRoots.hpp" - -kern_return_t KauthHandler_Init(); -kern_return_t KauthHandler_Cleanup(); - -#endif /* KauthHandler_h */ diff --git a/ProjFS.Mac/PrjFSKext/KauthHandlerPrivate.hpp b/ProjFS.Mac/PrjFSKext/KauthHandlerPrivate.hpp deleted file mode 100644 index 72a3ec158a..0000000000 --- a/ProjFS.Mac/PrjFSKext/KauthHandlerPrivate.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "kernel-header-wrappers/kauth.h" - -// Define missing symbol when building with 10.13 SDK/Xcode 9 -#ifndef KAUTH_FILEOP_WILL_RENAME -#define KAUTH_FILEOP_WILL_RENAME 8 -#endif diff --git a/ProjFS.Mac/PrjFSKext/KauthHandlerTestable.hpp b/ProjFS.Mac/PrjFSKext/KauthHandlerTestable.hpp deleted file mode 100644 index a609a54b65..0000000000 --- a/ProjFS.Mac/PrjFSKext/KauthHandlerTestable.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "public/PrjFSCommon.h" -#include -#include "../PrjFSKext/kernel-header-wrappers/kauth.h" -#include "../PrjFSKext/kernel-header-wrappers/vnode.h" -#include "../PrjFSKext/PerformanceTracing.hpp" -#include "../PrjFSKext/VirtualizationRoots.hpp" -#include "KauthHandler.hpp" -#include "KauthHandlerPrivate.hpp" - -#ifndef __cplusplus -#error None of the kext code is set up for being called from C or Objective-C; change the including file to C++ or Objective-C++ -#endif - -#ifndef KEXT_UNIT_TESTING -#error This class should only be called for unit tests -#endif - -struct FsidInode; - -extern uint32_t s_maxPendingRenames; -extern uint32_t s_pendingRenameCount; - -KEXT_STATIC_INLINE bool FileFlagsBitIsSet(uint32_t fileFlags, uint32_t bit); -KEXT_STATIC_INLINE bool ActionBitIsSet(kauth_action_t action, kauth_action_t mask); -KEXT_STATIC_INLINE bool TryGetFileIsFlaggedAsInRoot(vnode_t vnode, vfs_context_t context, bool* flaggedInRoot); -KEXT_STATIC bool IsFileSystemCrawler(const char* procname); -KEXT_STATIC bool ShouldIgnoreVnodeType(vtype vnodeType, vnode_t vnode); -KEXT_STATIC int HandleVnodeOperation( - kauth_cred_t credential, - void* idata, - kauth_action_t action, - uintptr_t arg0, - uintptr_t arg1, - uintptr_t arg2, - uintptr_t arg3); -KEXT_STATIC int HandleFileOpOperation( - kauth_cred_t credential, - void* idata, - kauth_action_t action, - uintptr_t arg0, - uintptr_t arg1, - uintptr_t arg2, - uintptr_t arg3); -KEXT_STATIC bool ShouldHandleVnodeOpEvent( - // In params: - PerfTracer* perfTracer, - vfs_context_t context, - const vnode_t vnode, - kauth_action_t action, - - // Out params: - uint32_t* vnodeFileFlags, - int* pid, - char procname[MAXCOMLEN + 1], - int* kauthResult, - int* kauthError); -KEXT_STATIC bool ShouldHandleFileOpEvent( - // In params: - PerfTracer* perfTracer, - vfs_context_t context, - const vnode_t vnode, - const char* path, - kauth_action_t action, - bool isDirectory, - - // Out params: - VirtualizationRootHandle* root, - int* pid); -KEXT_STATIC void UseMainForkIfNamedStream(vnode_t& vnode, bool& putVnodeWhenDone); -KEXT_STATIC bool CurrentProcessIsAllowedToHydrate(); -KEXT_STATIC bool InitPendingRenames(); -KEXT_STATIC void CleanupPendingRenames(); -KEXT_STATIC void ResizePendingRenames(uint32_t newMaxPendingRenames); -KEXT_STATIC void RecordPendingRenameOperation(vnode_t vnode); -KEXT_STATIC bool DeleteOpIsForRename(vnode_t vnode); - diff --git a/ProjFS.Mac/PrjFSKext/KextLog.cpp b/ProjFS.Mac/PrjFSKext/KextLog.cpp deleted file mode 100644 index ee2cf64eed..0000000000 --- a/ProjFS.Mac/PrjFSKext/KextLog.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include -#include -#include -#include -#include - -#include "KextLog.hpp" -#include "Locks.hpp" -#include "Memory.hpp" -#include "PrjFSLogUserClient.hpp" -#include "public/PrjFSLogClientShared.h" - -static PrjFSLogUserClient* s_currentUserClient; -static RWLock s_kextLogRWLock = {}; - -struct KextLog_StackMessageBuffer -{ - KextLog_MessageHeader header; - char logString[128]; -}; - -bool KextLog_Init() -{ - s_kextLogRWLock = RWLock_Alloc(); - if (!RWLock_IsValid(s_kextLogRWLock)) - { - return false; - } - return true; -} - -void KextLog_Cleanup() -{ - if (RWLock_IsValid(s_kextLogRWLock)) - { - RWLock_FreeMemory(&s_kextLogRWLock); - } -} - -bool KextLog_RegisterUserClient(PrjFSLogUserClient* userClient) -{ - bool success = false; - - RWLock_AcquireExclusive(s_kextLogRWLock); - { - if (s_currentUserClient == nullptr) - { - s_currentUserClient = userClient; - success = true; - } - } - RWLock_ReleaseExclusive(s_kextLogRWLock); - - return success; -} - -void KextLog_DeregisterUserClient(PrjFSLogUserClient* userClient) -{ - RWLock_AcquireExclusive(s_kextLogRWLock); - { - if (userClient == s_currentUserClient) - { - s_currentUserClient = nullptr; - } - } - RWLock_ReleaseExclusive(s_kextLogRWLock); -} - -void KextLog_Printf(KextLog_Level loglevel, const char* fmt, ...) -{ - if (nullptr == s_currentUserClient) - { - return; - } - - // Stack-allocated message with 128-character string buffer for fast path - struct KextLog_StackMessageBuffer message = {}; - KextLog_MessageHeader* messagePtr = &message.header; - - va_list args; - va_start(args, fmt); - int messageLength = vsnprintf(message.logString, sizeof(message.logString), fmt, args); - va_end (args); - uint32_t messageSize; - if (__builtin_uadd_overflow(sizeof(KextLog_MessageHeader), messageLength, &messageSize) || - __builtin_uadd_overflow(messageSize, 1, &messageSize)) - { - // Overflow occurred. - return; - } - - uint32_t messageFlags = 0; - bool messageAllocated = false; - if (messageLength >= sizeof(message.logString)) - { - messagePtr = static_cast(Memory_Alloc(messageSize)); - if (nullptr != messagePtr) - { - messageAllocated = true; - va_start(args, fmt); - vsnprintf(messagePtr->logString, messageLength + 1, fmt, args); - va_end (args); - } - else - { - // Send the truncated in-stack message after all if allocation failed - messagePtr = &message.header; - messageSize = sizeof(message); - messageFlags |= LogMessageFlag_LogMessageTruncated; - } - } - - RWLock_AcquireShared(s_kextLogRWLock); - { - if (s_currentUserClient != nullptr) - { - messagePtr->flags = messageFlags; - messagePtr->level = loglevel; - uint64_t time = mach_absolute_time(); - messagePtr->machAbsoluteTimestamp = time; - s_currentUserClient->sendLogMessage(messagePtr, messageSize); - } - } - RWLock_ReleaseShared(s_kextLogRWLock); - - if (messageAllocated) - { - Memory_Free(messagePtr, messageSize); - } -} - -const void* KextLog_Unslide(const void* pointer) -{ - vm_offset_t outPointer = 0; - vm_kernel_unslide_or_perm_external(reinterpret_cast(pointer), &outPointer); - return reinterpret_cast(outPointer); -} diff --git a/ProjFS.Mac/PrjFSKext/KextLog.hpp b/ProjFS.Mac/PrjFSKext/KextLog.hpp deleted file mode 100644 index 8b85b375e2..0000000000 --- a/ProjFS.Mac/PrjFSKext/KextLog.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef KextLog_h -#define KextLog_h - -#include "public/PrjFSCommon.h" -#include "PrjFSClasses.hpp" -#include "public/PrjFSLogClientShared.h" -#include "VnodeUtilities.hpp" -#include "kernel-header-wrappers/vnode.h" -#include "kernel-header-wrappers/mount.h" -#include - -// Redeclared as printf-like to get format string warnings on assertf() -extern "C" void panic(const char* fmt, ...) __printflike(1, 2); - -bool KextLog_Init(); -void KextLog_Cleanup(); - -#define KextLog_Error(format, ...) KextLog_Printf(KEXTLOG_ERROR, format, ##__VA_ARGS__) -#define KextLog_Info(format, ...) KextLog_Printf(KEXTLOG_INFO, format, ##__VA_ARGS__) -#define KextLog(format, ...) KextLog_Printf(KEXTLOG_DEFAULT, format, ##__VA_ARGS__) - -bool KextLog_RegisterUserClient(PrjFSLogUserClient* userClient); -void KextLog_DeregisterUserClient(PrjFSLogUserClient* userClient); -void KextLog_Printf(KextLog_Level loglevel, const char* fmt, ...) __printflike(2,3); -// Prepares a kernel pointer for printing to user space without revealing the -// genuine kernel-space address, which would be a security issue. -const void* KextLog_Unslide(const void* pointer); - - -// Helper macros/function for logging with file paths. Note that the path must -// be the last % format code in the format string, but the vnode is the first -// argument following the format string. An unfortunate implementation detail, -// but not too problematic if you just use the macros and not the function directly. -// -// The reason for the helper function/macro split is that we want to encourage -// the compiler to create a new stack frame to hold the path buffer in order to -// avoid bloating the caller's stack frame; we can't do this in a macro. -// We need the macros below for the format string concatenation; we can't do -// this in a function. -struct vnode; -extern "C" int vn_getpath(struct vnode *vp, char *pathbuf, int *len); -template - void KextLogFile_Printf(KextLog_Level loglevel, struct vnode* vnode, const char* fmt, args... a) - { - char vnodePath[PrjFSMaxPath] = ""; - int vnodePathLength = PrjFSMaxPath; - vn_getpath(vnode, vnodePath, &vnodePathLength); - const char* vnodeTypeString = Vnode_GetTypeAsString(vnode); - KextLog_Printf(loglevel, fmt, a..., vnodePath, vnodeTypeString); - } - -// The dummy _os_log_verify_format_str() expression here is for using its -// compile time printf format checking, as template varargs can't be annotated -// as __printflike. -// The %s at the end of the format string for the vnode path is implicit. -#define KextLog_FileError(vnode, format, ...) ({ _os_log_verify_format_str(format, ##__VA_ARGS__); KextLogFile_Printf(KEXTLOG_ERROR, vnode, format " (vnode path: '%s', type = %s)", ##__VA_ARGS__); }) -#define KextLog_FileInfo(vnode, format, ...) ({ _os_log_verify_format_str(format, ##__VA_ARGS__); KextLogFile_Printf(KEXTLOG_INFO, vnode, format " (vnode path: '%s', type = %s)", ##__VA_ARGS__); }) -#define KextLog_File(vnode, format, ...) ({ _os_log_verify_format_str(format, ##__VA_ARGS__); KextLogFile_Printf(KEXTLOG_DEFAULT, vnode, format " (vnode path: '%s', type = %s)", ##__VA_ARGS__); }) - - -// See comments for KextLogFile_Printf() above for rationale. -template - void KextLog_PrintfVnodePathAndProperties(KextLog_Level loglevel, struct vnode* vnode, const char* fmt, args... a) - { - char vnodePath[PrjFSMaxPath] = ""; - int vnodePathLength = PrjFSMaxPath; - vn_getpath(vnode, vnodePath, &vnodePathLength); - - const char* name = vnode_getname(vnode); - mount_t mount = vnode_mount(vnode); - vfsstatfs* vfsStat = mount != nullptr ? vfs_statfs(mount) : nullptr; - - KextLog_Printf(loglevel, fmt, a..., vnodePath, name ?: "[NULL]", vnode_vtype(vnode), vnode_isrecycled(vnode) ? "yes" : "no", vfsStat ? vfsStat->f_mntonname : "[NULL]"); - - if (name != nullptr) - { - // Cancels out the vnode_getname() call above, which incremented the refcount on the returned string. - vnode_putname(name); - } - } - -#define KextLog_ErrorVnodeProperties(vnode, format, ...) \ - ({ \ - _os_log_verify_format_str(format, ##__VA_ARGS__); \ - KextLog_PrintfVnodeProperties(KEXTLOG_ERROR, vnode, format " (vnode name: '%s', type: %d, recycling: %s, mount point mounted at path '%s')", ##__VA_ARGS__); \ - }) - - -// See comments for KextLogFile_Printf() above for rationale. -template - void KextLog_PrintfVnodeProperties(KextLog_Level loglevel, struct vnode* vnode, const char* fmt, args... a) - { - const char* name = vnode_getname(vnode); - mount_t mount = vnode_mount(vnode); - vfsstatfs* vfsStat = mount != nullptr ? vfs_statfs(mount) : nullptr; - - KextLog_Printf(loglevel, fmt, a..., name ?: "[NULL]", vnode_vtype(vnode), vnode_isrecycled(vnode) ? "yes" : "no", vfsStat ? vfsStat->f_mntonname : "[NULL]"); - - if (name != nullptr) - { - // Cancels out the vnode_getname() call above, which incremented the refcount on the returned string. - vnode_putname(name); - } - } - -#define KextLog_ErrorVnodePathAndProperties(vnode, format, ...) \ - ({ \ - _os_log_verify_format_str(format, ##__VA_ARGS__); \ - KextLog_PrintfVnodePathAndProperties(KEXTLOG_ERROR, vnode, format " (vnode path: '%s', name: '%s', type: %d, recycling: %s, mount point mounted at path '%s')", ##__VA_ARGS__); \ - }) - - -#define KextLog_VnodeOp(vnode, vnodeType, procname, action, message) \ - do { \ - if (VDIR == (vnodeType)) \ - { \ - KextLog_File( \ - vnode, \ - message ". Proc name: %s. Directory vnode action: %s%s%s%s%s%s%s%s%s%s%s%s%s \n ", \ - procname, \ - (action & KAUTH_VNODE_LIST_DIRECTORY) ? " \n KAUTH_VNODE_LIST_DIRECTORY" : "", \ - (action & KAUTH_VNODE_ADD_FILE) ? " \n KAUTH_VNODE_ADD_FILE" : "", \ - (action & KAUTH_VNODE_SEARCH) ? " \n KAUTH_VNODE_SEARCH" : "", \ - (action & KAUTH_VNODE_DELETE) ? " \n KAUTH_VNODE_DELETE" : "", \ - (action & KAUTH_VNODE_ADD_SUBDIRECTORY) ? " \n KAUTH_VNODE_ADD_SUBDIRECTORY" : "", \ - (action & KAUTH_VNODE_DELETE_CHILD) ? " \n KAUTH_VNODE_DELETE_CHILD" : "", \ - (action & KAUTH_VNODE_READ_ATTRIBUTES) ? " \n KAUTH_VNODE_READ_ATTRIBUTES" : "", \ - (action & KAUTH_VNODE_WRITE_ATTRIBUTES) ? " \n KAUTH_VNODE_WRITE_ATTRIBUTES" : "", \ - (action & KAUTH_VNODE_READ_EXTATTRIBUTES) ? " \n KAUTH_VNODE_READ_EXTATTRIBUTES" : "", \ - (action & KAUTH_VNODE_WRITE_EXTATTRIBUTES) ? " \n KAUTH_VNODE_WRITE_EXTATTRIBUTES" : "", \ - (action & KAUTH_VNODE_READ_SECURITY) ? " \n KAUTH_VNODE_READ_SECURITY" : "", \ - (action & KAUTH_VNODE_WRITE_SECURITY) ? " \n KAUTH_VNODE_WRITE_SECURITY" : "", \ - (action & KAUTH_VNODE_TAKE_OWNERSHIP) ? " \n KAUTH_VNODE_TAKE_OWNERSHIP" : ""); \ - } \ - else \ - { \ - KextLog_File( \ - vnode, \ - message ". Proc name: %s. File vnode action: %s%s%s%s%s%s%s%s%s%s%s%s \n ", \ - procname, \ - (action & KAUTH_VNODE_READ_DATA) ? " \n KAUTH_VNODE_READ_DATA" : "", \ - (action & KAUTH_VNODE_WRITE_DATA) ? " \n KAUTH_VNODE_WRITE_DATA" : "", \ - (action & KAUTH_VNODE_EXECUTE) ? " \n KAUTH_VNODE_EXECUTE" : "", \ - (action & KAUTH_VNODE_DELETE) ? " \n KAUTH_VNODE_DELETE" : "", \ - (action & KAUTH_VNODE_APPEND_DATA) ? " \n KAUTH_VNODE_APPEND_DATA" : "", \ - (action & KAUTH_VNODE_READ_ATTRIBUTES) ? " \n KAUTH_VNODE_READ_ATTRIBUTES" : "", \ - (action & KAUTH_VNODE_WRITE_ATTRIBUTES) ? " \n KAUTH_VNODE_WRITE_ATTRIBUTES" : "", \ - (action & KAUTH_VNODE_READ_EXTATTRIBUTES) ? " \n KAUTH_VNODE_READ_EXTATTRIBUTES" : "", \ - (action & KAUTH_VNODE_WRITE_EXTATTRIBUTES) ? " \n KAUTH_VNODE_WRITE_EXTATTRIBUTES" : "", \ - (action & KAUTH_VNODE_READ_SECURITY) ? " \n KAUTH_VNODE_READ_SECURITY" : "", \ - (action & KAUTH_VNODE_WRITE_SECURITY) ? " \n KAUTH_VNODE_WRITE_SECURITY" : "", \ - (action & KAUTH_VNODE_TAKE_OWNERSHIP) ? " \n KAUTH_VNODE_TAKE_OWNERSHIP" : ""); \ - } \ - } while (0) - -#endif /* KextLog_h */ diff --git a/ProjFS.Mac/PrjFSKext/Locks.cpp b/ProjFS.Mac/PrjFSKext/Locks.cpp deleted file mode 100644 index c4662c810e..0000000000 --- a/ProjFS.Mac/PrjFSKext/Locks.cpp +++ /dev/null @@ -1,209 +0,0 @@ -#include -#include -#include -#include -#include - -#include "public/PrjFSCommon.h" -#include "Locks.hpp" - -static lck_grp_t* s_lockGroup = nullptr; - -kern_return_t Locks_Init() -{ - if (nullptr != s_lockGroup) - { - return KERN_FAILURE; - } - - s_lockGroup = lck_grp_alloc_init(PrjFSKextBundleId, LCK_GRP_ATTR_NULL); - if (nullptr == s_lockGroup) - { - return KERN_FAILURE; - } - - return KERN_SUCCESS; -} - -kern_return_t Locks_Cleanup() -{ - if (nullptr != s_lockGroup) - { - lck_grp_free(s_lockGroup); - s_lockGroup = nullptr; - - return KERN_SUCCESS; - } - - return KERN_FAILURE; -} - -// Mutex implementation functions - -Mutex Mutex_Alloc() -{ - return (Mutex){ lck_mtx_alloc_init(s_lockGroup, LCK_ATTR_NULL) }; -} - -void Mutex_FreeMemory(Mutex* mutex) -{ - lck_mtx_free(mutex->p, s_lockGroup); - mutex->p = nullptr; -} - -bool Mutex_IsValid(Mutex mutex) -{ - return mutex.p != nullptr; -} - -void Mutex_Acquire(Mutex mutex) -{ - lck_mtx_lock(mutex.p); -} - -void Mutex_Release(Mutex mutex) -{ - lck_mtx_unlock(mutex.p); -} - -void Mutex_Sleep(int seconds, void* channel, Mutex* _Nullable mutex) -{ - struct timespec timeout; - timeout.tv_sec = seconds; - timeout.tv_nsec = 0; - - msleep(channel, nullptr != mutex ? mutex->p : nullptr, PUSER, "org.vfsforgit.PrjFSKext.Sleep", &timeout); -} - -// RWLock implementation functions - -RWLock RWLock_Alloc() -{ - return (RWLock){ lck_rw_alloc_init(s_lockGroup, LCK_ATTR_NULL) }; -} - -bool RWLock_IsValid(RWLock rwLock) -{ - return rwLock.p != nullptr; -} - -void RWLock_FreeMemory(RWLock* rwLock) -{ - lck_rw_free(rwLock->p, s_lockGroup); - rwLock->p = nullptr; -} - -void RWLock_AcquireShared(RWLock& rwLock) -{ - lck_rw_lock_shared(rwLock.p); - -#if PRJFS_LOCK_CORRECTNESS_CHECKS - assert(rwLock.exclOwner == nullptr); - atomic_fetch_add(&rwLock.sharedOwnersCount, 1); - uint32_t lowBits = static_cast(reinterpret_cast(current_thread())); - atomic_fetch_xor(&rwLock.sharedOwnersXor, lowBits); -#endif -} - -void RWLock_ReleaseShared(RWLock& rwLock) -{ -#if PRJFS_LOCK_CORRECTNESS_CHECKS - uint32_t lowBits = static_cast(reinterpret_cast(current_thread())); - atomic_fetch_xor(&rwLock.sharedOwnersXor, lowBits); - atomic_fetch_sub(&rwLock.sharedOwnersCount, 1); - assert(rwLock.exclOwner == nullptr); -#endif - - lck_rw_unlock_shared(rwLock.p); -} - -void RWLock_AcquireExclusive(RWLock& rwLock) -{ - lck_rw_lock_exclusive(rwLock.p); - -#if PRJFS_LOCK_CORRECTNESS_CHECKS - assert(rwLock.sharedOwnersCount == 0); - assert(rwLock.sharedOwnersXor == 0); - rwLock.exclOwner = current_thread(); -#endif -} - -void RWLock_ReleaseExclusive(RWLock& rwLock) -{ -#if PRJFS_LOCK_CORRECTNESS_CHECKS - assert(rwLock.sharedOwnersCount == 0); - assert(rwLock.sharedOwnersXor == 0); - assert(rwLock.exclOwner == current_thread()); - rwLock.exclOwner = nullptr; -#endif - - lck_rw_unlock_exclusive(rwLock.p); -} - -bool RWLock_AcquireSharedToExclusive(RWLock& rwLock) -{ -#if PRJFS_LOCK_CORRECTNESS_CHECKS - uint32_t lowBits = static_cast(reinterpret_cast(current_thread())); - atomic_fetch_xor(&rwLock.sharedOwnersXor, lowBits); - atomic_fetch_sub(&rwLock.sharedOwnersCount, 1); - assert(rwLock.exclOwner == nullptr); -#endif - - bool success = lck_rw_lock_shared_to_exclusive(rwLock.p); - -#if PRJFS_LOCK_CORRECTNESS_CHECKS - if (success) - { - assert(rwLock.sharedOwnersCount == 0); - assert(rwLock.sharedOwnersXor == 0); - rwLock.exclOwner = current_thread(); - } -#endif - - return success; -} - -void RWLock_DropExclusiveToShared(RWLock& rwLock) -{ -#if PRJFS_LOCK_CORRECTNESS_CHECKS - assert(rwLock.sharedOwnersCount == 0); - assert(rwLock.sharedOwnersXor == 0); - assert(rwLock.exclOwner == current_thread()); - rwLock.exclOwner = nullptr; -#endif - lck_rw_lock_exclusive_to_shared(rwLock.p); -#if PRJFS_LOCK_CORRECTNESS_CHECKS - assert(rwLock.exclOwner == nullptr); - atomic_fetch_add(&rwLock.sharedOwnersCount, 1); - uint32_t lowBits = static_cast(reinterpret_cast(current_thread())); - atomic_fetch_xor(&rwLock.sharedOwnersXor, lowBits); -#endif -} - - -SpinLock SpinLock_Alloc() -{ - return SpinLock { lck_spin_alloc_init(s_lockGroup, LCK_ATTR_NULL) }; -} - -void SpinLock_FreeMemory(SpinLock* lock) -{ - assert(lock->p != nullptr); - lck_spin_free(lock->p, s_lockGroup); - lock->p = nullptr; -} - -bool SpinLock_IsValid(SpinLock lock) -{ - return lock.p != nullptr; -} - -void SpinLock_Acquire(SpinLock lock) -{ - lck_spin_lock(lock.p); -} - -void SpinLock_Release(SpinLock lock) -{ - lck_spin_unlock(lock.p); -} diff --git a/ProjFS.Mac/PrjFSKext/Locks.hpp b/ProjFS.Mac/PrjFSKext/Locks.hpp deleted file mode 100644 index 11d3e63dec..0000000000 --- a/ProjFS.Mac/PrjFSKext/Locks.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef Locks_h -#define Locks_h - -#if DEBUG && !defined(KEXT_UNIT_TESTING) -// Enables validation of shared/exclusive lock invariants and owner checking (unit tests will use their own checks) -#define PRJFS_LOCK_CORRECTNESS_CHECKS 1 -#endif - -#include -#include "kernel-header-wrappers/stdatomic.h" - -typedef struct __lck_mtx_t__ lck_mtx_t; -typedef struct { lck_mtx_t* p; } Mutex; - -kern_return_t Locks_Init(); -kern_return_t Locks_Cleanup(); - -Mutex Mutex_Alloc(); -void Mutex_FreeMemory(Mutex* mutex); -bool Mutex_IsValid(Mutex mutex); - -void Mutex_Acquire(Mutex mutex); -void Mutex_Release(Mutex mutex); -void Mutex_Sleep(int seconds, void* channel, Mutex* mutex); - -typedef struct __lck_rw_t__ lck_rw_t; -struct thread; - -struct RWLock -{ - lck_rw_t* p; -#if PRJFS_LOCK_CORRECTNESS_CHECKS - struct thread* exclOwner; - atomic_uint_least32_t sharedOwnersXor; - atomic_uint_least32_t sharedOwnersCount; -#endif -}; - -RWLock RWLock_Alloc(); -bool RWLock_IsValid(RWLock rwLock); - -void RWLock_FreeMemory(RWLock* rwLock); - -void RWLock_AcquireShared(RWLock& rwLock); -void RWLock_ReleaseShared(RWLock& rwLock); - -void RWLock_AcquireExclusive(RWLock& rwLock); -void RWLock_ReleaseExclusive(RWLock& rwLock); -void RWLock_DropExclusiveToShared(RWLock& rwLock); -bool RWLock_AcquireSharedToExclusive(RWLock& rwLock); - -typedef struct __lck_spin_t__ lck_spin_t; - -struct SpinLock -{ - lck_spin_t* p; -}; - -SpinLock SpinLock_Alloc(); -void SpinLock_FreeMemory(SpinLock* lock); -bool SpinLock_IsValid(SpinLock lock); -void SpinLock_Acquire(SpinLock lock); -void SpinLock_Release(SpinLock lock); - -#endif /* Locks_h */ diff --git a/ProjFS.Mac/PrjFSKext/Memory.cpp b/ProjFS.Mac/PrjFSKext/Memory.cpp deleted file mode 100644 index 0307f9d739..0000000000 --- a/ProjFS.Mac/PrjFSKext/Memory.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include - -#include "public/PrjFSCommon.h" -#include "Memory.hpp" - -static OSMallocTag s_mallocTag = nullptr; - -kern_return_t Memory_Init() -{ - if (nullptr != s_mallocTag) - { - return KERN_FAILURE; - } - - s_mallocTag = OSMalloc_Tagalloc(PrjFSKextBundleId, OSMT_DEFAULT); - if (nullptr == s_mallocTag) - { - return KERN_FAILURE; - } - - return KERN_SUCCESS; -} - -kern_return_t Memory_Cleanup() -{ - if (nullptr != s_mallocTag) - { - OSMalloc_Tagfree(s_mallocTag); - s_mallocTag = nullptr; - - return KERN_SUCCESS; - } - - return KERN_FAILURE; -} - -void* Memory_Alloc(uint32_t size) -{ - return OSMalloc(size, s_mallocTag); -} - -void Memory_Free(void* buffer, uint32_t size) -{ - OSFree(buffer, size, s_mallocTag); -} diff --git a/ProjFS.Mac/PrjFSKext/Memory.hpp b/ProjFS.Mac/PrjFSKext/Memory.hpp deleted file mode 100644 index 9fc0562c4a..0000000000 --- a/ProjFS.Mac/PrjFSKext/Memory.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef Memory_h -#define Memory_h - -#include - -#if !defined(KEXT_UNIT_TESTING) || defined(TESTABLE_KEXT_TARGET) // Building kext -#include -#else // building unit tests/mocks -#include -#endif - -kern_return_t Memory_Init(); -kern_return_t Memory_Cleanup(); - -void* Memory_Alloc(uint32_t size); -void Memory_Free(void* buffer, uint32_t size); - -template -T* Memory_AllocArray(uint32_t arrayLength) -{ - uint32_t allocBytes; - if (__builtin_umul_overflow(arrayLength, sizeof(T), &allocBytes)) - { - // Overflow occurred. - return nullptr; - } - - return static_cast(Memory_Alloc(allocBytes)); -} - -template -void Memory_FreeArray(T* array, uint32_t arrayLength) -{ - uint32_t arrayBytes; - if (__builtin_umul_overflow(arrayLength, sizeof(T), &arrayBytes)) - { - // This should never occur, since Memory_AllocArray should not allow the initial computation to overflow. - assert(!"Overflow detected: was this array allocated through Memory_AllocArray?"); - } - else - { - Memory_Free(array, arrayBytes); - } -} - -#endif /* Memory_h */ diff --git a/ProjFS.Mac/PrjFSKext/Message_Kernel.cpp b/ProjFS.Mac/PrjFSKext/Message_Kernel.cpp deleted file mode 100644 index 396802a881..0000000000 --- a/ProjFS.Mac/PrjFSKext/Message_Kernel.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "Message_Kernel.hpp" -#include "ArrayUtilities.hpp" -#include -#include - -void Message_Init( - Message* spec, - MessageHeader* header, - uint64_t messageId, - MessageType messageType, - const FsidInode& fsidInode, - int32_t pid, - const char* procname, - const char* path, - const char* fromPath) -{ - header->messageId = messageId; - header->messageType = messageType; - header->fsidInode = fsidInode; - header->pid = pid; - - if (nullptr != procname) - { - strlcpy(header->procname, procname, sizeof(header->procname)); - } - else - { - header->procname[0] = '\0'; - } - - if (nullptr != path) - { - header->pathSizesBytes[MessagePath_Target] = strlen(path) + 1; - } - else - { - header->pathSizesBytes[MessagePath_Target] = 0; - } - - if (nullptr != fromPath) - { - header->pathSizesBytes[MessagePath_From] = strlen(fromPath) + 1; - } - else - { - header->pathSizesBytes[MessagePath_From] = 0; - } - - spec->messageHeader = header; - spec->paths[MessagePath_Target] = path; - spec->paths[MessagePath_From] = fromPath; -} - -uint32_t Message_Encode(void* buffer, const uint32_t bufferSize, const Message& message) -{ - uint8_t* bufferPosition = static_cast(buffer); - uint32_t bufferBytesRemain = bufferSize; - - assert(bufferSize >= sizeof(*message.messageHeader)); - memcpy(bufferPosition, message.messageHeader, sizeof(*message.messageHeader)); - bufferPosition += sizeof(*message.messageHeader); - bufferBytesRemain -= sizeof(*message.messageHeader); - - for (unsigned i = 0; i < Array_Size(message.messageHeader->pathSizesBytes); ++i) - { - uint16_t stringSize = message.messageHeader->pathSizesBytes[i]; - if (stringSize > 0) - { - assert(bufferSize >= stringSize); - memcpy(bufferPosition, message.paths[i], stringSize); - bufferPosition += stringSize; - bufferBytesRemain -= stringSize; - } - } - - return bufferSize - bufferBytesRemain; -} diff --git a/ProjFS.Mac/PrjFSKext/Message_Kernel.hpp b/ProjFS.Mac/PrjFSKext/Message_Kernel.hpp deleted file mode 100644 index 7ce2370cbb..0000000000 --- a/ProjFS.Mac/PrjFSKext/Message_Kernel.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "public/Message.h" - -void Message_Init( - Message* spec, - MessageHeader* header, - uint64_t messageId, - MessageType messageType, - const FsidInode& fsidInode, - int32_t pid, - const char* procname, - const char* path, - const char* fromPath); - -uint32_t Message_Encode(void* buffer, uint32_t bufferSize, const Message& message); - diff --git a/ProjFS.Mac/PrjFSKext/Message_Shared.cpp b/ProjFS.Mac/PrjFSKext/Message_Shared.cpp deleted file mode 100644 index 21313e9f84..0000000000 --- a/ProjFS.Mac/PrjFSKext/Message_Shared.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "public/Message.h" - -#ifdef KERNEL -#include -#else -#include -#endif - -uint32_t Message_EncodedSize(const MessageHeader* messageHeader) -{ - uint32_t size = sizeof(*messageHeader); - for (unsigned i = 0; i < MessagePath_Count; ++i) - { - bool overflow = __builtin_uadd_overflow(messageHeader->pathSizesBytes[i], size, &size); - assert(!overflow); // small constant plus small number of uint16_t should never overflow a uint32_t - } - return size; -} diff --git a/ProjFS.Mac/PrjFSKext/PerformanceTracing.cpp b/ProjFS.Mac/PrjFSKext/PerformanceTracing.cpp deleted file mode 100644 index d00b6186c9..0000000000 --- a/ProjFS.Mac/PrjFSKext/PerformanceTracing.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "PerformanceTracing.hpp" -#include "KextLog.hpp" -#include "kernel-header-wrappers/stdatomic.h" -#include -#include - -#if PRJFS_PERFORMANCE_TRACING_ENABLE -uint64_t PerfTracer::s_numTracers = 0; -#endif - -static PrjFSPerfCounterResult s_perfCounterResults[PrjFSPerfCounter_Count]; - -static void InitProbe(PrjFSPerfCounter counter); -static int Log2(unsigned long long nonZeroValue); - -void PerfTracing_Init() -{ - for (size_t i = 0; i < PrjFSPerfCounter_Count; ++i) - { - InitProbe((PrjFSPerfCounter)i); - } -} - -IOReturn PerfTracing_ExportDataUserClient(IOExternalMethodArguments* arguments) -{ -#if PRJFS_PERFORMANCE_TRACING_ENABLE - // The buffer will come in either as a memory descriptor or direct pointer, depending on size - if (nullptr != arguments->structureOutputDescriptor) - { - IOMemoryDescriptor* structureOutput = arguments->structureOutputDescriptor; - if (sizeof(s_perfCounterResults) != structureOutput->getLength()) - { - KextLog_Info("PerfTracing_ExportDataUserClient: structure output descriptor size %llu, expected %lu\n", structureOutput->getLength(), sizeof(s_perfCounterResults)); - return kIOReturnBadArgument; - } - - IOReturn result = structureOutput->prepare(kIODirectionIn); - if (kIOReturnSuccess == result) - { - structureOutput->writeBytes(0 /* offset */, s_perfCounterResults, sizeof(s_perfCounterResults)); - structureOutput->complete(kIODirectionIn); - } - return result; - } - - if (arguments->structureOutput == nullptr || arguments->structureOutputSize != sizeof(s_perfCounterResults)) - { - KextLog_Info("PerfTracing_ExportDataUserClient: structure output size %u, expected %lu\n", arguments->structureOutputSize, sizeof(s_perfCounterResults)); - return kIOReturnBadArgument; - } - - memcpy(arguments->structureOutput, s_perfCounterResults, sizeof(s_perfCounterResults)); - return kIOReturnSuccess; -#else - return kIOReturnUnsupported; -#endif -} - -void PerfTracing_RecordSample(PrjFSPerfCounter counter, uint64_t startTime, uint64_t endTime) -{ - PrjFSPerfCounterResult* result = &s_perfCounterResults[counter]; - - uint64_t interval = endTime - startTime; - - atomic_fetch_add(&result->numSamples, 1); - - if (0 != interval) - { - atomic_fetch_add(&result->sum, interval); - - // Update minimum sample if necessary - { - uint64_t oldMin = atomic_load(&result->min); - while (interval < oldMin && !atomic_compare_exchange_weak(&result->min, &oldMin, interval)) - {} - } - - // Update maximum sample if necessary - { - uint64_t oldMax = atomic_load(&result->max); - while (interval > oldMax && !atomic_compare_exchange_weak(&result->max, &oldMax, interval)) - {} - } - - int intervalLog2 = Log2(interval); - atomic_fetch_add(&result->sampleBuckets[intervalLog2], 1); - } -} - -static void InitProbe(PrjFSPerfCounter counter) -{ - s_perfCounterResults[counter] = PrjFSPerfCounterResult{ .min = UINT64_MAX }; -} - -// Computes the floor of the base-2 logarithm of the provided positive integer. -// The __builtin_clzll() function counts the number of 0 bits until the most -// significant 1 bit in the argument. For log2, the position of this most -// significant 1 bit counting from the least significant bit is needed. -static int Log2(unsigned long long nonZeroValue) -{ - static const int maxBitIndex = sizeof(nonZeroValue) * CHAR_BIT - 1; - return maxBitIndex - __builtin_clzll(nonZeroValue); -} diff --git a/ProjFS.Mac/PrjFSKext/PerformanceTracing.hpp b/ProjFS.Mac/PrjFSKext/PerformanceTracing.hpp deleted file mode 100644 index 7b4f1559c1..0000000000 --- a/ProjFS.Mac/PrjFSKext/PerformanceTracing.hpp +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include "public/PrjFSCommon.h" -#include "public/PrjFSPerfCounter.h" - -#include -#include - -void PerfTracing_Init(); -void PerfTracing_RecordSample(PrjFSPerfCounter counter, uint64_t startTime, uint64_t endTime); - -struct IOExternalMethodArguments; -IOReturn PerfTracing_ExportDataUserClient(IOExternalMethodArguments* arguments); - -class PerfTracer -{ -private: -#if PRJFS_PERFORMANCE_TRACING_ENABLE - static uint64_t s_numTracers; - bool isEnabled; -#endif - -public: - inline PerfTracer(); - inline bool IsEnabled(); - inline void IncrementCount(PrjFSPerfCounter counter, bool ignoreSampling = false); -}; - -inline PerfTracer::PerfTracer() -{ -#if PRJFS_PERFORMANCE_TRACING_ENABLE - // Set this value to N for a sampling rate of 1/N - const int sampleEveryNthTracer = 100; - - // This increment is not thread-safe, and that is ok. If there is a race here, we will - // sometimes under-count and sometimes over-count, but it should all balance out. - // And if we want to capture every event, we can simply replace this logic with - // this->isEnabled = true; - uint64_t id = s_numTracers++; - - this->isEnabled = (id % sampleEveryNthTracer == 0); -#endif -} - -inline bool PerfTracer::IsEnabled() -{ -#if PRJFS_PERFORMANCE_TRACING_ENABLE - return this->isEnabled; -#else - return false; -#endif -} - -inline void PerfTracer::IncrementCount(PrjFSPerfCounter counter, bool ignoreSampling /* = false */) -{ -#if PRJFS_PERFORMANCE_TRACING_ENABLE - if (ignoreSampling || this->IsEnabled()) - { - PerfTracing_RecordSample(counter, 0, 0); - } -#endif -} - -class PerfSample -{ -private: -#if PRJFS_PERFORMANCE_TRACING_ENABLE - PerfTracer* perfTracer; - PrjFSPerfCounter counter; - uint64_t startTimestamp; -#endif - -public: - inline PerfSample(PerfTracer* perfTracer, PrjFSPerfCounter counter); - inline ~PerfSample(); -}; - -inline PerfSample::PerfSample(PerfTracer* perfTracer, PrjFSPerfCounter counter) -#if PRJFS_PERFORMANCE_TRACING_ENABLE - : - perfTracer(perfTracer), - counter(counter), - startTimestamp(perfTracer->IsEnabled() ? mach_absolute_time() : 0) -#endif -{ -} - -inline PerfSample::~PerfSample() -{ -#if PRJFS_PERFORMANCE_TRACING_ENABLE - if (this->perfTracer->IsEnabled()) - { - PerfTracing_RecordSample(this->counter, this->startTimestamp, mach_absolute_time()); - } -#endif -} diff --git a/ProjFS.Mac/PrjFSKext/PrjFSClasses.hpp b/ProjFS.Mac/PrjFSKext/PrjFSClasses.hpp deleted file mode 100644 index 1693f79a91..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSClasses.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -// IOKit class naming convention is to use reverse DNS notation -#define PrjFSService org_vfsforgit_PrjFS -class PrjFSService; -#define PrjFSProviderUserClient org_vfsforgit_PrjFSProviderUserClient -class PrjFSProviderUserClient; -#define PrjFSLogUserClient org_vfsforgit_PrjFSLogUserClient -class PrjFSLogUserClient; -#define PrjFSOfflineIOUserClient org_vfsforgit_PrjFSOfflineIOUserClient -class PrjFSOfflineIOUserClient; diff --git a/ProjFS.Mac/PrjFSKext/PrjFSKext.cpp b/ProjFS.Mac/PrjFSKext/PrjFSKext.cpp deleted file mode 100644 index 97a8ee6e54..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSKext.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include - -#include "KextLog.hpp" -#include "KauthHandler.hpp" -#include "Locks.hpp" -#include "Memory.hpp" -#include "PerformanceTracing.hpp" - -extern "C" kern_return_t PrjFSKext_Start(kmod_info_t* ki, void* d); -extern "C" kern_return_t PrjFSKext_Stop(kmod_info_t* ki, void* d); - -kern_return_t PrjFSKext_Start(kmod_info_t* ki, void* d) -{ - PerfTracing_Init(); - - if (Locks_Init()) - { - goto CleanupAndFail; - } - - if (Memory_Init()) - { - goto CleanupAndFail; - } - - if (!KextLog_Init()) - { - goto CleanupAndFail; - } - - if (KauthHandler_Init()) - { - goto CleanupAndFail; - } - - KextLog_Info("PrjFSKext (Start)"); - return KERN_SUCCESS; - -CleanupAndFail: - KextLog_Error("PrjFSKext failed to start"); - - PrjFSKext_Stop(nullptr, nullptr); - return KERN_FAILURE; -} - -kern_return_t PrjFSKext_Stop(kmod_info_t* ki, void* d) -{ - kern_return_t result = KERN_SUCCESS; - - if (KauthHandler_Cleanup()) - { - result = KERN_FAILURE; - } - - if (Memory_Cleanup()) - { - result = KERN_FAILURE; - } - - KextLog_Info("PrjFSKext (Stop)"); - - KextLog_Cleanup(); - - if (Locks_Cleanup()) - { - result = KERN_FAILURE; - } - - return result; -} diff --git a/ProjFS.Mac/PrjFSKext/PrjFSLogUserClient.cpp b/ProjFS.Mac/PrjFSKext/PrjFSLogUserClient.cpp deleted file mode 100644 index 7ddfa2ef3b..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSLogUserClient.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include "PrjFSLogUserClient.hpp" -#include "public/PrjFSLogClientShared.h" -#include "KextLog.hpp" -#include "public/PrjFSCommon.h" -#include "public/PrjFSVnodeCacheHealth.h" -#include "PerformanceTracing.hpp" -#include "VnodeCache.hpp" -#include - - -OSDefineMetaClassAndStructors(PrjFSLogUserClient, IOUserClient); -// Amount of memory to set aside for kernel -> userspace log messages. -static const uint32_t LogMessageQueueCapacityBytes = 1024 * 1024; - - -static const IOExternalMethodDispatch LogUserClientDispatch[] = -{ - [LogSelector_FetchProfilingData] = - { - .function = &PrjFSLogUserClient::fetchProfilingData, - .checkScalarInputCount = 0, - .checkStructureInputSize = 0, - .checkScalarOutputCount = 0, - .checkStructureOutputSize = PrjFSPerfCounter_Count * sizeof(PrjFSPerfCounterResult), // array of results - }, - [LogSelector_FetchVnodeCacheHealth] = - { - .function = &PrjFSLogUserClient::fetchVnodeCacheHealth, - .checkScalarInputCount = 0, - .checkStructureInputSize = 0, - .checkScalarOutputCount = 0, - .checkStructureOutputSize = sizeof(PrjFSVnodeCacheHealth), - }, -}; - - -bool PrjFSLogUserClient::initWithTask( - task_t owningTask, - void* securityToken, - UInt32 type, - OSDictionary* properties) -{ - if (!this->super::initWithTask(owningTask, securityToken, type, properties)) - { - return false; - } - - this->dataQueueWriterMutex = Mutex_Alloc(); - if (!Mutex_IsValid(this->dataQueueWriterMutex)) - { - this->cleanUp(); - return false; - } - - this->dataQueue = IOSharedDataQueue::withCapacity(LogMessageQueueCapacityBytes); - if (nullptr == this->dataQueue) - { - this->cleanUp(); - return false; - } - - this->dataQueueMemory = this->dataQueue->getMemoryDescriptor(); - if (nullptr == this->dataQueueMemory) - { - this->cleanUp(); - return false; - } - - this->logMessageDropped = false; - return true; - -} - -void PrjFSLogUserClient::cleanUp() -{ - // clientClose() is not called if the user client class is terminated, e.g. from kextunload. - // So deregister if we're still registered. - KextLog_DeregisterUserClient(this); - if (Mutex_IsValid(this->dataQueueWriterMutex)) - { - Mutex_FreeMemory(&this->dataQueueWriterMutex); - } - - OSSafeReleaseNULL(this->dataQueueMemory); - OSSafeReleaseNULL(this->dataQueue); -} - -void PrjFSLogUserClient::free() -{ - this->cleanUp(); - this->super::free(); -} - -IOReturn PrjFSLogUserClient::clientClose() -{ - KextLog_DeregisterUserClient(this); - this->terminate(0); - return kIOReturnSuccess; -} - -IOReturn PrjFSLogUserClient::clientMemoryForType(UInt32 type, IOOptionBits* options, IOMemoryDescriptor** memory) -{ - if (LogMemoryType_MessageQueue == type) - { - IOMemoryDescriptor* queueMemory; - - Mutex_Acquire(this->dataQueueWriterMutex); - { - queueMemory = this->dataQueueMemory; - if (queueMemory != nullptr) - { - queueMemory->retain(); - } - } - Mutex_Release(this->dataQueueWriterMutex); - - *memory = queueMemory; - return nullptr == queueMemory ? kIOReturnError : kIOReturnSuccess; - } - - return this->super::clientMemoryForType(type, options, memory); -} - -IOReturn PrjFSLogUserClient::registerNotificationPort(mach_port_t port, UInt32 type, io_user_reference_t refCon) -{ - if (type == LogPortType_MessageQueue) - { - if (port == MACH_PORT_NULL) - { - return kIOReturnError; - } - - Mutex_Acquire(this->dataQueueWriterMutex); - { - assert(nullptr != this->dataQueue); - this->dataQueue->setNotificationPort(port); - } - Mutex_Release(this->dataQueueWriterMutex); - - return kIOReturnSuccess; - } - else - { - return this->super::registerNotificationPort(port, type, refCon); - } -} - -void PrjFSLogUserClient::sendLogMessage(KextLog_MessageHeader* message, uint32_t size) -{ - Mutex_Acquire(this->dataQueueWriterMutex); - { - if (this->logMessageDropped) - { - this->logMessageDropped = false; - message->flags |= LogMessageFlag_LogMessagesDropped; - } - - bool ok = this->dataQueue->enqueue(message, size); - if (!ok) - { - this->logMessageDropped = true; - } - } - Mutex_Release(this->dataQueueWriterMutex); -} - -IOReturn PrjFSLogUserClient::externalMethod( - uint32_t selector, - IOExternalMethodArguments* arguments, - IOExternalMethodDispatch* dispatch, - OSObject* target, - void* reference) -{ - IOExternalMethodDispatch local_dispatch = {}; - if (selector < sizeof(LogUserClientDispatch) / sizeof(LogUserClientDispatch[0])) - { - if (nullptr != LogUserClientDispatch[selector].function) - { - local_dispatch = LogUserClientDispatch[selector]; - dispatch = &local_dispatch; - target = this; - } - } - return this->super::externalMethod(selector, arguments, dispatch, target, reference); -} - -IOReturn PrjFSLogUserClient::fetchProfilingData( - OSObject* target, - void* reference, - IOExternalMethodArguments* arguments) -{ - return PerfTracing_ExportDataUserClient(arguments); -} - -IOReturn PrjFSLogUserClient::fetchVnodeCacheHealth( - OSObject* target, - void* reference, - IOExternalMethodArguments* arguments) -{ - return VnodeCache_ExportHealthData(arguments); -} - diff --git a/ProjFS.Mac/PrjFSKext/PrjFSLogUserClient.hpp b/ProjFS.Mac/PrjFSKext/PrjFSLogUserClient.hpp deleted file mode 100644 index efadf3d837..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSLogUserClient.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "PrjFSClasses.hpp" -#include "Locks.hpp" -#include "public/PrjFSCommon.h" -#include - -class IOSharedDataQueue; -struct KextLog_MessageHeader; - -class PrjFSLogUserClient : public IOUserClient -{ - OSDeclareDefaultStructors(PrjFSLogUserClient); -private: - typedef IOUserClient super; - IOSharedDataQueue* dataQueue; - IOMemoryDescriptor* dataQueueMemory; - Mutex dataQueueWriterMutex; - bool logMessageDropped; - void cleanUp(); -public: - virtual bool initWithTask(task_t owningTask, void* securityToken, UInt32 type, OSDictionary* properties) override; - - virtual void free() override; - virtual IOReturn clientClose() override; - virtual IOReturn clientMemoryForType(UInt32 type, IOOptionBits* options, IOMemoryDescriptor** memory) override; - virtual IOReturn registerNotificationPort(mach_port_t port, UInt32 type, io_user_reference_t refCon) override; - - virtual IOReturn externalMethod( - uint32_t selector, - IOExternalMethodArguments* arguments, - IOExternalMethodDispatch* dispatch = nullptr, - OSObject* target = nullptr, - void* reference = nullptr) override; - - - static IOReturn fetchProfilingData( - OSObject* target, - void* reference, - IOExternalMethodArguments* arguments); - - static IOReturn fetchVnodeCacheHealth( - OSObject* target, - void* reference, - IOExternalMethodArguments* arguments); - - void sendLogMessage(KextLog_MessageHeader* message, uint32_t size); -}; diff --git a/ProjFS.Mac/PrjFSKext/PrjFSOfflineIOUserClient.cpp b/ProjFS.Mac/PrjFSKext/PrjFSOfflineIOUserClient.cpp deleted file mode 100644 index 137b35f8b9..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSOfflineIOUserClient.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "PrjFSOfflineIOUserClient.hpp" -#include "VirtualizationRoots.hpp" -#include - -OSDefineMetaClassAndStructors(PrjFSOfflineIOUserClient, IOUserClient); - -bool PrjFSOfflineIOUserClient::initWithTask( - task_t owningTask, void* securityToken, UInt32 type, OSDictionary* properties) -{ - if (!this->super::initWithTask(owningTask, securityToken, type, properties)) - { - return false; - } - - this->pid = proc_selfpid(); - bool success = VirtualizationRoots_AddOfflineIOProcess(this->pid); - if (!success) - { - this->pid = 0; - } - - return success; -} - -IOReturn PrjFSOfflineIOUserClient::clientClose() -{ - this->terminate(0); - return kIOReturnSuccess; -} - -void PrjFSOfflineIOUserClient::stop(IOService* provider) -{ - if (this->pid != 0) - { - VirtualizationRoots_RemoveOfflineIOProcess(this->pid); - this->pid = 0; - } - - this->super::stop(provider); -} - diff --git a/ProjFS.Mac/PrjFSKext/PrjFSOfflineIOUserClient.hpp b/ProjFS.Mac/PrjFSKext/PrjFSOfflineIOUserClient.hpp deleted file mode 100644 index 6832929209..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSOfflineIOUserClient.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "PrjFSClasses.hpp" -#include - -class PrjFSOfflineIOUserClient : public IOUserClient -{ - OSDeclareDefaultStructors(PrjFSOfflineIOUserClient); -private: - typedef IOUserClient super; - pid_t pid; - -public: - // IOUserClient methods: - virtual bool initWithTask(task_t owningTask, void* securityToken, UInt32 type, OSDictionary* properties) override; - virtual IOReturn clientClose() override; - - // IOService methods: - virtual void stop(IOService* provider) override; -}; - diff --git a/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClient.cpp b/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClient.cpp deleted file mode 100644 index 957f08ab77..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClient.cpp +++ /dev/null @@ -1,302 +0,0 @@ -#include "PrjFSProviderUserClientPrivate.hpp" -#include "public/PrjFSCommon.h" -#include "public/PrjFSProviderClientShared.h" -#include "public/Message.h" -#include "ProviderMessaging.hpp" -#include "VirtualizationRoots.hpp" - -#include -#include - -OSDefineMetaClassAndStructors(PrjFSProviderUserClient, IOUserClient); - -// Amount of memory to set aside for kernel -> userspace messages. -// Should be chosen to comfortably hold "enough" Message structs and associated path strings. -static const uint32_t ProviderMessageQueueCapacityBytes = 100 * 1024; - - -const IOExternalMethodDispatch PrjFSProviderUserClient::ProviderUserClientDispatch[] = -{ - [ProviderSelector_RegisterVirtualizationRootPath] = - { - .function = &PrjFSProviderUserClient::registerVirtualizationRoot, - .checkScalarInputCount = 0, - .checkStructureInputSize = kIOUCVariableStructureSize, // null-terminated string: virtualisation root path - .checkScalarOutputCount = 1, // returned errno - .checkStructureOutputSize = 0 - }, - [ProviderSelector_KernelMessageResponse] = - { - .function = &PrjFSProviderUserClient::kernelMessageResponse, - .checkScalarInputCount = 2, // message id, response type - .checkStructureInputSize = 0, - .checkScalarOutputCount = 0, - .checkStructureOutputSize = 0 - }, -}; - -bool PrjFSProviderUserClient::initWithTask( - task_t owningTask, - void* securityToken, - UInt32 type, - OSDictionary* properties) -{ - this->virtualizationRootHandle = RootHandle_None; - this->pid = proc_selfpid(); - - if (!this->super::initWithTask(owningTask, securityToken, type, properties)) - { - return false; - } - - this->dataQueueWriterMutex = Mutex_Alloc(); - if (!Mutex_IsValid(this->dataQueueWriterMutex)) - { - goto CleanupAndFail; - } - - this->dataQueue = IOSharedDataQueue::withCapacity(ProviderMessageQueueCapacityBytes); - if (nullptr == this->dataQueue) - { - goto CleanupAndFail; - } - - this->dataQueueMemory = this->dataQueue->getMemoryDescriptor(); - if (nullptr == this->dataQueueMemory) - { - goto CleanupAndFail; - } - - return true; - -CleanupAndFail: - if (Mutex_IsValid(this->dataQueueWriterMutex)) - { - Mutex_FreeMemory(&this->dataQueueWriterMutex); - } - - OSSafeReleaseNULL(this->dataQueueMemory); - OSSafeReleaseNULL(this->dataQueue); - return false; -} - -void PrjFSProviderUserClient::stop(IOService* provider) -{ - // On the code path of process dying or disconnecting, the following is unnecessary, - // but if the kext is unloaded with providers attached, clientClose() will never be - // called on them so we do the cleanup here. - this->cleanupProviderRegistration(); - - this->super::stop(provider); -} - -void PrjFSProviderUserClient::free() -{ - OSSafeReleaseNULL(this->dataQueueMemory); - OSSafeReleaseNULL(this->dataQueue); - if (Mutex_IsValid(this->dataQueueWriterMutex)) - { - Mutex_FreeMemory(&this->dataQueueWriterMutex); - } - - this->super::free(); -} - -// Called when the user process explicitly or implicitly (process death) closes -// the connection. -IOReturn PrjFSProviderUserClient::clientClose() -{ - this->cleanupProviderRegistration(); - - this->terminate(0); - return kIOReturnSuccess; -} - -void PrjFSProviderUserClient::cleanupProviderRegistration() -{ - Mutex_Acquire(this->dataQueueWriterMutex); - { - VirtualizationRootHandle root = this->virtualizationRootHandle; - this->virtualizationRootHandle = RootHandle_None; - if (RootHandle_None != root) - { - ActiveProvider_Disconnect(root, this); - } - } - Mutex_Release(this->dataQueueWriterMutex); -} - -// Called when user process requests memory-mapping of kernel data -// structures via IOConnectMapMemory64(). -IOReturn PrjFSProviderUserClient::clientMemoryForType( - UInt32 type, - IOOptionBits* options, - IOMemoryDescriptor** memory) -{ - switch (type) - { - case ProviderMemoryType_MessageQueue: - { - IOMemoryDescriptor* queueMemory; - - Mutex_Acquire(this->dataQueueWriterMutex); - { - queueMemory = this->dataQueueMemory; - if (queueMemory != nullptr) - { - queueMemory->retain(); // Matched internally in IOUserClient - } - } - Mutex_Release(this->dataQueueWriterMutex); - - *memory = queueMemory; - return nullptr == queueMemory ? kIOReturnError : kIOReturnSuccess; - } - break; - } - - return kIOReturnError; -} - -IOReturn PrjFSProviderUserClient::registerNotificationPort( - mach_port_t port, - UInt32 type, - io_user_reference_t refCon) -{ - if (type == ProviderPortType_MessageQueue) - { - if(port == MACH_PORT_NULL) - { - return kIOReturnError; - } - - Mutex_Acquire(this->dataQueueWriterMutex); - { - assert(nullptr != this->dataQueue); - this->dataQueue->setNotificationPort(port); - } - Mutex_Release(this->dataQueueWriterMutex); - - return kIOReturnSuccess; - } - else - { - return this->super::registerNotificationPort(port, type, refCon); - } -} - -IOReturn PrjFSProviderUserClient::externalMethod( - uint32_t selector, - IOExternalMethodArguments* arguments, - IOExternalMethodDispatch* dispatch, - OSObject* target, - void* reference) -{ - IOExternalMethodDispatch local_dispatch = {}; - if (selector < sizeof(ProviderUserClientDispatch) / sizeof(ProviderUserClientDispatch[0])) - { - if (nullptr != ProviderUserClientDispatch[selector].function) - { - local_dispatch = ProviderUserClientDispatch[selector]; - dispatch = &local_dispatch; - target = this; - } - } - return this->super::externalMethod(selector, arguments, dispatch, target, reference); -} - -IOReturn PrjFSProviderUserClient::kernelMessageResponse( - OSObject* target, - void* reference, - IOExternalMethodArguments* arguments) -{ - return static_cast(target)->kernelMessageResponse( - arguments->scalarInput[0], - static_cast(arguments->scalarInput[1])); -} - -IOReturn PrjFSProviderUserClient::kernelMessageResponse(uint64_t messageId, MessageType responseType) -{ - ProviderMessaging_HandleKernelMessageResponse(this->virtualizationRootHandle, messageId, responseType); - return kIOReturnSuccess; -} - -IOReturn PrjFSProviderUserClient::registerVirtualizationRoot( - OSObject* target, - void* reference, - IOExternalMethodArguments* arguments) -{ - // We don't support larger strings, including those large enough to warrant a memory descriptor - if (arguments->structureInputSize > PrjFSMaxPath || nullptr == arguments->structureInput) - { - return kIOReturnBadArgument; - } - - return static_cast(target)->registerVirtualizationRoot( - static_cast(arguments->structureInput), - arguments->structureInputSize, - &arguments->scalarOutput[0]); -} - -IOReturn PrjFSProviderUserClient::registerVirtualizationRoot(const char* rootPath, size_t rootPathSize, uint64_t* outError) -{ - if (rootPathSize == 0 || strnlen(rootPath, rootPathSize) != rootPathSize - 1) - { - *outError = EINVAL; - return kIOReturnSuccess; - } - else if (this->virtualizationRootHandle != RootHandle_None) - { - // Already set - *outError = EBUSY; - return kIOReturnSuccess; - } - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(this, this->pid, rootPath); - if (0 == result.error) - { - this->virtualizationRootHandle = result.root; - - // Sets the root index in the IORegistry for diagnostic purposes - char location[5] = ""; - snprintf(location, sizeof(location), "%d", result.root); - this->setLocation(location); - } - - *outError = result.error; - - return kIOReturnSuccess; -} - -void PrjFSProviderUserClient::sendMessage(const void* message, uint32_t size) -{ - Mutex_Acquire(this->dataQueueWriterMutex); - { - // IOSharedDataQueue::enqueue() only reads (memcpy source), but doesn't take a const pointer for some reason - bool ok = this->dataQueue->enqueue(const_cast(message), size); - assert(ok); - - // TODO: block here and try again when space has cleared if enqueueing fails - } - Mutex_Release(this->dataQueueWriterMutex); -} - -void ProviderUserClient_SendMessage(PrjFSProviderUserClient* userClient, const void* message, uint32_t size) -{ - userClient->sendMessage(message, size); -} - -void ProviderUserClient_UpdatePathProperty(PrjFSProviderUserClient* userClient, const char* providerPath) -{ - userClient->setProperty(PrjFSProviderPathKey, providerPath); -} - -void ProviderUserClient_Retain(PrjFSProviderUserClient* userClient) -{ - userClient->retain(); -} - -void ProviderUserClient_Release(PrjFSProviderUserClient* userClient) -{ - userClient->release(); -} diff --git a/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClient.hpp b/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClient.hpp deleted file mode 100644 index 98c179290e..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClient.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "PrjFSClasses.hpp" -#include - -void ProviderUserClient_UpdatePathProperty(PrjFSProviderUserClient* userClient, const char* providerPath); -void ProviderUserClient_Retain(PrjFSProviderUserClient* userClient); -void ProviderUserClient_Release(PrjFSProviderUserClient* userClient); -void ProviderUserClient_SendMessage(PrjFSProviderUserClient* userClient, const void* message, uint32_t size); diff --git a/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClientPrivate.hpp b/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClientPrivate.hpp deleted file mode 100644 index 12dda2c3c0..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSProviderUserClientPrivate.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#if defined(KEXT_UNIT_TESTING) -#error This class definition should not leak to the testable kext build or the unit tests -#endif - -#include "PrjFSProviderUserClient.hpp" -#include "Locks.hpp" -#include "public/Message.h" -#include "VirtualizationRoots.hpp" -#include - -struct MessageHeader; -struct VirtualizationRoot; -class IOSharedDataQueue; -class PrjFSProviderUserClient : public IOUserClient -{ - OSDeclareDefaultStructors(PrjFSProviderUserClient); -private: - typedef IOUserClient super; - IOSharedDataQueue* dataQueue; - IOMemoryDescriptor* dataQueueMemory; - Mutex dataQueueWriterMutex; - pid_t pid; - // The root for which this is the provider; RootHandle_None prior to registration - VirtualizationRootHandle virtualizationRootHandle; - - static const IOExternalMethodDispatch ProviderUserClientDispatch[]; - -public: - // IOUserClient methods: - virtual bool initWithTask( - task_t owningTask, void* securityToken, UInt32 type, - OSDictionary* properties) override; - - virtual IOReturn externalMethod( - uint32_t selector, - IOExternalMethodArguments* arguments, - IOExternalMethodDispatch* dispatch = nullptr, - OSObject* target = nullptr, - void* reference = nullptr) override; - virtual IOReturn clientMemoryForType( - UInt32 type, - IOOptionBits* options, - IOMemoryDescriptor** memory) override; - virtual IOReturn registerNotificationPort( - mach_port_t port, - UInt32 type, - io_user_reference_t refCon) override; - - virtual IOReturn clientClose() override; - - // IOService methods: - virtual void stop(IOService* provider) override; - - // OSObject methods: - virtual void free() override; - - - void sendMessage(const void* message, uint32_t size); - -private: - void cleanupProviderRegistration(); - - // External methods: - static IOReturn registerVirtualizationRoot( - OSObject* target, - void* reference, - IOExternalMethodArguments* arguments); - IOReturn registerVirtualizationRoot(const char* rootPath, size_t rootPathSize, uint64_t* outError); - - static IOReturn kernelMessageResponse( - OSObject* target, - void* reference, - IOExternalMethodArguments* arguments); - IOReturn kernelMessageResponse(uint64_t messageId, MessageType responseType); -}; diff --git a/ProjFS.Mac/PrjFSKext/PrjFSService.cpp b/ProjFS.Mac/PrjFSKext/PrjFSService.cpp deleted file mode 100644 index 7e3eddc356..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSService.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "public/PrjFSCommon.h" -#include "public/PrjFSProviderClientShared.h" -#include "PrjFSService.hpp" -#include "PrjFSProviderUserClientPrivate.hpp" -#include "PrjFSLogUserClient.hpp" -#include "PrjFSOfflineIOUserClient.hpp" -#include "KextLog.hpp" -#include "VirtualizationRoots.hpp" - -#include -#include -#include - -OSDefineMetaClassAndStructors(PrjFSService, IOService); - -// We really only want one instance of this class -static PrjFSService* service_singleton = nullptr; - -bool PrjFSService::start(IOService* provider) -{ - bool ok = this->super::start(provider); - if (!ok) - { - return false; - } - - // Protect agaist multiple instances being created - if (!OSCompareAndSwapPtr(nullptr, this, &service_singleton)) - { - return false; - } - - // Perform one-off initialisation here: - - - // Set a more readable name for us to find in IORegistry - this->setName("PrjFS"); - - OSString* kextVersion = OSString::withCString(PrjFSKextVersion); - this->setProperty(PrjFSKextVersionKey, kextVersion); - OSSafeReleaseNULL(kextVersion); - - // Publishes the service, ready for matching - this->registerService(); - - return true; -} - -void PrjFSService::stop(IOService* provider) -{ - // Perform one-off shutdown here: - - - if (!OSCompareAndSwapPtr(this, nullptr, &service_singleton)) - { - KextLog_Error("PrjFSService::stop: Warning: failed to deregister PrjFS singleton service. Bug?\n"); - } - this->super::stop(provider); -} - -static bool InitAttachAndStartUserClient( - PrjFSService* service, IOUserClient* client, task_t owningTask, - void* securityID, UInt32 type, OSDictionary* properties) -{ - if (nullptr == client) - { - return false; - } - - if (client->initWithTask(owningTask, securityID, type, properties)) - { - if (client->attach(service) && client->start(service)) - { - return true; - } - client->detach(service); - } - - client->release(); - return false; -} - -static void StopDetachReleaseUserClient(IOService* service, IOUserClient* client) -{ - client->stop(service); - client->detach(service); - client->release(); -} - -IOReturn PrjFSService::newUserClient( - task_t owningTask, - void* securityID, - UInt32 type, - OSDictionary* properties, - IOUserClient** handler) -{ - IOReturn result = kIOReturnUnsupported; - switch (type) - { - case UserClientType_Provider: - { - PrjFSProviderUserClient* provider_client = new PrjFSProviderUserClient(); - if (InitAttachAndStartUserClient(this, provider_client, owningTask, securityID, type, properties)) - { - *handler = provider_client; - result = kIOReturnSuccess; - } - } - break; - case UserClientType_Log: - { - PrjFSLogUserClient* log_client = new PrjFSLogUserClient(); - if (InitAttachAndStartUserClient(this, log_client, owningTask, securityID, type, properties)) - { - if (KextLog_RegisterUserClient(log_client)) - { - *handler = log_client; - result = kIOReturnSuccess; - } - else - { - StopDetachReleaseUserClient(this, log_client); - result = kIOReturnExclusiveAccess; - } - } - } - break; - case UserClientType_OfflineIO: - { - PrjFSOfflineIOUserClient* offline_io_client = new PrjFSOfflineIOUserClient(); - if (InitAttachAndStartUserClient(this, offline_io_client, owningTask, securityID, type, properties)) - { - *handler = offline_io_client; - result = kIOReturnSuccess; - } - } - break; - } - - return result; -} diff --git a/ProjFS.Mac/PrjFSKext/PrjFSService.hpp b/ProjFS.Mac/PrjFSKext/PrjFSService.hpp deleted file mode 100644 index b6e8491830..0000000000 --- a/ProjFS.Mac/PrjFSKext/PrjFSService.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "PrjFSClasses.hpp" -#include - -class PrjFSService : public IOService -{ - OSDeclareDefaultStructors(PrjFSService); -private: - typedef IOService super; -public: - // IOService overrides: - virtual bool start(IOService* provider) override; - virtual void stop(IOService* provider) override; - virtual IOReturn newUserClient( - task_t owningTask, void* securityID, UInt32 type, - OSDictionary* properties, IOUserClient** handler ) override; -}; diff --git a/ProjFS.Mac/PrjFSKext/ProviderMessaging.cpp b/ProjFS.Mac/PrjFSKext/ProviderMessaging.cpp deleted file mode 100644 index 6e1402cd5a..0000000000 --- a/ProjFS.Mac/PrjFSKext/ProviderMessaging.cpp +++ /dev/null @@ -1,232 +0,0 @@ -#include "ProviderMessaging.hpp" -#include "Locks.hpp" -#include "KextLog.hpp" -#include "Message_Kernel.hpp" -#include "kernel-header-wrappers/stdatomic.h" - -#include -#include -#include - -// Structs -struct OutstandingMessage -{ - MessageHeader request; - MessageType result; - bool receivedResult; - VirtualizationRootHandle rootHandle; - - LIST_ENTRY(OutstandingMessage) _list_privates; - -}; - -// State -static LIST_HEAD(OutstandingMessage_Head, OutstandingMessage) s_outstandingMessages = LIST_HEAD_INITIALIZER(OutstandingMessage_Head); -static Mutex s_outstandingMessagesMutex = {}; -static atomic_uint_least64_t s_nextMessageId; -static volatile bool s_isShuttingDown; - - -bool ProviderMessaging_Init() -{ - LIST_INIT(&s_outstandingMessages); - s_nextMessageId = 1; - - s_isShuttingDown = false; - - s_outstandingMessagesMutex = Mutex_Alloc(); - if (!Mutex_IsValid(s_outstandingMessagesMutex)) - { - goto CleanupAndFail; - } - - return true; - -CleanupAndFail: - ProviderMessaging_Cleanup(); - return false; -} - - -void ProviderMessaging_Cleanup() -{ - if (Mutex_IsValid(s_outstandingMessagesMutex)) - { - Mutex_FreeMemory(&s_outstandingMessagesMutex); - } -} - - -void ProviderMessaging_HandleKernelMessageResponse(VirtualizationRootHandle providerVirtualizationRootHandle, uint64_t messageId, MessageType responseType) -{ - switch (responseType) - { - case MessageType_Response_Success: - case MessageType_Response_Fail: - { - Mutex_Acquire(s_outstandingMessagesMutex); - { - OutstandingMessage* outstandingMessage; - LIST_FOREACH(outstandingMessage, &s_outstandingMessages, _list_privates) - { - if (outstandingMessage->request.messageId == messageId && outstandingMessage->rootHandle == providerVirtualizationRootHandle) - { - // Save the response for the blocked thread. - outstandingMessage->result = responseType; - outstandingMessage->receivedResult = true; - - wakeup(outstandingMessage); - - break; - } - } - } - Mutex_Release(s_outstandingMessagesMutex); - break; - } - - // The follow are not valid responses to kernel messages - case MessageType_Invalid: - case MessageType_KtoU_EnumerateDirectory: - case MessageType_KtoU_RecursivelyEnumerateDirectory: - case MessageType_KtoU_HydrateFile: - case MessageType_KtoU_NotifyFileModified: - case MessageType_KtoU_NotifyFilePreDelete: - case MessageType_KtoU_NotifyFilePreDeleteFromRename: - case MessageType_KtoU_NotifyDirectoryPreDelete: - case MessageType_KtoU_NotifyFileCreated: - case MessageType_KtoU_NotifyFileRenamed: - case MessageType_KtoU_NotifyDirectoryRenamed: - case MessageType_KtoU_NotifyFileHardLinkCreated: - case MessageType_Result_Aborted: - default: - KextLog_Error("KauthHandler_HandleKernelMessageResponse: Unexpected responseType: %d", responseType); - break; - } - - return; -} - -void ProviderMessaging_AbortOutstandingEventsForProvider(VirtualizationRootHandle providerVirtualizationRootHandle) -{ - // Mark all outstanding messages for this root as aborted and wake up the waiting threads - Mutex_Acquire(s_outstandingMessagesMutex); - { - OutstandingMessage* outstandingMessage; - LIST_FOREACH(outstandingMessage, &s_outstandingMessages, _list_privates) - { - if (outstandingMessage->rootHandle == providerVirtualizationRootHandle) - { - outstandingMessage->receivedResult = true; - outstandingMessage->result = MessageType_Result_Aborted; - wakeup(outstandingMessage); - } - } - } - Mutex_Release(s_outstandingMessagesMutex); -} - -bool ProviderMessaging_TrySendRequestAndWaitForResponse( - VirtualizationRootHandle root, - MessageType messageType, - const vnode_t vnode, - const FsidInode& vnodeFsidInode, - const char* vnodePath, - const char* fromPath, - int pid, - const char* procname, - int* kauthResult, - int* kauthError) -{ - // To be useful, the message needs to either provide an FSID/inode pair or a path - assert(vnodePath != nullptr || (vnodeFsidInode.fsid.val[0] != 0 || vnodeFsidInode.fsid.val[1] != 0) || fromPath != nullptr); - bool result = false; - - OutstandingMessage message = - { - .receivedResult = false, - .rootHandle = root, - }; - - uint64_t nextMessageId = atomic_fetch_add(&s_nextMessageId, UINT64_C(1)); - - Message messageSpec = {}; - Message_Init( - &messageSpec, - &(message.request), - nextMessageId, - messageType, - vnodeFsidInode, - pid, - procname, - vnodePath, - fromPath); - - if (s_isShuttingDown) - { - return false; - } - - Mutex_Acquire(s_outstandingMessagesMutex); - { - LIST_INSERT_HEAD(&s_outstandingMessages, &message, _list_privates); - } - Mutex_Release(s_outstandingMessagesMutex); - - errno_t sendError = ActiveProvider_SendMessage(root, messageSpec); - - Mutex_Acquire(s_outstandingMessagesMutex); - { - if (0 != sendError) - { - // If message delivery fails, block the operation to avoid missed messages in the provider. - *kauthResult = KAUTH_RESULT_DENY; - } - else - { - while (!message.receivedResult && - !s_isShuttingDown) - { - // TODO: appropriately handle unresponsive providers - Mutex_Sleep(5, &message, &s_outstandingMessagesMutex); - } - - if (s_isShuttingDown) - { - *kauthResult = KAUTH_RESULT_DENY; - } - else if (MessageType_Response_Success == message.result) - { - *kauthResult = KAUTH_RESULT_DEFER; - result = true; - } - else - { - // Default error code is EACCES. See errno.h for more codes. - *kauthError = EAGAIN; - *kauthResult = KAUTH_RESULT_DENY; - } - } - - LIST_REMOVE(&message, _list_privates); - } - Mutex_Release(s_outstandingMessagesMutex); - - return result; -} - -void ProviderMessaging_AbortAllOutstandingEvents() -{ - // Wake up all sleeping threads so they can see that that we're shutting down and return an error - Mutex_Acquire(s_outstandingMessagesMutex); - { - s_isShuttingDown = true; - - OutstandingMessage* outstandingMessage; - LIST_FOREACH(outstandingMessage, &s_outstandingMessages, _list_privates) - { - wakeup(outstandingMessage); - } - } - Mutex_Release(s_outstandingMessagesMutex); -} diff --git a/ProjFS.Mac/PrjFSKext/ProviderMessaging.hpp b/ProjFS.Mac/PrjFSKext/ProviderMessaging.hpp deleted file mode 100644 index 55ffa6f584..0000000000 --- a/ProjFS.Mac/PrjFSKext/ProviderMessaging.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "VirtualizationRoots.hpp" -#include "public/Message.h" - -bool ProviderMessaging_Init(); -void ProviderMessaging_AbortAllOutstandingEvents(); -void ProviderMessaging_Cleanup(); - -bool ProviderMessaging_TrySendRequestAndWaitForResponse( - VirtualizationRootHandle root, - MessageType messageType, - const vnode_t vnode, - const FsidInode& vnodeFsidInode, - const char* vnodePath, - const char* fromPath, - int pid, - const char* procname, - int* kauthResult, - int* kauthError); - -void ProviderMessaging_HandleKernelMessageResponse(VirtualizationRootHandle providerVirtualizationRootHandle, uint64_t messageId, MessageType responseType); -void ProviderMessaging_AbortOutstandingEventsForProvider(VirtualizationRootHandle providerVirtualizationRootHandle); diff --git a/ProjFS.Mac/PrjFSKext/VirtualizationRoots.cpp b/ProjFS.Mac/PrjFSKext/VirtualizationRoots.cpp deleted file mode 100644 index a61f8c13b1..0000000000 --- a/ProjFS.Mac/PrjFSKext/VirtualizationRoots.cpp +++ /dev/null @@ -1,749 +0,0 @@ -#include -#include -#include - -#include "public/PrjFSCommon.h" -#include "public/PrjFSXattrs.h" -#include "Message_Kernel.hpp" -#include "VirtualizationRoots.hpp" -#include "VirtualizationRootsPrivate.hpp" -#include "Memory.hpp" -#include "Locks.hpp" -#include "KextLog.hpp" -#include "PrjFSProviderUserClient.hpp" -#include "ProviderMessaging.hpp" -#include "kernel-header-wrappers/mount.h" -#include "kernel-header-wrappers/stdatomic.h" -#include "VnodeUtilities.hpp" -#include "PerformanceTracing.hpp" -#include "ArrayUtilities.hpp" - -#ifdef KEXT_UNIT_TESTING -#include "VirtualizationRootsTestable.hpp" -#endif - -static RWLock s_virtualizationRootsLock = {}; - -// Current length of the s_virtualizationRoots array -KEXT_STATIC uint16_t s_maxVirtualizationRoots = 0; -static const uint16_t MaxVirtualizationRootsLimit = INT16_MAX + 1u; -KEXT_STATIC VirtualizationRoot* s_virtualizationRoots = nullptr; - -static constexpr uint32_t MaxOfflineIOPIDs = 128; -// Also protected by the lock -static uint32_t s_offlineIOPIDCount = 0; -static pid_t s_offlineIOPIDs[MaxOfflineIOPIDs] = {}; - -// Looks up the vnode/vid and fsid/inode pairs among the known roots -static VirtualizationRootHandle FindRootAtVnode_Locked(vnode_t vnode, uint32_t vid, FsidInode fileId); - -static void RefreshRootVnodeIfNecessary_Locked(VirtualizationRootHandle rootHandle, vnode_t vnode, uint32_t vid, FsidInode fileId); -static bool FsidsAreEqual(fsid_t a, fsid_t b); -KEXT_STATIC bool PathInsideDirectory(const char* directoryPath, const char* path); -static void GrowVirtualizationRootArrayWithMemory_Locked(VirtualizationRoot* newMemory, uint16_t newLength); - -// Looks up the vnode and fsid/inode pair among the known roots, and if not found, -// detects if there is a hitherto-unknown root at vnode by checking attributes. -static VirtualizationRootHandle FindOrDetectRootAtVnode(vnode_t vnode, const FsidInode& vnodeFsidInode); - -static VirtualizationRootHandle FindUnusedIndex_Locked(); - -KEXT_STATIC VirtualizationRootHandle FindOrInsertVirtualizationRoot_LockedMayUnlock(vnode_t vnode, uint32_t vid, FsidInode persistentIds, const char* path); - -ActiveProviderProperties VirtualizationRoot_GetActiveProvider(VirtualizationRootHandle rootHandle) -{ - ActiveProviderProperties result = { false, 0 }; - if (rootHandle < 0) - { - return result; - } - - RWLock_AcquireShared(s_virtualizationRootsLock); - { - result.isOnline = - rootHandle < s_maxVirtualizationRoots - && s_virtualizationRoots[rootHandle].inUse - && nullptr != s_virtualizationRoots[rootHandle].providerUserClient; - - if (result.isOnline) - { - result.pid = s_virtualizationRoots[rootHandle].providerPid; - } - } - RWLock_ReleaseShared(s_virtualizationRootsLock); - - return result; -} - -bool VirtualizationRoot_IsValidRootHandle(VirtualizationRootHandle rootIndex) -{ - return (rootIndex > RootHandle_None); -} - -kern_return_t VirtualizationRoots_Init() -{ - if (RWLock_IsValid(s_virtualizationRootsLock)) - { - return KERN_FAILURE; - } - - s_virtualizationRootsLock = RWLock_Alloc(); - if (!RWLock_IsValid(s_virtualizationRootsLock)) - { - return KERN_FAILURE; - } - - // Start with a small size so the resizing logic is regularly tested - s_maxVirtualizationRoots = 4; - s_virtualizationRoots = Memory_AllocArray(s_maxVirtualizationRoots); - if (nullptr == s_virtualizationRoots) - { - return KERN_RESOURCE_SHORTAGE; - } - - for (VirtualizationRootHandle i = 0; i < s_maxVirtualizationRoots; ++i) - { - s_virtualizationRoots[i] = VirtualizationRoot{ }; - } - - atomic_thread_fence(memory_order_seq_cst); - - return KERN_SUCCESS; -} - -kern_return_t VirtualizationRoots_Cleanup() -{ - if (s_virtualizationRoots != nullptr) - { - for (uint32_t i = 0; i < s_maxVirtualizationRoots; ++i) - { - // If there are still providers registered at this point, we will leak vnodes - assert(s_virtualizationRoots[i].providerUserClient == nullptr); - } - - Memory_FreeArray(s_virtualizationRoots, s_maxVirtualizationRoots); - s_virtualizationRoots = nullptr; - s_maxVirtualizationRoots = 0; - } - - assert(s_offlineIOPIDCount == 0); - - if (RWLock_IsValid(s_virtualizationRootsLock)) - { - RWLock_FreeMemory(&s_virtualizationRootsLock); - return KERN_SUCCESS; - } - - return KERN_FAILURE; -} - -VirtualizationRootHandle VirtualizationRoot_FindForVnode( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter functionCounter, - PrjFSPerfCounter innerLoopCounter, - vnode_t _Nonnull initialVnode, - vfs_context_t _Nonnull context) -{ - PerfSample findForVnodeSample(perfTracer, functionCounter); - - VirtualizationRootHandle rootHandle = RootHandle_None; - errno_t error = 0; - vnode_t vnode = initialVnode; - - if (vnode_isdir(vnode)) - { - error = vnode_get(vnode); - if (error != 0) - { - KextLog_ErrorVnodePathAndProperties(vnode, "VirtualizationRoot_FindForVnode: vnode_get() failed (error = %d) on vnode %p:%u which we'd expect to be live", error, KextLog_Unslide(vnode), vnode_vid(vnode)); - return RootHandle_None; - } - } - else - { - vnode = vnode_getparent(vnode); - } - - // Search up the tree until we hit a known virtualization root or THE root of the file system - while (RootHandle_None == rootHandle && NULLVP != vnode && !vnode_isvroot(vnode)) - { - PerfSample iterationSample(perfTracer, innerLoopCounter); - - FsidInode vnodeFsidInode = Vnode_GetFsidAndInode(vnode, context, false /* Here we care about identity, not path */); - - rootHandle = FindOrDetectRootAtVnode(vnode, vnodeFsidInode); - - // If FindOrDetectRootAtVnode returned a "special" handle other - // than RootHandle_None, we want to stop the search and return that. - if (rootHandle != RootHandle_None) - { - break; - } - - vnode_t parent = vnode_getparent(vnode); - if (NULLVP == parent) - { - KextLog_FileError(vnode, "VirtualizationRoot_FindForVnode: vnode_getparent returned nullptr on vnode that is not root of a mount point"); - } - vnode_put(vnode); - vnode = parent; - } - - if (NULLVP != vnode) - { - vnode_put(vnode); - } - - return rootHandle; -} - -static VirtualizationRootHandle FindOrDetectRootAtVnode(vnode_t _Nonnull vnode, const FsidInode& vnodeFsidInode) -{ - uint32_t vid = vnode_vid(vnode); - - VirtualizationRootHandle rootIndex; - bool rootVnodeStale = false; - - RWLock_AcquireShared(s_virtualizationRootsLock); - { - rootIndex = FindRootAtVnode_Locked(vnode, vid, vnodeFsidInode); - rootVnodeStale = - rootIndex >= 0 - && (s_virtualizationRoots[rootIndex].rootVNode != vnode - || s_virtualizationRoots[rootIndex].rootVNodeVid != vid); - } - RWLock_ReleaseShared(s_virtualizationRootsLock); - - if (rootIndex == RootHandle_None) - { - PrjFSVirtualizationRootXAttrData rootXattr = {}; - SizeOrError xattrResult = Vnode_ReadXattr(vnode, PrjFSVirtualizationRootXAttrName, &rootXattr, sizeof(rootXattr)); - if (xattrResult.error == 0) - { - // TODO: check xattr contents - - const char* path = nullptr; -#if DEBUG // Offline roots shouldn't need their path filled, and vn_getpath() may fail anyway. Poison the value so any dependency will trip over it. - char pathBuffer[PrjFSMaxPath + 6] = "DEBUG:"; - int pathLength = static_cast(sizeof(pathBuffer) - strlen(pathBuffer)); - assertf(pathLength >= PATH_MAX, "Poisoning the string shouldn't make the buffer too short (vn_getpath expects PATH_MAX = %u, got %u)", PATH_MAX, pathLength); - errno_t error = vn_getpath(vnode, pathBuffer + strlen(pathBuffer), &pathLength); - if (error != 0) - { - KextLog_ErrorVnodeProperties(vnode, "FindOrDetectRootAtVnode: vn_getpath failed (error = %d)", error); - } - else - { - path = pathBuffer; - } -#endif - - RWLock_AcquireExclusive(s_virtualizationRootsLock); - { - rootIndex = FindOrInsertVirtualizationRoot_LockedMayUnlock(vnode, vid, vnodeFsidInode, path); - - // TODO: error handling - assert(rootIndex >= 0); - } - RWLock_ReleaseExclusive(s_virtualizationRootsLock); - } - else if (xattrResult.error != ENOATTR) - { - KextLog_FileError(vnode, "FindOrDetectRootAtVnode: Vnode_ReadXattr/mac_vnop_getxattr failed with errno %d", xattrResult.error); - } - } - else if (rootVnodeStale) - { - RWLock_AcquireExclusive(s_virtualizationRootsLock); - { - RefreshRootVnodeIfNecessary_Locked(rootIndex, vnode, vid, vnodeFsidInode); - } - RWLock_ReleaseExclusive(s_virtualizationRootsLock); - } - - return rootIndex; -} - -static VirtualizationRootHandle FindUnusedIndex_Locked() -{ - for (uint32_t i = 0; i < s_maxVirtualizationRoots; ++i) - { - if (!s_virtualizationRoots[i].inUse) - { - return i; - } - } - - return RootHandle_None; -} - -static void GrowVirtualizationRootArrayWithMemory_Locked(VirtualizationRoot* newMemory, uint16_t newLength) -{ - uint32_t oldSizeBytes = sizeof(s_virtualizationRoots[0]) * s_maxVirtualizationRoots; - memcpy(newMemory, s_virtualizationRoots, oldSizeBytes); - Memory_FreeArray(s_virtualizationRoots, s_maxVirtualizationRoots); - s_virtualizationRoots = newMemory; - - Array_DefaultInit(&s_virtualizationRoots[s_maxVirtualizationRoots], newLength - s_maxVirtualizationRoots); - - s_maxVirtualizationRoots = newLength; -} - -static bool FsidsAreEqual(fsid_t a, fsid_t b) -{ - return a.val[0] == b.val[0] && a.val[1] == b.val[1]; -} - -static VirtualizationRootHandle FindRootAtVnode_Locked(vnode_t vnode, uint32_t vid, FsidInode fileId) -{ - for (uint32_t i = 0; i < s_maxVirtualizationRoots; ++i) - { - VirtualizationRoot& rootEntry = s_virtualizationRoots[i]; - if (!rootEntry.inUse) - { - continue; - } - - if (rootEntry.rootVNode == vnode && rootEntry.rootVNodeVid == vid) - { - assertf( - FsidsAreEqual(fileId.fsid, rootEntry.rootFsid) && fileId.inode == rootEntry.rootInode, - "FindRootAtVnode_Locked: matching root vnode/vid but not fsid/inode? vnode %p:%u. rootEntry fsid 0x%x:%x, inode 0x%llx, searching for fsid 0x%x:%x, inode 0x%llx", - KextLog_Unslide(vnode), vid, rootEntry.rootFsid.val[0], rootEntry.rootFsid.val[1], rootEntry.rootInode, fileId.fsid.val[0], fileId.fsid.val[1], fileId.inode); - return i; - } - else if (rootEntry.rootVNode == vnode) - { - assert(rootEntry.providerUserClient == nullptr); - } - - - if (FsidsAreEqual(rootEntry.rootFsid, fileId.fsid) && rootEntry.rootInode == fileId.inode) - { - assertf(rootEntry.providerUserClient == nullptr, "Finding root vnode based on FSID/inode equality but not vnode identity (recycled vnode) should only happen if no provider is active. Root index %d, provider PID %d, IOUC %p path '%s'", - i, rootEntry.providerPid, KextLog_Unslide(rootEntry.providerUserClient), rootEntry.path); - // root vnode must be stale - KextLog_File(vnode, "FindRootAtVnode_Locked: virtualization root %d (path: \"%s\", fsid: 0x%x:%x, inode: 0x%llx) directory vnode %p:%u has gone stale, new vnode %p:%u", - i, rootEntry.path, rootEntry.rootFsid.val[0], rootEntry.rootFsid.val[1], rootEntry.rootInode, KextLog_Unslide(rootEntry.rootVNode), rootEntry.rootVNodeVid, KextLog_Unslide(vnode), vid); - return i; - } - } - - return RootHandle_None; -} - -static void RefreshRootVnodeIfNecessary_Locked(VirtualizationRootHandle rootHandle, vnode_t vnode, uint32_t vid, FsidInode fileId) -{ - VirtualizationRoot& rootEntry = s_virtualizationRoots[rootHandle]; - if (rootEntry.rootVNode == vnode && rootEntry.rootVNodeVid == vid) - { - return; - } - - assertf( - FsidsAreEqual(rootEntry.rootFsid, fileId.fsid) && rootEntry.rootInode == fileId.inode, - "RefreshRootVnodeIfNecessary_Locked: expecting matching FSID/inode for new vnode on root %d (%s). " - "Root's: FSID 0x%x:%x, inode 0x%llx; new vnode's FSID: 0x%x:%x, inode: 0x%llx; " - "old vnode %p:%u, new %p:%u", - rootHandle, rootEntry.path, - rootEntry.rootFsid.val[0], rootEntry.rootFsid.val[1], rootEntry.rootInode, fileId.fsid.val[0], fileId.fsid.val[1], fileId.inode, - KextLog_Unslide(rootEntry.rootVNode), rootEntry.rootVNodeVid, KextLog_Unslide(vnode), vid); - - assertf( - rootEntry.providerUserClient == nullptr, - "RefreshRootVnodeIfNecessary_Locked: only root vnodes with no active provider should be recycled! Virtualization root %d (path: \"%s\", fsid: 0x%x:%x, inode: 0x%llx) directory vnode %p:%u has gone stale, refreshing with new vnode %p:%u", - rootHandle, rootEntry.path, rootEntry.rootFsid.val[0], rootEntry.rootFsid.val[1], rootEntry.rootInode, KextLog_Unslide(rootEntry.rootVNode), rootEntry.rootVNodeVid, KextLog_Unslide(vnode), vid); - KextLog_File(vnode, "RefreshRootVnodeIfNecessary_Locked: virtualization root %d (path: \"%s\", fsid: 0x%x:%x, inode: 0x%llx) directory vnode %p:%u has gone stale, refreshing with new vnode %p:%u", - rootHandle, rootEntry.path, rootEntry.rootFsid.val[0], rootEntry.rootFsid.val[1], rootEntry.rootInode, KextLog_Unslide(rootEntry.rootVNode), rootEntry.rootVNodeVid, KextLog_Unslide(vnode), vid); - rootEntry.rootVNode = vnode; - rootEntry.rootVNodeVid = vid; -} - -KEXT_STATIC VirtualizationRootHandle FindOrInsertVirtualizationRoot_LockedMayUnlock(vnode_t virtualizationRootVNode, uint32_t rootVid, FsidInode persistentIds, const char* path) -{ - VirtualizationRootHandle rootIndex; - bool allocFailed = false; - do - { - rootIndex = FindRootAtVnode_Locked(virtualizationRootVNode, rootVid, persistentIds); - if (rootIndex >= 0) - { - return rootIndex; - } - - rootIndex = FindUnusedIndex_Locked(); - if (rootIndex < 0) - { - // No space, resize array - uint16_t newLength = MIN(s_maxVirtualizationRoots * 2u, MaxVirtualizationRootsLimit); - if (newLength <= s_maxVirtualizationRoots || allocFailed) - { - KextLog_Error( - "FindOrInsertVirtualizationRoot_LockedMayUnlock: growing virtualization root array for root at '%s' failed: %s, array has reached %u items, resize target %u items\n", - path, - allocFailed ? "memory allocation failed" : "out of valid indices", - s_maxVirtualizationRoots, - newLength); - return RootHandle_None; - } - - // Must drop lock to safely allocate memory with blocking alloc - RWLock_ReleaseExclusive(s_virtualizationRootsLock); - VirtualizationRoot* grownArray = Memory_AllocArray(newLength); - RWLock_AcquireExclusive(s_virtualizationRootsLock); - - if (grownArray == nullptr) - { - // Allocation failed; recheck for space since relocking, and if that fails, give up. - allocFailed = true; - continue; - } - - uint16_t newLengthAgain = MIN(s_maxVirtualizationRoots * 2u, MaxVirtualizationRootsLimit); - if (newLengthAgain != newLength || s_maxVirtualizationRoots == MaxVirtualizationRootsLimit) - { - KextLog_Info("FindOrInsertVirtualizationRoot_LockedMayUnlock: Beaten to the resize (newLength = %u, newLengthAgain = %u) by another thread, starting over.", newLength, newLengthAgain); - // another thread already resized, start over. - Memory_FreeArray(grownArray, newLength); - continue; - } - - GrowVirtualizationRootArrayWithMemory_Locked(grownArray, newLength); - } - } while (rootIndex < 0); - - assert(rootIndex < s_maxVirtualizationRoots); - assert(!s_virtualizationRoots[rootIndex].inUse); - - VirtualizationRoot* root = &s_virtualizationRoots[rootIndex]; - - root->inUse = true; - - root->rootVNode = virtualizationRootVNode; - root->rootVNodeVid = rootVid; - root->rootFsid = persistentIds.fsid; - root->rootInode = persistentIds.inode; - if (path != nullptr) - { - strlcpy(root->path, path, sizeof(root->path)); - } - - KextLog_File(virtualizationRootVNode, "FindOrInsertVirtualizationRoot_LockedMayUnlock: virtualization root inserted at index %d: (path: \"%s\", fsid: 0x%x:%x, inode: 0x%llx) directory vnode %p:%u.", - rootIndex, path, persistentIds.fsid.val[0], persistentIds.fsid.val[1], persistentIds.inode, KextLog_Unslide(virtualizationRootVNode), rootVid); - - return rootIndex; -} - -// Return values: -// 0: Virtualization root found and successfully registered -// ENOMEM: Too many virtualization roots. -// ENOTDIR: Selected virtualization root path does not resolve to a directory. -// EBUSY: Already a provider for this virtualization root. -// ENOENT: Error returned by vnode_lookup. -// Any error returned by the call to vn_getpath() -VirtualizationRootResult VirtualizationRoot_RegisterProviderForPath(PrjFSProviderUserClient* userClient, pid_t clientPID, const char* virtualizationRootPath) -{ - assert(nullptr != virtualizationRootPath); - assert(nullptr != userClient); - - vnode_t virtualizationRootVNode = NULLVP; - vfs_context_t _Nonnull vfsContext = vfs_context_create(nullptr); - - VirtualizationRootHandle rootIndex = RootHandle_None; - errno_t err = vnode_lookup(virtualizationRootPath, /* flags: */ VNODE_LOOKUP_NOFOLLOW, &virtualizationRootVNode, vfsContext); - if (0 == err) - { - if (!VirtualizationRoot_VnodeIsOnAllowedFilesystem(virtualizationRootVNode)) - { - err = ENODEV; - } - else if (!vnode_isdir(virtualizationRootVNode)) - { - err = ENOTDIR; - } - else - { - char virtualizationRootCanonicalPath[PrjFSMaxPath] = ""; - int virtualizationRootCanonicalPathLength = sizeof(virtualizationRootCanonicalPath); - err = vn_getpath(virtualizationRootVNode, virtualizationRootCanonicalPath, &virtualizationRootCanonicalPathLength); - - if (0 != err) - { - KextLog_ErrorVnodeProperties( - virtualizationRootVNode, "VirtualizationRoot_RegisterProviderForPath: vn_getpath failed (error = %d) for vnode looked up from path '%s'", err, virtualizationRootPath); - } - else - { - FsidInode vnodeIds = Vnode_GetFsidAndInode(virtualizationRootVNode, vfsContext, false /* If a root has multiple hardlinks to it, only track one instance of it. */); - uint32_t rootVid = vnode_vid(virtualizationRootVNode); - - RWLock_AcquireExclusive(s_virtualizationRootsLock); - { - rootIndex = FindOrInsertVirtualizationRoot_LockedMayUnlock(virtualizationRootVNode, rootVid, vnodeIds, virtualizationRootCanonicalPath); - - if (rootIndex >= 0) - { - RefreshRootVnodeIfNecessary_Locked(rootIndex, virtualizationRootVNode, rootVid, vnodeIds); - - VirtualizationRoot& root = s_virtualizationRoots[rootIndex]; - assert(root.rootVNode == virtualizationRootVNode); - - if (nullptr != root.providerUserClient) - { - // Only one provider per root - err = EBUSY; - rootIndex = RootHandle_None; - } - else - { - root.providerUserClient = userClient; - root.providerPid = clientPID; - strlcpy(root.path, virtualizationRootCanonicalPath, sizeof(root.path)); - KextLog_File(virtualizationRootVNode, "VirtualizationRoot_RegisterProviderForPath: registered provider (PID %d, IOUC %p) for virtualization root %d: (path: \"%s\", fsid: 0x%x:%x, inode: 0x%llx) directory vnode %p:%u.", - clientPID, KextLog_Unslide(userClient), rootIndex, root.path, root.rootFsid.val[0], root.rootFsid.val[1], root.rootInode, KextLog_Unslide(virtualizationRootVNode), rootVid); - - // Acquire strong reference while provider is connected - err = vnode_get(virtualizationRootVNode); - assert(err == 0); // we already hold 1 strong reference from vnode_lookup so this should always succeed - } - } - else - { - KextLog_Error("VirtualizationRoot_RegisterProviderForPath: failed to insert new root '%s' for provider with PID %u", virtualizationRootPath, clientPID); - err = ENOMEM; - } - } - RWLock_ReleaseExclusive(s_virtualizationRootsLock); - - if (0 == err) - { - ProviderUserClient_UpdatePathProperty(userClient, virtualizationRootCanonicalPath); - } - } - } - } - - if (VirtualizationRoot_IsValidRootHandle(rootIndex)) - { - vfs_setauthcache_ttl(vnode_mount(virtualizationRootVNode), 0); - } - - if (NULLVP != virtualizationRootVNode) - { - vnode_put(virtualizationRootVNode); - } - - vfs_context_rele(vfsContext); - - return VirtualizationRootResult { err, rootIndex }; -} - -void ActiveProvider_Disconnect(VirtualizationRootHandle rootIndex, PrjFSProviderUserClient* _Nonnull userClient) -{ - assert(rootIndex >= 0); - RWLock_AcquireExclusive(s_virtualizationRootsLock); - { - assert(rootIndex <= s_maxVirtualizationRoots); - - VirtualizationRoot* root = &s_virtualizationRoots[rootIndex]; - assert(nullptr != root->providerUserClient); - assertf(userClient == root->providerUserClient, "ActiveProvider_Disconnect: disconnecting provider IOUC %p for root index %d (%s), but expecting IOUC %p", - KextLog_Unslide(userClient), rootIndex, root->path, KextLog_Unslide(root->providerUserClient)); - - assert(NULLVP != root->rootVNode); - - KextLog_File(root->rootVNode, "ActiveProvider_Disconnect: disconnecting provider (PID %d, IOUC %p) for virtualization root %d: (path: \"%s\", fsid: 0x%x:%x, inode: 0x%llx) directory vnode %p:%u.", - root->providerPid, KextLog_Unslide(root->providerUserClient), rootIndex, root->path, root->rootFsid.val[0], root->rootFsid.val[1], root->rootInode, KextLog_Unslide(root->rootVNode), root->rootVNodeVid); - - - vnode_put(root->rootVNode); - root->providerPid = 0; - - root->providerUserClient = nullptr; - - RWLock_DropExclusiveToShared(s_virtualizationRootsLock); - - ProviderMessaging_AbortOutstandingEventsForProvider(rootIndex); - } - RWLock_ReleaseShared(s_virtualizationRootsLock); -} - -errno_t ActiveProvider_SendMessage(VirtualizationRootHandle rootIndex, const Message& message) -{ - assert(rootIndex >= 0); - - PrjFSProviderUserClient* userClient = nullptr; - - RWLock_AcquireShared(s_virtualizationRootsLock); - { - assert(rootIndex < s_maxVirtualizationRoots); - - userClient = s_virtualizationRoots[rootIndex].providerUserClient; - if (nullptr != userClient) - { - ProviderUserClient_Retain(userClient); - } - } - RWLock_ReleaseShared(s_virtualizationRootsLock); - - if (nullptr != userClient) - { - uint32_t messageSize = Message_EncodedSize(message.messageHeader); - uint8_t messageMemory[messageSize]; - uint32_t bytesUsed OS_UNUSED = Message_Encode(messageMemory, messageSize, message); - assertf(bytesUsed == messageSize, "bytes used by Message_Encode (%u) should match Message_EncodedSize's prediction (%u)", bytesUsed, messageSize); - - ProviderUserClient_SendMessage(userClient, messageMemory, messageSize); - ProviderUserClient_Release(userClient); - return 0; - } - else - { - return EIO; - } -} - -bool VirtualizationRoot_VnodeIsOnAllowedFilesystem(vnode_t vnode) -{ - vfsstatfs* vfsStat = vfs_statfs(vnode_mount(vnode)); - return - 0 == strncmp("hfs", vfsStat->f_fstypename, sizeof(vfsStat->f_fstypename)) - || 0 == strncmp("apfs", vfsStat->f_fstypename, sizeof(vfsStat->f_fstypename)); -} - -KEXT_STATIC bool PathInsideDirectory(const char* directoryPath, const char* path) -{ - if (!strprefix(path, directoryPath)) - { - return false; - } - - // string prefix alone is not sufficient, must not return true for the following: - // /path/to/some/dir - // /path/to/some/directory/containing/file - // so check for the '/' positioning: - - size_t directoryPathLength = strlen(directoryPath); - if (directoryPathLength >= 1 && directoryPath[directoryPathLength - 1] == '/') - { - // directoryPath ends with a "/", so no ambiguity - return true; - } - else if (path[directoryPathLength] == '\0' // path is identical to directoryPath - || path[directoryPathLength] == '/') // path really is strictly below directoryPath - { - return true; - } - - return false; -} - -VirtualizationRootHandle ActiveProvider_FindForPath(const char* _Nonnull path) -{ - VirtualizationRootHandle matchingHandle = RootHandle_None; - - RWLock_AcquireShared(s_virtualizationRootsLock); - { - for (uint32_t i = 0; i < s_maxVirtualizationRoots; ++i) - { - VirtualizationRoot& root = s_virtualizationRoots[i]; - if (root.inUse && root.providerUserClient != nullptr && PathInsideDirectory(root.path, path)) - { - matchingHandle = i; - break; - } - } - } - RWLock_ReleaseShared(s_virtualizationRootsLock); - - return matchingHandle; -} - -/// Tests whether pid or its parent, grandparent, etc. process is registered for offline I/O -bool VirtualizationRoots_ProcessMayAccessOfflineRoots(pid_t pid) -{ - bool result = false; - - RWLock_AcquireShared(s_virtualizationRootsLock); - { - while (true) - { - for (uint32_t i = 0; i < s_offlineIOPIDCount; ++i) - { - if (s_offlineIOPIDs[i] == pid) - { - result = true; - goto done; - } - } - - // Walk up the process hierarchy - proc_t process = proc_find(pid); - if (process == nullptr) - { - // Process exited since last proc_ppid call. - break; - } - - pid = proc_ppid(process); - proc_rele(process); - - // Stop when we hit the launchd root process - if (pid <= 1) - { - break; - } - } - done: {} - } - RWLock_ReleaseShared(s_virtualizationRootsLock); - - return result; -} - -bool VirtualizationRoots_AddOfflineIOProcess(pid_t pid) -{ - bool success = false; - - RWLock_AcquireExclusive(s_virtualizationRootsLock); - { - if (s_offlineIOPIDCount < MaxOfflineIOPIDs) - { - s_offlineIOPIDs[s_offlineIOPIDCount] = pid; - ++s_offlineIOPIDCount; - success = true; - } - } - RWLock_ReleaseExclusive(s_virtualizationRootsLock); - - return success; -} - -void VirtualizationRoots_RemoveOfflineIOProcess(pid_t pid) -{ - bool removed = false; - - RWLock_AcquireExclusive(s_virtualizationRootsLock); - { - assert(s_offlineIOPIDCount > 0); - - for (uint32_t i = 0; i < s_offlineIOPIDCount; ++i) - { - if (s_offlineIOPIDs[i] == pid) - { - --s_offlineIOPIDCount; - if (i != s_offlineIOPIDCount) - { - // move last element to fill vacated slot - s_offlineIOPIDs[i] = s_offlineIOPIDs[s_offlineIOPIDCount]; - } - - removed = true; - break; - } - } - } - RWLock_ReleaseExclusive(s_virtualizationRootsLock); - - assert(removed); -} diff --git a/ProjFS.Mac/PrjFSKext/VirtualizationRoots.hpp b/ProjFS.Mac/PrjFSKext/VirtualizationRoots.hpp deleted file mode 100644 index 7c27c23ada..0000000000 --- a/ProjFS.Mac/PrjFSKext/VirtualizationRoots.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "public/FsidInode.h" -#include "PrjFSClasses.hpp" -#include "public/PrjFSPerfCounter.h" -#include "PerformanceTracing.hpp" -#include "kernel-header-wrappers/vnode.h" - -typedef int16_t VirtualizationRootHandle; - -// Zero and positive values indicate a handle for a valid virtualization -// root. Other values have special meanings: -enum VirtualizationRootSpecialHandle : VirtualizationRootHandle -{ - // Not in a virtualization root. - RootHandle_None = -1, - // Root/non-root state not known. Useful reset value for invalidating cached state. - RootHandle_Indeterminate = -2, - // Vnode is not in a virtualization root, but below a provider's registered temp directory - RootHandle_ProviderTemporaryDirectory = -3, -}; - -kern_return_t VirtualizationRoots_Init(void); -kern_return_t VirtualizationRoots_Cleanup(void); - -VirtualizationRootHandle VirtualizationRoot_FindForVnode( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter functionCounter, - PrjFSPerfCounter innerLoopCounter, - vnode_t _Nonnull vnode, - vfs_context_t _Nonnull context); - -VirtualizationRootHandle ActiveProvider_FindForPath( - const char* _Nonnull path); - -struct ActiveProviderProperties -{ - bool isOnline; - pid_t pid; -}; - -struct VirtualizationRootResult -{ - errno_t error; - VirtualizationRootHandle root; -}; -VirtualizationRootResult VirtualizationRoot_RegisterProviderForPath(PrjFSProviderUserClient* _Nonnull userClient, pid_t clientPID, const char* _Nonnull virtualizationRootPath); -void ActiveProvider_Disconnect(VirtualizationRootHandle rootHandle, PrjFSProviderUserClient* _Nonnull userClient); - -struct Message; -errno_t ActiveProvider_SendMessage(VirtualizationRootHandle rootHandle, const Message& message); -bool VirtualizationRoot_VnodeIsOnAllowedFilesystem(vnode_t _Nonnull vnode); -bool VirtualizationRoot_IsValidRootHandle(VirtualizationRootHandle rootHandle); -ActiveProviderProperties VirtualizationRoot_GetActiveProvider(VirtualizationRootHandle rootHandle); -bool VirtualizationRoots_ProcessMayAccessOfflineRoots(pid_t pid); -bool VirtualizationRoots_AddOfflineIOProcess(pid_t pid); -void VirtualizationRoots_RemoveOfflineIOProcess(pid_t pid); diff --git a/ProjFS.Mac/PrjFSKext/VirtualizationRootsPrivate.hpp b/ProjFS.Mac/PrjFSKext/VirtualizationRootsPrivate.hpp deleted file mode 100644 index 2ff6dce08a..0000000000 --- a/ProjFS.Mac/PrjFSKext/VirtualizationRootsPrivate.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -struct VirtualizationRoot -{ - bool inUse; - // If this is a nullptr, there is no active provider for this virtualization root (offline root) - PrjFSProviderUserClient* providerUserClient; - pid_t providerPid; - // For an active root, this is retained (vnode_get), for an offline one, it is not, so it may be stale (check the vid) - vnode_t rootVNode; - uint32_t rootVNodeVid; - - // Mount point ID + persistent, on-disk ID for the root directory, so we can - // identify it if the vnode of an offline root gets recycled. - fsid_t rootFsid; - uint64_t rootInode; - - // Only contains a valid path for online roots (active provider) - char path[PrjFSMaxPath]; -}; - -#if defined(KEXT_UNIT_TESTING) && !defined(TESTABLE_KEXT_TARGET) // Building unit tests -#include -static_assert(std::is_trivially_copyable::value, "The array of VirtualizationRoot objects is resized using memcpy"); -#endif diff --git a/ProjFS.Mac/PrjFSKext/VirtualizationRootsTestable.hpp b/ProjFS.Mac/PrjFSKext/VirtualizationRootsTestable.hpp deleted file mode 100644 index dc467877f5..0000000000 --- a/ProjFS.Mac/PrjFSKext/VirtualizationRootsTestable.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#ifndef KEXT_UNIT_TESTING -#error Don't #include this file in non-testing builds -#endif - -#include "VirtualizationRoots.hpp" -#include "VirtualizationRootsPrivate.hpp" - -extern uint16_t s_maxVirtualizationRoots; -extern VirtualizationRoot* s_virtualizationRoots; - -KEXT_STATIC VirtualizationRootHandle FindOrInsertVirtualizationRoot_LockedMayUnlock(vnode_t virtualizationRootVNode, uint32_t rootVid, FsidInode persistentIds, const char* path); -KEXT_STATIC bool PathInsideDirectory(const char* directoryPath, const char* path); - diff --git a/ProjFS.Mac/PrjFSKext/VnodeCache.cpp b/ProjFS.Mac/PrjFSKext/VnodeCache.cpp deleted file mode 100644 index ff03f36992..0000000000 --- a/ProjFS.Mac/PrjFSKext/VnodeCache.cpp +++ /dev/null @@ -1,534 +0,0 @@ -#include -#include -#include "Locks.hpp" -#include "VnodeCache.hpp" -#include "Memory.hpp" -#include "KextLog.hpp" -#include "../PrjFSKext/public/PrjFSCommon.h" -#include "../PrjFSKext/public/PrjFSVnodeCacheHealth.h" - -#include "VnodeCachePrivate.hpp" - -#ifdef KEXT_UNIT_TESTING -#include "VnodeCacheTestable.hpp" -#endif - -KEXT_STATIC_INLINE void InvalidateCache_ExclusiveLocked(); -KEXT_STATIC_INLINE uintptr_t ComputeVnodeHashIndex(vnode_t _Nonnull vnode); -KEXT_STATIC_INLINE uint32_t ComputePow2CacheCapacity(int expectedVnodeCount); - -KEXT_STATIC bool TryGetVnodeRootFromCache( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - /* out parameters */ - VirtualizationRootHandle& rootHandle); - -KEXT_STATIC void FindVnodeRootFromDiskAndUpdateCache( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vfs_context_t _Nonnull context, - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - UpdateCacheBehavior updateEntryBehavior, - /* out parameters */ - VirtualizationRootHandle& rootHandle); - -KEXT_STATIC void InsertEntryToInvalidatedCache_ExclusiveLocked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - VirtualizationRootHandle rootHandle); - -KEXT_STATIC bool TryFindVnodeIndex_Locked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - /* out parameters */ - uintptr_t& vnodeIndex); - -KEXT_STATIC bool TryInsertOrUpdateEntry_ExclusiveLocked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - bool forceRefreshEntry, - VirtualizationRootHandle rootHandle); - -KEXT_STATIC_INLINE void InitCacheStats(); -KEXT_STATIC_INLINE void AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat healthStat, uint64_t value); - -KEXT_STATIC uint32_t s_entriesCapacity; -KEXT_STATIC VnodeCacheEntry* s_entries; - -// s_entriesCapacity will always be a power of 2, and so we can compute the modulo -// using (value & s_ModBitmask) rather than (value % s_entriesCapacity); -KEXT_STATIC uintptr_t s_ModBitmask; - -KEXT_STATIC VnodeCacheStats s_cacheStats; - -static RWLock s_entriesLock; - -kern_return_t VnodeCache_Init() -{ - if (RWLock_IsValid(s_entriesLock)) - { - return KERN_FAILURE; - } - - s_entriesLock = RWLock_Alloc(); - if (!RWLock_IsValid(s_entriesLock)) - { - return KERN_FAILURE; - } - - s_entriesCapacity = ComputePow2CacheCapacity(desiredvnodes); - s_ModBitmask = s_entriesCapacity - 1; - - s_entries = Memory_AllocArray(s_entriesCapacity); - if (nullptr == s_entries) - { - s_entriesCapacity = 0; - return KERN_RESOURCE_SHORTAGE; - } - - memset(s_entries, 0, s_entriesCapacity * sizeof(VnodeCacheEntry)); - - InitCacheStats(); - - PerfTracing_RecordSample(PrjFSPerfCounter_CacheCapacity, 0, s_entriesCapacity); - - return KERN_SUCCESS; -} - -kern_return_t VnodeCache_Cleanup() -{ - if (nullptr != s_entries) - { - Memory_FreeArray(s_entries, s_entriesCapacity); - s_entries = nullptr; - s_entriesCapacity = 0; - } - - if (RWLock_IsValid(s_entriesLock)) - { - RWLock_FreeMemory(&s_entriesLock); - return KERN_SUCCESS; - } - - return KERN_FAILURE; -} - -VirtualizationRootHandle VnodeCache_FindRootForVnode( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheHitCounter, - PrjFSPerfCounter cacheMissCounter, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vnode_t _Nonnull vnode, - vfs_context_t _Nonnull context) -{ - VirtualizationRootHandle rootHandle = RootHandle_None; - uintptr_t vnodeHashIndex = ComputeVnodeHashIndex(vnode); - uint32_t vnodeVid = vnode_vid(vnode); - - if (TryGetVnodeRootFromCache(vnode, vnodeHashIndex, vnodeVid, rootHandle)) - { - perfTracer->IncrementCount(cacheHitCounter, true /*ignoreSampling*/); - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_TotalFindRootForVnodeHits, 1ULL); - return rootHandle; - } - - perfTracer->IncrementCount(cacheMissCounter, true /*ignoreSampling*/); - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_TotalFindRootForVnodeMisses, 1ULL); - - FindVnodeRootFromDiskAndUpdateCache( - perfTracer, - cacheMissFallbackFunctionCounter, - cacheMissFallbackFunctionInnerLoopCounter, - context, - vnode, - vnodeHashIndex, - vnodeVid, - UpdateCacheBehavior_TrustCurrentEntry, - rootHandle); - - return rootHandle; -} - -VirtualizationRootHandle VnodeCache_RefreshRootForVnode( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheHitCounter, - PrjFSPerfCounter cacheMissCounter, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vnode_t _Nonnull vnode, - vfs_context_t _Nonnull context) -{ - VirtualizationRootHandle rootHandle = RootHandle_None; - uintptr_t vnodeHashIndex = ComputeVnodeHashIndex(vnode); - uint32_t vnodeVid = vnode_vid(vnode); - - perfTracer->IncrementCount(cacheMissCounter, true /*ignoreSampling*/); - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_TotalRefreshRootForVnode, 1ULL); - - FindVnodeRootFromDiskAndUpdateCache( - perfTracer, - cacheMissFallbackFunctionCounter, - cacheMissFallbackFunctionInnerLoopCounter, - context, - vnode, - vnodeHashIndex, - vnodeVid, - UpdateCacheBehavior_ForceRefresh, - rootHandle); - - return rootHandle; -} - -VirtualizationRootHandle VnodeCache_InvalidateVnodeRootAndGetLatestRoot( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheHitCounter, - PrjFSPerfCounter cacheMissCounter, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vnode_t _Nonnull vnode, - vfs_context_t _Nonnull context) -{ - VirtualizationRootHandle rootHandle = RootHandle_None; - uintptr_t vnodeHashIndex = ComputeVnodeHashIndex(vnode); - uint32_t vnodeVid = vnode_vid(vnode); - - perfTracer->IncrementCount(cacheMissCounter, true /*ignoreSampling*/); - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_TotalInvalidateVnodeRoot, 1ULL); - - FindVnodeRootFromDiskAndUpdateCache( - perfTracer, - cacheMissFallbackFunctionCounter, - cacheMissFallbackFunctionInnerLoopCounter, - context, - vnode, - vnodeHashIndex, - vnodeVid, - UpdateCacheBehavior_InvalidateEntry, - rootHandle); - - return rootHandle; -} - -void VnodeCache_InvalidateCache(PerfTracer* _Nonnull perfTracer) -{ - perfTracer->IncrementCount(PrjFSPerfCounter_CacheInvalidateCount, true /*ignoreSampling*/); - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_InvalidateEntireCacheCount, 1ULL); - - RWLock_AcquireExclusive(s_entriesLock); - { - InvalidateCache_ExclusiveLocked(); - } - RWLock_ReleaseExclusive(s_entriesLock); -} - -IOReturn VnodeCache_ExportHealthData(IOExternalMethodArguments* _Nonnull arguments) -{ - PrjFSVnodeCacheHealth healthData = - { - .cacheCapacity = s_entriesCapacity, - .cacheEntries = s_cacheStats.cacheEntries, // cacheEntries is reset to 0 when VnodeCache_InvalidateCache is called - .invalidateEntireCacheCount = atomic_exchange_explicit(&s_cacheStats.healthStats[VnodeCacheHealthStat_InvalidateEntireCacheCount], 0ULL, memory_order_relaxed), - .totalCacheLookups = atomic_exchange_explicit(&s_cacheStats.healthStats[VnodeCacheHealthStat_TotalCacheLookups], 0ULL, memory_order_relaxed), - .totalLookupCollisions = atomic_exchange_explicit(&s_cacheStats.healthStats[VnodeCacheHealthStat_TotalLookupCollisions], 0ULL, memory_order_relaxed), - .totalFindRootForVnodeHits = atomic_exchange_explicit(&s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeHits], 0ULL, memory_order_relaxed), - .totalFindRootForVnodeMisses = atomic_exchange_explicit(&s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeMisses], 0ULL, memory_order_relaxed), - .totalRefreshRootForVnode = atomic_exchange_explicit(&s_cacheStats.healthStats[VnodeCacheHealthStat_TotalRefreshRootForVnode], 0ULL, memory_order_relaxed), - .totalInvalidateVnodeRoot = atomic_exchange_explicit(&s_cacheStats.healthStats[VnodeCacheHealthStat_TotalInvalidateVnodeRoot], 0ULL, memory_order_relaxed), - }; - - // The buffer will come in either as a memory descriptor or direct pointer, depending on size - if (nullptr != arguments->structureOutputDescriptor) - { - IOMemoryDescriptor* structureOutput = arguments->structureOutputDescriptor; - if (sizeof(healthData) != structureOutput->getLength()) - { - KextLog( - "VnodeCache_ExportHealthData: structure output descriptor size %llu, expected %lu\n", - static_cast(structureOutput->getLength()), - sizeof(healthData)); - return kIOReturnBadArgument; - } - - IOReturn result = structureOutput->prepare(kIODirectionIn); - if (kIOReturnSuccess == result) - { - structureOutput->writeBytes(0 /* offset */, &healthData, sizeof(healthData)); - structureOutput->complete(kIODirectionIn); - } - - return result; - } - - if (arguments->structureOutput == nullptr || arguments->structureOutputSize != sizeof(PrjFSVnodeCacheHealth)) - { - KextLog("VnodeCache_ExportHealthData: structure output size %u, expected %lu\n", arguments->structureOutputSize, sizeof(healthData)); - return kIOReturnBadArgument; - } - - memcpy(arguments->structureOutput, &healthData, sizeof(healthData)); - return kIOReturnSuccess; -} - -KEXT_STATIC_INLINE void InvalidateCache_ExclusiveLocked() -{ - memset(s_entries, 0, s_entriesCapacity * sizeof(VnodeCacheEntry)); - atomic_store_explicit(&s_cacheStats.cacheEntries, 0U, memory_order_relaxed); -} - -KEXT_STATIC_INLINE uint32_t ComputePow2CacheCapacity(int expectedVnodeCount) -{ - uint32_t idealCacheCapacity = expectedVnodeCount * 2; - - // Start with a cache capacity of MinPow2VnodeCacheCapacity, and keep increasing - // it by powers of 2 until the capacity is larger than idealCacheCapacity *or* we've - // hit the maximum allowed cache capcity - uint32_t cacheCapacity = MinPow2VnodeCacheCapacity; - while ((cacheCapacity < idealCacheCapacity) && - (cacheCapacity < MaxPow2VnodeCacheCapacity)) - { - // Increase cacheCapacity by a power of 2 - cacheCapacity = cacheCapacity << 1; - } - - return cacheCapacity; -} - -KEXT_STATIC_INLINE uintptr_t ComputeVnodeHashIndex(vnode_t _Nonnull vnode) -{ - uintptr_t vnodeAddress = reinterpret_cast(vnode); - return (vnodeAddress >> 3) & s_ModBitmask; -} - -KEXT_STATIC bool TryGetVnodeRootFromCache( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - /* out parameters */ - VirtualizationRootHandle& rootHandle) -{ - bool rootFound = false; - rootHandle = RootHandle_None; - - RWLock_AcquireShared(s_entriesLock); - { - uintptr_t vnodeIndex; - if (TryFindVnodeIndex_Locked(vnode, vnodeHashIndex, /*out*/ vnodeIndex)) - { - if (vnode == s_entries[vnodeIndex].vnode && - vnodeVid == s_entries[vnodeIndex].vid && - RootHandle_Indeterminate != s_entries[vnodeIndex].virtualizationRoot) - { - rootFound = true; - rootHandle = s_entries[vnodeIndex].virtualizationRoot; - } - } - } - RWLock_ReleaseShared(s_entriesLock); - - return rootFound; -} - -KEXT_STATIC void FindVnodeRootFromDiskAndUpdateCache( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vfs_context_t _Nonnull context, - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - UpdateCacheBehavior updateEntryBehavior, - /* out parameters */ - VirtualizationRootHandle& rootHandle) -{ - rootHandle = VirtualizationRoot_FindForVnode( - perfTracer, - cacheMissFallbackFunctionCounter, - cacheMissFallbackFunctionInnerLoopCounter, - vnode, - context); - - bool forceRefreshEntry; - VirtualizationRootHandle rootToInsert; - switch (updateEntryBehavior) - { - case UpdateCacheBehavior_ForceRefresh: - rootToInsert = rootHandle; - forceRefreshEntry = true; - break; - - case UpdateCacheBehavior_InvalidateEntry: - rootToInsert = RootHandle_Indeterminate; - forceRefreshEntry = true; - break; - - case UpdateCacheBehavior_TrustCurrentEntry: - rootToInsert = rootHandle; - forceRefreshEntry = false; - break; - - default: - assertf( - false, - "FindVnodeRootFromDiskAndUpdateCache: Invalid updateEntryBehavior %d", - updateEntryBehavior); - - rootToInsert = rootHandle; - forceRefreshEntry = false; - break; - } - - RWLock_AcquireExclusive(s_entriesLock); - { - if (!TryInsertOrUpdateEntry_ExclusiveLocked( - vnode, - vnodeHashIndex, - vnodeVid, - forceRefreshEntry, - rootToInsert)) - { - // TryInsertOrUpdateEntry_ExclusiveLocked can only fail if the cache is full - perfTracer->IncrementCount(PrjFSPerfCounter_CacheFullCount, true /*ignoreSampling*/); - InvalidateCache_ExclusiveLocked(); - InsertEntryToInvalidatedCache_ExclusiveLocked(vnode, vnodeHashIndex, vnodeVid, rootToInsert); - - } - } - RWLock_ReleaseExclusive(s_entriesLock); -} - -// InsertEntryToInvalidatedCache_ExclusiveLocked is a helper function for inserting a vnode -// into the cache after the cache has been invalidated. This functionality is in its own function -// (rather than in FindVnodeRootFromDiskAndUpdateCache) so that the kext unit tests can force its call -// to TryInsertOrUpdateEntry_ExclusiveLocked to fail. -KEXT_STATIC void InsertEntryToInvalidatedCache_ExclusiveLocked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - VirtualizationRootHandle rootToInsert) -{ - if(!TryInsertOrUpdateEntry_ExclusiveLocked( - vnode, - vnodeHashIndex, - vnodeVid, - true, // forceRefreshEntry - rootToInsert)) - { - KextLog_Error( - "InsertEntryToInvalidatedCache_ExclusiveLocked: inserting vnode (%p:%u) failed", - KextLog_Unslide(vnode), - vnodeVid); - } -} - -KEXT_STATIC bool TryFindVnodeIndex_Locked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - /* out parameters */ - uintptr_t& vnodeIndex) -{ - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_TotalCacheLookups, 1ULL); - - // Walk from the starting index until we do one of the following: - // -> Find the vnode - // -> Find where the vnode should be inserted (i.e. NULLVP) - // -> Have looped all the way back to where we started - uint64_t totalSteps = 0; - vnodeIndex = vnodeHashIndex; - while (vnode != s_entries[vnodeIndex].vnode && NULLVP != s_entries[vnodeIndex].vnode) - { - ++totalSteps; - ++vnodeIndex; - if (vnodeIndex == s_entriesCapacity) - { - vnodeIndex = 0; - } - - if (vnodeIndex == vnodeHashIndex) - { - // Looped through the entire cache and didn't find an empty slot or the vnode - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_TotalLookupCollisions, totalSteps); - return false; - } - } - - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_TotalLookupCollisions, totalSteps); - return true; -} - -KEXT_STATIC bool TryInsertOrUpdateEntry_ExclusiveLocked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - bool forceRefreshEntry, - VirtualizationRootHandle rootHandle) -{ - uintptr_t vnodeIndex; - if (TryFindVnodeIndex_Locked(vnode, vnodeHashIndex, /*out*/ vnodeIndex)) - { - if (forceRefreshEntry || - NULLVP == s_entries[vnodeIndex].vnode || - vnodeVid != s_entries[vnodeIndex].vid || - RootHandle_Indeterminate == s_entries[vnodeIndex].virtualizationRoot) - { - if (NULLVP == s_entries[vnodeIndex].vnode) - { - atomic_fetch_add_explicit(&s_cacheStats.cacheEntries, 1U, memory_order_relaxed); - } - - s_entries[vnodeIndex].vnode = vnode; - s_entries[vnodeIndex].vid = vnodeVid; - s_entries[vnodeIndex].virtualizationRoot = rootHandle; - } - else - { - if (rootHandle != s_entries[vnodeIndex].virtualizationRoot) - { - KextLog_FileError( - vnode, - "TryInsertOrUpdateEntry_ExclusiveLocked: vnode (%p:%u) has different root in cache(%hd) than was found walking tree(%hd)", - KextLog_Unslide(vnode), - vnodeVid, - s_entries[vnodeIndex].virtualizationRoot, - rootHandle); - } - } - - return true; - } - - return false; -} - -KEXT_STATIC_INLINE void InitCacheStats() -{ - atomic_store_explicit(&s_cacheStats.cacheEntries, 0U, memory_order_relaxed); - - for (int32_t i = 0; i < VnodeCacheHealthStat_Count; ++i) - { - atomic_store_explicit(&s_cacheStats.healthStats[i], 0ULL, memory_order_relaxed); - } -} - -KEXT_STATIC_INLINE void AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat healthStat, uint64_t value) -{ - uint64_t statValue = atomic_fetch_add_explicit(&s_cacheStats.healthStats[healthStat], value, memory_order_relaxed); - if (statValue > (UINT64_MAX - 1000)) - { - // The logging daemon is not fetching stats quickly enough (or not running at all) - // to avoid overflow set this stat back to zero - KextLog( - "AtomicFetchAddCacheHealthStat: health stat %d (%s) nearing UINT64_MAX(%llu), resetting to zero", - healthStat, - VnodeCacheHealthStatNames[healthStat], - statValue); - - atomic_store_explicit(&s_cacheStats.healthStats[healthStat], 0ULL, memory_order_relaxed); - } -} diff --git a/ProjFS.Mac/PrjFSKext/VnodeCache.hpp b/ProjFS.Mac/PrjFSKext/VnodeCache.hpp deleted file mode 100644 index 9c746683f4..0000000000 --- a/ProjFS.Mac/PrjFSKext/VnodeCache.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include "VirtualizationRoots.hpp" - -kern_return_t VnodeCache_Init(); - -kern_return_t VnodeCache_Cleanup(); - -IOReturn VnodeCache_ExportHealthData(IOExternalMethodArguments* _Nonnull arguments); - -VirtualizationRootHandle VnodeCache_FindRootForVnode( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheHitCounter, - PrjFSPerfCounter cacheMissCounter, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vnode_t _Nonnull vnode, - vfs_context_t _Nonnull context); - -VirtualizationRootHandle VnodeCache_RefreshRootForVnode( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheHitCounter, - PrjFSPerfCounter cacheMissCounter, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vnode_t _Nonnull vnode, - vfs_context_t _Nonnull context); - -VirtualizationRootHandle VnodeCache_InvalidateVnodeRootAndGetLatestRoot( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheHitCounter, - PrjFSPerfCounter cacheMissCounter, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vnode_t _Nonnull vnode, - vfs_context_t _Nonnull context); - -void VnodeCache_InvalidateCache(PerfTracer* _Nonnull perfTracer); diff --git a/ProjFS.Mac/PrjFSKext/VnodeCachePrivate.hpp b/ProjFS.Mac/PrjFSKext/VnodeCachePrivate.hpp deleted file mode 100644 index 335b817797..0000000000 --- a/ProjFS.Mac/PrjFSKext/VnodeCachePrivate.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "kernel-header-wrappers/stdatomic.h" -#include "public/ArrayUtils.hpp" - -enum UpdateCacheBehavior -{ - UpdateCacheBehavior_Invalid = 0, - - // If the current entry is up-to-date it will be used - UpdateCacheBehavior_TrustCurrentEntry, - - // The current entry will be replaced with the new root - UpdateCacheBehavior_ForceRefresh, - - // The current entry will have its root marked as invalid (forcing the next lookup to find the root) - UpdateCacheBehavior_InvalidateEntry, -}; - -struct VnodeCacheEntry -{ - vnode_t vnode; - uint32_t vid; // vnode generation number - VirtualizationRootHandle virtualizationRoot; -}; - -enum VnodeCacheHealthStat : int32_t -{ - VnodeCacheHealthStat_InvalidateEntireCacheCount, - VnodeCacheHealthStat_TotalCacheLookups, - VnodeCacheHealthStat_TotalLookupCollisions, - VnodeCacheHealthStat_TotalFindRootForVnodeHits, - VnodeCacheHealthStat_TotalFindRootForVnodeMisses, - VnodeCacheHealthStat_TotalRefreshRootForVnode, - VnodeCacheHealthStat_TotalInvalidateVnodeRoot, - - VnodeCacheHealthStat_Count -}; - -static constexpr const char* const VnodeCacheHealthStatNames[VnodeCacheHealthStat_Count] = -{ - [VnodeCacheHealthStat_InvalidateEntireCacheCount] = "InvalidateEntireCacheCount", - [VnodeCacheHealthStat_TotalCacheLookups] = "TotalCacheLookups", - [VnodeCacheHealthStat_TotalLookupCollisions] = "TotalLookupCollisions", - [VnodeCacheHealthStat_TotalFindRootForVnodeHits] = "TotalFindRootForVnodeHits", - [VnodeCacheHealthStat_TotalFindRootForVnodeMisses] = "TotalFindRootForVnodeMisses", - [VnodeCacheHealthStat_TotalRefreshRootForVnode] = "TotalRefreshRootForVnode", - [VnodeCacheHealthStat_TotalInvalidateVnodeRoot] = "TotalInvalidateVnodeRoot", -}; - -struct VnodeCacheStats -{ - _Atomic(uint32_t) cacheEntries; - _Atomic(uint64_t) healthStats[VnodeCacheHealthStat_Count]; -}; - -static_assert(AllArrayElementsInitialized(VnodeCacheHealthStatNames), "There must be an initialization of VnodeCacheHealthStatNames elements corresponding to each VnodeCacheHealthStat enum value"); - -// Allow cache the cache to use between 4 MB and 64 MB of memory (assuming 16 bytes per VnodeCacheEntry) -KEXT_STATIC const uint32_t MinPow2VnodeCacheCapacity = 0x040000; -KEXT_STATIC const uint32_t MaxPow2VnodeCacheCapacity = 0x400000; diff --git a/ProjFS.Mac/PrjFSKext/VnodeCacheTestable.hpp b/ProjFS.Mac/PrjFSKext/VnodeCacheTestable.hpp deleted file mode 100644 index d0c70b6591..0000000000 --- a/ProjFS.Mac/PrjFSKext/VnodeCacheTestable.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "public/PrjFSCommon.h" -#include "public/FsidInode.h" -#include "public/PrjFSPerfCounter.h" -#include - -#ifndef __cplusplus -#error None of the kext code is set up for being called from C or Objective-C; change the including file to C++ or Objective-C++ -#endif - -#ifndef KEXT_UNIT_TESTING -#error This class should only be called for unit tests -#endif - -// Forward declarations for unit testing -class PerfTracer; -struct VnodeCacheEntry; - -KEXT_STATIC_INLINE void InvalidateCache_ExclusiveLocked(); -KEXT_STATIC_INLINE uintptr_t ComputeVnodeHashIndex(vnode_t _Nonnull vnode); -KEXT_STATIC_INLINE uint32_t ComputePow2CacheCapacity(int expectedVnodeCount); - -KEXT_STATIC bool TryGetVnodeRootFromCache( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - /* out parameters */ - VirtualizationRootHandle& rootHandle); - -KEXT_STATIC void FindVnodeRootFromDiskAndUpdateCache( - PerfTracer* _Nonnull perfTracer, - PrjFSPerfCounter cacheMissFallbackFunctionCounter, - PrjFSPerfCounter cacheMissFallbackFunctionInnerLoopCounter, - vfs_context_t _Nonnull context, - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - UpdateCacheBehavior updateEntryBehavior, - /* out parameters */ - VirtualizationRootHandle& rootHandle); - -KEXT_STATIC void InsertEntryToInvalidatedCache_ExclusiveLocked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHashIndex, - uint32_t vnodeVid, - VirtualizationRootHandle rootHandle); - -KEXT_STATIC bool TryFindVnodeIndex_Locked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHash, - /* out parameters */ - uintptr_t& vnodeIndex); - -KEXT_STATIC bool TryInsertOrUpdateEntry_ExclusiveLocked( - vnode_t _Nonnull vnode, - uintptr_t vnodeHash, - uint32_t vnodeVid, - bool forceRefreshEntry, - VirtualizationRootHandle rootHandle); - -KEXT_STATIC_INLINE void InitCacheStats(); -KEXT_STATIC_INLINE void AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat healthStat, uint64_t value); - -// Static variables used for maintaining Vnode cache state -extern uint32_t s_entriesCapacity; -extern uintptr_t s_ModBitmask; -extern VnodeCacheEntry* _Nullable s_entries; -extern VnodeCacheStats s_cacheStats; - diff --git a/ProjFS.Mac/PrjFSKext/VnodeUtilities.cpp b/ProjFS.Mac/PrjFSKext/VnodeUtilities.cpp deleted file mode 100644 index 7b118899b0..0000000000 --- a/ProjFS.Mac/PrjFSKext/VnodeUtilities.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "VnodeUtilities.hpp" -#include "KextLog.hpp" -#include "kernel-header-wrappers/vnode.h" -#include "kernel-header-wrappers/mount.h" -#include - -extern "C" int mac_vnop_getxattr(struct vnode *, const char *, char *, size_t, size_t *); - -#ifndef KEXT_UNIT_TESTING // Kext testing environment has mock versions of these functions -FsidInode Vnode_GetFsidAndInode(vnode_t vnode, vfs_context_t _Nonnull context, bool useLinkIDForInode) -{ - vnode_attr attrs; - VATTR_INIT(&attrs); - // va_linkid is unique per hard link on HFS+, va_fileid identifies the link target. Always equal on APFS, no way to distinguish between links. - VATTR_WANTED(&attrs, va_linkid); - - int errno = vnode_getattr(vnode, &attrs, context); - if (0 != errno) - { - KextLog_FileError(vnode, "Vnode_GetFsidAndInode: vnode_getattr failed with errno %d", errno); - } - - vfsstatfs* statfs = vfs_statfs(vnode_mount(vnode)); - return { statfs->f_fsid, attrs.va_linkid }; -} - -SizeOrError Vnode_ReadXattr(vnode_t vnode, const char* xattrName, void* buffer, size_t bufferSize) -{ - size_t actualSize = bufferSize; - errno_t error = mac_vnop_getxattr(vnode, xattrName, static_cast(buffer), bufferSize, &actualSize); - return SizeOrError { actualSize, error }; -} -#endif - -const char* Vnode_GetTypeAsString(vnode_t _Nullable vnode) -{ - if (vnode == NULLVP) - { - return "[NULL]"; - } - switch (vnode_vtype(vnode)) - { - case VNON: return "VNON"; - case VREG: return "VREG"; - case VDIR: return "VDIR"; - case VBLK: return "VBLK"; - case VCHR: return "VCHR"; - case VLNK: return "VLNK"; - case VSOCK: return "VSOCK"; - case VFIFO: return "VFIFO"; - case VBAD: return "VBAD"; - case VSTR: return "VSTR"; - case VCPLX: return "VCPLX"; - } - return "[???]"; -} diff --git a/ProjFS.Mac/PrjFSKext/VnodeUtilities.hpp b/ProjFS.Mac/PrjFSKext/VnodeUtilities.hpp deleted file mode 100644 index b1c153b95f..0000000000 --- a/ProjFS.Mac/PrjFSKext/VnodeUtilities.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include -#include "public/FsidInode.h" - -struct SizeOrError -{ - size_t size; - errno_t error; -}; - -SizeOrError Vnode_ReadXattr(vnode_t _Nonnull vnode, const char* _Nonnull xattrName, void* _Nullable buffer, size_t bufferSize); -FsidInode Vnode_GetFsidAndInode(vnode_t _Nonnull vnode, vfs_context_t _Nonnull context, bool useLinkIDForInode); -const char* _Nonnull Vnode_GetTypeAsString(vnode_t _Nullable vnode); diff --git a/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/kauth.h b/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/kauth.h deleted file mode 100644 index a94b4876f3..0000000000 --- a/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/kauth.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#if defined(KEXT_UNIT_TESTING) && !defined(TESTABLE_KEXT_TARGET) // Building unit tests - -#include -typedef struct __lck_grp__ lck_grp_t; -#include - -#else - -#include - -#endif diff --git a/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/mount.h b/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/mount.h deleted file mode 100644 index 257d4951a6..0000000000 --- a/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/mount.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#if defined(KEXT_UNIT_TESTING) && !defined(TESTABLE_KEXT_TARGET) // Building unit tests - -typedef uint32_t user32_addr_t; -typedef uint32_t user32_size_t; -#include - -#else // Building the kext or the testing version of it - -// triggers various header doc related warnings -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdocumentation" -#include -#pragma clang diagnostic pop - -#endif diff --git a/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/stdatomic.h b/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/stdatomic.h deleted file mode 100644 index f96008d22d..0000000000 --- a/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/stdatomic.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#ifdef KEXT_UNIT_TESTING -#include -#define _Atomic(X) std::atomic< X > -using std::atomic_uint_least32_t; -using std::atomic_uint_least64_t; -using std::atomic_int; -using std::memory_order_seq_cst; -using std::memory_order_relaxed; -using std::atomic_store_explicit; -using std::atomic_exchange_explicit; -using std::atomic_fetch_add_explicit; -#else -#include -#endif diff --git a/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/vnode.h b/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/vnode.h deleted file mode 100644 index 9a12aa7daa..0000000000 --- a/ProjFS.Mac/PrjFSKext/kernel-header-wrappers/vnode.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#if defined(KEXT_UNIT_TESTING) && !defined(TESTABLE_KEXT_TARGET) // Building unit tests - -#include // vnode.h below references struct timespec -// Definitions to make the kernel vnode.h compile in user space -typedef struct ipc_port* ipc_port_t; -enum uio_rw {}; -enum uio_seg {}; - -#include - -#else // Building the kext or the testing version of it - -// triggers various header doc related warnings -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdocumentation" -#include -#pragma clang diagnostic pop - -#endif diff --git a/ProjFS.Mac/PrjFSKext/public/ArrayUtils.hpp b/ProjFS.Mac/PrjFSKext/public/ArrayUtils.hpp deleted file mode 100644 index 7cac7d3ebc..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/ArrayUtils.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -template - constexpr bool AllArrayElementsInitialized(T (&array)[N], size_t fromIndex = 0) -{ - return - fromIndex >= N - ? true - : (array[fromIndex] != T() - && AllArrayElementsInitialized(array, fromIndex + 1)); -} diff --git a/ProjFS.Mac/PrjFSKext/public/FsidInode.h b/ProjFS.Mac/PrjFSKext/public/FsidInode.h deleted file mode 100644 index e0e4a8cdd3..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/FsidInode.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef FsidInode_h -#define FsidInode_h - -#include -#include - -struct FsidInode -{ - fsid_t fsid; - uint64_t inode; -}; - -#endif /* FsidInode_h */ diff --git a/ProjFS.Mac/PrjFSKext/public/Message.h b/ProjFS.Mac/PrjFSKext/public/Message.h deleted file mode 100644 index 2af36931ef..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/Message.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef Message_h -#define Message_h - -#include -#include "PrjFSCommon.h" -#include "FsidInode.h" - -typedef enum -{ - MessageType_Invalid = 0, - - // Messages from kernel to user mode - MessageType_KtoU_EnumerateDirectory, - MessageType_KtoU_RecursivelyEnumerateDirectory, - MessageType_KtoU_HydrateFile, - - MessageType_KtoU_NotifyFileModified, - MessageType_KtoU_NotifyFilePreDelete, - MessageType_KtoU_NotifyFilePreDeleteFromRename, - MessageType_KtoU_NotifyDirectoryPreDelete, - MessageType_KtoU_NotifyFileCreated, - MessageType_KtoU_NotifyFileRenamed, - MessageType_KtoU_NotifyDirectoryRenamed, - MessageType_KtoU_NotifyFileHardLinkCreated, - MessageType_KtoU_NotifyFilePreConvertToFull, - - // Responses - MessageType_Response_Success, - MessageType_Response_Fail, - - // Other message outcomes - MessageType_Result_Aborted, - -} MessageType; - -enum MessagePathField -{ - MessagePath_Target = 0, - MessagePath_From, - - MessagePath_Count, -}; - -struct MessageHeader -{ - // The message id is used to correlate a response to its request - uint64_t messageId; - - // The message type indicates the type of request or response - uint32_t messageType; // values of type MessageType - - // fsid and inode of the file - FsidInode fsidInode; - - // For messages from kernel to user mode, indicates the PID of the process that initiated the I/O - int32_t pid; - char procname[MAXCOMLEN + 1]; - - // Sizes of the flexible-length, nul-terminated paths following the message body. - // Sizes include the nul characters but can be 0 to indicate total absence. - uint16_t pathSizesBytes[MessagePath_Count]; -}; - -// Description of a decomposed, in-memory message header plus variable length string field -struct Message -{ - const MessageHeader* messageHeader; - const char* paths[MessagePath_Count]; -}; - - -uint32_t Message_EncodedSize(const MessageHeader* messageHeader); - -#endif /* Message_h */ diff --git a/ProjFS.Mac/PrjFSKext/public/PrjFSCommon.h b/ProjFS.Mac/PrjFSKext/public/PrjFSCommon.h deleted file mode 100644 index 6357e90246..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/PrjFSCommon.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef PrjFSCommon_h -#define PrjFSCommon_h - -#include "PrjFSVersion.h" -#include -#include - -#define PrjFSMaxPath 1024 -#define PrjFSKextBundleId "org.vfsforgit.PrjFSKext" - -#define PrjFSServiceClass "org_vfsforgit_PrjFS" - -#define PrjFSKextVersion PrjFSVersionString -// Name of property on the main PrjFS IOService indicating the kext version, to be checked by user space -#define PrjFSKextVersionKey "org.vfsforgit.PrjFSKext.Version" - -#define PrjFSProviderPathKey "org.vfsforgit.PrjFSKext.ProviderUserClient.Path" - -typedef enum -{ - FileFlags_Invalid = 0, - - FileFlags_IsInVirtualizationRoot = 0x00000008, // UF_OPAQUE - FileFlags_IsEmpty = 0x00000010, // UF_NOUNLINK - -} FileFlags; - -// User client types to be passed to IOServiceOpen. -enum PrjFSServiceUserClientType -{ - UserClientType_Invalid = 0, - - UserClientType_Provider, - UserClientType_Log, - UserClientType_OfflineIO, -}; - -// When building the kext in user space for unit testing, we want some functions -// to have external linkage in order to make them testable -#ifdef KEXT_UNIT_TESTING -#define KEXT_STATIC -#define KEXT_STATIC_INLINE -#else -#define KEXT_STATIC static -#define KEXT_STATIC_INLINE static inline -#endif - -namespace PrjFSDarwinMajorVersion -{ - enum - { - MacOS10_13_HighSierra = 17, - MacOS10_14_Mojave = 18, - MacOS10_15_Catalina = 19, - }; -} - -#endif /* PrjFSCommon_h */ diff --git a/ProjFS.Mac/PrjFSKext/public/PrjFSLogClientShared.h b/ProjFS.Mac/PrjFSKext/public/PrjFSLogClientShared.h deleted file mode 100644 index fd5e675fe8..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/PrjFSLogClientShared.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -// External method selectors for log user clients -enum PrjFSLogUserClientSelector -{ - LogSelector_Invalid = 0, - - LogSelector_FetchProfilingData, - LogSelector_FetchVnodeCacheHealth, -}; - -enum PrjFSLogUserClientMemoryType -{ - LogMemoryType_Invalid = 0, - - LogMemoryType_MessageQueue, -}; - -enum PrjFSLogUserClientPortType -{ - LogPortType_Invalid = 0, - - LogPortType_MessageQueue, -}; - -// Log message levels, these correspond to their os_log counterparts -enum KextLog_Level : uint32_t -{ - KEXTLOG_ERROR = 0, // For important failures, always gets logged and causes INFO messages to be committed too. - KEXTLOG_DEFAULT = 2, // Run-of-the-mill messages that shouldn't be too frequent as they always are logged to disk. - KEXTLOG_INFO = 1, // Verbose logging that might help with diagnosing problems; these don't usually get logged to disk, except when an ERROR is loggod. -}; - -struct KextLog_MessageHeader -{ - uint32_t flags; - KextLog_Level level; - uint64_t machAbsoluteTimestamp; - char logString[0]; -}; - -enum KextLog_MessageFlag -{ - LogMessageFlag_LogMessagesDropped = 0x1, - LogMessageFlag_LogMessageTruncated = 0x2, -}; - diff --git a/ProjFS.Mac/PrjFSKext/public/PrjFSPerfCounter.h b/ProjFS.Mac/PrjFSKext/public/PrjFSPerfCounter.h deleted file mode 100644 index ded0712590..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/PrjFSPerfCounter.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef PrjFSPerfCounter_h -#define PrjFSPerfCounter_h - -enum PrjFSPerfCounter : int32_t -{ - // Note: ensure that any changes to this list are reflected in the PerfCounterNames array of strings - - PrjFSPerfCounter_VnodeOp, - PrjFSPerfCounter_VnodeOp_GetPath, - PrjFSPerfCounter_VnodeOp_BasicVnodeChecks, - PrjFSPerfCounter_VnodeOp_ShouldHandle, - PrjFSPerfCounter_VnodeOp_ShouldHandle_IsVnodeAccessCheck, - PrjFSPerfCounter_VnodeOp_ShouldHandle_IgnoredVnodeAccessCheck, - PrjFSPerfCounter_VnodeOp_ShouldHandle_ReadFileFlags, - PrjFSPerfCounter_VnodeOp_ShouldHandle_NotInAnyRoot, - PrjFSPerfCounter_VnodeOp_ShouldHandle_CheckFileSystemCrawler, - PrjFSPerfCounter_VnodeOp_ShouldHandle_DeniedFileSystemCrawler, - PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_TemporaryDirectory, - PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_NoRootFound, - PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_ProviderOffline, - PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_OriginatedByProvider, - PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_UserRestriction, - PrjFSPerfCounter_VnodeOp_PreDelete, - PrjFSPerfCounter_VnodeOp_EnumerateDirectory, - PrjFSPerfCounter_VnodeOp_RecursivelyEnumerateDirectory, - PrjFSPerfCounter_VnodeOp_HydrateFile, - PrjFSPerfCounter_VnodeOp_PreConvertToFull, - - PrjFSPerfCounter_FileOp, - PrjFSPerfCounter_FileOp_ShouldHandle, - PrjFSPerfCounter_FileOp_ShouldHandle_FindVirtualizationRoot, - PrjFSPerfCounter_FileOp_Vnode_Cache_Hit, - PrjFSPerfCounter_FileOp_Vnode_Cache_Miss, - PrjFSPerfCounter_FileOp_FindRoot, - PrjFSPerfCounter_FileOp_FindRoot_Iteration, - PrjFSPerfCounter_FileOp_ShouldHandle_NoRootFound, - PrjFSPerfCounter_FileOp_ShouldHandle_FindProviderPathBased, - PrjFSPerfCounter_FileOp_ShouldHandle_NoProviderFound, - PrjFSPerfCounter_FileOp_ShouldHandle_CheckProvider, - PrjFSPerfCounter_FileOp_ShouldHandle_OfflineRoot, - PrjFSPerfCounter_FileOp_ShouldHandle_OriginatedByProvider, - PrjFSPerfCounter_FileOp_Renamed, - PrjFSPerfCounter_FileOp_HardLinkCreated, - PrjFSPerfCounter_FileOp_FileModified, - PrjFSPerfCounter_FileOp_FileCreated, - - PrjFSPerfCounter_CacheCapacity, - PrjFSPerfCounter_CacheInvalidateCount, - PrjFSPerfCounter_CacheFullCount, - PrjFSPerfCounter_Count, -}; - -constexpr unsigned int PrjFSPerfCounterBuckets = 64; - -struct PrjFSPerfCounterResult -{ - _Atomic uint64_t numSamples; - - // Units: Mach absolute time - _Atomic uint64_t sum; - _Atomic uint64_t min; - _Atomic uint64_t max; - - // log-scale histogram buckets - _Atomic uint64_t sampleBuckets[PrjFSPerfCounterBuckets]; -}; - -#endif /* PrjFSPerfCounter_h */ diff --git a/ProjFS.Mac/PrjFSKext/public/PrjFSProviderClientShared.h b/ProjFS.Mac/PrjFSKext/public/PrjFSProviderClientShared.h deleted file mode 100644 index 824ec14d36..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/PrjFSProviderClientShared.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -// External method selectors for provider user clients -enum PrjFSProviderUserClientSelector -{ - ProviderSelector_Invalid = 0, - - ProviderSelector_RegisterVirtualizationRootPath, - ProviderSelector_KernelMessageResponse, -}; - -enum PrjFSProviderUserClientMemoryType -{ - ProviderMemoryType_Invalid = 0, - - ProviderMemoryType_MessageQueue, -}; - -enum PrjFSProviderUserClientPortType -{ - ProviderPortType_Invalid = 0, - - ProviderPortType_MessageQueue, -}; diff --git a/ProjFS.Mac/PrjFSKext/public/PrjFSVnodeCacheHealth.h b/ProjFS.Mac/PrjFSKext/public/PrjFSVnodeCacheHealth.h deleted file mode 100644 index 7c2fc64a36..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/PrjFSVnodeCacheHealth.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -struct PrjFSVnodeCacheHealth -{ - // Total capacity of the vnode cache - uint32_t cacheCapacity; - - // Number of entries in the cache (i.e. the number of slots in use) - uint32_t cacheEntries; - - // Number of times that the entire cache has been invalidated - uint64_t invalidateEntireCacheCount; - - // Number of (internal) lookups in the cache array. This value tracks how many times - // the VnodeCache functions had to perform a lookup in the cache - uint64_t totalCacheLookups; - - // Number of collisions that occurred when looking up entries in the cache. Each step - // of the linear probe counts as a collision. - uint64_t totalLookupCollisions; - - // Number of times that VnodeCache_FindRootForVnode found an up-to-date vnode in the cache - uint64_t totalFindRootForVnodeHits; - - // Number of times that VnodeCache_FindRootForVnode did not find the vnode in the cache (or the - // vnode in the cache was not up-to-date). - uint64_t totalFindRootForVnodeMisses; - - // Number of times VnodeCache_RefreshRootForVnode was called - uint64_t totalRefreshRootForVnode; - - // Number of times VnodeCache_InvalidateVnodeRootAndGetLatestRoot was called - uint64_t totalInvalidateVnodeRoot; -}; diff --git a/ProjFS.Mac/PrjFSKext/public/PrjFSXattrs.h b/ProjFS.Mac/PrjFSKext/public/PrjFSXattrs.h deleted file mode 100644 index b0cfa061aa..0000000000 --- a/ProjFS.Mac/PrjFSKext/public/PrjFSXattrs.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - - -// Xattrs used by both kernel and user component - -// TODO: Issue #584, update xattr names -#define PrjFSVirtualizationRootXAttrName "org.vfsforgit.xattr.virtualizationroot" -#define PrjFSFileXAttrName "org.vfsforgit.xattr.file" - -#define PrjFS_PlaceholderIdLength 128 - -static const int32_t PlaceholderMagicNumber = 0x12345678; -static const int32_t PlaceholderFormatVersion = 1; - -struct PrjFSXattrHeader -{ - int32_t magicNumber; - int32_t formatVersion; -}; - -struct PrjFSVirtualizationRootXAttrData -{ - PrjFSXattrHeader header; -}; - -struct PrjFSFileXAttrData -{ - PrjFSXattrHeader header; - - unsigned char providerId[PrjFS_PlaceholderIdLength]; - unsigned char contentId[PrjFS_PlaceholderIdLength]; -}; diff --git a/ProjFS.Mac/PrjFSKextLogDaemon/Info.plist b/ProjFS.Mac/PrjFSKextLogDaemon/Info.plist deleted file mode 100644 index 8e47e55e25..0000000000 --- a/ProjFS.Mac/PrjFSKextLogDaemon/Info.plist +++ /dev/null @@ -1,16 +0,0 @@ - - - - - CFBundleIdentifier - org.vfsforgit.prjfs.PrjFSKextLogDaemon - CFBundleInfoDictionaryVersion - 6.0 - CFBundleVersion - $(PRJFS_BUNDLE_VERSION) - CFBundleName - PrjFSKextLogDaemon - CFBundleShortVersionString - $(PRJFS_BUNDLE_SHORT_VERSION_STRING) - - diff --git a/ProjFS.Mac/PrjFSKextLogDaemon/PrjFSKextLogDaemon.cpp b/ProjFS.Mac/PrjFSKextLogDaemon/PrjFSKextLogDaemon.cpp deleted file mode 100644 index c8143dfd41..0000000000 --- a/ProjFS.Mac/PrjFSKextLogDaemon/PrjFSKextLogDaemon.cpp +++ /dev/null @@ -1,611 +0,0 @@ -#include "../PrjFSKext/public/PrjFSLogClientShared.h" -#include "../PrjFSKext/public/PrjFSVnodeCacheHealth.h" -#include "../PrjFSLib/Json/JsonWriter.hpp" -#include "../PrjFSLib/PrjFSUser.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using std::atomic_exchange; -using std::atomic_uint32_t; -using std::hex; -using std::lock_guard; -using std::mutex; -using std::ostringstream; -using std::string; - -static const char PrjFSKextLogDaemon_OSLogSubsystem[] = "org.vfsforgit.prjfs.PrjFSKextLogDaemon"; -static const char PanicLogDirectory[] = "/Library/Logs/DiagnosticReports"; -static const char PanicLogTimestampDirectory[] = "/usr/local/vfsforgit/diagnostics"; -static const char PanicLogTimestampFile[] = "/usr/local/vfsforgit/diagnostics/LastRun.txt"; -static const int INVALID_SOCKET_FD = -1; - -static os_log_t s_daemonLogger, s_kextLogger; -static IONotificationPortRef s_notificationPort; - -static int s_messageListenerSocket = INVALID_SOCKET_FD; -static const char MessageListenerSocketPath[] = "/usr/local/GitService/pipe/vfs-c780ac06-135a-4e9e-ab6c-d41e2d265baa"; -static const string MessageKey = "message"; -enum class MessageType -{ - Info, - Error, - VnodeCacheHealth -}; - -static mutex s_messageListenerMutex; - -static atomic_uint32_t s_droppedMessageCount(0); - -static void LogPanics(); -static long GetFileModifiedTime(const char* path); -static bool DoesPathExist(const char* path); -static void StartLoggingKextMessages(io_connect_t connection, io_service_t service); -static void HandleSigterm(int sig, siginfo_t* info, void* uc); -static void SetupExitSignalHandler(); - -static dispatch_source_t StartPeriodicLoggingTimer(io_connect_t connection); -static void FetchAndLogKextHealthData(io_connect_t connection); - -static void ReportDroppedKextMessages(); - -// LogXXX functions will log to the OS as well as the message listener -static void LogDaemonError(const string& message); -static void LogKextMessage(os_log_type_t messageLogType, uint32_t messageFlags, const char* message, int messageLength); -static void LogKextHealthData(const PrjFSVnodeCacheHealth& healthData); - -static void CreatePipeToMessageListener(); -static void ClosePipeToMessageListener_Locked(); -static string MessageTypeToString(MessageType messageType); -static void WriteJsonToMessageListener(MessageType messageType, const JsonWriter& jsonMessage); - -int main(int argc, const char* argv[]) -{ -#ifdef DEBUG - printf("PrjFSKextLogDaemon starting up"); - fflush(stdout); -#endif - - s_daemonLogger = os_log_create(PrjFSKextLogDaemon_OSLogSubsystem, "daemon"); - s_kextLogger = os_log_create(PrjFSKextLogDaemon_OSLogSubsystem, "kext"); - - os_log(s_daemonLogger, "PrjFSKextLogDaemon starting up"); - - CreatePipeToMessageListener(); - JsonWriter messageWriter; - messageWriter.Add(MessageKey, "PrjFSKextLogDaemon starting up"); - WriteJsonToMessageListener(MessageType::Info, messageWriter); - - s_notificationPort = IONotificationPortCreate(kIOMasterPortDefault); - IONotificationPortSetDispatchQueue(s_notificationPort, dispatch_get_main_queue()); - - PrjFSService_WatchContext* watchContext = PrjFSService_WatchForServiceAndConnect( - s_notificationPort, - UserClientType_Log, - [](io_service_t service, io_connect_t connection, bool serviceVersionMismatch, IOReturn connectResult, PrjFSService_WatchContext* context) - { - if (connectResult != kIOReturnSuccess || connection == IO_OBJECT_NULL) - { - io_string_t servicePath = ""; - IORegistryEntryGetPath(service, kIOServicePlane, servicePath); - if (serviceVersionMismatch) - { - CFTypeRef kextVersionObj = IORegistryEntryCreateCFProperty(service, CFSTR(PrjFSKextVersionKey), kCFAllocatorDefault, 0); - os_log_error( - s_daemonLogger, - "Failed to connect to newly matched PrjFS kernel service at '%{public}s'; version mismatch. Expected %{public}s, kernel service version %{public}@", - servicePath, PrjFSKextVersion, kextVersionObj); - - JsonWriter messageWriter; - messageWriter.Add(MessageKey, "Failed to connect to newly matched PrjFS kernel service at '" + string(servicePath) + "'; version mismatch."); - WriteJsonToMessageListener(MessageType::Error, messageWriter); - - if (kextVersionObj != nullptr) - { - CFRelease(kextVersionObj); - } - } - else - { - ostringstream errorMessage; - errorMessage << "Failed to connect to newly matched PrjFS kernel service at '" << servicePath << "'; connecting failed with error 0x" << hex << connectResult; - LogDaemonError(errorMessage.str()); - } - } - else - { - StartLoggingKextMessages(connection, service); - } - }); - if (nullptr == watchContext) - { - LogDaemonError("PrjFSKextLogDaemon failed to register for service notifications."); - return 1; - } - - SetupExitSignalHandler(); - - os_log(s_daemonLogger, "PrjFSKextLogDaemon running"); - - LogPanics(); - CFRunLoopRun(); - - os_log(s_daemonLogger, "PrjFSKextLogDaemon shutting down"); - - PrjFSService_StopWatching(watchContext); - - { - lock_guard lock(s_messageListenerMutex); - ClosePipeToMessageListener_Locked(); - } - - return 0; -} - -static os_log_type_t KextLogLevelAsOSLogType(KextLog_Level level) -{ - switch (level) - { - case KEXTLOG_INFO: - return OS_LOG_TYPE_INFO; - case KEXTLOG_DEFAULT: - return OS_LOG_TYPE_DEFAULT; - case KEXTLOG_ERROR: - default: - return OS_LOG_TYPE_ERROR; - } -} - -static void LogPanics() -{ - DIR* dr = opendir(PanicLogDirectory); - if (dr == nullptr) - { - ostringstream errorMessage; - errorMessage << "Unable to open " << PanicLogDirectory << ", errno=" << errno << ", errorstr=" << strerror(errno); - LogDaemonError(errorMessage.str()); - return; - } - - // Look up the last run time - long lastRunTimestamp = GetFileModifiedTime(PanicLogTimestampFile); - - struct dirent* dirEntry; - while ((dirEntry = readdir(dr)) != nullptr) - { - // Only check files - if(dirEntry->d_type == DT_REG) - { - // Check for .panic extension - const char* ext = strrchr(dirEntry->d_name,'.'); - if(ext != nullptr && strcmp(ext, ".panic") == 0) - { - // Get the full name of the panic file - string fullFileName = PanicLogDirectory; - fullFileName += "/"; - fullFileName += dirEntry->d_name; - - // Verify it's after the last timestamp - long panicLogTime = GetFileModifiedTime(fullFileName.c_str()); - if (panicLogTime == 0) - { - // Record error if we can't determine timestamp - os_log(s_daemonLogger, "Unable to run stat on panic at %{public}s errno=%d strerror=%{public}s\n", fullFileName.c_str(), errno, strerror(errno)); - continue; - } - - if (panicLogTime >= lastRunTimestamp) - { - // Check if 'org.vfsforgit.PrjFSKext(' occurs in the log - // If it does, then PrjFSKext may have caused the panic and we should log an error - FILE* fp = fopen(fullFileName.c_str(), "r"); - if (fp != nullptr) - { - char* line = nullptr; - size_t len = 0; - ssize_t read; - while ((read = getline(&line, &len, fp)) != -1) - { - if (strstr(line, "org.vfsforgit.PrjFSKext(") != nullptr) - { - ostringstream errorMessage; - errorMessage << "Found panic at " << fullFileName.c_str() << "\n"; - LogDaemonError(errorMessage.str()); - break; - } - } - - fclose(fp); - free(line); - } - else - { - os_log(s_daemonLogger, "Unable open %{public}s errno=%d strerror=%{public}s\n", fullFileName.c_str(), errno, strerror(errno)); - continue; - } - } - } - } - } - closedir(dr); - - // Update timestamp file - if (!DoesPathExist(PanicLogTimestampDirectory)) - { - mkdir(PanicLogTimestampDirectory, 0755); - } - - FILE* fp = fopen(PanicLogTimestampFile, "a"); - if (fp != nullptr) - { - fprintf(fp, "Completed Panic Sweep for :%ld\n", lastRunTimestamp); - fclose(fp); - } - else - { - os_log(s_daemonLogger, "Unable to save panic timestamp file %{public}s errno=%d strerror=%{public}s\n", PanicLogTimestampFile, errno, strerror(errno)); - } -} - -static long GetFileModifiedTime(const char* path) -{ - struct stat attr; - if (stat(path, &attr) == -1) - { - return 0; - } - return attr.st_mtimespec.tv_sec; -} - -static bool DoesPathExist(const char* path) -{ - struct stat attr; - if (stat(path, &attr) == -1) - { - return false; - } - return true; -} - -static void StartLoggingKextMessages(io_connect_t connection, io_service_t prjfsService) -{ - uint64_t prjfsServiceEntryID = 0; - IORegistryEntryGetRegistryEntryID(prjfsService, &prjfsServiceEntryID); - - std::shared_ptr logDataQueue(new DataQueueResources {}); - if (!PrjFSService_DataQueueInit(logDataQueue.get(), connection, LogPortType_MessageQueue, LogMemoryType_MessageQueue, dispatch_get_main_queue())) - { - ostringstream errorMessage; - errorMessage << "StartLoggingKextMessages: Failed to set up log message data queue an connection to service with registry entry 0x" << hex << prjfsServiceEntryID; - LogDaemonError(errorMessage.str()); - IOServiceClose(connection); - return; - } - - os_log(s_daemonLogger, "Started logging kext messages from PrjFS IOService with registry entry id 0x%llx", prjfsServiceEntryID); - - dispatch_source_set_event_handler(logDataQueue->dispatchSource, ^{ - DataQueue_ClearMachNotification(logDataQueue->notificationPort); - - while (IODataQueueEntry* entry = DataQueue_Peek(logDataQueue->queueMemory)) - { - int messageSize = entry->size; - if (messageSize >= sizeof(KextLog_MessageHeader) + 2) - { - struct KextLog_MessageHeader message = {}; - memcpy(&message, entry->data, sizeof(KextLog_MessageHeader)); - int logStringLength = messageSize - sizeof(KextLog_MessageHeader) - 1; - os_log_type_t messageLogType = KextLogLevelAsOSLogType(message.level); - LogKextMessage( - messageLogType, - message.flags, - reinterpret_cast(entry->data + sizeof(KextLog_MessageHeader)), - logStringLength); - } - else - { - ostringstream errorMessage; - errorMessage << "StartLoggingKextMessages: Malformed message received from kext. messageSize = " << messageSize - << ", expecting " << sizeof(KextLog_MessageHeader) + 2 << "or more"; - LogDaemonError(errorMessage.str()); - } - - DataQueue_Dequeue(logDataQueue->queueMemory, nullptr, nullptr); - } - }); - dispatch_resume(logDataQueue->dispatchSource); - - dispatch_source_t timer = StartPeriodicLoggingTimer(connection); - - PrjFSService_WatchForServiceTermination( - prjfsService, - s_notificationPort, - [prjfsServiceEntryID, connection, logDataQueue, timer]() - { - DataQueue_Dispose(logDataQueue.get(), connection, LogMemoryType_MessageQueue); - - if (nullptr != timer) - { - dispatch_cancel(timer); - dispatch_release(timer); - } - - IOServiceClose(connection); - os_log(s_daemonLogger, "Stopped logging kext messages from PrjFS IOService with registry entry id 0x%llx", prjfsServiceEntryID); - }); -} - -static void HandleSigterm(int sig, siginfo_t* info, void* uc) -{ - // Does nothing, unlike the default implementation which immediately aborts exits the process -} - -// Sets up handling of SIGTERM so that shutting down the daemon can be logged (at the end of main()) -// This is for clarity, so that we know if absence of logs is because there was nothing logged, or because the log daemon shut down. -static void SetupExitSignalHandler() -{ - dispatch_source_t signalSource = - dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0 /* no mask values for signal dispatch sources */, dispatch_get_main_queue()); - dispatch_source_set_event_handler(signalSource, ^{ - CFRunLoopStop(CFRunLoopGetMain()); - }); - dispatch_resume(signalSource); - - struct sigaction newAction = { .sa_sigaction = HandleSigterm }; - struct sigaction oldAction = {}; - sigaction(SIGTERM, &newAction, &oldAction); -} - -static dispatch_source_t StartPeriodicLoggingTimer(io_connect_t connection) -{ - dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - dispatch_source_set_timer( - timer, - DISPATCH_TIME_NOW, // start - 30 * 60 * NSEC_PER_SEC, // interval - 10 * NSEC_PER_SEC); // leeway - dispatch_source_set_event_handler(timer, ^{ - // Every time the timer fires attempt to connect (if not already connected) - CreatePipeToMessageListener(); - FetchAndLogKextHealthData(connection); - ReportDroppedKextMessages(); - }); - dispatch_resume(timer); - return timer; -} - -static void FetchAndLogKextHealthData(io_connect_t connection) -{ - PrjFSVnodeCacheHealth healthData; - size_t out_size = sizeof(healthData); - IOReturn ret = IOConnectCallStructMethod(connection, LogSelector_FetchVnodeCacheHealth, nullptr, 0, &healthData, &out_size); - if (ret == kIOReturnUnsupported) - { - LogDaemonError("FetchAndLogKextHealthData: IOConnectCallStructMethod failed for LogSelector_FetchVnodeCacheHealth, ret: kIOReturnUnsupported"); - } - else if (ret == kIOReturnSuccess) - { - LogKextHealthData(healthData); - } - else - { - ostringstream errorMessage; - errorMessage << "FetchAndLogKextHealthData: Fetching profiling data from kernel failed, ret: 0x" << hex << ret; - LogDaemonError(errorMessage.str()); - } -} - -static void ReportDroppedKextMessages() -{ - // We should only report the number of drops reported during the last time interval (i.e. since the last - // time ReportDroppedKextMessages was called). Capture the current value of s_droppedMessageCount and reset it to 0. - uint32_t droppedMessageCount = atomic_exchange(&s_droppedMessageCount, 0U); - - if (droppedMessageCount > 0) - { - ostringstream message; - message << "ReportAnyDroppedMessages: " << droppedMessageCount << " reports of dropped kext log messages since last checked"; - LogDaemonError(message.str()); - } -} - -static void LogDaemonError(const string& message) -{ - os_log_error(s_daemonLogger, "%{public}s", message.c_str()); - - JsonWriter messageWriter; - messageWriter.Add(MessageKey, message); - WriteJsonToMessageListener(MessageType::Error, messageWriter); -} - -static void LogKextMessage(os_log_type_t messageLogType, uint32_t messageFlags, const char* message, int messageLength) -{ - if (messageFlags & LogMessageFlag_LogMessagesDropped) - { - if (1 == ++s_droppedMessageCount) - { - // Don't log every time a message is dropped to avoid flooding the logs under heavy load - LogDaemonError("LogKextMessage: One or more kext log messages have been dropped"); - } - } - - os_log_with_type( - s_kextLogger, - messageLogType, - "%{public}s%{public}.*s", - (messageFlags & LogMessageFlag_LogMessageTruncated) ? "TRUNCATED: " : "", - messageLength, - message); - - if (OS_LOG_TYPE_ERROR == messageLogType) - { - JsonWriter messageWriter; - messageWriter.Add(MessageKey, string(message, messageLength)); - messageWriter.Add("truncated", (messageFlags & LogMessageFlag_LogMessageTruncated) == LogMessageFlag_LogMessageTruncated); - WriteJsonToMessageListener(MessageType::Error, messageWriter); - } -} - -static void LogKextHealthData(const PrjFSVnodeCacheHealth& healthData) -{ - os_log( - s_kextLogger, - "PrjFS Vnode Cache Health: CacheCapacity=%u, CacheEntries=%u, InvalidationCount=%llu, CacheLookups=%llu, LookupCollisions=%llu, FindRootHits=%llu, FindRootMisses=%llu, RefreshRoot=%llu, InvalidateRoot=%llu", - healthData.cacheCapacity, - healthData.cacheEntries, - healthData.invalidateEntireCacheCount, - healthData.totalCacheLookups, - healthData.totalLookupCollisions, - healthData.totalFindRootForVnodeHits, - healthData.totalFindRootForVnodeMisses, - healthData.totalRefreshRootForVnode, - healthData.totalInvalidateVnodeRoot); - - JsonWriter healthDataWriter; - healthDataWriter.Add("CacheCapacity", healthData.cacheCapacity); - healthDataWriter.Add("CacheEntries", healthData.cacheEntries); - healthDataWriter.Add("InvalidationCount", healthData.invalidateEntireCacheCount); - healthDataWriter.Add("CacheLookups", healthData.totalCacheLookups); - healthDataWriter.Add("LookupCollisions", healthData.totalLookupCollisions); - healthDataWriter.Add("FindRootHits", healthData.totalFindRootForVnodeHits); - healthDataWriter.Add("FindRootMisses", healthData.totalFindRootForVnodeMisses); - healthDataWriter.Add("RefreshRoot", healthData.totalRefreshRootForVnode); - healthDataWriter.Add("InvalidateRoot", healthData.totalInvalidateVnodeRoot); - WriteJsonToMessageListener(MessageType::VnodeCacheHealth, healthDataWriter); -} - -static void CreatePipeToMessageListener() -{ - lock_guard lock(s_messageListenerMutex); - - if (INVALID_SOCKET_FD != s_messageListenerSocket) - { - // Already connected - return; - } - - int error = 0; - - s_messageListenerSocket = socket(PF_UNIX, SOCK_STREAM, 0); - if (s_messageListenerSocket < 0) - { - error = errno; - os_log(s_daemonLogger, "CreatePipeToMessageListener: Failed to create socket, error: %d", error); - s_messageListenerSocket = INVALID_SOCKET_FD; - return; - } - - struct sockaddr_un socket_address; - memset(&socket_address, 0, sizeof(struct sockaddr_un)); - - socket_address.sun_family = AF_UNIX; - - static_assert( - sizeof(MessageListenerSocketPath) <= sizeof(socket_address.sun_path), - "socket_address.sun_path is not large enough for MessageListenerSocketPath"); - strlcpy(socket_address.sun_path, MessageListenerSocketPath, sizeof(socket_address.sun_path)); - - if(0 == connect(s_messageListenerSocket, (struct sockaddr *) &socket_address, sizeof(struct sockaddr_un))) - { - os_log( - s_daemonLogger, - "CreatePipeToMessageListener: Connected to message listener at %{public}s", - MessageListenerSocketPath); - return; - } - - error = errno; - os_log( - s_daemonLogger, - "CreatePipeToMessageListener: Failed to connect socket at %{public}s, error: %d", - MessageListenerSocketPath, - error); - - ClosePipeToMessageListener_Locked(); -} - -static void ClosePipeToMessageListener_Locked() -{ - if (INVALID_SOCKET_FD != s_messageListenerSocket) - { - close(s_messageListenerSocket); - s_messageListenerSocket = INVALID_SOCKET_FD; - } -} - -static string MessageTypeToString(MessageType messageType) -{ - switch (messageType) { - case MessageType::Info: - return "info"; - - case MessageType::Error: - return "error"; - - case MessageType::VnodeCacheHealth: - return "health.vnodeCache"; - } - - return "invalid"; -} - -static void WriteJsonToMessageListener(MessageType messageType, const JsonWriter& jsonMessage) -{ - lock_guard lock(s_messageListenerMutex); - - if (INVALID_SOCKET_FD == s_messageListenerSocket) - { - return; - } - - JsonWriter fullMessageWriter; - fullMessageWriter.Add("version", PrjFSKextVersion); - fullMessageWriter.Add("providerName", "Microsoft.Git.GVFS"); - - - fullMessageWriter.Add("eventName", "kext." + MessageTypeToString(messageType)); - fullMessageWriter.Add("payload", jsonMessage); - - string fullMessage = fullMessageWriter.ToString() + "\n"; - size_t bytesRemaining = fullMessage.length(); - while (bytesRemaining > 0) - { - size_t offset = fullMessage.length() - bytesRemaining; - ssize_t bytesWritten = write(s_messageListenerSocket, fullMessage.c_str() + offset, bytesRemaining); - - if (-1 == bytesWritten) - { - if (EINTR != errno) - { - break; - } - } - else - { - bytesRemaining -= bytesWritten; - } - } - - int error = errno; - if (bytesRemaining != 0) - { - os_log( - s_daemonLogger, - "WriteJsonToMessageListener: Failed to write message '%{public}s' to listener. Error: %d, Bytes remaining: %zu", - fullMessage.c_str(), - error, - bytesRemaining); - - // If anything goes wrong close the socket. The next time the timer fires we'll re-connect - ClosePipeToMessageListener_Locked(); - } -} diff --git a/ProjFS.Mac/PrjFSKextLogDaemon/org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist b/ProjFS.Mac/PrjFSKextLogDaemon/org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist deleted file mode 100644 index c2b1d91750..0000000000 --- a/ProjFS.Mac/PrjFSKextLogDaemon/org.vfsforgit.prjfs.PrjFSKextLogDaemon.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - Label - org.vfsforgit.prjfs.PrjFSKextLogDaemon - Program - /usr/local/vfsforgit/PrjFSKextLogDaemon - KeepAlive - - RunAtLoad - - - diff --git a/ProjFS.Mac/PrjFSKextTests/HandleFileOpOperationTests.mm b/ProjFS.Mac/PrjFSKextTests/HandleFileOpOperationTests.mm deleted file mode 100644 index 23a1b53c5d..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/HandleFileOpOperationTests.mm +++ /dev/null @@ -1,481 +0,0 @@ -#include "../PrjFSKext/KauthHandlerTestable.hpp" -#include "../PrjFSKext/VirtualizationRoots.hpp" -#include "../PrjFSKext/PrjFSProviderUserClient.hpp" -#include "../PrjFSKext/VirtualizationRootsTestable.hpp" -#include "../PrjFSKext/VnodeCachePrivate.hpp" -#include "../PrjFSKext/VnodeCacheTestable.hpp" -#include "../PrjFSKext/PerformanceTracing.hpp" -#include "../PrjFSKext/public/Message.h" -#include "../PrjFSKext/ProviderMessaging.hpp" -#include "../PrjFSKext/public/PrjFSXattrs.h" -#include "../PrjFSKext/kernel-header-wrappers/kauth.h" -#import "KextAssertIntegration.h" -#import -#include "KextMockUtilities.hpp" -#include "MockVnodeAndMount.hpp" -#include "MockProc.hpp" -#include "ProviderMessagingMock.hpp" -#include "VnodeCacheEntriesWrapper.hpp" -#include - -using std::make_tuple; -using std::shared_ptr; -using std::vector; -using std::string; -using KextMock::_; - -class PrjFSProviderUserClient -{ -}; - -@interface HandleFileOpOperationTests : PFSKextTestCase -@end - -@implementation HandleFileOpOperationTests -{ - vfs_context_t context; - const char* repoPath; - const char* filePath; - const char* dirPath; - const char* nonRepoFilePath; - const char* fromPath; - const char* fromPathOutOfRepo; - const char* otherRepoPath; - const char* fromPathOtherRepo; - VirtualizationRootHandle repoHandle; - VirtualizationRootHandle otherRepoHandle; - PrjFSProviderUserClient dummyClient; - pid_t dummyClientPid; - PrjFSProviderUserClient otherDummyClient; - pid_t otherDummyClientPid; - shared_ptr testMount; - shared_ptr repoRootVnode; - shared_ptr testFileVnode; - shared_ptr nonRepoFileVnode; - shared_ptr otherRepoRootVnode; - shared_ptr testDirVnode; - VnodeCacheEntriesWrapper cacheWrapper; -} - -- (void) setUp -{ - [super setUp]; - - kern_return_t initResult = VirtualizationRoots_Init(); - XCTAssertEqual(initResult, KERN_SUCCESS); - context = vfs_context_create(NULL); - dummyClientPid = 100; - otherDummyClientPid = 200; - - cacheWrapper.AllocateCache(); - - // Create Vnode Tree - repoPath = "/Users/test/code/Repo"; - filePath = "/Users/test/code/Repo/file"; - dirPath = "/Users/test/code/Repo/dir"; - fromPath = "/Users/test/code/Repo/originalfile"; - nonRepoFilePath = "/Users/test/code/NotInRepo/file"; - fromPathOutOfRepo = "/Users/test/code/NotInRepo/fromfile"; - otherRepoPath = "/Users/test/code/OtherRepo"; - fromPathOtherRepo = "/Users/test/code/OtherRepo/fromfile"; - testMount = mount::Create(); - repoRootVnode = testMount->CreateVnodeTree(repoPath, VDIR); - testFileVnode = testMount->CreateVnodeTree(filePath); - otherRepoRootVnode = testMount->CreateVnodeTree(otherRepoPath, VDIR); - nonRepoFileVnode = testMount->CreateVnodeTree(nonRepoFilePath); - testDirVnode = testMount->CreateVnodeTree(dirPath, VDIR); - - // Register provider for the repository path (Simulate a mount) - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&dummyClient, dummyClientPid, repoPath); - XCTAssertEqual(result.error, 0); - self->repoHandle = result.root; - - result = VirtualizationRoot_RegisterProviderForPath(&otherDummyClient, otherDummyClientPid, otherRepoPath); - XCTAssertEqual(result.error, 0); - self->otherRepoHandle = result.root; - - MockProcess_AddContext(context, 501 /*pid*/); - MockProcess_SetSelfInfo(501, "Test"); - MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/); - - ProvidermessageMock_ResetResultCount(); - ProviderMessageMock_SetDefaultRequestResult(true); - ProviderMessageMock_SetSecondRequestResult(true); -} - -- (void) tearDownProviders -{ - if (VirtualizationRoot_IsValidRootHandle(self->repoHandle) && VirtualizationRoot_GetActiveProvider(self->repoHandle).isOnline) - { - ActiveProvider_Disconnect(self->repoHandle, &dummyClient); - self->repoHandle = RootHandle_None; - } - - if (VirtualizationRoot_IsValidRootHandle(self->otherRepoHandle) && VirtualizationRoot_GetActiveProvider(self->otherRepoHandle).isOnline) - { - ActiveProvider_Disconnect(self->otherRepoHandle, &otherDummyClient); - self->otherRepoHandle = RootHandle_None; - } -} - -- (void) tearDown -{ - [self tearDownProviders]; - - testMount.reset(); - repoRootVnode.reset(); - testFileVnode.reset(); - otherRepoRootVnode.reset(); - nonRepoFileVnode.reset(); - testDirVnode.reset(); - cacheWrapper.FreeCache(); - - VirtualizationRoots_Cleanup(); - vfs_context_rele(context); - MockVnodes_CheckAndClear(); - MockCalls::Clear(); - MockProcess_Reset(); - - [super tearDown]; -} - -- (void) testOpen { - // KAUTH_FILEOP_OPEN should trigger callbacks for files not flagged as in the virtualization root. - testFileVnode->attrValues.va_flags = 0; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_OPEN, - reinterpret_cast(testFileVnode.get()), - reinterpret_cast(filePath), - 0, - 0); - - XCTAssertTrue( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_NotifyFileCreated, - testFileVnode.get(), - _, - _, - _, - _, - _, - _)); -} - -- (void) testOpenInVirtualizationRoot { - // KAUTH_FILEOP_OPEN should not trigger any callbacks for files that already flagged as in the virtualization root. - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_OPEN, - reinterpret_cast(testFileVnode.get()), - reinterpret_cast(filePath), - 0, - 0); - - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void) testCloseWithModifed { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_CLOSE, - reinterpret_cast(testFileVnode.get()), - reinterpret_cast(filePath), - KAUTH_FILEOP_CLOSE_MODIFIED, - 0); - - XCTAssertTrue( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_NotifyFileModified, - testFileVnode.get(), - _, - filePath, - _, - _, - _, - _)); -} - -- (void) testCloseWithModifedWithBitChange { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_CLOSE, - reinterpret_cast(testFileVnode.get()), - reinterpret_cast(filePath), - KAUTH_FILEOP_CLOSE_MODIFIED | 1<<2, - 0); - - XCTAssertTrue( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_NotifyFileModified, - testFileVnode.get(), - _, - filePath, - _, - _, - _, - _)); -} - -- (void) testCloseWithModifedOnDirectory { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_CLOSE, - reinterpret_cast(testDirVnode.get()), - reinterpret_cast(dirPath), - KAUTH_FILEOP_CLOSE_MODIFIED, - 0); - - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - - -- (void) testCloseWithoutModifed { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_CLOSE, - reinterpret_cast(testFileVnode.get()), - reinterpret_cast(filePath), - 0, - 0); - - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void)testFileopHardlink -{ - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_LINK, - reinterpret_cast(fromPath), - reinterpret_cast(filePath), - 0, - 0); - - // from & target repos are the same, should message exactly once - XCTAssertEqual(1, MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse)); - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->repoHandle, - MessageType_KtoU_NotifyFileHardLinkCreated, - _)); -} - -- (void)testFileopHardlinkOutsideRepo -{ - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_LINK, - reinterpret_cast(fromPathOutOfRepo), - reinterpret_cast(nonRepoFilePath), - 0, - 0); - - // neither from & target are in a root, should not message anyone - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void)testFileopHardlinkIntoRepo -{ - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_LINK, - reinterpret_cast(fromPathOutOfRepo), - reinterpret_cast(filePath), - 0, - 0); - - // fromPath is outside repo, filePath is inside, should message exactly once - XCTAssertEqual(1, MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse)); - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->repoHandle, - MessageType_KtoU_NotifyFileHardLinkCreated, - _)); -} - -- (void)testFileopHardlinkOtherRepo -{ - // Link file from one repo into another - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_LINK, - reinterpret_cast(fromPathOtherRepo), - reinterpret_cast(filePath), - 0, - 0); - - // fromPath is for another repo than filePath, should message both providers - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->repoHandle, - MessageType_KtoU_NotifyFileHardLinkCreated, - _)); - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->otherRepoHandle, - MessageType_KtoU_NotifyFileHardLinkCreated, - _)); -} - -- (void)testFileopHardlinkOtherRepoOffline -{ - // Move file from an offline repo into a live one - ActiveProvider_Disconnect(self->otherRepoHandle, &otherDummyClient); - - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_LINK, - reinterpret_cast(fromPathOtherRepo), - reinterpret_cast(filePath), - 0, - 0); - - // fromPath is in an offline repo, which can't be messaged - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->repoHandle, - MessageType_KtoU_NotifyFileHardLinkCreated, - _)); - XCTAssertFalse(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->otherRepoHandle, - _, - _)); - XCTAssertEqual(1, MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void)testFileopHardlinkOutOfRepo -{ - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_LINK, - reinterpret_cast(fromPath), - reinterpret_cast(nonRepoFilePath), - 0, - 0); - - // filePath is outside repo, fromPath is inside, should message exactly once - XCTAssertEqual(1, MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse)); - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->repoHandle, - MessageType_KtoU_NotifyFileHardLinkCreated, - _)); -} - -- (void)testFileopHardlinkOtherRepoProviderPID -{ - MockProcess_Reset(); - MockProcess_AddContext(context, self->dummyClientPid /*pid*/); - MockProcess_SetSelfInfo(self->dummyClientPid, "Test"); - MockProcess_AddProcess(self->dummyClientPid /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "GVFS.Mount" /*name*/); - - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_LINK, - reinterpret_cast(fromPathOtherRepo), - reinterpret_cast(filePath), - 0, - 0); - - // fromPath is for another repo than filePath, but this is the target provider's PID, so only message "from" provider - XCTAssertFalse(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->repoHandle, - _, - _)); - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->otherRepoHandle, - MessageType_KtoU_NotifyFileHardLinkCreated, - _)); -} - -- (void)testFileopHardlinkOtherRepoOtherProviderPID -{ - MockProcess_Reset(); - MockProcess_AddContext(context, self->otherDummyClientPid /*pid*/); - MockProcess_SetSelfInfo(self->otherDummyClientPid, "Test"); - MockProcess_AddProcess(self->otherDummyClientPid /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "GVFS.Mount" /*name*/); - - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - HandleFileOpOperation( - nullptr, - nullptr, - KAUTH_FILEOP_LINK, - reinterpret_cast(fromPathOtherRepo), - reinterpret_cast(filePath), - 0, - 0); - - // fromPath is for another repo than filePath, but this is the "from" provider's PID, so only message target provider - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->repoHandle, - MessageType_KtoU_NotifyFileHardLinkCreated, - _)); - XCTAssertFalse(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - self->otherRepoHandle, - _, - _)); -} - -- (void)testNoPendingRenameRecordedOnIneligibleFilesystem -{ - shared_ptr testMountNone = mount::Create("msdos", fsid_t{}, 0); - const string testMountPath = "/Volumes/USBSTICK"; - shared_ptr testMountRoot = testMountNone->CreateVnodeTree(testMountPath, VDIR); - const string badfsFilePath = testMountPath + "/file"; - shared_ptr testVnode = testMountNone->CreateVnodeTree(badfsFilePath); - const string renamedFilePath = badfsFilePath + "_renamed"; - - HandleFileOpOperation( - nullptr, // credential - nullptr, /* idata, unused */ - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(testVnode.get()), - reinterpret_cast(badfsFilePath.c_str()), - reinterpret_cast(renamedFilePath.c_str()), - 0); // unused - XCTAssertEqual(0, s_pendingRenameCount); -} - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/HandleOperationTests.mm b/ProjFS.Mac/PrjFSKextTests/HandleOperationTests.mm deleted file mode 100644 index 31431dc9d7..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/HandleOperationTests.mm +++ /dev/null @@ -1,1491 +0,0 @@ -#include "../PrjFSKext/KauthHandlerTestable.hpp" -#include "../PrjFSKext/VirtualizationRoots.hpp" -#include "../PrjFSKext/PrjFSProviderUserClient.hpp" -#include "../PrjFSKext/VirtualizationRootsTestable.hpp" -#include "../PrjFSKext/VnodeCachePrivate.hpp" -#include "../PrjFSKext/VnodeCacheTestable.hpp" -#include "../PrjFSKext/PerformanceTracing.hpp" -#include "../PrjFSKext/public/Message.h" -#include "../PrjFSKext/ProviderMessaging.hpp" -#include "../PrjFSKext/public/PrjFSXattrs.h" -#include "../PrjFSKext/kernel-header-wrappers/kauth.h" -#import "KextAssertIntegration.h" -#import -#include "KextMockUtilities.hpp" -#include "MockVnodeAndMount.hpp" -#include "MockProc.hpp" -#include "ProviderMessagingMock.hpp" -#include "VnodeCacheEntriesWrapper.hpp" -#include - -using std::make_tuple; -using std::shared_ptr; -using std::vector; -using std::string; -using std::extent; -using KextMock::_; - -// Darwin version of running kernel -int version_major = PrjFSDarwinMajorVersion::MacOS10_14_Mojave; - -static const pid_t RunningMockProcessPID = 501; - -class PrjFSProviderUserClient -{ -}; - -static void SetPrjFSFileXattrData(const shared_ptr& vnode) -{ - PrjFSFileXAttrData rootXattr = {}; - vector rootXattrData(sizeof(rootXattr), 0x00); - memcpy(rootXattrData.data(), &rootXattr, rootXattrData.size()); - vnode->xattrs.insert(make_pair(PrjFSFileXAttrName, rootXattrData)); -} - -static void TestForDarwinVersionRange(int versionMin, int versionMax, void(^testBlock)(void)) -{ - const int savedVersion = version_major; - for (int version = versionMin; version <= versionMax; ++version) - { - version_major = version; - testBlock(); - } - version_major = savedVersion; -} - -static void TestForAllSupportedDarwinVersions(void(^testBlock)(void)) -{ - TestForDarwinVersionRange(PrjFSDarwinMajorVersion::MacOS10_13_HighSierra, PrjFSDarwinMajorVersion::MacOS10_15_Catalina, testBlock); -} - -@interface HandleVnodeOperationTests : PFSKextTestCase -@end - -@implementation HandleVnodeOperationTests -{ - vfs_context_t context; - string repoPath; - string filePath; - string dirPath; - VirtualizationRootHandle dummyRepoHandle; - PrjFSProviderUserClient dummyClient; - pid_t dummyClientPid; - shared_ptr testMount; - shared_ptr repoRootVnode; - shared_ptr testFileVnode; - shared_ptr testDirVnode; - VnodeCacheEntriesWrapper cacheWrapper; -} - -- (void) setUp -{ - [super setUp]; - - kern_return_t initResult = VirtualizationRoots_Init(); - XCTAssertEqual(initResult, KERN_SUCCESS); - context = vfs_context_create(NULL); - dummyClientPid = 100; - - cacheWrapper.AllocateCache(); - - // Create Vnode Tree - repoPath = "/Users/test/code/Repo"; - filePath = "/Users/test/code/Repo/file"; - dirPath = "/Users/test/code/Repo/dir"; - testMount = mount::Create(); - repoRootVnode = testMount->CreateVnodeTree(repoPath, VDIR); - testFileVnode = testMount->CreateVnodeTree(filePath); - testDirVnode = testMount->CreateVnodeTree(dirPath, VDIR); - - // Register provider for the repository path (Simulate a mount) - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&dummyClient, dummyClientPid, repoPath.c_str()); - XCTAssertEqual(result.error, 0); - self->dummyRepoHandle = result.root; - - MockProcess_AddContext(context, RunningMockProcessPID /*pid*/); - MockProcess_SetSelfInfo(RunningMockProcessPID, "Test"); - MockProcess_AddProcess(RunningMockProcessPID, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/); - - ProvidermessageMock_ResetResultCount(); - ProviderMessageMock_SetDefaultRequestResult(true); - ProviderMessageMock_SetSecondRequestResult(true); -} - -- (void) tearDown -{ - if (VirtualizationRoot_GetActiveProvider(self->dummyRepoHandle).isOnline) - { - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - } - - testMount.reset(); - repoRootVnode.reset(); - testFileVnode.reset(); - testDirVnode.reset(); - cacheWrapper.FreeCache(); - - VirtualizationRoots_Cleanup(); - CleanupPendingRenames(); - vfs_context_rele(context); - MockVnodes_CheckAndClear(); - MockCalls::Clear(); - MockProcess_Reset(); - - [super tearDown]; -} - -- (void) removeAllVirtualizationRoots -{ - if (VirtualizationRoot_GetActiveProvider(self->dummyRepoHandle).isOnline) - { - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - } - - VirtualizationRoots_Cleanup(); - VirtualizationRoots_Init(); -} - -- (void) testEmptyFileHydrates { - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - - kauth_action_t actions[] = - { - KAUTH_VNODE_READ_ATTRIBUTES, - KAUTH_VNODE_WRITE_ATTRIBUTES, - KAUTH_VNODE_READ_EXTATTRIBUTES, - KAUTH_VNODE_WRITE_EXTATTRIBUTES, - KAUTH_VNODE_READ_DATA, - KAUTH_VNODE_WRITE_DATA, - KAUTH_VNODE_EXECUTE, - KAUTH_VNODE_APPEND_DATA, - }; - const int actionCount = std::extent::value; - - for (int i = 0; i < actionCount; i++) - { - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertTrue( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); - MockCalls::Clear(); - } -} - -- (void) testFileDeleteHydratesOnlyWhenNecessary -{ - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - - TestForAllSupportedDarwinVersions(^{ - InitPendingRenames(); - XCTAssertEqual( - KAUTH_RESULT_DEFER, - HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(self->context), - reinterpret_cast(self->testFileVnode.get()), - 0, - 0)); - bool didHydrate = - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - self->testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr); - - // On High Sierra, any delete causes hydration as it might be due to a rename: - if (version_major <= PrjFSDarwinMajorVersion::MacOS10_13_HighSierra) - { - XCTAssertTrue(didHydrate); - } - else - { - // On Mojave+, no hydration on non-rename delete: - XCTAssertFalse(didHydrate); - } - - MockCalls::Clear(); - CleanupPendingRenames(); - }); -} - -- (void) testVnodeAccessCausesNoEvent { - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - - const int actionCount = 9; - kauth_action_t actions[actionCount] = - { - KAUTH_VNODE_READ_ATTRIBUTES | KAUTH_VNODE_ACCESS, - KAUTH_VNODE_WRITE_ATTRIBUTES | KAUTH_VNODE_ACCESS, - KAUTH_VNODE_READ_EXTATTRIBUTES | KAUTH_VNODE_ACCESS, - KAUTH_VNODE_WRITE_EXTATTRIBUTES | KAUTH_VNODE_ACCESS, - KAUTH_VNODE_READ_DATA | KAUTH_VNODE_ACCESS, - KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_ACCESS, - KAUTH_VNODE_EXECUTE | KAUTH_VNODE_ACCESS, - KAUTH_VNODE_DELETE | KAUTH_VNODE_ACCESS, - KAUTH_VNODE_APPEND_DATA | KAUTH_VNODE_ACCESS, - }; - - for (int i = 0; i < actionCount; i++) - { - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); - MockCalls::Clear(); - } -} - - -- (void) testNonEmptyFileDoesNotHydrate { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - const int actionCount = 9; - kauth_action_t actions[actionCount] = - { - KAUTH_VNODE_READ_ATTRIBUTES, - KAUTH_VNODE_WRITE_ATTRIBUTES, - KAUTH_VNODE_READ_EXTATTRIBUTES, - KAUTH_VNODE_WRITE_EXTATTRIBUTES, - KAUTH_VNODE_READ_DATA, - KAUTH_VNODE_WRITE_DATA, - KAUTH_VNODE_EXECUTE, - KAUTH_VNODE_DELETE, - KAUTH_VNODE_APPEND_DATA, - }; - - for (int i = 0; i < actionCount; i++) - { - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - nullptr)); - MockCalls::Clear(); - } -} - -- (void) testNonEmptyFileWithPrjFSFileXAttrNameDoesNotHydrate { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - - const int actionCount = 9; - kauth_action_t actions[actionCount] = - { - KAUTH_VNODE_READ_ATTRIBUTES, - KAUTH_VNODE_WRITE_ATTRIBUTES, - KAUTH_VNODE_READ_EXTATTRIBUTES, - KAUTH_VNODE_WRITE_EXTATTRIBUTES, - KAUTH_VNODE_READ_DATA, - KAUTH_VNODE_WRITE_DATA, - KAUTH_VNODE_EXECUTE, - KAUTH_VNODE_DELETE, - KAUTH_VNODE_APPEND_DATA, - }; - - for (int i = 0; i < actionCount; i++) - { - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - nullptr)); - MockCalls::Clear(); - } -} - -- (void) testEventsThatShouldNotHydrate { - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - - const int actionCount = 4; - kauth_action_t actions[actionCount] = - { - KAUTH_VNODE_DELETE_CHILD, - KAUTH_VNODE_READ_SECURITY, - KAUTH_VNODE_WRITE_SECURITY, - KAUTH_VNODE_TAKE_OWNERSHIP - }; - - for (int i = 0; i < actionCount; i++) - { - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - nullptr)); - MockCalls::Clear(); - } -} - -- (void) testHydrationOnDeleteWhenRenamingFile { - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - - // Hydration on delete only occurs if deletion is caused by rename - string renamedFilePath = filePath + "_renamed"; - HandleFileOpOperation( - nullptr, // credential - nullptr, /* idata, unused */ - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(testFileVnode.get()), - reinterpret_cast(filePath.c_str()), - reinterpret_cast(renamedFilePath.c_str()), - 0); // unused - - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertTrue(MockCalls::DidCallFunctionsInOrder( - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr), - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_NotifyFilePreDeleteFromRename, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)) - ); - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 2); -} - -- (void) testDeleteFileNonRenamed -{ - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - - InitPendingRenames(); - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_NotifyFilePreDelete, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); - - // Should not hydrate if delete is not caused by rename - XCTAssertFalse(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - _, - _, - _, - _, - _, - _, - _, - nullptr)); - CleanupPendingRenames(); -} - -- (void) testConcurrentRenameOperationRecording -{ - InitPendingRenames(); - self->testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - - string otherFilePath = repoPath + "/otherFile"; - shared_ptr otherTestFileVnode = testMount->CreateVnodeTree(otherFilePath); - otherTestFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - - string renamedFilePath = filePath + "_renamed"; - string renamedOtherFilePath = otherFilePath + "_renamed"; - - MockProcess_SetCurrentThreadIndex(0); - HandleFileOpOperation( - nullptr, // credential - nullptr, /* idata, unused */ - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(testFileVnode.get()), - reinterpret_cast(filePath.c_str()), - reinterpret_cast(renamedFilePath.c_str()), - 0); // unused - - MockProcess_SetCurrentThreadIndex(1); - HandleFileOpOperation( - nullptr, // credential - nullptr, /* idata, unused */ - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(otherTestFileVnode.get()), - reinterpret_cast(otherFilePath.c_str()), - reinterpret_cast(renamedOtherFilePath.c_str()), - 0); // unused - - MockProcess_SetCurrentThreadIndex(0); - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - - MockProcess_SetCurrentThreadIndex(1); - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(otherTestFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - - XCTAssertTrue(MockCalls::DidCallFunctionsInOrder( - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr), - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_NotifyFilePreDeleteFromRename, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr), - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_HydrateFile, - otherTestFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr), - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_NotifyFilePreDeleteFromRename, - otherTestFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)) - ); - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 4); - CleanupPendingRenames(); -} - - -- (void) testHydrationOnDeleteWhenRenamingDirectory -{ - testDirVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - // Hydration on delete only occurs if deletion is caused by rename - string renamedDirPath = self->dirPath + "_renamed"; - HandleFileOpOperation( - nullptr, // credential - nullptr, /* idata, unused */ - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(self->testDirVnode.get()), - reinterpret_cast(self->dirPath.c_str()), - reinterpret_cast(renamedDirPath.c_str()), - 0); // unused - - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertTrue(MockCalls::DidCallFunctionsInOrder( - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_RecursivelyEnumerateDirectory, - testDirVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr), - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_NotifyDirectoryPreDelete, - testDirVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)) - ); - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 2); -} - -- (void) testDeleteDirNonRenamed -{ - testDirVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - TestForAllSupportedDarwinVersions(^{ - InitPendingRenames(); - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(self->context), - reinterpret_cast(self->testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertTrue(MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_NotifyDirectoryPreDelete, - self->testDirVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); - - // Should not enumerate if delete is not caused by rename, except on High Sierra - bool didEnumerate = MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_RecursivelyEnumerateDirectory, - self->testDirVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr); - if (version_major <= PrjFSDarwinMajorVersion::MacOS10_13_HighSierra) - { - XCTAssertTrue(didEnumerate); - } - else - { - XCTAssertFalse(didEnumerate); - } - - MockCalls::Clear(); - CleanupPendingRenames(); - }); -} - -- (void) testEmptyDirectoryEnumerates { - testDirVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - const int actionCount = 5; - kauth_action_t actions[actionCount] = - { - KAUTH_VNODE_LIST_DIRECTORY, - KAUTH_VNODE_SEARCH, - KAUTH_VNODE_READ_SECURITY, - KAUTH_VNODE_READ_ATTRIBUTES, - KAUTH_VNODE_READ_EXTATTRIBUTES - }; - - for (int i = 0; i < actionCount; i++) - { - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertTrue( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_EnumerateDirectory, - testDirVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); - MockCalls::Clear(); - } -} - -- (void) testEventsThatShouldNotDirectoryEnumerates { - testDirVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - const int actionCount = 9; - kauth_action_t actions[actionCount] = - { - KAUTH_VNODE_WRITE_DATA, - KAUTH_VNODE_ADD_FILE, - KAUTH_VNODE_APPEND_DATA, - KAUTH_VNODE_ADD_SUBDIRECTORY, - KAUTH_VNODE_DELETE_CHILD, - KAUTH_VNODE_WRITE_ATTRIBUTES, - KAUTH_VNODE_WRITE_EXTATTRIBUTES, - KAUTH_VNODE_WRITE_SECURITY, - KAUTH_VNODE_TAKE_OWNERSHIP - }; - - for (int i = 0; i < actionCount; i++) - { - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_EnumerateDirectory, - testDirVnode.get(), - _, - _, - _, - _, - _, - nullptr)); - MockCalls::Clear(); - } -} - -- (void) testNonEmptyDirectoryDoesNotEnumerate { - testDirVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - const int actionCount = 5; - kauth_action_t actions[actionCount] = - { - KAUTH_VNODE_LIST_DIRECTORY, - KAUTH_VNODE_SEARCH, - KAUTH_VNODE_READ_SECURITY, - KAUTH_VNODE_READ_ATTRIBUTES, - KAUTH_VNODE_READ_EXTATTRIBUTES - }; - - for (int i = 0; i < actionCount; i++) - { - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); - } -} - --(void) testWriteFile { - // If we have FileXattrData attribute we should trigger MessageType_KtoU_NotifyFilePreConvertToFull to remove it - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_WRITE_DATA, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertTrue(MockCalls::DidCallFunctionsInOrder( - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr), - ProviderMessaging_TrySendRequestAndWaitForResponse, - make_tuple( - _, - MessageType_KtoU_NotifyFilePreConvertToFull, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)) - ); - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 2); -} - --(void) testWriteFileHydrated { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - // If we have FileXattrData attribute we should trigger MessageType_KtoU_NotifyFilePreConvertToFull to remove it - SetPrjFSFileXattrData(testFileVnode); - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_WRITE_DATA, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); - XCTAssertTrue( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_NotifyFilePreConvertToFull, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 1); -} - --(void) testWriteFileHydratedOfflineRoot -{ - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - - XCTAssertEqual( - KAUTH_RESULT_DENY, - HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_WRITE_DATA, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0)); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - _, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); -} - --(void) testWriteFlaggedNonRepoFile -{ - string testFilePath = "/Users/test/code/otherproject/file"; - shared_ptr testFile = testMount->CreateVnodeTree(testFilePath); - testFile->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - XCTAssertEqual( - KAUTH_RESULT_DEFER, - HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_WRITE_DATA, - reinterpret_cast(context), - reinterpret_cast(testFile.get()), - 0, - 0)); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - _, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); -} - --(void) testWriteFileFull { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_WRITE_DATA, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void) testEventsAreIgnored { - testFileVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot | FileFlags_IsEmpty; - const int actionCount = 3; - kauth_action_t actions[actionCount] = - { - KAUTH_VNODE_WRITE_SECURITY, - KAUTH_VNODE_TAKE_OWNERSHIP, - KAUTH_VNODE_ACCESS - }; - - for (int i = 0; i < actionCount; i++) - { - // Verify for File node - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - XCTAssertFalse( - MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); - MockCalls::Clear(); - - // Verify for Directory node - HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0); - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); - MockCalls::Clear(); - } -} - -- (void) testDeleteWithNoVirtualizationRoot { - [self removeAllVirtualizationRoots]; - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void) testDeleteWhenRequestFails { - ProviderMessageMock_SetDefaultRequestResult(false); - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DENY); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 1); -} - -- (void) testDeleteDirectoryWithDisappearingVirtualizationRoot { - ProviderMessageMock_SetRequestSideEffect( - [&]() - { - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - }); - testDirVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 1); -} - -// When the first call to getVirtualizationRoot fails, ensure no more calls are made -- (void) testRenameDirectoryWhenFirstRequestFails { - ProviderMessageMock_SetDefaultRequestResult(false); - testDirVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - string renamedDirPath = self->dirPath + "_renamed"; - HandleFileOpOperation( - nullptr, // credential - nullptr, /* idata, unused */ - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(self->testDirVnode.get()), - reinterpret_cast(self->dirPath.c_str()), - reinterpret_cast(renamedDirPath.c_str()), - 0); // unused - - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DENY); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 1); -} - -- (void) testDeleteDirectoryForRenameWhenSecondRequestFails { - ProviderMessageMock_SetSecondRequestResult(false); - - // Hydration on delete only occurs if deletion is caused by rename - string renamedDirPath = self->dirPath + "_renamed"; - HandleFileOpOperation( - nullptr, // credential - nullptr, /* idata, unused */ - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(self->testDirVnode.get()), - reinterpret_cast(self->dirPath.c_str()), - reinterpret_cast(renamedDirPath.c_str()), - 0); // unused - - testDirVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DENY); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 2); -} - -- (void) testRenameDirectoryWithNoVirtualizationRoot { - [self removeAllVirtualizationRoots]; - testDirVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - string renamedDirPath = self->dirPath + "_renamed"; - HandleFileOpOperation( - nullptr, // credential - nullptr, // idata, unused - KAUTH_FILEOP_WILL_RENAME, - reinterpret_cast(self->testDirVnode.get()), - reinterpret_cast(self->dirPath.c_str()), - reinterpret_cast(renamedDirPath.c_str()), - 0); // unused - - XCTAssertEqual( - KAUTH_RESULT_DEFER, - HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_DELETE, - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0)); - - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void) testReadAttributesDirectoryWithNoVirtualizationRoot { - [self removeAllVirtualizationRoots]; - testDirVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_READ_ATTRIBUTES, - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void) testReadAttributesDirectoryWhenRequestFails { - ProviderMessageMock_SetDefaultRequestResult(false); - testDirVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_READ_ATTRIBUTES, - reinterpret_cast(context), - reinterpret_cast(testDirVnode.get()), - 0, - 0) == KAUTH_RESULT_DENY); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 1); -} - -- (void) testReadAttributesWithNoVirtualizationRoot { - [self removeAllVirtualizationRoots]; - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_READ_ATTRIBUTES, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void) testReadAttributesWhenRequestFails { - ProviderMessageMock_SetDefaultRequestResult(false); - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_READ_ATTRIBUTES, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DENY); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 1); -} - -- (void) testWriteWithDisappearingVirtualizationRoot { - // Tests provider disappearing between hydration and attempting to convert to full. - - // Start with empty file… - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(self->testFileVnode); - - // First message to provider marks the file as hydrated and also disconnects the providers - ProviderMessageMock_SetRequestSideEffect( - [&]() - { - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - // mark as hydrated - testFileVnode->attrValues.va_flags &= ~FileFlags_IsEmpty; - }); - - // File should become hydrated but write access should be denied due to failure to convert to full. - XCTAssertEqual( - KAUTH_RESULT_DENY, - HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_WRITE_DATA, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0)); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 1); -} - -- (void) testWriteWhenSecondRequestFails { - ProviderMessageMock_SetSecondRequestResult(false); - SetPrjFSFileXattrData(testFileVnode); - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_WRITE_DATA, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DENY); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 2); -} - -- (void) testWriteDataToForkedPath { - // By setting namedStream, HandleVnodeOperation with treat testFileVnode as a named fork - testFileVnode->namedStream = true; - testFileVnode->GetParentVnode()->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - - XCTAssertTrue(HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_WRITE_DATA, - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0) == KAUTH_RESULT_DEFER); - - XCTAssertTrue(MockCalls::CallCount(ProviderMessaging_TrySendRequestAndWaitForResponse) == 0); -} - -- (void) testIneligibleFilesystemType -{ - shared_ptr testMountNone = mount::Create("msdos", fsid_t{}, 0); - shared_ptr testVnodeNone = testMountNone->CreateVnodeTree("/Volumes/USBSTICK", VDIR); - - XCTAssertEqual( - KAUTH_RESULT_DEFER, - HandleVnodeOperation( - nullptr, - nullptr, - KAUTH_VNODE_ADD_FILE, - reinterpret_cast(context), - reinterpret_cast(testVnodeNone.get()), - 0, - 0)); - XCTAssertFalse(MockCalls::DidCallFunction(ProviderMessaging_TrySendRequestAndWaitForResponse)); -} - -- (void) testOfflineRootDeniesAccessToEmptyFile -{ - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - - kauth_action_t actions[] = - { - KAUTH_VNODE_WRITE_ATTRIBUTES, - KAUTH_VNODE_WRITE_EXTATTRIBUTES, - KAUTH_VNODE_WRITE_DATA, - KAUTH_VNODE_APPEND_DATA, - KAUTH_VNODE_READ_DATA, - KAUTH_VNODE_READ_ATTRIBUTES, - KAUTH_VNODE_EXECUTE, - KAUTH_VNODE_READ_EXTATTRIBUTES, - }; - const size_t actionCount = extent::value; - - for (size_t i = 0; i < actionCount; i++) - { - XCTAssertEqual( - KAUTH_RESULT_DENY, - HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0)); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); - MockCalls::Clear(); - } -} - -- (void) testOfflineRootDeniesFileAndDirectoryCreation -{ - self->testDirVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(self->testDirVnode); - - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - - kauth_action_t actions[] = - { - KAUTH_VNODE_ADD_SUBDIRECTORY, - KAUTH_VNODE_ADD_FILE - }; - const size_t actionCount = extent::value; - - for (size_t i = 0; i < actionCount; i++) - { - XCTAssertEqual( - KAUTH_RESULT_DENY, - HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(self->context), - reinterpret_cast(self->testDirVnode.get()), - 0, - 0)); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse)); - MockCalls::Clear(); - } -} - - -- (void) testOfflineRootDeniesRename -{ - // Where we can detect renames (Mojave and newer), file/directory renames - // should be prevented when the provider is offline. On High Sierra we have - // to let them happen as we can't distinguish them from file deletions - // before it's already happened. - - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - - // 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) - { - 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 -{ - testFileVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - SetPrjFSFileXattrData(testFileVnode); - - ActiveProvider_Disconnect(self->dummyRepoHandle, &self->dummyClient); - - XCTAssertTrue(VirtualizationRoots_AddOfflineIOProcess(RunningMockProcessPID)); - - kauth_action_t actions[] = - { - KAUTH_VNODE_READ_ATTRIBUTES, - KAUTH_VNODE_WRITE_ATTRIBUTES, - KAUTH_VNODE_READ_EXTATTRIBUTES, - KAUTH_VNODE_WRITE_EXTATTRIBUTES, - KAUTH_VNODE_READ_DATA, - KAUTH_VNODE_WRITE_DATA, - KAUTH_VNODE_EXECUTE, - KAUTH_VNODE_DELETE, - KAUTH_VNODE_APPEND_DATA, - }; - const size_t actionCount = extent::value; - - for (size_t i = 0; i < actionCount; i++) - { - XCTAssertEqual( - KAUTH_RESULT_DEFER, - HandleVnodeOperation( - nullptr, - nullptr, - actions[i], - reinterpret_cast(context), - reinterpret_cast(testFileVnode.get()), - 0, - 0)); - XCTAssertFalse( - MockCalls::DidCallFunction( - ProviderMessaging_TrySendRequestAndWaitForResponse, - _, - MessageType_KtoU_HydrateFile, - testFileVnode.get(), - _, - _, - _, - _, - _, - _, - nullptr)); - MockCalls::Clear(); - } - - VirtualizationRoots_RemoveOfflineIOProcess(RunningMockProcessPID); -} - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/Info.plist b/ProjFS.Mac/PrjFSKextTests/Info.plist deleted file mode 100644 index 6c40a6cd0c..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/ProjFS.Mac/PrjFSKextTests/KauthHandlerTests.mm b/ProjFS.Mac/PrjFSKextTests/KauthHandlerTests.mm deleted file mode 100644 index 4309e7a4a4..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/KauthHandlerTests.mm +++ /dev/null @@ -1,407 +0,0 @@ -#include "../PrjFSKext/kernel-header-wrappers/vnode.h" -#include "../PrjFSKext/KauthHandlerTestable.hpp" -#include "../PrjFSKext/PerformanceTracing.hpp" -#include "../PrjFSKext/VirtualizationRootsTestable.hpp" -#import "KextAssertIntegration.h" -#import -#include "KextLogMock.h" -#include "KextMockUtilities.hpp" -#include "MockVnodeAndMount.hpp" -#include "MockProc.hpp" -#include "VnodeCacheEntriesWrapper.hpp" - -using std::shared_ptr; -using std::string; - -@interface KauthHandlerTests : PFSKextTestCase -@end - -@implementation KauthHandlerTests -{ - VnodeCacheEntriesWrapper cacheWrapper; - vfs_context_t context; -} - -- (void) setUp { - [super setUp]; - kern_return_t initResult = VirtualizationRoots_Init(); - XCTAssertEqual(initResult, KERN_SUCCESS); - self->cacheWrapper.AllocateCache(); - context = vfs_context_create(nullptr); - MockProcess_AddContext(context, 501 /*pid*/); - MockProcess_SetSelfInfo(501, "Test"); - MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/); -} - -- (void) tearDown { - MockProcess_Reset(); - self->cacheWrapper.FreeCache(); - MockVnodes_CheckAndClear(); - VirtualizationRoots_Cleanup(); - MockCalls::Clear(); - [super tearDown]; -} - -- (void)testActionBitIsSet { - XCTAssertTrue(ActionBitIsSet(KAUTH_VNODE_READ_DATA, KAUTH_VNODE_READ_DATA)); - XCTAssertTrue(ActionBitIsSet(KAUTH_VNODE_WRITE_DATA, KAUTH_VNODE_WRITE_DATA)); - XCTAssertTrue(ActionBitIsSet(KAUTH_VNODE_WRITE_DATA, KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA)); - XCTAssertTrue(ActionBitIsSet(KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, KAUTH_VNODE_WRITE_DATA)); - XCTAssertFalse(ActionBitIsSet(KAUTH_VNODE_WRITE_DATA, KAUTH_VNODE_READ_DATA)); -} - -- (void)testIsFileSystemCrawler { - XCTAssertTrue(IsFileSystemCrawler("mds")); - XCTAssertTrue(IsFileSystemCrawler("mdworker")); - XCTAssertTrue(IsFileSystemCrawler("mdworker_shared")); - XCTAssertTrue(IsFileSystemCrawler("mds_stores")); - XCTAssertTrue(IsFileSystemCrawler("fseventsd")); - XCTAssertTrue(IsFileSystemCrawler("Spotlight")); - XCTAssertFalse(IsFileSystemCrawler("mds_")); - XCTAssertFalse(IsFileSystemCrawler("spotlight")); - XCTAssertFalse(IsFileSystemCrawler("git")); -} - -- (void)testFileFlagsBitIsSet { - XCTAssertTrue(FileFlagsBitIsSet(FileFlags_IsEmpty, FileFlags_IsEmpty)); - XCTAssertTrue(FileFlagsBitIsSet(FileFlags_IsInVirtualizationRoot, FileFlags_IsInVirtualizationRoot)); - XCTAssertFalse(FileFlagsBitIsSet(FileFlags_IsInVirtualizationRoot, FileFlags_IsEmpty)); - XCTAssertFalse(FileFlagsBitIsSet(FileFlags_IsInVirtualizationRoot, FileFlags_Invalid)); -} - -- (void)testShouldIgnoreVnodeType { - shared_ptr testMount = mount::Create(); - shared_ptr testVnode = testMount->CreateVnode("/foo"); - XCTAssertTrue(ShouldIgnoreVnodeType(VNON, testVnode.get())); - XCTAssertTrue(ShouldIgnoreVnodeType(VBLK, testVnode.get())); - XCTAssertTrue(ShouldIgnoreVnodeType(VCHR, testVnode.get())); - XCTAssertTrue(ShouldIgnoreVnodeType(VSOCK, testVnode.get())); - XCTAssertTrue(ShouldIgnoreVnodeType(VFIFO, testVnode.get())); - XCTAssertTrue(ShouldIgnoreVnodeType(VBAD, testVnode.get())); - XCTAssertFalse(ShouldIgnoreVnodeType(VREG, testVnode.get())); - XCTAssertFalse(ShouldIgnoreVnodeType(VDIR, testVnode.get())); - XCTAssertFalse(ShouldIgnoreVnodeType(VLNK, testVnode.get())); - XCTAssertFalse(ShouldIgnoreVnodeType(VSTR, testVnode.get())); - XCTAssertFalse(ShouldIgnoreVnodeType(VCPLX, testVnode.get())); - XCTAssertFalse(ShouldIgnoreVnodeType(static_cast(1000), testVnode.get())); -} - -- (void)testFileFlaggedInRoot { - bool fileFlaggedInRoot; - shared_ptr testMount = mount::Create(); - shared_ptr testVnode = vnode::Create(testMount, "/foo"); - - testVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - XCTAssertTrue(TryGetFileIsFlaggedAsInRoot(testVnode.get(), context, &fileFlaggedInRoot)); - XCTAssertTrue(fileFlaggedInRoot); - - testVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot | UF_NODUMP; - XCTAssertTrue(TryGetFileIsFlaggedAsInRoot(testVnode.get(), context, &fileFlaggedInRoot)); - XCTAssertTrue(fileFlaggedInRoot); - - testVnode->attrValues.va_flags = FileFlags_IsEmpty; - XCTAssertTrue(TryGetFileIsFlaggedAsInRoot(testVnode.get(), context, &fileFlaggedInRoot)); - XCTAssertFalse(fileFlaggedInRoot); - - testVnode->attrValues.va_flags = FileFlags_Invalid; - XCTAssertTrue(TryGetFileIsFlaggedAsInRoot(testVnode.get(), context, &fileFlaggedInRoot)); - XCTAssertFalse(fileFlaggedInRoot); - - testVnode->attrValues.va_flags = 0x00000100; - XCTAssertTrue(TryGetFileIsFlaggedAsInRoot(testVnode.get(), context, &fileFlaggedInRoot)); - XCTAssertFalse(fileFlaggedInRoot); - - testVnode->errors.getattr = EBADF; - XCTAssertFalse(TryGetFileIsFlaggedAsInRoot(testVnode.get(), context, &fileFlaggedInRoot)); -} - -- (void)testShouldHandleVnodeOpEvent { - // In Parameters - shared_ptr testMount = mount::Create(); - shared_ptr testVnode = vnode::Create(testMount, "/foo"); - testVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - PerfTracer perfTracer; - kauth_action_t action = KAUTH_VNODE_READ_DATA; - - // Out Parameters - uint32_t vnodeFileFlags; - int pid; - char procname[MAXCOMLEN + 1] = ""; - int kauthResult; - int kauthError; - - - // Test Success Case - XCTAssertTrue( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnode.get(), - action, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DEFER); - - - // Test KAUTH_VNODE_ACCESS is not handled - XCTAssertFalse( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnode.get(), - KAUTH_VNODE_ACCESS, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DEFER); - - // Test KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA is not handled - XCTAssertFalse( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnode.get(), - KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DEFER); - - // Test invalid File System - shared_ptr testMountNone = mount::Create("none", fsid_t{}, 0); - shared_ptr testVnodeNone = vnode::Create(testMountNone, "/none"); - XCTAssertFalse( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnodeNone.get(), - action, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DEFER); - - - // Test invalid VNODE Type - shared_ptr testVnodeInvalidType = vnode::Create(testMount, "/foo2", VNON); - XCTAssertFalse( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnodeInvalidType.get(), - action, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DEFER); - - - // Test failure reading attr - testVnode->errors.getattr = EBADF; - XCTAssertFalse( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnode.get(), - action, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DENY); - // reset to valid value - testVnode->errors.getattr = 0; - - - // Test invalid file flag - testVnode->attrValues.va_flags = FileFlags_IsEmpty; - XCTAssertFalse( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnode.get(), - action, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DEFER); - // reset to valid value - testVnode->attrValues.va_flags = FileFlags_IsInVirtualizationRoot; - - - // Test with file crawler trying to populate an empty file - testVnode->attrValues.va_flags = FileFlags_IsEmpty | FileFlags_IsInVirtualizationRoot; - MockProcess_Reset(); - MockProcess_SetSelfInfo(501, "Test"); - MockProcess_AddContext(context, 501 /*pid*/); - MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "mds" /*name*/); - XCTAssertFalse( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnode.get(), - action, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DENY); - - // Test with finder trying to populate an empty file - MockProcess_Reset(); - MockProcess_SetSelfInfo(501, "Test"); - MockProcess_AddContext(context, 501 /*pid*/); - MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "Finder" /*name*/); - XCTAssertTrue( - ShouldHandleVnodeOpEvent( - &perfTracer, - context, - testVnode.get(), - action, - &vnodeFileFlags, - &pid, - procname, - &kauthResult, - &kauthError)); - XCTAssertEqual(kauthResult, KAUTH_RESULT_DEFER); -} - -- (void)testCurrentProcessIsAllowedToHydrate { - // Defaults should pass for all tests - XCTAssertTrue(CurrentProcessIsAllowedToHydrate()); - MockProcess_Reset(); - - // Process and its parents are owned by a system user, and the executable is not a whitelisted service. - MockProcess_AddContext(context, 500 /*pid*/); - MockProcess_SetSelfInfo(500, "Test"); - MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/); - MockProcess_AddProcess(500 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/); - XCTAssertFalse(CurrentProcessIsAllowedToHydrate()); - MockProcess_Reset(); - - // The service "amfid" and its parents are owned by system users, but the process name is whitelisted for hydration. - MockProcess_AddContext(context, 500 /*pid*/); - MockProcess_SetSelfInfo(500, "amfid"); - MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/); - MockProcess_AddProcess(500 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/); - XCTAssertTrue(CurrentProcessIsAllowedToHydrate()); - MockProcess_Reset(); - - // Test a process with a service UID, valid parent pid, but proc_find fails to find parent pid - MockCalls::Clear(); - MockProcess_AddContext(context, 500 /*pid*/); - MockProcess_SetSelfInfo(500, "Test"); - MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/); - MockProcess_AddProcess(500 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/); - XCTAssertFalse(CurrentProcessIsAllowedToHydrate()); - XCTAssertTrue(MockCalls::DidCallFunction(KextMessageLogged, KEXTLOG_ERROR)); - MockProcess_Reset(); - - // 'sudo' scenario: Root process with non-root parent - MockProcess_AddContext(context, 502 /*pid*/); - MockProcess_SetSelfInfo(502, "Test"); - MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/); - MockProcess_AddCredential(2 /*credentialId*/, 501 /*UID*/); - MockProcess_AddProcess(502 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/); - MockProcess_AddProcess(501 /*pid*/, 2 /*credentialId*/, 1 /*ppid*/, "test" /*name*/); - XCTAssertTrue(CurrentProcessIsAllowedToHydrate()); - MockProcess_Reset(); - - // Process and it's parent are service users - MockProcess_AddContext(context, 502 /*pid*/); - MockProcess_SetSelfInfo(502, "Test"); - MockProcess_AddCredential(1 /*credentialId*/, 1 /*UID*/); - MockProcess_AddCredential(2 /*credentialId*/, 2 /*UID*/); - MockProcess_AddProcess(502 /*pid*/, 1 /*credentialId*/, 501 /*ppid*/, "test" /*name*/); - MockProcess_AddProcess(501 /*pid*/, 2 /*credentialId*/, 1 /*ppid*/, "test" /*name*/); - XCTAssertFalse(CurrentProcessIsAllowedToHydrate()); -} - -- (void)testUseMainForkIfNamedStream { - shared_ptr testMount = mount::Create(); - const char* filePath = "/Users/test/code/Repo/file"; - shared_ptr testFileVnode = testMount->CreateVnodeTree(filePath); - - // Out Parameters to test - bool putVnodeWhenDone; - vnode_t testVnode = testFileVnode.get(); - - testVnode->namedStream = false; - UseMainForkIfNamedStream(testVnode, putVnodeWhenDone); - XCTAssertFalse(putVnodeWhenDone); - XCTAssertTrue(testVnode == testFileVnode.get()); - - testVnode->namedStream = true; - UseMainForkIfNamedStream(testVnode, putVnodeWhenDone); - XCTAssertTrue(putVnodeWhenDone); - XCTAssertTrue(testVnode == testFileVnode->GetParentVnode().get()); - vnode_put(testVnode); -} - -- (void)testResizePendingRenamesRace -{ - InitPendingRenames(); - XCTAssertLessThan(s_maxPendingRenames, 16); - ResizePendingRenames(16); - ResizePendingRenames(16); - XCTAssertEqual(s_maxPendingRenames, 16); - CleanupPendingRenames(); -} - -- (void)testResizePendingRenamesLogsLargeArrayError -{ - shared_ptr testMount = mount::Create(); - string filePath = "/Users/test/code/Repo/file"; - shared_ptr testFile = testMount->CreateVnodeTree(filePath); - - InitPendingRenames(); - ResizePendingRenames(16); - s_pendingRenameCount = 16; // pretend we're full - XCTAssertFalse(MockCalls::DidCallFunction(KextMessageLogged, KEXTLOG_ERROR)); - RecordPendingRenameOperation(testFile.get()); - XCTAssertTrue(MockCalls::DidCallFunction(KextMessageLogged, KEXTLOG_ERROR)); - XCTAssertEqual(s_pendingRenameCount, 17); - XCTAssertGreaterThan(s_maxPendingRenames, 16); - XCTAssertTrue(DeleteOpIsForRename(testFile.get())); - XCTAssertEqual(s_pendingRenameCount, 16); - s_pendingRenameCount = 0; - CleanupPendingRenames(); -} - -- (void)testPendingRenamesOutOfOrderInsertAndRemoval -{ - shared_ptr testMount = mount::Create(); - string file1Path = "/Users/test/code/Repo/file1"; - string file2Path = "/Users/test/code/Repo/file2"; - shared_ptr testFile1 = testMount->CreateVnodeTree(file1Path); - shared_ptr testFile2 = testMount->CreateVnodeTree(file2Path); - - InitPendingRenames(); - RecordPendingRenameOperation(testFile1.get()); - MockProcess_SetCurrentThreadIndex(1); - RecordPendingRenameOperation(testFile2.get()); - MockProcess_SetCurrentThreadIndex(0); - XCTAssertTrue(DeleteOpIsForRename(testFile1.get())); - MockProcess_SetCurrentThreadIndex(1); - XCTAssertTrue(DeleteOpIsForRename(testFile2.get())); - MockProcess_SetCurrentThreadIndex(0); - XCTAssertEqual(s_pendingRenameCount, 0); - CleanupPendingRenames(); -} - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/KextAssertIntegration.h b/ProjFS.Mac/PrjFSKextTests/KextAssertIntegration.h deleted file mode 100644 index ea2bda84de..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/KextAssertIntegration.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#import - -// XCTestCase subclass that registers itself with the kext assert integration system -@interface PFSKextTestCase : XCTestCase - -- (void) setUp; -- (void) tearDown; - -- (void) setExpectedFailedKextAssertionCount:(uint32_t)failedAssertionCount; - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/KextAssertIntegration.m b/ProjFS.Mac/PrjFSKextTests/KextAssertIntegration.m deleted file mode 100644 index 0d1af0b571..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/KextAssertIntegration.m +++ /dev/null @@ -1,90 +0,0 @@ -#import "KextAssertIntegration.h" -#include - -extern void Assert(const char* file, unsigned line, const char* expression); - -static pthread_key_t s_currentTestCaseKey; - -static PFSKextTestCase* CurrentTestCase() -{ - return (__bridge PFSKextTestCase*)pthread_getspecific(s_currentTestCaseKey); -} - -@implementation PFSKextTestCase -{ - uint32_t expectedKextAssertionFailures; -} - -+ (void) initialize -{ - pthread_key_create(&s_currentTestCaseKey, NULL); -} - -- (void) setUp -{ - pthread_setspecific(s_currentTestCaseKey, (__bridge void*)self); -} - -- (void) tearDown -{ - XCTAssertEqual(self->expectedKextAssertionFailures, 0, "Expected assertions apparently did not occur"); - self->expectedKextAssertionFailures = 0; - pthread_setspecific(s_currentTestCaseKey, NULL); -} - -- (void) setExpectedFailedKextAssertionCount:(uint32_t)failedAssertionCount -{ - self->expectedKextAssertionFailures = failedAssertionCount; -} - -- (bool) isKextAssertionFailureExpected -{ - if (self->expectedKextAssertionFailures > 0) - { - --self->expectedKextAssertionFailures; - return true; - } - else - { - return false; - } -} - -@end - -// catches kext assert() -void Assert(const char* file, unsigned line, const char* expression) -{ - PFSKextTestCase* test = CurrentTestCase(); - bool expected = [test isKextAssertionFailureExpected]; - if (expected) - { - return; - } - - NSString* expressionString = [NSString stringWithCString:expression encoding:NSUTF8StringEncoding]; - _XCTFailureHandler(test, YES, file, line, expressionString, @""); -} - -// catches kext assertf() - currently no source file & line number support -void panic(const char* format, ...) -{ - PFSKextTestCase* test = CurrentTestCase(); - bool expected = [test isKextAssertionFailureExpected]; - if (expected) - { - return; - } - - va_list arguments; - char* panicString = NULL; - - va_start(arguments, format); - vasprintf(&panicString, format, arguments); - va_end(arguments); - - NSString* panicStringObj = [NSString stringWithUTF8String:panicString]; - free(panicString); - - _XCTFailureHandler(test, YES, "unknown file", 0, panicStringObj, @""); -} diff --git a/ProjFS.Mac/PrjFSKextTests/KextLogMock.cpp b/ProjFS.Mac/PrjFSKextTests/KextLogMock.cpp deleted file mode 100644 index 4ef638e309..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/KextLogMock.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "KextMockUtilities.hpp" -#include "KextLogMock.h" - -const void* KextLog_Unslide(const void* pointer) -{ - return pointer; -} - -void KextMessageLogged(KextLog_Level level) -{ -} - -void KextLog_Printf(KextLog_Level level, const char* format, ...) -{ - MockCalls::RecordFunctionCall( - KextMessageLogged, - level); -} - diff --git a/ProjFS.Mac/PrjFSKextTests/KextLogMock.h b/ProjFS.Mac/PrjFSKextTests/KextLogMock.h deleted file mode 100644 index 934050dc9f..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/KextLogMock.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "../PrjFSKext/KextLog.hpp" - -// These functions are used for tracking logging in MockCalls -void KextMessageLogged(KextLog_Level level); diff --git a/ProjFS.Mac/PrjFSKextTests/KextMockUtilities.cpp b/ProjFS.Mac/PrjFSKextTests/KextMockUtilities.cpp deleted file mode 100644 index 2dafcb101e..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/KextMockUtilities.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "KextMockUtilities.hpp" - -MockCalls MockCalls::singleton; -namespace KextMock -{ - PlaceholderValue _; -} - -void MockCalls::Clear() -{ - singleton.nextCallSequenceNumber = 0; - for (FunctionCallRecorder* typeRegister : singleton.functionTypeCallRecorders) - { - typeRegister->Clear(); - } -} diff --git a/ProjFS.Mac/PrjFSKextTests/KextMockUtilities.hpp b/ProjFS.Mac/PrjFSKextTests/KextMockUtilities.hpp deleted file mode 100644 index 498e854871..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/KextMockUtilities.hpp +++ /dev/null @@ -1,271 +0,0 @@ -#pragma once - -#include -#include - -namespace KextMock -{ - struct PlaceholderValue - {}; - - // Matches with any parameter value - // ParameterPlaceholderValue can be used: - // - When calling kext functions for parameter values that do not matter to the test - // - When checking if a function was called (i.e. DidCallFunction). ParameterPlaceholderValue - // is a match for all parameter values. - extern PlaceholderValue _; -}; - -class FunctionCallRecorder -{ -public: - virtual void Clear() = 0; - virtual ~FunctionCallRecorder() {} -}; - -template - class SpecificFunctionCallRecorder : public FunctionCallRecorder -{ - typedef R (*FunctionPointerType)(ARGS...); - typedef std::tuple::type...> ArgumentValueTuple; - - struct FunctionCall - { - int64_t callSequenceNumber; - std::unique_ptr argumentValues; - }; - - typedef std::unordered_multimap RecordedCallMap; - - RecordedCallMap recordedCalls; - static SpecificFunctionCallRecorder functionTypeRegister; - - void RecordFunctionCall(FunctionPointerType function, int64_t sequenceNumber, std::unique_ptr&& argumentValues); - bool DidCallFunction(FunctionPointerType function); - template - bool DidCallFunction(FunctionPointerType function, CHECK_ARGS... checkArgs); - int CallCount(FunctionPointerType function); - int64_t EarliestSequenceNumberForCallMatching(FunctionPointerType function, int64_t sequenceNumberGreaterThan); - template - int64_t EarliestSequenceNumberForCallMatching(FunctionPointerType function, int64_t sequenceNumberGreaterThan, const std::tuple& checkArgTuple); - - virtual void Clear() override - { - this->recordedCalls.clear(); - } - - friend class MockCalls; -}; - -template - SpecificFunctionCallRecorder SpecificFunctionCallRecorder::functionTypeRegister; - -template - void SpecificFunctionCallRecorder::RecordFunctionCall(FunctionPointerType function, int64_t sequenceNumber, std::unique_ptr&& argumentValues) -{ - this->recordedCalls.insert(std::make_pair(function, FunctionCall { sequenceNumber, std::move(argumentValues) })); -} - -template - bool SpecificFunctionCallRecorder::DidCallFunction(FunctionPointerType function) -{ - return this->EarliestSequenceNumberForCallMatching(function, -1) > -1; -} - -template - int64_t SpecificFunctionCallRecorder::EarliestSequenceNumberForCallMatching(FunctionPointerType function, int64_t sequenceNumberGreaterThan) -{ - std::pair foundCalls = this->recordedCalls.equal_range(function); - for (typename RecordedCallMap::const_iterator foundCall = foundCalls.first; foundCall != foundCalls.second; ++foundCall) - { - if (foundCall->second.callSequenceNumber > sequenceNumberGreaterThan) - { - return foundCall->second.callSequenceNumber; - } - } - - return sequenceNumberGreaterThan; -} - - -class MockCalls -{ - std::unordered_set functionTypeCallRecorders; - int64_t nextCallSequenceNumber = 0; - - static MockCalls singleton; - -public: - template - static void RecordFunctionCall(R (*fn)(ARGS...), ACTUAL_ARGS... args) - { - singleton.functionTypeCallRecorders.insert(&SpecificFunctionCallRecorder::functionTypeRegister); - - // Makes a copy of all arguments (even if they were passed by reference; pointers stay pointers, however) - typedef std::tuple::type...> ArgumentValueTuple; - std::unique_ptr argumentValues(std::make_unique(args...)); - - SpecificFunctionCallRecorder::functionTypeRegister.RecordFunctionCall(fn, singleton.nextCallSequenceNumber++, std::move(argumentValues)); - } - - - static void Clear(); - - static bool DidCallAnyFunctions() - { - return singleton.nextCallSequenceNumber > 0; - } - - template - static int CallCount(R (*fn)(ARGS...)) - { - return SpecificFunctionCallRecorder::functionTypeRegister.CallCount(fn); - } - - template - static bool DidCallFunction(R (*fn)(ARGS...)) - { - return SpecificFunctionCallRecorder::functionTypeRegister.DidCallFunction(fn); - } - - template - static bool DidCallFunction(R (*fn)(ARGS...), CHECK_ARGS... checkArgs) - { - return SpecificFunctionCallRecorder::functionTypeRegister.DidCallFunction(fn, checkArgs...); - } - - static bool FunctionCallOrderCheck(int64_t sequenceNumberGreaterThan) - { - return true; - } - - template - static int64_t EarliestSequenceNumberForCallMatching(int64_t sequenceNumberGreaterThan, R (*fn)(ARGS...)) - { - return SpecificFunctionCallRecorder::functionTypeRegister.EarliestSequenceNumberForCallMatching(fn, sequenceNumberGreaterThan); - } - - template - static int64_t EarliestSequenceNumberForCallMatching(int64_t sequenceNumberGreaterThan, R (*fn)(ARGS...), const std::tuple& checkArgTuple) - { - return SpecificFunctionCallRecorder::functionTypeRegister.EarliestSequenceNumberForCallMatching(fn, sequenceNumberGreaterThan, checkArgTuple); - } - - template - static bool FunctionCallOrderCheck(int64_t sequenceNumberGreaterThan, FN_T* function1, FN_TYPES... functions) - { - int64_t callSequenceNumber = MockCalls::EarliestSequenceNumberForCallMatching(sequenceNumberGreaterThan, function1); - if (callSequenceNumber > sequenceNumberGreaterThan) - { - return FunctionCallOrderCheck(callSequenceNumber, functions...); - } - else - { - return false; - } - } - - template - static bool FunctionCallOrderCheck( - int64_t sequenceNumberGreaterThan, - FN_T* function1, - const std::tuple& function1ArgumentTuple, - FN_TYPES... functions) - { - int64_t callSequenceNumber = MockCalls::EarliestSequenceNumberForCallMatching(sequenceNumberGreaterThan, function1, function1ArgumentTuple); - if (callSequenceNumber > sequenceNumberGreaterThan) - { - return FunctionCallOrderCheck(callSequenceNumber, functions...); - } - else - { - return false; - } - } - - template - static bool FunctionCallOrderCheck( - int64_t sequenceNumberGreaterThan, - FN_T* function1, - KextMock::PlaceholderValue, - FN_TYPES... functions) - { - int64_t callSequenceNumber = MockCalls::EarliestSequenceNumberForCallMatching(sequenceNumberGreaterThan, function1); - if (callSequenceNumber > sequenceNumberGreaterThan) - { - return FunctionCallOrderCheck(callSequenceNumber, functions...); - } - else - { - return false; - } - } - - template - static bool DidCallFunctionsInOrder(FN_TYPES... functions) - { - return MockCalls::FunctionCallOrderCheck(-1, functions...); - } -}; - -template -bool ArgumentIsEqual(ARG_T& argument, CHECK_T check) -{ - return argument == check; -} - -template -bool ArgumentIsEqual(ARG_T& argument, KextMock::PlaceholderValue) -{ - return true; -} - -template -bool ArgumentsAreEqual(TUPLE_T& arguments, CHECK_TUPLE_T check, std::index_sequence) -{ - return (ArgumentIsEqual(std::get(arguments), std::get(check)) && ...); -} - -template -template - bool SpecificFunctionCallRecorder::DidCallFunction(FunctionPointerType function, CHECK_ARGS... checkArgs) -{ - std::pair foundCalls = this->recordedCalls.equal_range(function); - for (typename RecordedCallMap::const_iterator foundCall = foundCalls.first; foundCall != foundCalls.second; ++foundCall) - { - if (foundCall->second.argumentValues) - { - if (ArgumentsAreEqual(*foundCall->second.argumentValues, std::forward_as_tuple(checkArgs...), std::index_sequence_for{})) - { - return true; - } - } - } - return false; -} - -template -int SpecificFunctionCallRecorder::CallCount(FunctionPointerType function) -{ - std::pair foundCalls = this->recordedCalls.equal_range(function); - return static_cast(std::distance(foundCalls.first, foundCalls.second)); -} - -template -template -int64_t SpecificFunctionCallRecorder::EarliestSequenceNumberForCallMatching(FunctionPointerType function, int64_t sequenceNumberGreaterThan, const std::tuple& checkArgTuple) -{ - std::pair foundCalls = this->recordedCalls.equal_range(function); - for (typename RecordedCallMap::const_iterator foundCall = foundCalls.first; foundCall != foundCalls.second; ++foundCall) - { - if (foundCall->second.argumentValues && foundCall->second.callSequenceNumber > sequenceNumberGreaterThan) - { - if (ArgumentsAreEqual(*foundCall->second.argumentValues, checkArgTuple, std::index_sequence_for{})) - { - return foundCall->second.callSequenceNumber; - } - } - } - - return sequenceNumberGreaterThan; -} diff --git a/ProjFS.Mac/PrjFSKextTests/MemoryTests.mm b/ProjFS.Mac/PrjFSKextTests/MemoryTests.mm deleted file mode 100644 index 25c315d7c1..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/MemoryTests.mm +++ /dev/null @@ -1,21 +0,0 @@ -#import "KextAssertIntegration.h" -#include "../PrjFSKext/Memory.hpp" - -@interface MemoryTests : PFSKextTestCase - -@end - -@implementation MemoryTests - -- (void)testAllocArrayReturnsNullOnOverflow { - long* array = Memory_AllocArray(UINT32_MAX); - XCTAssert(array == nullptr); -} - -- (void)testAllocArrayWorksOnNoOverflow { - long* array = Memory_AllocArray(10); - XCTAssert(array != nullptr); - Memory_FreeArray(array, 10); -} - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/MessageTests.mm b/ProjFS.Mac/PrjFSKextTests/MessageTests.mm deleted file mode 100644 index 6c4a3ac3f8..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/MessageTests.mm +++ /dev/null @@ -1,47 +0,0 @@ -#import "KextAssertIntegration.h" - -#include "../PrjFSKext/Message_Kernel.hpp" - -@interface MessageTests : PFSKextTestCase - -@end - -@implementation MessageTests - -- (void)setUp -{ - [super setUp]; -} - -- (void)tearDown -{ - [super tearDown]; -} - -- (void)testMessageEncodedSize -{ - MessageHeader testHeader = {}; - XCTAssertEqual(sizeof(testHeader), Message_EncodedSize(&testHeader)); - - testHeader.pathSizesBytes[MessagePath_Target] = UINT16_MAX; - testHeader.pathSizesBytes[MessagePath_From] = UINT16_MAX; - - static const uint64_t maxSize = sizeof(testHeader) + UINT16_MAX * 2llu; - XCTAssertEqual(maxSize, Message_EncodedSize(&testHeader)); - - srand(0); - for (unsigned i = 0; i < 10000; ++i) - { - uint16_t targetSize = testHeader.pathSizesBytes[MessagePath_Target] = rand() & UINT16_MAX; - uint16_t fromSize = testHeader.pathSizesBytes[MessagePath_From] = rand() & UINT16_MAX; - uint32_t size = Message_EncodedSize(&testHeader); - XCTAssertGreaterThanOrEqual(size, sizeof(testHeader)); - XCTAssertLessThanOrEqual(size, maxSize); - XCTAssertGreaterThan(size, targetSize); - XCTAssertGreaterThan(size, fromSize); - XCTAssertGreaterThanOrEqual(size, sizeof(testHeader) + targetSize + fromSize); - } - -} - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/MockPerfTracing.cpp b/ProjFS.Mac/PrjFSKextTests/MockPerfTracing.cpp deleted file mode 100644 index ad3257b71c..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/MockPerfTracing.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "../PrjFSKext/PerformanceTracing.hpp" -#include - -#if PRJFS_PERFORMANCE_TRACING_ENABLE -uint64_t PerfTracer::s_numTracers = 0; -#endif - -void PerfTracing_RecordSample(PrjFSPerfCounter counter, uint64_t startTime, uint64_t endTime) -{ -} - diff --git a/ProjFS.Mac/PrjFSKextTests/MockProc.cpp b/ProjFS.Mac/PrjFSKextTests/MockProc.cpp deleted file mode 100644 index 3f7fd938c7..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/MockProc.cpp +++ /dev/null @@ -1,184 +0,0 @@ -#include "MockProc.hpp" -#include -#include - -struct thread -{ -}; - -using std::make_pair; -using std::map; -using std::string; - -static map s_credentialMap; -static map s_contextMap; -static map s_processMap; -static int s_selfPid; -static string s_selfName; -static uint16_t s_currentThreadIndex = 0; -static thread s_threadPool[MockProcess_ThreadPoolSize] = {}; - -void MockProcess_Reset() -{ - s_processMap.clear(); - s_credentialMap.clear(); - s_contextMap.clear(); - MockProcess_SetCurrentThreadIndex(0); -} - -void MockProcess_SetSelfInfo(int selfPid, const string& selfName) -{ - s_selfPid = selfPid; - s_selfName = selfName; -} - -int proc_pid(proc_t proc) -{ - return proc->pid; -} - -void proc_name(int pid, char* buf, int size) -{ - map::const_iterator found = s_processMap.find(pid); - if (found == s_processMap.end()) - { - assert("pid not found in s_processMap"); - } - else - { - strlcpy(buf, found->second.name.c_str(), size); - } -} - -proc_t vfs_context_proc(vfs_context_t ctx) -{ - map::const_iterator foundPidId = s_contextMap.find(ctx); - if (foundPidId == s_contextMap.end()) - { - assert("ctx not found in s_contextMap"); - return nullptr; - } - - return proc_find(foundPidId->second); -} - -int vfs_context_pid(vfs_context_t ctx) -{ - return proc_pid(vfs_context_proc(ctx)); -} - -proc_t proc_self(void) -{ - map::iterator selfIter = s_processMap.find(s_selfPid); - if (selfIter == s_processMap.end()) - { - assert("pid not found in s_processMap"); - return nullptr; - } - else - { - return &(selfIter->second); - } -} - -kauth_cred_t kauth_cred_proc_ref(proc_t procp) -{ - map::const_iterator found = s_processMap.find(procp->pid); - if (found == s_processMap.end()) - { - assert("pid not found in s_processMap"); - return nullptr; - } - else - { - return reinterpret_cast(found->second.credentialId); - } -} - -uid_t kauth_cred_getuid(kauth_cred_t _cred) -{ - map::const_iterator found = s_credentialMap.find(reinterpret_cast(_cred)); - if (found == s_credentialMap.end()) - { - assert("uid not found in s_credentialMap"); - return -1; - } - else - { - return found->second; - } -} - -void kauth_cred_unref(kauth_cred_t *_cred) -{ -} - -int proc_ppid(proc_t p) -{ - map::const_iterator found = s_processMap.find(p->pid); - if (found == s_processMap.end()) - { - assert("pid not found in s_processMap"); - return -1; - } - else - { - return found->second.ppid; - } -} - -proc_t proc_find(int pid) -{ - map::iterator procIter = s_processMap.find(pid); - if (procIter == s_processMap.end()) - { - assert("pid not found in s_processMap"); - return nullptr; - } - else - { - return &(procIter->second); - } -} - -int proc_rele(proc_t p) -{ - return 1; -} - -int proc_selfpid(void) -{ - return s_selfPid; -} - -void proc_selfname(char* buf, int size) -{ - strlcpy(buf, s_selfName.c_str(), size); -} - -void MockProcess_AddCredential(uintptr_t credentialId, uid_t UID) -{ - s_credentialMap.insert(make_pair(credentialId, UID)); -} - -void MockProcess_AddContext(vfs_context_t contextId, int pid) -{ - s_contextMap.insert(make_pair(contextId, pid)); -} - -void MockProcess_AddProcess(int pid, uintptr_t credentialId, int ppid, string name) -{ - proc process = proc {pid, credentialId, ppid, name}; - s_processMap.insert(make_pair(pid, process)); -} - -kernel_thread_t current_thread() -{ - return &s_threadPool[s_currentThreadIndex]; -} - -void MockProcess_SetCurrentThreadIndex(uint16_t threadIndex) -{ - assert(threadIndex < MockProcess_ThreadPoolSize); - s_currentThreadIndex = threadIndex; -} diff --git a/ProjFS.Mac/PrjFSKextTests/MockProc.hpp b/ProjFS.Mac/PrjFSKextTests/MockProc.hpp deleted file mode 100644 index e81ad20e27..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/MockProc.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "../PrjFSKext/kernel-header-wrappers/vnode.h" -#include - -// In the kernel/kext this type is "thread_t" but there already exists a -// conflicting Mach type in user space hence the name change. -typedef struct thread* kernel_thread_t; - -// Kernel functions that are being mocked -extern "C" -{ - int proc_pid(proc_t); - void proc_name(int pid, char* buf, int size); - proc_t vfs_context_proc(vfs_context_t ctx); - int vfs_context_pid(vfs_context_t ctx); - proc_t proc_self(void); - kauth_cred_t kauth_cred_proc_ref(proc_t procp); - uid_t kauth_cred_getuid(kauth_cred_t _cred); - void kauth_cred_unref(kauth_cred_t *_cred); - int proc_ppid(proc_t); - proc_t proc_find(int pid); - int proc_rele(proc_t p); - int proc_selfpid(void); - kernel_thread_t current_thread(void); - void proc_selfname(char* buf, int size); -} - -struct proc { - int pid; - uintptr_t credentialId; - int ppid; - std::string name; -}; - -void MockProcess_SetSelfInfo(int selfPid, const std::string& selfName); -void MockProcess_AddCredential(uintptr_t credentialId, uid_t UID); -void MockProcess_AddContext(vfs_context_t context, int pid); -void MockProcess_AddProcess(int pid, uintptr_t credentialId, int ppid, std::string procName); -void MockProcess_Reset(); - -// Tests will only need to simulate a small number of threads, so we provide an existing, indexed pool. -constexpr uint16_t MockProcess_ThreadPoolSize = 2; -void MockProcess_SetCurrentThreadIndex(uint16_t threadIndex); diff --git a/ProjFS.Mac/PrjFSKextTests/MockVnodeAndMount.cpp b/ProjFS.Mac/PrjFSKextTests/MockVnodeAndMount.cpp deleted file mode 100644 index 24b57d7ce3..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/MockVnodeAndMount.cpp +++ /dev/null @@ -1,388 +0,0 @@ -#include "MockVnodeAndMount.hpp" -#include "../PrjFSKext/VnodeUtilities.hpp" -#include "KextMockUtilities.hpp" -#include -#include - -using std::string; -using std::unordered_map; -using std::shared_ptr; -using std::weak_ptr; -using std::pair; -using std::vector; -using std::min; - -typedef unordered_map> PathToVnodeMap; -typedef unordered_map> WeakVnodeMap; - -static PathToVnodeMap s_vnodesByPath; -static WeakVnodeMap s_allVnodes; - -shared_ptr mount::Create(const char* fileSystemTypeName, fsid_t fsid, uint64_t initialInode) -{ - shared_ptr result(new mount{}); - result->weakSelfPointer = result; - assert(strlen(fileSystemTypeName) + 1 < sizeof(result->statfs.f_fstypename)); - result->statfs.f_fsid = fsid; - result->nextInode = initialInode; - strlcpy(result->statfs.f_fstypename, fileSystemTypeName, sizeof(result->statfs.f_fstypename)); - - return result; -} - - -vnode::vnode(const shared_ptr& mount) : - mountPoint(mount), - name(nullptr), - inode(mount != nullptr ? mount->nextInode++ : 0) -{ -} - -vnode::vnode(const std::shared_ptr& mount, VnodeCreationProperties properties) : - mountPoint(mount), - name(nullptr), - inode(properties.inode == UINT64_MAX ? mount->nextInode++ : properties.inode), - type(properties.type), - parent(properties.parent) -{ -} - -vnode::~vnode() -{ - assert(this->ioCount == 0); -} - -static string ParentPathString(const string& path) -{ - assert(path.length() > 0); - size_t lastSlashPos = path.find_last_of('/'); - assert(lastSlashPos != string::npos); - if (lastSlashPos == 0) - { - return "/"; - } - else if (lastSlashPos == path.length() - 1) // path ends in "/" - { - lastSlashPos = path.find_last_of('/', lastSlashPos - 1); - } - - return path.substr(0, lastSlashPos); -} - -// This creates a vnode at the given path below the mount point, and ensures -// that a hierarchy of directory vnodes exists all the way to the root. -// -// For example: -// mount->CreateVnodeTree("/path/to/file", VREG) will produce: -// vnode VREG "/path/to/file" -// vnode VDIR "/path/to" -// vnode VDIR "/path" -// vnode VDIR "/" -// While walking up the path, we stop creating vnodes if one already exists for -// a particular directory. For example, if the example above is followed by: -// mount->CreateVnodeTree("/path/for/other/file", VREG) will only generate: -// vnode VREG "/path/for/other/file" -// vnode VDIR "/path/for/other" -// vnode VDIR "/path/for" -// Because the vnode generated for "/path" in the previous example will be -// re-used as the parent for the "/path/for" vnode. -shared_ptr mount::CreateVnodeTree(const string& path, vtype vnodeType) -{ - assert(path[0] == '/'); // Only absolute paths allowed - shared_ptr parentVnode; - - if (path != "/") - { - string parentPath = ParentPathString(path); - PathToVnodeMap::const_iterator found = s_vnodesByPath.find(parentPath); - - if (found != s_vnodesByPath.end()) - { - parentVnode = found->second.lock(); - } - - if (!parentVnode) - { - parentVnode = this->CreateVnodeTree(parentPath, VDIR); - } - } - - shared_ptr fileVnode = vnode::Create(this->weakSelfPointer.lock(), path.c_str(), vnodeType); - fileVnode->parent = parentVnode; - - if (path == "/") - { - this->rootVnode = fileVnode; - } - - return fileVnode; -} - -std::shared_ptr mount::CreateVnode(std::string path, VnodeCreationProperties properties) -{ - uint64_t inode = properties.inode; - if (inode == UINT64_MAX) - { - inode = this->nextInode++; - } - shared_ptr newVnode(new vnode(this->weakSelfPointer.lock(), properties)); - s_allVnodes.insert(make_pair(newVnode.get(), weak_ptr(newVnode))); - newVnode->weakSelfPointer = newVnode; - newVnode->SetAndRegisterPath(path); - return newVnode; -} - -shared_ptr vnode::Create(const shared_ptr& mount, const char* path, vtype vnodeType) -{ - shared_ptr result(new vnode(mount)); - s_allVnodes.insert(make_pair(result.get(), weak_ptr(result))); - result->weakSelfPointer = result; - result->SetAndRegisterPath(path); - result->type = vnodeType; - return result; -} - -void vnode::StartRecycling() -{ - s_vnodesByPath.erase(this->path); - this->path.clear(); - this->type = VBAD; - this->vid++; - this->parent.reset(); - this->isRecycling = true; -} - -void vnode::SetAndRegisterPath(const string& newPath) -{ - s_vnodesByPath.erase(this->path); - - this->path = newPath; - size_t lastSlash = this->path.rfind('/'); - if (lastSlash == string::npos) - { - this->name = this->path.c_str(); - } - else - { - this->name = this->path.c_str() + lastSlash + 1; - } - - // Any existing vnodes for that path should be destroyed/recycled by now - assert(!s_vnodesByPath[newPath].lock()); - s_vnodesByPath[newPath] = this->weakSelfPointer; -} - -int vnode_isrecycled(vnode_t vnode) -{ - return vnode->IsRecycling(); -} - -const char* vnode_getname(vnode_t vnode) -{ - return vnode->GetName(); -} - -void vnode_putname(const char* name) -{ - // TODO: track name reference counts -} - -int vnode_getattr(vnode_t vp, struct vnode_attr* vap, vfs_context_t ctx) -{ - if (vp->errors.getattr != 0) - { - return vp->errors.getattr; - } - - if (VATTR_IS_ACTIVE(vap, va_flags)) - { - VATTR_RETURN(vap, va_flags, vp->attrValues.va_flags); - } - - return 0; -} - -int vnode_isdir(vnode_t vnode) -{ - return vnode_vtype(vnode) == VDIR; -} - -int vn_getpath(vnode_t vnode, char* pathBuffer, int* pathLengthInOut) -{ - assert(*pathLengthInOut >= MAXPATHLEN); - if (vnode->errors.getpath != 0) - { - return vnode->errors.getpath; - } - else if (vnode->path.empty() || vnode->isRecycling) - { - // TODO: check what the real vn_getpath() would return here - return EIO; - } - else - { - strlcpy(pathBuffer, vnode->path.c_str(), MIN(*pathLengthInOut, MAXPATHLEN)); - return 0; - } -} - -// TODO: Perhaps switch to the real version of this function and mock the KPIs it uses -FsidInode Vnode_GetFsidAndInode(vnode_t vnode, vfs_context_t vfsContext, bool useLinkIDForInode) -{ - // TODO: extend vnode mock to distinguish betweeen fileid and linkid? - return FsidInode{ vnode->GetMountPoint()->GetFsid(), vnode->GetInode() }; -} - -errno_t vnode_lookup(const char* path, int flags, vnode_t* foundVnode, vfs_context_t vfsContext) -{ - PathToVnodeMap::const_iterator found = s_vnodesByPath.find(path); - if (found == s_vnodesByPath.end()) - { - return ENOENT; - } - else if (shared_ptr vnode = found->second.lock()) - { - // vnode_lookup returns a vnode with an iocount - errno_t error = vnode->RetainIOCount(); - if (error == 0) - { - *foundVnode = vnode.get(); - } - - return 0; - } - else - { - s_vnodesByPath.erase(found); - return ENOENT; - } -} - -uint32_t vnode_vid(vnode_t vnode) -{ - return vnode->GetVid(); -} - -vtype vnode_vtype(vnode_t vnode) -{ - return vnode->GetVnodeType(); -} - -mount_t vnode_mount(vnode_t vnode) -{ - return vnode->GetMountPoint(); -} - -void vnode::ReleaseIOCount() -{ - assert(this->ioCount > 0); - --this->ioCount; -} - -int vnode_put(vnode_t vnode) -{ - vnode->ReleaseIOCount(); - return 0; -} - -int vnode_get(vnode_t vnode) -{ - return vnode->RetainIOCount(); -} - -errno_t vnode::RetainIOCount() -{ - if (this->ioCount > 0 || !this->isRecycling) - { - ++this->ioCount; - return 0; - } - else - { - // TODO: check what real vnode_get returns for recycled vnodes - return EBADF; - } -} - - -vfsstatfs* vfs_statfs(mount_t mountPoint) -{ - return &mountPoint->statfs; -} - -vfs_context_t vfs_context_create(vfs_context_t contextToClone) -{ - return nullptr; -} - -int vfs_context_rele(vfs_context_t vfsContext) -{ - return 0; -} - - -void vfs_setauthcache_ttl(mount_t mountPoint, int ttl) -{ - MockCalls::RecordFunctionCall(vfs_setauthcache_ttl, mountPoint, ttl); -} - -void MockVnodes_CheckAndClear() -{ - // All of the vnodes in s_allVnodes should have been destroyed by the time MockVnodes_CheckAndClear is called - for (WeakVnodeMap::const_iterator cur = s_allVnodes.begin(); cur != s_allVnodes.end(); ++cur) - { - shared_ptr strong = cur->second.lock(); - assert(!strong); - } - - s_allVnodes.clear(); -} - -vnode::BytesOrError vnode::ReadXattr(const char* xattrName) -{ - // TODO: add support for explicit error mocking - XattrMap::const_iterator found = this->xattrs.find(xattrName); - if (found == this->xattrs.end()) - { - return BytesOrError{ .error = ENOATTR }; - } - else - { - return BytesOrError{ .error = 0, .bytes = found->second }; - } -} - -SizeOrError Vnode_ReadXattr(vnode_t _Nonnull vnode, const char* _Nonnull xattrName, void* _Nullable buffer, size_t bufferSize) -{ - vnode::BytesOrError xattrResult = vnode->ReadXattr(xattrName); - if (xattrResult.error == 0) - { - memcpy(buffer, xattrResult.bytes.data(), min(xattrResult.bytes.size(), bufferSize)); - return SizeOrError { xattrResult.bytes.size(), 0 }; - } - else - { - return SizeOrError { 0, xattrResult.error }; - } -} - -vnode_t vnode_getparent(vnode_t vnode) -{ - shared_ptr parentVnode = vnode->GetParentVnode(); - if (parentVnode) - { - parentVnode->RetainIOCount(); - } - - return parentVnode.get(); -} - -int vnode_isvroot(vnode_t vnode) -{ - return vnode->GetMountPoint()->GetRootVnode().get() == vnode; -} - -int vnode_isnamedstream(vnode_t vp) -{ - return vp->namedStream ? 1 : 0; -} diff --git a/ProjFS.Mac/PrjFSKextTests/MockVnodeAndMount.hpp b/ProjFS.Mac/PrjFSKextTests/MockVnodeAndMount.hpp deleted file mode 100644 index d67d67a46a..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/MockVnodeAndMount.hpp +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once - -#include "../PrjFSKext/kernel-header-wrappers/vnode.h" -#include "../PrjFSKext/kernel-header-wrappers/mount.h" -#include "../PrjFSKext/public/FsidInode.h" -#include -#include -#include -#include - -// An aggregate for setting properties of newly created mock vnodes with sensible defaults. -struct VnodeCreationProperties -{ - vtype type = VREG; - // UINT64_MAX is a special value that causes the associated mount point to - // automatically assign an inode. Set this field to something else to - // explicitly choose an inode number. - uint64_t inode = UINT64_MAX; - std::shared_ptr parent; -}; - -// The struct names mount and vnode are dictated by mount_t and vnode_t's -// definitions as (opaque/forward declared) pointers to those structs. -// As the (testable) kext treats them as entirely opaque, we can implement -// them as we wish for purposes of testing. - -struct mount -{ -private: - vfsstatfs statfs; - uint64_t nextInode; - std::weak_ptr weakSelfPointer; - std::weak_ptr rootVnode; - -public: - static std::shared_ptr Create(const char* fileSystemTypeName = "hfs", fsid_t fsid = fsid_t{}, uint64_t initialInode = 0); - - std::shared_ptr CreateVnodeTree(const std::string& path, vtype vnodeType = VREG); - // By default, CreateVnode() will create a regular file with an - // auto-assigned inode and no existing parent vnode. - // See struct VnodeCreationProperties for changing this. - std::shared_ptr CreateVnode(std::string path, VnodeCreationProperties properties = VnodeCreationProperties{}); - - fsid_t GetFsid() const { return this->statfs.f_fsid; } - std::shared_ptr GetRootVnode() const { return this->rootVnode.lock(); } - - friend struct vnode; - friend vfsstatfs* vfs_statfs(mount_t mountPoint); -}; - -// On the subject of mock vnode lifetimes and memory management: -// -// To create a standalone mock vnode, use mount::CreateVnode(), if you need a -// full directory hierarchy, use mount::CreateVnodeTree(). -// Currently, vnode::Create() also exists and works, but it will soon be -// replaced by an equivalent mount::CreateVnode() overload. -// Don't attempt to create vnodes directly via operator new/constructors. -// -// Tests are expected to manage the lifetimes of mock vnodes they create by -// maintaining shared_ptr references. When all of these references go -// out of scope at the end of the test, this should cause all mock vnodes to -// be destroyed, so an extra sign of successful test execution is that there are -// no live vnodes left after the test. (Call MockVnodes_CheckAndClear() in -// tearDown) -// -// This is possible because vnodes will internally retain strong references to -// their registered parent vnode, but not the other way around, so there are no -// circular references. Similarly, and for the same reason, mock mount points do -// not hold strong references to mock vnodes, not even their root vnode, but -// vnodes hold strong references to their mount point. -// -// This way, we can have a mock mount point as a test class instance variable -// and don't have to manually clear out the rootVnode after every test. -// -struct VnodeMockErrors -{ - errno_t getpath = 0; - errno_t getattr = 0; -}; - -struct vnode -{ -private: - std::weak_ptr weakSelfPointer; - std::shared_ptr mountPoint; - std::shared_ptr parent; - -public: - typedef std::unordered_map> XattrMap; - XattrMap xattrs; - -private: - bool isRecycling = false; - vtype type = VREG; - uint64_t inode; - uint32_t vid; - int32_t ioCount = 0; - - std::string path; - const char* name; - - void SetAndRegisterPath(const std::string& path); - - explicit vnode(const std::shared_ptr& mount); - explicit vnode(const std::shared_ptr& mount, VnodeCreationProperties properties); - - vnode(const vnode&) = delete; - vnode& operator=(const vnode&) = delete; - -public: - static std::shared_ptr Create(const std::shared_ptr& mount, const char* path, vtype vnodeType = VREG); - ~vnode(); - - VnodeMockErrors errors = {}; - vnode_attr attrValues = {}; - bool namedStream = false; - - uint64_t GetInode() const { return this->inode; } - uint32_t GetVid() const { return this->vid; } - const char* GetName() const { return this->name; } - mount_t GetMountPoint() const { return this->mountPoint.get(); } - bool IsRecycling() const { return this->isRecycling; } - vtype GetVnodeType() const { return this->type; } - void SetVnodeType(vtype vtype) { this->type = vtype; } - std::shared_ptr const GetParentVnode() { return this->parent; } - - struct BytesOrError - { - errno_t error; - std::vector bytes; - }; - - BytesOrError ReadXattr(const char* xattrName); - - void StartRecycling(); - - errno_t RetainIOCount(); - void ReleaseIOCount(); - - friend struct mount; - friend int vnode_getattr(vnode_t vp, struct vnode_attr* vap, vfs_context_t ctx); - friend int vn_getpath(vnode_t vnode, char* pathBuffer, int* pathLengthInOut); - friend int vnode_isnamedstream(vnode_t vp); -}; - - -void MockVnodes_CheckAndClear(); - diff --git a/ProjFS.Mac/PrjFSKextTests/PrjFSKextTests.exp b/ProjFS.Mac/PrjFSKextTests/PrjFSKextTests.exp deleted file mode 100644 index dc33b33ab2..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/PrjFSKextTests.exp +++ /dev/null @@ -1,6 +0,0 @@ -# The explicit exported symbol list allows dead code stripping to work on the -# test bundle binary. - -_OBJC_CLASS_$* -_OBJC_METACLASS_$* - diff --git a/ProjFS.Mac/PrjFSKextTests/ProviderMessagingMock.cpp b/ProjFS.Mac/PrjFSKextTests/ProviderMessagingMock.cpp deleted file mode 100644 index 3b62a4cb46..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/ProviderMessagingMock.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "../PrjFSKext/ProviderMessaging.hpp" -#include "../PrjFSKext/Locks.hpp" -#include "../PrjFSKext/VirtualizationRoots.hpp" -#include "../PrjFSKext/kernel-header-wrappers/kauth.h" -#include "KextLogMock.h" -#include "KextMockUtilities.hpp" - -#include -#include -#include -#include "ProviderMessagingMock.hpp" -#include - -using std::function; -using std::move; - -bool static s_defaultRequestResult = true; -bool static s_secondRequestResult = true; -static function s_trySendRequestSideEffect; -int static s_requestCount = 0; - -bool ProviderMessaging_Init() -{ - return true; -} - - -void ProviderMessaging_Cleanup() -{ -} - -void ProviderMessageMock_SetDefaultRequestResult(bool success) -{ - s_defaultRequestResult = success; -} - -void ProviderMessageMock_SetRequestSideEffect(function sideEffectFunction) -{ - s_trySendRequestSideEffect = move(sideEffectFunction); -} - -void ProviderMessageMock_SetSecondRequestResult(bool secondRequestResult) -{ - s_secondRequestResult = secondRequestResult; -} - -void ProvidermessageMock_ResetResultCount() -{ - s_requestCount = 0; - s_trySendRequestSideEffect = nullptr; -} - -void ProviderMessaging_HandleKernelMessageResponse(VirtualizationRootHandle providerVirtualizationRootHandle, uint64_t messageId, MessageType responseType) -{ -} - -void ProviderMessaging_AbortOutstandingEventsForProvider(VirtualizationRootHandle providerVirtualizationRootHandle) -{ -} - -bool ProviderMessaging_TrySendRequestAndWaitForResponse( - VirtualizationRootHandle root, - MessageType messageType, - const vnode_t vnode, - const FsidInode& vnodeFsidInode, - const char* vnodePath, - const char* fromPath, - int pid, - const char* procname, - int* kauthResult, - int* kauthError) -{ - MockCalls::RecordFunctionCall( - ProviderMessaging_TrySendRequestAndWaitForResponse, - root, - messageType, - vnode, - vnodeFsidInode, - vnodePath, - fromPath, - pid, - procname, - kauthResult, - kauthError); - - if (s_trySendRequestSideEffect) - { - s_trySendRequestSideEffect(); - } - - s_requestCount++; - - bool result; - if (s_requestCount == 2) - { - result = s_secondRequestResult; - } - else - { - result = s_defaultRequestResult; - } - - *kauthResult = KAUTH_RESULT_DEFER; - if (result == false) - { - *kauthResult = KAUTH_RESULT_DENY; - } - - return result; -} - -void ProviderMessaging_AbortAllOutstandingEvents() -{ -} diff --git a/ProjFS.Mac/PrjFSKextTests/ProviderMessagingMock.hpp b/ProjFS.Mac/PrjFSKextTests/ProviderMessagingMock.hpp deleted file mode 100644 index 3584de9fe6..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/ProviderMessagingMock.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#include - -void ProvidermessageMock_ResetResultCount(); -void ProviderMessageMock_SetDefaultRequestResult(bool success); - -void ProviderMessageMock_SetRequestSideEffect(std::function sideEffectFunction); -void ProviderMessageMock_SetSecondRequestResult(bool secondRequestResult); diff --git a/ProjFS.Mac/PrjFSKextTests/ShouldHandleFileOpTests.mm b/ProjFS.Mac/PrjFSKextTests/ShouldHandleFileOpTests.mm deleted file mode 100644 index 8aa9226328..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/ShouldHandleFileOpTests.mm +++ /dev/null @@ -1,315 +0,0 @@ -#include "../PrjFSKext/kernel-header-wrappers/vnode.h" -#include "../PrjFSKext/KauthHandlerTestable.hpp" -#include "../PrjFSKext/PerformanceTracing.hpp" -#include "../PrjFSKext/VirtualizationRootsTestable.hpp" -#import "KextAssertIntegration.h" -#include "MockProc.hpp" -#include "MockVnodeAndMount.hpp" -#include "VnodeCacheEntriesWrapper.hpp" - -using std::shared_ptr; - -// Dummy implementation -class PrjFSProviderUserClient -{ -}; - -@interface ShouldHandleFileOpTests : PFSKextTestCase -@end - -@implementation ShouldHandleFileOpTests -{ - VnodeCacheEntriesWrapper cacheWrapper; - vfs_context_t context; - PerfTracer perfTracer; - PrjFSProviderUserClient userClient; - shared_ptr testMount; - std::string repoPath; - shared_ptr repoRootVnode; - shared_ptr testVnodeFile; -} - -- (void) setUp { - [super setUp]; - kern_return_t initResult = VirtualizationRoots_Init(); - XCTAssertEqual(initResult, KERN_SUCCESS); - self->cacheWrapper.AllocateCache(); - self->context = vfs_context_create(nullptr); - MockProcess_AddContext(self->context, 501 /*pid*/); - MockProcess_SetSelfInfo(501, "Test"); - MockProcess_AddProcess(501 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/); - - self->testMount = mount::Create(); - self->repoPath = "/Users/test/code/Repo"; - self->repoRootVnode = self->testMount->CreateVnodeTree(self->repoPath, VDIR); - self->testVnodeFile = self->testMount->CreateVnodeTree(self->repoPath + "/file.txt"); -} - -- (void) tearDown { - self->testVnodeFile.reset(); - self->repoRootVnode.reset(); - self->testMount.reset(); - MockProcess_Reset(); - self->cacheWrapper.FreeCache(); - MockVnodes_CheckAndClear(); - VirtualizationRoots_Cleanup(); - [super tearDown]; -} - -- (void)testRootHandle { - int pid; - VirtualizationRootHandle testRootHandle = RootHandle_None; - VirtualizationRootResult testRootResult; - - // Invalid Root Handle Test - XCTAssertFalse( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - self->repoRootVnode.get(), - nullptr, // path - KAUTH_FILEOP_RENAME, - true, // isDirectory, - &testRootHandle, - &pid)); - - - testRootResult = VirtualizationRoot_RegisterProviderForPath( - &self->userClient, - 0, - self->repoPath.c_str()); - XCTAssertEqual(0, testRootResult.error); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(testRootResult.root)); - - // With Valid Root Handle we should pass - XCTAssertTrue( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - self->repoRootVnode.get(), - nullptr, // path - KAUTH_FILEOP_RENAME, - true, // isDirectory, - &testRootHandle, - &pid)); - XCTAssertEqual(testRootHandle, testRootResult.root); - - if (VirtualizationRoot_IsValidRootHandle(testRootResult.root)) - { - ActiveProvider_Disconnect(testRootResult.root, &userClient); - } -} - -- (void)testUnsupportedFileSystem { - shared_ptr testVnode = vnode::Create(self->testMount, "/none"); - int pid; - VirtualizationRootHandle testRootHandle; - - // Invalid File System should fail - XCTAssertFalse( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - testVnode.get(), - nullptr, // path - KAUTH_FILEOP_RENAME, - true, // isDirectory, - &testRootHandle, - &pid)); -} - -- (void)testUnsupportedVnodeType { - shared_ptr testVnodeUnsupportedType = vnode::Create(self->testMount, "/foo", VNON); - - VirtualizationRootResult testRootResult = VirtualizationRoot_RegisterProviderForPath( - &self->userClient, - 0, - self->repoPath.c_str()); - XCTAssertEqual(0, testRootResult.error); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(testRootResult.root)); - - VirtualizationRootHandle testRootHandle; - int pid; - // Invalid Vnode Type should fail - XCTAssertFalse( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - testVnodeUnsupportedType.get(), - nullptr, // path - KAUTH_FILEOP_RENAME, - true, // isDirectory, - &testRootHandle, - &pid)); - - if (VirtualizationRoot_IsValidRootHandle(testRootResult.root)) - { - ActiveProvider_Disconnect(testRootResult.root, &userClient); - } -} - -- (void)testProviderOffline { - VirtualizationRootHandle testRootHandle = RootHandle_None; - VirtualizationRootResult testRootResult = VirtualizationRoot_RegisterProviderForPath( - &self->userClient, - 0, - self->repoPath.c_str()); - XCTAssertEqual(0, testRootResult.error); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(testRootResult.root)); - - if (VirtualizationRoot_IsValidRootHandle(testRootResult.root)) - { - ActiveProvider_Disconnect(testRootResult.root, &userClient); - } - - int pid; - // Fail when the provider is not online - XCTAssertFalse( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - self->testVnodeFile.get(), - nullptr, // path - KAUTH_FILEOP_RENAME, - true, // isDirectory, - &testRootHandle, - &pid)); -} - -- (void)testProviderInitiatedIO { - VirtualizationRootHandle testRootHandle = RootHandle_None; - VirtualizationRootResult testRootResult = VirtualizationRoot_RegisterProviderForPath( - &self->userClient, - 0, - self->repoPath.c_str()); - XCTAssertEqual(0, testRootResult.error); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(testRootResult.root)); - - // Fail when pid matches provider pid - MockProcess_Reset(); - MockProcess_AddContext(self->context, 0 /*pid*/); - MockProcess_SetSelfInfo(0, "Test"); - MockProcess_AddProcess(0 /*pid*/, 1 /*credentialId*/, 1 /*ppid*/, "test" /*name*/); - int pid; - XCTAssertFalse( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - self->testVnodeFile.get(), - nullptr, // path - KAUTH_FILEOP_RENAME, - true, // isDirectory, - &testRootHandle, - &pid)); - - if (VirtualizationRoot_IsValidRootHandle(testRootResult.root)) - { - ActiveProvider_Disconnect(testRootResult.root, &userClient); - } -} - -- (void)testVnodeCacheUpdated { - shared_ptr testVnodeDirectory = self->testMount->CreateVnodeTree(self->repoPath + "/directory", VDIR); - - VirtualizationRootHandle rootHandle; - int pid; - VirtualizationRootResult testRootResult = VirtualizationRoot_RegisterProviderForPath( - &self->userClient, - 0, - self->repoPath.c_str()); - XCTAssertEqual(0, testRootResult.error); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(testRootResult.root)); - - // KAUTH_FILEOP_OPEN - XCTAssertTrue( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - self->testVnodeFile.get(), - nullptr, // path - KAUTH_FILEOP_OPEN, - false, // isDirectory, - &rootHandle, - &pid)); - XCTAssertEqual(rootHandle, testRootResult.root); - // Finding the root should have added testVnodeFile to the cache - // IMPORTANT: This check assumes that the vnode cache is empty before the above call to ShouldHandleFileOpEvent - XCTAssertEqual(self->testVnodeFile.get(), self->cacheWrapper[ComputeVnodeHashIndex(self->testVnodeFile.get())].vnode); - XCTAssertEqual(rootHandle, self->cacheWrapper[ComputeVnodeHashIndex(self->testVnodeFile.get())].virtualizationRoot); - - // KAUTH_FILEOP_LINK - XCTAssertTrue( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - self->testVnodeFile.get(), - nullptr, // path - KAUTH_FILEOP_LINK, - false, // isDirectory, - &rootHandle, - &pid)); - XCTAssertEqual(rootHandle, testRootResult.root); - // KAUTH_FILEOP_LINK should invalidate the cache entry for testVnodeFile - XCTAssertEqual(self->testVnodeFile.get(), self->cacheWrapper[ComputeVnodeHashIndex(self->testVnodeFile.get())].vnode); - XCTAssertEqual(RootHandle_Indeterminate, self->cacheWrapper[ComputeVnodeHashIndex(self->testVnodeFile.get())].virtualizationRoot); - - // KAUTH_FILEOP_RENAME (file) - // Set a different value in the cache for testVnodeFile's root to validate that the cache is refreshed for renames - self->cacheWrapper[ComputeVnodeHashIndex(self->testVnodeFile.get())].virtualizationRoot = rootHandle + 1; - XCTAssertTrue( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - self->testVnodeFile.get(), - nullptr, // path - KAUTH_FILEOP_RENAME, - false, // isDirectory, - &rootHandle, - &pid)); - XCTAssertEqual(rootHandle, testRootResult.root); - // The cache should have been refreshed for KAUTH_FILEOP_RENAME - XCTAssertEqual(self->testVnodeFile.get(), self->cacheWrapper[ComputeVnodeHashIndex(self->testVnodeFile.get())].vnode); - XCTAssertEqual(rootHandle, self->cacheWrapper[ComputeVnodeHashIndex(self->testVnodeFile.get())].virtualizationRoot); - - // KAUTH_FILEOP_RENAME (directory) - // Directory KAUTH_FILEOP_RENAME events should invalidate the entire cache and then insert - // only the directory vnode into the cache - self->cacheWrapper.FillAllEntries(); - XCTAssertTrue( - ShouldHandleFileOpEvent( - &self->perfTracer, - self->context, - testVnodeDirectory.get(), - nullptr, // path - KAUTH_FILEOP_RENAME, - true, // isDirectory, - &rootHandle, - &pid)); - XCTAssertEqual(rootHandle, testRootResult.root); - - // Validate the cache is empty except for the testVnodeDirectory entry - uintptr_t directoryVnodeHash = ComputeVnodeHashIndex(testVnodeDirectory.get()); - for (uintptr_t index = 0; index < self->cacheWrapper.GetCapacity(); ++index) - { - if (index == directoryVnodeHash) - { - XCTAssertEqual(testVnodeDirectory.get(), self->cacheWrapper[directoryVnodeHash].vnode); - XCTAssertEqual(testVnodeDirectory->GetVid(), self->cacheWrapper[directoryVnodeHash].vid); - XCTAssertEqual(rootHandle, self->cacheWrapper[directoryVnodeHash].virtualizationRoot); - } - else - { - XCTAssertEqual(nullptr, self->cacheWrapper[index].vnode); - XCTAssertEqual(0, self->cacheWrapper[index].vid); - XCTAssertEqual(0, self->cacheWrapper[index].virtualizationRoot); - } - } - - if (VirtualizationRoot_IsValidRootHandle(testRootResult.root)) - { - ActiveProvider_Disconnect(testRootResult.root, &userClient); - } -} - - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/TestLocks.cpp b/ProjFS.Mac/PrjFSKextTests/TestLocks.cpp deleted file mode 100644 index d560cd4fa5..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/TestLocks.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "../PrjFSKext/Locks.hpp" - -struct __lck_rw_t__ -{ -}; - -RWLock RWLock_Alloc() -{ - return RWLock{ new __lck_rw_t__{} }; -} - -void RWLock_AcquireExclusive(RWLock& lock) -{ -} - -void RWLock_ReleaseExclusive(RWLock& lock) -{ -} - -void RWLock_AcquireShared(RWLock& lock) -{ -} - -void RWLock_DropExclusiveToShared(RWLock& lock) -{ -} - -void RWLock_ReleaseShared(RWLock& lock) -{ -} - -bool RWLock_IsValid(RWLock lock) -{ - return lock.p != nullptr; -} - -void RWLock_FreeMemory(RWLock* rwLock) -{ - delete rwLock->p; - rwLock->p = nullptr; -} - -void SpinLock_Acquire(SpinLock lock) -{ -} - -void SpinLock_Release(SpinLock lock) -{ -} - -SpinLock SpinLock_Alloc() -{ - return SpinLock{}; -} - -void SpinLock_FreeMemory(SpinLock* lock) -{ -} - -bool SpinLock_IsValid(SpinLock lock) -{ - return true; -} diff --git a/ProjFS.Mac/PrjFSKextTests/TestMemory.cpp b/ProjFS.Mac/PrjFSKextTests/TestMemory.cpp deleted file mode 100644 index 23bc5dbf90..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/TestMemory.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "../PrjFSKext/Memory.hpp" -#include - -struct TrackedKextMemoryAllocation -{ - uint32_t size; - uint8_t data[]; -}; - -void Memory_Free(void* memory, uint32_t sizeBytes) -{ - TrackedKextMemoryAllocation* memoryObject = reinterpret_cast(static_cast(memory) - offsetof(TrackedKextMemoryAllocation, data)); - assert(memoryObject->size == sizeBytes); - free(memoryObject); -} - -void* Memory_Alloc(uint32_t sizeBytes) -{ - TrackedKextMemoryAllocation* memoryObject = static_cast(malloc(sizeBytes + sizeof(TrackedKextMemoryAllocation))); - memoryObject->size = sizeBytes; - return memoryObject->data; -} diff --git a/ProjFS.Mac/PrjFSKextTests/VirtualizationRootsTests.mm b/ProjFS.Mac/PrjFSKextTests/VirtualizationRootsTests.mm deleted file mode 100644 index 58caf63b96..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/VirtualizationRootsTests.mm +++ /dev/null @@ -1,595 +0,0 @@ -#include "../PrjFSKext/VirtualizationRoots.hpp" -#include "../PrjFSKext/VirtualizationRootsTestable.hpp" -#include "../PrjFSKext/PrjFSProviderUserClient.hpp" -#include "../PrjFSKext/kernel-header-wrappers/mount.h" -#include "../PrjFSKext/Memory.hpp" -#include "../PrjFSKext/Locks.hpp" -#include "../PrjFSKext/VnodeUtilities.hpp" -#include "../PrjFSKext/KextLog.hpp" -#include "../PrjFSKext/public/PrjFSXattrs.h" -#include "KextMockUtilities.hpp" -#include "MockVnodeAndMount.hpp" - -#import "KextAssertIntegration.h" -#include -#include -#include - -using std::shared_ptr; -using std::vector; -using std::make_tuple; -using KextMock::_; - -extern "C" int strprefix(const char* string1, const char* string2); - -class PrjFSProviderUserClient -{ -}; - - -void ProviderUserClient_UpdatePathProperty(PrjFSProviderUserClient* userClient, const char* providerPath) -{ - MockCalls::RecordFunctionCall(ProviderUserClient_UpdatePathProperty, userClient, providerPath); -} - -static void SetRootXattrData(shared_ptr vnode) -{ - PrjFSVirtualizationRootXAttrData rootXattr = {}; - vector rootXattrData(sizeof(rootXattr), 0x00); - memcpy(rootXattrData.data(), &rootXattr, rootXattrData.size()); - vnode->xattrs.insert(make_pair(PrjFSVirtualizationRootXAttrName, rootXattrData)); -} - -@interface VirtualizationRootsTests : PFSKextTestCase - -@end - -@implementation VirtualizationRootsTests -{ - PrjFSProviderUserClient dummyClient; - pid_t dummyClientPid; - PerfTracer dummyTracer; - shared_ptr testMountPoint; - vfs_context_t dummyVFSContext; -} - -- (void)setUp -{ - [super setUp]; - - srand(0); - self->dummyClientPid = 100; - - // This is roughly what "real" fsids look like - fsid_t testMountFsid = { (1 << 24) | (rand() % 32), (rand() % 16) }; - - // HFS+ inodes are < UINT32_MAX (APFS' are 64-bit) - uint64_t testMountInitialInode = rand(); - - testMountPoint = mount::Create("hfs", testMountFsid, testMountInitialInode); - - kern_return_t initResult = VirtualizationRoots_Init(); - XCTAssertEqual(initResult, KERN_SUCCESS); - - self->dummyVFSContext = vfs_context_create(nullptr); -} - -- (void)tearDown -{ - vfs_context_rele(self->dummyVFSContext); - VirtualizationRoots_Cleanup(); - - MockVnodes_CheckAndClear(); - MockCalls::Clear(); - - [super tearDown]; -} - - -- (void)testRegisterProviderForPath_NonexistentPath -{ - const char* path = "/Users/test/code/Does/Not/Exist"; - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, ENOENT); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(result.root)); - - XCTAssertFalse(MockCalls::DidCallFunction(vfs_setauthcache_ttl)); -} - -- (void)testRegisterProviderForPath_NonDirectoryPath -{ - const char* path = "/Users/test/code/NotADirectory.cpp"; - - shared_ptr vnode = self->testMountPoint->CreateVnode(path); - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, ENOTDIR); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(result.root)); - - XCTAssertFalse(MockCalls::DidCallFunction(vfs_setauthcache_ttl)); -} - -- (void)testRegisterProviderForPath_DisallowedFileSystem -{ - const char* path = "/Volumes/USBStick/repo"; - - fsid_t fatFsid = self->testMountPoint->GetFsid(); - fatFsid.val[1]++; - shared_ptr fatMount(mount::Create("msdos", fatFsid, rand())); - - shared_ptr vnode = vnode::Create(fatMount, path, VDIR); - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, ENODEV); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(result.root)); - - XCTAssertFalse(MockCalls::DidCallFunction(vfs_setauthcache_ttl)); -} - -- (void)testRegisterProviderForPath_GetPathError -{ - const char* path = "/Users/test/code/RepoNotInNamecache"; - - shared_ptr vnode = vnode::Create(self->testMountPoint, path, VDIR); - vnode->errors.getpath = EINVAL; - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, EINVAL); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(result.root)); - - XCTAssertFalse(MockCalls::DidCallFunction(vfs_setauthcache_ttl)); - XCTAssertFalse(MockCalls::DidCallFunction(ProviderUserClient_UpdatePathProperty)); -} - -- (void)testRegisterProviderForPath_ExistingRoot -{ - const char* path = "/Users/test/code/Repo"; - - shared_ptr vnode = vnode::Create(self->testMountPoint, path, VDIR); - - const VirtualizationRootHandle rootIndex = 2; - XCTAssertLessThan(rootIndex, s_maxVirtualizationRoots); - - s_virtualizationRoots[rootIndex].inUse = true; - s_virtualizationRoots[rootIndex].rootVNode = vnode.get(); - s_virtualizationRoots[rootIndex].rootVNodeVid = vnode_vid(vnode.get()); - s_virtualizationRoots[rootIndex].rootFsid = self->testMountPoint->GetFsid(); - s_virtualizationRoots[rootIndex].rootInode = vnode->GetInode(); - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, 0); - XCTAssertEqual(result.root, rootIndex); - XCTAssertEqual(s_virtualizationRoots[result.root].providerUserClient, &self->dummyClient); - - XCTAssertTrue(MockCalls::DidCallFunction(vfs_setauthcache_ttl, _, 0)); - XCTAssertTrue(MockCalls::DidCallFunction(ProviderUserClient_UpdatePathProperty, &self->dummyClient, _)); - - ActiveProvider_Disconnect(result.root, &self->dummyClient); -} - -- (void)testRegisterProviderForPath_ProviderExists -{ - const char* path = "/Users/test/code/Repo"; - - shared_ptr vnode = vnode::Create(self->testMountPoint, path, VDIR); - - const VirtualizationRootHandle rootIndex = 1; - XCTAssertLessThan(rootIndex, s_maxVirtualizationRoots); - - PrjFSProviderUserClient existingClient; - const pid_t existingClientPid = 50; - - - s_virtualizationRoots[rootIndex].inUse = true; - s_virtualizationRoots[rootIndex].rootVNode = vnode.get(); - s_virtualizationRoots[rootIndex].rootVNodeVid = vnode->GetVid(); - s_virtualizationRoots[rootIndex].rootFsid = self->testMountPoint->GetFsid(); - s_virtualizationRoots[rootIndex].rootInode = vnode->GetInode(); - - vnode_get(s_virtualizationRoots[rootIndex].rootVNode); - - s_virtualizationRoots[rootIndex].providerUserClient = &existingClient; - s_virtualizationRoots[rootIndex].providerPid = existingClientPid; - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, EBUSY); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(result.root)); - XCTAssertNotEqual(s_virtualizationRoots[rootIndex].providerUserClient, &self->dummyClient); - - XCTAssertFalse(MockCalls::DidCallFunction(vfs_setauthcache_ttl)); - XCTAssertFalse(MockCalls::DidCallFunction(ProviderUserClient_UpdatePathProperty)); - - s_virtualizationRoots[rootIndex].providerUserClient = nullptr; - vnode_put(s_virtualizationRoots[rootIndex].rootVNode); -} - - -- (void)testRegisterProviderForPath_InsertionSucceeded -{ - const char* path = "/Users/test/code/Repo"; - - shared_ptr vnode = vnode::Create(self->testMountPoint, path, VDIR); - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, 0); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(result.root)); - if (VirtualizationRoot_IsValidRootHandle(result.root)) - { - XCTAssertEqual(s_virtualizationRoots[result.root].providerUserClient, &self->dummyClient); - - XCTAssertTrue(MockCalls::DidCallFunction(vfs_setauthcache_ttl)); - XCTAssertTrue(MockCalls::DidCallFunction(ProviderUserClient_UpdatePathProperty)); - - ActiveProvider_Disconnect(result.root, &self->dummyClient); - } -} - -- (void)testRegisterProviderForPath_TwoMountPointsInsertionSucceeded -{ - const char* path1 = "/Users/test/code/Repo"; - shared_ptr vnode1 = vnode::Create(self->testMountPoint, path1, VDIR); - - const char* path2 = "/Volumes/Code/Repo"; - shared_ptr secondMountPoint = mount::Create(); - shared_ptr vnode2 = vnode::Create(secondMountPoint, path2, VDIR); - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path1); - XCTAssertEqual(result.error, 0); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(result.root)); - - PrjFSProviderUserClient dummyClient2; - VirtualizationRootResult result2 = VirtualizationRoot_RegisterProviderForPath(&dummyClient2, 1000, path2); - XCTAssertEqual(result2.error, 0); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(result2.root)); - - XCTAssertNotEqual(result.root, result2.root); - - if (VirtualizationRoot_IsValidRootHandle(result.root)) - { - XCTAssertEqual(s_virtualizationRoots[result.root].providerUserClient, &self->dummyClient); - - XCTAssertTrue(MockCalls::DidCallFunction(vfs_setauthcache_ttl, self->testMountPoint.get(), _)); - XCTAssertTrue(MockCalls::DidCallFunction(ProviderUserClient_UpdatePathProperty, &self->dummyClient, _)); - - ActiveProvider_Disconnect(result.root, &self->dummyClient); - } - - if (VirtualizationRoot_IsValidRootHandle(result2.root)) - { - XCTAssertEqual(s_virtualizationRoots[result2.root].providerUserClient, &dummyClient2); - - XCTAssertTrue(MockCalls::DidCallFunction(vfs_setauthcache_ttl, secondMountPoint.get(), 0)); - XCTAssertTrue(MockCalls::DidCallFunction(ProviderUserClient_UpdatePathProperty, &dummyClient2, _)); - - ActiveProvider_Disconnect(result2.root, &dummyClient2); - } - - XCTAssertTrue(MockCalls::DidCallFunctionsInOrder( - ProviderUserClient_UpdatePathProperty, make_tuple(&self->dummyClient, _), - vfs_setauthcache_ttl, make_tuple(self->testMountPoint.get(), _), - ProviderUserClient_UpdatePathProperty, _, - vfs_setauthcache_ttl)); -} - -- (void)testRegisterProviderForPath_ArrayFull -{ - const char* path = "/Users/test/code/Repo"; - - shared_ptr vnode = vnode::Create(self->testMountPoint, path, VDIR); - - Memory_FreeArray(s_virtualizationRoots, s_maxVirtualizationRoots); - s_maxVirtualizationRoots = INT16_MAX + 1; - s_virtualizationRoots = Memory_AllocArray(INT16_MAX + 1); - memset(s_virtualizationRoots, 0, s_maxVirtualizationRoots * sizeof(s_virtualizationRoots[0])); - - for (uint32_t i = 0; i < s_maxVirtualizationRoots; ++i) - { - s_virtualizationRoots[i].inUse = true; - } - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, ENOMEM); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(result.root)); - - XCTAssertFalse(MockCalls::DidCallFunction(vfs_setauthcache_ttl)); - XCTAssertFalse(MockCalls::DidCallFunction(ProviderUserClient_UpdatePathProperty)); -} - -- (void)testRegisterProviderForPath_ResizeArray -{ - const char* path = "/Users/test/code/Repo"; - - shared_ptr vnode = vnode::Create(self->testMountPoint, path, VDIR); - - // Start with 4 "full" array items, forcing resize on next insertion - Memory_FreeArray(s_virtualizationRoots, s_maxVirtualizationRoots); - s_maxVirtualizationRoots = 4; - s_virtualizationRoots = Memory_AllocArray(s_maxVirtualizationRoots); - memset(s_virtualizationRoots, 0, s_maxVirtualizationRoots * sizeof(s_virtualizationRoots[0])); - - for (uint32_t i = 0; i < s_maxVirtualizationRoots; ++i) - { - s_virtualizationRoots[i].inUse = true; - } - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, 0); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(result.root)); - - if (VirtualizationRoot_IsValidRootHandle(result.root)) - { - ActiveProvider_Disconnect(result.root, &self->dummyClient); - } -} - - -- (void)testRegisterProviderForPath_ExistingRecycledRoot -{ - const char* path = "/Users/test/code/Repo"; - - shared_ptr oldVnode = vnode::Create(self->testMountPoint, path, VDIR); - - const VirtualizationRootHandle rootIndex = 2; - XCTAssertLessThan(rootIndex, s_maxVirtualizationRoots); - - s_virtualizationRoots[rootIndex].inUse = true; - s_virtualizationRoots[rootIndex].rootVNode = oldVnode.get(); - s_virtualizationRoots[rootIndex].rootVNodeVid = oldVnode->GetVid(); - s_virtualizationRoots[rootIndex].rootFsid = self->testMountPoint->GetFsid(); - uint64_t inode = oldVnode->GetInode(); - s_virtualizationRoots[rootIndex].rootInode = inode; - - oldVnode->StartRecycling(); - - shared_ptr newVnode = self->testMountPoint->CreateVnode(path, VnodeCreationProperties{ VDIR, oldVnode->GetInode() }); - - VirtualizationRootResult result = VirtualizationRoot_RegisterProviderForPath(&self->dummyClient, self->dummyClientPid, path); - XCTAssertEqual(result.error, 0); - XCTAssertEqual(result.root, rootIndex); - XCTAssertEqual(s_virtualizationRoots[result.root].providerUserClient, &self->dummyClient); - XCTAssertEqual(s_virtualizationRoots[result.root].rootVNode, newVnode.get()); - XCTAssertEqual(s_virtualizationRoots[result.root].rootVNodeVid, newVnode->GetVid()); - - XCTAssertTrue(MockCalls::DidCallFunction(vfs_setauthcache_ttl, self->testMountPoint.get(), 0)); - XCTAssertTrue(MockCalls::DidCallFunction(ProviderUserClient_UpdatePathProperty)); - - ActiveProvider_Disconnect(result.root, &self->dummyClient); -} - -- (void)testVnodeIsOnAllowedFilesystem -{ - shared_ptr testMountHfs = mount::Create("hfs", fsid_t{}, 0); - shared_ptr testVnodeHfs = vnode::Create(testMountHfs, "/hfs"); - XCTAssertTrue(VirtualizationRoot_VnodeIsOnAllowedFilesystem(testVnodeHfs.get())); - - shared_ptr testMountApfs = mount::Create("apfs", fsid_t{}, 0); - shared_ptr testVnodeApfs = vnode::Create(testMountApfs, "/apfs"); - XCTAssertTrue(VirtualizationRoot_VnodeIsOnAllowedFilesystem(testVnodeApfs.get())); - - shared_ptr testMountFoo = mount::Create("foo", fsid_t{}, 0); - shared_ptr testVnodeFoo = vnode::Create(testMountFoo, "/foo"); - XCTAssertFalse(VirtualizationRoot_VnodeIsOnAllowedFilesystem(testVnodeFoo.get())); -} - -- (void)testIsValidRootHandle -{ - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(0)); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(1)); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(2)); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(RootHandle_None)); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(RootHandle_Indeterminate)); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(RootHandle_ProviderTemporaryDirectory)); - XCTAssertFalse(VirtualizationRoot_IsValidRootHandle(-100)); -} - -// Check that VirtualizationRoot_FindForVnode correctly identifies the virtualization root for files below that root. -- (void)testFindForVnode_FileInRoot -{ - const char* repoPath = "/Users/test/code/Repo"; - const char* filePath = "/Users/test/code/Repo/file"; - const char* deeplyNestedPath = "/Users/test/code/Repo/deeply/nested/sub/directories/with/a/file"; - - shared_ptr repoRootVnode = self->testMountPoint->CreateVnodeTree(repoPath, VDIR); - shared_ptr testFileVnode = self->testMountPoint->CreateVnodeTree(filePath); - shared_ptr deepFileVnode = self->testMountPoint->CreateVnodeTree(deeplyNestedPath); - - VirtualizationRootHandle repoRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock(repoRootVnode.get(), repoRootVnode->GetVid(), FsidInode{ repoRootVnode->GetMountPoint()->GetFsid(), repoRootVnode->GetInode() }, repoPath); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(repoRootHandle)); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode(&self->dummyTracer, PrjFSPerfCounter_VnodeOp_FindRoot, PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, testFileVnode.get(), self->dummyVFSContext); - XCTAssertEqual(foundRoot, repoRootHandle); - - foundRoot = VirtualizationRoot_FindForVnode(&self->dummyTracer, PrjFSPerfCounter_VnodeOp_FindRoot, PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, deepFileVnode.get(), self->dummyVFSContext); - XCTAssertEqual(foundRoot, repoRootHandle); -} - -// Check that files outside a root are correctly identified as such by VirtualizationRoot_FindForVnode -- (void)testFindForVnode_FileNotInRoot -{ - const char* repoPath = "/Users/test/code/Repo"; - const char* filePath = "/Users/test/code/NotVirtualizedRepo/file"; - - shared_ptr repoRootVnode = self->testMountPoint->CreateVnodeTree(repoPath, VDIR); - shared_ptr testFileVnode = self->testMountPoint->CreateVnodeTree(filePath); - - - VirtualizationRootHandle repoRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock(repoRootVnode.get(), repoRootVnode->GetVid(), FsidInode{ repoRootVnode->GetMountPoint()->GetFsid(), repoRootVnode->GetInode() }, repoPath); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(repoRootHandle)); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode(&self->dummyTracer, PrjFSPerfCounter_VnodeOp_FindRoot, PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, testFileVnode.get(), self->dummyVFSContext); - - XCTAssertEqual(foundRoot, RootHandle_None); -} - -// Check that VirtualizationRoot_FindForVnode can discover virtualization roots from the root directory's xattr -- (void)testFindForVnode_FileInUndetectedRoot -{ - const char* repoPath = "/Users/test/code/Repo"; - const char* filePath = "/Users/test/code/Repo/file"; - - shared_ptr repoRootVnode = self->testMountPoint->CreateVnodeTree(repoPath, VDIR); - shared_ptr testFileVnode = self->testMountPoint->CreateVnodeTree(filePath); - - SetRootXattrData(repoRootVnode); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode(&self->dummyTracer, PrjFSPerfCounter_VnodeOp_FindRoot, PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, testFileVnode.get(), self->dummyVFSContext); - - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(foundRoot)); -} - -// A regression test of VirtualizationRoot_FindForVnode for bug #797. -// * Verify the virtualization root ends up with the inode of the root directory vnode. -// * Verify that the virtualization root does not change identity when the root directory vnode is recycled. -// * Verify that the root vnode registered in the virtualization root is refreshed to the new root vnode. -- (void)testFindForVnode_DetectRootRecycleThenFindRootForOtherFile -{ - const char* repoPath = "/Users/test/code/Repo"; - const char* filePath = "/Users/test/code/Repo/file"; - const char* otherFilePath = "/Users/test/code/Repo/some/otherfile"; - - shared_ptr repoRootVnode = self->testMountPoint->CreateVnodeTree(repoPath, VDIR); - shared_ptr testFileVnode = self->testMountPoint->CreateVnodeTree(filePath); - - SetRootXattrData(repoRootVnode); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode(&self->dummyTracer, PrjFSPerfCounter_VnodeOp_FindRoot, PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, testFileVnode.get(), self->dummyVFSContext); - - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(foundRoot)); - - if (VirtualizationRoot_IsValidRootHandle(foundRoot)) - { - uint64_t inode = repoRootVnode->GetInode(); - XCTAssertEqual(s_virtualizationRoots[foundRoot].rootInode, inode); - - repoRootVnode->StartRecycling(); - - shared_ptr newRepoRootVnode = self->testMountPoint->CreateVnode(repoPath, VnodeCreationProperties { .inode = repoRootVnode->GetInode(), .type = VDIR }); - - shared_ptr otherTestFileVnode = self->testMountPoint->CreateVnodeTree(otherFilePath); - VirtualizationRootHandle foundRootForOther = VirtualizationRoot_FindForVnode(&self->dummyTracer, PrjFSPerfCounter_VnodeOp_FindRoot, PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, otherTestFileVnode.get(), self->dummyVFSContext); - - XCTAssertEqual(foundRootForOther, foundRoot, "Despite recycling the repo root vnode, we should end up with the same handle"); - XCTAssertEqual(s_virtualizationRoots[foundRootForOther].rootVNode, newRepoRootVnode.get(), "Vnode should be refreshed"); - XCTAssertEqual(s_virtualizationRoots[foundRootForOther].rootVNodeVid, newRepoRootVnode->GetVid(), "Vnode VID should be refreshed"); - XCTAssertEqual(s_virtualizationRoots[foundRoot].rootInode, inode); - } -} - -// A variation on the above 2 tests but starting with a directory vnode, taking -// the 'if (vnode_isdir(vnode))' branch in VirtualizationRoot_FindForVnode. -- (void)testFindForVnode_DirectoryInUndetectedRoot -{ - const char* repoPath = "/Users/test/code/Repo"; - const char* subdirPath = "/Users/test/code/Repo/some/nested/subdir"; - - shared_ptr repoRootVnode = self->testMountPoint->CreateVnodeTree(repoPath, VDIR); - shared_ptr testSubdirVnode = self->testMountPoint->CreateVnodeTree(subdirPath, VDIR); - - XCTAssertTrue(vnode_isdir(testSubdirVnode.get())); - - SetRootXattrData(repoRootVnode); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode(&self->dummyTracer, PrjFSPerfCounter_VnodeOp_FindRoot, PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, testSubdirVnode.get(), self->dummyVFSContext); - - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(foundRoot)); - if (VirtualizationRoot_IsValidRootHandle(foundRoot)) - { - XCTAssertEqual(s_virtualizationRoots[foundRoot].rootInode, repoRootVnode->GetInode()); - } -} - -// This helper function is defined in the kernel, not in stdlib. Returns 1 if s2 is a prefix of s1 -int strprefix(const char* string1, const char* string2) -{ - while (true) - { - char c = *string2; - if (c == '\0') - { - return 1; - } - else if (c != *string1) - { - return 0; - } - - ++string1; - ++string2; - } -} - -- (void) testPathInsideDirectory -{ - XCTAssertTrue(PathInsideDirectory("/some/directory", "/some/directory/containing/file")); - XCTAssertTrue(PathInsideDirectory("/directory/with/slash/", "/directory/with/slash/containing/file")); - XCTAssertTrue(PathInsideDirectory("/", "/below/root/directory")); - XCTAssertTrue(PathInsideDirectory("/a/directory/itself", "/a/directory/itself")); - - XCTAssertFalse(PathInsideDirectory("/some/dir", "/some/directory/containing/file")); - XCTAssertFalse(PathInsideDirectory("/a/directory", "/a/dir")); - XCTAssertFalse(PathInsideDirectory("/some/directory/with/sub/directories", "/some/directory")); -} - -- (void) testOfflineIOProcessArrayOperations -{ - XCTAssertTrue(VirtualizationRoots_AddOfflineIOProcess(10)); - // Processes can technically register more than once - XCTAssertTrue(VirtualizationRoots_AddOfflineIOProcess(10)); - // Adding so many items should resize the array a few times - XCTAssertTrue(VirtualizationRoots_AddOfflineIOProcess(11)); - XCTAssertTrue(VirtualizationRoots_AddOfflineIOProcess(12)); - XCTAssertTrue(VirtualizationRoots_AddOfflineIOProcess(13)); - - for (pid_t testPid = 1; testPid < 20; ++testPid) - { - bool allowed = VirtualizationRoots_ProcessMayAccessOfflineRoots(testPid); - if (testPid < 10 || testPid > 13) - { - XCTAssertFalse(allowed); - } - else - { - XCTAssertTrue(allowed); - } - } - - // remove from the middle of the array - VirtualizationRoots_RemoveOfflineIOProcess(11); - - for (pid_t testPid = 1; testPid < 20; ++testPid) - { - bool allowed = VirtualizationRoots_ProcessMayAccessOfflineRoots(testPid); - if (testPid < 10 || testPid > 13 || testPid == 11) - { - XCTAssertFalse(allowed); - } - else - { - XCTAssertTrue(allowed); - } - } - - XCTAssertTrue(VirtualizationRoots_AddOfflineIOProcess(14)); - - for (pid_t testPid = 1; testPid < 20; ++testPid) - { - bool allowed = VirtualizationRoots_ProcessMayAccessOfflineRoots(testPid); - if (testPid < 10 || testPid > 14 || testPid == 11) - { - XCTAssertFalse(allowed); - } - else - { - XCTAssertTrue(allowed); - } - } - - // These must balance out - VirtualizationRoots_RemoveOfflineIOProcess(10); - VirtualizationRoots_RemoveOfflineIOProcess(10); - VirtualizationRoots_RemoveOfflineIOProcess(14); - VirtualizationRoots_RemoveOfflineIOProcess(12); - VirtualizationRoots_RemoveOfflineIOProcess(13); -} - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/VnodeCacheEntriesWrapper.hpp b/ProjFS.Mac/PrjFSKextTests/VnodeCacheEntriesWrapper.hpp deleted file mode 100644 index 59e85605c4..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/VnodeCacheEntriesWrapper.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include "../PrjFSKext/VirtualizationRoots.hpp" -#include "../PrjFSKext/VnodeCachePrivate.hpp" -#include "../PrjFSKext/VnodeCacheTestable.hpp" - -// Helper class for interacting with s_entries, s_entriesCapacity, and s_ModBitmask -class VnodeCacheEntriesWrapper -{ -public: - VnodeCacheEntriesWrapper() - { - s_entries = nullptr; - } - - ~VnodeCacheEntriesWrapper() - { - this->FreeCache(); - } - - void AllocateCache() - { - s_entriesCapacity = 64; - s_ModBitmask = s_entriesCapacity - 1; - s_entries = new VnodeCacheEntry[s_entriesCapacity]; - - for (uint32_t i = 0; i < s_entriesCapacity; ++i) - { - memset(&(s_entries[i]), 0, sizeof(VnodeCacheEntry)); - } - } - - void FreeCache() - { - s_entriesCapacity = 0; - - if (nullptr != s_entries) - { - delete[] s_entries; - s_entries = nullptr; - } - - this->dummyMount.reset(); - this->dummyNode.reset(); - } - - void FillAllEntries() - { - // Keep these dummy instances alive until FreeCache is called to ensure that - // no subsequent allocations overlap with the vnode we're using to fill the cache - this->dummyMount = mount::Create(); - this->dummyNode = dummyMount->CreateVnodeTree("/DUMMY"); - - for (uint32_t i = 0; i < s_entriesCapacity; ++i) - { - s_entries[i].vnode = this->dummyNode.get(); - } - } - - void MarkEntryAsFree(const uintptr_t entryIndex) - { - s_entries[entryIndex].vnode = nullptr; - } - - VnodeCacheEntry& operator[] (const uintptr_t entryIndex) - { - return s_entries[entryIndex]; - } - - uint32_t GetCapacity() const - { - return s_entriesCapacity; - } - -private: - std::shared_ptr dummyMount; - std::shared_ptr dummyNode; -}; diff --git a/ProjFS.Mac/PrjFSKextTests/VnodeCacheTests.mm b/ProjFS.Mac/PrjFSKextTests/VnodeCacheTests.mm deleted file mode 100644 index af36849933..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/VnodeCacheTests.mm +++ /dev/null @@ -1,803 +0,0 @@ -#import "KextAssertIntegration.h" -#include "MockVnodeAndMount.hpp" -#include "KextLogMock.h" -#include "KextMockUtilities.hpp" -#include "VnodeCacheEntriesWrapper.hpp" -#include "../PrjFSKext/VirtualizationRootsTestable.hpp" -#include "../PrjFSKext/VnodeCache.hpp" -#include "../PrjFSKext/VnodeCachePrivate.hpp" -#include "../PrjFSKext/VnodeCacheTestable.hpp" - -using KextMock::_; -using std::shared_ptr; - -@interface VnodeCacheTests : PFSKextTestCase -@end - -@implementation VnodeCacheTests -{ - std::string repoPath; - shared_ptr testMount; - shared_ptr repoRootVnode; - shared_ptr testVnodeFile1; - shared_ptr testVnodeFile2; - shared_ptr testVnodeFile3; - PerfTracer dummyPerfTracer; - vfs_context_t dummyVFSContext; - VnodeCacheEntriesWrapper cacheWrapper; -} - -static const VirtualizationRootHandle DummyRootHandle = 51; -static const VirtualizationRootHandle DummyRootHandleTwo = 52; - -- (void)setUp -{ - [super setUp]; - - kern_return_t initResult = VirtualizationRoots_Init(); - XCTAssertEqual(initResult, KERN_SUCCESS); - - self->testMount = mount::Create(); - self->repoPath = "/Users/test/code/Repo"; - self->repoRootVnode = self->testMount->CreateVnodeTree(repoPath, VDIR); - self->testVnodeFile1 = testMount->CreateVnodeTree(repoPath + "/file1"); - self->testVnodeFile2 = testMount->CreateVnodeTree(repoPath + "/file2"); - self->testVnodeFile3 = testMount->CreateVnodeTree(repoPath + "/file3"); - self->dummyVFSContext = vfs_context_create(nullptr); - self->cacheWrapper.AllocateCache(); - InitCacheStats(); -} - -- (void)tearDown -{ - self->cacheWrapper.FreeCache(); - vfs_context_rele(self->dummyVFSContext); - self->testVnodeFile3.reset(); - self->testVnodeFile2.reset(); - self->testVnodeFile1.reset(); - self->repoRootVnode.reset(); - self->testMount.reset(); - MockCalls::Clear(); - VirtualizationRoots_Cleanup(); - - [super tearDown]; -} - -- (void)testInitCacheStats { - // We need to validate that InitCacheStats sets all of the cache health stats to zero, so - // first set them all to something non-zero - atomic_store_explicit(&s_cacheStats.cacheEntries, 1U, memory_order_relaxed); - - for (int32_t i = 0; i < VnodeCacheHealthStat_Count; ++i) - { - atomic_store_explicit(&s_cacheStats.healthStats[i], 1ULL, memory_order_relaxed); - } - - InitCacheStats(); - - XCTAssertTrue(s_cacheStats.cacheEntries == 0); - - for (int32_t i = 0; i < VnodeCacheHealthStat_Count; ++i) - { - XCTAssertTrue(s_cacheStats.healthStats[i] == 0); - } -} - -- (void)testAtomicFetchAddCacheHealthStat { - for (int32_t i = 0; i < VnodeCacheHealthStat_Count; ++i) - { - AtomicFetchAddCacheHealthStat(static_cast(i), 1ULL); - XCTAssertTrue(s_cacheStats.healthStats[i] == 1); - } - - for (int32_t i = 0; i < VnodeCacheHealthStat_Count; ++i) - { - AtomicFetchAddCacheHealthStat(static_cast(i), 2ULL); - XCTAssertTrue(s_cacheStats.healthStats[i] == 3); - } - - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); - - atomic_store_explicit(&s_cacheStats.healthStats[VnodeCacheHealthStat_InvalidateEntireCacheCount], UINT64_MAX - 1000, memory_order_relaxed); - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_InvalidateEntireCacheCount, 1ULL); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_InvalidateEntireCacheCount] == UINT64_MAX - 999); - AtomicFetchAddCacheHealthStat(VnodeCacheHealthStat_InvalidateEntireCacheCount, 1ULL); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_InvalidateEntireCacheCount] == 0ULL); - - XCTAssertTrue(MockCalls::DidCallFunction(KextMessageLogged, KEXTLOG_DEFAULT)); -} - -- (void)testVnodeCache_FindRootForVnode_EmptyCache { - VirtualizationRootHandle repoRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock( - self->repoRootVnode.get(), - self->repoRootVnode->GetVid(), - FsidInode{ self->repoRootVnode->GetMountPoint()->GetFsid(), self->repoRootVnode->GetInode() }, - self->repoPath.c_str()); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(repoRootHandle)); - - // We don't care which mocks were called during the initialization code (above) - MockCalls::Clear(); - - XCTAssertTrue(repoRootHandle == VnodeCache_FindRootForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext)); - - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeHits] == 0); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeMisses] == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalCacheLookups] == 2); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalLookupCollisions] == 0); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testVnodeCache_FindRootForVnode_FullCache { - VirtualizationRootHandle repoRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock( - self->repoRootVnode.get(), - self->repoRootVnode->GetVid(), - FsidInode{ self->repoRootVnode->GetMountPoint()->GetFsid(), self->repoRootVnode->GetInode() }, - self->repoPath.c_str()); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(repoRootHandle)); - - self->cacheWrapper.FillAllEntries(); - - // We don't care which mocks were called during the initialization code (above) - MockCalls::Clear(); - - XCTAssertTrue(repoRootHandle == VnodeCache_FindRootForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext)); - - XCTAssertTrue(s_cacheStats.cacheEntries == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeHits] == 0); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeMisses] == 1); - - // 3 -> The initial lookup, the attempt to update the full cache, the lookup after emptying the cache - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalCacheLookups] == 3); - - // Capacity * 2 -> The first two lookups (before invalidating the cache), will collide with every entry - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalLookupCollisions] == self->cacheWrapper.GetCapacity() * 2); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testVnodeCache_FindRootForVnode_VnodeInCache { - VirtualizationRootHandle repoRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock( - self->repoRootVnode.get(), - self->repoRootVnode->GetVid(), - FsidInode{ self->repoRootVnode->GetMountPoint()->GetFsid(), self->repoRootVnode->GetInode() }, - self->repoPath.c_str()); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(repoRootHandle)); - - // We don't care which mocks were called during the initialization code (above) - MockCalls::Clear(); - - // The first call to VnodeCache_FindRootForVnode results in the vnode being added to the cache - XCTAssertTrue(repoRootHandle == VnodeCache_FindRootForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext)); - - XCTAssertTrue(s_cacheStats.cacheEntries == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeHits] == 0); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeMisses] == 1); - - uintptr_t vnodeIndex = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[vnodeIndex].vnode); - XCTAssertTrue(self->testVnodeFile1->GetVid() == self->cacheWrapper[vnodeIndex].vid); - XCTAssertTrue(repoRootHandle == self->cacheWrapper[vnodeIndex].virtualizationRoot); - - XCTAssertTrue(repoRootHandle == VnodeCache_FindRootForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext)); - - XCTAssertTrue(s_cacheStats.cacheEntries == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeHits] == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeMisses] == 1); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testVnodeCache_RefreshRootForVnode { - VirtualizationRootHandle repoRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock( - self->repoRootVnode.get(), - self->repoRootVnode->GetVid(), - FsidInode{ self->repoRootVnode->GetMountPoint()->GetFsid(), self->repoRootVnode->GetInode() }, - self->repoPath.c_str()); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(repoRootHandle)); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext); - XCTAssertEqual(foundRoot, repoRootHandle); - - // We don't care which mocks were called during the initialization code (above) - MockCalls::Clear(); - - // Make sure the cache is empty to start - InvalidateCache_ExclusiveLocked(); - - // Insert testFileVnode with DummyRootHandle as its root - uintptr_t indexFromHash = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - uint32_t testVnodeVid = self->testVnodeFile1->GetVid(); - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - false, // forceRefreshEntry - DummyRootHandle)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - - // VnodeCache_RefreshRootForVnode should - // force a lookup of the new root and set it in the cache - VirtualizationRootHandle rootHandle = VnodeCache_RefreshRootForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext); - XCTAssertTrue(rootHandle == repoRootHandle); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(rootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - - XCTAssertTrue(s_cacheStats.cacheEntries == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalRefreshRootForVnode] == 1); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testVnodeCache_InvalidateVnodeRootAndGetLatestRoot { - VirtualizationRootHandle repoRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock( - self->repoRootVnode.get(), - self->repoRootVnode->GetVid(), - FsidInode{ self->repoRootVnode->GetMountPoint()->GetFsid(), self->repoRootVnode->GetInode() }, - self->repoPath.c_str()); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(repoRootHandle)); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext); - XCTAssertEqual(foundRoot, repoRootHandle); - - // We don't care which mocks were called during the initialization code (above) - MockCalls::Clear(); - - // Make sure the cache is empty to start - InvalidateCache_ExclusiveLocked(); - - // Insert testFileVnode with DummyRootHandle as its root - uintptr_t indexFromHash = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - uint32_t testVnodeVid = self->testVnodeFile1->GetVid(); - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - false, // forceRefreshEntry - DummyRootHandle)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - - // VnodeCache_InvalidateVnodeRootAndGetLatestRoot should return the real root and - // set the entry in the cache to RootHandle_Indeterminate - VirtualizationRootHandle rootHandle = VnodeCache_InvalidateVnodeRootAndGetLatestRoot( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext); - XCTAssertTrue(rootHandle == repoRootHandle); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(RootHandle_Indeterminate == self->cacheWrapper[indexFromHash].virtualizationRoot); - - XCTAssertTrue(s_cacheStats.cacheEntries == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalInvalidateVnodeRoot] == 1); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testVnodeCache_InvalidateCache_SetsMemoryToZeros { - self->cacheWrapper.FillAllEntries(); - s_cacheStats.cacheEntries = self->cacheWrapper.GetCapacity(); - - shared_ptr emptyArray(static_cast(calloc(self->cacheWrapper.GetCapacity(), sizeof(VnodeCacheEntry))), free); - XCTAssertTrue(0 != memcmp(emptyArray.get(), s_entries, sizeof(VnodeCacheEntry) * self->cacheWrapper.GetCapacity())); - - VnodeCache_InvalidateCache(&self->dummyPerfTracer); - XCTAssertTrue(0 == memcmp(emptyArray.get(), s_entries, sizeof(VnodeCacheEntry) * self->cacheWrapper.GetCapacity())); - - XCTAssertTrue(s_cacheStats.cacheEntries == 0); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_InvalidateEntireCacheCount] == 1); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testInvalidateCache_ExclusiveLocked_SetsMemoryToZeros { - self->cacheWrapper.FillAllEntries(); - s_cacheStats.cacheEntries = self->cacheWrapper.GetCapacity(); - - shared_ptr emptyArray(static_cast(calloc(self->cacheWrapper.GetCapacity(), sizeof(VnodeCacheEntry))), free); - XCTAssertTrue(0 != memcmp(emptyArray.get(), s_entries, sizeof(VnodeCacheEntry) * self->cacheWrapper.GetCapacity())); - - InvalidateCache_ExclusiveLocked(); - XCTAssertTrue(0 == memcmp(emptyArray.get(), s_entries, sizeof(VnodeCacheEntry) * self->cacheWrapper.GetCapacity())); - XCTAssertTrue(s_cacheStats.cacheEntries == 0); - - // VnodeCacheHealthStat_InvalidateEntireCacheCount is adjusted by VnodeCache_InvalidateCache - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_InvalidateEntireCacheCount] == 0); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testComputePow2CacheCapacity { - - // At a minimum ComputePow2CacheCapacity should return the minimum value in AllowedPow2CacheCapacities - XCTAssertTrue(MinPow2VnodeCacheCapacity == ComputePow2CacheCapacity(0)); - - // ComputePow2CacheCapacity should round up to the nearest power of 2 (after multiplying expectedVnodeCount by 2) - int expectedVnodeCount = MinPow2VnodeCacheCapacity/2 + 1; - XCTAssertTrue(MinPow2VnodeCacheCapacity << 1 == ComputePow2CacheCapacity(expectedVnodeCount)); - - // ComputePow2CacheCapacity should be capped at the maximum value in AllowedPow2CacheCapacities - XCTAssertTrue(MaxPow2VnodeCacheCapacity == ComputePow2CacheCapacity(MaxPow2VnodeCacheCapacity + 1)); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryGetVnodeRootFromCache_VnodeInCache { - uintptr_t testIndex = 5; - self->cacheWrapper[testIndex].vnode = self->testVnodeFile1.get(); - self->cacheWrapper[testIndex].vid = self->testVnodeFile1->GetVid(); - self->cacheWrapper[testIndex].virtualizationRoot = DummyRootHandle; - - VirtualizationRootHandle rootHandle = 1; - XCTAssertTrue( - TryGetVnodeRootFromCache( - self->testVnodeFile1.get(), - testIndex, - self->testVnodeFile1->GetVid(), - rootHandle)); - XCTAssertTrue(DummyRootHandle == rootHandle); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryGetVnodeRootFromCache_VnodeNotInCache { - VirtualizationRootHandle rootHandle = 1; - XCTAssertFalse( - TryGetVnodeRootFromCache( - self->testVnodeFile1.get(), - ComputeVnodeHashIndex(self->testVnodeFile1.get()), - self->testVnodeFile1->GetVid(), - rootHandle)); - XCTAssertTrue(RootHandle_None == rootHandle); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testInsertEntryToInvalidatedCache_ExclusiveLocked_CacheFull { - // In production InsertEntryToInvalidatedCache_ExclusiveLocked should never - // be called when the cache is full, but by this test doing so we can validate - // that InsertEntryToInvalidatedCache_ExclusiveLocked logs an error if it fails to - // insert a vnode into the cache - self->cacheWrapper.FillAllEntries(); - - InsertEntryToInvalidatedCache_ExclusiveLocked( - self->testVnodeFile1.get(), - ComputeVnodeHashIndex(self->testVnodeFile1.get()), - self->testVnodeFile1->GetVid(), - DummyRootHandle); - - XCTAssertTrue(MockCalls::DidCallFunction(KextMessageLogged, KEXTLOG_ERROR)); -} - -- (void)testFindVnodeRootFromDiskAndUpdateCache_RefreshAndInvalidateEntry { - VirtualizationRootHandle onDiskRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock( - self->repoRootVnode.get(), - self->repoRootVnode->GetVid(), - FsidInode{ self->repoRootVnode->GetMountPoint()->GetFsid(), self->repoRootVnode->GetInode() }, - self->repoPath.c_str()); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(onDiskRootHandle)); - XCTAssertTrue(DummyRootHandle != onDiskRootHandle); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext); - XCTAssertEqual(foundRoot, onDiskRootHandle); - - // We don't care which mocks were called during the initialization code (above) - MockCalls::Clear(); - - // Make sure the cache is empty - InvalidateCache_ExclusiveLocked(); - - // Insert testFileVnode with DummyRootHandle as its root - uintptr_t indexFromHash = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - uint32_t testVnodeVid = self->testVnodeFile1->GetVid(); - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - false, // forceRefreshEntry - DummyRootHandle)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - - // FindVnodeRootFromDiskAndUpdateCache with UpdateCacheBehavior_ForceRefresh should - // force a lookup of the new root and set it in the cache - VirtualizationRootHandle rootHandle; - FindVnodeRootFromDiskAndUpdateCache( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - self->dummyVFSContext, - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - UpdateCacheBehavior_ForceRefresh, - /* out parameters */ - rootHandle); - XCTAssertTrue(rootHandle == onDiskRootHandle); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(rootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - - // UpdateCacheBehavior_InvalidateEntry means that the root in the cache should be - // set to RootHandle_Indeterminate, but the real root will still be returned - FindVnodeRootFromDiskAndUpdateCache( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - self->dummyVFSContext, - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - UpdateCacheBehavior_InvalidateEntry, - /* out parameters */ - rootHandle); - XCTAssertTrue(rootHandle == onDiskRootHandle); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(RootHandle_Indeterminate == self->cacheWrapper[indexFromHash].virtualizationRoot); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testFindVnodeRootFromDiskAndUpdateCache_FullCache { - VirtualizationRootHandle repoRootHandle = FindOrInsertVirtualizationRoot_LockedMayUnlock( - self->repoRootVnode.get(), - self->repoRootVnode->GetVid(), - FsidInode{ self->repoRootVnode->GetMountPoint()->GetFsid(), self->repoRootVnode->GetInode() }, - self->repoPath.c_str()); - XCTAssertTrue(VirtualizationRoot_IsValidRootHandle(repoRootHandle)); - - VirtualizationRootHandle foundRoot = VirtualizationRoot_FindForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext); - XCTAssertEqual(foundRoot, repoRootHandle); - - // We don't care which mocks were called during the initialization code (above) - MockCalls::Clear(); - - // Fill every entry in the cache - self->cacheWrapper.FillAllEntries(); - - // Insert testFileVnode with DummyRootHandle as its root - uintptr_t indexFromHash = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - uint32_t testVnodeVid = self->testVnodeFile1->GetVid(); - - // UpdateCacheBehavior_TrustCurrentEntry will use the current entry if present - // In this case there is no entry for the vnode and so the cache will be invalidated - // and a new entry added - VirtualizationRootHandle rootHandle; - FindVnodeRootFromDiskAndUpdateCache( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - self->dummyVFSContext, - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - UpdateCacheBehavior_TrustCurrentEntry, - /* out parameters */ - rootHandle); - XCTAssertTrue(rootHandle == repoRootHandle); - - for (uintptr_t index = 0; index < self->cacheWrapper.GetCapacity(); ++index) - { - if (index == indexFromHash) - { - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(rootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - } - else - { - XCTAssertTrue(nullptr == self->cacheWrapper[index].vnode); - XCTAssertTrue(0 == self->cacheWrapper[index].vid); - XCTAssertTrue(0 == self->cacheWrapper[index].virtualizationRoot); - } - } - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testFindVnodeRootFromDiskAndUpdateCache_InvalidUpdateCacheBehaviorValue { - uintptr_t indexFromHash = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - uint32_t testVnodeVid = self->testVnodeFile1->GetVid(); - - // This test is to ensure 100% coverage of FindVnodeRootFromDiskAndUpdateCache - // In production FindVnodeRootFromDiskAndUpdateCache would panic when it sees an - // invalid UpdateCacheBehavior, but in user-mode the assertf if a no-op - VirtualizationRootHandle rootHandle; - [self setExpectedFailedKextAssertionCount:1]; - FindVnodeRootFromDiskAndUpdateCache( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - self->dummyVFSContext, - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - UpdateCacheBehavior_Invalid, - /* out parameters */ - rootHandle); -} - -- (void)testTryFindVnodeIndex_Locked_ReturnsVnodeHashIndexWhenSlotEmpty { - uintptr_t vnodeHashIndex = 5; - uintptr_t cacheIndex; - XCTAssertTrue(TryFindVnodeIndex_Locked(self->testVnodeFile1.get(), vnodeHashIndex, /* out */ cacheIndex)); - XCTAssertTrue(cacheIndex == vnodeHashIndex); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalLookupCollisions] == 0); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryFindVnodeIndex_Locked_ReturnsFalseWhenCacheFull { - self->cacheWrapper.FillAllEntries(); - uintptr_t vnodeIndex; - XCTAssertFalse( - TryFindVnodeIndex_Locked( - self->testVnodeFile1.get(), - ComputeVnodeHashIndex(self->testVnodeFile1.get()), - /* out */ vnodeIndex)); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalLookupCollisions] == self->cacheWrapper.GetCapacity()); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryFindVnodeIndex_Locked_WrapsToBeginningWhenResolvingCollisions { - self->cacheWrapper.FillAllEntries(); - uintptr_t emptyIndex = 2; - cacheWrapper.MarkEntryAsFree(emptyIndex); - - uintptr_t vnodeHashIndex = 5; - uintptr_t vnodeIndex; - XCTAssertTrue(TryFindVnodeIndex_Locked(self->testVnodeFile1.get(), vnodeHashIndex, /* out */ vnodeIndex)); - XCTAssertTrue(emptyIndex == vnodeIndex); - - uint64_t expectedCollisions = (self->cacheWrapper.GetCapacity() - vnodeHashIndex) + emptyIndex; - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalLookupCollisions] == expectedCollisions); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryFindVnodeIndex_Locked_ReturnsLastIndexWhenEmptyAndResolvingCollisions { - self->cacheWrapper.FillAllEntries(); - uintptr_t emptyIndex = cacheWrapper.GetCapacity() - 1; - cacheWrapper.MarkEntryAsFree(emptyIndex); - - uintptr_t vnodeHashIndex = 5; - uintptr_t vnodeIndex; - XCTAssertTrue(TryFindVnodeIndex_Locked(self->testVnodeFile1.get(), vnodeHashIndex, /* out */ vnodeIndex)); - XCTAssertTrue(emptyIndex == vnodeIndex); - - uint64_t expectedCollisions = (self->cacheWrapper.GetCapacity() - 1) - vnodeHashIndex; - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalLookupCollisions] == expectedCollisions); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryInsertOrUpdateEntry_ExclusiveLocked_ReturnsFalseWhenFull { - self->cacheWrapper.FillAllEntries(); - - XCTAssertFalse( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - ComputeVnodeHashIndex(self->testVnodeFile1.get()), - self->testVnodeFile1->GetVid(), - true, // forceRefreshEntry - DummyRootHandle)); - - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalLookupCollisions] == self->cacheWrapper.GetCapacity()); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryInsertOrUpdateEntry_ExclusiveLocked_ReplacesIndeterminateEntry { - uintptr_t indexFromHash = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - uint32_t testVnodeVid = self->testVnodeFile1->GetVid(); - - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - false, // forceRefreshEntry - DummyRootHandle)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - true, // forceRefreshEntry - RootHandle_Indeterminate)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(RootHandle_Indeterminate == self->cacheWrapper[indexFromHash].virtualizationRoot); - - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - testVnodeVid, - false, // forceRefreshEntry - DummyRootHandle)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(testVnodeVid == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - - XCTAssertTrue(DummyRootHandle == VnodeCache_FindRootForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext)); - - XCTAssertTrue(s_cacheStats.cacheEntries == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeHits] == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeMisses] == 0); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryInsertOrUpdateEntry_ExclusiveLocked_ReplacesEntryAfterRecyclingVnode { - uintptr_t indexFromHash = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - self->testVnodeFile1->GetVid(), - false, // forceRefreshEntry - DummyRootHandle)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(self->testVnodeFile1->GetVid() == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - - self->testVnodeFile1->StartRecycling(); - - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - self->testVnodeFile1->GetVid(), - false, // forceRefreshEntry - DummyRootHandleTwo)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(self->testVnodeFile1->GetVid() == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandleTwo == self->cacheWrapper[indexFromHash].virtualizationRoot); - - XCTAssertTrue(DummyRootHandleTwo == VnodeCache_FindRootForVnode( - &self->dummyPerfTracer, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit, - PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss, - PrjFSPerfCounter_VnodeOp_FindRoot, - PrjFSPerfCounter_VnodeOp_FindRoot_Iteration, - self->testVnodeFile1.get(), - self->dummyVFSContext)); - - XCTAssertTrue(s_cacheStats.cacheEntries == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeHits] == 1); - XCTAssertTrue(s_cacheStats.healthStats[VnodeCacheHealthStat_TotalFindRootForVnodeMisses] == 0); - - // Sanity check: We don't expect any of the mock functions to have been called - XCTAssertFalse(MockCalls::DidCallAnyFunctions()); -} - -- (void)testTryInsertOrUpdateEntry_ExclusiveLocked_LogsErrorWhenCacheHasDifferentRoot { - uintptr_t indexFromHash = ComputeVnodeHashIndex(self->testVnodeFile1.get()); - - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - self->testVnodeFile1->GetVid(), - false, // forceRefreshEntry - DummyRootHandle)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(self->testVnodeFile1->GetVid() == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - - XCTAssertTrue( - TryInsertOrUpdateEntry_ExclusiveLocked( - self->testVnodeFile1.get(), - indexFromHash, - self->testVnodeFile1->GetVid(), - false, // forceRefreshEntry - DummyRootHandleTwo)); - XCTAssertTrue(self->testVnodeFile1.get() == self->cacheWrapper[indexFromHash].vnode); - XCTAssertTrue(self->testVnodeFile1->GetVid() == self->cacheWrapper[indexFromHash].vid); - XCTAssertTrue(DummyRootHandle == self->cacheWrapper[indexFromHash].virtualizationRoot); - - XCTAssertTrue(MockCalls::DidCallFunction(KextMessageLogged, KEXTLOG_ERROR)); -} - -@end diff --git a/ProjFS.Mac/PrjFSKextTests/VnodeUtilitiesTests.mm b/ProjFS.Mac/PrjFSKextTests/VnodeUtilitiesTests.mm deleted file mode 100644 index b4d768937b..0000000000 --- a/ProjFS.Mac/PrjFSKextTests/VnodeUtilitiesTests.mm +++ /dev/null @@ -1,47 +0,0 @@ -#include "VnodeUtilities.hpp" -#include "MockVnodeAndMount.hpp" -#import - -using std::shared_ptr; - -@interface VnodeUtilitiesTests : XCTestCase - -@end - -@implementation VnodeUtilitiesTests - -- (void)setUp -{ - [super setUp]; -} - -- (void)tearDown -{ - [super tearDown]; -} - -- (void)testVnodeGetTypeAsString -{ - // Ensure NULL vnode case is handled correctly - XCTAssertEqual(0, strcmp("[NULL]", Vnode_GetTypeAsString(NULLVP))); - - // VREG, VDIR, and VLNK are the most useful for our purposes in practice - shared_ptr testVnode = vnode::Create(nullptr, "/foo", VREG); - XCTAssertEqual(0, strcmp("VREG", Vnode_GetTypeAsString(testVnode.get()))); - testVnode->SetVnodeType(VDIR); - XCTAssertEqual(0, strcmp("VDIR", Vnode_GetTypeAsString(testVnode.get()))); - testVnode->SetVnodeType(VLNK); - XCTAssertEqual(0, strcmp("VLNK", Vnode_GetTypeAsString(testVnode.get()))); - - // Verify that we always get back some kind of sensible string, even if we go out of bounds - static_assert(VNON == 0, "We want to start iterating at 0"); - for (unsigned vtype = VNON; vtype < VCPLX + 10; ++vtype) - { - testVnode->SetVnodeType(static_cast(vtype)); - const char* vtypeString = Vnode_GetTypeAsString(testVnode.get()); - XCTAssertNotEqual(vtypeString, nullptr); - XCTAssertGreaterThan(strlen(vtypeString), 0); - } -} - -@end diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/CallbackDelegates.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/CallbackDelegates.cs deleted file mode 100644 index cf81bdeaee..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/CallbackDelegates.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace PrjFSLib.Mac -{ - // Callbacks - public delegate Result EnumerateDirectoryCallback( - ulong commandId, - string relativePath, - int triggeringProcessId, - string triggeringProcessName); - - public delegate Result GetFileStreamCallback( - ulong commandId, - string relativePath, - [MarshalAs(UnmanagedType.LPArray, SizeConst = Interop.PrjFSLib.PlaceholderIdLength)] - byte[] providerId, - [MarshalAs(UnmanagedType.LPArray, SizeConst = Interop.PrjFSLib.PlaceholderIdLength)] - byte[] contentId, - int triggeringProcessId, - string triggeringProcessName, - IntPtr fileHandle); - - public delegate Result NotifyOperationCallback( - ulong commandId, - string relativePath, - string relativeFromPath, - [MarshalAs(UnmanagedType.LPArray, SizeConst = Interop.PrjFSLib.PlaceholderIdLength)] - byte[] providerId, - [MarshalAs(UnmanagedType.LPArray, SizeConst = Interop.PrjFSLib.PlaceholderIdLength)] - byte[] contentId, - int triggeringProcessId, - string triggeringProcessName, - bool isDirectory, - NotificationType notificationType); - - public delegate void LogErrorCallback( - string errorMessage); - - public delegate void LogWarningCallback( - string warningMessage); - - public delegate void LogInfoCallback( - string infoMessage); - - // Pre-event notifications - public delegate Result NotifyPreDeleteEvent( - string relativePath, - bool isDirectory); - - public delegate Result NotifyFilePreConvertToFullEvent( - string relativePath); - - public delegate Result NotifyPreModifyEvent( - string relativePath); - - // Informational post-event notifications - public delegate void NotifyNewFileCreatedEvent( - string relativePath, - bool isDirectory); - - public delegate void NotifyFileRenamedEvent( - string relativeDestinationPath, - bool isDirectory); - - public delegate void NotifyHardLinkCreatedEvent( - string relativeSourcePath, - string relativeNewLinkPath); - - public delegate void NotifyFileModified( - string relativePath); - - public delegate void NotifyFileDeleted( - string relativePath, - bool isDirectory); -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/Interop/Callbacks.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/Interop/Callbacks.cs deleted file mode 100644 index 9d88328be9..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/Interop/Callbacks.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Runtime.InteropServices; - -namespace PrjFSLib.Mac.Interop -{ - [StructLayout(LayoutKind.Sequential)] - internal struct Callbacks - { - public EnumerateDirectoryCallback OnEnumerateDirectory; - public GetFileStreamCallback OnGetFileStream; - public NotifyOperationCallback OnNotifyOperation; - public LogErrorCallback OnLogError; - public LogWarningCallback OnLogWarning; - public LogInfoCallback OnLogInfo; - } -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/Interop/PrjFSLib.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/Interop/PrjFSLib.cs deleted file mode 100644 index 09cf81bf38..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/Interop/PrjFSLib.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace PrjFSLib.Mac.Interop -{ - internal static class PrjFSLib - { - public const int PlaceholderIdLength = 128; - private const string PrjFSLibPath = "libPrjFSLib.dylib"; - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_StartVirtualizationInstance")] - public static extern Result StartVirtualizationInstance( - string virtualizationRootFullPath, - Callbacks callbacks, - uint poolThreadCount); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_ConvertDirectoryToVirtualizationRoot")] - public static extern Result ConvertDirectoryToVirtualizationRoot( - string virtualizationRootFullPath); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_WritePlaceholderDirectory")] - public static extern Result WritePlaceholderDirectory( - string relativePath); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_WritePlaceholderFile")] - public static extern Result WritePlaceholderFile( - string relativePath, - [MarshalAs(UnmanagedType.LPArray, SizeConst = PlaceholderIdLength)] - byte[] providerId, - [MarshalAs(UnmanagedType.LPArray, SizeConst = PlaceholderIdLength)] - byte[] contentId, - ushort fileMode); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_WriteSymLink")] - public static extern Result WriteSymLink( - string relativePath, - string symLinkTarget); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_UpdatePlaceholderFileIfNeeded")] - public static extern Result UpdatePlaceholderFileIfNeeded( - string relativePath, - [MarshalAs(UnmanagedType.LPArray, SizeConst = PlaceholderIdLength)] - byte[] providerId, - [MarshalAs(UnmanagedType.LPArray, SizeConst = PlaceholderIdLength)] - byte[] contentId, - ushort fileMode, - UpdateType updateType, - ref UpdateFailureCause failureCause); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_ReplacePlaceholderFileWithSymLink")] - public static extern Result ReplacePlaceholderFileWithSymLink( - string relativePath, - string symLinkTarget, - UpdateType updateType, - ref UpdateFailureCause failureCause); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_DeleteFile")] - public static extern Result DeleteFile( - string relativePath, - UpdateType updateType, - ref UpdateFailureCause failureCause); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_WriteFileContents")] - public static extern Result WriteFileContents( - IntPtr fileHandle, - IntPtr bytes, - uint byteCount); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_RegisterForOfflineIO")] - public static extern Result RegisterForOfflineIO(); - - [DllImport(PrjFSLibPath, EntryPoint = "PrjFS_UnregisterForOfflineIO")] - public static extern Result UnregisterForOfflineIO(); - } -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/NotificationMapping.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/NotificationMapping.cs deleted file mode 100644 index 7d26a0ffc4..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/NotificationMapping.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace PrjFSLib.Mac -{ - public class NotificationMapping - { - public NotificationType NotificationMask { get; set; } - public string NotificationRelativeRoot { get; set; } - } -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/NotificationType.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/NotificationType.cs deleted file mode 100644 index cd3a1a5e4a..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/NotificationType.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace PrjFSLib.Mac -{ - [Flags] - public enum NotificationType - { - Invalid = 0x00000000, - - None = 0x00000001, - NewFileCreated = 0x00000004, - PreDelete = 0x00000010, - PreDeleteFromRename = 0x00000011, - FileRenamed = 0x00000080, - HardLinkCreated = 0x00000100, - PreConvertToFull = 0x00001000, - - PreModify = 0x10000001, - FileModified = 0x10000002, - FileDeleted = 0x10000004, - } -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/OfflineIO.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/OfflineIO.cs deleted file mode 100644 index a33bf82613..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/OfflineIO.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -namespace PrjFSLib.Mac.Managed -{ - public class OfflineIO - { - public static bool RegisterForOfflineIO() - { - return Result.Success == Interop.PrjFSLib.RegisterForOfflineIO(); - } - - public static bool UnregisterForOfflineIO() - { - return Result.Success == Interop.PrjFSLib.UnregisterForOfflineIO(); - } - } -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/PrjFSLib.Mac.Managed.csproj b/ProjFS.Mac/PrjFSLib.Mac.Managed/PrjFSLib.Mac.Managed.csproj deleted file mode 100644 index daef8c6785..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/PrjFSLib.Mac.Managed.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - netcoreapp2.1;netstandard2.0 - x64 - Debug;Release - ..\..\GVFS\GVFS.Build\GVFS.ruleset - true - - - - true - - - true - - - - ..\..\..\BuildOutput\ProjFS.Mac\PrjFSLib.Mac.Managed\bin\$(Platform)\$(Configuration)\ - ..\..\..\BuildOutput\ProjFS.Mac\PrjFSLib.Mac.Managed\obj\$(Platform)\$(Configuration)\ - - - - true - full - false - TRACE;DEBUG - - - - true - TRACE;RELEASE - - - - - - - - - all - - - diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/Result.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/Result.cs deleted file mode 100644 index 77fe0b0773..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/Result.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace PrjFSLib.Mac -{ - public enum Result : uint - { - Invalid = 0x00000000, - - Success = 0x00000001, - Pending = 0x00000002, - - // Bugs in the caller - EInvalidArgs = 0x10000001, - EInvalidOperation = 0x10000002, - ENotSupported = 0x10000004, - - // Runtime errors - EDriverNotLoaded = 0x20000001, - EOutOfMemory = 0x20000002, - EFileNotFound = 0x20000004, - EPathNotFound = 0x20000008, - EAccessDenied = 0x20000010, - EInvalidHandle = 0x20000020, - EIOError = 0x20000040, - ENotAVirtualizationRoot = 0x20000080, - EVirtualizationRootAlreadyExists = 0x20000100, - EDirectoryNotEmpty = 0x20000200, - EVirtualizationInvalidOperation = 0x20000400, - - ENotYetImplemented = 0xFFFFFFFF, - } -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/UpdateFailureCause.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/UpdateFailureCause.cs deleted file mode 100644 index c68349372e..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/UpdateFailureCause.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace PrjFSLib.Mac -{ - [Flags] - public enum UpdateFailureCause - { - NoFailure = 0x00000000, - DirtyData = 0x00000002, - ReadOnly = 0x00000008, - } -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/UpdateType.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/UpdateType.cs deleted file mode 100644 index b0f021823b..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/UpdateType.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace PrjFSLib.Mac -{ - [Flags] - public enum UpdateType - { - Invalid = 0x00000000, - - AllowReadOnly = 0x00000020, - } -} diff --git a/ProjFS.Mac/PrjFSLib.Mac.Managed/VirtualizationInstance.cs b/ProjFS.Mac/PrjFSLib.Mac.Managed/VirtualizationInstance.cs deleted file mode 100644 index fa3fb79a90..0000000000 --- a/ProjFS.Mac/PrjFSLib.Mac.Managed/VirtualizationInstance.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace PrjFSLib.Mac -{ - public class VirtualizationInstance - { - public const int PlaceholderIdLength = Interop.PrjFSLib.PlaceholderIdLength; - - // We must hold a reference to the delegate to prevent garbage collection - private NotifyOperationCallback preventGCOnNotifyOperationDelegate; - - // References held to these delegates via class properties - public virtual EnumerateDirectoryCallback OnEnumerateDirectory { get; set; } - public virtual GetFileStreamCallback OnGetFileStream { get; set; } - public virtual LogErrorCallback OnLogError { get; set; } - public virtual LogWarningCallback OnLogWarning { get; set; } - public virtual LogInfoCallback OnLogInfo { get; set; } - - public virtual NotifyFileModified OnFileModified { get; set; } - public virtual NotifyFilePreConvertToFullEvent OnFilePreConvertToFull { get; set; } - public virtual NotifyPreDeleteEvent OnPreDelete { get; set; } - public virtual NotifyNewFileCreatedEvent OnNewFileCreated { get; set; } - public virtual NotifyFileRenamedEvent OnFileRenamed { get; set; } - public virtual NotifyHardLinkCreatedEvent OnHardLinkCreated { get; set; } - - public static Result ConvertDirectoryToVirtualizationRoot(string fullPath) - { - return Interop.PrjFSLib.ConvertDirectoryToVirtualizationRoot(fullPath); - } - - public virtual Result StartVirtualizationInstance( - string virtualizationRootFullPath, - uint poolThreadCount) - { - Interop.Callbacks callbacks = new Interop.Callbacks - { - OnEnumerateDirectory = this.OnEnumerateDirectory, - OnGetFileStream = this.OnGetFileStream, - OnNotifyOperation = this.preventGCOnNotifyOperationDelegate = new NotifyOperationCallback(this.OnNotifyOperation), - OnLogError = this.OnLogError, - OnLogWarning = this.OnLogWarning, - OnLogInfo = this.OnLogInfo - }; - - return Interop.PrjFSLib.StartVirtualizationInstance( - virtualizationRootFullPath, - callbacks, - poolThreadCount); - } - - public virtual Result StopVirtualizationInstance() - { - throw new NotImplementedException(); - } - - public virtual Result WriteFileContents( - IntPtr fileHandle, - byte[] bytes, - uint byteCount) - { - GCHandle bytesHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - try - { - return Interop.PrjFSLib.WriteFileContents( - fileHandle, - bytesHandle.AddrOfPinnedObject(), - byteCount); - } - finally - { - bytesHandle.Free(); - } - } - - public virtual Result DeleteFile( - string relativePath, - UpdateType updateFlags, - out UpdateFailureCause failureCause) - { - UpdateFailureCause deleteFailureCause = UpdateFailureCause.NoFailure; - Result result = Interop.PrjFSLib.DeleteFile( - relativePath, - updateFlags, - ref deleteFailureCause); - - failureCause = deleteFailureCause; - return result; - } - - public virtual Result WritePlaceholderDirectory( - string relativePath) - { - return Interop.PrjFSLib.WritePlaceholderDirectory(relativePath); - } - - public virtual Result WritePlaceholderFile( - string relativePath, - byte[] providerId, - byte[] contentId, - ushort fileMode) - { - if (providerId.Length != Interop.PrjFSLib.PlaceholderIdLength || - contentId.Length != Interop.PrjFSLib.PlaceholderIdLength) - { - throw new ArgumentException(); - } - - return Interop.PrjFSLib.WritePlaceholderFile( - relativePath, - providerId, - contentId, - fileMode); - } - - public virtual Result WriteSymLink( - string relativePath, - string symLinkTarget) - { - return Interop.PrjFSLib.WriteSymLink(relativePath, symLinkTarget); - } - - public virtual Result UpdatePlaceholderIfNeeded( - string relativePath, - byte[] providerId, - byte[] contentId, - ushort fileMode, - UpdateType updateFlags, - out UpdateFailureCause failureCause) - { - if (providerId.Length != Interop.PrjFSLib.PlaceholderIdLength || - contentId.Length != Interop.PrjFSLib.PlaceholderIdLength) - { - throw new ArgumentException(); - } - - UpdateFailureCause updateFailureCause = UpdateFailureCause.NoFailure; - Result result = Interop.PrjFSLib.UpdatePlaceholderFileIfNeeded( - relativePath, - providerId, - contentId, - fileMode, - updateFlags, - ref updateFailureCause); - - failureCause = updateFailureCause; - return result; - } - - public virtual Result ReplacePlaceholderFileWithSymLink( - string relativePath, - string symLinkTarget, - UpdateType updateFlags, - out UpdateFailureCause failureCause) - { - UpdateFailureCause updateFailureCause = UpdateFailureCause.NoFailure; - Result result = Interop.PrjFSLib.ReplacePlaceholderFileWithSymLink( - relativePath, - symLinkTarget, - updateFlags, - ref updateFailureCause); - - failureCause = updateFailureCause; - return result; - } - - public virtual Result CompleteCommand( - ulong commandId, - Result result) - { - throw new NotImplementedException(); - } - - public virtual Result ConvertDirectoryToPlaceholder( - string relativeDirectoryPath) - { - throw new NotImplementedException(); - } - - private Result OnNotifyOperation( - ulong commandId, - string relativePath, - string relativeFromPath, - byte[] providerId, - byte[] contentId, - int triggeringProcessId, - string triggeringProcessName, - bool isDirectory, - NotificationType notificationType) - { - switch (notificationType) - { - case NotificationType.PreDelete: - case NotificationType.PreDeleteFromRename: - return this.OnPreDelete(relativePath, isDirectory); - - case NotificationType.FileModified: - this.OnFileModified(relativePath); - return Result.Success; - - case NotificationType.NewFileCreated: - this.OnNewFileCreated(relativePath, isDirectory); - return Result.Success; - - case NotificationType.FileRenamed: - this.OnFileRenamed(relativePath, isDirectory); - return Result.Success; - - case NotificationType.HardLinkCreated: - this.OnHardLinkCreated(relativeFromPath, relativePath); - return Result.Success; - - case NotificationType.PreConvertToFull: - return this.OnFilePreConvertToFull(relativePath); - } - - return Result.ENotYetImplemented; - } - } -} diff --git a/ProjFS.Mac/PrjFSLib/Json/JsonWriter.cpp b/ProjFS.Mac/PrjFSLib/Json/JsonWriter.cpp deleted file mode 100644 index e0ef394681..0000000000 --- a/ProjFS.Mac/PrjFSLib/Json/JsonWriter.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "JsonWriter.hpp" - -using std::string; - -JsonWriter::JsonWriter() - : jsonBuffer("{") -{ -} - -JsonWriter::~JsonWriter() -{ -} - -void JsonWriter::Add(const string& key, const JsonWriter& value) -{ - this->AddCommaIfNeeded(); - this->AddKey(key); - this->jsonBuffer += value.ToString(); -} - -void JsonWriter::Add(const string& key, const string& value) -{ - this->AddCommaIfNeeded(); - this->AddKey(key); - this->AddString(value); -} - -void JsonWriter::Add(const std::string& key, int32_t value) -{ - this->AddUnquoted(key, value); -} - -void JsonWriter::Add(const string& key, uint32_t value) -{ - this->AddUnquoted(key, value); -} - -void JsonWriter::Add(const string& key, uint64_t value) -{ - this->AddUnquoted(key, value); -} - -string JsonWriter::ToString() const -{ - return this->jsonBuffer + "}"; -} - -void JsonWriter::AddCommaIfNeeded() -{ - if ("{" != this->jsonBuffer) - { - this->jsonBuffer += ","; - } -} - -void JsonWriter::AddKey(const string& key) -{ - this->AddString(key); - this->jsonBuffer += ":"; -} - -void JsonWriter::AddString(const string& value) -{ - this->jsonBuffer += "\""; - - for (char c : value) - { - if (c == '"') - { - this->jsonBuffer += "\\\""; - } - else if (c == '\\') - { - this->jsonBuffer += "\\\\"; - } - else if (c == '\n') - { - this->jsonBuffer += "\\n"; - } - else if (c == '\r') - { - this->jsonBuffer += "\\r"; - } - else if (c == '\t') - { - this->jsonBuffer += "\\t"; - } - else if (c == '\f') - { - this->jsonBuffer += "\\f"; - } - else if (c == '\b') - { - this->jsonBuffer += "\\b"; - } - else if (static_cast(c) < 0x20) - { - char buffer[16]; - snprintf(buffer, sizeof(buffer), "\\u%04x", c); - this->jsonBuffer += buffer; - } - else - { - this->jsonBuffer += c; - } - } - - this->jsonBuffer += "\""; -} diff --git a/ProjFS.Mac/PrjFSLib/Json/JsonWriter.hpp b/ProjFS.Mac/PrjFSLib/Json/JsonWriter.hpp deleted file mode 100644 index 3558bdd340..0000000000 --- a/ProjFS.Mac/PrjFSLib/Json/JsonWriter.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -class JsonWriter -{ -public: - JsonWriter(); - ~JsonWriter(); - - void Add(const std::string& key, const JsonWriter& value); - void Add(const std::string& key, const std::string& value); - - void Add(const std::string& key, int32_t value); - void Add(const std::string& key, uint32_t value); - void Add(const std::string& key, uint64_t value); - - std::string ToString() const; - -private: - void AddCommaIfNeeded(); - void AddKey(const std::string& key); - void AddString(const std::string& value); - - template - void AddUnquoted(const std::string& key, T value); - - std::string jsonBuffer; -}; - -template -void JsonWriter::AddUnquoted(const std::string& key, T value) -{ - this->AddCommaIfNeeded(); - this->AddKey(key); - this->jsonBuffer += std::to_string(value); -} diff --git a/ProjFS.Mac/PrjFSLib/PrjFSLib.cpp b/ProjFS.Mac/PrjFSLib/PrjFSLib.cpp deleted file mode 100644 index c7f1f87e75..0000000000 --- a/ProjFS.Mac/PrjFSLib/PrjFSLib.cpp +++ /dev/null @@ -1,1736 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "stdlib.h" - -#include "PrjFSLib.h" -#include "../PrjFSKext/public/PrjFSCommon.h" -#include "../PrjFSKext/public/PrjFSXattrs.h" -#include "../PrjFSKext/public/Message.h" -#include "PrjFSUser.hpp" - -#define STRINGIFY(s) #s - -using std::cerr; -using std::cout; -using std::dec; -using std::endl; -using std::extent; -using std::hex; -using std::is_pod; -using std::lock_guard; -using std::make_pair; -using std::make_shared; -using std::map; -using std::move; -using std::mutex; -using std::oct; -using std::ostringstream; -using std::pair; -using std::queue; -using std::set; -using std::shared_ptr; -using std::stack; -using std::string; - -typedef lock_guard mutex_lock; - -// Structs -struct _PrjFS_FileHandle -{ - FILE* file; -}; - -struct FsidInodeCompare -{ - bool operator() (const FsidInode& lhs, const FsidInode& rhs) const - { - if (lhs.fsid.val[0] != rhs.fsid.val[0]) - { - return lhs.fsid.val[0] < rhs.fsid.val[0]; - } - - if (lhs.fsid.val[1] != rhs.fsid.val[1]) - { - return lhs.fsid.val[1] < rhs.fsid.val[1]; - } - - return lhs.inode < rhs.inode; - } -}; - -struct MutexAndUseCount -{ - shared_ptr mutex; - int useCount; -}; - -typedef map FileMutexMap; - -static fsid_t s_virtualizationRoot_fsid; - -// Function prototypes -static bool SetBitInFileFlags(const char* fullPath, uint32_t bit, bool value); -static bool IsBitSetInFileFlags(const char* fullPath, uint32_t bit); - -static bool InitializeEmptyPlaceholder(const char* fullPath); -template static bool InitializeEmptyPlaceholder(const char* fullPath, TPlaceholder* data, const char* xattrName); -static errno_t AddXAttr(const char* fullPath, const char* name, const void* value, size_t size); -static bool TryGetXAttr(const char* fullPath, const char* name, size_t expectedSize, _Out_ void* value); -static errno_t RemoveXAttrWithoutFollowingLinks(const char* fullPath, const char* name); - -static inline PrjFS_NotificationType KUMessageTypeToNotificationType(MessageType kuNotificationType); - -static bool IsVirtualizationRoot(const char* fullPath); -static void CombinePaths(const char* root, const char* relative, char (&combined)[PrjFSMaxPath]); -static const char* GetRelativePath(const char* fullPath, const char* root); - -static errno_t SendKernelMessageResponse(uint64_t messageId, MessageType responseType); -static errno_t RegisterVirtualizationRootPath(const char* fullPath); - -static PrjFS_Result RecursivelyMarkAllChildrenAsInRoot(const char* fullDirectoryPath); - -static void HandleKernelRequest(void* messageMemory, uint32_t messageSize); -static PrjFS_Result HandleEnumerateDirectoryRequest(const MessageHeader* request, const char* absolutePath, const char* relativePath); -static PrjFS_Result HandleRecursivelyEnumerateDirectoryRequest(const MessageHeader* request, const char* absolutePath, const char* relativePath); -static PrjFS_Result HandleHydrateFileRequest(const MessageHeader* request, const char* absolutePath, const char* relativePath); -static PrjFS_Result HydrateFile(const char* absolutePath, const char* relativePath, FsidInode fsidInode, pid_t pid, const char* procname); -static PrjFS_Result HandleNewFileInRootNotification( - const MessageHeader* request, - const char* relativePath, - const char* fullPath, - const char* relativeFromPath, - bool isDirectory, - PrjFS_NotificationType notificationType); -static PrjFS_Result HandleFileNotification( - const MessageHeader* request, - const char* relativePath, - const char* fullPath, - const char* relativeFromPath, - bool isDirectory, - PrjFS_NotificationType notificationType); - -static void FindNewFoldersInRootAndNotifyProvider(const MessageHeader* request, const char* relativePath); -static bool IsDirEntChildDirectory(const dirent* directoryEntry); - -static Message ParseMessageMemory(const void* messageMemory, uint32_t size); - -#ifdef DEBUG -static const char* NotificationTypeToString(PrjFS_NotificationType notificationType); -#endif - -static FileMutexMap::iterator CheckoutFileMutexIterator(const FsidInode& fsidInode); -static void ReturnFileMutexIterator(FileMutexMap::iterator lockIterator); - -static void LogError(const char* formatString, ...) __attribute__((__format__ (printf, 1, 2))); -static void LogWarning(const char* formatString, ...) __attribute__((__format__ (printf, 1, 2))); -static void LogInfo(const char* formatString, ...) __attribute__((__format__ (printf, 1, 2))); - -// State -static io_connect_t s_kernelServiceConnection = IO_OBJECT_NULL; -static string s_virtualizationRootFullPath; -static PrjFS_Callbacks s_callbacks; -static dispatch_queue_t s_messageQueueDispatchQueue; -static dispatch_queue_t s_kernelRequestHandlingConcurrentQueue; - -static mutex s_kernelServiceOfflineClientMutex; -static uint32_t s_kernelServiceOfflineClientCount = 0; -static io_connect_t s_kernelServiceOfflineWriterConnection = IO_OBJECT_NULL; - - -// Map of FsidInode -> MutexAndUseCount for that FsidInode, plus mutex to protect the map itself. -static FileMutexMap s_fileLocks; -static mutex s_fileLocksMutex; - -// The full API is defined in the header, but only the minimal set of functions needed -// for the initial MirrorProvider implementation are listed here. Calling any other function -// will lead to a linker error for now. - -// Public functions - -PrjFS_Result PrjFS_RegisterForOfflineIO() -{ - mutex_lock lock(s_kernelServiceOfflineClientMutex); - - // Reference counting offline I/O registrations so that each process only - // maintains a single user client connection to the kext even if multiple - // registration calls are made. - if (s_kernelServiceOfflineClientCount == 0) - { - assert(s_kernelServiceOfflineWriterConnection == IO_OBJECT_NULL); - s_kernelServiceOfflineWriterConnection = PrjFSService_ConnectToDriver( - UserClientType_OfflineIO, - false); // Don't require user/kernel versions to match, as this type of user client does not have a version-sensitive API at this time. - if (s_kernelServiceOfflineWriterConnection == IO_OBJECT_NULL) - { - return PrjFS_Result_EDriverNotLoaded; - } - } - - ++s_kernelServiceOfflineClientCount; - - return PrjFS_Result_Success; -} - -PrjFS_Result PrjFS_UnregisterForOfflineIO() -{ - mutex_lock lock(s_kernelServiceOfflineClientMutex); - - // If these assertions fail, the calling code has called Unregister - // more times than Register succeeded, indicating a bug. - assert(s_kernelServiceOfflineClientCount > 0); - assert(s_kernelServiceOfflineWriterConnection != IO_OBJECT_NULL); - --s_kernelServiceOfflineClientCount; - if (s_kernelServiceOfflineClientCount == 0) - { - IOServiceClose(s_kernelServiceOfflineWriterConnection); - s_kernelServiceOfflineWriterConnection = IO_OBJECT_NULL; - } - - return PrjFS_Result_Success; -} - -PrjFS_Result PrjFS_StartVirtualizationInstance( - _In_ const char* virtualizationRootFullPath, - _In_ PrjFS_Callbacks callbacks, - _In_ unsigned int poolThreadCount) -{ -#ifdef DEBUG - cout - << "PrjFS_StartVirtualizationInstance(" - << virtualizationRootFullPath << ", " - << callbacks.EnumerateDirectory << ", " - << callbacks.GetFileStream << ", " - << callbacks.NotifyOperation << ", " - << callbacks.LogError << "," - << callbacks.LogWarning << "," - << callbacks.LogInfo << "," - << poolThreadCount << ")" << endl; -#endif - - if (nullptr == virtualizationRootFullPath || - nullptr == callbacks.EnumerateDirectory || - nullptr == callbacks.GetFileStream || - nullptr == callbacks.NotifyOperation || - nullptr == callbacks.LogError || - nullptr == callbacks.LogWarning || - nullptr == callbacks.LogInfo) - { - return PrjFS_Result_EInvalidArgs; - } - - if (!s_virtualizationRootFullPath.empty()) - { - LogError("PrjFS_StartVirtualizationInstance: s_virtualizationRootFullPath is not empty"); - return PrjFS_Result_EInvalidOperation; - } - - if (!IsVirtualizationRoot(virtualizationRootFullPath)) - { - return PrjFS_Result_ENotAVirtualizationRoot; - } - - s_kernelServiceConnection = PrjFSService_ConnectToDriver(UserClientType_Provider); - if (IO_OBJECT_NULL == s_kernelServiceConnection) - { - return PrjFS_Result_EDriverNotLoaded; - } - - DataQueueResources dataQueue; - s_messageQueueDispatchQueue = dispatch_queue_create("PrjFS Kernel Message Handling", DISPATCH_QUEUE_SERIAL); - if (!PrjFSService_DataQueueInit(&dataQueue, s_kernelServiceConnection, ProviderPortType_MessageQueue, ProviderMemoryType_MessageQueue, s_messageQueueDispatchQueue)) - { - LogError("PrjFS_StartVirtualizationInstance: Failed to set up shared data queue."); - return PrjFS_Result_EInvalidOperation; - } - - s_virtualizationRootFullPath = virtualizationRootFullPath; - s_callbacks = callbacks; - - errno_t error = RegisterVirtualizationRootPath(virtualizationRootFullPath); - if (error != 0) - { - LogError("PrjFS_StartVirtualizationInstance: Registering virtualization root failed: error=%d strerror=%s", error, strerror(error)); - return PrjFS_Result_EInvalidOperation; - } - - struct statfs rootAttributes; - if (0 != statfs(virtualizationRootFullPath, &rootAttributes)) - { - LogError("PrjFS_StartVirtualizationInstance: statfs failed on %s errno=%d, strerror=%s", virtualizationRootFullPath, errno, strerror(errno)); - return PrjFS_Result_EInvalidOperation; - } - s_virtualizationRoot_fsid = rootAttributes.f_fsid; - - s_kernelRequestHandlingConcurrentQueue = dispatch_queue_create("PrjFS Kernel Request Handling", DISPATCH_QUEUE_CONCURRENT); - - dispatch_source_set_event_handler(dataQueue.dispatchSource, ^{ - DataQueue_ClearMachNotification(dataQueue.notificationPort); - - while (1) - { - IODataQueueEntry* entry = DataQueue_Peek(dataQueue.queueMemory); - if (nullptr == entry) - { - // No more items in queue - break; - } - - uint32_t messageSize = entry->size; - if (messageSize < sizeof(Message)) - { - LogError("PrjFS_StartVirtualizationInstance: Bad message size: got %d bytes, expected minimum of %lu, skipping. Kernel/user version mismatch?", messageSize, sizeof(Message)); - DataQueue_Dequeue(dataQueue.queueMemory, nullptr, nullptr); - continue; - } - - void* messageMemory = malloc(messageSize); - uint32_t dequeuedSize = messageSize; - IOReturn result = DataQueue_Dequeue(dataQueue.queueMemory, messageMemory, &dequeuedSize); - if (kIOReturnSuccess != result || dequeuedSize != messageSize) - { - LogError("PrjFS_StartVirtualizationInstance: Unexpected result dequeueing message - result 0x%08x dequeued %d/%d bytes", result, dequeuedSize, messageSize); - abort(); - } - - dispatch_async( - s_kernelRequestHandlingConcurrentQueue, - ^{ - HandleKernelRequest(messageMemory, messageSize); - }); - } - }); - dispatch_resume(dataQueue.dispatchSource); - - return PrjFS_Result_Success; -} - -PrjFS_Result PrjFS_ConvertDirectoryToVirtualizationRoot( - _In_ const char* virtualizationRootFullPath) -{ -#ifdef DEBUG - cout - << "PrjFS_ConvertDirectoryToVirtualizationRoot(" - << virtualizationRootFullPath - << ")" << endl; -#endif - - if (nullptr == virtualizationRootFullPath) - { - return PrjFS_Result_EInvalidArgs; - } - - // TODO(#1373): walk entire parent chain to root and all child directories to leaf nodes, to make sure we find no other virtualization roots. - // It is not allowed to have nested virtualization roots. - - if (IsBitSetInFileFlags(virtualizationRootFullPath, FileFlags_IsInVirtualizationRoot) || - IsVirtualizationRoot(virtualizationRootFullPath)) - { - return PrjFS_Result_EVirtualizationRootAlreadyExists; - } - - PrjFSVirtualizationRootXAttrData rootXattrData = {}; - if (!InitializeEmptyPlaceholder( - virtualizationRootFullPath, - &rootXattrData, - PrjFSVirtualizationRootXAttrName)) - { - return PrjFS_Result_EIOError; - } - - return RecursivelyMarkAllChildrenAsInRoot(virtualizationRootFullPath); -} - -PrjFS_Result PrjFS_WritePlaceholderDirectory( - _In_ const char* relativePath) -{ -#ifdef DEBUG - cout - << "PrjFS_WritePlaceholderDirectory(" - << relativePath - << ")" << endl; -#endif - - if (nullptr == relativePath) - { - return PrjFS_Result_EInvalidArgs; - } - - PrjFS_Result result = PrjFS_Result_Invalid; - char fullPath[PrjFSMaxPath]; - CombinePaths(s_virtualizationRootFullPath.c_str(), relativePath, fullPath); - - if (mkdir(fullPath, 0777)) - { - switch(errno) - { - // TODO(#1371): Return more specific error codes for other failure scenarios - case ENOENT: // A component of the path prefix does not exist or path is an empty string - result = PrjFS_Result_EPathNotFound; - break; - default: - LogWarning("PrjFS_WritePlaceholderDirectory: mkdir failed errno=%d stderror=%s", errno, strerror(errno)); - result = PrjFS_Result_EIOError; - break; - } - - goto CleanupAndFail; - } - - if (!InitializeEmptyPlaceholder(fullPath)) - { - result = PrjFS_Result_EIOError; - goto CleanupAndFail; - } - - return PrjFS_Result_Success; - -CleanupAndFail: - // TODO(#1371): cleanup the directory on disk if needed - return result; -} - -PrjFS_Result PrjFS_WritePlaceholderFile( - _In_ const char* relativePath, - _In_ unsigned char providerId[PrjFS_PlaceholderIdLength], - _In_ unsigned char contentId[PrjFS_PlaceholderIdLength], - _In_ uint16_t fileMode) -{ -#ifdef DEBUG - cout - << "PrjFS_WritePlaceholderFile(" - << relativePath << ", " - << (int)providerId[0] << ", " - << (int)contentId[0] << ", " - << oct << fileMode << dec << ")" << endl; -#endif - - if (nullptr == relativePath) - { - return PrjFS_Result_EInvalidArgs; - } - - PrjFS_Result result = PrjFS_Result_Invalid; - PrjFSFileXAttrData fileXattrData = {}; - - char fullPath[PrjFSMaxPath]; - CombinePaths(s_virtualizationRootFullPath.c_str(), relativePath, fullPath); - - // Mode "wx" means: - // - "w": Open for writing. The stream is positioned at the beginning of the file. Create the file if it does not exist. - // - "x": If the file already exists, fopen() fails, and sets errno to EEXIST. - FILE* file = fopen(fullPath, "wx"); - if (nullptr == file) - { - switch(errno) - { - // TODO(#1371): Return more specific error codes for other failure scenarios - case ENOENT: // A directory component in fullPath does not exist or is a dangling symbolic link. - result = PrjFS_Result_EPathNotFound; - break; - case EEXIST: // The file already exists - default: - LogWarning("PrjFS_WritePlaceholderFile: fopen failed filename=%s errorno=%d stderror=%s", fullPath, errno, strerror(errno)); - result = PrjFS_Result_EIOError; - break; - } - - goto CleanupAndFail; - } - - fclose(file); - file = nullptr; - - memcpy(fileXattrData.providerId, providerId, PrjFS_PlaceholderIdLength); - memcpy(fileXattrData.contentId, contentId, PrjFS_PlaceholderIdLength); - - if (!InitializeEmptyPlaceholder( - fullPath, - &fileXattrData, - PrjFSFileXAttrName)) - { - result = PrjFS_Result_EIOError; - goto CleanupAndFail; - } - - if ((fileMode & (S_IXUSR|S_IXGRP|S_IXOTH)) != 0) - { - struct stat fileAttributes; - if (0 != lstat(fullPath, &fileAttributes)) - { - LogWarning("PrjFS_WritePlaceholderFile: lstat failed on %s errno=%d, strerror=%s", fullPath, errno, strerror(errno)); - result = PrjFS_Result_EIOError; - goto CleanupAndFail; - } - - FsidInode fsidInode; - fsidInode.inode = fileAttributes.st_ino; - fsidInode.fsid = s_virtualizationRoot_fsid; - - if (HydrateFile(fullPath, relativePath, fsidInode, 1, "placeholder") != PrjFS_Result_Success) - { - LogWarning("PrjFS_WritePlaceholderFile: failed to hydrate executable %s", fullPath); - result = PrjFS_Result_EIOError; - goto CleanupAndFail; - } - } - - // TODO(#1370): Only call chmod if fileMode is different than the default file mode - if (chmod(fullPath, fileMode)) - { - LogWarning("PrjFS_WritePlaceholderFile: failed to change permissions for %s errno=%d strerror=%s", fullPath, errno, strerror(errno)); - result = PrjFS_Result_EIOError; - goto CleanupAndFail; - } - - return PrjFS_Result_Success; - -CleanupAndFail: - if (nullptr != file) - { - // TODO(#234): we now have a partially created placeholder file. Should we delete it? - // A better pattern would likely be to create the file in a tmp location, fully initialize its state, then move it into the requested path - fclose(file); - file = nullptr; - } - - return result; -} - -PrjFS_Result PrjFS_WriteSymLink( - _In_ const char* relativePath, - _In_ const char* symLinkTarget) -{ -#ifdef DEBUG - cout - << "PrjFS_WriteSymLink(" - << relativePath << ", " - << symLinkTarget << ")" << endl; -#endif - - if (nullptr == relativePath || nullptr == symLinkTarget) - { - return PrjFS_Result_EInvalidArgs; - } - - char fullPath[PrjFSMaxPath]; - CombinePaths(s_virtualizationRootFullPath.c_str(), relativePath, fullPath); - - if(symlink(symLinkTarget, fullPath)) - { - LogWarning("PrjFS_WriteSymLink: symLink call failed symLinkTarget=%s fullPath=%s errno=%d, strerror=%s", symLinkTarget, fullPath, errno, strerror(errno)); - goto CleanupAndFail; - } - - // TODO(#391): Handles failures of SetBitInFileFlags - SetBitInFileFlags(fullPath, FileFlags_IsInVirtualizationRoot, true); - - return PrjFS_Result_Success; - -CleanupAndFail: - - return PrjFS_Result_EIOError; - -} - -PrjFS_Result PrjFS_UpdatePlaceholderFileIfNeeded( - _In_ const char* relativePath, - _In_ unsigned char providerId[PrjFS_PlaceholderIdLength], - _In_ unsigned char contentId[PrjFS_PlaceholderIdLength], - _In_ uint16_t fileMode, - _In_ PrjFS_UpdateType updateFlags, - _Out_ PrjFS_UpdateFailureCause* failureCause) -{ -#ifdef DEBUG - cout - << "PrjFS_UpdatePlaceholderFileIfNeeded(" - << relativePath << ", " - << (int)providerId[0] << ", " - << (int)contentId[0] << ", " - << oct << fileMode << dec << ", " - << hex << updateFlags << dec << ")" << endl; -#endif - - // TODO(#1372): Check if the contentId or fileMode have changed before proceeding - // with the update - - PrjFS_Result result = PrjFS_DeleteFile(relativePath, updateFlags, failureCause); - if (result != PrjFS_Result_Success) - { - LogWarning("PrjFS_UpdatePlaceholderFileIfNeeded: PrjFS_DeleteFile call failed relativePath=%s updateFlags=%d failureCause=%d", relativePath, updateFlags, *failureCause); - return result; - } - - // TODO(#1372): Ensure that races with hydration are handled properly - return PrjFS_WritePlaceholderFile(relativePath, providerId, contentId, fileMode); -} - -PrjFS_Result PrjFS_ReplacePlaceholderFileWithSymLink( - _In_ const char* relativePath, - _In_ const char* symLinkTarget, - _In_ PrjFS_UpdateType updateFlags, - _Out_ PrjFS_UpdateFailureCause* failureCause) -{ -#ifdef DEBUG - cout - << "PrjFS_ReplacePlaceholderFileWithSymLink(" - << relativePath << ", " - << symLinkTarget << ", " - << hex << updateFlags << dec << ")" << endl; -#endif - - PrjFS_Result result = PrjFS_DeleteFile(relativePath, updateFlags, failureCause); - if (result != PrjFS_Result_Success) - { - LogWarning("PrjFS_ReplacePlaceholderFileWithSymLink: PrjFS_DeleteFile call failed relativePath=%s updateFlags=%d failureCause=%d", relativePath, updateFlags, *failureCause); - return result; - } - - return PrjFS_WriteSymLink(relativePath, symLinkTarget); -} - -PrjFS_Result PrjFS_DeleteFile( - _In_ const char* relativePath, - _In_ PrjFS_UpdateType updateFlags, - _Out_ PrjFS_UpdateFailureCause* failureCause) -{ -#ifdef DEBUG - cout - << "PrjFS_DeleteFile(" - << relativePath << ", " - << hex << updateFlags << dec << ")" << endl; -#endif - - *failureCause = PrjFS_UpdateFailureCause_Invalid; - - if (nullptr == relativePath) - { - return PrjFS_Result_EInvalidArgs; - } - - // TODO(#1372): Ensure that races with hydration are handled properly - - char fullPath[PrjFSMaxPath]; - CombinePaths(s_virtualizationRootFullPath.c_str(), relativePath, fullPath); - - struct stat path_stat; - if (0 != lstat(fullPath, &path_stat)) - { - switch(errno) - { - case ENOENT: // A component of fullPath does not exist - case ENOTDIR: // A component of fullPath is not a directory - return PrjFS_Result_Success; - default: - LogWarning("PrjFS_DeleteFile: stat call failed fullPath=%s errno=%d strerror=%s", fullPath, errno, strerror(errno)); - return PrjFS_Result_EIOError; - } - } - - if (!(S_ISREG(path_stat.st_mode) || S_ISDIR(path_stat.st_mode))) - { - // Only files and directories can be deleted with PrjFS_DeleteFile - // Anything else should be treated as a full file - LogWarning("PrjFS_DeleteFile: path is not a regular file or directory %s, %d", fullPath, path_stat.st_mode); - *failureCause = PrjFS_UpdateFailureCause_FullFile; - return PrjFS_Result_EVirtualizationInvalidOperation; - } - - if (S_ISREG(path_stat.st_mode)) - { - // TODO(#1372): Determine if we need a similar check for directories as well - PrjFSFileXAttrData xattrData = {}; - if (!TryGetXAttr(fullPath, PrjFSFileXAttrName, sizeof(PrjFSFileXAttrData), &xattrData)) - { - LogWarning("PrjFS_DeleteFile: failing because we were unable to get PrjFSFileXAttrName fullPath=%s", fullPath); - *failureCause = PrjFS_UpdateFailureCause_FullFile; - return PrjFS_Result_EVirtualizationInvalidOperation; - } - } - - if (0 != remove(fullPath)) - { - switch(errno) - { - case ENOENT: // A component of fullPath does not exist - case ENOTDIR: // A component of fullPath is not a directory - return PrjFS_Result_Success; - case ENOTEMPTY: - return PrjFS_Result_EDirectoryNotEmpty; - default: - LogWarning("PrjFS_DeleteFile: remove failed fullPath=%s errno=%d strerror=%s", fullPath, errno, strerror(errno)); - return PrjFS_Result_EIOError; - } - } - - return PrjFS_Result_Success; -} - -PrjFS_Result PrjFS_WriteFileContents( - _In_ const PrjFS_FileHandle* fileHandle, - _In_ const void* bytes, - _In_ unsigned int byteCount) -{ -#ifdef DEBUG - cout - << "PrjFS_WriteFile(" - << fileHandle->file << ", " - << (int)((char*)bytes)[0] << ", " - << (int)((char*)bytes)[1] << ", " - << (int)((char*)bytes)[2] << ", " - << byteCount << ")" << endl; -#endif - - if (nullptr == fileHandle->file || - nullptr == bytes) - { - return PrjFS_Result_EInvalidArgs; - } - - if (byteCount != fwrite(bytes, 1, byteCount, fileHandle->file)) - { - LogWarning("PrjFS_WriteFileContents: byte count is incorrect"); - return PrjFS_Result_EIOError; - } - - return PrjFS_Result_Success; -} - -// Private functions - - -static Message ParseMessageMemory(const void* messageMemory, uint32_t size) -{ - const MessageHeader* header = static_cast(messageMemory); - if (size != Message_EncodedSize(header)) - { - LogError("ParseMessageMemory: invariant failed, bad message? message size = %u, expecting minimum of %zu\n", size, sizeof(*header)); - abort(); - } - - Message parsedMessage = { header }; - - const char* messagePosition = static_cast(messageMemory) + sizeof(*header); - uint32_t messageBytesRemain = size - sizeof(*header); - for (unsigned i = 0; i < extent::value; ++i) - { - if (header->pathSizesBytes[i] > 0) - { - uint16_t stringSize = header->pathSizesBytes[i]; - assert(messageBytesRemain >= stringSize); - const char* string = messagePosition; - // Path string should fit exactly in reserved memory, with nul terminator in end position - assert(strnlen(string, stringSize) == stringSize - 1); - messagePosition += stringSize; - messageBytesRemain -= stringSize; - - parsedMessage.paths[i] = string; - } - } - - assert(messageBytesRemain == 0); - - return parsedMessage; -} - -static void HandleKernelRequest(void* messageMemory, uint32_t messageSize) -{ - PrjFS_Result result = PrjFS_Result_EIOError; - - Message request = ParseMessageMemory(messageMemory, messageSize); - const MessageHeader* requestHeader = request.messageHeader; - - const char* absolutePath = nullptr; - const char* relativePath = nullptr; - - // We expect a non-null request.path for messages sent from the FILEOP handler, - // whereas messages originating in the kext's vnode handler will only fill - // the fsid/inode, so we need to look up the path below. - char pathBuffer[PrjFSMaxPath]; - if (request.paths[MessagePath_Target] == nullptr) - { - fsid_t fsid = request.messageHeader->fsidInode.fsid; - ssize_t pathSize = fsgetpath(pathBuffer, sizeof(pathBuffer), &fsid, request.messageHeader->fsidInode.inode); - - if (pathSize < 0) - { - ostringstream ss; - ss - << "MessageType: " << requestHeader->messageType << " " - << "PrjFSLib.HandleKernelRequest: fsgetpath failed for fsid 0x" - << hex << fsid.val[0] << ":" << hex << fsid.val[1] - << ", inode " - << dec << request.messageHeader->fsidInode.inode - << "; error = " - << errno - << "(" << strerror(errno) << ")" - << endl; - string errorMessage = ss.str(); - - s_callbacks.LogError(errorMessage.c_str()); - result = PrjFS_Result_Success; - goto CleanupAndReturn; - } - else - { - absolutePath = pathBuffer; - relativePath = GetRelativePath(pathBuffer, s_virtualizationRootFullPath.c_str()); - if (relativePath == nullptr) - { - LogWarning("HandleKernelRequest: relativePath is [NULL] and pathSize > 0, pathBuffer=%s virtualizationRootPath=%s", pathBuffer, s_virtualizationRootFullPath.c_str()); - goto CleanupAndReturn; - } -#if DEBUG - cout - << "PrjFSLib.HandleKernelRequest: fsgetpath for fsid 0x" - << hex << fsid.val[0] << ":" << hex << fsid.val[1] - << ", inode " - << dec << request.messageHeader->fsidInode.inode - << " -> '" - << pathBuffer - << "' -> relative path '" << relativePath - << "'" - << endl; -#endif - } - } - else - { - absolutePath = request.paths[MessagePath_Target]; - relativePath = GetRelativePath(absolutePath, s_virtualizationRootFullPath.c_str()); - if (relativePath == nullptr) - { - LogWarning("HandleKernelRequest: absolutePath=%s virtualizationRootPath=%s relativePath=[NULL]", absolutePath, s_virtualizationRootFullPath.c_str()); - goto CleanupAndReturn; - } - } - - switch (requestHeader->messageType) - { - case MessageType_KtoU_EnumerateDirectory: - { - result = HandleEnumerateDirectoryRequest(requestHeader, absolutePath, relativePath); - break; - } - - case MessageType_KtoU_RecursivelyEnumerateDirectory: - { - result = HandleRecursivelyEnumerateDirectoryRequest(requestHeader, absolutePath, relativePath); - break; - } - - case MessageType_KtoU_HydrateFile: - { - result = HandleHydrateFileRequest(requestHeader, absolutePath, relativePath); - break; - } - - case MessageType_KtoU_NotifyFileModified: - case MessageType_KtoU_NotifyFilePreDelete: - case MessageType_KtoU_NotifyFilePreDeleteFromRename: - case MessageType_KtoU_NotifyDirectoryPreDelete: - case MessageType_KtoU_NotifyFilePreConvertToFull: - { - result = HandleFileNotification( - requestHeader, - relativePath, - absolutePath, - nullptr, /* relativeFromPath */ - requestHeader->messageType == MessageType_KtoU_NotifyDirectoryPreDelete, // isDirectory - KUMessageTypeToNotificationType(static_cast(requestHeader->messageType))); - break; - } - - case MessageType_KtoU_NotifyFileHardLinkCreated: - { - const char* absoluteFromPath = request.paths[MessagePath_From]; - const char* relativeFromPath = GetRelativePath(absoluteFromPath, s_virtualizationRootFullPath.c_str()); - if (relativeFromPath == nullptr) - { - goto CleanupAndReturn; - } - -#ifdef DEBUG - cout << "PrjFSLib.HandleKernelRequest: " << "hard-linked "; - cout << "from this root (relative path " << relativeFromPath << ") "; - cout << "into this root (relative path " << relativePath << ")"; - cout << endl; -#endif - - if (strcmp(relativePath, "") == 0) - { - result = HandleFileNotification( - requestHeader, - relativePath, - absolutePath, - relativeFromPath, - false, // isDirectory; TODO: may yet be a directory - KUMessageTypeToNotificationType(static_cast(requestHeader->messageType))); - } - else - { - result = HandleNewFileInRootNotification( - requestHeader, - relativePath, - absolutePath, - relativeFromPath, - false, // isDirectory; TODO: may yet be a directory - KUMessageTypeToNotificationType(static_cast(requestHeader->messageType))); - } - - break; - } - - case MessageType_KtoU_NotifyFileCreated: - case MessageType_KtoU_NotifyFileRenamed: - case MessageType_KtoU_NotifyDirectoryRenamed: - { - bool isDirectory = requestHeader->messageType == MessageType_KtoU_NotifyDirectoryRenamed; - result = HandleNewFileInRootNotification( - requestHeader, - relativePath, - absolutePath, - nullptr, /* relativeFromPath */ - isDirectory, - KUMessageTypeToNotificationType(static_cast(requestHeader->messageType))); - break; - } - } - - // async callbacks are not yet implemented - assert(PrjFS_Result_Pending != result); - -CleanupAndReturn: - if (PrjFS_Result_Pending != result) - { - MessageType responseType = - PrjFS_Result_Success == result - ? MessageType_Response_Success - : MessageType_Response_Fail; - - SendKernelMessageResponse(requestHeader->messageId, responseType); - } - - free(messageMemory); -} - -static PrjFS_Result HandleEnumerateDirectoryRequest(const MessageHeader* request, const char* absolutePath, const char* relativePath) -{ -#ifdef DEBUG - cout - << "PrjFSLib.HandleEnumerateDirectoryRequest: " - << absolutePath - << " (root-relative: " << relativePath << ")" - << " Process name: " << request->procname - << " Pid: " << request->pid - << endl; -#endif - - if (!IsBitSetInFileFlags(absolutePath, FileFlags_IsEmpty)) - { - return PrjFS_Result_Success; - } - - PrjFS_Result result; - FileMutexMap::iterator mutexIterator = CheckoutFileMutexIterator(request->fsidInode); - { - mutex_lock lock(*(mutexIterator->second.mutex)); - if (!IsBitSetInFileFlags(absolutePath, FileFlags_IsEmpty)) - { - result = PrjFS_Result_Success; - goto CleanupAndReturn; - } - - result = s_callbacks.EnumerateDirectory( - 0 /* commandId */, - relativePath, - request->pid, - request->procname); - - if (PrjFS_Result_Success == result) - { - if (!SetBitInFileFlags(absolutePath, FileFlags_IsEmpty, false)) - { - // TODO(#1374): how should we handle this scenario where the provider thinks it succeeded, but we were unable to - // update placeholder metadata? - LogWarning("HandleEnumerateDirectoryRequest: SetBitInFileFlags failed on %s", absolutePath); - result = PrjFS_Result_EIOError; - } - } - } - -CleanupAndReturn: - ReturnFileMutexIterator(mutexIterator); - - return result; -} - -static PrjFS_Result HandleRecursivelyEnumerateDirectoryRequest(const MessageHeader* request, const char* absolutePath, const char* relativePath) -{ -#ifdef DEBUG - cout - << "PrjFSLib.HandleRecursivelyEnumerateDirectoryRequest: " - << absolutePath - << " (root-relative: " << relativePath << ")" - << " Process name: " << request->procname - << " Pid: " << request->pid - << endl; -#endif - - DIR* directory = nullptr; - PrjFS_Result result = PrjFS_Result_Success; - queue directoryRelativePaths; - directoryRelativePaths.push(relativePath); - - // Walk each directory, expanding those that are found to be empty - char path[PrjFSMaxPath]; - while (!directoryRelativePaths.empty()) - { - string directoryRelativePath(directoryRelativePaths.front()); - directoryRelativePaths.pop(); - - CombinePaths(s_virtualizationRootFullPath.c_str(), directoryRelativePath.c_str(), path); - - result = HandleEnumerateDirectoryRequest(request, path, directoryRelativePath.c_str()); - if (result != PrjFS_Result_Success) - { - LogWarning("HandleRecursivelyEnumerateDirectoryRequest: HandleEnumerateDirectoryRequest failed on %s %d", path, result); - goto CleanupAndReturn; - } - - directory = opendir(path); - if (nullptr == directory) - { - LogWarning("HandleRecursivelyEnumerateDirectoryRequest: failed to open dir %s errno=%d strerror=%s", path, errno, strerror(errno)); - result = PrjFS_Result_EIOError; - goto CleanupAndReturn; - } - - dirent* dirEntry = readdir(directory); - while (dirEntry != nullptr) - { - if (IsDirEntChildDirectory(dirEntry)) - { - CombinePaths(directoryRelativePath.c_str(), dirEntry->d_name, path); - directoryRelativePaths.emplace(path); - } - - dirEntry = readdir(directory); - } - - closedir(directory); - directory = nullptr; - } - -CleanupAndReturn: - if (directory != nullptr) - { - closedir(directory); - } - - return result; -} - -static PrjFS_Result HydrateFile(const char* absolutePath, const char* relativePath, FsidInode fsidInode, pid_t pid, const char* procname) -{ - PrjFSFileXAttrData xattrData = {}; - if (!TryGetXAttr(absolutePath, PrjFSFileXAttrName, sizeof(PrjFSFileXAttrData), &xattrData)) - { - LogWarning("HandleHydrateFileRequest: TryGetXAttr PrjFSFileXAttrName failed %s", absolutePath); - return PrjFS_Result_EIOError; - } - - if (!IsBitSetInFileFlags(absolutePath, FileFlags_IsEmpty)) - { - return PrjFS_Result_Success; - } - - PrjFS_Result result; - PrjFS_FileHandle fileHandle; - - FileMutexMap::iterator mutexIterator = CheckoutFileMutexIterator(fsidInode); - - { - mutex_lock lock(*(mutexIterator->second.mutex)); - if (!IsBitSetInFileFlags(absolutePath, FileFlags_IsEmpty)) - { - result = PrjFS_Result_Success; - goto CleanupAndReturn; - } - - // Mode "rb+" means: - // - The file must already exist - // - The handle is opened for reading and writing - // - We are allowed to seek to somewhere other than end of stream for writing - fileHandle.file = fopen(absolutePath, "rb+"); - if (nullptr == fileHandle.file) - { - LogWarning("HandleHydrateFileRequest: fopen with mode 'rb+' failed %s errno=%d strerror=%s", absolutePath, errno, strerror(errno)); - result = PrjFS_Result_EIOError; - goto CleanupAndReturn; - } - - // Seek back to the beginning so the provider can overwrite the empty contents - if (fseek(fileHandle.file, 0, 0)) - { - LogWarning("HandleHydrateFileRequest: fseek failed %s errno=%d strerror=%s", absolutePath, errno, strerror(errno)); - fclose(fileHandle.file); - result = PrjFS_Result_EIOError; - goto CleanupAndReturn; - } - - result = s_callbacks.GetFileStream( - 0 /* comandId */, - relativePath, - xattrData.providerId, - xattrData.contentId, - pid, - procname, - &fileHandle); - - fflush(fileHandle.file); - - // Don't block on closing the file to avoid deadlock with some Antivirus software - dispatch_async(s_kernelRequestHandlingConcurrentQueue, ^{ - if (fclose(fileHandle.file)) - { - LogWarning("HandleHydrateFileRequest: fclose failed %s errno=%d, strerror=%s", absolutePath, errno, strerror(errno)); - // TODO(#1374): under what conditions can fclose fail? How do we recover? - } - }); - - if (PrjFS_Result_Success == result) - { - // TODO(#1374): validate that the total bytes written match the size that was reported on the placeholder in the first place - // Potential bugs if we don't: - // * The provider writes fewer bytes than expected. The hydrated is left with extra padding up to the original reported size. - // * The provider writes more bytes than expected. The write succeeds, but whatever tool originally opened the file may have already - // allocated the originally reported size, and now the contents appear truncated. - - if (!SetBitInFileFlags(absolutePath, FileFlags_IsEmpty, false)) - { - // TODO(#1374): how should we handle this scenario where the provider thinks it succeeded, but we were unable to - // update placeholder metadata? - LogWarning("HandleHydrateFileRequest: SetBitInFileFlags failed %s", absolutePath); - result = PrjFS_Result_EIOError; - } - } - } - -CleanupAndReturn: - ReturnFileMutexIterator(mutexIterator); - return result; -} - -static PrjFS_Result HandleHydrateFileRequest(const MessageHeader* request, const char* absolutePath, const char* relativePath) -{ -#ifdef DEBUG - cout - << "PrjFSLib.HandleHydrateFileRequest: " - << absolutePath - << " (root-relative: " << relativePath << ")" - << " Process name: " << request->procname - << " Pid: " << request->pid - << endl; -#endif - - return HydrateFile(absolutePath, relativePath, request->fsidInode, request->pid, request->procname); -} - -static PrjFS_Result HandleNewFileInRootNotification( - const MessageHeader* request, - const char* relativePath, - const char* absolutePath, - const char* relativeFromPath, - bool isDirectory, - PrjFS_NotificationType notificationType) -{ -#ifdef DEBUG - cout - << "HandleNewFileInRootNotification: " << absolutePath - << " (root-relative: " << relativePath << ")" - << " (root-relative from: " << (relativeFromPath == nullptr ? "[NULL]" : relativeFromPath) << ")" - << " Process name: " << request->procname - << " Pid: " << request->pid - << " notificationType: " << NotificationTypeToString(notificationType) - << " isDirectory: " << isDirectory << endl; -#endif - - // Whenever a new file shows up in the root, we need to check if its ancestor - // directories are flagged as in root. If they are not, flag them as in root and - // notify the provider - FindNewFoldersInRootAndNotifyProvider(request, relativePath); - - PrjFS_Result result = HandleFileNotification( - request, - relativePath, - absolutePath, - relativeFromPath, - isDirectory, - notificationType); - - // TODO(#391): Handle SetBitInFileFlags failures - SetBitInFileFlags(absolutePath, FileFlags_IsInVirtualizationRoot, true); - - return result; -} - -static PrjFS_Result HandleFileNotification( - const MessageHeader* request, - const char* relativePath, - const char* absolutePath, - const char* relativeFromPath, - bool isDirectory, - PrjFS_NotificationType notificationType) -{ -#ifdef DEBUG - cout - << "PrjFSLib.HandleFileNotification: " << absolutePath - << " (root-relative: " << relativePath << ")" - << " (root-relative from: " << (relativeFromPath == nullptr ? "[NULL]" : relativeFromPath) << ")" - << " Process name: " << request->procname - << " Pid: " << request->pid - << " notificationType: " << NotificationTypeToString(notificationType) - << " isDirectory: " << isDirectory << endl; -#endif - - PrjFSFileXAttrData xattrData = {}; - bool placeholderFile = TryGetXAttr(absolutePath, PrjFSFileXAttrName, sizeof(PrjFSFileXAttrData), &xattrData); - - PrjFS_Result result = s_callbacks.NotifyOperation( - 0 /* commandId */, - relativePath, - relativeFromPath, - xattrData.providerId, - xattrData.contentId, - request->pid, - request->procname, - isDirectory, - notificationType, - nullptr /* destinationRelativePath */); - - // Remove placeholder xattrs for renames (PreDeleteFromRename) because: - // - Renames are treated as "Delete old path" + "create new path", and new files should not be marked as placeholders - // - The target of the rename might be outside of the root, and we should not allow placeholder files to - // live outside of the virtualization root (as the kext won't know what provider they belong to) - if (result == PrjFS_Result_Success && - placeholderFile && - (PrjFS_NotificationType_PreConvertToFull == notificationType || PrjFS_NotificationType_PreDeleteFromRename == notificationType)) - { - errno_t error = RemoveXAttrWithoutFollowingLinks(absolutePath, PrjFSFileXAttrName); - if (0 != error && ENOATTR != error) - { - // It's expected that RemoveXAttrWithoutFollowingLinks return ENOATTR if - // another thread has removed the attribute - LogError("HandleFileNotification: RemoveXAttrWithoutFollowingLinks failed for '%s', error=%d strerror=%s", absolutePath, error, strerror(error)); - } - } - - return result; -} - -static void FindNewFoldersInRootAndNotifyProvider(const MessageHeader* request, const char* relativePath) -{ - // Walk up the directory tree and notify the provider about any directories - // not flagged as being in the root - stack> newFolderPaths; - string parentPath(relativePath); - size_t lastDirSeparator = parentPath.find_last_of('/'); - while (lastDirSeparator != string::npos && lastDirSeparator > 0) - { - parentPath = parentPath.substr(0, lastDirSeparator); - char parentFullPath[PrjFSMaxPath]; - CombinePaths(s_virtualizationRootFullPath.c_str(), parentPath.c_str(), parentFullPath); - if (IsBitSetInFileFlags(parentFullPath, FileFlags_IsInVirtualizationRoot)) - { - break; - } - else - { - newFolderPaths.emplace(make_pair(parentPath, parentFullPath)); - lastDirSeparator = parentPath.find_last_of('/'); - } - } - - while (!newFolderPaths.empty()) - { - const pair& parentFolderPath = newFolderPaths.top(); - - HandleFileNotification( - request, - parentFolderPath.first.c_str(), - parentFolderPath.second.c_str(), - nullptr, /* relativeFromPath */ - true, // isDirectory - PrjFS_NotificationType_NewFileCreated); - - // TODO(#391): Handle SetBitInFileFlags failures - SetBitInFileFlags(parentFolderPath.second.c_str(), FileFlags_IsInVirtualizationRoot, true); - - newFolderPaths.pop(); - } -} - -static bool IsDirEntChildDirectory(const dirent* directoryEntry) -{ - return - directoryEntry->d_type == DT_DIR && - 0 != strncmp(".", directoryEntry->d_name, sizeof(directoryEntry->d_name)) && - 0 != strncmp("..", directoryEntry->d_name, sizeof(directoryEntry->d_name)); -} - -static bool InitializeEmptyPlaceholder(const char* fullPath) -{ - bool result = - SetBitInFileFlags(fullPath, FileFlags_IsInVirtualizationRoot, true) && - SetBitInFileFlags(fullPath, FileFlags_IsEmpty, true); - - if (!result) - { - LogWarning("InitializeEmptyPlaceholder: failed for path %s", fullPath); - } - - return result; -} - -template -static bool InitializeEmptyPlaceholder(const char* fullPath, TPlaceholder* data, const char* xattrName) -{ - if (InitializeEmptyPlaceholder(fullPath)) - { - data->header.magicNumber = PlaceholderMagicNumber; - data->header.formatVersion = PlaceholderFormatVersion; - - static_assert(is_pod(), "TPlaceholder must be a POD struct"); - - errno_t result = AddXAttr(fullPath, xattrName, data, sizeof(TPlaceholder)); - if (0 == result) - { - return true; - } - else - { - LogError("InitializeEmptyPlaceholder: AddXAttr failed for '%s', error=%d strerror=%s", fullPath, result, strerror(result)); - } - } - - return false; -} - -static bool IsVirtualizationRoot(const char* fullPath) -{ - PrjFSVirtualizationRootXAttrData data = {}; - if (TryGetXAttr(fullPath, PrjFSVirtualizationRootXAttrName, sizeof(PrjFSVirtualizationRootXAttrData), &data)) - { - return true; - } - - return false; -} - -static void CombinePaths(const char* root, const char* relative, char (&combined)[PrjFSMaxPath]) -{ - snprintf(combined, PrjFSMaxPath, "%s/%s", root, relative); -} - -static bool SetBitInFileFlags(const char* fullPath, uint32_t bit, bool value) -{ - struct stat fileAttributes; - if (lstat(fullPath, &fileAttributes)) - { - LogWarning("SetBitInFileFlags: lstat failed on %s errno=%d, strerror=%s", fullPath, errno, strerror(errno)); - return false; - } - - uint32_t newValue; - if (value) - { - newValue = fileAttributes.st_flags | bit; - } - else - { - newValue = fileAttributes.st_flags & ~bit; - } - - if (lchflags(fullPath, newValue)) - { - LogWarning("SetBitInFileFlags: lchflags failed on %s errno=%d, strerror=%s", fullPath, errno, strerror(errno)); - return false; - } - - return true; -} - -static bool IsBitSetInFileFlags(const char* fullPath, uint32_t bit) -{ - struct stat fileAttributes; - if (lstat(fullPath, &fileAttributes)) - { - LogWarning("IsBitSetInFileFlags: lstat failed on %s errno=%d strerror=%s", fullPath, errno, strerror(errno)); - return false; - } - - return fileAttributes.st_flags & bit; -} - -static errno_t AddXAttr(const char* fullPath, const char* name, const void* value, size_t size) -{ - if (0 != setxattr(fullPath, name, value, size, 0, XATTR_NOFOLLOW)) - { - // We do not log a warning here, since files in the .git folder are expected to fail this check - return errno; - } - - return 0; -} - -static bool TryGetXAttr(const char* fullPath, const char* name, size_t expectedSize, _Out_ void* value) -{ - if (expectedSize != getxattr(fullPath, name, value, expectedSize, 0, XATTR_NOFOLLOW)) - { - return false; - } - - // TODO(#1369): also validate the magic number and format version. - // It's easy to check their expected values, but we will need to decide what to do if they are incorrect. - - return true; -} - -static errno_t RemoveXAttrWithoutFollowingLinks(const char* fullPath, const char* name) -{ - if (0 != removexattr(fullPath, name, XATTR_NOFOLLOW)) - { - LogWarning("RemoveXAttrWithoutFollowingLinks: removexattr failed on %s errno=%d strerror=%s", fullPath, errno, strerror(errno)); - return errno; - } - - return 0; -} - -static inline PrjFS_NotificationType KUMessageTypeToNotificationType(MessageType kuNotificationType) -{ - switch(kuNotificationType) - { - case MessageType_KtoU_NotifyFileModified: - return PrjFS_NotificationType_FileModified; - - case MessageType_KtoU_NotifyFilePreDelete: - case MessageType_KtoU_NotifyDirectoryPreDelete: - return PrjFS_NotificationType_PreDelete; - - case MessageType_KtoU_NotifyFilePreDeleteFromRename: - return PrjFS_NotificationType_PreDeleteFromRename; - - case MessageType_KtoU_NotifyFilePreConvertToFull: - return PrjFS_NotificationType_PreConvertToFull; - - case MessageType_KtoU_NotifyFileCreated: - return PrjFS_NotificationType_NewFileCreated; - - case MessageType_KtoU_NotifyFileRenamed: - case MessageType_KtoU_NotifyDirectoryRenamed: - return PrjFS_NotificationType_FileRenamed; - - case MessageType_KtoU_NotifyFileHardLinkCreated: - return PrjFS_NotificationType_HardLinkCreated; - - // Non-notification types - case MessageType_Invalid: - case MessageType_KtoU_EnumerateDirectory: - case MessageType_KtoU_RecursivelyEnumerateDirectory: - case MessageType_KtoU_HydrateFile: - case MessageType_Response_Success: - case MessageType_Response_Fail: - case MessageType_Result_Aborted: - return PrjFS_NotificationType_Invalid; - } -} - -static errno_t SendKernelMessageResponse(uint64_t messageId, MessageType responseType) -{ - const uint64_t inputs[] = { messageId, responseType }; - IOReturn callResult = IOConnectCallScalarMethod( - s_kernelServiceConnection, - ProviderSelector_KernelMessageResponse, - inputs, extent::value, // scalar inputs - nullptr, nullptr); // no outputs - return callResult == kIOReturnSuccess ? 0 : EBADMSG; -} - -static errno_t RegisterVirtualizationRootPath(const char* fullPath) -{ - uint64_t error = EBADMSG; - uint32_t output_count = 1; - size_t pathSize = strlen(fullPath) + 1; - IOReturn callResult = IOConnectCallMethod( - s_kernelServiceConnection, - ProviderSelector_RegisterVirtualizationRootPath, - nullptr, 0, // no scalar inputs - fullPath, pathSize, // struct input - &error, &output_count, // scalar output - nullptr, nullptr); // no struct output - assert(callResult == kIOReturnSuccess); - return static_cast(error); -} - -static PrjFS_Result RecursivelyMarkAllChildrenAsInRoot(const char* fullDirectoryPath) -{ - DIR* directory = nullptr; - PrjFS_Result result = PrjFS_Result_Success; - queue directoryRelativePaths; - directoryRelativePaths.push(""); - - char fullPath[PrjFSMaxPath]; - char relativePath[PrjFSMaxPath]; - - while (!directoryRelativePaths.empty()) - { - string directoryRelativePath(directoryRelativePaths.front()); - directoryRelativePaths.pop(); - - CombinePaths(fullDirectoryPath, directoryRelativePath.c_str(), fullPath); - directory = opendir(fullPath); - if (nullptr == directory) - { - LogWarning("RecursivelyMarkAllChildrenAsInRoot: failed to open %s errno=%d, strerror=%s", fullPath, errno, strerror(errno)); - result = PrjFS_Result_EIOError; - goto CleanupAndReturn; - } - - dirent* dirEntry = readdir(directory); - while (dirEntry != nullptr) - { - bool entryIsDirectoryToUpdate = IsDirEntChildDirectory(dirEntry); - if (entryIsDirectoryToUpdate || dirEntry->d_type == DT_LNK || dirEntry->d_type == DT_REG) - { - CombinePaths(directoryRelativePath.c_str(), dirEntry->d_name, relativePath); - CombinePaths(fullDirectoryPath, relativePath, fullPath); - if (!SetBitInFileFlags(fullPath, FileFlags_IsInVirtualizationRoot, true)) - { - LogWarning("RecursivelyMarkAllChildrenAsInRoot: failed to set FileFlags_IsInVirtualizationRoot for fullPath=%s", fullPath); - result = PrjFS_Result_EIOError; - goto CleanupAndReturn; - } - - if (entryIsDirectoryToUpdate) - { - directoryRelativePaths.emplace(relativePath); - } - } - - dirEntry = readdir(directory); - } - - closedir(directory); - directory = nullptr; - } - -CleanupAndReturn: - if (directory != nullptr) - { - closedir(directory); - } - - return result; - -} - - -#ifdef DEBUG -static const char* NotificationTypeToString(PrjFS_NotificationType notificationType) -{ - switch(notificationType) - { - case PrjFS_NotificationType_Invalid: - return STRINGIFY(PrjFS_NotificationType_Invalid); - - case PrjFS_NotificationType_None: - return STRINGIFY(PrjFS_NotificationType_None); - case PrjFS_NotificationType_NewFileCreated: - return STRINGIFY(PrjFS_NotificationType_NewFileCreated); - case PrjFS_NotificationType_PreDelete: - return STRINGIFY(PrjFS_NotificationType_PreDelete); - case PrjFS_NotificationType_FileRenamed: - return STRINGIFY(PrjFS_NotificationType_FileRenamed); - case PrjFS_NotificationType_PreDeleteFromRename: - return STRINGIFY(PrjFS_NotificationType_PreDeleteFromRename); - case PrjFS_NotificationType_HardLinkCreated: - return STRINGIFY(PrjFS_NotificationType_HardLinkCreated); - case PrjFS_NotificationType_PreConvertToFull: - return STRINGIFY(PrjFS_NotificationType_PreConvertToFull); - - case PrjFS_NotificationType_PreModify: - return STRINGIFY(PrjFS_NotificationType_PreModify); - case PrjFS_NotificationType_FileModified: - return STRINGIFY(PrjFS_NotificationType_FileModified); - case PrjFS_NotificationType_FileDeleted: - return STRINGIFY(PrjFS_NotificationType_FileDeleted); - } -} -#endif - -static FileMutexMap::iterator CheckoutFileMutexIterator(const FsidInode& fsidInode) -{ - mutex_lock lock(s_fileLocksMutex); - FileMutexMap::iterator iter = s_fileLocks.find(fsidInode); - if (iter == s_fileLocks.end()) - { - pair newEntry = s_fileLocks.insert( - FileMutexMap::value_type(fsidInode, { make_shared(), 1 })); - assert(newEntry.second); - return newEntry.first; - } - else - { - iter->second.useCount++; - return iter; - } -} - -static void ReturnFileMutexIterator(FileMutexMap::iterator lockIterator) -{ - mutex_lock lock(s_fileLocksMutex); - lockIterator->second.useCount--; - if (lockIterator->second.useCount == 0) - { - s_fileLocks.erase(lockIterator); - } -} - -static const char* GetRelativePath(const char* fullPath, const char* root) -{ - // Hardlinks will send an empty path when files are linked outside the virtualization root - if (strcmp(fullPath, "") == 0) - { - LogInfo("GetRelativePath: returning an empty path. This should be the result of a hardlink outside the virtualization root root=%s fullPath=%s\n", root, fullPath); - return ""; - } - - const char removePart[] = "/System/Volumes/Data/"; - const int removePartLength = sizeof(removePart) - 1; - const char* realFullPath; - if (strncmp(fullPath, removePart, removePartLength) == 0 - && strncmp(root, removePart, removePartLength) != 0) - { - // If fullPath contains "/System/Volumes/Data/", but the root does not remove it for comparison - // "/System/Volumes/Data/Users/account" is effectively "/Users/account" and should be compared as such - realFullPath = fullPath + removePartLength - 1; - } - else - { - realFullPath = fullPath; - } - - size_t rootLength = strlen(root); - size_t pathLength = strlen(realFullPath); - if (pathLength < rootLength || 0 != memcmp(realFullPath, root, rootLength)) - { - LogError("GetRelativePath: root path '%s' is not a prefix of path:'%s' originalPath:'%s'\n", root, realFullPath, fullPath); - return nullptr; - } - - const char* relativePath = realFullPath + rootLength; - if (relativePath[0] == '/') - { - relativePath++; - } - else if (rootLength > 0 && root[rootLength - 1] != '/' && pathLength > rootLength) - { - LogError("GetRelativePath: root path '%s' is not a parent directory of path:'%s' originalPath:%s (just a string prefix)\n", root, realFullPath, fullPath); - return nullptr; - } - - return relativePath; -} - -static void LogError(const char* formatString, ...) -{ - va_list dataArgs; - char* logString = nullptr; - - va_start(dataArgs, formatString); - vasprintf(&logString, formatString, dataArgs); - va_end(dataArgs); - - if (s_callbacks.LogError != nullptr) - { - s_callbacks.LogError(logString); - } - else - { - #ifdef DEBUG - cout << "LogError: s_callbacks.Info is [NULL], Unable to log: %s " << logString << endl; - #endif - } - - free(logString); -} - -static void LogWarning(const char* formatString, ...) -{ - va_list dataArgs; - char* logString = nullptr; - - va_start(dataArgs, formatString); - vasprintf(&logString, formatString, dataArgs); - va_end(dataArgs); - - if (s_callbacks.LogWarning != nullptr) - { - s_callbacks.LogWarning(logString); - } - else - { - #ifdef DEBUG - cout << "LogWarning: s_callbacks.LogWarning is [NULL], Unable to log: %s " << logString << endl; - #endif - } - - free(logString); -} - -static void LogInfo(const char* formatString, ...) -{ - va_list dataArgs; - char* logString = nullptr; - - va_start(dataArgs, formatString); - vasprintf(&logString, formatString, dataArgs); - va_end(dataArgs); - - if (s_callbacks.LogInfo != nullptr) - { - s_callbacks.LogInfo(logString); - } - else - { - #ifdef DEBUG - cout << "LogInfo: s_callbacks.LogInfo is [NULL], Unable to log: %s " << logString << endl; - #endif - } - - free(logString); -} diff --git a/ProjFS.Mac/PrjFSLib/PrjFSLib.h b/ProjFS.Mac/PrjFSLib/PrjFSLib.h deleted file mode 100644 index 9c57d52402..0000000000 --- a/ProjFS.Mac/PrjFSLib/PrjFSLib.h +++ /dev/null @@ -1,205 +0,0 @@ -#ifndef PrjFSLib_h -#define PrjFSLib_h - -#include "../PrjFSKext/public/PrjFSXattrs.h" -#include - -#define _In_ -#define _Out_ - -typedef struct _PrjFS_FileHandle PrjFS_FileHandle; - -typedef struct _PrjFS_Callbacks PrjFS_Callbacks; - -typedef enum -{ - PrjFS_Result_Invalid = 0x00000000, - - PrjFS_Result_Success = 0x00000001, - PrjFS_Result_Pending = 0x00000002, - - // Bugs in the caller - PrjFS_Result_EInvalidArgs = 0x10000001, - PrjFS_Result_EInvalidOperation = 0x10000002, - PrjFS_Result_ENotSupported = 0x10000004, - - // Runtime errors - PrjFS_Result_EDriverNotLoaded = 0x20000001, - PrjFS_Result_EOutOfMemory = 0x20000002, - PrjFS_Result_EFileNotFound = 0x20000004, - PrjFS_Result_EPathNotFound = 0x20000008, - PrjFS_Result_EAccessDenied = 0x20000010, - PrjFS_Result_EInvalidHandle = 0x20000020, - PrjFS_Result_EIOError = 0x20000040, - PrjFS_Result_ENotAVirtualizationRoot = 0x20000080, - PrjFS_Result_EVirtualizationRootAlreadyExists = 0x20000100, - PrjFS_Result_EDirectoryNotEmpty = 0x20000200, - PrjFS_Result_EVirtualizationInvalidOperation = 0x20000400, - - PrjFS_Result_ENotYetImplemented = 0xFFFFFFFF, - -} PrjFS_Result; - -typedef enum -{ - PrjFS_NotificationType_Invalid = 0x00000000, - - PrjFS_NotificationType_None = 0x00000001, - PrjFS_NotificationType_NewFileCreated = 0x00000004, - PrjFS_NotificationType_PreDelete = 0x00000010, - PrjFS_NotificationType_PreDeleteFromRename = 0x00000011, - PrjFS_NotificationType_FileRenamed = 0x00000080, - PrjFS_NotificationType_HardLinkCreated = 0x00000100, - PrjFS_NotificationType_PreConvertToFull = 0x00001000, - - PrjFS_NotificationType_PreModify = 0x10000001, - PrjFS_NotificationType_FileModified = 0x10000002, - PrjFS_NotificationType_FileDeleted = 0x10000004, - -} PrjFS_NotificationType; - -typedef struct -{ - _In_ PrjFS_NotificationType NotificationBitMask; - _In_ const char* NotificationRelativeRoot; - -} PrjFS_NotificationMapping; - -extern "C" PrjFS_Result PrjFS_StartVirtualizationInstance( - _In_ const char* virtualizationRootFullPath, - _In_ PrjFS_Callbacks callbacks, - _In_ unsigned int poolThreadCount); - -PrjFS_Result PrjFS_StopVirtualizationInstance(); - -extern "C" PrjFS_Result PrjFS_ConvertDirectoryToVirtualizationRoot( - _In_ const char* virtualizationRootFullPath); - -PrjFS_Result PrjFS_ConvertDirectoryToPlaceholder( - _In_ const char* relativePath); - -extern "C" PrjFS_Result PrjFS_WritePlaceholderDirectory( - _In_ const char* relativePath); - -extern "C" PrjFS_Result PrjFS_WritePlaceholderFile( - _In_ const char* relativePath, - _In_ unsigned char providerId[PrjFS_PlaceholderIdLength], - _In_ unsigned char contentId[PrjFS_PlaceholderIdLength], - _In_ uint16_t fileMode); - -extern "C" PrjFS_Result PrjFS_WriteSymLink( - _In_ const char* relativePath, - _In_ const char* symLinkTarget); - -extern "C" PrjFS_Result PrjFS_RegisterForOfflineIO(); -extern "C" PrjFS_Result PrjFS_UnregisterForOfflineIO(); - -typedef enum -{ - PrjFS_UpdateType_Invalid = 0x00000000, - - PrjFS_UpdateType_AllowReadOnly = 0x00000020, - -} PrjFS_UpdateType; - -typedef enum -{ - PrjFS_UpdateFailureCause_Invalid = 0x00000000, - - PrjFS_UpdateFailureCause_FullFile = 0x00000002, - PrjFS_UpdateFailureCause_ReadOnly = 0x00000008, - -} PrjFS_UpdateFailureCause; - -extern "C" PrjFS_Result PrjFS_UpdatePlaceholderFileIfNeeded( - _In_ const char* relativePath, - _In_ unsigned char providerId[PrjFS_PlaceholderIdLength], - _In_ unsigned char contentId[PrjFS_PlaceholderIdLength], - _In_ uint16_t fileMode, - _In_ PrjFS_UpdateType updateFlags, - _Out_ PrjFS_UpdateFailureCause* failureCause); - -extern "C" PrjFS_Result PrjFS_ReplacePlaceholderFileWithSymLink( - _In_ const char* relativePath, - _In_ const char* symLinkTarget, - _In_ PrjFS_UpdateType updateFlags, - _Out_ PrjFS_UpdateFailureCause* failureCause); - -extern "C" PrjFS_Result PrjFS_DeleteFile( - _In_ const char* relativePath, - _In_ PrjFS_UpdateType updateFlags, - _Out_ PrjFS_UpdateFailureCause* failureCause); - -extern "C" PrjFS_Result PrjFS_WriteFileContents( - _In_ const PrjFS_FileHandle* fileHandle, - _In_ const void* bytes, - _In_ unsigned int byteCount); - -typedef enum -{ - PrjFS_FileState_Invalid = 0x00000000, - - PrjFS_FileState_Placeholder = 0x00000001, - PrjFS_FileState_HydratedPlaceholder = 0x00000002, - PrjFS_FileState_Full = 0x00000008, - -} PrjFS_FileState; - -PrjFS_Result PrjFS_GetOnDiskFileState( - _In_ const char* fullPath, - _Out_ unsigned int* fileState); - -typedef PrjFS_Result (PrjFS_EnumerateDirectoryCallback)( - _In_ unsigned long commandId, - _In_ const char* relativePath, - _In_ int triggeringProcessId, - _In_ const char* triggeringProcessName); - -typedef PrjFS_Result (PrjFS_GetFileStreamCallback)( - _In_ unsigned long commandId, - _In_ const char* relativePath, - _In_ unsigned char providerId[PrjFS_PlaceholderIdLength], - _In_ unsigned char contentId[PrjFS_PlaceholderIdLength], - _In_ int triggeringProcessId, - _In_ const char* triggeringProcessName, - - _In_ const PrjFS_FileHandle* fileHandle); - -typedef PrjFS_Result (PrjFS_NotifyOperationCallback)( - _In_ unsigned long commandId, - _In_ const char* relativePath, - _In_ const char* relativeFromPath, - _In_ unsigned char providerId[PrjFS_PlaceholderIdLength], - _In_ unsigned char contentId[PrjFS_PlaceholderIdLength], - _In_ int triggeringProcessId, - _In_ const char* triggeringProcessName, - - _In_ bool isDirectory, - _In_ PrjFS_NotificationType notificationType, - _In_ const char* destinationRelativePath); - -typedef void (PrjFS_LogErrorCallback)( - _In_ const char* errorMessage); - -typedef void (PrjFS_LogWarningCallback)( - _In_ const char* warningMessage); - -typedef void (PrjFS_LogInfoCallback)( - _In_ const char* infoMessage); - -typedef struct _PrjFS_Callbacks -{ - _In_ PrjFS_EnumerateDirectoryCallback* EnumerateDirectory; - _In_ PrjFS_GetFileStreamCallback* GetFileStream; - _In_ PrjFS_NotifyOperationCallback* NotifyOperation; - _In_ PrjFS_LogErrorCallback* LogError; - _In_ PrjFS_LogWarningCallback* LogWarning; - _In_ PrjFS_LogInfoCallback* LogInfo; - -} PrjFS_Callbacks; - -PrjFS_Result PrjFS_CompleteCommand( - _In_ unsigned long commandId, - _In_ PrjFS_Result result); - -#endif /* PrjFSLib_h */ diff --git a/ProjFS.Mac/PrjFSLib/PrjFSUser.cpp b/ProjFS.Mac/PrjFSLib/PrjFSUser.cpp deleted file mode 100644 index 032808be77..0000000000 --- a/ProjFS.Mac/PrjFSLib/PrjFSUser.cpp +++ /dev/null @@ -1,364 +0,0 @@ -#include "PrjFSUser.hpp" -#include -#include -#include -#include -#include -#include - -struct DarwinVersion -{ - unsigned long major, minor, revision; -}; - -struct PrjFSService_WatchContext -{ - std::function discoveryCallback; - io_iterator_t notificationIterator; - PrjFSServiceUserClientType clientType; -}; - - -typedef decltype(IODataQueueDequeue)* ioDataQueueDequeueFunctionPtr; -static ioDataQueueDequeueFunctionPtr ioDataQueueDequeueFunction = nullptr; -typedef decltype(IODataQueuePeek)* ioDataQueuePeekFunctionPtr; -static ioDataQueuePeekFunctionPtr ioDataQueuePeekFunction = nullptr; - -static void InitDataQueueFunctions(); - -bool PrjFSService_ValidateVersion(io_service_t prjfsService) -{ - CFTypeRef kextVersionObj = IORegistryEntryCreateCFProperty(prjfsService, CFSTR(PrjFSKextVersionKey), kCFAllocatorDefault, 0); - CFStringRef kextVersionString; - if (nullptr == kextVersionObj || CFStringGetTypeID() != CFGetTypeID(kextVersionObj)) - { - std::cerr << "PrjFS kernel service does not advertise valid kext version property\n"; - goto CleanupAndFail; - } - - kextVersionString = static_cast(kextVersionObj); - if (kCFCompareEqualTo != CFStringCompare(kextVersionString, CFSTR(PrjFSKextVersion), 0 /* options: case sensitive */)) - { - const char* kextVersion = CFStringGetCStringPtr(kextVersionString, kCFStringEncodingUTF8) ?: "???"; - std::cerr << "PrjFS kernel service interface version mismatch. Kernel: " << kextVersion << ", this library expects: " << PrjFSKextVersion << std::endl; - goto CleanupAndFail; - } - - CFRelease(kextVersionString); - - return true; - -CleanupAndFail: - if (nullptr != kextVersionObj) - { - CFRelease(kextVersionObj); - } - - return false; -} - -io_connect_t PrjFSService_ConnectToDriver(enum PrjFSServiceUserClientType clientType, bool performVersionValidation) -{ - CFDictionaryRef matchDict = IOServiceMatching(PrjFSServiceClass); - io_service_t prjfsService = IOServiceGetMatchingService(kIOMasterPortDefault, matchDict); // matchDict consumed - - io_connect_t connection = IO_OBJECT_NULL; - - if (prjfsService == IO_OBJECT_NULL) - { - std::cerr << "Failed to find instance of service class " PrjFSServiceClass << std::endl; - return IO_OBJECT_NULL; - } - - if (performVersionValidation && !PrjFSService_ValidateVersion(prjfsService)) - { - IOObjectRelease(prjfsService); - return IO_OBJECT_NULL; - } - - kern_return_t result = IOServiceOpen(prjfsService, mach_task_self(), clientType, &connection); - IOObjectRelease(prjfsService); - if (kIOReturnSuccess != result || IO_OBJECT_NULL == connection) - { - std::cerr << "Failed to open connection to kernel service; error: 0x" << std::hex << result << ", connection 0x" << std::hex << connection << std::endl; - connection = IO_OBJECT_NULL; - } - - return connection; -} - -static void ServiceMatched( - void* refcon, - io_iterator_t iterator) -{ - PrjFSService_WatchContext* context = static_cast(refcon); - - while (io_service_t prjfsService = IOIteratorNext(context->notificationIterator)) - { - io_connect_t connection = IO_OBJECT_NULL; - IOReturn result = kIOReturnError; - bool serviceVersionMatches = PrjFSService_ValidateVersion(prjfsService); - if (serviceVersionMatches) - { - result = IOServiceOpen(prjfsService, mach_task_self(), context->clientType, &connection); - } - - context->discoveryCallback(prjfsService, connection, !serviceVersionMatches, result, context); - - IOObjectRelease(prjfsService); - } -} - -PrjFSService_WatchContext* PrjFSService_WatchForServiceAndConnect( - IONotificationPortRef notificationPort, - enum PrjFSServiceUserClientType clientType, - std::function discoveryCallback) -{ - CFDictionaryRef matchDict = IOServiceMatching(PrjFSServiceClass); - PrjFSService_WatchContext* context = new PrjFSService_WatchContext { std::move(discoveryCallback), IO_OBJECT_NULL, clientType }; - IOReturn result = IOServiceAddMatchingNotification( - notificationPort, - kIOMatchedNotification, - matchDict, // dictionary is consumed, no CFRelease needed - ServiceMatched, - context, - &context->notificationIterator); - if (kIOReturnSuccess == result) - { - ServiceMatched(context, context->notificationIterator); - return context; - } - else - { - delete context; - return nullptr; - } -} - -void PrjFSService_StopWatching(PrjFSService_WatchContext* context) -{ - IOObjectRelease(context->notificationIterator); - delete context; -} - -struct TerminationNotificationContext -{ - std::function terminationCallback; - io_iterator_t terminatedServiceIterator; -}; - -static void ServiceTerminated(void* refcon, io_iterator_t iterator) -{ - TerminationNotificationContext* context = static_cast(refcon); - - io_service_t terminatedService = IOIteratorNext(iterator); - if (terminatedService != IO_OBJECT_NULL) - { - IOObjectRelease(terminatedService); - context->terminationCallback(); - IOObjectRelease(iterator); - delete context; - } -} - -void PrjFSService_WatchForServiceTermination(io_service_t service, IONotificationPortRef notificationPort, std::function terminationCallback) -{ - uint64_t serviceEntryID = 0; - IORegistryEntryGetRegistryEntryID(service, &serviceEntryID); - CFMutableDictionaryRef serviceMatching = IORegistryEntryIDMatching(serviceEntryID); - TerminationNotificationContext* context = new TerminationNotificationContext { std::move(terminationCallback), }; - kern_return_t result = IOServiceAddMatchingNotification(notificationPort, kIOTerminatedNotification, serviceMatching, ServiceTerminated, context, &context->terminatedServiceIterator); - if (result != kIOReturnSuccess) - { - delete context; - } - else - { - ServiceTerminated(context, context->terminatedServiceIterator); - } -} - - -bool PrjFSService_DataQueueInit( - DataQueueResources* outQueue, - io_connect_t connection, - uint32_t clientPortType, - uint32_t clientMemoryType, - dispatch_queue_t eventHandlingQueue) -{ - IOReturn result; - - memset(outQueue, 0, sizeof(*outQueue)); - - outQueue->notificationPort = IODataQueueAllocateNotificationPort(); - if (outQueue->notificationPort == MACH_PORT_NULL) - { - goto CleanupAndFail; - } - - result = IOConnectSetNotificationPort(connection, clientPortType, outQueue->notificationPort, 0); - if (kIOReturnSuccess != result) - { - goto CleanupAndFail; - } - - IOConnectMapMemory64( - connection, - clientMemoryType, - mach_task_self(), - &outQueue->queueMemoryAddress, - &outQueue->queueMemorySize, - kIOMapAnywhere); - if (0 == outQueue->queueMemoryAddress) - { - goto CleanupAndFail; - } - - outQueue->queueMemory = reinterpret_cast(outQueue->queueMemoryAddress); - outQueue->dispatchSource = dispatch_source_create( - DISPATCH_SOURCE_TYPE_MACH_RECV, - outQueue->notificationPort, - 0, // mask, not used by mach port sources - eventHandlingQueue); - return true; - -CleanupAndFail: - if (0 != outQueue->queueMemoryAddress) - { - IOConnectUnmapMemory64(connection, clientMemoryType, mach_task_self(), outQueue->queueMemoryAddress); - } - - if (MACH_PORT_NULL != outQueue->notificationPort) - { - mach_port_deallocate(mach_task_self(), outQueue->notificationPort); - } - - memset(outQueue, 0, sizeof(*outQueue)); - return false; -} - -void DataQueue_Dispose(DataQueueResources* queueResources, io_connect_t connection, uint32_t clientMemoryType) -{ - if (nullptr != queueResources->dispatchSource) - { - dispatch_cancel(queueResources->dispatchSource); - dispatch_release(queueResources->dispatchSource); - queueResources->dispatchSource = nullptr; - } - - if (MACH_PORT_NULL != queueResources->notificationPort) - { - mach_port_deallocate(mach_task_self(), queueResources->notificationPort); - } - - if (0 != queueResources->queueMemoryAddress) - { - IOConnectUnmapMemory64(connection, clientMemoryType, mach_task_self(), queueResources->queueMemoryAddress); - } -} - -void DataQueue_ClearMachNotification(mach_port_t port) -{ - struct { - mach_msg_header_t msgHdr; - mach_msg_trailer_t trailer; - } msg; - mach_msg(&msg.msgHdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), port, 0, MACH_PORT_NULL); -} - - -IOReturn DataQueue_Dequeue(IODataQueueMemory* dataQueue, void* data, uint32_t* dataSize) -{ - if (nullptr == ioDataQueueDequeueFunction) - { - InitDataQueueFunctions(); - } - return ioDataQueueDequeueFunction(dataQueue, data, dataSize); -} - -IODataQueueEntry* DataQueue_Peek(IODataQueueMemory* dataQueue) -{ - if (nullptr == ioDataQueuePeekFunction) - { - InitDataQueueFunctions(); - } - return ioDataQueuePeekFunction(dataQueue); -} - - -static bool GetDarwinVersion(DarwinVersion& outVersion) -{ - utsname unameInfo = {}; - if (0 != uname(&unameInfo)) - { - return false; - } - - char* fieldEnd = nullptr; - unsigned long majorVersion = strtoul(unameInfo.release, &fieldEnd, 10); - if (nullptr == fieldEnd || *fieldEnd != '.') - { - return false; - } - - unsigned long minorVersion = strtoul(fieldEnd + 1, &fieldEnd, 10); - if (nullptr == fieldEnd || (*fieldEnd != '.' && *fieldEnd != '\0')) - { - return false; - } - - outVersion.major = majorVersion; - outVersion.minor = minorVersion; - outVersion.revision = 0; - - if (*fieldEnd != '\0') - { - unsigned long revision = strtoul(fieldEnd + 1, &fieldEnd, 10); - if (nullptr == fieldEnd || (*fieldEnd != '.' && *fieldEnd != '\0')) - { - return false; - } - outVersion.revision = revision; - } - - return true; -} - -static void InitDataQueueFunctions() -{ - ioDataQueueDequeueFunction = &IODataQueueDequeue; - ioDataQueuePeekFunction = &IODataQueuePeek; - - DarwinVersion osVersion = {}; - if (!GetDarwinVersion(osVersion)) - { - return; - } - - if ((osVersion.major == PrjFSDarwinMajorVersion::MacOS10_13_HighSierra && osVersion.minor >= 7) // macOS 10.13.6+ - || (osVersion.major == PrjFSDarwinMajorVersion::MacOS10_14_Mojave && osVersion.minor == 0)) // macOS 10.14(.0) exactly - { - void* dataQueueLibrary = dlopen("libSharedDataQueue.dylib", RTLD_LAZY); - if (nullptr == dataQueueLibrary) - { - fprintf(stderr, "Error opening data queue client library: %s\n", dlerror()); - } - else - { - void* sym = dlsym(dataQueueLibrary, "IODataQueueDequeue"); - if (nullptr != sym) - { - ioDataQueueDequeueFunction = reinterpret_cast(sym); - } - - sym = dlsym(dataQueueLibrary, "IODataQueuePeek"); - if (nullptr != sym) - { - ioDataQueuePeekFunction = reinterpret_cast(sym); - } - - // Allow the dataQueueLibrary handle to leak; if we called dlclose(), - // the library would be unloaded, breaking our function pointers. - } - } -} diff --git a/ProjFS.Mac/PrjFSLib/PrjFSUser.hpp b/ProjFS.Mac/PrjFSLib/PrjFSUser.hpp deleted file mode 100644 index 45632cd8ef..0000000000 --- a/ProjFS.Mac/PrjFSLib/PrjFSUser.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "../PrjFSKext/public/PrjFSCommon.h" -#include "../PrjFSKext/public/PrjFSProviderClientShared.h" -#include -#include -#include -#include - -struct DataQueueResources -{ - mach_port_t notificationPort; - IODataQueueMemory* queueMemory; - mach_vm_address_t queueMemoryAddress; - mach_vm_address_t queueMemorySize; - dispatch_source_t dispatchSource; -}; - -io_connect_t PrjFSService_ConnectToDriver(enum PrjFSServiceUserClientType clientType, bool performVersionValidation = true); - -struct PrjFSService_WatchContext; -PrjFSService_WatchContext* PrjFSService_WatchForServiceAndConnect( - struct IONotificationPort* notificationPort, - enum PrjFSServiceUserClientType clientType, - std::function discoveryCallback); -void PrjFSService_StopWatching(PrjFSService_WatchContext* context); -bool PrjFSService_ValidateVersion(io_service_t prjfsService); - -void PrjFSService_WatchForServiceTermination(io_service_t service, struct IONotificationPort* notificationPort, std::function terminationCallback); - - -bool PrjFSService_DataQueueInit( - DataQueueResources* outQueue, - io_connect_t connection, - uint32_t clientPortType, - uint32_t clientMemoryType, - dispatch_queue_t eventHandlingQueue); -void DataQueue_Dispose(DataQueueResources* queueResources, io_connect_t connection, uint32_t clientMemoryType); - -IODataQueueEntry* DataQueue_Peek(IODataQueueMemory* dataQueue); -IOReturn DataQueue_Dequeue(IODataQueueMemory* dataQueue, void* data, uint32_t* dataSize); -void DataQueue_ClearMachNotification(mach_port_t port); diff --git a/ProjFS.Mac/PrjFSLib/prjfs-log/kext-perf-tracing.cpp b/ProjFS.Mac/PrjFSLib/prjfs-log/kext-perf-tracing.cpp deleted file mode 100644 index f600e9f927..0000000000 --- a/ProjFS.Mac/PrjFSLib/prjfs-log/kext-perf-tracing.cpp +++ /dev/null @@ -1,246 +0,0 @@ -#include "kext-perf-tracing.hpp" -#include "../../PrjFSKext/public/ArrayUtils.hpp" -#include "../../PrjFSKext/public/PrjFSCommon.h" -#include "../../PrjFSKext/public/PrjFSPerfCounter.h" -#include "../../PrjFSKext/public/PrjFSLogClientShared.h" -#include -#include -#include -#include -#include -#include -#include -#include - -// non-breaking space -#define NBSP_STR u8"\u00A0" - -using std::max; -using std::string; -using std::begin; -using std::end; - -static mach_timebase_info_data_t s_machTimebase; - -static uint64_t nanosecondsFromAbsoluteTime(uint64_t machAbsoluteTime) -{ - return static_cast<__uint128_t>(machAbsoluteTime) * s_machTimebase.numer / s_machTimebase.denom; -} - -static constexpr const char* const PerfCounterNames[PrjFSPerfCounter_Count] = -{ - [PrjFSPerfCounter_VnodeOp] = "HandleVnodeOperation", - [PrjFSPerfCounter_VnodeOp_GetPath] = " |--GetPath", - [PrjFSPerfCounter_VnodeOp_BasicVnodeChecks] = " |--BasicVnodeChecks", - [PrjFSPerfCounter_VnodeOp_ShouldHandle] = " |--ShouldHandleVnodeOpEvent", - [PrjFSPerfCounter_VnodeOp_ShouldHandle_IsVnodeAccessCheck] = " | |--IsVnodeAccessCheck", - [PrjFSPerfCounter_VnodeOp_ShouldHandle_IgnoredVnodeAccessCheck] = " | | |--IgnoredVnodeAccessCheck", - [PrjFSPerfCounter_VnodeOp_ShouldHandle_ReadFileFlags] = " | |--TryReadVNodeFileFlags", - [PrjFSPerfCounter_VnodeOp_ShouldHandle_NotInAnyRoot] = " | | |--NotInAnyRoot", - [PrjFSPerfCounter_VnodeOp_ShouldHandle_CheckFileSystemCrawler] = " | |--IsFileSystemCrawler", - [PrjFSPerfCounter_VnodeOp_ShouldHandle_DeniedFileSystemCrawler] = " | |--Denied", - [PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot] = " |--TryGetVirtualizationRoot", - [PrjFSPerfCounter_VnodeOp_Vnode_Cache_Hit] = " | |--VnodeCacheHit", - [PrjFSPerfCounter_VnodeOp_Vnode_Cache_Miss] = " | |--VnodeCacheMiss", - [PrjFSPerfCounter_VnodeOp_FindRoot] = " | | |--FindForVnode", - [PrjFSPerfCounter_VnodeOp_FindRoot_Iteration] = " | | | |--inner_loop_iterations", - [PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_TemporaryDirectory] = " | |--TemporaryDirectory", - [PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_NoRootFound] = " | |--NoRootFound", - [PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_ProviderOffline] = " | |--ProviderOffline", - [PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_OriginatedByProvider] = " | |--OriginatedByProvider", - [PrjFSPerfCounter_VnodeOp_GetVirtualizationRoot_UserRestriction] = " | |--UserRestriction", - [PrjFSPerfCounter_VnodeOp_PreDelete] = " |--RaisePreDeleteEvent", - [PrjFSPerfCounter_VnodeOp_EnumerateDirectory] = " |--RaiseEnumerateDirectoryEvent", - [PrjFSPerfCounter_VnodeOp_RecursivelyEnumerateDirectory] = " |--RaiseRecursivelyEnumerateEvent", - [PrjFSPerfCounter_VnodeOp_HydrateFile] = " |--RaiseHydrateFileEvent", - [PrjFSPerfCounter_VnodeOp_PreConvertToFull] = " |--RaisePreConvertToFull", - [PrjFSPerfCounter_FileOp] = "HandleFileOpOperation", - [PrjFSPerfCounter_FileOp_ShouldHandle] = " |--ShouldHandleFileOpEvent", - [PrjFSPerfCounter_FileOp_ShouldHandle_FindVirtualizationRoot] = " | |--FindVirtualizationRoot", - [PrjFSPerfCounter_FileOp_Vnode_Cache_Hit] = " | | |--VnodeCacheHit", - [PrjFSPerfCounter_FileOp_Vnode_Cache_Miss] = " | | |--VnodeCacheMiss", - [PrjFSPerfCounter_FileOp_FindRoot] = " | | | |--FindForVnode", - [PrjFSPerfCounter_FileOp_FindRoot_Iteration] = " | | | | |--inner_loop_iterations", - [PrjFSPerfCounter_FileOp_ShouldHandle_NoRootFound] = " | | |--NoRootFound", - [PrjFSPerfCounter_FileOp_ShouldHandle_FindProviderPathBased] = " | |--FindActiveProviderForPath", - [PrjFSPerfCounter_FileOp_ShouldHandle_NoProviderFound] = " | | |--NoProviderFound", - [PrjFSPerfCounter_FileOp_ShouldHandle_CheckProvider] = " | |--CheckProvider", - [PrjFSPerfCounter_FileOp_ShouldHandle_OfflineRoot] = " | | |--OfflineRoot", - [PrjFSPerfCounter_FileOp_ShouldHandle_OriginatedByProvider] = " | |--OriginatedByProvider", - [PrjFSPerfCounter_FileOp_Renamed] = " |--RaiseRenamedEvent", - [PrjFSPerfCounter_FileOp_HardLinkCreated] = " |--RaiseHardLinkCreatedEvent", - [PrjFSPerfCounter_FileOp_FileModified] = " |--RaiseFileModifiedEvent", - [PrjFSPerfCounter_FileOp_FileCreated] = " |--RaiseFileCreatedEvent", - [PrjFSPerfCounter_CacheCapacity] = "VnodeCacheCapacity", - [PrjFSPerfCounter_CacheInvalidateCount] = "VnodeCacheInvalidationCount", - [PrjFSPerfCounter_CacheFullCount] = "VnodeCacheFullCount", -}; - -static_assert(AllArrayElementsInitialized(PerfCounterNames), "There must be an initialization of PerfCounterNames elements corresponding to each PrjFSPerfCounter enum value"); - - -static double FindSuitablPrefixedUnitFromNS(double nanoSeconds, const char*& outUnit) -{ - double value = nanoSeconds; - outUnit = "ns"; - if (value > 1000.0) - { - value /= 1000.0; - outUnit = "µs"; - if (value > 1000.0) - { - value /= 1000.0; - outUnit = "ms"; - if (value > 1000.0) - { - value /= 1000.0; - outUnit = "s" NBSP_STR; - } - } - } - - return value; -} - -static void PadStringRight(string& s, const string& with, size_t padWidth) -{ - s.reserve(s.size() + with.size() * padWidth); - for (size_t i = 0; i < padWidth; ++i) - { - s += with; - } -} - -static string GenerateHistogramScaleLabel() -{ - string histogramScaleLabel; - // Generate 5 scale labels, aiming for an interval of 10 buckets (Factor of 2¹⁰ = 1024 between labels.) - // This should produce something like "|←1.00ns |←1.02µs |←1.05ms |←1.07s |←1100s " - // Variation between Mach Absolute Time units on different hardware will determine the exact labels. - // Most of this code is tricky for 2 reasons: left-alignment of labels (printf formatting right-aligns) - // and use of non-ASCII unicode code points which throw off the byte & character column correspondence. - // This means we can't use the byte position for histogram bucket alignment. - unsigned int markerBucketPosition = 0; - for (unsigned int marker = 0; marker < 5; ++marker) - { - double markerNanoseconds = - static_cast(UINT64_C(1) << markerBucketPosition) * s_machTimebase.numer / s_machTimebase.denom; - const char* prefixedUnit; - double markerTime = FindSuitablPrefixedUnitFromNS(markerNanoseconds, prefixedUnit); - - char markerLabel[20]; - int scaleLabelWidth = snprintf( - markerLabel, - sizeof(markerLabel), - "%.*f", - markerTime > 999 ? 0 : 2, // Drop decimals for labels with 4+ integral digits - markerTime); - - histogramScaleLabel += "|←"; - histogramScaleLabel += markerLabel; - histogramScaleLabel += prefixedUnit; - - // This is counting character columns, not bytes - scaleLabelWidth += 2 + 2; // 2 for "|←" and 2 for "ns"/"µs"/"s "/etc. - - // Aim for 10 columns, but if it's more, ensure everything else is shifted along - int rightPadWidth = max(0, 10 - scaleLabelWidth); - PadStringRight(histogramScaleLabel, NBSP_STR, rightPadWidth); - markerBucketPosition += scaleLabelWidth + rightPadWidth; - } - - // The text on the right hand side of the histogram's column header - static const char histogramHeaderTitle[] = "Histogram" NBSP_STR; - static const size_t histogramHeaderTitleColumns = 10; // can't use strlen due to UTF-8 - // Padding to fill out the character columns between the marker labels and the header title - PadStringRight(histogramScaleLabel, NBSP_STR, PrjFSPerfCounterBuckets - histogramHeaderTitleColumns - markerBucketPosition); - histogramScaleLabel += histogramHeaderTitle; - return histogramScaleLabel; -} - -bool PrjFSLog_FetchAndPrintKextProfilingData(io_connect_t connection) -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - mach_timebase_info(&s_machTimebase); - }); - - PrjFSPerfCounterResult counters[PrjFSPerfCounter_Count]; - size_t out_size = sizeof(counters); - IOReturn ret = IOConnectCallStructMethod(connection, LogSelector_FetchProfilingData, nullptr, 0, counters, &out_size); - if (ret == kIOReturnUnsupported) - { - return false; - } - else if (ret == kIOReturnSuccess) - { - static std::string histogramScaleLabel = GenerateHistogramScaleLabel(); - - printf(" Counter [ Samples ][Total time (ns)][Mean (ns) ][Min (ns)][Max (ns) ][%s]\n", - histogramScaleLabel.c_str()); - printf("--------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n"); - - for (int32_t i = 0; i < PrjFSPerfCounter_Count; ++i) - { - double numSamples = counters[i].numSamples; - printf( - "%2u %-35s [%10llu]", - i, - PerfCounterNames[i], - counters[i].numSamples); - - if (counters[i].min != UINT64_MAX) - { - // The values on the counter are reported in units of mach absolute time - double sum = counters[i].sum; - - uint64_t sumNS = nanosecondsFromAbsoluteTime(sum); - uint64_t meanNS = numSamples > 0 ? sumNS / numSamples : 0; - - printf( - "[%15llu][%10llu][%10llu][%10llu]", - sumNS, - meanNS, - nanosecondsFromAbsoluteTime(counters[i].min), - nanosecondsFromAbsoluteTime(counters[i].max)); - - static const char* const barGraphItems[9] = { - NBSP_STR, "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", - }; - - // Find the bucket with the largest number of samples; use the 8/8 - // bar symbol for that, and make all other buckets relative to it. - // (Defining the overall number of samples across all buckets as - // 100% on the scale would limit the resolution of the information - // you could read from the graph; the 8/8 bar symbol would only be - // used on distributions very concentrated on one bucket.) - _Atomic uint64_t(&buckets)[PrjFSPerfCounterBuckets] = counters[i].sampleBuckets; - uint64_t bucketMax = *std::max_element(begin(buckets), end(buckets)); - if (bucketMax > 0) // Should normally not be 0 if we get here, but defends against divide by 0 in case of a bug - { - printf("["); - for (size_t bucket = 0; bucket < PrjFSPerfCounterBuckets; ++bucket) - { - // Always round up so we have a clear distinction between buckets with zero and even a single item. - uint64_t eighths = (8 * buckets[bucket] + bucketMax - 1) / bucketMax; - assert(eighths >= 0); - assert(eighths <= 8); - printf("%s", barGraphItems[eighths]); - } - printf("]"); - } - } - printf("\n"); - } - } - else - { - fprintf(stderr, "fetching profiling data from kernel failed: 0x%x\n", ret); - return false; - } - - printf("\n"); - fflush(stdout); - - return true; -} diff --git a/ProjFS.Mac/PrjFSLib/prjfs-log/kext-perf-tracing.hpp b/ProjFS.Mac/PrjFSLib/prjfs-log/kext-perf-tracing.hpp deleted file mode 100644 index fedbf92dac..0000000000 --- a/ProjFS.Mac/PrjFSLib/prjfs-log/kext-perf-tracing.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -bool PrjFSLog_FetchAndPrintKextProfilingData(io_connect_t connection); diff --git a/ProjFS.Mac/PrjFSLib/prjfs-log/prjfs-log.cpp b/ProjFS.Mac/PrjFSLib/prjfs-log/prjfs-log.cpp deleted file mode 100644 index 548a015953..0000000000 --- a/ProjFS.Mac/PrjFSLib/prjfs-log/prjfs-log.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include "PrjFSUser.hpp" -#include "kext-perf-tracing.hpp" -#include "../../PrjFSKext/public/PrjFSLogClientShared.h" -#include -#include -#include -#include -#include - -static const char* KextLogLevelAsString(KextLog_Level level); -static uint64_t NanosecondsFromAbsoluteTime(uint64_t machAbsoluteTime); -static dispatch_source_t StartKextProfilingDataPolling(io_connect_t connection); -static void ProcessLogMessagesOnConnection(io_connect_t connection, io_service_t prjfsService); - -static mach_timebase_info_data_t s_machTimebase; -static uint64_t s_machStartTime; -static IONotificationPortRef s_notificationPort; - -int main(int argc, const char * argv[]) -{ - mach_timebase_info(&s_machTimebase); - s_machStartTime = mach_absolute_time(); - - - s_notificationPort = IONotificationPortCreate(kIOMasterPortDefault); - IONotificationPortSetDispatchQueue(s_notificationPort, dispatch_get_main_queue()); - - PrjFSService_WatchContext* watchContext = PrjFSService_WatchForServiceAndConnect( - s_notificationPort, UserClientType_Log, - [](io_service_t service, io_connect_t connection, bool serviceVersionMismatch, IOReturn connectResult, PrjFSService_WatchContext* context) - { - if (connection != IO_OBJECT_NULL) - { - ProcessLogMessagesOnConnection(connection, service); - } - else - { - std::cerr << "Failed to connect to matched kernel service; result = 0x" << std::hex << connectResult << std::endl; - } - }); - if (nullptr == watchContext) - { - std::cerr << "Failed to register for IOService notifications.\n"; - return 1; - } - - - CFRunLoopRun(); - - PrjFSService_StopWatching(watchContext); - - return 0; -} - -struct LogConnectionState -{ - DataQueueResources dataQueue; - unsigned lineCount; -}; - -static void ProcessLogMessagesOnConnection(io_connect_t connection, io_service_t prjfsService) -{ - std::shared_ptr logState(new LogConnectionState {{}, 0 }); - if (!PrjFSService_DataQueueInit(&logState->dataQueue, connection, LogPortType_MessageQueue, LogMemoryType_MessageQueue, dispatch_get_main_queue())) - { - std::cerr << "Failed to set up shared data queue on connection 0x" << std::hex << connection << ".\n"; - IOServiceClose(connection); - return; - } - - uint64_t prjfsServiceEntryID = 0; - IORegistryEntryGetRegistryEntryID(prjfsService, &prjfsServiceEntryID); - - { - uint64_t timeOffsetMS = NanosecondsFromAbsoluteTime(mach_absolute_time() - s_machStartTime) / NSEC_PER_MSEC; - printf("(0x%x: %5d: %5llu.%03llu) START: Processing log messages from service with ID 0x%llx\n", - connection, logState->lineCount, timeOffsetMS / 1000u, timeOffsetMS % 1000u, prjfsServiceEntryID); - } - - fflush(stdout); - ++logState->lineCount; - - dispatch_source_set_event_handler(logState->dataQueue.dispatchSource, ^{ - DataQueue_ClearMachNotification(logState->dataQueue.notificationPort); - - while(IODataQueueEntry* entry = DataQueue_Peek(logState->dataQueue.queueMemory)) - { - int messageSize = entry->size; - if (messageSize >= sizeof(KextLog_MessageHeader) + 2) - { - struct KextLog_MessageHeader message = {}; - memcpy(&message, entry->data, sizeof(KextLog_MessageHeader)); - const char* messageType = KextLogLevelAsString(message.level); - int logStringLength = messageSize - sizeof(KextLog_MessageHeader) - 1; - - uint64_t timeOffsetNS = NanosecondsFromAbsoluteTime(message.machAbsoluteTimestamp - s_machStartTime); - uint64_t timeOffsetMS = timeOffsetNS / NSEC_PER_MSEC; - - printf("(0x%x: %5d: %5llu.%03llu) %s: %.*s\n", connection, logState->lineCount, timeOffsetMS / 1000u, timeOffsetMS % 1000u, messageType, logStringLength, entry->data + sizeof(KextLog_MessageHeader)); - logState->lineCount++; - } - - DataQueue_Dequeue(logState->dataQueue.queueMemory, nullptr, nullptr); - } - - fflush(stdout); - }); - dispatch_resume(logState->dataQueue.dispatchSource); - - dispatch_source_t timer = nullptr; - if (PrjFSLog_FetchAndPrintKextProfilingData(connection)) - { - timer = StartKextProfilingDataPolling(connection); - } - - PrjFSService_WatchForServiceTermination( - prjfsService, - s_notificationPort, - [timer, connection, logState, prjfsServiceEntryID]() - { - uint64_t timeOffsetMS = NanosecondsFromAbsoluteTime(mach_absolute_time() - s_machStartTime) / NSEC_PER_MSEC; - printf("(0x%x: %5d: %5llu.%03llu) STOP: service with ID 0x%llx has terminated\n", connection, logState->lineCount, timeOffsetMS / 1000u, timeOffsetMS % 1000u, prjfsServiceEntryID); - fflush(stdout); - logState->lineCount++; - - DataQueue_Dispose(&logState->dataQueue, connection, LogMemoryType_MessageQueue); - - if (nullptr != timer) - { - dispatch_cancel(timer); - dispatch_release(timer); - } - - IOServiceClose(connection); - }); -} - -static const char* KextLogLevelAsString(KextLog_Level level) -{ - switch (level) - { - case KEXTLOG_ERROR: - return "Error"; - case KEXTLOG_INFO: - return "Info"; - case KEXTLOG_DEFAULT: - return "Default"; - default: - return "Unknown"; - } -} - -static uint64_t NanosecondsFromAbsoluteTime(uint64_t machAbsoluteTime) -{ - return static_cast<__uint128_t>(machAbsoluteTime) * s_machTimebase.numer / s_machTimebase.denom; -} - -static dispatch_source_t StartKextProfilingDataPolling(io_connect_t connection) -{ - dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); - dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 15 * NSEC_PER_SEC, 10 * NSEC_PER_SEC); - dispatch_source_set_event_handler(timer, ^{ - PrjFSLog_FetchAndPrintKextProfilingData(connection); - }); - dispatch_resume(timer); - return timer; -} - diff --git a/ProjFS.Mac/PrjFSLibTests/Info.plist b/ProjFS.Mac/PrjFSLibTests/Info.plist deleted file mode 100644 index 6c40a6cd0c..0000000000 --- a/ProjFS.Mac/PrjFSLibTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/ProjFS.Mac/PrjFSLibTests/JsonWriterTests.mm b/ProjFS.Mac/PrjFSLibTests/JsonWriterTests.mm deleted file mode 100644 index 0d4ee2f9b2..0000000000 --- a/ProjFS.Mac/PrjFSLibTests/JsonWriterTests.mm +++ /dev/null @@ -1,258 +0,0 @@ -#include "../PrjFSLib/Json/JsonWriter.hpp" -#include -#include -#include -#import - -using std::make_pair; -using std::numeric_limits; -using std::pair; -using std::string; -using std::to_string; -using std::vector; - -@interface JsonWriterTests : XCTestCase -@end - -@implementation JsonWriterTests -{ -} - -- (void) setUp -{ -} - -- (void) tearDown -{ -} - -- (void) testAddNothing { - string expectedResult = "{}"; - JsonWriter writer; - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testAddString { - string expectedResult = "{\"testKey\":\"testValue\"}"; - JsonWriter writer; - writer.Add("testKey", "testValue"); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testAddInt32 { - string expectedResult = "{\"testKey\":32}"; - JsonWriter writer; - writer.Add("testKey", 32); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testAddUInt32 { - string expectedResult = "{\"testKey\":32}"; - JsonWriter writer; - writer.Add("testKey", static_cast(32)); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testAddNegativeUInt32 { - string expectedResult = "{\"testKey\":" + to_string(numeric_limits::max()) + "}"; - JsonWriter writer; - writer.Add("testKey", static_cast(-1)); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testAddUInt64 { - string expectedResult = "{\"testKey\":32}"; - JsonWriter writer; - writer.Add("testKey", static_cast(32)); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testAddMultiplePairs { - string expectedResult = - "{" - "\"testKey\":\"testValue\"," - "\"testInt\":32," - "\"testUInt64\":9223372036854775807," - "\"test2ndString\":\"testValue2\"" - "}"; - JsonWriter writer; - writer.Add("testKey", "testValue"); - writer.Add("testInt", 32); - writer.Add("testUInt64", 9223372036854775807ULL); - writer.Add("test2ndString", "testValue2"); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testEscapingCharacters { - vector> escapedCharacters = - { - make_pair('\"', "\\\""), - make_pair('\\', "\\\\"), - make_pair('\n', "\\n"), - make_pair('\r', "\\r"), - make_pair('\t', "\\t"), - make_pair('\f', "\\f"), - make_pair('\b', "\\b"), - make_pair(static_cast(2), "\\u0002"), - make_pair(static_cast(0x13), "\\u0013"), - make_pair(static_cast(0x20), " "), - }; - - for (const pair& charPair : escapedCharacters) - { - string expectedResult = - "{" - "\"testKey\":\"" + charPair.second + "\"" - "}"; - - JsonWriter writer; - writer.Add("testKey", string(1, charPair.first)); - string jsonResult = writer.ToString(); - - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); - } -} - -- (void) testMultipleEscapedCharacters { - string expectedResult = - "{" - "\"testKey1\":\"testLine1\\r\\nTestList2\"," - "\"testKey2\":\"\\f\\t content\"" - "}"; - JsonWriter writer; - writer.Add("testKey1", "testLine1\r\nTestList2"); - writer.Add("testKey2", "\f\t content"); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testEscapedCharactersInKeys { - string expectedResult = - "{" - "\"testKeyLine1\\r\\nTestKeyList2\":\"testValue\"," - "\"\\f\\t key\":32" - "}"; - JsonWriter writer; - writer.Add("testKeyLine1\r\nTestKeyList2", "testValue"); - writer.Add("\f\t key", 32); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); -} - -- (void) testNestedJson { - { - string expectedResult = - "{" - "\"testKey\":\"testdata\"," - "\"jsonPayload\":{\"payloadKey\":32}" - "}"; - JsonWriter writer; - writer.Add("testKey", "testdata"); - JsonWriter payloadWriter; - payloadWriter.Add("payloadKey", 32); - writer.Add("jsonPayload", payloadWriter); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); - } - - { - string expectedResult = - "{" - "\"jsonPayload\":{\"payloadKey\":32}," - "\"testKey\":\"testdata\"" - "}"; - JsonWriter writer; - JsonWriter payloadWriter; - payloadWriter.Add("payloadKey", 32); - writer.Add("jsonPayload", payloadWriter); - writer.Add("testKey", "testdata"); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); - } - - { - string expectedResult = - "{" - "\"testKey\":\"testdata\"," - "\"jsonPayload\":{\"nestedPayload\":{\"nestedKey\":\"nestedString\"}}," - "\"lastKey\":64" - "}"; - JsonWriter writer; - writer.Add("testKey", "testdata"); - JsonWriter nestedPayloadWriter; - nestedPayloadWriter.Add("nestedKey", "nestedString"); - JsonWriter payloadWriter; - payloadWriter.Add("nestedPayload", nestedPayloadWriter); - writer.Add("jsonPayload", payloadWriter); - writer.Add("lastKey", 64); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); - } - - { - string expectedResult = - "{" - "\"testKey\":\"testdata\"," - "\"jsonPayload\":{}," - "\"lastKey\":64" - "}"; - JsonWriter writer; - writer.Add("testKey", "testdata"); - JsonWriter payloadWriter; - writer.Add("jsonPayload", payloadWriter); - writer.Add("lastKey", 64); - string jsonResult = writer.ToString(); - XCTAssertTrue( - jsonResult == expectedResult, - "%s", - ("Expected result: " + expectedResult + " Result: " + jsonResult).c_str()); - } -} - -@end diff --git a/ProjFS.Mac/Scripts/Build.sh b/ProjFS.Mac/Scripts/Build.sh deleted file mode 100755 index 26a9f748bc..0000000000 --- a/ProjFS.Mac/Scripts/Build.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/bin/bash - -CONFIGURATION=$1 -if [ -z $CONFIGURATION ]; then - CONFIGURATION=Debug -fi - -VERSION=$2 -if [ -z $VERSION ]; then - VERSION="0.2.173.2" -fi - -SCRIPTDIR=$(dirname ${BASH_SOURCE[0]}) -SRCDIR=$SCRIPTDIR/../.. -ROOTDIR=$SRCDIR/.. -PACKAGES=$ROOTDIR/packages -COVERAGEDIR=$ROOTDIR/BuildOutput/ProjFS.Mac/Coverage - -PROJFS=$SRCDIR/ProjFS.Mac - -echo "Generating PrjFSVersion.h as $VERSION..." -$SCRIPTDIR/GeneratePrjFSVersionHeader.sh $VERSION || exit 1 - -echo "Generating PrjFSConfig.xcconfig for $VERSION..." -$SCRIPTDIR/GeneratePrjFSXCConfig.sh $VERSION || exit 1 - -xcodebuild -configuration $CONFIGURATION -project $PROJFS/PrjFS.xcodeproj -scheme 'Build All' -derivedDataPath $ROOTDIR/BuildOutput/ProjFS.Mac/Native build || exit 1 - -if !(gem list --local | grep xcpretty); then - echo "Attempting to run 'sudo gem install xcpretty'. This may ask you for your password to gain admin privileges" - sudo gem install xcpretty -fi - -# Run Tests and put output into a xml file -set -o pipefail -xcodebuild -configuration $CONFIGURATION -enableCodeCoverage YES -project $PROJFS/PrjFS.xcodeproj -derivedDataPath $COVERAGEDIR -scheme 'Build All' test | xcpretty -r junit --output $PROJFS/TestResultJunit.xml || exit 1 -set +o pipefail - -while read -rd $'\0' file; do - COVERAGE_FILE="$file" -done < <(find $COVERAGEDIR -name "*xccovreport" -print0) - -if [[ $COVERAGE_FILE == "" ]]; then - echo "Error: No coverage file found" - exit 1 -fi - -# xcperfect will display pretty output for code coverage. The terminal output isn't supported by our build system yet, but a bug has been filed. Once support is added we'll switch to the prettier format. -#if !(gem list --local | grep xcperfect); then -# echo "Attempting to run 'sudo gem install xcperfect'. This may ask you for your password to gain admin privileges" -# sudo gem install xcperfect -#fi -#xcrun xccov view "$COVERAGE_FILE" --json | TERM=xterm-256color xcperfect - -printf "\n\nCode Coverage Report\n" -xcrun xccov view "$COVERAGE_FILE" | tee $PROJFS/CoverageResult.txt - -# Fail on any line that doesn't show %100 coverage and isn't on the exclusion list or hpp/cpp -while read line; do - if [[ $line != *"100.00%"* ]] && - [[ $line == *"%"* ]] && - # PrjFSKext exclusions - [[ $line != *"AllArrayElementsInitialized"* ]] && #Function is used for compile time checks only - [[ $line != *"KauthHandler_Init"* ]] && - [[ $line != *"KauthHandler_Cleanup"* ]] && - [[ $line != *"InitPendingRenames"* ]] && - [[ $line != *"HandleFileOpOperation"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"TryGetVirtualizationRoot"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"WaitForListenerCompletion"* ]] && - [[ $line != *"KextLog_"* ]] && - [[ $line != *"Definition"* ]] && - [[ $line != *"PerfTracer"* ]] && - [[ $line != *"VirtualizationRoot_GetActiveProvider"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"VirtualizationRoots_Init"* ]] && - [[ $line != *"VirtualizationRoots_Cleanup"* ]] && - [[ $line != *"FindOrInsertVirtualizationRoot_LockedMayUnlock"* ]] && # Race compensation path not covered, currently can't provoke this in tests. - [[ $line != *"VnodeCache_Init"* ]] && - [[ $line != *"VnodeCache_Cleanup"* ]] && - [[ $line != *"VnodeCache_ExportHealthData"* ]] && # IOKit related functions are not unit tested - [[ $line != *"FindOrDetectRootAtVnode"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"FindUnusedIndexOrGrow_Locked"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"FindRootAtVnode_Locked"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"ActiveProvider_"* ]] && - [[ $line != *"GetRelativePath"* ]] && - [[ $line != *"VirtualizationRoot_GetRootRelativePath"* ]] && - [[ $line != *"VirtualizationRoots_AddOfflineIOProcess"* ]] && # The only branch not covered is one for dealing with a race. We can't yet provoke this in tests. - [[ $line != *"MockCalls"* ]] && - [[ $line != *"VnodeCacheEntriesWrapper"* ]] && - [[ $line != *"PerfTracing_"* ]] && - [[ $line != *"proc_"* ]] && - [[ $line != *"ParentPathString"* ]] && - [[ $line != *"SetAndRegisterPath"* ]] && - [[ $line != *"vn_"* ]] && - [[ $line != *"vfs_"* ]] && - [[ $line != *"vnode_lookup"* ]] && - [[ $line != *"RetainIOCount"* ]] && - [[ $line != *"ProviderMessaging_"* ]] && - [[ $line != *"RWLock_DropExclusiveToShared"* ]] && - # Not going down the "unexpected" code path in isKextAssertionFailureExpected, Assert & panic is good! - [[ $line != *"PFSKextTestCase isKextAssertionFailureExpected"* ]] && - [[ $line != *"Assert"* ]] && - [[ $line != *"panic"* ]] && - - # PrjFSLib exclusions - [[ $line != *"PrjFS_"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"FsidInodeCompare"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"ParseMessageMemory"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"HandleKernelRequest"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"HandleEnumerateDirectoryRequest"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"HandleRecursivelyEnumerateDirectoryRequest"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"HandleHydrateFileRequest"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"FindNewFoldersInRootAndNotifyProvider"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"IsDirEntChildDirectory"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"InitializeEmptyPlaceholder"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"HydrateFile"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"IsVirtualizationRoot"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"CombinePaths"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"SetBitInFileFlags"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"IsBitSetInFileFlags"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"AddXAttr"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"TryGetXAttr"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"RemoveXAttrWithoutFollowingLinks"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"KUMessageTypeToNotificationType"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"SendKernelMessageResponse"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"RegisterVirtualizationRootPath"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"RecursivelyMarkAllChildrenAsInRoot"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"CheckoutFileMutexIterator"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"ReturnFileMutexIterator"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"GetRelativePath"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"LogError"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"LogWarning"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"LogInfo"* ]] && #SHOULD ADD COVERAGE - [[ $line != *"PrjFSService_"* ]] && #TODO: DECIDE IF COVERAGE REQUIRED, IOKIT RELATED - [[ $line != *"ServiceMatched"* ]] && #TODO: DECIDE IF COVERAGE REQUIRED, IOKIT RELATED - [[ $line != *"ServiceTerminated"* ]] && #TODO: DECIDE IF COVERAGE REQUIRED, IOKIT RELATED - [[ $line != *"DataQueue_"* ]] && #TODO: DECIDE IF COVERAGE REQUIRED, IOKIT RELATED - [[ $line != *"GetDarwinVersion"* ]] && #TODO: DECIDE IF COVERAGE REQUIRED, IOKIT RELATED - [[ $line != *"InitDataQueueFunctions"* ]] && #TODO: DECIDE IF COVERAGE REQUIRED, IOKIT RELATED - [[ $line != *".dylib"* ]] && - # Global exclusions - [[ $line != *".xctest"* ]] && - [[ $line != *".cpp"* ]] && - [[ $line != *".a"* ]] && - [[ $line != *".m"* ]] && - [[ $line != *".hpp"* ]]; then - echo "Error: not at 100% Code Coverage $line" - exit 1 - fi -done < $PROJFS/CoverageResult.txt - -# If we're building the Profiling(Release) configuration, remove Profiling() for building .NET code -if [ "$CONFIGURATION" == "Profiling(Release)" ]; then - CONFIGURATION=Release -fi - -dotnet restore $PROJFS/PrjFSLib.Mac.Managed/PrjFSLib.Mac.Managed.csproj /p:Configuration=$CONFIGURATION /p:Platform=x64 --packages $PACKAGES || exit 1 -dotnet build $PROJFS/PrjFSLib.Mac.Managed/PrjFSLib.Mac.Managed.csproj /p:Configuration=$CONFIGURATION /p:Platform=x64 || exit 1 diff --git a/ProjFS.Mac/Scripts/GeneratePrjFSVersionHeader.sh b/ProjFS.Mac/Scripts/GeneratePrjFSVersionHeader.sh deleted file mode 100755 index b590557ca3..0000000000 --- a/ProjFS.Mac/Scripts/GeneratePrjFSVersionHeader.sh +++ /dev/null @@ -1,16 +0,0 @@ -VERSION=$1 -if [ -z $VERSION ]; then - VERSION="0.2.173.2" -fi - -# Generate PrjFSVersion.h -SCRIPTDIR=$(dirname ${BASH_SOURCE[0]}) -VERSIONHEADER=$SCRIPTDIR/../../../BuildOutput/PrjFSVersion.h - -echo "Generating $VERSIONHEADER with version $VERSION" - -cat >$VERSIONHEADER <