in GVFS/GVFS.Virtualization/Projection/GitIndexProjection.GitIndexParser.cs [213:341]
private FileSystemTaskResult ParseIndex(
ITracer tracer,
Stream indexStream,
GitIndexEntry resuableParsedIndexEntry,
Func<GitIndexEntry, FileSystemTaskResult> entryAction)
{
this.indexStream = indexStream;
this.indexStream.Position = 0;
this.ReadNextPage();
if (this.page[0] != 'D' ||
this.page[1] != 'I' ||
this.page[2] != 'R' ||
this.page[3] != 'C')
{
throw new InvalidDataException("Incorrect magic signature for index: " + string.Join(string.Empty, this.page.Take(4).Select(c => (char)c)));
}
this.Skip(4);
uint indexVersion = this.ReadFromIndexHeader();
if (indexVersion != 4)
{
throw new InvalidDataException("Unsupported index version: " + indexVersion);
}
uint entryCount = this.ReadFromIndexHeader();
// Don't want to flood the logs on large indexes so only log every 500ms
const int LoggingTicksThreshold = 5000000;
long nextLogTicks = DateTime.UtcNow.Ticks + LoggingTicksThreshold;
SortedFolderEntries.InitializePools(tracer, entryCount);
LazyUTF8String.InitializePools(tracer, entryCount);
resuableParsedIndexEntry.ClearLastParent();
int previousPathLength = 0;
bool parseMode = GVFSPlatform.Instance.FileSystem.SupportsFileMode;
FileSystemTaskResult result = FileSystemTaskResult.Success;
for (int i = 0; i < entryCount; i++)
{
if (parseMode)
{
this.Skip(26);
// 4-bit object type
// valid values in binary are 1000(regular file), 1010(symbolic link) and 1110(gitlink)
// 3-bit unused
// 9-bit unix permission. Only 0755 and 0644 are valid for regular files. (Legacy repos can also contain 664)
// Symbolic links and gitlinks have value 0 in this field.
ushort indexFormatTypeAndMode = this.ReadUInt16();
FileTypeAndMode typeAndMode = new FileTypeAndMode(indexFormatTypeAndMode);
switch (typeAndMode.Type)
{
case FileType.Regular:
if (typeAndMode.Mode != FileMode755 &&
typeAndMode.Mode != FileMode644 &&
typeAndMode.Mode != FileMode664)
{
throw new InvalidDataException($"Invalid file mode {typeAndMode.GetModeAsOctalString()} found for regular file in index");
}
break;
case FileType.SymLink:
case FileType.GitLink:
if (typeAndMode.Mode != 0)
{
throw new InvalidDataException($"Invalid file mode {typeAndMode.GetModeAsOctalString()} found for link file({typeAndMode.Type:X}) in index");
}
break;
default:
throw new InvalidDataException($"Invalid file type {typeAndMode.Type:X} found in index");
}
resuableParsedIndexEntry.TypeAndMode = typeAndMode;
this.Skip(12);
}
else
{
this.Skip(40);
}
this.ReadSha(resuableParsedIndexEntry);
ushort flags = this.ReadUInt16();
if (flags == 0)
{
throw new InvalidDataException("Invalid flags found in index");
}
resuableParsedIndexEntry.MergeState = (MergeStage)((flags >> 12) & 3);
bool isExtended = (flags & ExtendedBit) == ExtendedBit;
resuableParsedIndexEntry.PathLength = (ushort)(flags & 0xFFF);
resuableParsedIndexEntry.SkipWorktree = false;
if (isExtended)
{
ushort extendedFlags = this.ReadUInt16();
resuableParsedIndexEntry.SkipWorktree = (extendedFlags & SkipWorktreeBit) == SkipWorktreeBit;
}
int replaceLength = this.ReadReplaceLength();
resuableParsedIndexEntry.ReplaceIndex = previousPathLength - replaceLength;
int bytesToRead = resuableParsedIndexEntry.PathLength - resuableParsedIndexEntry.ReplaceIndex + 1;
this.ReadPath(resuableParsedIndexEntry, resuableParsedIndexEntry.ReplaceIndex, bytesToRead);
previousPathLength = resuableParsedIndexEntry.PathLength;
result = entryAction.Invoke(resuableParsedIndexEntry);
if (result != FileSystemTaskResult.Success)
{
return result;
}
if (DateTime.UtcNow.Ticks > nextLogTicks)
{
tracer.RelatedInfo($"{i}/{entryCount} index entries parsed.");
nextLogTicks = DateTime.UtcNow.Ticks + LoggingTicksThreshold;
}
}
tracer.RelatedInfo($"Finished parsing {entryCount} index entries.");
return result;
}