示例#1
0
        // Asynchronously moves the underlying archive stream position pointer to the beginning of the next header.
        internal async ValueTask AdvanceDataStreamIfNeededAsync(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (_previouslyReadEntry == null)
            {
                return;
            }

            if (_archiveStream.CanSeek)
            {
                Debug.Assert(_previouslyReadEntry._header._endOfHeaderAndDataAndBlockAlignment > 0);
                _archiveStream.Position = _previouslyReadEntry._header._endOfHeaderAndDataAndBlockAlignment;
            }
            else if (_previouslyReadEntry._header._size > 0)
            {
                // When working with seekable streams, every time we return an entry, we avoid advancing the pointer beyond the data section
                // This is so the user can read the data if desired. But if the data was not read by the user, we need to advance the pointer
                // here until it's located at the beginning of the next entry header.
                // This should only be done if the previous entry came from a TarReader and it still had its original SubReadStream or SeekableSubReadStream.

                if (_previouslyReadEntry._header._dataStream is not SubReadStream dataStream)
                {
                    return;
                }

                if (!dataStream.HasReachedEnd)
                {
                    // If the user did not advance the position, we need to make sure the position
                    // pointer is located at the beginning of the next header.
                    if (dataStream.Position < (_previouslyReadEntry._header._size - 1))
                    {
                        long bytesToSkip = _previouslyReadEntry._header._size - dataStream.Position;
                        await TarHelpers.AdvanceStreamAsync(_archiveStream, bytesToSkip, cancellationToken).ConfigureAwait(false);

                        await TarHelpers.SkipBlockAlignmentPaddingAsync(_archiveStream, _previouslyReadEntry._header._size, cancellationToken).ConfigureAwait(false);

                        dataStream.HasReachedEnd = true; // Now the pointer is beyond the limit, so any read attempts should throw
                    }
                }
            }
        }
示例#2
0
        private async Task ProcessDataBlockAsync(Stream archiveStream, bool copyData, CancellationToken cancellationToken)
        {
            bool skipBlockAlignmentPadding = true;

            switch (_typeFlag)
            {
            case TarEntryType.ExtendedAttributes or TarEntryType.GlobalExtendedAttributes:
                await ReadExtendedAttributesBlockAsync(archiveStream, cancellationToken).ConfigureAwait(false);

                break;

            case TarEntryType.LongLink or TarEntryType.LongPath:
                await ReadGnuLongPathDataBlockAsync(archiveStream, cancellationToken).ConfigureAwait(false);

                break;

            case TarEntryType.BlockDevice:
            case TarEntryType.CharacterDevice:
            case TarEntryType.Directory:
            case TarEntryType.Fifo:
            case TarEntryType.HardLink:
            case TarEntryType.SymbolicLink:
                // No data section
                if (_size > 0)
                {
                    throw new FormatException(string.Format(SR.TarSizeFieldTooLargeForEntryType, _typeFlag));
                }
                break;

            case TarEntryType.RegularFile:
            case TarEntryType.V7RegularFile:      // Treated as regular file
            case TarEntryType.ContiguousFile:     // Treated as regular file
            case TarEntryType.DirectoryList:      // Contains the list of filesystem entries in the data section
            case TarEntryType.MultiVolume:        // Contains portion of a file
            case TarEntryType.RenamedOrSymlinked: // Might contain data
            case TarEntryType.SparseFile:         // Contains portion of a file
            case TarEntryType.TapeVolume:         // Might contain data
            default:                              // Unrecognized entry types could potentially have a data section
                _dataStream = await GetDataStreamAsync(archiveStream, copyData, _size, cancellationToken).ConfigureAwait(false);

                if (_dataStream is SeekableSubReadStream)
                {
                    await TarHelpers.AdvanceStreamAsync(archiveStream, _size, cancellationToken).ConfigureAwait(false);
                }
                else if (_dataStream is SubReadStream)
                {
                    // This stream gives the user the chance to optionally read the data section
                    // when the underlying archive stream is unseekable
                    skipBlockAlignmentPadding = false;
                }

                break;
            }

            if (skipBlockAlignmentPadding)
            {
                if (_size > 0)
                {
                    await TarHelpers.SkipBlockAlignmentPaddingAsync(archiveStream, _size, cancellationToken).ConfigureAwait(false);
                }

                if (archiveStream.CanSeek)
                {
                    _endOfHeaderAndDataAndBlockAlignment = archiveStream.Position;
                }
            }
        }