| | 1 | | using System; |
| | 2 | | using System.IO; |
| | 3 | |
|
| | 4 | | namespace ICSharpCode.SharpZipLib.Tar |
| | 5 | | { |
| | 6 | | /// <summary> |
| | 7 | | /// The TarBuffer class implements the tar archive concept |
| | 8 | | /// of a buffered input stream. This concept goes back to the |
| | 9 | | /// days of blocked tape drives and special io devices. In the |
| | 10 | | /// C# universe, the only real function that this class |
| | 11 | | /// performs is to ensure that files have the correct "record" |
| | 12 | | /// size, or other tars will complain. |
| | 13 | | /// <p> |
| | 14 | | /// You should never have a need to access this class directly. |
| | 15 | | /// TarBuffers are created by Tar IO Streams. |
| | 16 | | /// </p> |
| | 17 | | /// </summary> |
| | 18 | | public class TarBuffer |
| | 19 | | { |
| | 20 | |
|
| | 21 | | /* A quote from GNU tar man file on blocking and records |
| | 22 | | A `tar' archive file contains a series of blocks. Each block |
| | 23 | | contains `BLOCKSIZE' bytes. Although this format may be thought of as |
| | 24 | | being on magnetic tape, other media are often used. |
| | 25 | |
|
| | 26 | | Each file archived is represented by a header block which describes |
| | 27 | | the file, followed by zero or more blocks which give the contents of |
| | 28 | | the file. At the end of the archive file there may be a block filled |
| | 29 | | with binary zeros as an end-of-file marker. A reasonable system should |
| | 30 | | write a block of zeros at the end, but must not assume that such a |
| | 31 | | block exists when reading an archive. |
| | 32 | |
|
| | 33 | | The blocks may be "blocked" for physical I/O operations. Each |
| | 34 | | record of N blocks is written with a single 'write ()' |
| | 35 | | operation. On magnetic tapes, the result of such a write is a single |
| | 36 | | record. When writing an archive, the last record of blocks should be |
| | 37 | | written at the full size, with blocks after the zero block containing |
| | 38 | | all zeros. When reading an archive, a reasonable system should |
| | 39 | | properly handle an archive whose last record is shorter than the rest, |
| | 40 | | or which contains garbage records after a zero block. |
| | 41 | | */ |
| | 42 | |
|
| | 43 | | #region Constants |
| | 44 | | /// <summary> |
| | 45 | | /// The size of a block in a tar archive in bytes. |
| | 46 | | /// </summary> |
| | 47 | | /// <remarks>This is 512 bytes.</remarks> |
| | 48 | | public const int BlockSize = 512; |
| | 49 | |
|
| | 50 | | /// <summary> |
| | 51 | | /// The number of blocks in a default record. |
| | 52 | | /// </summary> |
| | 53 | | /// <remarks> |
| | 54 | | /// The default value is 20 blocks per record. |
| | 55 | | /// </remarks> |
| | 56 | | public const int DefaultBlockFactor = 20; |
| | 57 | |
|
| | 58 | | /// <summary> |
| | 59 | | /// The size in bytes of a default record. |
| | 60 | | /// </summary> |
| | 61 | | /// <remarks> |
| | 62 | | /// The default size is 10KB. |
| | 63 | | /// </remarks> |
| | 64 | | public const int DefaultRecordSize = BlockSize * DefaultBlockFactor; |
| | 65 | | #endregion |
| | 66 | |
|
| | 67 | | /// <summary> |
| | 68 | | /// Get the record size for this buffer |
| | 69 | | /// </summary> |
| | 70 | | /// <value>The record size in bytes. |
| | 71 | | /// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></value> |
| | 72 | | public int RecordSize { |
| | 73 | | get { |
| 360 | 74 | | return recordSize; |
| | 75 | | } |
| | 76 | | } |
| | 77 | |
|
| | 78 | | /// <summary> |
| | 79 | | /// Get the TAR Buffer's record size. |
| | 80 | | /// </summary> |
| | 81 | | /// <returns>The record size in bytes. |
| | 82 | | /// This is equal to the <see cref="BlockFactor"/> multiplied by the <see cref="BlockSize"/></returns> |
| | 83 | | [Obsolete("Use RecordSize property instead")] |
| | 84 | | public int GetRecordSize() |
| | 85 | | { |
| 0 | 86 | | return recordSize; |
| | 87 | | } |
| | 88 | |
|
| | 89 | | /// <summary> |
| | 90 | | /// Get the Blocking factor for the buffer |
| | 91 | | /// </summary> |
| | 92 | | /// <value>This is the number of blocks in each record.</value> |
| | 93 | | public int BlockFactor { |
| | 94 | | get { |
| 4198 | 95 | | return blockFactor; |
| | 96 | | } |
| | 97 | | } |
| | 98 | |
|
| | 99 | | /// <summary> |
| | 100 | | /// Get the TAR Buffer's block factor |
| | 101 | | /// </summary> |
| | 102 | | /// <returns>The block factor; the number of blocks per record.</returns> |
| | 103 | | [Obsolete("Use BlockFactor property instead")] |
| | 104 | | public int GetBlockFactor() |
| | 105 | | { |
| 0 | 106 | | return blockFactor; |
| | 107 | | } |
| | 108 | |
|
| | 109 | | /// <summary> |
| | 110 | | /// Construct a default TarBuffer |
| | 111 | | /// </summary> |
| 78 | 112 | | protected TarBuffer() |
| | 113 | | { |
| 78 | 114 | | } |
| | 115 | |
|
| | 116 | | /// <summary> |
| | 117 | | /// Create TarBuffer for reading with default BlockFactor |
| | 118 | | /// </summary> |
| | 119 | | /// <param name="inputStream">Stream to buffer</param> |
| | 120 | | /// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns> |
| | 121 | | public static TarBuffer CreateInputTarBuffer(Stream inputStream) |
| | 122 | | { |
| 0 | 123 | | if (inputStream == null) { |
| 0 | 124 | | throw new ArgumentNullException(nameof(inputStream)); |
| | 125 | | } |
| | 126 | |
|
| 0 | 127 | | return CreateInputTarBuffer(inputStream, DefaultBlockFactor); |
| | 128 | | } |
| | 129 | |
|
| | 130 | | /// <summary> |
| | 131 | | /// Construct TarBuffer for reading inputStream setting BlockFactor |
| | 132 | | /// </summary> |
| | 133 | | /// <param name="inputStream">Stream to buffer</param> |
| | 134 | | /// <param name="blockFactor">Blocking factor to apply</param> |
| | 135 | | /// <returns>A new <see cref="TarBuffer"/> suitable for input.</returns> |
| | 136 | | public static TarBuffer CreateInputTarBuffer(Stream inputStream, int blockFactor) |
| | 137 | | { |
| 5 | 138 | | if (inputStream == null) { |
| 0 | 139 | | throw new ArgumentNullException(nameof(inputStream)); |
| | 140 | | } |
| | 141 | |
|
| 5 | 142 | | if (blockFactor <= 0) { |
| 0 | 143 | | throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative"); |
| | 144 | | } |
| | 145 | |
|
| 5 | 146 | | var tarBuffer = new TarBuffer(); |
| 5 | 147 | | tarBuffer.inputStream = inputStream; |
| 5 | 148 | | tarBuffer.outputStream = null; |
| 5 | 149 | | tarBuffer.Initialize(blockFactor); |
| | 150 | |
|
| 5 | 151 | | return tarBuffer; |
| | 152 | | } |
| | 153 | |
|
| | 154 | | /// <summary> |
| | 155 | | /// Construct TarBuffer for writing with default BlockFactor |
| | 156 | | /// </summary> |
| | 157 | | /// <param name="outputStream">output stream for buffer</param> |
| | 158 | | /// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns> |
| | 159 | | public static TarBuffer CreateOutputTarBuffer(Stream outputStream) |
| | 160 | | { |
| 0 | 161 | | if (outputStream == null) { |
| 0 | 162 | | throw new ArgumentNullException(nameof(outputStream)); |
| | 163 | | } |
| | 164 | |
|
| 0 | 165 | | return CreateOutputTarBuffer(outputStream, DefaultBlockFactor); |
| | 166 | | } |
| | 167 | |
|
| | 168 | | /// <summary> |
| | 169 | | /// Construct TarBuffer for writing Tar output to streams. |
| | 170 | | /// </summary> |
| | 171 | | /// <param name="outputStream">Output stream to write to.</param> |
| | 172 | | /// <param name="blockFactor">Blocking factor to apply</param> |
| | 173 | | /// <returns>A new <see cref="TarBuffer"/> suitable for output.</returns> |
| | 174 | | public static TarBuffer CreateOutputTarBuffer(Stream outputStream, int blockFactor) |
| | 175 | | { |
| 73 | 176 | | if (outputStream == null) { |
| 0 | 177 | | throw new ArgumentNullException(nameof(outputStream)); |
| | 178 | | } |
| | 179 | |
|
| 73 | 180 | | if (blockFactor <= 0) { |
| 0 | 181 | | throw new ArgumentOutOfRangeException(nameof(blockFactor), "Factor cannot be negative"); |
| | 182 | | } |
| | 183 | |
|
| 73 | 184 | | var tarBuffer = new TarBuffer(); |
| 73 | 185 | | tarBuffer.inputStream = null; |
| 73 | 186 | | tarBuffer.outputStream = outputStream; |
| 73 | 187 | | tarBuffer.Initialize(blockFactor); |
| | 188 | |
|
| 73 | 189 | | return tarBuffer; |
| | 190 | | } |
| | 191 | |
|
| | 192 | | /// <summary> |
| | 193 | | /// Initialization common to all constructors. |
| | 194 | | /// </summary> |
| | 195 | | void Initialize(int archiveBlockFactor) |
| | 196 | | { |
| 78 | 197 | | blockFactor = archiveBlockFactor; |
| 78 | 198 | | recordSize = archiveBlockFactor * BlockSize; |
| 78 | 199 | | recordBuffer = new byte[RecordSize]; |
| | 200 | |
|
| 78 | 201 | | if (inputStream != null) { |
| 5 | 202 | | currentRecordIndex = -1; |
| 5 | 203 | | currentBlockIndex = BlockFactor; |
| 5 | 204 | | } else { |
| 73 | 205 | | currentRecordIndex = 0; |
| 73 | 206 | | currentBlockIndex = 0; |
| | 207 | | } |
| 73 | 208 | | } |
| | 209 | |
|
| | 210 | | /// <summary> |
| | 211 | | /// Determine if an archive block indicates End of Archive. End of |
| | 212 | | /// archive is indicated by a block that consists entirely of null bytes. |
| | 213 | | /// All remaining blocks for the record should also be null's |
| | 214 | | /// However some older tars only do a couple of null blocks (Old GNU tar for one) |
| | 215 | | /// and also partial records |
| | 216 | | /// </summary> |
| | 217 | | /// <param name = "block">The data block to check.</param> |
| | 218 | | /// <returns>Returns true if the block is an EOF block; false otherwise.</returns> |
| | 219 | | [Obsolete("Use IsEndOfArchiveBlock instead")] |
| | 220 | | public bool IsEOFBlock(byte[] block) |
| | 221 | | { |
| 0 | 222 | | if (block == null) { |
| 0 | 223 | | throw new ArgumentNullException(nameof(block)); |
| | 224 | | } |
| | 225 | |
|
| 0 | 226 | | if (block.Length != BlockSize) { |
| 0 | 227 | | throw new ArgumentException("block length is invalid"); |
| | 228 | | } |
| | 229 | |
|
| 0 | 230 | | for (int i = 0; i < BlockSize; ++i) { |
| 0 | 231 | | if (block[i] != 0) { |
| 0 | 232 | | return false; |
| | 233 | | } |
| | 234 | | } |
| | 235 | |
|
| 0 | 236 | | return true; |
| | 237 | | } |
| | 238 | |
|
| | 239 | |
|
| | 240 | | /// <summary> |
| | 241 | | /// Determine if an archive block indicates the End of an Archive has been reached. |
| | 242 | | /// End of archive is indicated by a block that consists entirely of null bytes. |
| | 243 | | /// All remaining blocks for the record should also be null's |
| | 244 | | /// However some older tars only do a couple of null blocks (Old GNU tar for one) |
| | 245 | | /// and also partial records |
| | 246 | | /// </summary> |
| | 247 | | /// <param name = "block">The data block to check.</param> |
| | 248 | | /// <returns>Returns true if the block is an EOF block; false otherwise.</returns> |
| | 249 | | public static bool IsEndOfArchiveBlock(byte[] block) |
| | 250 | | { |
| 3 | 251 | | if (block == null) { |
| 0 | 252 | | throw new ArgumentNullException(nameof(block)); |
| | 253 | | } |
| | 254 | |
|
| 3 | 255 | | if (block.Length != BlockSize) { |
| 0 | 256 | | throw new ArgumentException("block length is invalid"); |
| | 257 | | } |
| | 258 | |
|
| 1030 | 259 | | for (int i = 0; i < BlockSize; ++i) { |
| 514 | 260 | | if (block[i] != 0) { |
| 2 | 261 | | return false; |
| | 262 | | } |
| | 263 | | } |
| | 264 | |
|
| 1 | 265 | | return true; |
| | 266 | | } |
| | 267 | |
|
| | 268 | | /// <summary> |
| | 269 | | /// Skip over a block on the input stream. |
| | 270 | | /// </summary> |
| | 271 | | public void SkipBlock() |
| | 272 | | { |
| 0 | 273 | | if (inputStream == null) { |
| 0 | 274 | | throw new TarException("no input stream defined"); |
| | 275 | | } |
| | 276 | |
|
| 0 | 277 | | if (currentBlockIndex >= BlockFactor) { |
| 0 | 278 | | if (!ReadRecord()) { |
| 0 | 279 | | throw new TarException("Failed to read a record"); |
| | 280 | | } |
| | 281 | | } |
| | 282 | |
|
| 0 | 283 | | currentBlockIndex++; |
| 0 | 284 | | } |
| | 285 | |
|
| | 286 | | /// <summary> |
| | 287 | | /// Read a block from the input stream. |
| | 288 | | /// </summary> |
| | 289 | | /// <returns> |
| | 290 | | /// The block of data read. |
| | 291 | | /// </returns> |
| | 292 | | public byte[] ReadBlock() |
| | 293 | | { |
| 3 | 294 | | if (inputStream == null) { |
| 0 | 295 | | throw new TarException("TarBuffer.ReadBlock - no input stream defined"); |
| | 296 | | } |
| | 297 | |
|
| 3 | 298 | | if (currentBlockIndex >= BlockFactor) { |
| 3 | 299 | | if (!ReadRecord()) { |
| 0 | 300 | | throw new TarException("Failed to read a record"); |
| | 301 | | } |
| | 302 | | } |
| | 303 | |
|
| 3 | 304 | | byte[] result = new byte[BlockSize]; |
| | 305 | |
|
| 3 | 306 | | Array.Copy(recordBuffer, (currentBlockIndex * BlockSize), result, 0, BlockSize); |
| 3 | 307 | | currentBlockIndex++; |
| 3 | 308 | | return result; |
| | 309 | | } |
| | 310 | |
|
| | 311 | | /// <summary> |
| | 312 | | /// Read a record from data stream. |
| | 313 | | /// </summary> |
| | 314 | | /// <returns> |
| | 315 | | /// false if End-Of-File, else true. |
| | 316 | | /// </returns> |
| | 317 | | bool ReadRecord() |
| | 318 | | { |
| 3 | 319 | | if (inputStream == null) { |
| 0 | 320 | | throw new TarException("no input stream stream defined"); |
| | 321 | | } |
| | 322 | |
|
| 3 | 323 | | currentBlockIndex = 0; |
| | 324 | |
|
| 3 | 325 | | int offset = 0; |
| 3 | 326 | | int bytesNeeded = RecordSize; |
| | 327 | |
|
| 6 | 328 | | while (bytesNeeded > 0) { |
| 3 | 329 | | long numBytes = inputStream.Read(recordBuffer, offset, bytesNeeded); |
| | 330 | |
|
| | 331 | | // |
| | 332 | | // NOTE |
| | 333 | | // We have found EOF, and the record is not full! |
| | 334 | | // |
| | 335 | | // This is a broken archive. It does not follow the standard |
| | 336 | | // blocking algorithm. However, because we are generous, and |
| | 337 | | // it requires little effort, we will simply ignore the error |
| | 338 | | // and continue as if the entire record were read. This does |
| | 339 | | // not appear to break anything upstream. We used to return |
| | 340 | | // false in this case. |
| | 341 | | // |
| | 342 | | // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix. |
| | 343 | | // |
| 3 | 344 | | if (numBytes <= 0) { |
| | 345 | | break; |
| | 346 | | } |
| | 347 | |
|
| 3 | 348 | | offset += (int)numBytes; |
| 3 | 349 | | bytesNeeded -= (int)numBytes; |
| | 350 | | } |
| | 351 | |
|
| 3 | 352 | | currentRecordIndex++; |
| 3 | 353 | | return true; |
| | 354 | | } |
| | 355 | |
|
| | 356 | | /// <summary> |
| | 357 | | /// Get the current block number, within the current record, zero based. |
| | 358 | | /// </summary> |
| | 359 | | /// <remarks>Block numbers are zero based values</remarks> |
| | 360 | | /// <seealso cref="RecordSize"/> |
| | 361 | | public int CurrentBlock { |
| 0 | 362 | | get { return currentBlockIndex; } |
| | 363 | | } |
| | 364 | |
|
| | 365 | | /// <summary> |
| | 366 | | /// Get/set flag indicating ownership of the underlying stream. |
| | 367 | | /// When the flag is true <see cref="Close"></see> will close the underlying stream also. |
| | 368 | | /// </summary> |
| | 369 | | public bool IsStreamOwner { |
| 0 | 370 | | get { return isStreamOwner_; } |
| 4 | 371 | | set { isStreamOwner_ = value; } |
| | 372 | | } |
| | 373 | |
|
| | 374 | | /// <summary> |
| | 375 | | /// Get the current block number, within the current record, zero based. |
| | 376 | | /// </summary> |
| | 377 | | /// <returns> |
| | 378 | | /// The current zero based block number. |
| | 379 | | /// </returns> |
| | 380 | | /// <remarks> |
| | 381 | | /// The absolute block number = (<see cref="GetCurrentRecordNum">record number</see> * <see cref="BlockFactor">block |
| | 382 | | /// </remarks> |
| | 383 | | [Obsolete("Use CurrentBlock property instead")] |
| | 384 | | public int GetCurrentBlockNum() |
| | 385 | | { |
| 0 | 386 | | return currentBlockIndex; |
| | 387 | | } |
| | 388 | |
|
| | 389 | | /// <summary> |
| | 390 | | /// Get the current record number. |
| | 391 | | /// </summary> |
| | 392 | | /// <returns> |
| | 393 | | /// The current zero based record number. |
| | 394 | | /// </returns> |
| | 395 | | public int CurrentRecord { |
| 0 | 396 | | get { return currentRecordIndex; } |
| | 397 | | } |
| | 398 | |
|
| | 399 | | /// <summary> |
| | 400 | | /// Get the current record number. |
| | 401 | | /// </summary> |
| | 402 | | /// <returns> |
| | 403 | | /// The current zero based record number. |
| | 404 | | /// </returns> |
| | 405 | | [Obsolete("Use CurrentRecord property instead")] |
| | 406 | | public int GetCurrentRecordNum() |
| | 407 | | { |
| 0 | 408 | | return currentRecordIndex; |
| | 409 | | } |
| | 410 | |
|
| | 411 | | /// <summary> |
| | 412 | | /// Write a block of data to the archive. |
| | 413 | | /// </summary> |
| | 414 | | /// <param name="block"> |
| | 415 | | /// The data to write to the archive. |
| | 416 | | /// </param> |
| | 417 | | public void WriteBlock(byte[] block) |
| | 418 | | { |
| 148 | 419 | | if (block == null) { |
| 0 | 420 | | throw new ArgumentNullException(nameof(block)); |
| | 421 | | } |
| | 422 | |
|
| 148 | 423 | | if (outputStream == null) { |
| 0 | 424 | | throw new TarException("TarBuffer.WriteBlock - no output stream defined"); |
| | 425 | | } |
| | 426 | |
|
| 148 | 427 | | if (block.Length != BlockSize) { |
| 0 | 428 | | string errorText = string.Format("TarBuffer.WriteBlock - block to write has length '{0}' which is not the block |
| 0 | 429 | | block.Length, BlockSize); |
| 0 | 430 | | throw new TarException(errorText); |
| | 431 | | } |
| | 432 | |
|
| 148 | 433 | | if (currentBlockIndex >= BlockFactor) { |
| 4 | 434 | | WriteRecord(); |
| | 435 | | } |
| | 436 | |
|
| 148 | 437 | | Array.Copy(block, 0, recordBuffer, (currentBlockIndex * BlockSize), BlockSize); |
| 148 | 438 | | currentBlockIndex++; |
| 148 | 439 | | } |
| | 440 | |
|
| | 441 | | /// <summary> |
| | 442 | | /// Write an archive record to the archive, where the record may be |
| | 443 | | /// inside of a larger array buffer. The buffer must be "offset plus |
| | 444 | | /// record size" long. |
| | 445 | | /// </summary> |
| | 446 | | /// <param name="buffer"> |
| | 447 | | /// The buffer containing the record data to write. |
| | 448 | | /// </param> |
| | 449 | | /// <param name="offset"> |
| | 450 | | /// The offset of the record data within buffer. |
| | 451 | | /// </param> |
| | 452 | | public void WriteBlock(byte[] buffer, int offset) |
| | 453 | | { |
| 4042 | 454 | | if (buffer == null) { |
| 0 | 455 | | throw new ArgumentNullException(nameof(buffer)); |
| | 456 | | } |
| | 457 | |
|
| 4042 | 458 | | if (outputStream == null) { |
| 0 | 459 | | throw new TarException("TarBuffer.WriteBlock - no output stream stream defined"); |
| | 460 | | } |
| | 461 | |
|
| 4042 | 462 | | if ((offset < 0) || (offset >= buffer.Length)) { |
| 0 | 463 | | throw new ArgumentOutOfRangeException(nameof(offset)); |
| | 464 | | } |
| | 465 | |
|
| 4042 | 466 | | if ((offset + BlockSize) > buffer.Length) { |
| 0 | 467 | | string errorText = string.Format("TarBuffer.WriteBlock - record has length '{0}' with offset '{1}' which is less |
| 0 | 468 | | buffer.Length, offset, recordSize); |
| 0 | 469 | | throw new TarException(errorText); |
| | 470 | | } |
| | 471 | |
|
| 4042 | 472 | | if (currentBlockIndex >= BlockFactor) { |
| 128 | 473 | | WriteRecord(); |
| | 474 | | } |
| | 475 | |
|
| 4042 | 476 | | Array.Copy(buffer, offset, recordBuffer, (currentBlockIndex * BlockSize), BlockSize); |
| | 477 | |
|
| 4042 | 478 | | currentBlockIndex++; |
| 4042 | 479 | | } |
| | 480 | |
|
| | 481 | | /// <summary> |
| | 482 | | /// Write a TarBuffer record to the archive. |
| | 483 | | /// </summary> |
| | 484 | | void WriteRecord() |
| | 485 | | { |
| 205 | 486 | | if (outputStream == null) { |
| 0 | 487 | | throw new TarException("TarBuffer.WriteRecord no output stream defined"); |
| | 488 | | } |
| | 489 | |
|
| 205 | 490 | | outputStream.Write(recordBuffer, 0, RecordSize); |
| 205 | 491 | | outputStream.Flush(); |
| | 492 | |
|
| 205 | 493 | | currentBlockIndex = 0; |
| 205 | 494 | | currentRecordIndex++; |
| 205 | 495 | | } |
| | 496 | |
|
| | 497 | | /// <summary> |
| | 498 | | /// WriteFinalRecord writes the current record buffer to output any unwritten data is present. |
| | 499 | | /// </summary> |
| | 500 | | /// <remarks>Any trailing bytes are set to zero which is by definition correct behaviour |
| | 501 | | /// for the end of a tar stream.</remarks> |
| | 502 | | void WriteFinalRecord() |
| | 503 | | { |
| 73 | 504 | | if (outputStream == null) { |
| 0 | 505 | | throw new TarException("TarBuffer.WriteFinalRecord no output stream defined"); |
| | 506 | | } |
| | 507 | |
|
| 73 | 508 | | if (currentBlockIndex > 0) { |
| 73 | 509 | | int dataBytes = currentBlockIndex * BlockSize; |
| 73 | 510 | | Array.Clear(recordBuffer, dataBytes, RecordSize - dataBytes); |
| 73 | 511 | | WriteRecord(); |
| | 512 | | } |
| | 513 | |
|
| 73 | 514 | | outputStream.Flush(); |
| 73 | 515 | | } |
| | 516 | |
|
| | 517 | | /// <summary> |
| | 518 | | /// Close the TarBuffer. If this is an output buffer, also flush the |
| | 519 | | /// current block before closing. |
| | 520 | | /// </summary> |
| | 521 | | public void Close() |
| | 522 | | { |
| 78 | 523 | | if (outputStream != null) { |
| 73 | 524 | | WriteFinalRecord(); |
| | 525 | |
|
| 73 | 526 | | if (isStreamOwner_) { |
| 72 | 527 | | outputStream.Close(); |
| | 528 | | } |
| 73 | 529 | | outputStream = null; |
| 78 | 530 | | } else if (inputStream != null) { |
| 5 | 531 | | if (isStreamOwner_) { |
| 4 | 532 | | inputStream.Close(); |
| | 533 | | } |
| 5 | 534 | | inputStream = null; |
| | 535 | | } |
| 5 | 536 | | } |
| | 537 | |
|
| | 538 | | #region Instance Fields |
| | 539 | | Stream inputStream; |
| | 540 | | Stream outputStream; |
| | 541 | |
|
| | 542 | | byte[] recordBuffer; |
| | 543 | | int currentBlockIndex; |
| | 544 | | int currentRecordIndex; |
| | 545 | |
|
| 78 | 546 | | int recordSize = DefaultRecordSize; |
| 78 | 547 | | int blockFactor = DefaultBlockFactor; |
| 78 | 548 | | bool isStreamOwner_ = true; |
| | 549 | | #endregion |
| | 550 | | } |
| | 551 | | } |