From 317d1c2ffe68246b3f4386b42084ef03ce354cba Mon Sep 17 00:00:00 2001 From: Tyrie Vella Date: Thu, 12 Jun 2025 16:34:16 -0700 Subject: [PATCH] Always index locally for prefetch packs The GVFS protocol includes an index file along with pack file in the prefetch workflow (primarily used on a new clone to fetch all commits and trees). Currently, GVFS blindly accepts that index file. This pull request changes GVFS prefetch to discard the index sent by the server and always create an index locally. This provides improved security and verification of the pack file, at the expense of performance for the first clone of a repository on a new drive. --- GVFS/GVFS.Common/Git/GitObjects.cs | 79 +++++++++++++----------------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/GVFS/GVFS.Common/Git/GitObjects.cs b/GVFS/GVFS.Common/Git/GitObjects.cs index c2fe88b805..b0b30faad9 100644 --- a/GVFS/GVFS.Common/Git/GitObjects.cs +++ b/GVFS/GVFS.Common/Git/GitObjects.cs @@ -706,64 +706,53 @@ private LooseObjectToWrite GetLooseObjectDestination(string sha) bytesDownloaded += packLength; - // We will try to build an index if the server does not send one - if (pack.IndexStream == null) + // We can't trust the index file from the server, so we will always build our own. + // We still need to consume and handle any exceptions from the index stream though. + var canContinue = true; + GitProcess.Result result; + if (this.TryBuildIndex(activity, packTempPath, out result, gitProcess)) { - GitProcess.Result result; - if (!this.TryBuildIndex(activity, packTempPath, out result, gitProcess)) + tempPacks.Add(new TempPrefetchPackAndIdx(pack.Timestamp, packName, packTempPath, packFlushTask, idxName, idxTempPath, idxFlushTask: null)); + if (pack.IndexStream != null) { - if (packFlushTask != null) + try { - packFlushTask.Wait(); + bytesDownloaded += pack.IndexStream.Length; + if (pack.IndexStream.CanSeek) + { + pack.IndexStream.Seek(0, SeekOrigin.End); + } + else + { + pack.IndexStream.CopyTo(Stream.Null); + } + } + catch (Exception e) + { + canContinue = false; + EventMetadata metadata = CreateEventMetadata(e); + activity.RelatedWarning(metadata, "Failed to read to end of index stream"); } - - // Move whatever has been successfully downloaded so far - Exception moveException; - this.TryFlushAndMoveTempPacks(tempPacks, ref latestTimestamp, out moveException); - - return new RetryWrapper.CallbackResult(null, true); } - - tempPacks.Add(new TempPrefetchPackAndIdx(pack.Timestamp, packName, packTempPath, packFlushTask, idxName, idxTempPath, idxFlushTask: null)); } else { - Task indexFlushTask; - if (this.TryWriteTempFile(activity, pack.IndexStream, idxTempPath, out indexLength, out indexFlushTask)) + canContinue = false; + } + + if (!canContinue) + { + if (packFlushTask != null) { - tempPacks.Add(new TempPrefetchPackAndIdx(pack.Timestamp, packName, packTempPath, packFlushTask, idxName, idxTempPath, indexFlushTask)); + packFlushTask.Wait(); } - else - { - bytesDownloaded += indexLength; - - // Try to build the index manually, then retry the prefetch - GitProcess.Result result; - if (this.TryBuildIndex(activity, packTempPath, out result, gitProcess)) - { - // If we were able to recreate the failed index - // we can start the prefetch at the next timestamp - tempPacks.Add(new TempPrefetchPackAndIdx(pack.Timestamp, packName, packTempPath, packFlushTask, idxName, idxTempPath, idxFlushTask: null)); - } - else - { - if (packFlushTask != null) - { - packFlushTask.Wait(); - } - } - // Move whatever has been successfully downloaded so far - Exception moveException; - this.TryFlushAndMoveTempPacks(tempPacks, ref latestTimestamp, out moveException); + // Move whatever has been successfully downloaded so far + Exception moveException; + this.TryFlushAndMoveTempPacks(tempPacks, ref latestTimestamp, out moveException); - // The download stream will not be in a good state if the index download fails. - // So we have to restart the prefetch - return new RetryWrapper.CallbackResult(null, true); - } + return new RetryWrapper.CallbackResult(null, true); } - - bytesDownloaded += indexLength; } Exception exception = null;