xenadmin/XenOvfTransport/DiskStream.cs

253 lines
8.9 KiB
C#
Raw Normal View History

/* Copyright (c) Citrix Systems Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Based upon code copyrighted as below.
*/
//
// Copyright (c) 2008-2010, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.IO;
using DiscUtils;
using DiscUtils.Iscsi;
namespace XenOvfTransport
{
// iSCSI disk stream to workaround a bug in DiscUtils.DiskStream.
public class DiskStream : SparseStream
{
private Session _session;
private long _lun;
private long _length;
private long _position;
private int _blockSize;
private bool _canWrite;
private bool _canRead;
public DiskStream(Session session, long lun, FileAccess access)
{
_session = session;
_lun = lun;
LunCapacity capacity = session.GetCapacity(lun);
_blockSize = capacity.BlockSize;
////////////////////////////////////////////////////////////////////////////////////////////////////
// CORRECTION
// SCSI Read Capacity response includes the *last* logical block address (LBA) and NOT
// the logical block count as interpreted by DiscUtils.DiskStream.
// Account for the last block.
_length = (capacity.LogicalBlockCount + 1) * capacity.BlockSize;
////////////////////////////////////////////////////////////////////////////////////////////////////
_canWrite = (access != FileAccess.Read);
_canRead = (access != FileAccess.Write);
}
public override bool CanRead
{
get { return _canRead; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return _canWrite; }
}
public override void Flush()
{
}
public override long Length
{
get { return _length; }
}
public override long Position
{
get
{
return _position;
}
set
{
_position = value;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
if (!CanRead)
{
throw new InvalidOperationException("Attempt to read from read-only stream");
}
int maxToRead = (int)Math.Min(_length - _position, count);
long firstBlock = _position / _blockSize;
////////////////////////////////////////////////////////////////////////////////////////////////////
// CORRECTION
// DiscUtils.Utilities is not accessible.
// long lastBlock = Utilities.Ceil(_position + maxToRead, _blockSize);
long lastBlock = ((_position + maxToRead) + (_blockSize - 1)) / _blockSize;
////////////////////////////////////////////////////////////////////////////////////////////////////
byte[] tempBuffer = new byte[(lastBlock - firstBlock) * _blockSize];
int numRead = _session.Read(_lun, firstBlock, (short)(lastBlock - firstBlock), tempBuffer, 0);
int numCopied = Math.Min(maxToRead, numRead);
Array.Copy(tempBuffer, _position - (firstBlock * _blockSize), buffer, offset, numCopied);
_position += numCopied;
return numCopied;
}
public override long Seek(long offset, SeekOrigin origin)
{
long effectiveOffset = offset;
if (origin == SeekOrigin.Current)
{
effectiveOffset += _position;
}
else if (origin == SeekOrigin.End)
{
effectiveOffset += _length;
}
if (effectiveOffset < 0)
{
throw new IOException("Attempt to move before beginning of disk");
}
else
{
_position = effectiveOffset;
return _position;
}
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
if (!CanWrite)
{
throw new IOException("Attempt to write to read-only stream");
}
if (_position + count > _length)
{
throw new IOException("Attempt to write beyond end of stream");
}
int numWritten = 0;
while (numWritten < count)
{
long block = _position / _blockSize;
uint offsetInBlock = (uint)(_position % _blockSize);
int toWrite = count - numWritten;
// Need to read - we're not handling a full block
if (offsetInBlock != 0 || toWrite < _blockSize)
{
toWrite = (int)Math.Min(toWrite, _blockSize - offsetInBlock);
byte[] blockBuffer = new byte[_blockSize];
int numRead = _session.Read(_lun, block, 1, blockBuffer, 0);
if (numRead != _blockSize)
{
throw new IOException("Incomplete read, received " + numRead + " bytes from 1 block");
}
// Overlay as much data as we have for this block
Array.Copy(buffer, offset + numWritten, blockBuffer, offsetInBlock, toWrite);
// Write the block back
_session.Write(_lun, block, 1, _blockSize, blockBuffer, 0);
}
else
{
// Processing at least one whole block, just write (after making sure to trim any partial sectors from the end)...
short numBlocks = (short)(toWrite / _blockSize);
toWrite = numBlocks * _blockSize;
_session.Write(_lun, block, numBlocks, _blockSize, buffer, offset + numWritten);
}
numWritten += toWrite;
_position += toWrite;
}
}
public override IEnumerable<StreamExtent> Extents
{
get
{
yield return new StreamExtent(0, _length);
}
}
}
}