From 68c0a581ebf5fa464da806109a7cb59d2747601e Mon Sep 17 00:00:00 2001 From: Richard Webb Date: Tue, 9 Jun 2020 19:02:59 +0100 Subject: [PATCH 1/4] Unit tests for using ZipFile to update file entries that have descriptors --- .../Zip/ZipFileHandling.cs | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs index 996b09213..370a7e3be 100644 --- a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs +++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs @@ -1611,5 +1611,88 @@ public void AddFileWithAlternateName() } } } + + /// + /// Test for https://github.com/icsharpcode/SharpZipLib/issues/147, when deleting items in a zip + /// + /// Whether Zip64 should be used in the test archive + [TestCase(UseZip64.On)] + [TestCase(UseZip64.Off)] + [Category("Zip")] + public void TestDescriptorUpdateOnDelete(UseZip64 useZip64) + { + MemoryStream msw = new MemoryStreamWithoutSeek(); + using (ZipOutputStream outStream = new ZipOutputStream(msw)) + { + outStream.UseZip64 = useZip64; + outStream.IsStreamOwner = false; + outStream.PutNextEntry(new ZipEntry("StripedMarlin")); + outStream.WriteByte(89); + + outStream.PutNextEntry(new ZipEntry("StripedMarlin2")); + outStream.WriteByte(91); + } + + var zipData = msw.ToArray(); + Assert.IsTrue(ZipTesting.TestArchive(zipData)); + + using (var memoryStream = new MemoryStream(zipData)) + { + using (var zipFile = new ZipFile(memoryStream, leaveOpen: true)) + { + zipFile.BeginUpdate(); + zipFile.Delete("StripedMarlin"); + zipFile.CommitUpdate(); + } + + memoryStream.Position = 0; + + using (var zipFile = new ZipFile(memoryStream, leaveOpen: true)) + { + Assert.That(zipFile.TestArchive(true), Is.True); + } + } + } + + /// + /// Test for https://github.com/icsharpcode/SharpZipLib/issues/147, when adding items to a zip + /// + /// Whether Zip64 should be used in the test archive + [TestCase(UseZip64.On)] + [TestCase(UseZip64.Off)] + [Category("Zip")] + public void TestDescriptorUpdateOnAdd(UseZip64 useZip64) + { + MemoryStream msw = new MemoryStreamWithoutSeek(); + using (ZipOutputStream outStream = new ZipOutputStream(msw)) + { + outStream.UseZip64 = useZip64; + outStream.IsStreamOwner = false; + outStream.PutNextEntry(new ZipEntry("StripedMarlin")); + outStream.WriteByte(89); + } + + var zipData = msw.ToArray(); + Assert.IsTrue(ZipTesting.TestArchive(zipData)); + + using (var memoryStream = new MemoryStream()) + { + memoryStream.Write(zipData, 0, zipData.Length); + + using (var zipFile = new ZipFile(memoryStream, leaveOpen: true)) + { + zipFile.BeginUpdate(); + zipFile.Add(new StringMemoryDataSource("stripey"), "Zebra"); + zipFile.CommitUpdate(); + } + + memoryStream.Position = 0; + + using (var zipFile = new ZipFile(memoryStream, leaveOpen: true)) + { + Assert.That(zipFile.TestArchive(true), Is.True); + } + } + } } } From 6411e6d050ce0e02b316546b39f5c1d6e43673b2 Mon Sep 17 00:00:00 2001 From: Richard Webb Date: Mon, 22 Jun 2020 22:47:20 +0100 Subject: [PATCH 2/4] fix size calculation in GetDescriptorSize --- src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index 59ba3f950..0b4e31ab3 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -2466,10 +2466,10 @@ private int GetDescriptorSize(ZipUpdate update) int result = 0; if ((update.Entry.Flags & (int)GeneralBitFlags.Descriptor) != 0) { - result = ZipConstants.DataDescriptorSize - 4; + result = ZipConstants.DataDescriptorSize; if (update.Entry.LocalHeaderRequiresZip64) { - result = ZipConstants.Zip64DataDescriptorSize - 4; + result = ZipConstants.Zip64DataDescriptorSize; } } return result; From 01e8135e675fbe3d66783c47f017656fd8c2cec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Tue, 11 Aug 2020 02:09:00 +0200 Subject: [PATCH 3/4] Handle optional descriptor signature when updating --- src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 214 ++++++++++++--------- 1 file changed, 124 insertions(+), 90 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index 0b4e31ab3..1c1ab77ac 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -367,10 +367,9 @@ public string Password } else { + rawPassword_ = value; key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(value)); } - - rawPassword_ = value; } } @@ -534,7 +533,7 @@ public ZipFile(Stream stream, bool leaveOpen) catch { DisposeInternal(true); - throw; + throw; } } else @@ -999,16 +998,19 @@ public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandl if (this[entryIndex].Crc != data.Crc) { status.AddError(); + resultHandler?.Invoke(status, "Descriptor CRC mismatch"); } if (this[entryIndex].CompressedSize != data.CompressedSize) { status.AddError(); + resultHandler?.Invoke(status, "Descriptor compressed size mismatch"); } if (this[entryIndex].Size != data.Size) { status.AddError(); + resultHandler?.Invoke(status, "Descriptor size mismatch"); } } } @@ -2105,7 +2107,7 @@ private void WriteLocalEntryHeader(ZipUpdate update) WriteLEShort(entry.Version); WriteLEShort(entry.Flags); - WriteLEShort((byte)entry.CompressionMethodForHeader); + WriteLEShort((byte)entry.CompressionMethod); WriteLEInt((int)entry.DosTime); if (!entry.HasCrc) @@ -2215,7 +2217,7 @@ private int WriteCentralDirectoryHeader(ZipEntry entry) unchecked { - WriteLEShort((byte)entry.CompressionMethodForHeader); + WriteLEShort((byte)entry.CompressionMethod); WriteLEInt((int)entry.DosTime); WriteLEInt((int)entry.Crc); } @@ -2382,26 +2384,37 @@ private byte[] GetBuffer() private void CopyDescriptorBytes(ZipUpdate update, Stream dest, Stream source) { - int bytesToCopy = GetDescriptorSize(update); + // Don't include the signature size to allow copy without seeking + var bytesToCopy = GetDescriptorSize(update, false); + + // Don't touch the source stream if no descriptor is present + if (bytesToCopy == 0) return; + + var buffer = GetBuffer(); - if (bytesToCopy > 0) + // Copy the first 4 bytes of the descriptor + source.Read(buffer, 0, sizeof(int)); + dest.Write(buffer, 0, sizeof(int)); + + if (BitConverter.ToUInt32(buffer, 0) != ZipConstants.DataDescriptorSignature) { - byte[] buffer = GetBuffer(); + // The initial bytes wasn't the descriptor, reduce the pending byte count + bytesToCopy -= buffer.Length; + } - while (bytesToCopy > 0) - { - int readSize = Math.Min(buffer.Length, bytesToCopy); + while (bytesToCopy > 0) + { + int readSize = Math.Min(buffer.Length, bytesToCopy); - int bytesRead = source.Read(buffer, 0, readSize); - if (bytesRead > 0) - { - dest.Write(buffer, 0, bytesRead); - bytesToCopy -= bytesRead; - } - else - { - throw new ZipException("Unxpected end of stream"); - } + int bytesRead = source.Read(buffer, 0, readSize); + if (bytesRead > 0) + { + dest.Write(buffer, 0, bytesRead); + bytesToCopy -= bytesRead; + } + else + { + throw new ZipException("Unxpected end of stream"); } } } @@ -2460,32 +2473,37 @@ private void CopyBytes(ZipUpdate update, Stream destination, Stream source, /// Get the size of the source descriptor for a . /// /// The update to get the size for. - /// The descriptor size, zero if there isnt one. - private int GetDescriptorSize(ZipUpdate update) + /// Whether to include the signature size + /// The descriptor size, zero if there isn't one. + private int GetDescriptorSize(ZipUpdate update, bool includingSignature) { - int result = 0; - if ((update.Entry.Flags & (int)GeneralBitFlags.Descriptor) != 0) - { - result = ZipConstants.DataDescriptorSize; - if (update.Entry.LocalHeaderRequiresZip64) - { - result = ZipConstants.Zip64DataDescriptorSize; - } - } - return result; + if (!((GeneralBitFlags)update.Entry.Flags).HasFlag(GeneralBitFlags.Descriptor)) + return 0; + + var descriptorWithSignature = update.Entry.LocalHeaderRequiresZip64 + ? ZipConstants.Zip64DataDescriptorSize + : ZipConstants.DataDescriptorSize; + + return includingSignature + ? descriptorWithSignature + : descriptorWithSignature - sizeof(int); } private void CopyDescriptorBytesDirect(ZipUpdate update, Stream stream, ref long destinationPosition, long sourcePosition) { - int bytesToCopy = GetDescriptorSize(update); + var buffer = GetBuffer(); ; + + stream.Position = sourcePosition; + stream.Read(buffer, 0, sizeof(int)); + var sourceHasSignature = BitConverter.ToUInt32(buffer, 0) == ZipConstants.DataDescriptorSignature; + + var bytesToCopy = GetDescriptorSize(update, sourceHasSignature); while (bytesToCopy > 0) { - var readSize = (int)bytesToCopy; - byte[] buffer = GetBuffer(); - stream.Position = sourcePosition; - int bytesRead = stream.Read(buffer, 0, readSize); + + var bytesRead = stream.Read(buffer, 0, bytesToCopy); if (bytesRead > 0) { stream.Position = destinationPosition; @@ -2496,7 +2514,7 @@ private void CopyDescriptorBytesDirect(ZipUpdate update, Stream stream, ref long } else { - throw new ZipException("Unxpected end of stream"); + throw new ZipException("Unexpected end of stream"); } } } @@ -2740,6 +2758,7 @@ private void CopyEntryDirect(ZipFile workFile, ZipUpdate update, ref long destin // Clumsy way of handling retrieving the original name and extra data length for now. // TODO: Stop re-reading name and data length in CopyEntryDirect. + uint nameLength = ReadLEUshort(); uint extraLength = ReadLEUshort(); @@ -2748,14 +2767,29 @@ private void CopyEntryDirect(ZipFile workFile, ZipUpdate update, ref long destin if (skipOver) { if (update.OffsetBasedSize != -1) + { destinationPosition += update.OffsetBasedSize; + } else + { // TODO: Find out why this calculation comes up 4 bytes short on some entries in ODT (Office Document Text) archives. // WinZip produces a warning on these entries: // "caution: value of lrec.csize (compressed size) changed from ..." - destinationPosition += - (sourcePosition - entryDataOffset) + NameLengthOffset + // Header size - update.Entry.CompressedSize + GetDescriptorSize(update); + + // Skip entry header + destinationPosition += (sourcePosition - entryDataOffset) + NameLengthOffset; + + // Skip entry compressed data + destinationPosition += update.Entry.CompressedSize; + + // Seek to end of entry to check for descriptor signature + baseStream_.Seek(destinationPosition, SeekOrigin.Begin); + + var descriptorHasSignature = ReadLEUint() == ZipConstants.DataDescriptorSignature; + + // Skip descriptor and it's signature (if present) + destinationPosition += GetDescriptorSize(update, descriptorHasSignature); + } } else { @@ -3613,9 +3647,9 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) { if (entry.Version >= ZipConstants.VERSION_AES) { - // Issue #471 - accept an empty string as a password, but reject null. + // OnKeysRequired(entry.Name); - if (rawPassword_ == null) + if (HaveKeys == false) { throw new ZipException("No password available for AES encrypted stream"); } @@ -4004,12 +4038,12 @@ public override long Position /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. /// - /// The sum of offset and count is larger than the buffer length. - /// Methods were called after the stream was closed. - /// The stream does not support reading. - /// buffer is null. - /// An I/O error occurs. - /// offset or count is negative. + /// The sum of offset and count is larger than the buffer length. + /// Methods were called after the stream was closed. + /// The stream does not support reading. + /// buffer is null. + /// An I/O error occurs. + /// offset or count is negative. public override int Read(byte[] buffer, int offset, int count) { return 0; @@ -4019,13 +4053,13 @@ public override int Read(byte[] buffer, int offset, int count) /// Sets the position within the current stream. /// /// A byte offset relative to the origin parameter. - /// A value of type indicating the reference point used to obtain the new position. + /// A value of type indicating the reference point used to obtain the new position. /// /// The new position within the current stream. /// - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. public override long Seek(long offset, SeekOrigin origin) { return 0; @@ -4035,9 +4069,9 @@ public override long Seek(long offset, SeekOrigin origin) /// Sets the length of the current stream. /// /// The desired length of the current stream in bytes. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// An I/O error occurs. - /// Methods were called after the stream was closed. + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// An I/O error occurs. + /// Methods were called after the stream was closed. public override void SetLength(long value) { } @@ -4048,12 +4082,12 @@ public override void SetLength(long value) /// An array of bytes. This method copies count bytes from buffer to the current stream. /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. /// The number of bytes to be written to the current stream. - /// An I/O error occurs. - /// The stream does not support writing. - /// Methods were called after the stream was closed. - /// buffer is null. - /// The sum of offset and count is greater than the buffer length. - /// offset or count is negative. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + /// buffer is null. + /// The sum of offset and count is greater than the buffer length. + /// offset or count is negative. public override void Write(byte[] buffer, int offset, int count) { baseStream_.Write(buffer, offset, count); @@ -4133,12 +4167,12 @@ public override int ReadByte() /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. /// - /// The sum of offset and count is larger than the buffer length. - /// Methods were called after the stream was closed. - /// The stream does not support reading. - /// buffer is null. - /// An I/O error occurs. - /// offset or count is negative. + /// The sum of offset and count is larger than the buffer length. + /// Methods were called after the stream was closed. + /// The stream does not support reading. + /// buffer is null. + /// An I/O error occurs. + /// offset or count is negative. public override int Read(byte[] buffer, int offset, int count) { lock (baseStream_) @@ -4172,12 +4206,12 @@ public override int Read(byte[] buffer, int offset, int count) /// An array of bytes. This method copies count bytes from buffer to the current stream. /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. /// The number of bytes to be written to the current stream. - /// An I/O error occurs. - /// The stream does not support writing. - /// Methods were called after the stream was closed. - /// buffer is null. - /// The sum of offset and count is greater than the buffer length. - /// offset or count is negative. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + /// buffer is null. + /// The sum of offset and count is greater than the buffer length. + /// offset or count is negative. public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); @@ -4187,9 +4221,9 @@ public override void Write(byte[] buffer, int offset, int count) /// When overridden in a derived class, sets the length of the current stream. /// /// The desired length of the current stream in bytes. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// An I/O error occurs. - /// Methods were called after the stream was closed. + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// An I/O error occurs. + /// Methods were called after the stream was closed. public override void SetLength(long value) { throw new NotSupportedException(); @@ -4199,13 +4233,13 @@ public override void SetLength(long value) /// When overridden in a derived class, sets the position within the current stream. /// /// A byte offset relative to the origin parameter. - /// A value of type indicating the reference point used to obtain the new position. + /// A value of type indicating the reference point used to obtain the new position. /// /// The new position within the current stream. /// - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. public override long Seek(long offset, SeekOrigin origin) { long newPos = readPos_; @@ -4230,7 +4264,7 @@ public override long Seek(long offset, SeekOrigin origin) throw new ArgumentException("Negative position is invalid"); } - if (newPos > end_) + if (newPos >= end_) { throw new IOException("Cannot seek past end"); } @@ -4241,7 +4275,7 @@ public override long Seek(long offset, SeekOrigin origin) /// /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// - /// An I/O error occurs. + /// An I/O error occurs. public override void Flush() { // Nothing to do. @@ -4252,9 +4286,9 @@ public override void Flush() /// /// /// The current position within the stream. - /// An I/O error occurs. - /// The stream does not support seeking. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support seeking. + /// Methods were called after the stream was closed. public override long Position { get { return readPos_ - start_; } @@ -4267,7 +4301,7 @@ public override long Position throw new ArgumentException("Negative position is invalid"); } - if (newPos > end_) + if (newPos >= end_) { throw new InvalidOperationException("Cannot seek past end"); } @@ -4280,8 +4314,8 @@ public override long Position /// /// /// A long value representing the length of the stream in bytes. - /// A class derived from Stream does not support seeking. - /// Methods were called after the stream was closed. + /// A class derived from Stream does not support seeking. + /// Methods were called after the stream was closed. public override long Length { get { return length_; } From 0fd6396551ffaa61f4802a57872b8c04827bb9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nils=20m=C3=A5s=C3=A9n?= Date: Tue, 11 Aug 2020 02:20:27 +0200 Subject: [PATCH 4/4] Handle optional descriptor signature when updating Without reverting unrelated code this time hopefully --- src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs | 111 ++++++++++----------- 1 file changed, 53 insertions(+), 58 deletions(-) diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs index 1c1ab77ac..9c9f433ce 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs @@ -367,9 +367,10 @@ public string Password } else { - rawPassword_ = value; key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(value)); } + + rawPassword_ = value; } } @@ -533,7 +534,7 @@ public ZipFile(Stream stream, bool leaveOpen) catch { DisposeInternal(true); - throw; + throw; } } else @@ -1919,11 +1920,9 @@ public void Modify(ZipEntry original, ZipEntry updated) if ( original == null ) { throw new ArgumentNullException("original"); } - if ( updated == null ) { throw new ArgumentNullException("updated"); } - CheckUpdating(); contentsEdited_ = true; updates_.Add(new ZipUpdate(original, updated)); @@ -2107,7 +2106,7 @@ private void WriteLocalEntryHeader(ZipUpdate update) WriteLEShort(entry.Version); WriteLEShort(entry.Flags); - WriteLEShort((byte)entry.CompressionMethod); + WriteLEShort((byte)entry.CompressionMethodForHeader); WriteLEInt((int)entry.DosTime); if (!entry.HasCrc) @@ -2217,7 +2216,7 @@ private int WriteCentralDirectoryHeader(ZipEntry entry) unchecked { - WriteLEShort((byte)entry.CompressionMethod); + WriteLEShort((byte)entry.CompressionMethodForHeader); WriteLEInt((int)entry.DosTime); WriteLEInt((int)entry.Crc); } @@ -2772,10 +2771,6 @@ private void CopyEntryDirect(ZipFile workFile, ZipUpdate update, ref long destin } else { - // TODO: Find out why this calculation comes up 4 bytes short on some entries in ODT (Office Document Text) archives. - // WinZip produces a warning on these entries: - // "caution: value of lrec.csize (compressed size) changed from ..." - // Skip entry header destinationPosition += (sourcePosition - entryDataOffset) + NameLengthOffset; @@ -3647,9 +3642,9 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry) { if (entry.Version >= ZipConstants.VERSION_AES) { - // + // Issue #471 - accept an empty string as a password, but reject null. OnKeysRequired(entry.Name); - if (HaveKeys == false) + if (rawPassword_ == null) { throw new ZipException("No password available for AES encrypted stream"); } @@ -4038,12 +4033,12 @@ public override long Position /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. /// - /// The sum of offset and count is larger than the buffer length. - /// Methods were called after the stream was closed. - /// The stream does not support reading. - /// buffer is null. - /// An I/O error occurs. - /// offset or count is negative. + /// The sum of offset and count is larger than the buffer length. + /// Methods were called after the stream was closed. + /// The stream does not support reading. + /// buffer is null. + /// An I/O error occurs. + /// offset or count is negative. public override int Read(byte[] buffer, int offset, int count) { return 0; @@ -4053,13 +4048,13 @@ public override int Read(byte[] buffer, int offset, int count) /// Sets the position within the current stream. /// /// A byte offset relative to the origin parameter. - /// A value of type indicating the reference point used to obtain the new position. + /// A value of type indicating the reference point used to obtain the new position. /// /// The new position within the current stream. /// - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. public override long Seek(long offset, SeekOrigin origin) { return 0; @@ -4069,9 +4064,9 @@ public override long Seek(long offset, SeekOrigin origin) /// Sets the length of the current stream. /// /// The desired length of the current stream in bytes. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// An I/O error occurs. - /// Methods were called after the stream was closed. + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// An I/O error occurs. + /// Methods were called after the stream was closed. public override void SetLength(long value) { } @@ -4082,12 +4077,12 @@ public override void SetLength(long value) /// An array of bytes. This method copies count bytes from buffer to the current stream. /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. /// The number of bytes to be written to the current stream. - /// An I/O error occurs. - /// The stream does not support writing. - /// Methods were called after the stream was closed. - /// buffer is null. - /// The sum of offset and count is greater than the buffer length. - /// offset or count is negative. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + /// buffer is null. + /// The sum of offset and count is greater than the buffer length. + /// offset or count is negative. public override void Write(byte[] buffer, int offset, int count) { baseStream_.Write(buffer, offset, count); @@ -4167,12 +4162,12 @@ public override int ReadByte() /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. /// - /// The sum of offset and count is larger than the buffer length. - /// Methods were called after the stream was closed. - /// The stream does not support reading. - /// buffer is null. - /// An I/O error occurs. - /// offset or count is negative. + /// The sum of offset and count is larger than the buffer length. + /// Methods were called after the stream was closed. + /// The stream does not support reading. + /// buffer is null. + /// An I/O error occurs. + /// offset or count is negative. public override int Read(byte[] buffer, int offset, int count) { lock (baseStream_) @@ -4206,12 +4201,12 @@ public override int Read(byte[] buffer, int offset, int count) /// An array of bytes. This method copies count bytes from buffer to the current stream. /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. /// The number of bytes to be written to the current stream. - /// An I/O error occurs. - /// The stream does not support writing. - /// Methods were called after the stream was closed. - /// buffer is null. - /// The sum of offset and count is greater than the buffer length. - /// offset or count is negative. + /// An I/O error occurs. + /// The stream does not support writing. + /// Methods were called after the stream was closed. + /// buffer is null. + /// The sum of offset and count is greater than the buffer length. + /// offset or count is negative. public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); @@ -4221,9 +4216,9 @@ public override void Write(byte[] buffer, int offset, int count) /// When overridden in a derived class, sets the length of the current stream. /// /// The desired length of the current stream in bytes. - /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. - /// An I/O error occurs. - /// Methods were called after the stream was closed. + /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. + /// An I/O error occurs. + /// Methods were called after the stream was closed. public override void SetLength(long value) { throw new NotSupportedException(); @@ -4233,13 +4228,13 @@ public override void SetLength(long value) /// When overridden in a derived class, sets the position within the current stream. /// /// A byte offset relative to the origin parameter. - /// A value of type indicating the reference point used to obtain the new position. + /// A value of type indicating the reference point used to obtain the new position. /// /// The new position within the current stream. /// - /// An I/O error occurs. - /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output. + /// Methods were called after the stream was closed. public override long Seek(long offset, SeekOrigin origin) { long newPos = readPos_; @@ -4264,7 +4259,7 @@ public override long Seek(long offset, SeekOrigin origin) throw new ArgumentException("Negative position is invalid"); } - if (newPos >= end_) + if (newPos > end_) { throw new IOException("Cannot seek past end"); } @@ -4275,7 +4270,7 @@ public override long Seek(long offset, SeekOrigin origin) /// /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. /// - /// An I/O error occurs. + /// An I/O error occurs. public override void Flush() { // Nothing to do. @@ -4286,9 +4281,9 @@ public override void Flush() /// /// /// The current position within the stream. - /// An I/O error occurs. - /// The stream does not support seeking. - /// Methods were called after the stream was closed. + /// An I/O error occurs. + /// The stream does not support seeking. + /// Methods were called after the stream was closed. public override long Position { get { return readPos_ - start_; } @@ -4301,7 +4296,7 @@ public override long Position throw new ArgumentException("Negative position is invalid"); } - if (newPos >= end_) + if (newPos > end_) { throw new InvalidOperationException("Cannot seek past end"); } @@ -4314,8 +4309,8 @@ public override long Position /// /// /// A long value representing the length of the stream in bytes. - /// A class derived from Stream does not support seeking. - /// Methods were called after the stream was closed. + /// A class derived from Stream does not support seeking. + /// Methods were called after the stream was closed. public override long Length { get { return length_; }