// Tries to read the contents of the GNU metadata entry, then tries to read the next entry, which could either be another GNU metadata entry // or the actual entry. Processes them all and returns the actual entry updating its path and/or linkpath fields as needed. private bool TryProcessGnuMetadataHeader(TarHeader header, bool copyData, out TarHeader finalHeader) { finalHeader = new(TarEntryFormat.Gnu); TarHeader?secondHeader = TarHeader.TryGetNextHeader(_archiveStream, copyData, TarEntryFormat.Gnu, processDataBlock: true); // Get the second entry, which is the actual entry if (secondHeader == null) { return(false); } // Can't have two identical metadata entries in a row if (secondHeader._typeFlag == header._typeFlag) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, secondHeader._typeFlag, header._typeFlag)); } // It's possible to have the two different metadata entries in a row if ((header._typeFlag is TarEntryType.LongLink && secondHeader._typeFlag is TarEntryType.LongPath) || (header._typeFlag is TarEntryType.LongPath && secondHeader._typeFlag is TarEntryType.LongLink)) { TarHeader?thirdHeader = TarHeader.TryGetNextHeader(_archiveStream, copyData, TarEntryFormat.Gnu, processDataBlock: true); // Get the third entry, which is the actual entry if (thirdHeader == null) { return(false); } // Can't have three GNU metadata entries in a row if (thirdHeader._typeFlag is TarEntryType.LongLink or TarEntryType.LongPath) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, thirdHeader._typeFlag, secondHeader._typeFlag)); } if (header._typeFlag is TarEntryType.LongLink) { Debug.Assert(header._linkName != null); Debug.Assert(secondHeader._name != null); thirdHeader._linkName = header._linkName; thirdHeader._name = secondHeader._name; } else if (header._typeFlag is TarEntryType.LongPath) { Debug.Assert(header._name != null); Debug.Assert(secondHeader._linkName != null); thirdHeader._name = header._name; thirdHeader._linkName = secondHeader._linkName; } finalHeader = thirdHeader; } // Only one metadata entry was found else { if (header._typeFlag is TarEntryType.LongLink) { Debug.Assert(header._linkName != null); secondHeader._linkName = header._linkName; } else if (header._typeFlag is TarEntryType.LongPath) { Debug.Assert(header._name != null); secondHeader._name = header._name; } finalHeader = secondHeader; } return(true); }
// Asynchronously tries to read the contents of the GNU metadata entry, then tries to read the next entry, which could either be another GNU metadata entry // or the actual entry. Processes them all and returns the actual entry updating its path and/or linkpath fields as needed. private async ValueTask <TarHeader?> TryProcessGnuMetadataHeaderAsync(TarHeader header, bool copyData, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Get the second entry, which is the actual entry TarHeader?secondHeader = await TarHeader.TryGetNextHeaderAsync(_archiveStream, copyData, TarEntryFormat.Gnu, processDataBlock : true, cancellationToken).ConfigureAwait(false); if (secondHeader == null) { return(null); } // Can't have two identical metadata entries in a row if (secondHeader._typeFlag == header._typeFlag) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, secondHeader._typeFlag, header._typeFlag)); } TarHeader finalHeader; // It's possible to have the two different metadata entries in a row if ((header._typeFlag is TarEntryType.LongLink && secondHeader._typeFlag is TarEntryType.LongPath) || (header._typeFlag is TarEntryType.LongPath && secondHeader._typeFlag is TarEntryType.LongLink)) { // Get the third entry, which is the actual entry TarHeader?thirdHeader = await TarHeader.TryGetNextHeaderAsync(_archiveStream, copyData, TarEntryFormat.Gnu, processDataBlock : true, cancellationToken).ConfigureAwait(false); if (thirdHeader == null) { return(null); } // Can't have three GNU metadata entries in a row if (thirdHeader._typeFlag is TarEntryType.LongLink or TarEntryType.LongPath) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, thirdHeader._typeFlag, secondHeader._typeFlag)); } if (header._typeFlag is TarEntryType.LongLink) { Debug.Assert(header._linkName != null); Debug.Assert(secondHeader._name != null); thirdHeader._linkName = header._linkName; thirdHeader._name = secondHeader._name; } else if (header._typeFlag is TarEntryType.LongPath) { Debug.Assert(header._name != null); Debug.Assert(secondHeader._linkName != null); thirdHeader._name = header._name; thirdHeader._linkName = secondHeader._linkName; } finalHeader = thirdHeader; } // Only one metadata entry was found else { if (header._typeFlag is TarEntryType.LongLink) { Debug.Assert(header._linkName != null); secondHeader._linkName = header._linkName; } else if (header._typeFlag is TarEntryType.LongPath) { Debug.Assert(header._name != null); secondHeader._name = header._name; } finalHeader = secondHeader; } return(finalHeader); }
private bool TryProcessExtendedAttributesHeader(TarHeader firstHeader, bool copyData, out TarHeader secondHeader) { secondHeader = default; secondHeader._format = TarFormat.Pax; // Now get the actual entry if (!secondHeader.TryGetNextHeader(_archiveStream, copyData)) { return(false); } // Should never read a GEA entry at this point if (secondHeader._typeFlag == TarEntryType.GlobalExtendedAttributes) { throw new FormatException(SR.TarTooManyGlobalExtendedAttributesEntries); } // Can't have two metadata entries in a row, no matter the archive format if (secondHeader._typeFlag is TarEntryType.ExtendedAttributes) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, TarEntryType.ExtendedAttributes, TarEntryType.ExtendedAttributes)); } Debug.Assert(firstHeader._extendedAttributes != null); if (GlobalExtendedAttributes != null) { // First, replace some of the entry's standard attributes with the global ones secondHeader.ReplaceNormalAttributesWithGlobalExtended(GlobalExtendedAttributes); } // Then replace all the standard attributes with the extended attributes ones, // overwriting the previous global replacements if needed secondHeader.ReplaceNormalAttributesWithExtended(firstHeader._extendedAttributes); return(true); }
// Attempts to read the next tar archive entry header. // Returns true if an entry header was collected successfully, false otherwise. // An entry header represents any typeflag that is contains metadata. // Metadata typeflags: ExtendedAttributes, GlobalExtendedAttributes, LongLink, LongPath. // Metadata typeflag entries get handled internally by this method until a valid header entry can be returned. private bool TryGetNextEntryHeader(out TarHeader header, bool copyData) { Debug.Assert(!_reachedEndMarkers); header = default; // Set the initial format that is expected to be retrieved when calling TarHeader.TryReadAttributes. // If the archive format is set to unknown here, it means this is the first entry we read and the value will be changed as fields get discovered. // If the archive format is initially detected as pax, then any subsequent entries detected as ustar will be assumed to be pax. header._format = Format; if (!header.TryGetNextHeader(_archiveStream, copyData)) { return(false); } // Special case: First header. Collect GEA from data section, then get next entry. if (header._typeFlag is TarEntryType.GlobalExtendedAttributes) { if (GlobalExtendedAttributes != null) { // We can only have one extended attributes entry. throw new FormatException(SR.TarTooManyGlobalExtendedAttributesEntries); } GlobalExtendedAttributes = header._extendedAttributes?.AsReadOnly(); header = default; header._format = TarFormat.Pax; try { if (!header.TryGetNextHeader(_archiveStream, copyData)) { return(false); } } catch (EndOfStreamException) { // Edge case: The only entry in the archive was a Global Extended Attributes entry Format = TarFormat.Pax; return(false); } if (header._typeFlag == TarEntryType.GlobalExtendedAttributes) { throw new FormatException(SR.TarTooManyGlobalExtendedAttributesEntries); } } // If a metadata typeflag entry is retrieved, handle it here, then read the next entry // PAX metadata if (header._typeFlag is TarEntryType.ExtendedAttributes) { if (!TryProcessExtendedAttributesHeader(header, copyData, out TarHeader mainHeader)) { return(false); } header = mainHeader; } // GNU metadata else if (header._typeFlag is TarEntryType.LongLink or TarEntryType.LongPath) { if (!TryProcessGnuMetadataHeader(header, copyData, out TarHeader mainHeader)) { return(false); } header = mainHeader; } // Common fields should always acquire a value Debug.Assert(header._name != null); Debug.Assert(header._linkName != null); // Initialize non-common string fields if necessary header._magic ??= string.Empty; header._version ??= string.Empty; header._gName ??= string.Empty; header._uName ??= string.Empty; header._prefix ??= string.Empty; return(true); }
// Constructor used when reading an existing archive. internal PaxTarEntry(TarHeader header, TarReader readerOfOrigin) : base(header, readerOfOrigin) { _header._extendedAttributes ??= new Dictionary <string, string>(); _readOnlyExtendedAttributes = null; }
// Constructor used when reading an existing archive. internal PaxGlobalExtendedAttributesTarEntry(TarHeader header, TarReader readerOfOrigin) : base(header, readerOfOrigin, TarEntryFormat.Pax) { }
// Constructor called when reading a TarEntry from a TarReader. internal PosixTarEntry(TarHeader header, TarReader readerOfOrigin, TarEntryFormat format) : base(header, readerOfOrigin, format) { }