123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- // TarBuffer.cs
- // Copyright (C) 2001 Mike Krueger
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either version 2
- // of the License, or (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- //
- // Linking this library statically or dynamically with other modules is
- // making a combined work based on this library. Thus, the terms and
- // conditions of the GNU General Public License cover the whole
- // combination.
- //
- // As a special exception, the copyright holders of this library give you
- // permission to link this library with independent modules to produce an
- // executable, regardless of the license terms of these independent
- // modules, and to copy and distribute the resulting executable under
- // terms of your choice, provided that you also meet, for each linked
- // independent module, the terms and conditions of the license of that
- // module. An independent module is a module which is not derived from
- // or based on this library. If you modify this library, you may extend
- // this exception to your version of the library, but you are not
- // obligated to do so. If you do not wish to do so, delete this
- // exception statement from your version.
- using System;
- using System.IO;
- namespace CommonMPQ.SharpZipLib.Tar
- {
-
- /// <summary>
- /// The TarBuffer class implements the tar archive concept
- /// of a buffered input stream. This concept goes back to the
- /// days of blocked tape drives and special io devices. In the
- /// C# universe, the only real function that this class
- /// performs is to ensure that files have the correct "record"
- /// size, or other tars will complain.
- /// <p>
- /// You should never have a need to access this class directly.
- /// TarBuffers are created by Tar IO Streams.
- /// </p>
- /// </summary>
- public class TarBuffer
- {
- /* A quote from GNU tar man file on blocking and records
- A `tar' archive file contains a series of blocks. Each block
- contains `BLOCKSIZE' bytes. Although this format may be thought of as
- being on magnetic tape, other media are often used.
- Each file archived is represented by a header block which describes
- the file, followed by zero or more blocks which give the contents of
- the file. At the end of the archive file there may be a block filled
- with binary zeros as an end-of-file marker. A reasonable system should
- write a block of zeros at the end, but must not assume that such a
- block exists when reading an archive.
- The blocks may be "blocked" for physical I/O operations. Each
- record of N blocks is written with a single 'write ()'
- operation. On magnetic tapes, the result of such a write is a single
- record. When writing an archive, the last record of blocks should be
- written at the full size, with blocks after the zero block containing
- all zeros. When reading an archive, a reasonable system should
- properly handle an archive whose last record is shorter than the rest,
- or which contains garbage records after a zero block.
- */
- #region Constants
- /// <summary>
- /// The size of a block in a tar archive in bytes.
- /// </summary>
- /// <remarks>This is 512 bytes.</remarks>
- public const int BlockSize = 512;
-
- /// <summary>
- /// The number of blocks in a default record.
- /// </summary>
- /// <remarks>
- /// The default value is 20 blocks per record.
- /// </remarks>
- public const int DefaultBlockFactor = 20;
-
- /// <summary>
- /// The size in bytes of a default record.
- /// </summary>
- /// <remarks>
- /// The default size is 10KB.
- /// </remarks>
- public const int DefaultRecordSize = BlockSize * DefaultBlockFactor;
- #endregion
- /// <summary>
- /// Get the record size for this buffer
- /// </summary>
- /// <value>The record size in bytes.
- /// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></value>
- public int RecordSize
- {
- get {
- return recordSize;
- }
- }
- /// <summary>
- /// Get the TAR Buffer's record size.
- /// </summary>
- /// <returns>The record size in bytes.
- /// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></returns>
- [Obsolete("Use RecordSize property instead")]
- public int GetRecordSize()
- {
- return recordSize;
- }
- /// <summary>
- /// Get the Blocking factor for the buffer
- /// </summary>
- /// <value>This is the number of blocks in each record.</value>
- public int BlockFactor {
- get {
- return blockFactor;
- }
- }
- /// <summary>
- /// Get the TAR Buffer's block factor
- /// </summary>
- /// <returns>The block factor; the number of blocks per record.</returns>
- [Obsolete("Use BlockFactor property instead")]
- public int GetBlockFactor()
- {
- return blockFactor;
- }
-
- /// <summary>
- /// Construct a default TarBuffer
- /// </summary>
- protected TarBuffer()
- {
- }
-
- /// <summary>
- /// Create TarBuffer for reading with default BlockFactor
- /// </summary>
- /// <param name="inputStream">Stream to buffer</param>
- /// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns>
- public static TarBuffer CreateInputTarBuffer(Stream inputStream)
- {
- if ( inputStream == null )
- {
- throw new ArgumentNullException("inputStream");
- }
- return CreateInputTarBuffer(inputStream, DefaultBlockFactor);
- }
- /// <summary>
- /// Construct TarBuffer for reading inputStream setting BlockFactor
- /// </summary>
- /// <param name="inputStream">Stream to buffer</param>
- /// <param name="blockFactor">Blocking factor to apply</param>
- /// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns>
- public static TarBuffer CreateInputTarBuffer(Stream inputStream, int blockFactor)
- {
- if ( inputStream == null )
- {
- throw new ArgumentNullException("inputStream");
- }
-
- if ( blockFactor <= 0 )
- {
- #if NETCF_1_0
- throw new ArgumentOutOfRangeException("blockFactor");
- #else
- throw new ArgumentOutOfRangeException("blockFactor", "Factor cannot be negative");
- #endif
- }
- TarBuffer tarBuffer = new TarBuffer();
- tarBuffer.inputStream = inputStream;
- tarBuffer.outputStream = null;
- tarBuffer.Initialize(blockFactor);
-
- return tarBuffer;
- }
- /// <summary>
- /// Construct TarBuffer for writing with default BlockFactor
- /// </summary>
- /// <param name="outputStream">output stream for buffer</param>
- /// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns>
- public static TarBuffer CreateOutputTarBuffer(Stream outputStream)
- {
- if ( outputStream == null )
- {
- throw new ArgumentNullException("outputStream");
- }
- return CreateOutputTarBuffer(outputStream, DefaultBlockFactor);
- }
- /// <summary>
- /// Construct TarBuffer for writing Tar output to streams.
- /// </summary>
- /// <param name="outputStream">Output stream to write to.</param>
- /// <param name="blockFactor">Blocking factor to apply</param>
- /// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns>
- public static TarBuffer CreateOutputTarBuffer(Stream outputStream, int blockFactor)
- {
- if ( outputStream == null )
- {
- throw new ArgumentNullException("outputStream");
- }
- if ( blockFactor <= 0 )
- {
- #if NETCF_1_0
- throw new ArgumentOutOfRangeException("blockFactor");
- #else
- throw new ArgumentOutOfRangeException("blockFactor", "Factor cannot be negative");
- #endif
- }
- TarBuffer tarBuffer = new TarBuffer();
- tarBuffer.inputStream = null;
- tarBuffer.outputStream = outputStream;
- tarBuffer.Initialize(blockFactor);
-
- return tarBuffer;
- }
-
- /// <summary>
- /// Initialization common to all constructors.
- /// </summary>
- void Initialize(int archiveBlockFactor)
- {
- blockFactor = archiveBlockFactor;
- recordSize = archiveBlockFactor * BlockSize;
- recordBuffer = new byte[RecordSize];
-
- if (inputStream != null) {
- currentRecordIndex = -1;
- currentBlockIndex = BlockFactor;
- }
- else {
- currentRecordIndex = 0;
- currentBlockIndex = 0;
- }
- }
-
- /// <summary>
- /// Determine if an archive block indicates End of Archive. End of
- /// archive is indicated by a block that consists entirely of null bytes.
- /// All remaining blocks for the record should also be null's
- /// However some older tars only do a couple of null blocks (Old GNU tar for one)
- /// and also partial records
- /// </summary>
- /// <param name = "block">The data block to check.</param>
- /// <returns>Returns true if the block is an EOF block; false otherwise.</returns>
- [Obsolete("Use IsEndOfArchiveBlock instead")]
- public bool IsEOFBlock(byte[] block)
- {
- if ( block == null ) {
- throw new ArgumentNullException("block");
- }
- if ( block.Length != BlockSize )
- {
- throw new ArgumentException("block length is invalid");
- }
- for (int i = 0; i < BlockSize; ++i) {
- if (block[i] != 0) {
- return false;
- }
- }
-
- return true;
- }
- /// <summary>
- /// Determine if an archive block indicates the End of an Archive has been reached.
- /// End of archive is indicated by a block that consists entirely of null bytes.
- /// All remaining blocks for the record should also be null's
- /// However some older tars only do a couple of null blocks (Old GNU tar for one)
- /// and also partial records
- /// </summary>
- /// <param name = "block">The data block to check.</param>
- /// <returns>Returns true if the block is an EOF block; false otherwise.</returns>
- public static bool IsEndOfArchiveBlock(byte[] block)
- {
- if ( block == null ) {
- throw new ArgumentNullException("block");
- }
- if ( block.Length != BlockSize ) {
- throw new ArgumentException("block length is invalid");
- }
- for ( int i = 0; i < BlockSize; ++i ) {
- if ( block[i] != 0 ) {
- return false;
- }
- }
- return true;
- }
-
- /// <summary>
- /// Skip over a block on the input stream.
- /// </summary>
- public void SkipBlock()
- {
- if (inputStream == null) {
- throw new TarException("no input stream defined");
- }
-
- if (currentBlockIndex >= BlockFactor) {
- if (!ReadRecord()) {
- throw new TarException("Failed to read a record");
- }
- }
-
- currentBlockIndex++;
- }
-
- /// <summary>
- /// Read a block from the input stream.
- /// </summary>
- /// <returns>
- /// The block of data read.
- /// </returns>
- public byte[] ReadBlock()
- {
- if (inputStream == null) {
- throw new TarException("TarBuffer.ReadBlock - no input stream defined");
- }
-
- if (currentBlockIndex >= BlockFactor) {
- if (!ReadRecord()) {
- throw new TarException("Failed to read a record");
- }
- }
-
- byte[] result = new byte[BlockSize];
-
- Array.Copy(recordBuffer, (currentBlockIndex * BlockSize), result, 0, BlockSize );
- currentBlockIndex++;
- return result;
- }
-
- /// <summary>
- /// Read a record from data stream.
- /// </summary>
- /// <returns>
- /// false if End-Of-File, else true.
- /// </returns>
- bool ReadRecord()
- {
- if (inputStream == null) {
- throw new TarException("no input stream stream defined");
- }
-
- currentBlockIndex = 0;
-
- int offset = 0;
- int bytesNeeded = RecordSize;
- while (bytesNeeded > 0) {
- long numBytes = inputStream.Read(recordBuffer, offset, bytesNeeded);
-
- //
- // NOTE
- // We have found EOF, and the record is not full!
- //
- // This is a broken archive. It does not follow the standard
- // blocking algorithm. However, because we are generous, and
- // it requires little effort, we will simply ignore the error
- // and continue as if the entire record were read. This does
- // not appear to break anything upstream. We used to return
- // false in this case.
- //
- // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
- //
- if (numBytes <= 0) {
- break;
- }
-
- offset += (int)numBytes;
- bytesNeeded -= (int)numBytes;
- }
-
- currentRecordIndex++;
- return true;
- }
-
- /// <summary>
- /// Get the current block number, within the current record, zero based.
- /// </summary>
- /// <remarks>Block numbers are zero based values</remarks>
- /// <seealso cref="RecordSize"/>
- public int CurrentBlock
- {
- get { return currentBlockIndex; }
- }
- /// <summary>
- /// Get/set flag indicating ownership of the underlying stream.
- /// When the flag is true <see cref="Close"></see> will close the underlying stream also.
- /// </summary>
- public bool IsStreamOwner
- {
- get { return isStreamOwner_; }
- set { isStreamOwner_ = value; }
- }
- /// <summary>
- /// Get the current block number, within the current record, zero based.
- /// </summary>
- /// <returns>
- /// The current zero based block number.
- /// </returns>
- /// <remarks>
- /// The absolute block number = (<see cref="GetCurrentRecordNum">record number</see> * <see cref="BlockFactor">block factor</see>) + <see cref="GetCurrentBlockNum">block number</see>.
- /// </remarks>
- [Obsolete("Use CurrentBlock property instead")]
- public int GetCurrentBlockNum()
- {
- return currentBlockIndex;
- }
-
- /// <summary>
- /// Get the current record number.
- /// </summary>
- /// <returns>
- /// The current zero based record number.
- /// </returns>
- public int CurrentRecord
- {
- get { return currentRecordIndex; }
- }
- /// <summary>
- /// Get the current record number.
- /// </summary>
- /// <returns>
- /// The current zero based record number.
- /// </returns>
- [Obsolete("Use CurrentRecord property instead")]
- public int GetCurrentRecordNum()
- {
- return currentRecordIndex;
- }
-
- /// <summary>
- /// Write a block of data to the archive.
- /// </summary>
- /// <param name="block">
- /// The data to write to the archive.
- /// </param>
- public void WriteBlock(byte[] block)
- {
- if ( block == null ) {
- throw new ArgumentNullException("block");
- }
- if (outputStream == null) {
- throw new TarException("TarBuffer.WriteBlock - no output stream defined");
- }
-
- if (block.Length != BlockSize) {
- string errorText = string.Format("TarBuffer.WriteBlock - block to write has length '{0}' which is not the block size of '{1}'",
- block.Length, BlockSize );
- throw new TarException(errorText);
- }
-
- if (currentBlockIndex >= BlockFactor) {
- WriteRecord();
- }
- Array.Copy(block, 0, recordBuffer, (currentBlockIndex * BlockSize), BlockSize);
- currentBlockIndex++;
- }
-
- /// <summary>
- /// Write an archive record to the archive, where the record may be
- /// inside of a larger array buffer. The buffer must be "offset plus
- /// record size" long.
- /// </summary>
- /// <param name="buffer">
- /// The buffer containing the record data to write.
- /// </param>
- /// <param name="offset">
- /// The offset of the record data within buffer.
- /// </param>
- public void WriteBlock(byte[] buffer, int offset)
- {
- if ( buffer == null ) {
- throw new ArgumentNullException("buffer");
- }
- if (outputStream == null) {
- throw new TarException("TarBuffer.WriteBlock - no output stream stream defined");
- }
-
- if ( (offset < 0) || (offset >= buffer.Length) )
- {
- throw new ArgumentOutOfRangeException("offset");
- }
- if ((offset + BlockSize) > buffer.Length) {
- string errorText = string.Format("TarBuffer.WriteBlock - record has length '{0}' with offset '{1}' which is less than the record size of '{2}'",
- buffer.Length, offset, recordSize);
- throw new TarException(errorText);
- }
-
- if (currentBlockIndex >= BlockFactor) {
- WriteRecord();
- }
-
- Array.Copy(buffer, offset, recordBuffer, (currentBlockIndex * BlockSize), BlockSize);
-
- currentBlockIndex++;
- }
-
- /// <summary>
- /// Write a TarBuffer record to the archive.
- /// </summary>
- void WriteRecord()
- {
- if (outputStream == null) {
- throw new TarException("TarBuffer.WriteRecord no output stream defined");
- }
-
- outputStream.Write(recordBuffer, 0, RecordSize);
- outputStream.Flush();
-
- currentBlockIndex = 0;
- currentRecordIndex++;
- }
-
- /// <summary>
- /// WriteFinalRecord writes the current record buffer to output any unwritten data is present.
- /// </summary>
- /// <remarks>Any trailing bytes are set to zero which is by definition correct behaviour
- /// for the end of a tar stream.</remarks>
- void WriteFinalRecord()
- {
- if (outputStream == null) {
- throw new TarException("TarBuffer.WriteFinalRecord no output stream defined");
- }
-
- if (currentBlockIndex > 0) {
- int dataBytes = currentBlockIndex * BlockSize;
- Array.Clear(recordBuffer, dataBytes, RecordSize - dataBytes);
- WriteRecord();
- }
- outputStream.Flush();
- }
-
- /// <summary>
- /// Close the TarBuffer. If this is an output buffer, also flush the
- /// current block before closing.
- /// </summary>
- public void Close()
- {
- if (outputStream != null) {
- WriteFinalRecord();
- if (isStreamOwner_) {
- outputStream.Close();
- }
- outputStream = null;
- }
- else if (inputStream != null) {
- if (isStreamOwner_) {
- inputStream.Close();
- }
- inputStream = null;
- }
- }
- #region Instance Fields
- Stream inputStream;
- Stream outputStream;
-
- byte[] recordBuffer;
- int currentBlockIndex;
- int currentRecordIndex;
- int recordSize = DefaultRecordSize;
- int blockFactor = DefaultBlockFactor;
- bool isStreamOwner_ = true;
- #endregion
- }
- }
- /* The original Java file had this header:
- *
- ** Authored by Timothy Gerard Endres
- ** <mailto:time@gjt.org> <http://www.trustice.com>
- **
- ** This work has been placed into the public domain.
- ** You may use this work in any way and for any purpose you wish.
- **
- ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
- ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
- ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
- ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
- ** REDISTRIBUTION OF THIS SOFTWARE.
- **
- */
|