-
guav © (24.03.08 11:42) [40]> [39] guav © (24.03.08 11:36)
> (простой способ воспроизвести [14] - пробовать на сжатом файле).
Сейчас что-то не получается просто сжать и изменять, чтобы файл "переехал". Но явление тоже наблюдал. -
> [40] guav © (24.03.08 11:42)
> Сейчас что-то не получается просто сжать и изменять, чтобы файл "переехал".
> Но явление тоже наблюдал.
Я уже написала процедуру, рекурсивно создающую дамп Run - ов всех объектов,
содержащихся в директории вместе с их именами и LCN - ми.
Осталось реализовать сравнение дампов,
и можно будет "во всеоружии" приступить к поимке переезжающих файлов :) -
Игорь Шевченко © (25.03.08 09:52) [42]Riply © (24.03.08 15:09) [41]
"Я нашел, как применить здесь нестирающиеся шины из полиструктурного волокна с вырожденными аминными связями и неполными кислородными группами. Но я не знаю пока, как использовать регенерирующий реактор на субтепловых нейтронах. Миша, Мишок! Как быть с реактором?"
(с) А и Б Стругацкие -
Мне не удалось воспроизвести "переезд" кластеров файла,
как прямое следствие записи в него.
Это означает только недостаточность моих знаний, но никак
не то, что данный "переезд" невозможен.
Уж не говоря о том, что не поймала сегодня - поймаю завтра :)
Повторюсь: эффект, я наблюдала и не раз.
Тогда он мне был совсем не нужен.
Я рассматривала возможности "безопасной прямой записи" данных в файл.
И то, что мне удалось зафиксировать "переезд"
ставило крест на моем способе "блокировки класеров".
Помню, расстроилась тогда :)
Зато мне удалось написать легко воспроизводимый пример,
доказывающий, что при обычной записи в файл,
никакой гарантии, что мы пишем в нужные кластеры нет :)
Я приведу только схему кода, т.к. в нем используется очень много
"самолапных" функций и классов (очень лень выдергивать все это. Sorry),
но попробую прокоментировать происходящее.
Приступим:function ReadWippedFile(const pSourName: PUNICODE_STRING): NTSTATUS;
const
StuffExt: WideString = '.stuff';
sHelloDolly: WideString = 'Hello Dolly !!!' + sLineBreak + 'Wipped file: ';
var
SourLcn, StuffLcn: _LCN;
StuffName: UNICODE_STRING;
MFTDisk: TMFTScan;
hFileObj: THandle;
pBuffer: Pointer;
BufferSize, SourceClustersCount, StuffClustersCount: DWord;
begin
StuffLcn := -1;
SourLcn := -1;
SourceClustersCount := 4;
StuffClustersCount := SourceClustersCount * 2;
RtlInitUnicodeString(@StuffName, PWideChar(pSourName.Buffer + StuffExt));
// Создаем тестовый файл и временный файл, в который будем перемещать класеры.
// Задаем им размеры.
Result := Nt_FileCreateSized(pSourName, SourceClustersCount * BytesPerCluster, SET_SIZE_FLAG_INSTANCE, nil);
if NT_SUCCESS(Result) then Result := Nt_FileCreateSized(@StuffName, StuffClustersCount * BytesPerCluster, SET_SIZE_FLAG_INSTANCE, nil);
if NT_SUCCESS(Result) then
begin
MFTDisk := TMFTScan.Create;
try // TMFTScan - класс для работы с диском
Result := MFTDisk._Set_CallBackFunctions(pSourName, nil); // настраиваем
if Result = STATUS_SUCCESS then Result := MFTDisk.NtGetObjectRetrievalPointers(@StuffName, 0);
if Result = STATUS_SUCCESS then
begin // получили RetrievalPointers. Проверяем их пригодность как буфера для перемещения
with PRETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData)^ do
if ExtentCount > 0 then
with Extents[0] do
if (Lcn.QuadPart > 0) and (NextVcn.QuadPart >= SourceClustersCount)
then StuffLcn := Extents[0].Lcn.QuadPart;
if StuffLcn <= 0 then
begin
ShowMessage('Stuff file is fragmented. Try again' + sLineBreak
+ Pars_RETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData));
Exit;
end
else Nt_DeleteFileU(@StuffName, 0);
Result := MFTDisk.NtGetObjectRetrievalPointers(pSourName, 0);
if Result = STATUS_SUCCESS then
begin // Запоминаем исходные RetrievalPointers нашего файла и записываем их в лог
with PRETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData)^ do
if ExtentCount > 0 then SourLcn := Extents[0].Lcn.QuadPart;
Log_WriteStatusNT('Try to move to: ' + IntToStr(StuffLcn) + sLineBreak
+ Pars_RETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData), Result);
end;
// Подготовка закончена, приступаем к работе
if Result = STATUS_SUCCESS then
begin // Открываем наш файл
Result := Nt_OpenFile(pSourName, FILE_WRITE_DATA or SYNCHRONIZE, FILE_SHARE_ALX_NONE,
FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT, nil, nil, hFileObj);
if Result = STATUS_SUCCESS then
try
BufferSize := SourceClustersCount * BytesPerCluster;
pBuffer := GetMemory(BufferSize);
if pBuffer <> nil then
try
Записываем в буфер приветствие для Dolly и имя файла.
FillBuffer(pBuffer, BufferSize, sHelloDolly + sLineBreakW + pSourName.Buffer);
Result := Nt_WriteFile(hFileObj, pBuffer, BufferSize, nil);
FlushFileBuffers(hFileObj);
// Записали данные в файл, приступаем к их уничтожению
NT_SUCCESS_SET_WORST(Result, Nt_SetFilePointer(hFileObj, nil, nil));
if NT_SUCCESS(Result) then
begin
FillChar(pBuffer^, BufferSize, 0); // Очистили буфер и записали первую половину файла
Result := Nt_WriteFile(hFileObj, pBuffer, BufferSize div 2, nil);
if Result = STATUS_SUCCESS then
begin
// Перемещаем кластеры нашего файла, записываем в лог новое расположение
Result := MFTDisk.MoveObjectClusters(pSourName, StuffLcn);
if NT_SUCCESS_SET_WORST(Result, MFTDisk.NtGetObjectRetrievalPointers(pSourName, 0))
then Log_WriteMessage(Pars_RETRIEVAL_POINTERS_BUFFER(MFTDisk.IoData.pData));
// и затираем вторую половину файла
NT_SUCCESS_SET_WORST(Result, Nt_WriteFile(hFileObj, IncPtr(pBuffer, BufferSize div 2), BufferSize div 2, nil));
end;
FillChar(pBuffer^, BufferSize, 0);
// читаем старые сектора нашего файла (прямое чтение)
Result := MFTDisk.ReadClustersToBuffer(pBuffer, SourLcn, SourceClustersCount);
if Result = STATUS_SUCCESS // и сохраняем их в др. файл
then Result := Nt_CreateFileData(@StuffName, pBuffer, BufferSize, nil, nil);
end;
finally
FreeMem(pBuffer);
end
else Result := STATUS_INSUFFICIENT_RESOURCES;
finally
NT_SUCCESS_SET_WORST(Result, NtClose(hFileObj));
end;
end;
end;
finally
MFTDisk.Free;
end;
end;
end;
Читаем лог:
Try to move to: 623215
ExtentCount 1
Index 0 Lcn 1778061 Count 4
The operation completed successfully
ExtentCount 1
Index 0 Lcn 623215 Count 4
The operation completed successfully
и видим, что посреди операции записи в файл, его кластеры были успешно перемещены туда,
куда мы и хотели.
Смотрим данные, полученные прямым чтением старых секторов:
H e l l o D o l l y ! ! !
W i p p e d f i l e :
\ ? ? \ I : \ Z _ S c a n T e s t D i r \ S o u r c e \ C l u s t e r e d F i l e . t x t
(Привожу только кусок, на самом деле считано все полностью). В новых кластерах, разумеется нули :)
P.S.
Операции затирания, прямого чтения и перемещения можно "тасовать" как угодно
(разумеется не нарушая логики). Результат остается тем же.
P.P.S.
Пример собирался "на коленке" так что строго не судите :)
Если возникнет необходимость - напишу нормальный (пригодный для компиляции).
Но очень лень этим заниматься. :) -
> [43] Riply © (25.03.08 09:52)
Несколько добавлений:
Как видно из примера для перемещения кластеров не использовался Handle файла.
MoveObjectClusters(pSourName, StuffLcn);
Это было сделано для того, чтобы показать, что "монопольное" открытие, тоже не спасает.
Перемещались кластеры "законными" методами с помощью FSCTL_MOVE_FILE -
WipeInfo (25.03.08 10:09) [45]Eraser
WipeInfo -
Игорь Шевченко © (25.03.08 10:26) [46]
> Зато мне удалось написать легко воспроизводимый пример,
> доказывающий, что при обычной записи в файл,
> никакой гарантии, что мы пишем в нужные кластеры нет
Вот же. А ошибку в примере ты уже нашла ? -
> [46] Игорь Шевченко © (25.03.08 10:26)
> Вот же. А ошибку в примере ты уже нашла ?
Ну вот. Как всегда :(
Пол ночи писала это дело, уже голова плохо соображает.
И где она (ошибка) ? -
> [46] Игорь Шевченко © (25.03.08 10:26)
Ну нельзя же так издеваться над софорумчанкой :)
В чем эта чертова ошибка ? -
guav © (25.03.08 11:57) [49]> [45] WipeInfo (25.03.08 10:09)
> Eraser
Вот кстати эту тулзу смотрел (код открыт).
На NTFS она логфайл не чистит, а на FAT имеются ошибки, которые моут привести к проче данных. -
Игорь Шевченко © (25.03.08 11:59) [50]Riply © (25.03.08 10:52) [48]
Ошибка в том, что при обычной записи в файл (несжатый) его кластеры автомагически не перемещаются. Ибо незачем. -
> [50] Игорь Шевченко © (25.03.08 11:59)
> Riply © (25.03.08 10:52) [48]
> Ошибка в том, что при обычной записи в файл (несжатый)
> его кластеры автомагически не перемещаются. Ибо незачем.
К "несжатый" надо еще добавить "неразраженный", и очень вероятно "не фрагментированный" :)
Ведь не случайно эффект я наблюдала на фрагментированных файлах.
Насчет "незачем", я бы не была столь категорична, ибо пути системы неисповедимы. :)
Это относиться и к сторонним программам, установленным на компьютер.
И если кто угодно из них имеет возможность переместить наш файл, когда мы в него пишем,
то какая здесь может быть гарантия ?
В примере я специально использовала стандартный файл, даже не фрагментированный,
что бы показать, что и в этом случае запись может происходить "не туда". -
Игорь Шевченко © (25.03.08 13:03) [52]Riply © (25.03.08 12:14) [51]
> Ведь не случайно эффект я наблюдала на фрагментированных
> файлах.
Воспроизводи. До тех пор, пока не воспроизвела, считается за глюк инструмента.
> В примере я специально использовала стандартный файл, даже
> не фрагментированный,
> что бы показать, что и в этом случае запись может происходить
> "не туда".
В примере ты его параллельно дварковала, причем влэндишным способом. -
> [52] Игорь Шевченко © (25.03.08 13:03)
> Воспроизводи. До тех пор, пока не воспроизвела, считается за глюк инструмента.
"Ох не легкая это работа, из болота тащить бегемота." (с)
Что ж делать, буду пытаться. Но требуется время (кроме удовольствия, еще, к сожалению, и работа существует :)
> В примере ты его параллельно дварковала, причем влэндишным способом.
:)
Дык дваркование из примера можно убрать и поместить
в другой порцесс. Просто так было легче совместить его с записью в файл :) -
Игорь Шевченко © (25.03.08 13:17) [54]Давай сделаем проще:
Ты создашь файл, стандартным образом, скопируешь файл с исходным текстов какого-нибудь твоего примера, килобайт на 10 в файл с именем a.txt.
После этого ты его аккуратно откроешь на запись, и пропишешь от начала до конца нулями или произвольным символом.var
AFile: HFILE;
FileSize, Worker: Cardinal;
I: Integer;
Buffer: array[0..1023] of char;
begin
FillChar(Buffer, SizeOf(Buffer), 65);
AFile := CreateFile('a.txt', GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
Win32Check(AFile <> INVALID_HANDLE_VALUE);
try
FileSize := GetFileSize(AFile, @Worker);
if Worker <> 0 then
raise Exception.Create('File too large to test');
for I := 0 to (FileSize div SizeOf(Buffer)) - 1 do
if not WriteFile(AFile, Buffer, SizeOf(Buffer), Worker, nil)
or (Worker <> SizeOf(Buffer)) then
raise Exception.Create('Write failed');
if FileSize mod SizeOf(Buffer) <> 0 then
if not WriteFile(AFile, Buffer, FileSize mod SizeOf(Buffer), Worker,
nil) or (Worker <> FileSize mod SizeOf(Buffer)) then
raise Exception.Create('Write failed');
finally
CloseHandle(AFile);
end;
end;
После этого ты любым тебе доступным инструментом восстановишь содержимое этого файла :)
Потом то же самое с сжатым файлом и с разреженным.
Договорились ? -
Игорь Шевченко © (25.03.08 13:20) [55]В дополнение к [54]
В параллель с файлом ты можешь делать все, что угодно. Но принцип такой, что после окончания всех процессов с файлом, ты, имея диск с NTFS, пытаешь восстановить. Разумеется, способ, когда ты заранее скопируешь кластеры, занимаемые файлом до его затирания, а потом перепишешь файл в другое место каким-либо способом, хотя бы и влэндишным, считается неспортивным.
Да, разумеется ход процесса восстановления надо в студию, чтобы пример был воспроизводимым. -
> [54] Игорь Шевченко © (25.03.08 13:17)
> Договорились ?
Т.е. мне надо найти условия, при которых данное затирание
будет работать не сто процентно.
Причем такие, что сбой в затирании был воспроизводим
при разумном числе попыток ?
Если я правильно поняла и мне дается время, то согласна :) -
Игорь Шевченко © (25.03.08 13:32) [57]Riply © (25.03.08 13:28) [56]
Тебе надо сделать
> После этого ты любым тебе доступным инструментом восстановишь
> содержимое этого файла
Время разумеется не ограничено :) -
> [57] Игорь Шевченко © (25.03.08 13:32)
> После этого ты любым тебе доступным инструментом восстановишь
> содержимое этого файла
Может не обязательно полное содержимое, а хотя бы один сектор (ну или кластер) ?
> Время разумеется не ограничено :)
Ну а после этого уточнения, я полностью согласная :) -
WipeInfo (25.03.08 14:38) [59]Удалено модератором
Примечание: Offtopic