diff --git a/GVFS.sln b/GVFS.sln index 2cee10493b..48bc59b3eb 100644 --- a/GVFS.sln +++ b/GVFS.sln @@ -131,6 +131,10 @@ 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.Linux.Managed", "ProjFS.Linux\PrjFSLib.Linux.Managed\PrjFSLib.Linux.Managed.csproj", "{63E57526-9825-44C8-9ACB-DC3296ADEE97}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Platform.Linux", "GVFS\GVFS.Platform.Linux\GVFS.Platform.Linux.csproj", "{B4593252-9FB4-431D-A4E1-634D09DE09DE}" +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}" @@ -141,6 +145,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.UnitTests", "GVFS\GVFS 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.Linux", "GVFS\GVFS\GVFS.Linux.csproj", "{A1A1A31A-A76E-4F12-92A7-91755320C9A4}" +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}" @@ -160,6 +166,8 @@ 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.Linux", "GVFS\GVFS.Mount\GVFS.Mount.Linux.csproj", "{B9F62A3B-72F5-4703-A5FE-3879FF930410}" +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}" @@ -176,6 +184,7 @@ 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} + {A1A1A31A-A76E-4F12-92A7-91755320C9A4} = {A1A1A31A-A76E-4F12-92A7-91755320C9A4} {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} @@ -187,6 +196,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.FunctionalTests", "GVF 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.Linux", "GVFS\GVFS.Hooks\GVFS.Hooks.Linux.csproj", "{E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}" + ProjectSection(ProjectDependencies) = postProject + {A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574} + EndProjectSection +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} @@ -201,243 +215,389 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.PostIndexChangedHook.W EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug.Linux|x64 = Debug.Linux|x64 Debug.Mac|x64 = Debug.Mac|x64 Debug.Windows|x64 = Debug.Windows|x64 + Release.Linux|x64 = Release.Linux|x64 Release.Mac|x64 = Release.Mac|x64 Release.Windows|x64 = Release.Windows|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1118B427-7063-422F-83B9-5023C8EC5A7A}.Debug.Linux|x64.ActiveCfg = Debug|x64 {1118B427-7063-422F-83B9-5023C8EC5A7A}.Debug.Mac|x64.ActiveCfg = Debug|x64 {1118B427-7063-422F-83B9-5023C8EC5A7A}.Debug.Windows|x64.ActiveCfg = Debug|x64 {1118B427-7063-422F-83B9-5023C8EC5A7A}.Debug.Windows|x64.Build.0 = Debug|x64 + {1118B427-7063-422F-83B9-5023C8EC5A7A}.Release.Linux|x64.ActiveCfg = Release|x64 {1118B427-7063-422F-83B9-5023C8EC5A7A}.Release.Mac|x64.ActiveCfg = Release|x64 {1118B427-7063-422F-83B9-5023C8EC5A7A}.Release.Windows|x64.ActiveCfg = Release|x64 {1118B427-7063-422F-83B9-5023C8EC5A7A}.Release.Windows|x64.Build.0 = Release|x64 + {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Linux|x64.Build.0 = Debug|x64 {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Mac|x64.ActiveCfg = Debug|x64 {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Mac|x64.Build.0 = Debug|x64 {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Windows|x64.ActiveCfg = Debug|x64 {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Windows|x64.Build.0 = Debug|x64 + {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Linux|x64.ActiveCfg = Release|x64 + {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Linux|x64.Build.0 = Release|x64 {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Mac|x64.ActiveCfg = Release|x64 {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Mac|x64.Build.0 = Release|x64 {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Windows|x64.ActiveCfg = Release|x64 {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Windows|x64.Build.0 = Release|x64 + {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Linux|x64.Build.0 = Debug|x64 {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Mac|x64.ActiveCfg = Debug|x64 {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Mac|x64.Build.0 = Debug|x64 {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Windows|x64.ActiveCfg = Debug|x64 {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Windows|x64.Build.0 = Debug|x64 + {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Linux|x64.ActiveCfg = Release|x64 + {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Linux|x64.Build.0 = Release|x64 {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Mac|x64.ActiveCfg = Release|x64 {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Mac|x64.Build.0 = Release|x64 {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Windows|x64.ActiveCfg = Release|x64 {07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Windows|x64.Build.0 = Release|x64 + {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Linux|x64.Build.0 = Debug|x64 {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Mac|x64.ActiveCfg = Debug|x64 {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Mac|x64.Build.0 = Debug|x64 {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Windows|x64.ActiveCfg = Debug|x64 {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Windows|x64.Build.0 = Debug|x64 + {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Release.Linux|x64.ActiveCfg = Release|x64 + {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Release.Linux|x64.Build.0 = Release|x64 {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Release.Mac|x64.ActiveCfg = Release|x64 {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Release.Mac|x64.Build.0 = Release|x64 {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Release.Windows|x64.ActiveCfg = Release|x64 {72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Release.Windows|x64.Build.0 = Release|x64 + {8E0D0989-21F6-4DD8-946C-39F992523CC6}.Debug.Linux|x64.ActiveCfg = Debug|x64 {8E0D0989-21F6-4DD8-946C-39F992523CC6}.Debug.Mac|x64.ActiveCfg = Debug|x64 {8E0D0989-21F6-4DD8-946C-39F992523CC6}.Debug.Windows|x64.ActiveCfg = Debug|x64 {8E0D0989-21F6-4DD8-946C-39F992523CC6}.Debug.Windows|x64.Build.0 = Debug|x64 + {8E0D0989-21F6-4DD8-946C-39F992523CC6}.Release.Linux|x64.ActiveCfg = Release|x64 {8E0D0989-21F6-4DD8-946C-39F992523CC6}.Release.Mac|x64.ActiveCfg = Release|x64 {8E0D0989-21F6-4DD8-946C-39F992523CC6}.Release.Windows|x64.ActiveCfg = Release|x64 {8E0D0989-21F6-4DD8-946C-39F992523CC6}.Release.Windows|x64.Build.0 = Release|x64 + {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Debug.Linux|x64.ActiveCfg = Debug|x64 {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Debug.Mac|x64.ActiveCfg = Debug|x64 {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Debug.Windows|x64.ActiveCfg = Debug|x64 {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Debug.Windows|x64.Build.0 = Debug|x64 + {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Release.Linux|x64.ActiveCfg = Release|x64 {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Release.Mac|x64.ActiveCfg = Release|x64 {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Release.Windows|x64.ActiveCfg = Release|x64 {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Release.Windows|x64.Build.0 = Release|x64 + {BDA91EE5-C684-4FC5-A90A-B7D677421917}.Debug.Linux|x64.ActiveCfg = Debug|x64 {BDA91EE5-C684-4FC5-A90A-B7D677421917}.Debug.Mac|x64.ActiveCfg = Debug|x64 {BDA91EE5-C684-4FC5-A90A-B7D677421917}.Debug.Windows|x64.ActiveCfg = Debug|x64 {BDA91EE5-C684-4FC5-A90A-B7D677421917}.Debug.Windows|x64.Build.0 = Debug|x64 + {BDA91EE5-C684-4FC5-A90A-B7D677421917}.Release.Linux|x64.ActiveCfg = Release|x64 {BDA91EE5-C684-4FC5-A90A-B7D677421917}.Release.Mac|x64.ActiveCfg = Release|x64 {BDA91EE5-C684-4FC5-A90A-B7D677421917}.Release.Windows|x64.ActiveCfg = Release|x64 {BDA91EE5-C684-4FC5-A90A-B7D677421917}.Release.Windows|x64.Build.0 = Release|x64 + {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}.Debug.Linux|x64.ActiveCfg = Debug|x64 {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}.Debug.Mac|x64.ActiveCfg = Debug|x64 {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}.Debug.Windows|x64.ActiveCfg = Debug|x64 {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}.Debug.Windows|x64.Build.0 = Debug|x64 + {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}.Release.Linux|x64.ActiveCfg = Release|x64 {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.Linux|x64.ActiveCfg = Debug|x64 + {03769A07-F216-456B-886B-E07CAF6C5E81}.Debug.Linux|x64.Build.0 = Debug|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.Linux|x64.ActiveCfg = Release|x64 + {03769A07-F216-456B-886B-E07CAF6C5E81}.Release.Linux|x64.Build.0 = Release|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.Linux|x64.ActiveCfg = Debug|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 + {5A6656D5-81C7-472C-9DC8-32D071CB2258}.Release.Linux|x64.ActiveCfg = Release|x64 {5A6656D5-81C7-472C-9DC8-32D071CB2258}.Release.Mac|x64.ActiveCfg = Release|x64 {5A6656D5-81C7-472C-9DC8-32D071CB2258}.Release.Windows|x64.ActiveCfg = Release|x64 {5A6656D5-81C7-472C-9DC8-32D071CB2258}.Release.Windows|x64.Build.0 = Release|x64 + {C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Debug.Linux|x64.ActiveCfg = Debug|x64 {C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Debug.Mac|x64.ActiveCfg = Debug|x64 {C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Debug.Windows|x64.ActiveCfg = Debug|x64 {C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Debug.Windows|x64.Build.0 = Debug|x64 + {C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Release.Linux|x64.ActiveCfg = Release|x64 {C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Release.Mac|x64.ActiveCfg = Release|x64 {C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Release.Windows|x64.ActiveCfg = Release|x64 {C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Release.Windows|x64.Build.0 = Release|x64 + {93B403FD-DAFB-46C5-9636-B122792A548A}.Debug.Linux|x64.ActiveCfg = Debug|x64 {93B403FD-DAFB-46C5-9636-B122792A548A}.Debug.Mac|x64.ActiveCfg = Debug|x64 {93B403FD-DAFB-46C5-9636-B122792A548A}.Debug.Windows|x64.ActiveCfg = Debug|x64 {93B403FD-DAFB-46C5-9636-B122792A548A}.Debug.Windows|x64.Build.0 = Debug|x64 + {93B403FD-DAFB-46C5-9636-B122792A548A}.Release.Linux|x64.ActiveCfg = Release|x64 {93B403FD-DAFB-46C5-9636-B122792A548A}.Release.Mac|x64.ActiveCfg = Release|x64 {93B403FD-DAFB-46C5-9636-B122792A548A}.Release.Windows|x64.ActiveCfg = Release|x64 {93B403FD-DAFB-46C5-9636-B122792A548A}.Release.Windows|x64.Build.0 = Release|x64 + {A4984251-840E-4622-AD0C-66DFCE2B2574}.Debug.Linux|x64.ActiveCfg = Debug|x64 {A4984251-840E-4622-AD0C-66DFCE2B2574}.Debug.Mac|x64.ActiveCfg = Debug|x64 {A4984251-840E-4622-AD0C-66DFCE2B2574}.Debug.Windows|x64.ActiveCfg = Debug|x64 {A4984251-840E-4622-AD0C-66DFCE2B2574}.Debug.Windows|x64.Build.0 = Debug|x64 + {A4984251-840E-4622-AD0C-66DFCE2B2574}.Release.Linux|x64.ActiveCfg = Release|x64 {A4984251-840E-4622-AD0C-66DFCE2B2574}.Release.Mac|x64.ActiveCfg = Release|x64 {A4984251-840E-4622-AD0C-66DFCE2B2574}.Release.Windows|x64.ActiveCfg = Release|x64 {A4984251-840E-4622-AD0C-66DFCE2B2574}.Release.Windows|x64.Build.0 = Release|x64 + {798DE293-6EDA-4DC4-9395-BE7A71C563E3}.Debug.Linux|x64.ActiveCfg = Debug|x64 {798DE293-6EDA-4DC4-9395-BE7A71C563E3}.Debug.Mac|x64.ActiveCfg = Debug|x64 {798DE293-6EDA-4DC4-9395-BE7A71C563E3}.Debug.Windows|x64.ActiveCfg = Debug|x64 {798DE293-6EDA-4DC4-9395-BE7A71C563E3}.Debug.Windows|x64.Build.0 = Debug|x64 + {798DE293-6EDA-4DC4-9395-BE7A71C563E3}.Release.Linux|x64.ActiveCfg = Release|x64 {798DE293-6EDA-4DC4-9395-BE7A71C563E3}.Release.Mac|x64.ActiveCfg = Release|x64 {798DE293-6EDA-4DC4-9395-BE7A71C563E3}.Release.Windows|x64.ActiveCfg = Release|x64 {798DE293-6EDA-4DC4-9395-BE7A71C563E3}.Release.Windows|x64.Build.0 = Release|x64 + {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893}.Debug.Linux|x64.ActiveCfg = Debug|x64 {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893}.Debug.Mac|x64.ActiveCfg = Debug|x64 {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893}.Debug.Windows|x64.ActiveCfg = Debug|x64 {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893}.Debug.Windows|x64.Build.0 = Debug|x64 + {3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893}.Release.Linux|x64.ActiveCfg = Release|x64 {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.Linux|x64.ActiveCfg = Debug|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.Linux|x64.ActiveCfg = Release|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.Linux|x64.ActiveCfg = Debug|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 + {2F63B22B-EE26-4266-BF17-28A9146483A1}.Release.Linux|x64.ActiveCfg = Release|x64 {2F63B22B-EE26-4266-BF17-28A9146483A1}.Release.Mac|x64.ActiveCfg = Release|x64 {2F63B22B-EE26-4266-BF17-28A9146483A1}.Release.Windows|x64.ActiveCfg = Release|x64 {2F63B22B-EE26-4266-BF17-28A9146483A1}.Release.Windows|x64.Build.0 = Release|x64 + {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug.Linux|x64.ActiveCfg = Debug|x64 {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug.Mac|x64.ActiveCfg = Debug|x64 {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug.Windows|x64.ActiveCfg = Debug|x64 {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug.Windows|x64.Build.0 = Debug|x64 + {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Release.Linux|x64.ActiveCfg = Release|x64 {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Release.Mac|x64.ActiveCfg = Release|x64 {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Release.Windows|x64.ActiveCfg = Release|x64 {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Release.Windows|x64.Build.0 = Release|x64 + {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Linux|x64.Build.0 = Debug|x64 {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Mac|x64.ActiveCfg = Debug|x64 {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Mac|x64.Build.0 = Debug|x64 {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Windows|x64.ActiveCfg = Debug|x64 {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Windows|x64.Build.0 = Debug|x64 + {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Linux|x64.ActiveCfg = Release|x64 + {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Linux|x64.Build.0 = Release|x64 {F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Mac|x64.ActiveCfg = Release|x64 {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 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Debug.Linux|x64.Build.0 = Debug|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Debug.Mac|x64.ActiveCfg = Debug|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Debug.Mac|x64.Build.0 = Debug|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Debug.Windows|x64.ActiveCfg = Debug|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Debug.Windows|x64.Build.0 = Debug|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Release.Linux|x64.ActiveCfg = Release|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Release.Linux|x64.Build.0 = Release|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Release.Mac|x64.ActiveCfg = Release|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Release.Mac|x64.Build.0 = Release|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Release.Windows|x64.ActiveCfg = Release|x64 + {63E57526-9825-44C8-9ACB-DC3296ADEE97}.Release.Windows|x64.Build.0 = Release|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Debug.Linux|x64.Build.0 = Debug|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Debug.Mac|x64.ActiveCfg = Debug|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Debug.Mac|x64.Build.0 = Debug|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Debug.Windows|x64.ActiveCfg = Debug|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Debug.Windows|x64.Build.0 = Debug|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Release.Linux|x64.ActiveCfg = Release|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Release.Linux|x64.Build.0 = Release|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Release.Mac|x64.ActiveCfg = Release|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Release.Mac|x64.Build.0 = Release|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Release.Windows|x64.ActiveCfg = Release|x64 + {B4593252-9FB4-431D-A4E1-634D09DE09DE}.Release.Windows|x64.Build.0 = Release|x64 + {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Linux|x64.Build.0 = Debug|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.Linux|x64.ActiveCfg = Release|x64 + {FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Linux|x64.Build.0 = Release|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.Linux|x64.ActiveCfg = Debug|x64 + {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Debug.Linux|x64.Build.0 = Debug|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.Linux|x64.ActiveCfg = Release|x64 + {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Release.Linux|x64.Build.0 = Release|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.Linux|x64.ActiveCfg = Debug|x64 + {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Debug.Linux|x64.Build.0 = Debug|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 {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Debug.Windows|x64.Build.0 = Debug|x64 + {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Release.Linux|x64.ActiveCfg = Release|x64 + {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Release.Linux|x64.Build.0 = Release|x64 {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Release.Mac|x64.ActiveCfg = Release|x64 {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Release.Mac|x64.Build.0 = Release|x64 {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Release.Windows|x64.ActiveCfg = Release|x64 {15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}.Release.Windows|x64.Build.0 = Release|x64 + {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Debug.Linux|x64.Build.0 = Debug|x64 {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Debug.Mac|x64.ActiveCfg = Debug|x64 {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Debug.Mac|x64.Build.0 = Debug|x64 {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Debug.Windows|x64.ActiveCfg = Debug|x64 {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Debug.Windows|x64.Build.0 = Debug|x64 + {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Release.Linux|x64.ActiveCfg = Release|x64 + {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Release.Linux|x64.Build.0 = Release|x64 {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Release.Mac|x64.ActiveCfg = Release|x64 {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Release.Mac|x64.Build.0 = Release|x64 {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Release.Windows|x64.ActiveCfg = Release|x64 {0D434FA7-6D8C-481E-B0CE-779B59EAEF53}.Release.Windows|x64.Build.0 = Release|x64 + {4CE404E7-D3FC-471C-993C-64615861EA63}.Debug.Linux|x64.ActiveCfg = Debug|x64 {4CE404E7-D3FC-471C-993C-64615861EA63}.Debug.Mac|x64.ActiveCfg = Debug|x64 {4CE404E7-D3FC-471C-993C-64615861EA63}.Debug.Windows|x64.ActiveCfg = Debug|x64 {4CE404E7-D3FC-471C-993C-64615861EA63}.Debug.Windows|x64.Build.0 = Debug|x64 + {4CE404E7-D3FC-471C-993C-64615861EA63}.Release.Linux|x64.ActiveCfg = Release|x64 {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 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Debug.Linux|x64.Build.0 = Debug|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Debug.Mac|x64.ActiveCfg = Debug|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Debug.Windows|x64.ActiveCfg = Debug|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Debug.Windows|x64.Build.0 = Debug|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Release.Linux|x64.ActiveCfg = Release|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Release.Linux|x64.Build.0 = Release|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Release.Mac|x64.ActiveCfg = Release|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Release.Windows|x64.ActiveCfg = Release|x64 + {A1A1A31A-A76E-4F12-92A7-91755320C9A4}.Release.Windows|x64.Build.0 = Release|x64 + {28939122-7263-41E7-A7E2-CBFB01AD6A04}.Debug.Linux|x64.ActiveCfg = Debug|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.Linux|x64.ActiveCfg = Release|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.Linux|x64.ActiveCfg = Debug|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 + {32220664-594C-4425-B9A0-88E0BE2F3D2A}.Release.Linux|x64.ActiveCfg = Release|x64 {32220664-594C-4425-B9A0-88E0BE2F3D2A}.Release.Mac|x64.ActiveCfg = Release|x64 {32220664-594C-4425-B9A0-88E0BE2F3D2A}.Release.Windows|x64.ActiveCfg = Release|x64 {32220664-594C-4425-B9A0-88E0BE2F3D2A}.Release.Windows|x64.Build.0 = Release|x64 + {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}.Debug.Linux|x64.ActiveCfg = Debug|x64 {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}.Debug.Mac|x64.ActiveCfg = Debug|x64 {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}.Debug.Windows|x64.ActiveCfg = Debug|x64 {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}.Debug.Windows|x64.Build.0 = Debug|x64 + {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}.Release.Linux|x64.ActiveCfg = Release|x64 {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 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Debug.Linux|x64.Build.0 = Debug|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Debug.Mac|x64.ActiveCfg = Debug|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Debug.Windows|x64.ActiveCfg = Debug|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Debug.Windows|x64.Build.0 = Debug|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Release.Linux|x64.ActiveCfg = Release|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Release.Linux|x64.Build.0 = Release|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Release.Mac|x64.ActiveCfg = Release|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Release.Windows|x64.ActiveCfg = Release|x64 + {B9F62A3B-72F5-4703-A5FE-3879FF930410}.Release.Windows|x64.Build.0 = Release|x64 + {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Debug.Linux|x64.ActiveCfg = Debug|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.Linux|x64.ActiveCfg = Release|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.Linux|x64.ActiveCfg = Debug|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 + {0F0A008E-AB12-40EC-A671-37A541B08C7F}.Release.Linux|x64.ActiveCfg = Release|x64 {0F0A008E-AB12-40EC-A671-37A541B08C7F}.Release.Mac|x64.ActiveCfg = Release|x64 {0F0A008E-AB12-40EC-A671-37A541B08C7F}.Release.Windows|x64.ActiveCfg = Release|x64 {0F0A008E-AB12-40EC-A671-37A541B08C7F}.Release.Windows|x64.Build.0 = Release|x64 + {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Linux|x64.Build.0 = Debug|x64 {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Mac|x64.ActiveCfg = Debug|x64 {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Mac|x64.Build.0 = Debug|x64 {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Windows|x64.ActiveCfg = Debug|x64 {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Windows|x64.Build.0 = Debug|x64 + {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Linux|x64.ActiveCfg = Release|x64 + {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Linux|x64.Build.0 = Release|x64 {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Mac|x64.ActiveCfg = Release|x64 {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Mac|x64.Build.0 = Release|x64 {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Windows|x64.ActiveCfg = Release|x64 {BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Windows|x64.Build.0 = Release|x64 + {FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Linux|x64.ActiveCfg = Debug|x64 {FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Mac|x64.ActiveCfg = Debug|x64 {FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Mac|x64.Build.0 = Debug|x64 {FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Windows|x64.ActiveCfg = Debug|x64 {FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Windows|x64.Build.0 = Debug|x64 + {FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Linux|x64.ActiveCfg = Release|x64 {FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Mac|x64.ActiveCfg = Release|x64 {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 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Debug.Linux|x64.ActiveCfg = Debug|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Debug.Linux|x64.Build.0 = Debug|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Debug.Mac|x64.ActiveCfg = Debug|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Debug.Windows|x64.ActiveCfg = Debug|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Debug.Windows|x64.Build.0 = Debug|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Release.Linux|x64.ActiveCfg = Release|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Release.Linux|x64.Build.0 = Release|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Release.Mac|x64.ActiveCfg = Release|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Release.Windows|x64.ActiveCfg = Release|x64 + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C}.Release.Windows|x64.Build.0 = Release|x64 + {4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Linux|x64.ActiveCfg = Debug|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.Linux|x64.ActiveCfg = Release|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.Linux|x64.ActiveCfg = Debug|x64 + {AECEC217-2499-403D-B0BB-2962B9BE5970}.Debug.Linux|x64.Build.0 = Debug|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.Linux|x64.ActiveCfg = Release|x64 + {AECEC217-2499-403D-B0BB-2962B9BE5970}.Release.Linux|x64.Build.0 = Release|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.Linux|x64.ActiveCfg = Debug|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 + {24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Release.Linux|x64.ActiveCfg = Release|x64 {24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Release.Mac|x64.ActiveCfg = Release|x64 {24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Release.Windows|x64.ActiveCfg = Release|x64 {24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Release.Windows|x64.Build.0 = Release|x64 @@ -465,18 +625,23 @@ Global {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} + {63E57526-9825-44C8-9ACB-DC3296ADEE97} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} + {B4593252-9FB4-431D-A4E1-634D09DE09DE} = {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} + {A1A1A31A-A76E-4F12-92A7-91755320C9A4} = {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} + {B9F62A3B-72F5-4703-A5FE-3879FF930410} = {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} + {E6E1BC4C-1CF6-4FFA-84A0-74E02AE4096C} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C} {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} diff --git a/GVFS/FastFetch/FastFetch.csproj b/GVFS/FastFetch/FastFetch.csproj index 90aac7cc48..bfd499b9a9 100644 --- a/GVFS/FastFetch/FastFetch.csproj +++ b/GVFS/FastFetch/FastFetch.csproj @@ -19,6 +19,11 @@ $(GVFSVersion) + + true + true + + @@ -35,14 +40,22 @@ - + + + + + PlatformLoader.Linux.cs + + + + PlatformLoader.Mac.cs - + diff --git a/GVFS/GVFS.Hooks/GVFS.Hooks.Linux.csproj b/GVFS/GVFS.Hooks/GVFS.Hooks.Linux.csproj new file mode 100644 index 0000000000..a084420005 --- /dev/null +++ b/GVFS/GVFS.Hooks/GVFS.Hooks.Linux.csproj @@ -0,0 +1,113 @@ + + + + Exe + GVFS.Hooks + netcoreapp2.1 + x64 + linux-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 + + + "LinuxPlatform.Shared.cs" + + + POSIX\POSIXFileSystem.Shared.cs + + + POSIX\POSIXPlatform.Shared.cs + + + + + + all + + + diff --git a/GVFS/GVFS.Hooks/GVFS.Hooks.Mac.csproj b/GVFS/GVFS.Hooks/GVFS.Hooks.Mac.csproj index 9cf4b76519..44da545fa5 100644 --- a/GVFS/GVFS.Hooks/GVFS.Hooks.Mac.csproj +++ b/GVFS/GVFS.Hooks/GVFS.Hooks.Mac.csproj @@ -20,6 +20,7 @@ $(GVFSVersion) + diff --git a/GVFS/GVFS.Hooks/HooksPlatform/GVFSHooksPlatform.Linux.cs b/GVFS/GVFS.Hooks/HooksPlatform/GVFSHooksPlatform.Linux.cs new file mode 100644 index 0000000000..213632e84e --- /dev/null +++ b/GVFS/GVFS.Hooks/HooksPlatform/GVFSHooksPlatform.Linux.cs @@ -0,0 +1,32 @@ +using GVFS.Platform.Linux; + +namespace GVFS.Hooks.HooksPlatform +{ + public static partial class GVFSHooksPlatform + { + public static string GetUpgradeHighestAvailableVersionDirectory() + { + return LinuxPlatform.GetUpgradeHighestAvailableVersionDirectoryImplementation(); + } + + public static bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage) + { + return LinuxPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage); + } + + public static string GetNamedPipeName(string enlistmentRoot) + { + return LinuxPlatform.GetNamedPipeNameImplementation(enlistmentRoot); + } + + public static string GetGitGuiBlockedMessage() + { + return "git gui is not supported in VFS for Git repos on Linux"; + } + + public static string GetUpgradeReminderNotification() + { + return LinuxPlatform.GetUpgradeReminderNotificationImplementation(); + } + } +} diff --git a/GVFS/GVFS.Mount/GVFS.Mount.Linux.csproj b/GVFS/GVFS.Mount/GVFS.Mount.Linux.csproj new file mode 100644 index 0000000000..8cbb003712 --- /dev/null +++ b/GVFS/GVFS.Mount/GVFS.Mount.Linux.csproj @@ -0,0 +1,35 @@ + + + + Exe + GVFS.Mount + netcoreapp2.1 + x64 + linux-x64 + + + + $(GVFSVersion) + + + $(GVFSVersion) + + + + + PlatformLoader.Linux.cs + + + + + + + + + + + all + + + + diff --git a/GVFS/GVFS.Platform.Linux/GVFS.Platform.Linux.csproj b/GVFS/GVFS.Platform.Linux/GVFS.Platform.Linux.csproj new file mode 100644 index 0000000000..8f7bfb7758 --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/GVFS.Platform.Linux.csproj @@ -0,0 +1,34 @@ + + + + netcoreapp2.1;netstandard2.0 + x64 + true + true + + + + $(GVFSVersion) + + + $(GVFSVersion) + + + + $(GVFSVersion) + + + + + + + + + + + + + all + + + diff --git a/GVFS/GVFS.Platform.Linux/LinuxDiskLayoutUpgradeData.cs b/GVFS/GVFS.Platform.Linux/LinuxDiskLayoutUpgradeData.cs new file mode 100644 index 0000000000..bca7601edc --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/LinuxDiskLayoutUpgradeData.cs @@ -0,0 +1,27 @@ +using GVFS.Common; +using GVFS.DiskLayoutUpgrades; + +namespace GVFS.Platform.Linux +{ + public class LinuxDiskLayoutUpgradeData : IDiskLayoutUpgradeData + { + public DiskLayoutUpgrade[] Upgrades + { + get + { + return new DiskLayoutUpgrade[0]; + } + } + + public DiskLayoutVersion Version => new DiskLayoutVersion( + currentMajorVersion: 19, + currentMinorVersion: 0, + minimumSupportedMajorVersion: 19); + + public bool TryParseLegacyDiskLayoutVersion(string dotGVFSPath, out int majorVersion) + { + majorVersion = 0; + return false; + } + } +} diff --git a/GVFS/GVFS.Platform.Linux/LinuxFileBasedLock.cs b/GVFS/GVFS.Platform.Linux/LinuxFileBasedLock.cs new file mode 100644 index 0000000000..03a1bfcadb --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/LinuxFileBasedLock.cs @@ -0,0 +1,133 @@ +using GVFS.Common; +using GVFS.Common.FileSystem; +using GVFS.Common.Tracing; +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace GVFS.Platform.Linux +{ + public class LinuxFileBasedLock : FileBasedLock + { + private int lockFileDescriptor; + + public LinuxFileBasedLock( + 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(LinuxFileBasedLock)}.{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(LinuxFileBasedLock)}.{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(LinuxFileBasedLock)}.{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(LinuxFileBasedLock)); + 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 0x0040 /* create if nonexistant */ + public const int OpenCreate = 0x0040; + + // #define EINTR 4 /* Interrupted system call */ + public const int EIntr = 4; + + // #define EAGAIN 11 /* Resource temporarily unavailable */ + // #define EWOULDBLOCK EAGAIN /* Operation would block */ + public const int EWouldBlock = 11; + + 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 uint FileMode644 = Convert.ToUInt32("644", 8); + + [DllImport("libc", EntryPoint = "open", SetLastError = true)] + public static extern int Open(string pathname, int flags, uint 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.Linux/LinuxFileSystem.cs b/GVFS/GVFS.Platform.Linux/LinuxFileSystem.cs new file mode 100644 index 0000000000..b2919bff99 --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/LinuxFileSystem.cs @@ -0,0 +1,162 @@ +using GVFS.Common; +using GVFS.Platform.POSIX; +using System; +using System.Runtime.InteropServices; + +namespace GVFS.Platform.Linux +{ + public class LinuxFileSystem : 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); + } + + [DllImport("libc", EntryPoint = "chmod", SetLastError = true)] + private static extern int Chmod(string pathname, uint 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 uint IFMT = Convert.ToUInt32("170000", 8); + + // #define S_IFSOCK 0140000 /* [XSI] socket */ + private static readonly uint IFSOCK = Convert.ToUInt32("0140000", 8); + + // #define S_IXUSR 0000100 /* [XSI] X for owner */ + private static readonly uint IXUSR = Convert.ToUInt32("100", 8); + + // #define S_IXGRP 0000010 /* [XSI] X for group */ + private static readonly uint IXGRP = Convert.ToUInt32("10", 8); + + // #define S_IXOTH 0000001 /* [XSI] X for other */ + private static readonly uint IXOTH = Convert.ToUInt32("1", 8); + + // #define _STAT_VER 1 + private static readonly int STAT_VER = 1; + + public static bool IsSock(uint mode) + { + // #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* socket */ + return (mode & IFMT) == IFSOCK; + } + + public static bool IsExecutable(uint mode) + { + return (mode & (IXUSR | IXGRP | IXOTH)) != 0; + } + + public static int Stat(string path, [Out] out StatBuffer buf) + { + return __XStat64(STAT_VER, path, out buf); + } + + // TODO(Linux): assumes recent GNU libc or ABI-compatible libc + [DllImport("libc", EntryPoint = "__xstat64", SetLastError = true)] + public static extern int __XStat64(int vers, string path, [Out] out StatBuffer buf); + + [StructLayout(LayoutKind.Sequential)] + public struct TimeSpec + { + public long Sec; + public long Nsec; + } + + // TODO(Linux): assumes stat64 field layout of x86-64 architecture + [StructLayout(LayoutKind.Sequential)] + public struct StatBuffer + { + public ulong Dev; /* ID of device containing file */ + public ulong Ino; /* File serial number */ + public ulong NLink; /* Number of hard links */ + public uint Mode; /* Mode of file (see below) */ + public uint UID; /* User ID of the file */ + public uint GID; /* Group ID of the file */ + public uint Padding; /* RESERVED: DO NOT USE! */ + public ulong RDev; /* Device ID if special file */ + public long Size; /* file size, in bytes */ + public long BlkSize; /* optimal blocksize for I/O */ + public long Blocks; /* blocks allocated for file */ + public TimeSpec ATimespec; /* time of last access */ + public TimeSpec MTimespec; /* time of last data modification */ + public TimeSpec CTimespec; /* time of last status change */ + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public long[] Reserved; /* RESERVED: DO NOT USE! */ + } + } + + private static class NativeFileReader + { + 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 long Read(int fd, [Out] byte[] buf, ulong count); + + private static bool TryReadOneByte(int fileDescriptor, byte[] buffer) + { + long numBytes = Read(fileDescriptor, buffer, 1); + + if (numBytes == -1) + { + return false; + } + + return true; + } + } + } +} diff --git a/GVFS/GVFS.Platform.Linux/LinuxFileSystemVirtualizer.cs b/GVFS/GVFS.Platform.Linux/LinuxFileSystemVirtualizer.cs new file mode 100644 index 0000000000..716620aceb --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/LinuxFileSystemVirtualizer.cs @@ -0,0 +1,763 @@ +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.Linux; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; + +namespace GVFS.Platform.Linux +{ + public class LinuxFileSystemVirtualizer : FileSystemVirtualizer + { + public static readonly byte[] PlaceholderVersionId = ToVersionIdByteArray(new byte[] { PlaceholderVersion }); + + private const int SymLinkTargetBufferSize = 4096; + + private const string ClassName = nameof(LinuxFileSystemVirtualizer); + + private VirtualizationInstance virtualizationInstance; + + public LinuxFileSystemVirtualizer(GVFSContext context, GVFSGitObjects gitObjects) + : this(context, gitObjects, virtualizationInstance: null) + { + } + + public LinuxFileSystemVirtualizer( + 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.virtualizationInstance.StopVirtualizationInstance(); + 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)), + (ulong)endOfFile, + 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)); + } + + 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)), + (ulong)endOfFile, + 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.WorkingDirectoryBackingRoot, + this.Context.Enlistment.WorkingDirectoryRoot, + threadCount); + + // TODO(Linux): note that most start errors are not reported + // because they can only be retrieved from projfs_stop() at present + 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; + } + + // TODO(Linux): wait for device ID from stat() to change + + this.Context.Tracer.RelatedEvent(EventLevel.Informational, $"{nameof(this.TryStart)}_StartedVirtualization", metadata: null); + return true; + } + + private static string ConvertDotPath(string path) + { + if (path == ".") + { + path = string.Empty; + } + + return path; + } + + 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, + int fd) + { + 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( + fd, + 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 + // Unfortunately we must do this check *after* the file is hydrated since the header isn't corrupt for trunctated objects on Linux + 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(LinuxFileSystemVirtualizer)}::{nameof(this.OnLogError)}: {errorMessage}"); + } + + private void OnLogWarning(string warningMessage) + { + this.Context.Tracer.RelatedWarning($"{nameof(LinuxFileSystemVirtualizer)}::{nameof(this.OnLogWarning)}: {warningMessage}"); + } + + private void OnLogInfo(string infoMessage) + { + this.Context.Tracer.RelatedInfo($"{nameof(LinuxFileSystemVirtualizer)}::{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) + { + // TODO(Linux): VFSForGit doesn't need the source path on Linux for correct behavior, + // but it is available if required in the future + this.OnFileRenamed( + relativeSourcePath: string.Empty, + relativeDestinationPath: relativeDestinationPath, + isDirectory: isDirectory); + } + + private void OnHardLinkCreated(string relativeNewLinkPath) + { + this.OnHardLinkCreated( + relativeExistingFilePath: string.Empty, + relativeNewLinkPath: relativeNewLinkPath); + } + + private Result OnEnumerateDirectory( + ulong commandId, + string relativePath, + int triggeringProcessId, + string triggeringProcessName) + { + relativePath = ConvertDotPath(relativePath); + + try + { + Result result; + try + { + IEnumerable projectedItems; + + // TODO(Linux): Pool these connections or schedule this work to run asynchronously using TryScheduleFileOrNetworkRequest + using (BlobSizes.BlobSizesConnection blobSizesConnection = this.FileSystemCallbacks.BlobSizes.CreateConnection()) + { + projectedItems = this.FileSystemCallbacks.GitIndexProjection.GetProjectedItems(CancellationToken.None, blobSizesConnection, relativePath); + } + + result = this.CreatePlaceholders(relativePath, projectedItems, triggeringProcessName); + } + catch (SizesUnavailableException e) + { + // TODO(Linux): Is this the correct Result to return? + result = Result.EIOError; + + EventMetadata metadata = this.CreateEventMetadata(relativePath, e); + metadata.Add("commandId", commandId); + metadata.Add(nameof(result), result.ToString("X") + "(" + result.ToString("G") + ")"); + this.Context.Tracer.RelatedError(metadata, nameof(this.OnEnumerateDirectory) + ": caught SizesUnavailableException"); + } + + return result; + } + 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(); + fileSystemResult = this.WritePlaceholderFile(childRelativePath, fileInfo.Size, 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(sha), sha); + this.Context.Tracer.RelatedError(metadata, $"{nameof(this.CreatePlaceholders)}: Write placeholder failed"); + + 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.Linux/LinuxPlatform.Shared.cs b/GVFS/GVFS.Platform.Linux/LinuxPlatform.Shared.cs new file mode 100644 index 0000000000..28528bd94e --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/LinuxPlatform.Shared.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using GVFS.Common; +using GVFS.Platform.POSIX; + +namespace GVFS.Platform.Linux +{ + public partial class LinuxPlatform + { + public const string DotGVFSRoot = ".vfsforgit"; + public const string UpgradeConfirmMessage = "`sudo gvfs upgrade --confirm --no-verify`"; + + public static string GetDataRootForGVFSImplementation() + { + // TODO(Linux): determine installation location and data path + string path = Environment.GetEnvironmentVariable("VFS4G_DATA_PATH"); + return path ?? "/var/run/vfsforgit"; + } + + 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.Linux/LinuxPlatform.cs b/GVFS/GVFS.Platform.Linux/LinuxPlatform.cs new file mode 100644 index 0000000000..0880b80afc --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/LinuxPlatform.cs @@ -0,0 +1,175 @@ +using GVFS.Common; +using GVFS.Common.FileSystem; +using GVFS.Common.Tracing; +using GVFS.Platform.POSIX; +using System.Collections.Generic; +using System.IO; + +namespace GVFS.Platform.Linux +{ + public partial class LinuxPlatform : POSIXPlatform + { + // TODO(Linux): determine installation location and upgrader path + private const string UpgradeProtectedDataDirectory = "/usr/local/vfsforgit_upgrader"; + + public LinuxPlatform() + { + } + + public override IDiskLayoutUpgradeData DiskLayoutUpgrade { get; } = new LinuxDiskLayoutUpgradeData(); + public override IKernelDriver KernelDriver { get; } = new ProjFSLib(); + public override string Name { get => "Linux"; } + public override GVFSPlatformConstants Constants { get; } = new LinuxPlatformConstants(); + public override IPlatformFileSystem FileSystem { get; } = new LinuxFileSystem(); + + public override string GVFSConfigPath + { + get + { + return Path.Combine(this.Constants.GVFSBinDirectoryPath, LocalGVFSConfig.FileName); + } + } + + /// + /// On Linux VFSForGit does not need to use system wide logs to track + /// installer messages. VFSForGit is able to specifiy a custom installer + /// log file as a commandline argument to the installer. + /// + public override bool SupportsSystemInstallLog + { + get + { + return false; + } + } + + 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 Linux, unlike Windows, there is no separate secure data root directory. + return LinuxPlatform.GetDataRootForGVFSImplementation(); + } + + public override string GetSecureDataRootForGVFSComponent(string componentName) + { + // On the Linux, unlike Windows, there is no separate secure data root directory. + return LinuxPlatform.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 LinuxPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage); + } + + public override string GetNamedPipeName(string enlistmentRoot) + { + return LinuxPlatform.GetNamedPipeNameImplementation(enlistmentRoot); + } + + public override FileBasedLock CreateFileBasedLock( + PhysicalFileSystem fileSystem, + ITracer tracer, + string lockPath) + { + return new LinuxFileBasedLock(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 null; + } + + public override ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformInteractions( + PhysicalFileSystem fileSystem, + ITracer tracer) + { + return new LinuxProductUpgraderPlatformStrategy(fileSystem, tracer); + } + + public class LinuxPlatformConstants : POSIXPlatformConstants + { + public override string InstallerExtension + { + get { return ".deb"; } + } + + public override string WorkingDirectoryBackingRootPath + { + get { return Path.Combine(this.DotGVFSRoot, "lower"); } + } + + public override string DotGVFSRoot + { + get { return LinuxPlatform.DotGVFSRoot; } + } + + public override string GVFSBinDirectoryPath + { + get { return Path.Combine("/usr", "local", this.GVFSBinDirectoryName); } + } + + public override string GVFSBinDirectoryName + { + get { return "vfsforgit"; } + } + + public override string UpgradeInstallAdviceMessage + { + get { return $"When ready, run {this.UpgradeConfirmCommandMessage} to upgrade."; } + } + + public override string UpgradeConfirmCommandMessage + { + get { return UpgradeConfirmMessage; } + } + + public override string StartServiceCommandMessage + { + // TODO(Linux): implement service daemon + get { return "Not yet implemented"; } + } + + public override string RunUpdateMessage + { + get { return $"Run {UpgradeConfirmMessage}."; } + } + + public override bool CaseSensitiveFileSystem => true; + } + } +} diff --git a/GVFS/GVFS.Platform.Linux/LinuxProductUpgraderPlatformStrategy.cs b/GVFS/GVFS.Platform.Linux/LinuxProductUpgraderPlatformStrategy.cs new file mode 100644 index 0000000000..75e206dcc4 --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/LinuxProductUpgraderPlatformStrategy.cs @@ -0,0 +1,62 @@ +using GVFS.Common; +using GVFS.Common.FileSystem; +using GVFS.Common.Tracing; +using System; +using System.IO; + +namespace GVFS.Platform.Linux +{ + public class LinuxProductUpgraderPlatformStrategy : ProductUpgraderPlatformStrategy + { + public LinuxProductUpgraderPlatformStrategy(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.Linux/ProjFSLib.cs b/GVFS/GVFS.Platform.Linux/ProjFSLib.cs new file mode 100644 index 0000000000..490567e2e5 --- /dev/null +++ b/GVFS/GVFS.Platform.Linux/ProjFSLib.cs @@ -0,0 +1,68 @@ +using GVFS.Common; +using GVFS.Common.FileSystem; +using GVFS.Common.Tracing; +using System; +using System.IO; + +namespace GVFS.Platform.Linux +{ + public class ProjFSLib : IKernelDriver + { + public bool EnumerationExpandsDirectories { get; } = true; + public bool EmptyPlaceholdersRequireFileSize { get; } = true; + + /* TODO(Linux): check for kernel fuse, libfuse v3, libprojfs; + * flesh out all methods below + */ + + public string LogsFolderPath + { + get + { + return Path.Combine(System.IO.Path.GetTempPath(), "ProjFSLib"); + } + } + + public bool IsGVFSUpgradeSupported() + { + return false; + } + + public bool IsSupported(string normalizedEnlistmentRootPath, out string warning, out string error) + { + warning = null; + error = null; + return true; + } + + public bool TryFlushLogs(out string error) + { + Directory.CreateDirectory(this.LogsFolderPath); + error = string.Empty; + return true; + } + + public bool IsReady(JsonTracer tracer, string enlistmentRoot, TextWriter output, out string error) + { + error = null; + return true; + } + + public bool RegisterForOfflineIO() + { + return true; + } + + public bool UnregisterForOfflineIO() + { + return true; + } + + public bool TryPrepareFolderForCallbacks(string folderPath, out string error, out Exception exception) + { + error = null; + exception = null; + return true; + } + } +} diff --git a/GVFS/GVFS.Platform.Mac/GVFS.Platform.Mac.csproj b/GVFS/GVFS.Platform.Mac/GVFS.Platform.Mac.csproj index d702269054..feb5604c7b 100644 --- a/GVFS/GVFS.Platform.Mac/GVFS.Platform.Mac.csproj +++ b/GVFS/GVFS.Platform.Mac/GVFS.Platform.Mac.csproj @@ -18,6 +18,10 @@ $(GVFSVersion) + + true + + @@ -32,7 +36,7 @@ - + diff --git a/GVFS/GVFS.PlatformLoader/PlatformLoader.Linux.cs b/GVFS/GVFS.PlatformLoader/PlatformLoader.Linux.cs new file mode 100644 index 0000000000..ed8ecd82eb --- /dev/null +++ b/GVFS/GVFS.PlatformLoader/PlatformLoader.Linux.cs @@ -0,0 +1,21 @@ +using GVFS.Common; +using GVFS.Common.Git; +using GVFS.Platform.Linux; +using GVFS.Virtualization.FileSystem; + +namespace GVFS.PlatformLoader +{ + public static class GVFSPlatformLoader + { + public static FileSystemVirtualizer CreateFileSystemVirtualizer(GVFSContext context, GVFSGitObjects gitObjects) + { + return new LinuxFileSystemVirtualizer(context, gitObjects); + } + + public static void Initialize() + { + GVFSPlatform.Register(new LinuxPlatform()); + return; + } + } +} diff --git a/GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj b/GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj index c351c3ba93..aa42534e24 100644 --- a/GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj +++ b/GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj @@ -50,8 +50,10 @@ + + diff --git a/GVFS/GVFS.Upgrader/GVFS.Upgrader.csproj b/GVFS/GVFS.Upgrader/GVFS.Upgrader.csproj index 3b9629484a..25995b9847 100644 --- a/GVFS/GVFS.Upgrader/GVFS.Upgrader.csproj +++ b/GVFS/GVFS.Upgrader/GVFS.Upgrader.csproj @@ -20,6 +20,11 @@ $(GVFSVersion) + + true + true + + @@ -35,7 +40,20 @@ - + + + netcoreapp2.1 + linux-x64 + $(DefineConstants);LINUX_BUILD + + + + + PlatformLoader.Linux.cs + + + + netcoreapp2.1 osx-x64 @@ -47,7 +65,7 @@ PlatformLoader.Mac.cs - + diff --git a/GVFS/GVFS.Upgrader/LinuxUpgradeOrchestrator.cs b/GVFS/GVFS.Upgrader/LinuxUpgradeOrchestrator.cs new file mode 100644 index 0000000000..92043f2844 --- /dev/null +++ b/GVFS/GVFS.Upgrader/LinuxUpgradeOrchestrator.cs @@ -0,0 +1,17 @@ +namespace GVFS.Upgrader +{ + public class LinuxUpgradeOrchestrator : UpgradeOrchestrator + { + public LinuxUpgradeOrchestrator(UpgradeOptions options) + : base(options) + { + } + + protected override bool TryMountRepositories(out string consoleError) + { + // Linux upgrader does not mount repositories + consoleError = null; + return true; + } + } +} diff --git a/GVFS/GVFS.Upgrader/UpgradeOrchestratorFactory.cs b/GVFS/GVFS.Upgrader/UpgradeOrchestratorFactory.cs index c2ce00dbcf..fec275d162 100644 --- a/GVFS/GVFS.Upgrader/UpgradeOrchestratorFactory.cs +++ b/GVFS/GVFS.Upgrader/UpgradeOrchestratorFactory.cs @@ -1,10 +1,14 @@ +using System; + namespace GVFS.Upgrader { public static class UpgradeOrchestratorFactory { public static UpgradeOrchestrator Create(UpgradeOptions options) { -#if MACOS_BUILD +#if LINUX_BUILD + return new LinuxUpgradeOrchestrator(options); +#elif MACOS_BUILD return new MacUpgradeOrchestrator(options); #elif WINDOWS_BUILD return new WindowsUpgradeOrchestrator(options); diff --git a/GVFS/GVFS/GVFS.Linux.csproj b/GVFS/GVFS/GVFS.Linux.csproj new file mode 100644 index 0000000000..116f594d97 --- /dev/null +++ b/GVFS/GVFS/GVFS.Linux.csproj @@ -0,0 +1,28 @@ + + + + Exe + gvfs + netcoreapp2.1 + x64 + linux-x64 + + + + $(GVFSVersion) + + + $(GVFSVersion) + + + + all + + + + + + + + + diff --git a/MirrorProvider/MirrorProvider.Linux/MirrorProvider.Linux.csproj b/MirrorProvider/MirrorProvider.Linux/MirrorProvider.Linux.csproj index 19adade198..2b88e7d4ad 100644 --- a/MirrorProvider/MirrorProvider.Linux/MirrorProvider.Linux.csproj +++ b/MirrorProvider/MirrorProvider.Linux/MirrorProvider.Linux.csproj @@ -38,7 +38,7 @@ diff --git a/ProjFS.Linux/PrjFSLib.Linux.Managed/PrjFSLib.Linux.Managed.csproj b/ProjFS.Linux/PrjFSLib.Linux.Managed/PrjFSLib.Linux.Managed.csproj index 34866e92a6..edc8fee45f 100644 --- a/ProjFS.Linux/PrjFSLib.Linux.Managed/PrjFSLib.Linux.Managed.csproj +++ b/ProjFS.Linux/PrjFSLib.Linux.Managed/PrjFSLib.Linux.Managed.csproj @@ -4,6 +4,7 @@ x64 Debug;Release ..\..\GVFS\GVFS.Build\GVFS.ruleset + true diff --git a/Scripts/Linux/BuildGVFSForLinux.sh b/Scripts/Linux/BuildGVFSForLinux.sh new file mode 100755 index 0000000000..a25c25c9c8 --- /dev/null +++ b/Scripts/Linux/BuildGVFSForLinux.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +. "$(dirname ${BASH_SOURCE[0]})/InitializeEnvironment.sh" + +CONFIGURATION=$1 +if [ -z $CONFIGURATION ]; then + CONFIGURATION=Debug +fi + +if [ ! -d $VFS_OUTPUTDIR ]; then + mkdir $VFS_OUTPUTDIR +fi + +echo 'Building Linux libraries...' +$VFS_SRCDIR/ProjFS.Linux/Scripts/Build.sh $CONFIGURATION || exit 1 + +# Create the directory where we'll do pre build tasks +BUILDDIR=$VFS_OUTPUTDIR/GVFS.Build +if [ ! -d $BUILDDIR ]; then + mkdir $BUILDDIR || exit 1 +fi + +# TODO(Linux): download VFS-enabled version of upstream Git +#echo 'Downloading a VFS-enabled version of Git...' +#$VFS_SCRIPTDIR/DownloadGVFSGit.sh || exit 1 +GITVERSION="$($VFS_SCRIPTDIR/GetGitVersionNumber.sh)" +#GITPATH="$(find $VFS_PACKAGESDIR/gitformac.gvfs.installer/$GITVERSION -type f -name *.dmg)" || exit 1 +#echo "Downloaded Git $GITVERSION" + +# TODO(Linux): git version based on .dmg filename: +# packages/gitformac.gvfs.installer/2.20190724.1/tools/git-2.22.0.vfs.1.1.49.ge8fe4ef9d1-intel-universal-snow-leopard.dmg +# downloaded from https://vfsforgit.myget.org/F/build-dependencies/api/v3/flatcontainer/gitformac.gvfs.installer/2.20190724.1/gitformac.gvfs.installer.2.20190724.1.nupkg +GITPATH="2.22.0.vfs.1.1.49" +# Now that we have a path containing the version number, generate GVFSConstants.GitVersion.cs +$VFS_SCRIPTDIR/GenerateGitVersionConstants.sh "$GITPATH" $BUILDDIR || exit 1 + +# If we're building the Profiling(Release) configuration, remove Profiling() for building .NET code +if [ "$CONFIGURATION" == "Profiling(Release)" ]; then + CONFIGURATION=Release +fi + +echo 'Generating CommonAssemblyVersion.cs...' +$VFS_SCRIPTDIR/GenerateCommonAssemblyVersion.sh || exit 1 + +echo 'Restoring packages...' +dotnet restore $VFS_SRCDIR/GVFS.sln /p:Configuration=$CONFIGURATION.Linux --packages $VFS_PACKAGESDIR /warnasmessage:MSB4011 || exit 1 +dotnet build $VFS_SRCDIR/GVFS.sln --runtime linux-x64 --framework netcoreapp2.1 --configuration $CONFIGURATION.Linux -p:CopyPrjFS=true /maxcpucount:1 /warnasmessage:MSB4011 || exit 1 + +# TODO(Linux): build native hook programs +NATIVEDIR=$VFS_SRCDIR/GVFS/GVFS.Native.Linux +#xcodebuild -configuration $CONFIGURATION -workspace $NATIVEDIR/GVFS.Native.Linux.xcworkspace build -scheme GVFS.Native.Linux -derivedDataPath $VFS_OUTPUTDIR/GVFS.Native.Linux || exit 1 + +if [ ! -d $VFS_PUBLISHDIR ]; then + mkdir $VFS_PUBLISHDIR || exit 1 +fi + +# TODO(Linux): copy native hook programs +#echo 'Copying native binaries to Publish directory...' +#cp $VFS_OUTPUTDIR/GVFS.Native.Linux/Build/Products/$CONFIGURATION/GVFS.ReadObjectHook $VFS_PUBLISHDIR || exit 1 +#cp $VFS_OUTPUTDIR/GVFS.Native.Linux/Build/Products/$CONFIGURATION/GVFS.VirtualFileSystemHook $VFS_PUBLISHDIR || exit 1 +#cp $VFS_OUTPUTDIR/GVFS.Native.Linux/Build/Products/$CONFIGURATION/GVFS.PostIndexChangedHook $VFS_PUBLISHDIR || exit 1 + +# Publish after native build, so installer package can include the native binaries. +dotnet publish $VFS_SRCDIR/GVFS.sln /p:Configuration=$CONFIGURATION.Linux /p:Platform=x64 -p:CopyPrjFS=true --runtime linux-x64 --framework netcoreapp2.1 --self-contained --output $VFS_PUBLISHDIR /maxcpucount:1 /warnasmessage:MSB4011 || exit 1 + +# TODO(Linux): copy installer (if any) +#echo 'Copying Git installer to the output directory...' +#$VFS_SCRIPTDIR/PublishGit.sh $GITPATH || exit 1 + +echo 'Running VFS for Git unit tests...' +$VFS_PUBLISHDIR/GVFS.UnitTests || exit 1 diff --git a/Scripts/Linux/DownloadGVFSGit.sh b/Scripts/Linux/DownloadGVFSGit.sh new file mode 100755 index 0000000000..54a742b906 --- /dev/null +++ b/Scripts/Linux/DownloadGVFSGit.sh @@ -0,0 +1,7 @@ +. "$(dirname ${BASH_SOURCE[0]})/InitializeEnvironment.sh" + +BUILDDIR=$VFS_OUTPUTDIR/GVFS.Build +GITVERSION="$($VFS_SCRIPTDIR/GetGitVersionNumber.sh)" +cp $VFS_SRCDIR/nuget.config $BUILDDIR +dotnet new classlib -n Restore.GitInstaller -o $BUILDDIR --force +dotnet add $BUILDDIR/Restore.GitInstaller.csproj package --package-directory $VFS_PACKAGESDIR GitForMac.GVFS.Installer --version $GITVERSION \ No newline at end of file diff --git a/Scripts/Linux/GVFS_Clone.sh b/Scripts/Linux/GVFS_Clone.sh new file mode 100755 index 0000000000..250d3aa9a1 --- /dev/null +++ b/Scripts/Linux/GVFS_Clone.sh @@ -0,0 +1,11 @@ +#!/bin/bash +. "$(dirname ${BASH_SOURCE[0]})/InitializeEnvironment.sh" + +REPOURL=$1 + +CONFIGURATION=$2 +if [ -z $CONFIGURATION ]; then + CONFIGURATION=Debug +fi + +$VFS_PUBLISHDIR/gvfs clone $REPOURL ~/GVFSTest --local-cache-path ~/GVFSTest/.gvfsCache --no-mount --no-prefetch diff --git a/Scripts/Linux/GVFS_Mount.sh b/Scripts/Linux/GVFS_Mount.sh new file mode 100755 index 0000000000..78e6f946ac --- /dev/null +++ b/Scripts/Linux/GVFS_Mount.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +. "$(dirname ${BASH_SOURCE[0]})/InitializeEnvironment.sh" + +CONFIGURATION=$1 +if [ -z $CONFIGURATION ]; then + CONFIGURATION=Debug +fi + +$VFS_PUBLISHDIR/gvfs mount ~/GVFSTest diff --git a/Scripts/Linux/GVFS_Unmount.sh b/Scripts/Linux/GVFS_Unmount.sh new file mode 100755 index 0000000000..36d7460d2b --- /dev/null +++ b/Scripts/Linux/GVFS_Unmount.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +. "$(dirname ${BASH_SOURCE[0]})/InitializeEnvironment.sh" + +CONFIGURATION=$1 +if [ -z $CONFIGURATION ]; then + CONFIGURATION=Debug +fi + +$VFS_PUBLISHDIR/gvfs unmount ~/GVFSTest diff --git a/Scripts/Linux/GenerateCommonAssemblyVersion.sh b/Scripts/Linux/GenerateCommonAssemblyVersion.sh new file mode 100755 index 0000000000..4e2535e7b6 --- /dev/null +++ b/Scripts/Linux/GenerateCommonAssemblyVersion.sh @@ -0,0 +1,11 @@ +. "$(dirname ${BASH_SOURCE[0]})/InitializeEnvironment.sh" + +GVFSPROPS=$VFS_SRCDIR/GVFS/GVFS.Build/GVFS.props +VERSIONNUMBER="$(cat $GVFSPROPS | grep GVFSVersion | grep -Eo '[0-9.]+(-\w+)?')" + +cat >$VFS_OUTPUTDIR/CommonAssemblyVersion.cs <