/// <summary>
        ///     Adds the specified addition for this bag.
        /// </summary>
        /// <param name="addition">The addition to add to this bag.</param>
        /// <returns>true if an addition was added from the specified stream; false otherwise.</returns>
        public bool Add(TarEntryStream addition)
        {
            var tmp = 0;

            switch (addition.Header.Flag)
            {
            case TarHeaderFlag.LongName:
                Name = addition.ReadString((int)(addition.Header.Size ?? 0), ref tmp);
                break;

            case TarHeaderFlag.LongLink:
                LinkName = addition.ReadString((int)(addition.Header.Size ?? 0), ref tmp);
                break;

            default:
                return(false);
            }

            _flags.Add(addition.Header.Flag);
            return(true);
        }
Example #2
0
        /// <summary>
        /// Untars the specified input stream and returns a stream for each file entry.
        /// </summary>
        /// <param name="inputStream">The input stream.</param>
        /// <returns>An enumeration of file entries. The inputstream can be read on each entry with a length of <see cref="TarEntryStream.Length"/></returns>
        /// <exception cref="InvalidDataException">If an invalid entry was found</exception>
        public static IEnumerable <TarEntryStream> Untar(this Stream inputStream)
        {
            var header = new byte[512];

            long position = 0;

            while (true)
            {
                int zeroBlockCount = 0;
                while (true)
                {
                    // Read the 512 byte block header
                    int length = inputStream.Read(header, 0, header.Length);
                    if (length < 512)
                    {
                        throw new InvalidDataException($"Invalid header block size < 512");
                    }
                    position += length;

                    // Check if the block is full of zero
                    bool isZero = true;
                    for (int i = 0; i < header.Length; i++)
                    {
                        if (header[i] != 0)
                        {
                            isZero = false;
                            break;
                        }
                    }

                    if (isZero)
                    {
                        // If it is full of zero two consecutive times, we have to exit
                        zeroBlockCount++;
                        if (zeroBlockCount == 1)
                        {
                            yield break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                // Read file name
                var fileName = GetString(header, 0, 100);

                // read checksum
                var checksum = ReadOctal(header, 148, 8);
                if (!checksum.HasValue)
                {
                    throw new InvalidDataException($"Invalid checksum for file entry [{fileName}] ");
                }

                // verify checksum
                uint checksumVerif = 0;
                for (int i = 0; i < header.Length; i++)
                {
                    var c = header[i];
                    if (i >= 148 && i < (148 + 8))
                    {
                        c = 32;
                    }
                    checksumVerif += c;
                }

                // Checksum is invalid, exit
                if (checksum.Value != checksumVerif)
                {
                    throw new InvalidDataException($"Invalid checksum verification for file entry [{fileName}] ");
                }

                // Read file size
                var fileSizeRead = ReadOctal(header, 124, 12);
                if (!fileSizeRead.HasValue)
                {
                    throw new InvalidDataException($"Invalid filesize for file entry [{fileName}] ");
                }

                var fileLength = fileSizeRead.Value;


                // Read the type of the file entry
                var type = header[156];
                // We support only the File type
                TarEntryStream tarEntryStream = null;
                if (type == '0')
                {
                    // Read timestamp
                    var unixTimeStamp = ReadOctal(header, 136, 12);
                    if (!unixTimeStamp.HasValue)
                    {
                        throw new InvalidDataException($"Invalid timestamp for file entry [{fileName}] ");
                    }
                    var lastModifiedTime = Epoch.AddSeconds(unixTimeStamp.Value).ToLocalTime();

                    // Double check magic ustar to load prefix filename
                    var ustar = GetString(header, 257, 8);
                    // Check for ustar only
                    if (ustar != null && ustar.Trim() == "ustar")
                    {
                        var prefixFileName = GetString(header, 345, 155);
                        fileName = prefixFileName + fileName;
                    }

                    tarEntryStream = new TarEntryStream(inputStream, position, fileLength)
                    {
                        FileName         = fileName,
                        LastModifiedTime = lastModifiedTime
                    };

                    // Wrap the region into a slice of the original stream
                    yield return(tarEntryStream);
                }

                // The end of the file entry is aligned on 512 bytes
                var untilPosition = (position + fileLength + 511) & ~511;
                if (tarEntryStream != null)
                {
                    position += tarEntryStream.Position;
                }

                // We seek to untilPosition by reading the remaining bytes
                // as we don't want to rely on stream.Seek/Position as it is
                // not working with GzipStream for example
                int delta;
                while ((delta = (int)(untilPosition - position)) > 0)
                {
                    delta = Math.Min(512, delta);
                    var readCount = inputStream.Read(header, 0, delta);
                    position += readCount;
                    if (readCount == 0)
                    {
                        break;
                    }
                }

                // If we are not at target position, there is an error, so exit
                if ((untilPosition - position) != 0)
                {
                    throw new InvalidDataException($"Invalid end of entry after file entry [{fileName}] ");
                }
            }
        }