// Copyright (C) 2010-2012, XFX Team // 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. // * Neither the name of the copyright holder nor the names of any // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // 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 OWNER 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. #include #include extern "C" { #include } namespace System { namespace IO { bool FileStream::CanRead() { return _access == FileAccess::Read || _access == FileAccess::ReadWrite; } bool FileStream::CanSeek() { return canSeek; } bool FileStream::CanWrite() { return _access == FileAccess::Write || _access == FileAccess::ReadWrite; } Int64 FileStream::Length() { if(handle == -1) throw ObjectDisposedException("FileStream", "The stream has been closed."); if(!canSeek) throw NotSupportedException("The stream does not support seeking."); Int64 length; if(XGetFileSize(handle, (unsigned int *)length) != STATUS_SUCCESS) throw IOException("Could not determine file size. The file may be corrupt."); if ((_writePos > 0) && ((_pos + _writePos) > length)) { length = _writePos + _pos; } return length; } Int64 FileStream::Position() { if (handle == -1) throw ObjectDisposedException("FileStream", "The stream has been closed."); if (!canSeek) throw NotSupportedException("The stream does not support seeking."); return (_pos + ((_readPos - _readLen) + _writePos)); } void FileStream::Position(Int64 newPosition) { if(canSeek == false) throw NotSupportedException("The stream does not support seeking"); if(newPosition < 0) throw ArgumentOutOfRangeException("newPosition", "Attempt to set the position to a negative value."); Seek(newPosition, SeekOrigin::Begin); } FileStream::FileStream() { handle = -1; } FileStream::FileStream(char* path, FileMode_t mode) { if(path == null || path == "") throw ArgumentNullException("path", "path was either NULL or an empty string."); _access = (mode == FileMode::Append ? FileAccess::Write : FileAccess::ReadWrite); XCreateFile(&handle, path, _access, FileShare::Read, mode, FILE_ATTRIBUTE_NORMAL); } FileStream::FileStream(char* path, FileMode_t mode, FileAccess_t access) { if(path == null || path == "") throw ArgumentNullException("path", "path was either NULL, or an empty string."); _access = access; XCreateFile(&handle, path, access, FILE_SHARE_READ | FILE_SHARE_WRITE, mode, FILE_ATTRIBUTE_NORMAL); } FileStream::FileStream(char* path, FileMode_t mode, FileAccess_t access, FileShare_t share) { if(path == null || path == "") throw ArgumentNullException("path", "path was either NULL, or an empty string."); XCreateFile(&handle, path, access, share, mode, FILE_ATTRIBUTE_NORMAL); } FileStream::FileStream(char* path, FileMode_t mode, FileAccess_t access, FileShare_t share, int bufferSize) { FileStream(path, mode, access, share, bufferSize, false); } FileStream::FileStream(char* path, FileMode_t mode, FileAccess_t access, FileShare_t share, int bufferSize, bool useAsync) { if (bufferSize <= 0) throw ArgumentOutOfRangeException("bufferSize", "Positive number required."); XCreateFile(&handle, path, access, share, mode, FILE_ATTRIBUTE_NORMAL); } FileStream::~FileStream() { Dispose(false); } void FileStream::Dispose(bool disposing) { try { if((handle != -1) && (_writePos > 0)) { FlushWrite(!disposing); } } catch(Exception) { } if((handle != -1)) { // ! Invalidate the handle handle = -1; } } void FileStream::Flush() { if(handle == -1) throw ObjectDisposedException("FileStream", "The stream has been closed."); if (_writePos > 0) { FlushWrite(false); } else if ((_readPos < _readLen) && canSeek) { FlushRead(); } _readPos = 0; _readLen = 0; } void FileStream::FlushWrite(bool calledFromFinalizer) { Write(_buffer, 0, _writePos); _writePos = 0; } int FileStream::Read(byte array[], int offset, int count) { if(handle == -1) throw ObjectDisposedException("FileStream", "The stream has been closed."); if(array == null) throw ArgumentNullException("array"); if (!CanRead()) throw NotSupportedException("Stream does not support reading"); int len = (sizeof(array)/sizeof(byte)); if (offset < 0) throw ArgumentOutOfRangeException("offset", "< 0"); if (count < 0) throw ArgumentOutOfRangeException("count", "< 0"); if (offset > len) throw ArgumentException("destination offset is beyond array size"); // reordered to avoid possible integer overflow if (offset > len - count) throw ArgumentException("Reading would overrun buffer"); UInt32 bytesRead; XReadFile(handle, &array[offset], count, &bytesRead); return bytesRead; } int FileStream::ReadByte() { void* data; UInt32 bytesRead; XReadFile(handle, data, 1, &bytesRead); if(bytesRead != 1) return -1; return (int)data; } long long FileStream::Seek(long long offset, SeekOrigin_t origin) { if(handle == -1) throw ObjectDisposedException("FileStream", "The stream has been closed."); FILE_POSITION_INFORMATION positionInfo; LARGE_INTEGER targetPointer; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; UInt32 filesize; // Calculate the target pointer switch (origin) { case SeekOrigin::Begin: // From the beginning of the file targetPointer.QuadPart = offset; break; case SeekOrigin::Current: // From the current position status = NtQueryInformationFile((void*)handle, &ioStatusBlock, &positionInfo, sizeof(positionInfo), FilePositionInformation); if (!NT_SUCCESS(status)) return RtlNtStatusToDosError(status); targetPointer.QuadPart = positionInfo.CurrentByteOffset.QuadPart + offset; break; case SeekOrigin::End: // From the end of the file status = XGetFileSize(handle, &filesize); if (!NT_SUCCESS(status)) return RtlNtStatusToDosError(status); targetPointer.QuadPart -= offset; break; } // Fill in the new position information positionInfo.CurrentByteOffset.u.HighPart = targetPointer.u.HighPart; positionInfo.CurrentByteOffset.u.LowPart= targetPointer.u.LowPart; // Set the new position status = NtSetInformationFile((void*)handle, &ioStatusBlock, &positionInfo, sizeof(positionInfo), FilePositionInformation); if (!NT_SUCCESS(status)) return RtlNtStatusToDosError(status); else { return targetPointer.QuadPart; } } void FileStream::SetLength(long long value) { if(!CanSeek()) throw NotSupportedException("The stream does not support seeking."); if(!CanWrite()) throw NotSupportedException("The stream does not support writing."); if(value < 0) throw ArgumentOutOfRangeException("value is less than 0"); Flush(); if (Position() > value) Position(value); } void FileStream::Write(byte array[], int offset, int count) { XWriteFile(handle, &array[offset], count, null); } void FileStream::WriteByte(byte value) { XWriteFile(handle, (void*)value, 1, null); } } }