C# WriteFile () прекращает запись в секторе 242 на USB-накопителях

Я написал ниже код для записи 0xFF чтобы все байты на мои устройства USB storeageбыл. По какой-то причине вызовы WriteFile() начинают ошибаться в секторе 242. Я сделал это на двух отдельных USB-накопителях, а затем рассмотрел устройства в шестнадцатеричном редакторе. Сектор 242, по-видимому, является началом таблицы распределения файлов на формируемом устройстве FAT16 и началом области загрузки на устройстве NTFS. Я уверен, что это не совпадение, что это правда erroring на эти места, я не знаю, как изменить это поведение. HRESULT, который я получаю, когда WriteFile терпит неудачу, -2147024891, который является E_ACCESSDENIED. Кто-нибудь знает, что может быть причиной проблемы?

Примечание: Если вы собираетесь запустить этот код в локальной системе, будьте очень осторожны, так как я жестко закодировал идентификатор физического устройства для моего USB-устройства. Не забудьте обновить переменную deviceId с помощью устройства, на которое вы пытаетесь написать. Вы не хотите разрушать свой жесткий водить.

    public enum EMoveMethod : uint
    {
        Begin = 0,
        Current = 1,
        End = 2
    }

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint SetFilePointer([In] SafeFileHandle hFile, [In] long lDistanceToMove, [Out] out int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod);

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

    [DllImport("kernel32", SetLastError = true)]
    internal extern static int ReadFile(SafeFileHandle handle, byte[] bytes, int numBytesToRead, out int numBytesRead, IntPtr overlapped_MustBeZero);

    [DllImport("kernel32.dll", SetLastError = true)]
    internal extern static int WriteFile(SafeFileHandle handle, byte[] bytes, int numBytesToWrite, out int numBytesWritten, IntPtr overlapped_MustBeZero);

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, byte[] lpInBuffer, int nInBufferSize, byte[] lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, IntPtr lpOverlapped);

    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern bool CloseHandle(SafeFileHandle handle);

public void wipeDisk()
{
        const uint OPEN_EXISTING = 3;
        const uint GENERIC_WRITE = (0x40000000);
        const uint FSCTL_LOCK_VOLUME = 0x00090018;
        const uint FSCTL_UNLOCK_VOLUME = 0x0009001c;
        const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;

        bool success = false;
        int intOut;
        string deviceId = @".PHYSICALDRIVE2";
        long DiskSize = 2056320000;

        SafeFileHandle diskHandle = CreateFile(deviceId, GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
        if (diskHandle.IsInvalid)
        {
            Console.WriteLine(deviceId + " open error.");
            return;
        }

        Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": opened.");

        success = DeviceIoControl(diskHandle, FSCTL_LOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
        if (!success)
        {
            Console.WriteLine(deviceId + " lock error.");
            CloseHandle(diskHandle);
            return;
        }

        Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": locked.");

        success = DeviceIoControl(diskHandle, FSCTL_DISMOUNT_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
        if (!success)
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": dismount error.");
            DeviceIoControl(diskHandle, FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
            CloseHandle(diskHandle);
            return;
        }

        Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unmounted.");

        int numBytesPerSector = 512;
        long numTotalSectors = DiskSize / 512;

        byte[] junkBytes = new byte[512];
        for (int x = 0; x < 512; x++)
        {
            junkBytes[x] = 0xFF;
        }

        for (long sectorNum = 0; sectorNum < numTotalSectors; sectorNum++)
        {
            int numBytesWritten = 0;
            int moveToHigh;

            uint rvalsfp = SetFilePointer(diskHandle, sectorNum * numBytesPerSector, out moveToHigh, EMoveMethod.Begin);

            Console.WriteLine("File pointer set " + Marshal.GetHRForLastWin32Error().ToString() + ": " + (sectorNum * numBytesPerSector).ToString());

            int rval = WriteFile(diskHandle, junkBytes, junkBytes.Length, out numBytesWritten, IntPtr.Zero);

            if (numBytesWritten != junkBytes.Length)
            {
                Console.WriteLine("Write error on track " + sectorNum.ToString() + " from " + (sectorNum * numBytesPerSector).ToString() + "-" + moveToHigh.ToString() + " " + Marshal.GetHRForLastWin32Error().ToString() + ": Only " + numBytesWritten.ToString() + "/" + junkBytes.Length.ToString() + " bytes written.");
                break;
            }
            else
            {
                Console.WriteLine("Write success " + Marshal.GetHRForLastWin32Error().ToString() + ": " + numBytesWritten.ToString() + "/" + junkBytes.Length.ToString() + " bytes written.");
            }
        }

        success = DeviceIoControl(diskHandle, FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
        if (success)
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unlocked.");
        }
        else
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": unlock error: " + Marshal.GetHRForLastWin32Error().ToString());
        }

        success = CloseHandle(diskHandle);
        if (success)
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": handle closed.");
        }
        else
        {
            Console.WriteLine(deviceId + " " + Marshal.GetHRForLastWin32Error().ToString() + ": close handle error: " + Marshal.GetHRForLastWin32Error().ToString());
        }
}

ИЗМЕНИТЬ/ОБНОВИТЬ

я смог заставить это работать успешно после выполнения низкоуровневой очистки USB-устройства с помощью стороннего инструмента. После того, как диск был полностью обнулен, я смог успешно написать на устройство. Кажется, что windows блокирует устройство, как только оно распознает допустимую файловую систему fat или ntfs и использование

    const uint FSCTL_LOCK_VOLUME = 0x00090018;
    const uint FSCTL_DISMOUNT_VOLUME = 0x00090020;

в паре с DeviceIoControl, похоже, не переопределяет блокировку windows имеет на устройстве.

кто-нибудь знает, как успешно заблокировать съемное USB-устройство в windows с помощью DeviceIoControl на диске с допустимой файловой системой?

я использовал несколько сторонних инструментов, которые делают то, что я пытаюсь сделать, и они работают успешно. Я знаю, что это возможно, но вся документация MSDN, которую я прочитал, не помогла решить проблему.

ИЗМЕНИТЬ / ОБНОВИТЬ 2

Это взято из http://msdn.microsoft.com/en-us/library/ff551353.aspx

"приложение должно заблокировать том, отключить том, или оба, прежде чем он может выдать dasd I/O. это ново для Windows Vista и было сделано для решения потенциально вредоносных методов.

  1. файловая система заблокирует все операции записи в зарезервированные разделы диска. В этом случае эти зарезервированные разделы включают MBR и две области жира. Для блокирования этих областях, вам нужно заблокировать том, отправив FSCTL_LOCK_VOLUME. Эту структуру необходимо создать на том же дескрипторе Тома, который выполняет фактические операции записи. Этот запрос может завершиться ошибкой, если есть открытые дескрипторы файлов. В этом случае приложение может принудительно отключить файловую систему, выдав FSCTL_DISMOUNT_VOLUME. Однако том фактически не демонтируется до закрытия дескриптора файла. До тех пор приложение может продолжать выдавать ввод-вывод DASD, используя тот же дескриптор файла, который в настоящее время открыто.

  2. существует расширенная область за пределами пространства томов, которая известна файловой системе, где операции записи будут заблокированы. Чтобы разрешить операции записи в эту область, необходимо выполнить fsctl_allow_extended_dasd_io на дескрипторе Тома.

вы можете использовать процедуру Win32 API DeviceIoControl для выпуска всех предыдущих FSCTSs."

Я считаю, что это именно то, что мы реализуем в коде выше, но это кажется, работает неправильно. Мы получаем ручку и блокируем и демонтируем устройство, чтобы мы могли правильно писать в защищенную область?

ИЗМЕНИТЬ / ОБНОВИТЬ 3

Ok это текущий порядок открытия дисков и томов.. Методы блокировки, демонтажа и т. д. работают именно в том порядке, который мы считаем неправильным..

SafeFileHandle volumeHandle = CreateFile(".E:",...);
LockVolume(volumeHandle);
DismountVolume(volumeHandle);
SafeFileHandle diskHandle = CreateFile(".PHYSICALDRIVE1"...);
WriteStuff(diskHandle);
//Fails...
UnlockVolume(volumeHandle);
CloseVolume(volumeHandle);
CloseDisk(diskHandle);

Я все еще получаю те же результаты, он работает только тогда, когда диск разгромлен.

3 ответов


существует путаница между диск и диск здесь.

если вы хотите полный доступ к диск (это ваш случай, когда вы используете \.\PHYSICALDRIVE), вы должны заблокировать все подключенные Тома, которые в основном все разделы (т. е. диски) вашего физического диска.

вместо FSCTL_LOCK_VOLUME на дескриптор, возвращенный CreateFile("\.\PHYSICALDRIVE"...), получить ручку на каждого установленные объем (который является диском, а не физическим диском) с помощью string.Replace("\\.\{0}:", DriveLetter) узор.

вы можете получить список смонтированных томов (в конечном счете, вы хотите список букв) для данного физического диска, используя IOCTL_DISK_GET_DRIVE_LAYOUT.


EDIT:

С MSDN :

запись на дескриптор диска будет успешным, если один из следующих условий:

секторов для записи не попадают в экстенты Тома.

сектора, которые будут записаны, чтобы попасть в монтируемый том, но вы явно заблокировали или демонтировали том с помощью использование FSCTL_LOCK_VOLUME или FSCTL_DISMOUNT_VOLUME.

секторов, написанные в том, что нет смонтированной файловой системы чем сырой.

Итак, в основном, то, что вы должны сделать, это:

  • получить дескриптор для каждого из томов
  • использовать FSCTL_LOCK_VOLUME или FSCTL_DISMOUNT_VOLUME на каждом томе. Если в томе не используется файл (т. е. нет открытого дескриптора любым процессом для любого файла),FSCTL_LOCK_VOLUME достаточно
  • получить дескриптор на физический диск
  • запись на физический диск
  • закрыть обе ручки. Закрытие ручки громкости отпустит замок.

также убедитесь, что вы используете приложение с правами администратора (с повышенными процесс).


Я предполагаю, что вы используете Windows Vista или позже. ОС блокирует любые попытки прямой записи в эти сектора, поэтому сначала вам нужно сделать блокировку. Подробнее об этом здесь:

http://msdn.microsoft.com/en-us/library/ff551353.aspx

также просто проверка, поэтому поднял этот пост:

CreateFile: прямая операция записи на необработанный диск "Доступ запрещен" - Vista, Win7

следственная информация там может быть полезно, HTH...


редактировать

я отредактировал этот ответ, чтобы отразить предложения ken2k.

предложение ken2k действительно исправило проблему, с которой я столкнулся. Я не уверен, почему мои предыдущие попытки использовать этот подход не увенчались успехом, однако я только что пересмотрел/настроил свой код, и этот подход, похоже, работает правильно.

вот шаги, которые я использовал для решения этой проблемы:

  • получить дескриптор для физических диск
  • получить дескриптор для каждого логического диска на физическом диске
  • блокировка каждого диска на физическом диске
  • демонтировать каждый диск на физическом диске
  • блокировка физического диска (необязательно)
  • демонтировать физический диск (необязательно)
  • используйте дескриптор физического диска, чтобы обнулить весь физический диск
  • разблокировать каждый логический диск
  • открыть физический диск (только если вы решите заблокировать диск)
  • закройте ручки логического диска
  • закройте дескриптор физического диска

Примечание: Если вы хотите выполнять операции "спина к спине" без завершения вашей программы, и вы использовали функциональность FSCTL_DISMOUNT_VOLUME, вам нужно будет "перемонтировать" диск, используя что-то похожее на следующее:

ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\CIMV2", "SELECT * FROM Win32_DiskDrive");

или

System.IO.DriveInfo.GetDrives();

для сопоставления ID логического диска с ID физического диска при попытке для блокировки каждого отдельного логического диска используйте следующий фрагмент кода, чтобы связать метки логического диска с метками физического диска:

    List<string> driveLetters = new List<string>();
    string deviceId = @"\.\PHYSICALDRIVE1";
    string queryString = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + deviceId + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
    ManagementObjectSearcher diskSearcher = new ManagementObjectSearcher("root\CIMV2", queryString);
    ManagementObjectCollection diskMoc = diskSearcher.Get();
    foreach (ManagementObject diskMo in diskMoc)
    {
        queryString = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + diskMo["DeviceID"] + "'} WHERE AssocClass = Win32_LogicalDiskToPartition";
        ManagementObjectSearcher driveSearcher = new ManagementObjectSearcher("root\CIMV2", queryString);

        ManagementObjectCollection driveMoc = driveSearcher.Get();
        foreach (ManagementObject driveMo in driveMoc)
        {
            driveLetters.Add("\\.\" + driveMo["DeviceID"].ToString());
        }
    }

Так, например, если метка физического диска -\.\PHYSICALDRIVE1 и содержит один логический диск с буквой диска "E", приведенный выше код будет отображать \.\E: to \.\PHYSICALDRIVE1.

согласно предложению ken2k это сопоставление также может быть сделано с помощью функции IOCTL_DISK_GET_DRIVE_LAYOUT.

надеюсь, это полезно для кого-то еще!

спасибо всем за помощь в указывая мне в правильном направлении!