Как удалять метаданные удаленного файла в системе FAT32

Мне нужен алгоритм для удаления метаданных удаленного файла в системе FAT32. Т.е пользователь удалят файл, заполненный псевдослучайными символами. Проблема в том, что программы для восстановления находят имя удаленного файла, а мне нужно его в идеале удалить, либо сделать так, чтобы имя было похоже хотя бы на "АААА.АААА".

Пока пытался разобраться, написал такой код:

#include <windows.h>
#include <iostream>
#include <filesystem>
#include <iomanip>
#include <sstream>


int main() {
    setlocale(LC_ALL, "rus");
    //Путь к диску U:, его открытие     L"\\\\.\\PhysicalDrive0\\Partition2";
    
    //Путь к разделу через физический диск
    const wchar_t* partitionU = L"\\\\.\\U:";

    HANDLE hDisk = CreateFile(partitionU, 
        GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE, 
        NULL, OPEN_EXISTING, 0, NULL);

    if (hDisk == INVALID_HANDLE_VALUE) {
        std::cout << GetLastError() << std::endl;
        return 0;
    }
    else std::cout << "Success!" << std::endl;
    //****************************************




    // Получаем информацию о геометрии диска
    DISK_GEOMETRY diskGeometry;
    DWORD bytesReturned;

    if (DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &diskGeometry, sizeof(diskGeometry), &bytesReturned, NULL)) {
        // Выводим информацию о геометрии диска
        std::cout << "DISK INFO\n*************************************\n";
        std::cout << "Тип диска: " << diskGeometry.MediaType << std::endl;
        std::cout << "Секторов на треке: " << diskGeometry.SectorsPerTrack << std::endl;
        std::cout << "Число головок: " << diskGeometry.TracksPerCylinder << std::endl;
        std::cout << "Общее число секторов: " << diskGeometry.Cylinders.QuadPart * diskGeometry.TracksPerCylinder * diskGeometry.SectorsPerTrack << std::endl;
    }
    else {
        std::cerr << "Ошибка при получении информации о геометрии диска. Код ошибки: " << GetLastError() << std::endl;
    }
    std::cout << "*****************************************\n\n\n" << std::endl;
    //*******************************************




    //Проверка атрибутов
    const wchar_t* diskPath = L"\\\\.\\U:";
    DWORD attributes = GetFileAttributes(diskPath);
    if (attributes != INVALID_FILE_ATTRIBUTES) {
        // Проверка наличия атрибута FILE_ATTRIBUTE_READONLY
        if (attributes & FILE_ATTRIBUTE_READONLY) {
            std::cout << "Диск доступен только для чтения." << std::endl;
        }
        else {
            std::cout << "Диск доступен для записи." << std::endl;
        }
    }
    else {
        std::cerr << "Ошибка при получении атрибутов диска. Код ошибки: " << GetLastError() << std::endl;
        
    }
    //******************

    //чтение данных с байта 4 194 304
    if (SetFilePointer(hDisk, 4867072, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
        std::cerr << "Ошибка установки указателя: ";
        return GetLastError();
    }
   
    byte buffer[1024];
    DWORD dwRead;

    if (ReadFile(hDisk, buffer, sizeof(buffer), &dwRead, NULL)) {
        std::cout << "Прочитано байт:" << dwRead << std::endl;
        for (DWORD i = 0; i < dwRead; ++i) {
            std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(buffer[i]) << " ";
        }
        std::cout << std::endl;
    }
    else {
        std::cerr << "Ошибка при чтении: " <<GetLastError() << std::endl;
    }
    //*********************************
   



    //************запись данных в байт
    //LARGE_INTEGER liMove;
    //liMove.QuadPart = 0x004005a0;

    byte* pointer = reinterpret_cast<byte*>(4867072);
    LARGE_INTEGER largeInt;
    largeInt.QuadPart = reinterpret_cast<LONG_PTR>(pointer);

    if (!SetFilePointerEx(hDisk, largeInt, NULL, FILE_BEGIN)) { //file_begin - от начала файла
        std::cerr << "Ошибка установки указателя: ";
        return GetLastError();
    }

    DWORD dwBytesWritten = 0;
    //const char dataToWrite[] = "\xd2";
    byte byte = 0xAA;
    if (WriteFile(hDisk, &byte, sizeof(byte) -1, &dwBytesWritten, NULL)) {
        std::cout << "Данные успешно записаны. Байтов записано: " << dwBytesWritten << std::endl;
    }
    else {
        std::cerr << "Ошибка при записи данных в файл. Код ошибки: " << GetLastError() << std::endl;
    }
    //***************************************
    CloseHandle(hDisk);
    return 0;
}

Заметил такие странности:

  1. Когда я пытаюсь обратиться к любому байту - выдается ошибка "Неверно переданные аргументы".
  2. Когда я обращаюсь к байту, с которого начинается файл - тогда все нормально, все читается (Самое интересное что этих проблем нет у NTFS. Т.е указатель можно ставить куда угодно, а в FAT - только на начало)
  3. В статьях написан примерно такой код, как у меня, и везде написано что байты перезаписываются прям на диске. У меня же это не работает.

Подскажите, пожалуйста, в каком направлении копать вообще?


Ответы (2 шт):

Автор решения: Vladimir Ignatenko

Чтобы программы восстановления удаленных не находили ваш файл, вам нужно удалять соответствующую запись в таблице FAT. Для этого нужен низкоуровневый доступ. Да и не имея опыта можно очень много дел наворотить.

Смысл FAT в том что в таблице лежит название файла, его аттрибуты и указатель, который указывает на начало файла. При удалении же файла средвами операционной среды (или когда вы его удаляете из своего приложения) в таблице FAT просто меняется признак, что файл удаленный. Больше ничего не меняется. И пока на это место в таблице не будет записана информация о каком-то другом файле - удаленный файл можно восстановить. И восстановится он корректно в том случае если не была проивзедена запись в те блоки на диске, которые он занимал.

Если же у вас задача состояит в том чтобы предотвратить восстановелние файла, то самое простое - это переписать соержимое файла. Т.е. открываете файл на запись и записываете в него любые символы в том количестве, сколько было байт в этом файле. Обычно просто пишут 0 и все. После этого даже если файл восстановят, то его содержимое будет недоступно (бесполезно).

Если нужно еще и имя файла скрыть, то тут тоже проще переименовать его, а потом уже удалить.

Все это делается стандартными средствами С/С++. И не надо лезть на низкий уровень. Да и риски что-то попутно сломать очень малы.

→ Ссылка
Автор решения: Qouk

Разобрался. FAT требует полностью переписывать сектор, так что в алгоритме нет ошибок, кроме указания в нём байта не являющегося началом сектора

→ Ссылка