Exemple #1
0
        // 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);
        }
Exemple #2
0
        // 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);
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        // 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);
        }
Exemple #5
0
 // 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)
 {
 }
Exemple #7
0
 // Constructor called when reading a TarEntry from a TarReader.
 internal PosixTarEntry(TarHeader header, TarReader readerOfOrigin, TarEntryFormat format)
     : base(header, readerOfOrigin, format)
 {
 }