1

I wrote code that can write and read data from physical disk. I open the physical disk using pInvoke CreateFile and use FileStream to perform read and write data.

When physical disk is online, every thing works great.

In case the physical disk is offline and I try to write to disk, I get an error System.IO.IOException: 'The media is write protected.'

How can I detect if disk is offline without trying to write to disk.

Here is the code that create the FileStream and perform writes to disk

    private const uint GENERIC_READ = 0x80000000;
    private const uint GENERIC_WRITE = 0x40000000;
    private const uint OPEN_EXISTING = 3;
    private const uint FILE_FLAG_NO_BUFFERING = 0x20000000;
    private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess,
                                        uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
                                        uint dwFlagsAndAttributes, IntPtr hTemplateFile);



    public void OpenFile()
    {
        m_handleValue = CreateFile(DevicePath, GENERIC_WRITE | GENERIC_READ,
                            0x3, IntPtr.Zero, OPEN_EXISTING,
                            FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH
                            , IntPtr.Zero);
        
        m_fileStream = new FileStream(m_handleValue, FileAccess.ReadWrite, 512);
    }



    public void WriteData(int offset, byte[] data)
    {
        m_fileStream.Seek(offset, SeekOrigin.Begin);
        m_fileStream.Write(data, 0, data.Length);
    }
Yaniv daye
  • 73
  • 7
  • 2
    In truth, it isn't possible to know with 100% certainty because you're depending on the device-drivers for that device to correctly and accurately report their status to Windows. Windows is giving you the error-message "The media is write protected." **because** it thinks the device is online and connected but write-protected, if Windows thought that the device is offline or unavailable it would give you a different error-message and error-code. – Dai Jul 26 '20 at 12:41
  • Why are you reimplementing `CreateFile` anyway? Your declaration doesn't add any functionality that isn't already-present in any of `FileStream`'s constructors. – Dai Jul 26 '20 at 12:42
  • You'd want to use WMI to check for device-status rather than Win32's `CreateFile` anyway. That said, it **may** be appropriate to actually allow the exception to be thrown - but only if an offline/unavailable device is truly an exceptional circumstance. – Dai Jul 26 '20 at 12:43
  • I didn't reimplement CreateFile, I use it with flags so I can skip the operating system (using FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH) – Yaniv daye Jul 26 '20 at 14:21
  • Those options are also available through other FileStream constructor overloads. – Dai Jul 26 '20 at 14:36
  • Not for physical disk, For physical disk I need to use extern CreateFile – Yaniv daye Jul 27 '20 at 05:00

1 Answers1

0

Using DeviceIoControl pInvoke we can review attribute DISK_ATTRIBUTE_OFFLINE

public static class RawDeviceInfoProvider
    {
        private const int IOCTL_DISK_GET_DISK_ATTRIBUTES = 0x000700F0;
        private const uint DISK_ATTRIBUTE_OFFLINE = 0x1;

        [StructLayout(LayoutKind.Sequential)]
        public struct GetDiskAttributes
        {
            public uint Version;
            public uint Reserved;
            public ulong Attributes;
        }

        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool DeviceIoControl(SafeHandle hDevice, uint dwIoControlCode, IntPtr lpInBuffer,
            uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);


        public static bool GetOnlineStatus(SafeFileHandle hDevice)
        {
            uint dummy;
            GetDiskAttributes attributes = new GetDiskAttributes();
            IntPtr lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(attributes));
            bool success = DeviceIoControl(hDevice, IOCTL_DISK_GET_DISK_ATTRIBUTES, IntPtr.Zero, 0,
                lpOutBuffer, (uint)Marshal.SizeOf(typeof(GetDiskAttributes)), out dummy, IntPtr.Zero);
            attributes = (GetDiskAttributes)Marshal.PtrToStructure(lpOutBuffer, typeof(GetDiskAttributes));
            Marshal.FreeHGlobal(lpOutBuffer);
            if (!success)
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new IOException("Unable to retrieve disk attributes, Error: " + errorCode);
            }
            bool isOffline = (attributes.Attributes & DISK_ATTRIBUTE_OFFLINE) > 0;
            return !isOffline;
        }
    }
Yaniv daye
  • 73
  • 7