| | | 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 | | } |