private void WriteAnyUnwrittenData()
 {
     if (currentWritingBlock != null)
     {
         underlyingStream.WriteBlock(currentWritingBlock, currentWritingBlockUsage);
         currentWritingBlock = null;
         currentReadingBlock = null;
     }
 }
        public override int Read(byte[] buffer, int bufferOffset, int count)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }
            if (bufferOffset + count > buffer.LongLength)
            {
                throw new ArgumentOutOfRangeException("bufferOffset");
            }

            if (count == 0)
            {
                return(0);
            }

            lock (locker)
            {
                // If the stream is used for both reading and writing, make sure we're reading everything that was written
                WriteAnyUnwrittenData();

                if (Position >= underlyingStream.Footer.TotalLength)
                {
                    return(0);
                }

                long startingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position);
                long blockOffset   = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Position);

                if (currentReadingBlock == null || currentReadingBlock.BlockNumber != startingBlock)
                {
                    currentReadingBlock = underlyingStream.ReadBlock(startingBlock);
                }

                int blockRead  = (int)Math.Min(currentReadingBlock.TotalStreamLength - Position, currentBlockSize - bufferOffset);
                int actualRead = Math.Min(count, blockRead);
                Array.Copy(currentReadingBlock.Data, blockOffset, buffer, bufferOffset, actualRead);
                // We use the fact that a stream doesn't have to read all data in one go to avoid a loop here.

                Position += actualRead;
                return(actualRead);
            }
        }
Exemple #3
0
        /// <summary>
        /// Writes a block to its correct place in the file.
        /// The block's TotalStreamLength is saved as the total length of the stream IF AND ONLY IF the written block is the last block in the stream.
        /// </summary>
        public void WriteBlock(EncryptedFile.Block block, long size)
        {
            if (isReadonly)
            {
                throw new InvalidOperationException("The current stream is read-only.");
            }

            lock (locker)
            {
                if (block == null)
                {
                    throw new ArgumentNullException("block");
                }
                if (block.BlockNumber < 0)
                {
                    throw new ArgumentOutOfRangeException("block", "Block number be non negative.");
                }
                if (block.Data == null || block.Data.Length != header.DecryptedBlockSize)
                {
                    throw new ArgumentException("Block must have data with length == " + header.DecryptedBlockSize);
                }

                long position = header.GetPhysicalPositionFromBlockNumber(block.BlockNumber);
                if (stream.Length - EncryptedFile.Footer.FooterSize < position)
                {
                    throw new InvalidOperationException("Write past end of file.");
                }

                stream.Position = position;
                var encrypted = settings.Codec.EncodeBlock(key, block.Data);

                Debug.Assert(encrypted.Data.Length == header.EncryptedBlockSize);

                stream.Write(encrypted.IV, 0, encrypted.IV.Length);
                stream.Write(encrypted.Data, 0, encrypted.Data.Length);
                totalUnencryptedSize += size;

                if (stream.Length <= stream.Position + EncryptedFile.Footer.FooterSize)
                {
                    footer.TotalLength = block.TotalEncryptedStreamLength;
                    WriteFooterInCurrentPosition(footer);
                }
            }
        }
		public override int Read(byte[] buffer, int bufferOffset, int count)
		{
			//precaution, should never be true
			if (underlyingStream.Header.MagicNumber != EncryptedFile.WithTotalSizeMagicNumber &&
				underlyingStream.Header.MagicNumber != EncryptedFile.DefaultMagicNumber)
				throw new ApplicationException("Invalid magic number in the encrypted file. Cannot proceed with reading.");

			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (count < 0)
				throw new ArgumentOutOfRangeException("count");
			if (bufferOffset + count > buffer.LongLength)
				throw new ArgumentOutOfRangeException("bufferOffset");

			if (count == 0)
				return 0;

			lock (locker)
			{
				// If the stream is used for both reading and writing, make sure we're reading everything that was written
				WriteAnyUnwrittenData();

				if (Position >= underlyingStream.Footer.TotalLength &&
					underlyingStream.Header.MagicNumber == EncryptedFile.DefaultMagicNumber)
					return 0;

				if (Position >= underlyingStream.Header.TotalUnencryptedSize && 
					underlyingStream.Header.MagicNumber == EncryptedFile.WithTotalSizeMagicNumber)
					return 0;
	
				if (underlyingStream.Header.MagicNumber != EncryptedFile.WithTotalSizeMagicNumber && 
					underlyingStream.Header.MagicNumber != EncryptedFile.DefaultMagicNumber)
					throw new ApplicationException("Invalid magic number in the encrypted file. Cannot proceed with reading.");

				var startingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position);
				var blockOffset = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Position);

				if (currentReadingBlock == null || currentReadingBlock.BlockNumber != startingBlock)
				{
					currentReadingBlock = underlyingStream.ReadBlock(startingBlock);
				}

				int blockRead;
				if (underlyingStream.Header.MagicNumber == EncryptedFile.DefaultMagicNumber)
				{
					blockRead = (int) Math.Min(currentReadingBlock.TotalEncryptedStreamLength - Position, currentBlockSize - blockOffset);
				}
				else
				{
					blockRead = (int) Math.Min(underlyingStream.Header.TotalUnencryptedSize - Position, currentBlockSize - blockOffset);
				}
				
				var actualRead = Math.Min(count, blockRead);
				Array.Copy(currentReadingBlock.Data, blockOffset, buffer, bufferOffset, actualRead);
				// We use the fact that a stream doesn't have to read all data in one go to avoid a loop here.

				Position += actualRead;
				return actualRead;
			}
		}
		private void WriteAnyUnwrittenData()
		{
			if (currentWritingBlock != null)
			{
				underlyingStream.WriteBlock(currentWritingBlock, currentWritingBlockUsage);
				currentWritingBlock = null;
				currentReadingBlock = null;
			}
		}
		public override void Write(byte[] buffer, int bufferOffset, int count)
		{
			if (isReadonly)
				throw new InvalidOperationException("The current stream is read-only.");

			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (count < 0)
				throw new ArgumentOutOfRangeException("count");
			if (bufferOffset + count > buffer.LongLength)
				throw new ArgumentOutOfRangeException("bufferOffset");

			if (count == 0)
				return;

			lock (locker)
			{
				while (true)
				{
					long startingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position);
					long endingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position + count - 1);

					long blockOffset = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Position);

					if (currentWritingBlock == null || currentWritingBlock.BlockNumber != startingBlock)
						// If we're writing into a different block than the one that's currently in memory
					{
						WriteAnyUnwrittenData();

						if (blockOffset != 0 || count < currentBlockSize)
						{
							// Read the existing block from the underlying stream, as we're only changing part of it
							currentWritingBlock = underlyingStream.ReadBlock(startingBlock);
						    var lastBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Length);
						    if (lastBlock == startingBlock)
						    {
						        // this is the last block, it may not be a full one, so we need to make sure that it
                                // the actual size reflect that
						        currentWritingBlockUsage = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Length);
						    }
						    else
						    {
						        currentWritingBlockUsage = currentWritingBlock.Data.Length;// full size
						    }
						}
						else
						{
							// We're writing the entire block in one go
							currentWritingBlock = new EncryptedFile.Block
							{
								BlockNumber = startingBlock,
								Data = new byte[currentBlockSize],
								TotalEncryptedStreamLength = underlyingStream.Footer.TotalLength
							};
						    currentWritingBlockUsage = 0;
						}
					}
                  
                    if (startingBlock == endingBlock)
						// If the entire write is done to the same block
					{
                        currentWritingBlockUsage = Math.Max(currentWritingBlockUsage, blockOffset + count);
                        Array.Copy(buffer, bufferOffset, currentWritingBlock.Data, blockOffset, count);
						Position += count;
						break;
					}
                    var countInCurrentBlock = currentBlockSize - (int)blockOffset;
                    currentWritingBlockUsage = Math.Max(currentWritingBlockUsage, blockOffset + countInCurrentBlock);
					
					Array.Copy(buffer, bufferOffset, currentWritingBlock.Data, blockOffset, countInCurrentBlock);
					Position += countInCurrentBlock;

					// Write the next block from the same buffer
					bufferOffset += countInCurrentBlock;
					count -= countInCurrentBlock;
				}
			}
		}
        public override int Read(byte[] buffer, int bufferOffset, int count)
        {
            //precaution, should never be true
            if (underlyingStream.Header.MagicNumber != EncryptedFile.WithTotalSizeMagicNumber &&
                underlyingStream.Header.MagicNumber != EncryptedFile.DefaultMagicNumber)
            {
                throw new ApplicationException("Invalid magic number in the encrypted file. Cannot proceed with reading.");
            }

            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }
            if (bufferOffset + count > buffer.LongLength)
            {
                throw new ArgumentOutOfRangeException("bufferOffset");
            }

            if (count == 0)
            {
                return(0);
            }

            lock (locker)
            {
                // If the stream is used for both reading and writing, make sure we're reading everything that was written
                WriteAnyUnwrittenData();

                if (Position >= underlyingStream.Footer.TotalLength &&
                    underlyingStream.Header.MagicNumber == EncryptedFile.DefaultMagicNumber)
                {
                    return(0);
                }

                if (Position >= underlyingStream.Header.TotalUnencryptedSize &&
                    underlyingStream.Header.MagicNumber == EncryptedFile.WithTotalSizeMagicNumber)
                {
                    return(0);
                }

                if (underlyingStream.Header.MagicNumber != EncryptedFile.WithTotalSizeMagicNumber &&
                    underlyingStream.Header.MagicNumber != EncryptedFile.DefaultMagicNumber)
                {
                    throw new ApplicationException("Invalid magic number in the encrypted file. Cannot proceed with reading.");
                }

                var startingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position);
                var blockOffset   = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Position);

                if (currentReadingBlock == null || currentReadingBlock.BlockNumber != startingBlock)
                {
                    currentReadingBlock = underlyingStream.ReadBlock(startingBlock);
                }

                var blockRead  = (int)Math.Min(underlyingStream.Header.TotalUnencryptedSize - Position, currentBlockSize - blockOffset);
                var actualRead = Math.Min(count, blockRead);
                Array.Copy(currentReadingBlock.Data, blockOffset, buffer, bufferOffset, actualRead);
                // We use the fact that a stream doesn't have to read all data in one go to avoid a loop here.

                Position += actualRead;
                return(actualRead);
            }
        }
        public override void Write(byte[] buffer, int bufferOffset, int count)
        {
            if (isReadonly)
            {
                throw new InvalidOperationException("The current stream is read-only.");
            }

            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }
            if (bufferOffset + count > buffer.LongLength)
            {
                throw new ArgumentOutOfRangeException("bufferOffset");
            }

            if (count == 0)
            {
                return;
            }

            lock (locker)
            {
                while (true)
                {
                    long startingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position);
                    long endingBlock   = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position + count - 1);

                    long blockOffset = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Position);

                    if (currentWritingBlock == null || currentWritingBlock.BlockNumber != startingBlock)
                    // If we're writing into a different block than the one that's currently in memory
                    {
                        WriteAnyUnwrittenData();

                        if (blockOffset != 0 || count < currentBlockSize)
                        {
                            // Read the existing block from the underlying stream, as we're only changing part of it
                            currentWritingBlock = underlyingStream.ReadBlock(startingBlock);
                            var lastBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Length);
                            if (lastBlock == startingBlock)
                            {
                                // this is the last block, it may not be a full one, so we need to make sure that it
                                // the actual size reflect that
                                currentWritingBlockUsage = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Length);
                            }
                            else
                            {
                                currentWritingBlockUsage = currentWritingBlock.Data.Length;                        // full size
                            }
                        }
                        else
                        {
                            // We're writing the entire block in one go
                            currentWritingBlock = new EncryptedFile.Block
                            {
                                BlockNumber = startingBlock,
                                Data        = new byte[currentBlockSize],
                                TotalEncryptedStreamLength = underlyingStream.Footer.TotalLength
                            };
                            currentWritingBlockUsage = 0;
                        }
                    }

                    if (startingBlock == endingBlock)
                    // If the entire write is done to the same block
                    {
                        currentWritingBlockUsage = Math.Max(currentWritingBlockUsage, blockOffset + count);
                        Array.Copy(buffer, bufferOffset, currentWritingBlock.Data, blockOffset, count);
                        Position += count;
                        break;
                    }
                    var countInCurrentBlock = currentBlockSize - (int)blockOffset;
                    currentWritingBlockUsage = Math.Max(currentWritingBlockUsage, blockOffset + countInCurrentBlock);

                    Array.Copy(buffer, bufferOffset, currentWritingBlock.Data, blockOffset, countInCurrentBlock);
                    Position += countInCurrentBlock;

                    // Write the next block from the same buffer
                    bufferOffset += countInCurrentBlock;
                    count        -= countInCurrentBlock;
                }
            }
        }
        public override void Write(byte[] buffer, int bufferOffset, int count)
        {
            if (isReadonly)
            {
                throw new InvalidOperationException("The current stream is read-only.");
            }

            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }
            if (bufferOffset + count > buffer.LongLength)
            {
                throw new ArgumentOutOfRangeException("bufferOffset");
            }

            if (count == 0)
            {
                return;
            }

            lock (locker)
            {
writeStart:

                long startingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position);
                long endingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position + count - 1);

                long blockOffset = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Position);

                if (currentWritingBlock == null || currentWritingBlock.BlockNumber != startingBlock)
                // If we're writing into a different block than the one that's currently in memory
                {
                    WriteAnyUnwrittenData();

                    if (blockOffset != 0 || count < currentBlockSize)
                    {
                        // Read the existing block from the underlying stream, as we're only changing part of it
                        currentWritingBlock = underlyingStream.ReadBlock(startingBlock);
                    }
                    else
                    {
                        // We're writing the entire block in one go
                        currentWritingBlock = new EncryptedFile.Block
                        {
                            BlockNumber       = startingBlock,
                            Data              = new byte[currentBlockSize],
                            TotalStreamLength = underlyingStream.Footer.TotalLength
                        };
                    }
                }

                if (startingBlock == endingBlock)
                // If the entire write is done to the same block
                {
                    Array.Copy(buffer, bufferOffset, currentWritingBlock.Data, blockOffset, count);
                    Position += count;
                }
                else
                {
                    var countInCurrentBlock = currentBlockSize - bufferOffset;
                    Array.Copy(buffer, bufferOffset, currentWritingBlock.Data, blockOffset, countInCurrentBlock);
                    Position += countInCurrentBlock;

                    // Write the next block from the same buffer
                    bufferOffset += countInCurrentBlock;
                    count        -= countInCurrentBlock;
                    goto writeStart;
                }
            }
        }
		public override int Read(byte[] buffer, int bufferOffset, int count)
		{
			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (count < 0)
				throw new ArgumentOutOfRangeException("count");
			if (bufferOffset + count > buffer.LongLength)
				throw new ArgumentOutOfRangeException("bufferOffset");

			if (count == 0)
				return 0;

			lock (locker)
			{
				// If the stream is used for both reading and writing, make sure we're reading everything that was written
				WriteAnyUnwrittenData();

				if (Position >= underlyingStream.Footer.TotalLength)
					return 0;

				long startingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position);
				long blockOffset = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Position);

				if (currentReadingBlock == null || currentReadingBlock.BlockNumber != startingBlock)
				{
					currentReadingBlock = underlyingStream.ReadBlock(startingBlock);
				}

				int blockRead = (int)Math.Min(currentReadingBlock.TotalStreamLength - Position, currentBlockSize - blockOffset);
				int actualRead = Math.Min(count, blockRead);
				Array.Copy(currentReadingBlock.Data, blockOffset, buffer, bufferOffset, actualRead);
				// We use the fact that a stream doesn't have to read all data in one go to avoid a loop here.

				Position += actualRead;
				return actualRead;
			}
		}
		public override void Write(byte[] buffer, int bufferOffset, int count)
		{
			if (isReadonly)
				throw new InvalidOperationException("The current stream is read-only.");

			if (buffer == null)
				throw new ArgumentNullException("buffer");
			if (count < 0)
				throw new ArgumentOutOfRangeException("count");
			if (bufferOffset + count > buffer.LongLength)
				throw new ArgumentOutOfRangeException("bufferOffset");

			if (count == 0)
				return;

			lock (locker)
			{
			writeStart:

				long startingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position);
				long endingBlock = underlyingStream.Header.GetBlockNumberFromLogicalPosition(Position + count - 1);

				long blockOffset = underlyingStream.Header.GetBlockOffsetFromLogicalPosition(Position);

				if (currentWritingBlock == null || currentWritingBlock.BlockNumber != startingBlock)
				// If we're writing into a different block than the one that's currently in memory
				{
					WriteAnyUnwrittenData();

					if (blockOffset != 0 || count < currentBlockSize)
					{
						// Read the existing block from the underlying stream, as we're only changing part of it
						currentWritingBlock = underlyingStream.ReadBlock(startingBlock);
					}
					else
					{
						// We're writing the entire block in one go
						currentWritingBlock = new EncryptedFile.Block
						{
							BlockNumber = startingBlock,
							Data = new byte[currentBlockSize],
							TotalStreamLength = underlyingStream.Footer.TotalLength
						};
					}
				}

				if (startingBlock == endingBlock)
				// If the entire write is done to the same block
				{
					Array.Copy(buffer, bufferOffset, currentWritingBlock.Data, blockOffset, count);
					Position += count;
				}
				else
				{
					var countInCurrentBlock = currentBlockSize - bufferOffset;
					Array.Copy(buffer, bufferOffset, currentWritingBlock.Data, blockOffset, countInCurrentBlock);
					Position += countInCurrentBlock;

					// Write the next block from the same buffer
					bufferOffset += countInCurrentBlock;
					count -= countInCurrentBlock;
					goto writeStart;
				}
			}
		}