// Asynchronously tries to read the contents of the PAX metadata entry as extended attributes, tries to also read the actual entry that follows, // and returns the actual entry with the processed extended attributes saved in the _extendedAttributes dictionary. private async ValueTask <TarHeader?> TryProcessExtendedAttributesHeaderAsync(TarHeader extendedAttributesHeader, bool copyData, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Now get the actual entry TarHeader?actualHeader = await TarHeader.TryGetNextHeaderAsync(_archiveStream, copyData, TarEntryFormat.Pax, cancellationToken).ConfigureAwait(false); if (actualHeader == null) { return(null); } // We're currently processing an extended attributes header, so we can never have two extended entries in a row if (actualHeader._typeFlag is TarEntryType.GlobalExtendedAttributes or TarEntryType.ExtendedAttributes or TarEntryType.LongLink or TarEntryType.LongPath) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, actualHeader._typeFlag, TarEntryType.ExtendedAttributes)); } // Can't have two extended attribute metadata entries in a row if (actualHeader._typeFlag is TarEntryType.ExtendedAttributes) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, TarEntryType.ExtendedAttributes, TarEntryType.ExtendedAttributes)); } // Replace all the attributes representing standard fields with the extended ones, if any actualHeader.ReplaceNormalAttributesWithExtended(extendedAttributesHeader.ExtendedAttributes); return(actualHeader); }
// Asynchronously 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 async ValueTask <(bool, TarHeader)> TryGetNextEntryHeaderAsync(bool copyData, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Debug.Assert(!_reachedEndMarkers); (bool result, TarHeader header) = await TarHeader.TryGetNextHeaderAsync(_archiveStream, copyData, TarEntryFormat.Unknown, cancellationToken).ConfigureAwait(false); if (!result) { return(false, default);
// Asynchronously 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 async ValueTask <TarHeader?> TryGetNextEntryHeaderAsync(bool copyData, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Debug.Assert(!_reachedEndMarkers); TarHeader?header = await TarHeader.TryGetNextHeaderAsync(_archiveStream, copyData, TarEntryFormat.Unknown, cancellationToken).ConfigureAwait(false); if (header == null) { return(null); } // If a metadata typeflag entry is retrieved, handle it here, then read the next entry // PAX metadata if (header._typeFlag is TarEntryType.ExtendedAttributes) { TarHeader?mainHeader = await TryProcessExtendedAttributesHeaderAsync(header, copyData, cancellationToken).ConfigureAwait(false); if (mainHeader == null) { return(null); } header = mainHeader; } // GNU metadata else if (header._typeFlag is TarEntryType.LongLink or TarEntryType.LongPath) { TarHeader?mainHeader = await TryProcessGnuMetadataHeaderAsync(header, copyData, cancellationToken).ConfigureAwait(false); if (mainHeader == null) { return(null); } header = mainHeader; } return(header); }
// Asynchronously tries to read the contents of the PAX metadata entry as extended attributes, tries to also read the actual entry that follows, // and returns the actual entry with the processed extended attributes saved in the _extendedAttributes dictionary. private async ValueTask <TarHeader?> TryProcessExtendedAttributesHeaderAsync(TarHeader extendedAttributesHeader, bool copyData, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Don't process the data block of the actual entry just yet, because there's a slim chance // that the extended attributes contain a size that we need to override in the header TarHeader?actualHeader = await TarHeader.TryGetNextHeaderAsync(_archiveStream, copyData, TarEntryFormat.Pax, processDataBlock : false, cancellationToken).ConfigureAwait(false); if (actualHeader == null) { return(null); } // We're currently processing an extended attributes header, so we can never have two extended entries in a row if (actualHeader._typeFlag is TarEntryType.GlobalExtendedAttributes or TarEntryType.ExtendedAttributes or TarEntryType.LongLink or TarEntryType.LongPath) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, actualHeader._typeFlag, TarEntryType.ExtendedAttributes)); } // Can't have two extended attribute metadata entries in a row if (actualHeader._typeFlag is TarEntryType.ExtendedAttributes) { throw new FormatException(string.Format(SR.TarUnexpectedMetadataEntry, TarEntryType.ExtendedAttributes, TarEntryType.ExtendedAttributes)); } // Replace all the attributes representing standard fields with the extended ones, if any actualHeader.ReplaceNormalAttributesWithExtended(extendedAttributesHeader.ExtendedAttributes); // We retrieved the extended attributes, now we can read the data, and always with the right size actualHeader.ProcessDataBlock(_archiveStream, copyData); return(actualHeader); }
// 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, 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, 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); }