Обработка элементов управления файловой системой
Обработка операции IRP_MJ_FILE_SYSTEM_CONTROL отличается от обработки буфера данных, требуемой для других операций в файловой системе. Это связано с тем, что каждая операция устанавливает свой конкретный механизм передачи данных для диспетчера ввода-вывода в составе своего кода управления с помощью макроса CTL_CODE. Кроме того, код элемента управления указывает доступ к файлу, необходимый вызывающей объекту. Файловая система должна быть особенно осведомлена об этой проблеме при определении кода элемента управления, так как этот доступ обеспечивается диспетчером ввода-вывода. Некоторые управляющие коды ввода-вывода (например, FSCTL_MOVE_FILE) указывают FILE_SPECIAL_ACCESS, который является механизмом, позволяющим файловой системе указать, что безопасность операции будет проверяться файловой системой напрямую. FILE_SPECIAL_ACCESS числовое эквивалентно FILE_ANY_ACCESS, поэтому диспетчер операций ввода-вывода не предоставляет никаких конкретных проверок безопасности, откладывая вместо этого в файловой системе. FILE_SPECIAL_ACCESS в основном предоставляет документацию о том, что файловая система будет выполнять дополнительные проверки.
Несколько операций файловой системы задают FILE_SPECIAL_ACCESS. Операция FSCTL_MOVE_FILE используется как часть интерфейса дефрагментации для файловых систем и задает FILE_SPECIAL_ACCESS. Так как вы хотите иметь возможность дефрагментировать открытые файлы, которые активно считываются и записываются, используемый дескриптор имеет только FILE_READ_ATTRIBUTES предоставлен доступ, чтобы избежать конфликтов доступа к общим ресурсам. Однако эта операция должна быть привилегированной, так как диск изменяется на низком уровне. Решение заключается в том, чтобы убедиться, что дескриптор, используемый для выдачи FSCTL_MOVE_FILE, является открытым пользовательским томом устройства хранения данных с прямым доступом (DASD), который является привилегированным дескриптором. Код файловой системы FASTFAT, гарантирующий выполнение этой операции с открытым пользовательским томом, находится в функции FatMoveFile (см. исходный файл fsctrl.c из примера fastfat, содержащегося в WDK):
//
// extract and decode the file object and check for type of open
//
if (FatDecodeFileObject( IrpSp->FileObject, &Vcb, &FcbOrDcb, &Ccb ) != UserVolumeOpen) {
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", STATUS_INVALID_PARAMETER);
return STATUS_INVALID_PARAMETER;
}
Структура, используемая операцией FSCTL_MOVE_FILE, указывает перемещаемый файл:
typedef struct {
HANDLE FileHandle;
LARGE_INTEGER StartingVcn;
LARGE_INTEGER StartingLcn;
ULONG ClusterCount;
} MOVE_FILE_DATA, *PMOVE_FILE_DATA;
Как отмечалось ранее, дескриптор, используемый для выдачи FSCTL_MOVE_FILE, является "открытой" операцией всего тома, в то время как операция фактически применяется к дескриптору файла, указанному во входном буфере MOVE_FILE_DATA. Это делает проверки безопасности для этой операции несколько сложными. Например, этот интерфейс должен преобразовать дескриптор файла в файловый объект, представляющий перемещаемый файл. Это требует тщательного рассмотрения со стороны любого драйвера. FastFAT делает это с помощью ObReferenceObject в защищенном режиме в функции FatMoveFile в исходном файле fsctrl.c в примере fastfat, который содержит WDK:
//
// Try to get a pointer to the file object from the handle passed in.
//
Status = ObReferenceObjectByHandle( InputBuffer->FileHandle,
0,
*IoFileObjectType,
Irp->RequestorMode,
&FileObject,
NULL );
if (!NT_SUCCESS(Status)) {
FatCompleteRequest( IrpContext, Irp, Status );
DebugTrace(-1, Dbg, "FatMoveFile -> %08lx\n", Status);
return Status;
}
// Complete the following steps to ensure that this is not an invalid attempt
//
// - check that the file object is opened on the same volume as the
// DASD handle used to call this routine.
//
// - extract and decode the file object and check for type of open.
//
// - if this is a directory, verify that it's not the root and that
// you are not trying to move the first cluster. You cannot move the
// first cluster because sub-directories have this cluster number
// in them and there is no safe way to simultaneously update them
// all.
//
// Allow movefile on the root directory if it's FAT32, since the root dir
// is a real chained file.
// //
Обратите внимание на использование Irp-RequestorMode>, чтобы убедиться, что если вызывающий объект является приложением в пользовательском режиме, дескриптор не может быть дескриптором ядра. Требуемый доступ равен 0, чтобы можно было переместить файл во время активного доступа. И, наконец, обратите внимание, что этот вызов должен выполняться в правильном контексте процесса, если вызов поступил в пользовательском режиме. Исходный код файловой системы FASTFAT также применяется в функции FatMoveFile в fsctrl.c:
//
// Force WAIT to true. There is a handle in the input buffer that can only
// be referenced within the originating process.
//
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
Эти семантические проверки безопасности, выполняемые файловой системой FAT, являются типичными для тех, которые требуются файловой системе для любой операции, которая передает дескриптор. Кроме того, файловая система FAT также должна выполнять проверки работоспособности, относящиеся к операции. Эти проверки работоспособности обеспечивают совместимость разрозненных параметров (например, перемещаемый файл находится на томе, который был открыт), чтобы предотвратить выполнение вызывающей стороной привилегированной операции, когда она не должна быть разрешена.
Для любой файловой системы правильная безопасность является важной частью операций управления файловой системой, к которым относятся:
Правильная проверка пользовательских дескрипторов.
Защита доступа пользователей к буферу.
Проверка семантики конкретной операции.
Во многих случаях код, необходимый для правильной проверки и безопасности, может составлять значительную часть кода в данной функции.