Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions GVFS/GVFS.Common/DiskLayoutUpgrades/DiskLayoutUpgrade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ public abstract class DiskLayoutUpgrade
protected abstract int SourceMinorVersion { get; }
protected abstract bool IsMajorUpgrade { get; }

public static bool TryRunAllUpgrades(string enlistmentRoot)
public static bool TryRunAllUpgrades(string enlistmentRoot, out string error, out ReturnCode returnCode)
{
majorVersionUpgrades = new Dictionary<int, MajorUpgrade>();
minorVersionUpgrades = new Dictionary<int, Dictionary<int, MinorUpgrade>>();
error = null;
returnCode = ReturnCode.Success;

foreach (DiskLayoutUpgrade upgrade in GVFSPlatform.Instance.DiskLayoutUpgrade.Upgrades)
{
Expand All @@ -31,7 +33,7 @@ public static bool TryRunAllUpgrades(string enlistmentRoot)
try
{
DiskLayoutUpgrade upgrade = null;
while (TryFindUpgrade(tracer, enlistmentRoot, out upgrade))
while (TryFindUpgrade(tracer, enlistmentRoot, out upgrade, out error, out returnCode))
{
if (upgrade == null)
{
Expand All @@ -40,11 +42,13 @@ public static bool TryRunAllUpgrades(string enlistmentRoot)

if (!upgrade.TryUpgrade(tracer, enlistmentRoot))
{
returnCode = ReturnCode.GenericError;
return false;
}

if (!CheckLayoutVersionWasIncremented(tracer, enlistmentRoot, upgrade))
{
returnCode = ReturnCode.GenericError;
return false;
}
}
Expand All @@ -55,6 +59,8 @@ public static bool TryRunAllUpgrades(string enlistmentRoot)
{
StartLogFile(enlistmentRoot, tracer);
tracer.RelatedError(e.ToString());
error = e.Message;
returnCode = ReturnCode.GenericError;
return false;
}
finally
Expand All @@ -64,17 +70,19 @@ public static bool TryRunAllUpgrades(string enlistmentRoot)
}
}

public static bool TryCheckDiskLayoutVersion(ITracer tracer, string enlistmentRoot, out string error)
public static bool TryCheckDiskLayoutVersion(ITracer tracer, string enlistmentRoot, out string error, out ReturnCode returnCode)
{
error = string.Empty;
returnCode = ReturnCode.Success;
int majorVersion;
int minorVersion;
try
{
if (TryGetDiskLayoutVersion(tracer, enlistmentRoot, out majorVersion, out minorVersion, out error))
if (TryGetDiskLayoutVersion(tracer, enlistmentRoot, out majorVersion, out minorVersion, out error, out returnCode))
{
if (majorVersion < GVFSPlatform.Instance.DiskLayoutUpgrade.Version.MinimumSupportedMajorVersion)
{
returnCode = ReturnCode.GenericError;
error = string.Format(
"Breaking change to GVFS disk layout has been made since cloning. \r\nEnlistment disk layout version: {0} \r\nGVFS disk layout version: {1} \r\nMinimum supported version: {2}",
majorVersion,
Expand All @@ -85,6 +93,7 @@ public static bool TryCheckDiskLayoutVersion(ITracer tracer, string enlistmentRo
}
else if (majorVersion > GVFSPlatform.Instance.DiskLayoutUpgrade.Version.CurrentMajorVersion)
{
returnCode = ReturnCode.GenericError;
error = string.Format(
"Changes to GVFS disk layout do not allow mounting after downgrade. Try mounting again using a more recent version of GVFS. \r\nEnlistment disk layout version: {0} \r\nGVFS disk layout version: {1}",
majorVersion,
Expand All @@ -94,6 +103,7 @@ public static bool TryCheckDiskLayoutVersion(ITracer tracer, string enlistmentRo
}
else if (majorVersion != GVFSPlatform.Instance.DiskLayoutUpgrade.Version.CurrentMajorVersion)
{
returnCode = ReturnCode.GenericError;
error = string.Format(
"GVFS disk layout version doesn't match current version. Try running 'gvfs mount' to upgrade. \r\nEnlistment disk layout version: {0}.{1} \r\nGVFS disk layout version: {2}.{3}",
majorVersion,
Expand All @@ -112,7 +122,12 @@ public static bool TryCheckDiskLayoutVersion(ITracer tracer, string enlistmentRo
RepoMetadata.Shutdown();
}

error = "Failed to read disk layout version. " + ConsoleHelper.GetGVFSLogMessage(enlistmentRoot);
if (returnCode != ReturnCode.MissingDiskLayoutVersion)
{
returnCode = ReturnCode.GenericError;
error = "Failed to read disk layout version. " + ConsoleHelper.GetGVFSLogMessage(enlistmentRoot);
}

return false;
}

Expand Down Expand Up @@ -256,13 +271,12 @@ private static bool CheckLayoutVersionWasIncremented(JsonTracer tracer, string e
return true;
}

private static bool TryFindUpgrade(JsonTracer tracer, string enlistmentRoot, out DiskLayoutUpgrade upgrade)
private static bool TryFindUpgrade(JsonTracer tracer, string enlistmentRoot, out DiskLayoutUpgrade upgrade, out string error, out ReturnCode returnCode)
{
int majorVersion;
int minorVersion;

string error;
if (!TryGetDiskLayoutVersion(tracer, enlistmentRoot, out majorVersion, out minorVersion, out error))
if (!TryGetDiskLayoutVersion(tracer, enlistmentRoot, out majorVersion, out minorVersion, out error, out returnCode))
{
StartLogFile(enlistmentRoot, tracer);
tracer.RelatedError(error);
Expand Down Expand Up @@ -300,6 +314,8 @@ private static bool TryFindUpgrade(JsonTracer tracer, string enlistmentRoot, out

// return true to indicate that we succeeded, and no upgrader was found
upgrade = null;
error = null;
returnCode = ReturnCode.Success;
return true;
}

Expand All @@ -309,9 +325,22 @@ private static bool TryGetDiskLayoutVersion(
out int majorVersion,
out int minorVersion,
out string error)
{
ReturnCode returnCode;
return TryGetDiskLayoutVersion(tracer, enlistmentRoot, out majorVersion, out minorVersion, out error, out returnCode);
}

private static bool TryGetDiskLayoutVersion(
ITracer tracer,
string enlistmentRoot,
out int majorVersion,
out int minorVersion,
out string error,
out ReturnCode returnCode)
{
majorVersion = 0;
minorVersion = 0;
returnCode = ReturnCode.GenericError;

string dotGVFSPath = Path.Combine(enlistmentRoot, GVFSPlatform.Instance.Constants.DotGVFSRoot);

Expand All @@ -323,13 +352,19 @@ private static bool TryGetDiskLayoutVersion(
return false;
}

if (!RepoMetadata.Instance.TryGetOnDiskLayoutVersion(out majorVersion, out minorVersion, out error))
if (!RepoMetadata.Instance.TryGetOnDiskLayoutVersion(out majorVersion, out minorVersion, out error, out returnCode))
{
if (returnCode == ReturnCode.MissingDiskLayoutVersion)
{
error += " " + ConsoleHelper.GetGVFSLogMessage(enlistmentRoot);
}

return false;
}
}

error = null;
returnCode = ReturnCode.Success;
return true;
}

Expand Down
13 changes: 12 additions & 1 deletion GVFS/GVFS.Common/RepoMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace GVFS.Common
{
public class RepoMetadata
{
public const string MissingDiskLayoutVersionMessage = "Enlistment disk layout version not found. The .gvfs metadata is incomplete. Make sure your work is pushed or backed up, then reclone this enlistment.";

private FileBasedDictionary<string, string> repoMetadata;
private ITracer tracer;

Expand Down Expand Up @@ -90,16 +92,24 @@ public static void Shutdown()
}

public bool TryGetOnDiskLayoutVersion(out int majorVersion, out int minorVersion, out string error)
{
ReturnCode returnCode;
return this.TryGetOnDiskLayoutVersion(out majorVersion, out minorVersion, out error, out returnCode);
}

public bool TryGetOnDiskLayoutVersion(out int majorVersion, out int minorVersion, out string error, out ReturnCode returnCode)
{
majorVersion = 0;
minorVersion = 0;
returnCode = ReturnCode.GenericError;

try
{
string value;
if (!this.repoMetadata.TryGetValue(Keys.DiskLayoutMajorVersion, out value))
{
error = "Enlistment disk layout version not found, check if a breaking change has been made to GVFS since cloning this enlistment.";
error = MissingDiskLayoutVersionMessage;
returnCode = ReturnCode.MissingDiskLayoutVersion;
return false;
}

Expand All @@ -125,6 +135,7 @@ public bool TryGetOnDiskLayoutVersion(out int majorVersion, out int minorVersion
}

error = null;
returnCode = ReturnCode.Success;
return true;
}

Expand Down
1 change: 1 addition & 0 deletions GVFS/GVFS.Common/ReturnCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ public enum ReturnCode
AuthenticationError = 9,
CredentialTimeout = 10,
RemoteGvfsConfigError = 11,
MissingDiskLayoutVersion = 12,
}
}
83 changes: 81 additions & 2 deletions GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/MountTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
public class MountTests : TestsWithEnlistmentPerFixture
{
private const int GVFSGenericError = 3;
// These values are a shipped command-line contract. Do not bind them to product constants.
private const int GVFSMissingDiskLayoutVersion = 12;
private const string MissingDiskLayoutVersionMessage = "The .gvfs metadata is incomplete";
private const string GVFSLogMessage = "Run 'gvfs log";
private const uint GenericRead = 2147483648;
private const uint FileFlagBackupSemantics = 3355443;
private readonly int fileDeletedBackgroundOperationCode;
Expand Down Expand Up @@ -99,6 +103,34 @@ public void MountChangesMountId()
.ShouldNotEqual(mountId, "gvfs.mount-id should change on every mount");
}

[TestCase]
public void MountFailsWithUniqueExitCodeWhenDiskLayoutVersionIsMissing()
{
this.Enlistment.UnmountGVFS();

string majorVersion;
string minorVersion;
GVFSHelpers.GetPersistedDiskLayoutVersion(this.Enlistment.DotGVFSRoot, out majorVersion, out minorVersion);
majorVersion.ShouldNotBeNull();
minorVersion.ShouldNotBeNull();

try
{
GVFSHelpers.DeletePersistedDiskLayoutMajorVersion(this.Enlistment.DotGVFSRoot);

this.MountShouldFail(
Comment thread
tyrielv marked this conversation as resolved.
Resolved
GVFSMissingDiskLayoutVersion,
MissingDiskLayoutVersionMessage,
additionalExpectedErrorMessage: GVFSLogMessage);
}
finally
{
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, majorVersion, minorVersion);
}

this.Enlistment.MountGVFS();
}

[TestCase]
public void MountFailsWhenNoOnDiskVersion()
{
Expand Down Expand Up @@ -126,7 +158,10 @@ public void MountFailsWhenNoOnDiskVersion()
this.fileSystem.MoveFile(versionDatabasePath, tempDatabasePath);
versionDatabasePath.ShouldNotExistOnDisk(this.fileSystem);

this.MountShouldFail("Failed to upgrade repo disk layout");
this.MountShouldFail(
GVFSMissingDiskLayoutVersion,
MissingDiskLayoutVersionMessage,
additionalExpectedErrorMessage: GVFSLogMessage);
}
finally
{
Expand All @@ -141,6 +176,43 @@ public void MountFailsWhenNoOnDiskVersion()
this.Enlistment.MountGVFS();
}

[TestCase]
public void MountReportsUpgradeExceptionWithoutStackTrace()
{
this.Enlistment.UnmountGVFS();

string metadataPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
string originalMetadata = File.ReadAllText(metadataPath);

try
{
File.WriteAllText(metadataPath, "\r\n");

string output = this.MountShouldFail(
GVFSGenericError,
"Failed to upgrade repo disk layout.",
additionalExpectedErrorMessage: GVFSLogMessage);

output.ShouldContain("Index was outside the bounds of the array.");
output.ShouldNotContain(
ignoreCase: false,
"System.IndexOutOfRangeException",
"FileBasedCollectionException",
"TryLoadFromDisk",
"TryRunAllUpgrades");

this.LatestGVFSLogShouldContain("System.IndexOutOfRangeException");
this.LatestGVFSLogShouldContain("TryLoadFromDisk");
this.LatestGVFSLogShouldContain("TryRunAllUpgrades");
}
finally
{
File.WriteAllText(metadataPath, originalMetadata);
}

this.Enlistment.MountGVFS();
}

[TestCase]
public void MountFailsWhenNoLocalCacheRootInRepoMetadata()
{
Expand Down Expand Up @@ -359,7 +431,7 @@ public void ProjFS_CMDHangNoneActiveInstance()
this.Enlistment.MountGVFS();
}

private void MountShouldFail(int expectedExitCode, string expectedErrorMessage, string mountWorkingDirectory = null)
private string MountShouldFail(int expectedExitCode, string expectedErrorMessage, string mountWorkingDirectory = null, string additionalExpectedErrorMessage = null)
{
string enlistmentRoot = this.Enlistment.EnlistmentRoot;

Expand All @@ -379,7 +451,14 @@ private void MountShouldFail(int expectedExitCode, string expectedErrorMessage,
{
string combinedOutput = result.Output + "\n" + result.Errors;
combinedOutput.ShouldContain(expectedErrorMessage);

if (additionalExpectedErrorMessage != null)
{
combinedOutput.ShouldContain(additionalExpectedErrorMessage);
}
}

return result.Output + "\n" + result.Errors;
}

private void MountShouldFail(string expectedErrorMessage, string mountWorkingDirectory = null)
Expand Down
Loading
Loading