Пример #1
0
        // Reads the long path found in the data section of a GNU entry of type 'K' or 'L'
        // and replaces Name or LinkName, respectively, with the found string.
        // Throws if end of stream is reached.
        private void ReadGnuLongPathDataBlock(Stream archiveStream)
        {
            Debug.Assert(_typeFlag is TarEntryType.LongLink or TarEntryType.LongPath);

            if (_size == 0)
            {
                return;
            }

            byte[] buffer = new byte[(int)_size];

            if (archiveStream.Read(buffer.AsSpan()) != _size)
            {
                throw new EndOfStreamException();
            }

            string longPath = TarHelpers.GetTrimmedUtf8String(buffer);

            if (_typeFlag == TarEntryType.LongLink)
            {
                _linkName = longPath;
            }
            else if (_typeFlag == TarEntryType.LongPath)
            {
                _name = longPath;
            }
        }
Пример #2
0
        // Reads the ustar prefix attribute.
        // Throws if a conversion to an expected data type fails.
        private void ReadUstarAttributes(Span <byte> buffer)
        {
            _prefix = TarHelpers.GetTrimmedUtf8String(buffer.Slice(FieldLocations.Prefix, FieldLengths.Prefix));

            // In ustar, Prefix is used to store the *leading* path segments of
            // Name, if the full path did not fit in the Name byte array.
            if (!string.IsNullOrEmpty(_prefix))
            {
                // Prefix never has a leading separator, so we add it
                // it should always  be a forward slash for compatibility
                _name = string.Format(UstarPrefixFormat, _prefix, _name);
            }
        }
Пример #3
0
        // Collects the GNU long path info from the buffer and sets it in the right field depending on the type flag.
        private void ReadGnuLongPathDataFromBuffer(ReadOnlySpan <byte> buffer)
        {
            string longPath = TarHelpers.GetTrimmedUtf8String(buffer);

            if (_typeFlag == TarEntryType.LongLink)
            {
                _linkName = longPath;
            }
            else if (_typeFlag == TarEntryType.LongPath)
            {
                _name = longPath;
            }
        }
Пример #4
0
        // Returns a dictionary containing the extended attributes collected from the provided byte buffer.
        private void ReadExtendedAttributesFromBuffer(ReadOnlySpan <byte> buffer, string name)
        {
            string dataAsString = TarHelpers.GetTrimmedUtf8String(buffer);

            using StringReader reader = new(dataAsString);

            while (TryGetNextExtendedAttribute(reader, out string?key, out string?value))
            {
                if (ExtendedAttributes.ContainsKey(key))
                {
                    throw new FormatException(string.Format(SR.TarDuplicateExtendedAttribute, name));
                }
                ExtendedAttributes.Add(key, value);
            }
        }
Пример #5
0
        // Collects the extended attributes found in the data section of a PAX entry of type 'x' or 'g'.
        // Throws if end of stream is reached or if an attribute is malformed.
        private void ReadExtendedAttributesBlock(Stream archiveStream)
        {
            Debug.Assert(_typeFlag is TarEntryType.ExtendedAttributes or TarEntryType.GlobalExtendedAttributes);

            // Regardless of the size, this entry should always have a valid dictionary object
            _extendedAttributes ??= new Dictionary <string, string>();

            if (_size == 0)
            {
                return;
            }

            // It is not expected that the extended attributes data section will be longer than int.MaxValue, considering
            // 4096 is a common max path length, and also the size field is 12 bytes long, which is under int.MaxValue.
            if (_size > int.MaxValue)
            {
                throw new InvalidOperationException(string.Format(SR.TarSizeFieldTooLargeForExtendedAttribute, _typeFlag.ToString()));
            }

            byte[] buffer = new byte[(int)_size];
            if (archiveStream.Read(buffer.AsSpan()) != _size)
            {
                throw new EndOfStreamException();
            }

            string dataAsString = TarHelpers.GetTrimmedUtf8String(buffer);

            using StringReader reader = new(dataAsString);

            while (TryGetNextExtendedAttribute(reader, out string?key, out string?value))
            {
                _extendedAttributes ??= new Dictionary <string, string>();

                if (_extendedAttributes.ContainsKey(key))
                {
                    throw new FormatException(string.Format(SR.TarDuplicateExtendedAttribute, _name));
                }
                _extendedAttributes.Add(key, value);
            }
        }
Пример #6
0
        // Reads the long path found in the data section of a GNU entry of type 'K' or 'L'
        // and replaces Name or LinkName, respectively, with the found string.
        // Throws if end of stream is reached.
        private void ReadGnuLongPathDataBlock(Stream archiveStream)
        {
            Debug.Assert(_typeFlag is TarEntryType.LongLink or TarEntryType.LongPath);

            if (_size == 0)
            {
                return;
            }

            byte[] buffer = new byte[(int)_size];
            archiveStream.ReadExactly(buffer);

            string longPath = TarHelpers.GetTrimmedUtf8String(buffer);

            if (_typeFlag == TarEntryType.LongLink)
            {
                _linkName = longPath;
            }
            else if (_typeFlag == TarEntryType.LongPath)
            {
                _name = longPath;
            }
        }
Пример #7
0
        // Attempts to read the fields shared by all formats and stores them in their expected data type.
        // Throws if any data type conversion fails.
        // Returns true on success, false if checksum is zero.
        private static TarHeader?TryReadCommonAttributes(Span <byte> buffer, TarEntryFormat initialFormat)
        {
            // Start by collecting fields that need special checks that return early when data is wrong

            // Empty checksum means this is an invalid (all blank) entry, finish early
            Span <byte> spanChecksum = buffer.Slice(FieldLocations.Checksum, FieldLengths.Checksum);

            if (TarHelpers.IsAllNullBytes(spanChecksum))
            {
                return(null);
            }
            int checksum = (int)TarHelpers.ParseOctal <uint>(spanChecksum);

            // Zero checksum means the whole header is empty
            if (checksum == 0)
            {
                return(null);
            }

            long size = (int)TarHelpers.ParseOctal <uint>(buffer.Slice(FieldLocations.Size, FieldLengths.Size));

            if (size < 0)
            {
                throw new FormatException(string.Format(SR.TarSizeFieldNegative));
            }

            // Continue with the rest of the fields that require no special checks
            TarHeader header = new(initialFormat,
                                   name : TarHelpers.GetTrimmedUtf8String(buffer.Slice(FieldLocations.Name, FieldLengths.Name)),
                                   mode : (int)TarHelpers.ParseOctal <uint>(buffer.Slice(FieldLocations.Mode, FieldLengths.Mode)),
                                   mTime : TarHelpers.GetDateTimeOffsetFromSecondsSinceEpoch((long)TarHelpers.ParseOctal <ulong>(buffer.Slice(FieldLocations.MTime, FieldLengths.MTime))),
                                   typeFlag : (TarEntryType)buffer[FieldLocations.TypeFlag])
            {
                _checksum = checksum,
                _size     = size,
                _uid      = (int)TarHelpers.ParseOctal <uint>(buffer.Slice(FieldLocations.Uid, FieldLengths.Uid)),
                _gid      = (int)TarHelpers.ParseOctal <uint>(buffer.Slice(FieldLocations.Gid, FieldLengths.Gid)),
                _linkName = TarHelpers.GetTrimmedUtf8String(buffer.Slice(FieldLocations.LinkName, FieldLengths.LinkName))
            };

            if (header._format == TarEntryFormat.Unknown)
            {
                header._format = header._typeFlag switch
                {
                    TarEntryType.ExtendedAttributes or
                    TarEntryType.GlobalExtendedAttributes => TarEntryFormat.Pax,

                                 TarEntryType.DirectoryList or
                                 TarEntryType.LongLink or
                                 TarEntryType.LongPath or
                                 TarEntryType.MultiVolume or
                                 TarEntryType.RenamedOrSymlinked or
                                 TarEntryType.TapeVolume => TarEntryFormat.Gnu,

                    // V7 is the only one that uses 'V7RegularFile'.
                                 TarEntryType.V7RegularFile => TarEntryFormat.V7,

                                 TarEntryType.SparseFile => throw new NotSupportedException(string.Format(SR.TarEntryTypeNotSupported, header._typeFlag)),

                          // We can quickly determine the *minimum* possible format if the entry type
                          // is the POSIX 'RegularFile', although later we could upgrade it to PAX or GNU
                          _ => (header._typeFlag == TarEntryType.RegularFile) ? TarEntryFormat.Ustar : TarEntryFormat.V7
                };
            }

            return(header);
        }
Пример #8
0
        // Attempts to read the fields shared by all formats and stores them in their expected data type.
        // Throws if any data type conversion fails.
        // Returns true on success, false if checksum is zero.
        private bool TryReadCommonAttributes(Span <byte> buffer)
        {
            // Start by collecting fields that need special checks that return early when data is wrong

            // Empty checksum means this is an invalid (all blank) entry, finish early
            Span <byte> spanChecksum = buffer.Slice(FieldLocations.Checksum, FieldLengths.Checksum);

            if (TarHelpers.IsAllNullBytes(spanChecksum))
            {
                return(false);
            }
            _checksum = TarHelpers.GetTenBaseNumberFromOctalAsciiChars(spanChecksum);
            // Zero checksum means the whole header is empty
            if (_checksum == 0)
            {
                return(false);
            }

            _size = TarHelpers.GetTenBaseNumberFromOctalAsciiChars(buffer.Slice(FieldLocations.Size, FieldLengths.Size));
            if (_size < 0)
            {
                throw new FormatException(string.Format(SR.TarSizeFieldNegative, _name));
            }

            // Continue with the rest of the fields that require no special checks

            _name = TarHelpers.GetTrimmedUtf8String(buffer.Slice(FieldLocations.Name, FieldLengths.Name));
            _mode = TarHelpers.GetTenBaseNumberFromOctalAsciiChars(buffer.Slice(FieldLocations.Mode, FieldLengths.Mode));
            _uid  = TarHelpers.GetTenBaseNumberFromOctalAsciiChars(buffer.Slice(FieldLocations.Uid, FieldLengths.Uid));
            _gid  = TarHelpers.GetTenBaseNumberFromOctalAsciiChars(buffer.Slice(FieldLocations.Gid, FieldLengths.Gid));
            int mTime = TarHelpers.GetTenBaseNumberFromOctalAsciiChars(buffer.Slice(FieldLocations.MTime, FieldLengths.MTime));

            _mTime    = TarHelpers.GetDateTimeFromSecondsSinceEpoch(mTime);
            _typeFlag = (TarEntryType)buffer[FieldLocations.TypeFlag];
            _linkName = TarHelpers.GetTrimmedUtf8String(buffer.Slice(FieldLocations.LinkName, FieldLengths.LinkName));

            if (_format == TarFormat.Unknown)
            {
                _format = _typeFlag switch
                {
                    TarEntryType.ExtendedAttributes or
                    TarEntryType.GlobalExtendedAttributes => TarFormat.Pax,

                                 TarEntryType.DirectoryList or
                                 TarEntryType.LongLink or
                                 TarEntryType.LongPath or
                                 TarEntryType.MultiVolume or
                                 TarEntryType.RenamedOrSymlinked or
                                 TarEntryType.SparseFile or
                                 TarEntryType.TapeVolume => TarFormat.Gnu,

                    // V7 is the only one that uses 'V7RegularFile'.
                                 TarEntryType.V7RegularFile => TarFormat.V7,

                    // We can quickly determine the *minimum* possible format if the entry type
                    // is the POSIX 'RegularFile', although later we could upgrade it to PAX or GNU
                                 _ => (_typeFlag == TarEntryType.RegularFile) ? TarFormat.Ustar : TarFormat.V7
                };
            }

            return(true);
        }