/* Copyright (C) 2014 Tal Aloni . All rights reserved. * * You can redistribute this program and/or modify it under the terms of * the GNU Lesser Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. */ using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32.SafeHandles; namespace DiskAccessLibrary { // FileStream reads will try to fill the internal buffer, // this may cause issues when reading the last few sectors of the disk, // (FileStream will try to read sectors that do not exist). // This can be solved by setting the FileStream buffer size to the sector size. // An alternative is to use FileStreamEx which does not use an internal read buffer at all. public class FileStreamEx : FileStream { [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool ReadFile(SafeFileHandle handle, byte[] buffer, uint numberOfBytesToRead, out uint numberOfBytesRead, IntPtr lpOverlapped); private bool m_releaseHandle = true; public FileStreamEx(SafeFileHandle handle, FileAccess access) : base(handle, access) { } /// The byte offset in array at which the read bytes will be placed /// The maximum number of bytes to read public override int Read(byte[] array, int offset, int count) { uint result; if (offset == 0) { ReadFile(this.SafeFileHandle, array, (uint)count, out result, IntPtr.Zero); } else { byte[] buffer = new byte[count]; ReadFile(this.SafeFileHandle, buffer, (uint)buffer.Length, out result, IntPtr.Zero); Array.Copy(buffer, 0, array, offset, buffer.Length); } if (count == result) { return (int)result; } else { int errorCode = Marshal.GetLastWin32Error(); string message = String.Format("Could not read from position {0} the requested number of bytes ({1}).", this.Position, count); ThrowIOError(errorCode, message); return 0; // this line will not be reached } } internal static void ThrowIOError(int errorCode, string defaultMessage) { if (errorCode == (int)Win32Error.ERROR_ACCESS_DENIED) { // UnauthorizedAccessException will be thrown if stream was opened only for writing or if a user is not an administrator throw new UnauthorizedAccessException(defaultMessage); } else if (errorCode == (int)Win32Error.ERROR_SHARING_VIOLATION) { throw new SharingViolationException(defaultMessage); } else if (errorCode == (int)Win32Error.ERROR_SECTOR_NOT_FOUND) { string message = defaultMessage + " The sector does not exist."; throw new IOException(message, (int)Win32Error.ERROR_SECTOR_NOT_FOUND); } else if (errorCode == (int)Win32Error.ERROR_CRC) { string message = defaultMessage + " Data Error (Cyclic Redundancy Check)."; throw new CyclicRedundancyCheckException(message); } else if (errorCode == (int)Win32Error.ERROR_NO_SYSTEM_RESOURCES) { throw new OutOfMemoryException(); } else { string message = defaultMessage + String.Format(" Win32 Error: {0}", errorCode); throw new IOException(message, errorCode); } } // we are working with disks, and we are only supposed to read sectors public override int ReadByte() { throw new NotImplementedException("Cannot read a single byte from disk"); } public void Close(bool releaseHandle) { m_releaseHandle = releaseHandle; this.Close(); } /// /// This will prevent the handle from being disposed /// protected override void Dispose(bool disposing) { if (m_releaseHandle) { base.Dispose(disposing); } else { try { this.Flush(); } catch { } } } } }