root/branches/Abonament/BazaReklam.Updater/ICSharpCode.SharpZipLib/Tar/TarBuffer.cs @ 763

Wersja 597, 18.0 KB (wprowadzona przez marek, 17 years temu)

re #165

Line 
1// TarBuffer.cs
2// Copyright (C) 2001 Mike Krueger
3//
4// This program is free software; you can redistribute it and/or
5// modify it under the terms of the GNU General Public License
6// as published by the Free Software Foundation; either version 2
7// of the License, or (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17//
18// Linking this library statically or dynamically with other modules is
19// making a combined work based on this library.  Thus, the terms and
20// conditions of the GNU General Public License cover the whole
21// combination.
22//
23// As a special exception, the copyright holders of this library give you
24// permission to link this library with independent modules to produce an
25// executable, regardless of the license terms of these independent
26// modules, and to copy and distribute the resulting executable under
27// terms of your choice, provided that you also meet, for each linked
28// independent module, the terms and conditions of the license of that
29// module.  An independent module is a module which is not derived from
30// or based on this library.  If you modify this library, you may extend
31// this exception to your version of the library, but you are not
32// obligated to do so.  If you do not wish to do so, delete this
33// exception statement from your version.
34
35using System;
36using System.IO;
37using System.Text;
38
39namespace ICSharpCode.SharpZipLib.Tar
40{
41       
42        /// <summary>
43        /// The TarBuffer class implements the tar archive concept
44        /// of a buffered input stream. This concept goes back to the
45        /// days of blocked tape drives and special io devices. In the
46        /// C# universe, the only real function that this class
47        /// performs is to ensure that files have the correct "record"
48        /// size, or other tars will complain.
49        /// <p>
50        /// You should never have a need to access this class directly.
51        /// TarBuffers are created by Tar IO Streams.
52        /// </p>
53        /// </summary>
54        public class TarBuffer
55        {
56
57/* A quote from GNU tar man file on blocking and records
58   A `tar' archive file contains a series of blocks.  Each block
59contains `BLOCKSIZE' bytes.  Although this format may be thought of as
60being on magnetic tape, other media are often used.
61
62   Each file archived is represented by a header block which describes
63the file, followed by zero or more blocks which give the contents of
64the file.  At the end of the archive file there may be a block filled
65with binary zeros as an end-of-file marker.  A reasonable system should
66write a block of zeros at the end, but must not assume that such a
67block exists when reading an archive.
68
69   The blocks may be "blocked" for physical I/O operations.  Each
70record of N blocks is written with a single 'write ()'
71operation.  On magnetic tapes, the result of such a write is a single
72record.  When writing an archive, the last record of blocks should be
73written at the full size, with blocks after the zero block containing
74all zeros.  When reading an archive, a reasonable system should
75properly handle an archive whose last record is shorter than the rest,
76or which contains garbage records after a zero block.
77*/
78
79                #region Constants
80                /// <summary>
81                /// The size of a block in a tar archive in bytes.
82                /// </summary>
83                /// <remarks>This is 512 bytes.</remarks>
84                public const int BlockSize = 512;
85               
86                /// <summary>
87                /// The number of blocks in a default record.
88                /// </summary>
89                /// <remarks>
90                /// The default value is 20 blocks per record.
91                /// </remarks>
92                public const int DefaultBlockFactor = 20;
93               
94                /// <summary>
95                /// The size in bytes of a default record.
96                /// </summary>
97                /// <remarks>
98                /// The default size is 10KB.
99                /// </remarks>
100                public const int DefaultRecordSize = BlockSize * DefaultBlockFactor;
101                #endregion
102
103                /// <summary>
104                /// Get the record size for this buffer
105                /// </summary>
106                /// <value>The record size in bytes.
107                /// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></value>
108                public int RecordSize
109                {
110                        get {
111                                return recordSize;
112                        }
113                }
114
115                /// <summary>
116                /// Get the TAR Buffer's record size.
117                /// </summary>
118                /// <returns>The record size in bytes.
119                /// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></returns>
120                [Obsolete("Use RecordSize property instead")]
121                public int GetRecordSize()
122                {
123                        return recordSize;
124                }
125
126                /// <summary>
127                /// Get the Blocking factor for the buffer
128                /// </summary>
129                /// <value>This is the number of block in each record.</value>
130                public int BlockFactor {
131                        get {
132                                return blockFactor;
133                        }
134                }
135
136                /// <summary>
137                /// Get the TAR Buffer's block factor
138                /// </summary>
139                /// <returns>The block factor; the number of blocks per record.</returns>
140                [Obsolete("Use BlockFactor property instead")]
141                public int GetBlockFactor()
142                {
143                        return this.blockFactor;
144                }
145               
146                /// <summary>
147                /// Construct a default TarBuffer
148                /// </summary>
149                protected TarBuffer()
150                {
151                }
152               
153                /// <summary>
154                /// Create TarBuffer for reading with default BlockFactor
155                /// </summary>
156                /// <param name="inputStream">Stream to buffer</param>
157                /// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns>
158                public static TarBuffer CreateInputTarBuffer(Stream inputStream)
159                {
160                        if ( inputStream == null )
161                        {
162                                throw new ArgumentNullException("inputStream");
163                        }
164
165                        return CreateInputTarBuffer(inputStream, TarBuffer.DefaultBlockFactor);
166                }
167
168                /// <summary>
169                /// Construct TarBuffer for reading inputStream setting BlockFactor
170                /// </summary>
171                /// <param name="inputStream">Stream to buffer</param>
172                /// <param name="blockFactor">Blocking factor to apply</param>
173                /// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns>
174                public static TarBuffer CreateInputTarBuffer(Stream inputStream, int blockFactor)
175                {
176                        if ( inputStream == null )
177                        {
178                                throw new ArgumentNullException("inputStream");
179                        }
180                       
181                        if ( blockFactor <= 0 )
182                        {
183#if NETCF_1_0
184                                throw new ArgumentOutOfRangeException("blockFactor");
185#else
186                                throw new ArgumentOutOfRangeException("blockFactor", "Factor cannot be negative");
187#endif                         
188                        }
189
190                        TarBuffer tarBuffer = new TarBuffer();
191                        tarBuffer.inputStream  = inputStream;
192                        tarBuffer.outputStream = null;
193                        tarBuffer.Initialize(blockFactor);
194                       
195                        return tarBuffer;
196                }
197
198                /// <summary>
199                /// Construct TarBuffer for writing with default BlockFactor
200                /// </summary>
201                /// <param name="outputStream">output stream for buffer</param>
202                /// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns>
203                public static TarBuffer CreateOutputTarBuffer(Stream outputStream)
204                {
205                        if ( outputStream == null )
206                        {
207                                throw new ArgumentNullException("outputStream");
208                        }
209
210                        return CreateOutputTarBuffer(outputStream, TarBuffer.DefaultBlockFactor);
211                }
212
213                /// <summary>
214                /// Construct TarBuffer for writing Tar output to streams.
215                /// </summary>
216                /// <param name="outputStream">Output stream to write to.</param>
217                /// <param name="blockFactor">Blocking factor to apply</param>
218                /// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns>
219                public static TarBuffer CreateOutputTarBuffer(Stream outputStream, int blockFactor)
220                {
221                        if ( outputStream == null )
222                        {
223                                throw new ArgumentNullException("outputStream");
224                        }
225
226                        if ( blockFactor <= 0 )
227                        {
228#if NETCF_1_0
229                                throw new ArgumentOutOfRangeException("blockFactor");
230#else
231                                throw new ArgumentOutOfRangeException("blockFactor", "Factor cannot be negative");
232#endif                         
233                        }
234
235                        TarBuffer tarBuffer = new TarBuffer();
236                        tarBuffer.inputStream  = null;
237                        tarBuffer.outputStream = outputStream;
238                        tarBuffer.Initialize(blockFactor);
239                       
240                        return tarBuffer;
241                }
242               
243                /// <summary>
244                /// Initialization common to all constructors.
245                /// </summary>
246                void Initialize(int blockFactor)
247                {
248                        this.blockFactor  = blockFactor;
249                        recordSize   = blockFactor * BlockSize;
250                        recordBuffer  = new byte[RecordSize];
251                       
252                        if (inputStream != null) {
253                                currentRecordIndex = -1;
254                                currentBlockIndex = BlockFactor;
255                        }
256                        else {
257                                currentRecordIndex = 0;
258                                currentBlockIndex = 0;
259                        }
260                }
261               
262                // TODO: IsEOFBlock could/should be static but this is a breaking change.
263
264                /// <summary>
265                /// Determine if an archive block indicates End of Archive. End of
266                /// archive is indicated by a block that consists entirely of null bytes.
267                /// All remaining blocks for the record should also be null's
268                /// However some older tars only do a couple of null blocks (Old GNU tar for one)
269                /// and also partial records
270                /// </summary>
271                /// <param name = "block">The data block to check.</param>
272                /// <returns>Returns true if the block is an EOF block; false otherwise.</returns>
273                [Obsolete("Use IsEndOfArchiveBlock instead")]
274                public bool IsEOFBlock(byte[] block)
275                {
276                        if ( block == null ) {
277                                throw new ArgumentNullException("block");
278                        }
279
280                        if ( block.Length != BlockSize )
281                        {
282                                throw new ArgumentException("block length is invalid");
283                        }
284
285                        for (int i = 0; i < BlockSize; ++i) {
286                                if (block[i] != 0) {
287                                        return false;
288                                }
289                        }
290                       
291                        return true;
292                }
293
294
295                /// <summary>
296                /// Determine if an archive block indicates the End of an Archive has been reached.
297                /// End of archive is indicated by a block that consists entirely of null bytes.
298                /// All remaining blocks for the record should also be null's
299                /// However some older tars only do a couple of null blocks (Old GNU tar for one)
300                /// and also partial records
301                /// </summary>
302                /// <param name = "block">The data block to check.</param>
303                /// <returns>Returns true if the block is an EOF block; false otherwise.</returns>
304                public static bool IsEndOfArchiveBlock(byte[] block)
305                {
306                        if ( block == null ) {
307                                throw new ArgumentNullException("block");
308                        }
309
310                        if ( block.Length != BlockSize ) {
311                                throw new ArgumentException("block length is invalid");
312                        }
313
314                        for ( int i = 0; i < BlockSize; ++i ) {
315                                if ( block[i] != 0 ) {
316                                        return false;
317                                }
318                        }
319
320                        return true;
321                }
322               
323                /// <summary>
324                /// Skip over a block on the input stream.
325                /// </summary>
326                public void SkipBlock()
327                {
328                        if (this.inputStream == null) {
329                                throw new TarException("no input stream defined");
330                        }
331                       
332                        if (currentBlockIndex >= BlockFactor) {
333                                if (!ReadRecord()) {
334                                        throw new TarException("Failed to read a record");
335                                }
336                        }
337                       
338                        currentBlockIndex++;
339                }
340               
341                /// <summary>
342                /// Read a block from the input stream.
343                /// </summary>
344                /// <returns>
345                /// The block of data read.
346                /// </returns>
347                public byte[] ReadBlock()
348                {
349                        if (inputStream == null) {
350                                throw new TarException("TarBuffer.ReadBlock - no input stream defined");
351                        }
352                       
353                        if (currentBlockIndex >= BlockFactor) {
354                                if (!ReadRecord()) {
355                                        throw new TarException("Failed to read a record");
356                                }
357                        }
358                       
359                        byte[] result = new byte[BlockSize];
360                       
361                        Array.Copy(recordBuffer, (currentBlockIndex * BlockSize), result, 0, BlockSize );
362                        currentBlockIndex++;
363                        return result;
364                }
365               
366                /// <summary>
367                /// Read a record from data stream.
368                /// </summary>
369                /// <returns>
370                /// false if End-Of-File, else true.
371                /// </returns>
372                bool ReadRecord()
373                {
374                        if (inputStream == null) {
375                                throw new TarException("no input stream stream defined");
376                        }
377                                               
378                        currentBlockIndex = 0;
379                       
380                        int offset = 0;
381                        int bytesNeeded = RecordSize;
382
383                        while (bytesNeeded > 0) {
384                                long numBytes = inputStream.Read(recordBuffer, offset, bytesNeeded);
385                               
386                                //
387                                // NOTE
388                                // We have found EOF, and the record is not full!
389                                //
390                                // This is a broken archive. It does not follow the standard
391                                // blocking algorithm. However, because we are generous, and
392                                // it requires little effort, we will simply ignore the error
393                                // and continue as if the entire record were read. This does
394                                // not appear to break anything upstream. We used to return
395                                // false in this case.
396                                //
397                                // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
398                                //
399                                if (numBytes <= 0) {
400                                        break;
401                                }
402                               
403                                offset      += (int)numBytes;
404                                bytesNeeded -= (int)numBytes;
405                        }
406                       
407                        currentRecordIndex++;
408                        return true;
409                }
410               
411                /// <summary>
412                /// Get the current block number, within the current record, zero based.
413                /// </summary>
414                public int CurrentBlock
415                {
416                        get { return currentBlockIndex; }
417                }
418
419                /// <summary>
420                /// Get the current block number, within the current record, zero based.
421                /// </summary>
422                /// <returns>
423                /// The current zero based block number.
424                /// </returns>
425                /// <remarks>
426                /// The absolute block number = (<see cref="GetCurrentRecordNum">record number</see> * <see cref="BlockFactor">block factor</see>) + <see cref="GetCurrentBlockNum">block number</see>.
427                /// </remarks>
428                [Obsolete("Use CurrentBlock property instead")]
429                public int GetCurrentBlockNum()
430                {
431                        return this.currentBlockIndex;
432                }
433               
434                /// <summary>
435                /// Get the current record number.
436                /// </summary>
437                /// <returns>
438                /// The current zero based record number.
439                /// </returns>
440                public int CurrentRecord
441                {
442                        get { return currentRecordIndex; }
443                }
444
445                /// <summary>
446                /// Get the current record number.
447                /// </summary>
448                /// <returns>
449                /// The current zero based record number.
450                /// </returns>
451                [Obsolete("Use CurrentRecord property instead")]
452                public int GetCurrentRecordNum()
453                {
454                        return this.currentRecordIndex;
455                }
456               
457                /// <summary>
458                /// Write a block of data to the archive.
459                /// </summary>
460                /// <param name="block">
461                /// The data to write to the archive.
462                /// </param>
463                public void WriteBlock(byte[] block)
464                {
465                        if ( block == null ) {
466                                throw new ArgumentNullException("block");
467                        }
468
469                        if (outputStream == null) {
470                                throw new TarException("TarBuffer.WriteBlock - no output stream defined");
471                        }
472                                               
473                        if (block.Length != BlockSize) {
474                                string errorText = string.Format("TarBuffer.WriteBlock - block to write has length '{0}' which is not the block size of '{1}'",
475                                        block.Length, BlockSize );
476                                throw new TarException(errorText);
477                        }
478                       
479                        if (currentBlockIndex >= BlockFactor) {
480                                WriteRecord();
481                        }
482
483                        Array.Copy(block, 0, recordBuffer, (currentBlockIndex * BlockSize), BlockSize);
484                        currentBlockIndex++;
485                }
486               
487                /// <summary>
488                /// Write an archive record to the archive, where the record may be
489                /// inside of a larger array buffer. The buffer must be "offset plus
490                /// record size" long.
491                /// </summary>
492                /// <param name="buffer">
493                /// The buffer containing the record data to write.
494                /// </param>
495                /// <param name="offset">
496                /// The offset of the record data within buffer.
497                /// </param>
498                public void WriteBlock(byte[] buffer, int offset)
499                {
500                        if ( buffer == null ) {
501                                throw new ArgumentNullException("buffer");
502                        }
503
504                        if (outputStream == null) {
505                                throw new TarException("TarBuffer.WriteBlock - no output stream stream defined");
506                        }
507                                               
508                        if ( (offset < 0) || (offset >= buffer.Length) )
509                        {
510                                throw new ArgumentOutOfRangeException("offset");
511                        }
512
513                        if ((offset + BlockSize) > buffer.Length) {
514                                string errorText = string.Format("TarBuffer.WriteBlock - record has length '{0}' with offset '{1}' which is less than the record size of '{2}'",
515                                        buffer.Length, offset, this.recordSize);
516                                throw new TarException(errorText);
517                        }
518                       
519                        if (currentBlockIndex >= BlockFactor) {
520                                WriteRecord();
521                        }
522                       
523                        Array.Copy(buffer, offset, recordBuffer, (currentBlockIndex * BlockSize), BlockSize);
524                       
525                        currentBlockIndex++;
526                }
527               
528                /// <summary>
529                /// Write a TarBuffer record to the archive.
530                /// </summary>
531                void WriteRecord()
532                {
533                        if (outputStream == null)
534                        {
535                                throw new TarException("TarBuffer.WriteRecord no output stream defined");
536                        }
537                       
538                        outputStream.Write(recordBuffer, 0, RecordSize);
539                        outputStream.Flush();
540                       
541                        currentBlockIndex = 0;
542                        currentRecordIndex++;
543                }
544               
545                /// <summary>
546                /// Flush the current record if it has any data in it.
547                /// </summary>
548                void Flush()
549                {
550                        if (outputStream == null)
551                        {
552                                throw new TarException("TarBuffer.Flush no output stream defined");
553                        }
554                       
555                        if (currentBlockIndex > 0)
556                        {
557                                int dataBytes = currentBlockIndex * BlockSize;
558                                Array.Clear(recordBuffer, dataBytes, RecordSize - dataBytes);
559                                WriteRecord();
560                        }
561
562                        outputStream.Flush();
563                }
564               
565                /// <summary>
566                /// Close the TarBuffer. If this is an output buffer, also flush the
567                /// current block before closing.
568                /// </summary>
569                public void Close()
570                {
571                        if (outputStream != null)
572                        {
573                                Flush();
574       
575                                outputStream.Close();
576                                outputStream = null;
577                        }
578                        else if (inputStream != null)
579                        {
580                                inputStream.Close();
581                                inputStream = null;
582                        }
583                }
584
585                #region Instance Fields
586                Stream inputStream;
587                Stream outputStream;
588               
589                byte[] recordBuffer;
590                int currentBlockIndex;
591                int currentRecordIndex;
592
593                int recordSize = DefaultRecordSize;
594                int blockFactor = DefaultBlockFactor;
595                #endregion
596        }
597}
598
599/* The original Java file had this header:
600        *
601        ** Authored by Timothy Gerard Endres
602        ** <mailto:time@gjt.org>  <http://www.trustice.com>
603        **
604        ** This work has been placed into the public domain.
605        ** You may use this work in any way and for any purpose you wish.
606        **
607        ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
608        ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
609        ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
610        ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
611        ** REDISTRIBUTION OF THIS SOFTWARE.
612        **
613        */
Notatka: Zobacz TracBrowser aby uzyskać więcej informacji.