/// <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(nameof(buffer)); } if (outputStream == null) { throw TarException.UnKnown("TarBuffer.WriteBlock - no output stream stream defined"); } if ((offset < 0) || (offset >= buffer.Length)) { throw new ArgumentOutOfRangeException(nameof(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 TarException.UnKnown(errorText); } if (currentBlockIndex >= BlockFactor) { WriteRecord(); } Array.Copy(buffer, offset, recordBuffer, (currentBlockIndex * BlockSize), BlockSize); currentBlockIndex++; }
/// <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(nameof(block)); } if (outputStream == null) { throw TarException.UnKnown("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 TarException.UnKnown(errorText); } if (currentBlockIndex >= BlockFactor) { WriteRecord(); } Array.Copy(block, 0, recordBuffer, (currentBlockIndex * BlockSize), BlockSize); currentBlockIndex++; }
/// <summary> /// Write a TarBuffer record to the archive. /// </summary> void WriteRecord() { if (outputStream == null) { throw TarException.UnKnown("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 TarException.UnKnown("TarBuffer.WriteFinalRecord no output stream defined"); } if (currentBlockIndex > 0) { int dataBytes = currentBlockIndex * BlockSize; Array.Clear(recordBuffer, dataBytes, RecordSize - dataBytes); WriteRecord(); } outputStream.Flush(); }
/// <summary> /// Skip over a block on the input stream. /// </summary> public void SkipBlock() { if (inputStream == null) { throw TarException.UnKnown("no input stream defined"); } if (currentBlockIndex >= BlockFactor) { if (!ReadRecord()) { throw TarException.UnKnown("Failed to read a record"); } } currentBlockIndex++; }
/// <summary> /// Read a record from data stream. /// </summary> /// <returns> /// false if End-Of-File, else true. /// </returns> bool ReadRecord() { if (inputStream == null) { throw TarException.UnKnown("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 '*****@*****.**' for this fix. // if (numBytes <= 0) { break; } offset += (int)numBytes; bytesNeeded -= (int)numBytes; } currentRecordIndex++; return(true); }
/// <summary> /// Read a block from the input stream. /// </summary> /// <returns> /// The block of data read. /// </returns> public byte[] ReadBlock() { if (inputStream == null) { throw TarException.UnKnown("TarBuffer.ReadBlock - no input stream defined"); } if (currentBlockIndex >= BlockFactor) { if (!ReadRecord()) { throw TarException.UnKnown("Failed to read a record"); } } byte[] result = new byte[BlockSize]; Array.Copy(recordBuffer, (currentBlockIndex * BlockSize), result, 0, BlockSize); currentBlockIndex++; return(result); }
/// <summary> /// Get the next entry in this tar archive. This will skip /// over any remaining data in the current entry, if there /// is one, and place the input stream at the header of the /// next entry, and read the header and instantiate a new /// TarEntry from the header bytes and return that entry. /// If there are no more entries in the archive, null will /// be returned to indicate that the end of the archive has /// been reached. /// </summary> /// <returns> /// The next TarEntry in the archive, or null. /// </returns> public TarEntry GetNextEntry() { if (hasHitEOF) { return(null); } if (currentEntry != null) { SkipToNextEntry(); } byte[] headerBuf = tarBuffer.ReadBlock(); if (headerBuf == null) { hasHitEOF = true; } else { hasHitEOF |= TarBuffer.IsEndOfArchiveBlock(headerBuf); } if (hasHitEOF) { currentEntry = null; } else { try { var header = new TarHeader(); header.ParseBuffer(headerBuf); if (!header.IsChecksumValid) { throw TarException.InvalidHeader("Header checksum is invalid"); } this.entryOffset = 0; this.entrySize = header.Size; StringBuilder longName = null; if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME) { byte[] nameBuffer = new byte[TarBuffer.BlockSize]; long numToRead = this.entrySize; longName = new StringBuilder(); while (numToRead > 0) { int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead)); if (numRead == -1) { throw TarException.UnKnown("Failed to read long name entry"); } longName.Append(TarHeader.ParseName(nameBuffer, 0, numRead).ToString()); numToRead -= numRead; } SkipToNextEntry(); headerBuf = this.tarBuffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_GHDR) { // POSIX global extended header // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = this.tarBuffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_XHDR) { // POSIX extended header // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = this.tarBuffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR) { // TODO: could show volume name when verbose SkipToNextEntry(); headerBuf = this.tarBuffer.ReadBlock(); } else if (header.TypeFlag != TarHeader.LF_NORMAL && header.TypeFlag != TarHeader.LF_OLDNORM && header.TypeFlag != TarHeader.LF_LINK && header.TypeFlag != TarHeader.LF_SYMLINK && header.TypeFlag != TarHeader.LF_DIR) { // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = tarBuffer.ReadBlock(); } if (entryFactory == null) { currentEntry = new TarEntry(headerBuf); if (longName != null) { currentEntry.Name = longName.ToString(); } } else { currentEntry = entryFactory.CreateEntry(headerBuf); } // Magic was checked here for 'ustar' but there are multiple valid possibilities // so this is not done anymore. entryOffset = 0; // TODO: Review How do we resolve this discrepancy?! entrySize = this.currentEntry.Size; } catch (TarException ex) { entrySize = 0; entryOffset = 0; currentEntry = null; string errorText = string.Format("Bad header in record {0} block {1} {2}", tarBuffer.CurrentRecord, tarBuffer.CurrentBlock, ex.Message); throw TarException.UnKnown(errorText); } } return(currentEntry); }