fastfat源码分析笔记-FatFsdCreate函数
2023-6-13 21:54:58 Author: 安全狗的自我修养(查看原文) 阅读量:9 收藏

注只用于自己上班坐地铁看,不要转发。

NTSTATUSFatFsdCreate (    IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,    IN PIRP Irp    )
/*++
Routine Description:
This routine implements the FSD part of the NtCreateFile and NtOpenFile API calls.
Arguments:
VolumeDeviceObject - Supplies the volume device object where the file/directory exists that we are trying to open/create
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The Fsd status for the Irp
--*/
{ NTSTATUS Status; PIRP_CONTEXT IrpContext = NULL;
BOOLEAN TopLevel;
// // If we were called with our file system device object instead of a // volume device object, just complete this request with STATUS_SUCCESS //
if ( FatDeviceIsFatFsdo( VolumeDeviceObject)) {
Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED;
IoCompleteRequest( Irp, IO_DISK_INCREMENT );
return STATUS_SUCCESS; }
TimerStart(Dbg);
DebugTrace(+1, Dbg, "FatFsdCreate\n", 0);
// // Call the common create routine, with block allowed if the operation // is synchronous. //
FsRtlEnterFileSystem();
TopLevel = FatIsIrpTopLevel( Irp );
try {
IrpContext = FatCreateIrpContext( Irp, TRUE );
Status = FatCommonCreate( IrpContext, Irp );
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
// // We had some trouble trying to perform the requested // operation, so we'll abort the I/O request with // the error status that we get back from the // execption code //
Status = FatProcessException( IrpContext, Irp, GetExceptionCode() ); }
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
FsRtlExitFileSystem();
// // And return to our caller //
DebugTrace(-1, Dbg, "FatFsdCreate -> %08lx\n", Status );
TimerStop(Dbg,"FatFsdCreate");
UNREFERENCED_PARAMETER( VolumeDeviceObject );
return Status;}

// FatFsdCreate是FAT文件系统驱动程序的一部分,用于实现NtCreateFile和NtOpenFile API调用的文件系统驱动程序部分。
// 函数的作用是处理文件的创建和打开操作。
// 函数接受两个参数:
// VolumeDeviceObject:表示文件所在的卷设备对象。// Irp:表示正在处理的IRP(I/O请求包)。// 函数返回一个NTSTATUS值,表示处理IRP的文件系统驱动程序的状态。
// 函数的主要逻辑如下:
// 首先检查VolumeDeviceObject是否是FAT文件系统设备对象,如果是,则直接将IRP的状态设置为STATUS_SUCCESS,// 信息设置为FILE_OPENED,然后完成IRP请求并返回STATUS_SUCCESS。
// 如果VolumeDeviceObject不是FAT文件系统设备对象,则开始处理创建和打开操作。
// 进入文件系统。// 检查IRP是否处于顶层(TopLevel)。// 创建一个IRP上下文(IrpContext)结构。// 调用FatCommonCreate函数进行公共的创建操作。// 如果发生异常,通过异常过滤器处理异常,并将异常代码转换为适当的错误状态。// 在处理完毕后,离开文件系统。// 返回处理结果的状态。
NTSTATUSFatCommonCreate ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp )
/*++
Routine Description:
This is the common routine for creating/opening a file called by both the fsd and fsp threads.
Arguments:
Irp - Supplies the Irp to process
Return Value:
NTSTATUS - the return status for the operation
--*/
{ NTSTATUS Status; IO_STATUS_BLOCK Iosb; PIO_STACK_LOCATION IrpSp;
PFILE_OBJECT FileObject; PFILE_OBJECT RelatedFileObject; UNICODE_STRING FileName; ULONG AllocationSize; PFILE_FULL_EA_INFORMATION EaBuffer; PACCESS_MASK DesiredAccess; ULONG Options; UCHAR FileAttributes; USHORT ShareAccess; ULONG EaLength;
BOOLEAN CreateDirectory; BOOLEAN SequentialOnly; BOOLEAN NoIntermediateBuffering; BOOLEAN OpenDirectory; BOOLEAN IsPagingFile; BOOLEAN OpenTargetDirectory; BOOLEAN DirectoryFile; BOOLEAN NonDirectoryFile; BOOLEAN NoEaKnowledge; BOOLEAN DeleteOnClose; BOOLEAN TemporaryFile; BOOLEAN FileNameOpenedDos = FALSE;
ULONG CreateDisposition;

// PVCB(Volume Control Block):卷控制块,用于表示文件系统的卷(卷是文件系统中的逻辑存储单元)。Vcb结构包含与特定卷相关的信息,如卷标、簇大小、簇位图等。
// PFCB(File Control Block):文件控制块,用于表示打开的文件对象。Fcb结构包含有关特定文件的信息,如文件大小、时间戳、文件属性等。
// PCCB(Context Control Block):上下文控制块,用于表示打开的文件的上下文信息。Ccb结构包含与文件相关的上下文数据,如当前文件指针位置、缓冲区等。
// PDCB(Directory Control Block):目录控制块,用于表示目录(文件夹)。Dcb结构包含与特定目录相关的信息,如目录的文件列表、子目录列表等。 PVCB Vcb;


// typedef struct _VCB {
// // // // This is a common head for the FAT volume file // //
// FSRTL_ADVANCED_FCB_HEADER VolumeFileHeader;
// // // // The links for the device queue off of FatData.VcbQueue // //
// LIST_ENTRY VcbLinks;
// // // // A pointer the device object passed in by the I/O system on a mount // // This is the target device object that the file system talks to when it // // needs to do any I/O (e.g., the disk stripper device object). // // // //// //// // 在挂载时由I/O系统传递的设备对象的指针// // 这是文件系统在需要进行任何I/O操作时与之通信的目标设备对象(例如,磁盘分割设备对象)。// // // PDEVICE_OBJECT TargetDeviceObject;
// // // // A pointer to the VPB for the volume passed in by the I/O system on // // a mount. // //
// PVPB Vpb;
// // // // The internal state of the device. This is a collection of fsd device // // state flags. // //
// ULONG VcbState; // VCB_CONDITION VcbCondition;
// // // // A pointer to the root DCB for this volume // //
// struct _FCB *RootDcb;
// // // // If the FAT has so many entries that the free cluster bitmap would // // be too large, we split the FAT into buckets, and only one bucket's // // worth of bits are kept in the bitmap. // //// //// // 如果FAT表的条目太多,使得空闲簇位图过大,我们将FAT分割成多个桶(bucket),// // 位图中仅保留一个桶的位(bit)信息。// // // ULONG NumberOfWindows; // PFAT_WINDOW Windows; // PFAT_WINDOW CurrentWindow;
// // // // A count of the number of file objects that have opened the volume // // for direct access, and their share access state. // //
// CLONG DirectAccessOpenCount; // SHARE_ACCESS ShareAccess;
// // // // A count of the number of file objects that have any file/directory // // opened on this volume, not including direct access. And also the // // count of the number of file objects that have a file opened for // // only read access (i.e., they cannot be modifying the disk). // // //// // 记录在该卷上打开的所有文件/目录的文件对象数目,不包括直接访问。// // 同时也记录只以读方式打开的文件对象数目(即它们不能修改磁盘)。// // // CLONG OpenFileCount; // CLONG ReadOnlyCount;
// // // // The bios parameter block field contains // // an unpacked copy of the bpb for the volume, it is initialized // // during mount time and can be read by everyone else after that. // //
// BIOS_PARAMETER_BLOCK Bpb;
// PUCHAR First0x24BytesOfBootSector;
// // // // The following structure contains information useful to the // // allocation support routines. Many of them are computed from // // elements of the Bpb, but are too involved to recompute every time // // they are needed. // //
// struct {
// LBO RootDirectoryLbo; // Lbo of beginning of root directory // LBO FileAreaLbo; // Lbo of beginning of file area // ULONG RootDirectorySize; // size of root directory in bytes
// ULONG NumberOfClusters; // total number of clusters on the volume // ULONG NumberOfFreeClusters; // number of free clusters on the volume
// UCHAR FatIndexBitSize; // indicates if 12, 16, or 32 bit fat table
// UCHAR LogOfBytesPerSector; // Log(Bios->BytesPerSector) // UCHAR LogOfBytesPerCluster; // Log(Bios->SectorsPerCluster)
// } AllocationSupport;
// // // // The following Mcb is used to keep track of dirty sectors in the Fat. // // Runs of holes denote clean sectors while runs of LBO == VBO denote // // dirty sectors. The VBOs are that of the volume file, starting at // // 0. The granuality of dirt is one sectors, and additions are only // // made in sector chunks to prevent problems with several simultaneous // // updaters. // //// //// // 下面的 Mcb 用于跟踪 FAT 中的脏扇区。连续的空洞表示干净的扇区,而 LBO == VBO 的连续块表示脏扇区。// // VBO 是卷文件的偏移量,从 0 开始。脏扇区的粒度为一个扇区,只能以扇区块为单位进行添加,// // 以防止多个同时更新程序引起的问题。// // // LARGE_MCB DirtyFatMcb;
// // // // The FreeClusterBitMap keeps track of all the clusters in the fat. // // A 1 means occupied while a 0 means free. It allows quick location // // of contiguous runs of free clusters. It is initialized on mount // // or verify. // //// //// // FreeClusterBitMap 用于跟踪 FAT 中的所有簇。// // 1 表示已被占用,0 表示空闲。它可以快速定位连续的空闲簇。// // 在挂载或验证时进行初始化。// // // RTL_BITMAP FreeClusterBitMap;
// // // // The following fast mutex controls access to the free cluster bit map // // and the buckets. // //// //// // 下面的快速互斥锁控制对于空闲簇位图和存储桶的访问。// // // FAST_MUTEX FreeClusterBitMapMutex;
// // // // A resource variable to control access to the volume specific data // // structures // //// //// // 一个资源变量,用于控制对于卷特定数据结构的访问。// // // ERESOURCE Resource;
// // // // A resource to make sure no one changes the volume bitmap while // // you're using it. Only for volumes with NumberOfWindows > 1. // //// //// // 一个资源,确保在使用卷位图时没有人更改它。仅适用于NumberOfWindows > 1的卷。// // // ERESOURCE ChangeBitMapResource;

// // // // The following field points to the file object used to do I/O to // // the virtual volume file. The virtual volume file maps sectors // // 0 through the end of fat and is of a fixed size (determined during // // mount) // //// //// // 下面的字段指向用于与虚拟卷文件进行 I/O 的文件对象。// // 虚拟卷文件映射从 0 扇区到 FAT 的末尾,并具有固定大小(在挂载时确定)。// // // PFILE_OBJECT VirtualVolumeFile;
// // // // The following field contains a record of special pointers used by // // MM and Cache to manipluate section objects. Note that the values // // are set outside of the file system. However the file system on an // // open/create will set the file object's SectionObject field to point // // to this field // //// //// // 下面的字段包含了由 MM(内存管理器)和 Cache(缓存)用于操作分区对象的特殊指针的记录。// // 注意,这些值是在文件系统之外设置的。但是,在打开/创建操作时,文件系统会将文件对象的// // SectionObject 字段设置为指向这个字段。// // // SECTION_OBJECT_POINTERS SectionObjectPointers;
// // // // The following fields is a hint cluster index used by the file system // // when allocating a new cluster. // //
// ULONG ClusterHint;
// // // // This field contains the "DeviceObject" that this volume is // // currently mounted on. Note Vcb->Vpb->RealDevice is constant. // //
// PDEVICE_OBJECT CurrentDevice;
// // // // This is a pointer to the file object and the Fcb which represent the ea data. // //
// PFILE_OBJECT VirtualEaFile; // struct _FCB *EaFcb;
// // // // The following field is a pointer to the file object that has the // // volume locked. if the VcbState has the locked flag set. // //
// PFILE_OBJECT FileObjectWithVcbLocked;
// // // // The following is the head of a list of notify Irps. // //
// LIST_ENTRY DirNotifyList;
// // // // The following is used to synchronize the dir notify list. // //
// PNOTIFY_SYNC NotifySync;
// // // // The following fast mutex is used to synchronize directory stream // // file object creation. // //
// FAST_MUTEX DirectoryFileCreationMutex;
// // // // This field holds the thread address of the current (or most recent // // depending on VcbState) thread doing a verify operation on this volume. // //
// PKTHREAD VerifyThread;
// // // // The following two structures are used for CleanVolume callbacks. // //
// KDPC CleanVolumeDpc; // KTIMER CleanVolumeTimer;
// // // // This field records the last time FatMarkVolumeDirty was called, and // // avoids excessive calls to push the CleanVolume forward in time. // //
// LARGE_INTEGER LastFatMarkVolumeDirtyCall;
// // // // The following fields holds a pointer to a struct which is used to // // hold performance counters. // //
// struct _FILE_SYSTEM_STATISTICS *Statistics;
// // // // The property tunneling cache for this volume // //
// TUNNEL Tunnel;
// // // // The media change count is returned by IOCTL_CHECK_VERIFY and // // is used to verify that no user-mode app has swallowed a media change // // notification. This is only meaningful for removable media. // //// //// // 媒体更改计数是由 IOCTL_CHECK_VERIFY 返回的,用于验证没有用户模式应用程序吞噬了媒体更改通知。// // 这仅对可移动介质有意义。// // // ULONG ChangeCount;
// // // // Preallocated VPB for swapout, so we are not forced to consider // // must succeed pool. // //
// PVPB SwapVpb;
// // // // Per volume threading of the close queues. // //
// LIST_ENTRY AsyncCloseList; // LIST_ENTRY DelayedCloseList;
// // // // Fast mutex used by the ADVANCED FCB HEADER in this structure // //
// FAST_MUTEX AdvancedFcbHeaderMutex;
// // // // This is the close context associated with the Virtual Volume File. // //
// PCLOSE_CONTEXT CloseContext;
// // // // How many close contexts were preallocated on this Vcb // // // #if DBG // ULONG CloseContextCount; // #endif // } VCB;
// VolumeFileHeader:文件系统高级文件控制块头部,用于表示FAT卷的文件头部信息。// VcbLinks:设备队列的链表项,用于将Vcb结构连接到全局Vcb队列。// TargetDeviceObject:目标设备对象指针,表示文件系统与之通信的目标设备对象(例如磁盘驱动器设备对象)。// Vpb:卷参数块指针,表示通过挂载传递给文件系统的卷参数块。// VcbState:设备的内部状态,是一组FSD设备状态标志。// VcbCondition:VCB的状态条件。// RootDcb:指向根目录DCB(目录控制块)的指针。// NumberOfWindows:FAT表的窗口数目,用于处理FAT表过大时的情况。// Windows:FAT窗口的指针数组。// CurrentWindow:当前FAT窗口的指针。// DirectAccessOpenCount:以直接访问方式打开卷的文件对象数量。// ShareAccess:共享访问状态。// OpenFileCount:在该卷上打开的文件/目录的文件对象数量(不包括直接访问方式)。// ReadOnlyCount:只读访问的文件对象数量(不能对磁盘进行修改)。// Bpb:BIOS参数块,存储了解包后的卷的BIOS参数块信息。// First0x24BytesOfBootSector:引导扇区的前24字节。// AllocationSupport:用于分配支持的结构体,包含了从BPB计算得出的一些信息。// DirtyFatMcb:用于跟踪FAT中的脏扇区的Mcb结构。// FreeClusterBitMap:空闲簇位图,用于快速定位连续的空闲簇。// FreeClusterBitMapMutex:用于控制对空闲簇位图和窗口的访问的快速互斥锁。// Resource:控制对卷特定数据结构的访问的资源变量。// ChangeBitMapResource:用于防止在使用卷位图时更改它的资源。// VirtualVolumeFile:用于对虚拟卷文件进行I/O的文件对象指针。// SectionObjectPointers:用于操作段对象的指针,由MM和Cache使用。// ClusterHint:分配新簇时使用的提示簇索引。// CurrentDevice:当前挂载该卷的设备对象。// VirtualEaFile:指向EA数据的文件对象指针。// EaFcb:指向表示EA数据的文件控制块。// FileObjectWithVcbLocked:如果VcbState中设置了锁定标志,则指向具有锁定该卷的文件对象。// DirNotifyList:通知IRP的链表头,用于管理目录通知。// NotifySync:用于同步目录通知列表的同步对象。// DirectoryFileCreationMutex:用于同步目录流文件对象创建的快速互斥锁。// VerifyThread:正在对该卷执行验证操作的线程的地址。// CleanVolumeDpc和CleanVolumeTimer:用于CleanVolume回调的DPC和定时器。// LastFatMarkVolumeDirtyCall:记录上次调用FatMarkVolumeDirty的时间,避免过多地推进CleanVolume的时间。// Statistics:用于存储性能计数器的结构体指针。// Tunnel:用于该卷的属性隧道缓存。// ChangeCount:由IOCTL_CHECK_VERIFY返回的媒体更改计数,用于验证没有用户模式应用程序忽略了媒体更改通知。仅对可移动介质有意义。// SwapVpb:用于换出的预分配VPB,以避免考虑必须成功分配的内存池。// AsyncCloseList和DelayedCloseList:用于异步关闭队列的每卷线程。// AdvancedFcbHeaderMutex:用于该结构中的高级FCB HEADER的快速互斥锁。// CloseContext:与虚拟卷文件关联的关闭上下文。// CloseContextCount:在该VCB上预分配的关闭上下文数量。
PFCB Fcb;


// typedef struct _FCB {
// // // // The following field is used for fast I/O // // // // The following comments refer to the use of the AllocationSize field // // of the FsRtl-defined header to the nonpaged Fcb. // // // // For a directory when we create a Dcb we will not immediately // // initialize the cache map, instead we will postpone it until our first // // call to FatReadDirectoryFile or FatPrepareWriteDirectoryFile. // // At that time we will search the Fat to find out the current allocation // // size (by calling FatLookupFileAllocationSize) and then initialize the // // cache map to this allocation size. // // // // For a file when we create an Fcb we will not immediately initialize // // the cache map, instead we will postpone it until we need it and // // then we determine the allocation size from either searching the // // fat to determine the real file allocation, or from the allocation // // that we've just allocated if we're creating a file. // // // // A value of -1 indicates that we do not know what the current allocation // // size really is, and need to examine the fat to find it. A value // // of than -1 is the real file/directory allocation size. // // // // Whenever we need to extend the allocation size we call // // FatAddFileAllocation which (if we're really extending the allocation) // // will modify the Fat, Mcb, and update this field. The caller // // of FatAddFileAllocation is then responsible for altering the Cache // // map size. // // // // We are now using the ADVANCED fcb header to support filter contexts // // at the stream level // ////// 以下字段用于快速I/O//// 下面的注释是针对非分页的Fcb的FsRtl定义头部中的AllocationSize字段的使用。//// 对于目录,在创建Dcb时,我们不会立即初始化缓存映射,而是将其推迟到我们第一次调用FatReadDirectoryFile或FatPrepareWriteDirectoryFile时。// 在那时,我们将搜索FAT以找出当前的分配大小(通过调用FatLookupFileAllocationSize),然后将缓存映射初始化为此分配大小。//// 对于文件,在创建Fcb时,我们也不会立即初始化缓存映射,而是将其推迟到需要时才初始化,并且从搜索FAT以确定实际文件分配还是从刚刚分配的分配获取分配大小(如果我们正在创建文件)。//// 值为-1表示我们不知道当前的分配大小是多少,需要检查FAT来查找。大于-1的值是真正的文件/目录分配大小。//// 每当我们需要扩展分配大小时,我们调用FatAddFileAllocation,它(如果我们真的扩展了分配)将修改FAT、Mcb,并更新此字段。然后,调用FatAddFileAllocation的调用者负责更改缓存映射的大小。//// 我们现在使用高级的Fcb头部来支持流级别的过滤上下文//
// FSRTL_ADVANCED_FCB_HEADER Header;
// // // // This structure contains fields which must be in non-paged pool. // //
//// 这个结构包含必须在非分页池中的字段。// // PNON_PAGED_FCB NonPaged;
// // // // The head of the fat alloaction chain. FirstClusterOfFile == 0 // // means that the file has no current allocation. // ////// FAT分配链的头部。FirstClusterOfFile == 0表示文件没有当前的分配。//
// ULONG FirstClusterOfFile;
// // // // The links for the queue of all fcbs for a specific dcb off of // // Dcb.ParentDcbQueue. For the root directory this queue is empty // // For a non-existent fcb this queue is off of the non existent // // fcb queue entry in the vcb. // ////// 链接到特定Dcb的所有Fcb的队列,通过Dcb.ParentDcbQueue进行链接。对于根目录,此队列为空。对于不存在的Fcb,此队列位于Vcb的不存在的Fcb队列条目处。// // LIST_ENTRY ParentDcbLinks;
// // // // A pointer to the Dcb that is the parent directory containing // // this fcb. If this record itself is the root dcb then this field // // is null. // ////// 指向包含此Fcb的Dcb的指针。如果此记录本身是根Dcb,则此字段为空。//
// struct _FCB *ParentDcb;
// // // // A pointer to the Vcb containing this Fcb // ////// 指向包含此Fcb的Vcb//
// PVCB Vcb;
// // // // The internal state of the Fcb. This is a collection Fcb state flags. // // Also the shared access for each time this file/directory is opened. // ////// Fcb的内部状态。这是一组Fcb状态标志。还有每次打开此文件/目录时的共享访问。// // ULONG FcbState; // FCB_CONDITION FcbCondition; // SHARE_ACCESS ShareAccess;
// #ifdef SYSCACHE_COMPILE
// // // // For syscache we keep a bitmask that tells us if we have dispatched IO for // // the page aligned chunks of the stream. // ////// 对于syscache,我们保留一个位掩码,告诉我们是否已经为流的页面对齐的块调度了IO。// // PULONG WriteMask; // ULONG WriteMaskData;
// #endif
// // // // A count of the number of file objects that have been opened for // // this file/directory, but not yet been cleaned up yet. This count // // is only used for data file objects, not for the Acl or Ea stream // // file objects. This count gets decremented in FatCommonCleanup, // // while the OpenCount below gets decremented in FatCommonClose. // //// // 这是已经打开但尚未清理的文件对象的数量。该计数仅用于数据文件对象,而不用于Acl或Ea流文件对象。// // 在FatCommonCleanup中递减该计数,而在下面的OpenCount中递减。 // CLONG UncleanCount;
// // // // A count of the number of file objects that have opened // // this file/directory. For files & directories the FsContext of the // // file object points to this record. // //// 打开了该文件/目录的文件对象数量计数。对于文件和目录,文件对象的FsContext指向该记录 // CLONG OpenCount;
// // // // A count of how many of "UncleanCount" handles were opened for // // non-cached I/O. // //
// CLONG NonCachedUncleanCount;
// // // // The following field is used to locate the dirent for this fcb/dcb. // // All directory are opened as mapped files so the only additional // // information we need to locate this dirent (beside its parent directory) // // is the byte offset for the dirent. Note that for the root dcb // // this field is not used. // //
// VBO DirentOffsetWithinDirectory;
// // // // The following field is filled in when there is an Lfn associated // // with this file. It is the STARTING offset of the Lfn. // //
// VBO LfnOffsetWithinDirectory;
// // // // Thess entries is kept in ssync with the dirent. It allows a more // // accurate verify capability and speeds up FatFastQueryBasicInfo(). // //
// LARGE_INTEGER CreationTime; // LARGE_INTEGER LastAccessTime; // LARGE_INTEGER LastWriteTime;
// // // // Valid data to disk // //
// ULONG ValidDataToDisk;
// // // // The following field contains the retrieval mapping structure // // for the file/directory. Note that for the Root Dcb this // // structure is set at mount time. Also note that in this // // implementation of Fat the Mcb really maps VBOs to LBOs and not // // VBNs to LBNs. // //
// LARGE_MCB Mcb;
// // // // The following union is cased off of the node type code for the fcb. // // There is a seperate case for the directory versus file fcbs. // //
// union {
// // // // A Directory Control Block (Dcb) // //
// struct {
// // // // A queue of all the fcbs/dcbs that are opened under this // // Dcb. // //
// LIST_ENTRY ParentDcbQueue;
// // // // The following field points to the file object used to do I/O to // // the directory file for this dcb. The directory file maps the // // sectors for the directory. This field is initialized by // // CreateRootDcb but is left null by CreateDcb. It isn't // // until we try to read/write the directory file that we // // create the stream file object for non root dcbs. // //
// ULONG DirectoryFileOpenCount; // PFILE_OBJECT DirectoryFile;
// // // // If the UnusedDirentVbo is != 0xffffffff, then the dirent at this // // offset is guarenteed to unused. A value of 0xffffffff means // // it has yet to be initialized. Note that a value beyond the // // end of allocation means that there an unused dirent, but we // // will have to allocate another cluster to use it. // // // // DeletedDirentHint contains lowest possible VBO of a deleted // // dirent (assuming as above that it is not 0xffffffff). // //
// VBO UnusedDirentVbo; // VBO DeletedDirentHint;
// // // // The following two entries links together all the Fcbs // // opened under this Dcb sorted in a splay tree by name. // // // // I'd like to go into why we have (and must have) two separate // // splay trees within the current fastfat architecture. I will // // provide some insight into what would have to change if we // // wanted to have a single UNICODE tree. // // // // What makes FAT unique is that both Oem and Unicode names sit // // side by side on disk. Several unique UNICODE names coming // // into fastfat can match a single OEM on-disk name, and there // // is really no way to enumerate all the possible UNICODE // // source strings that can map to a given OEM name. This argues // // for converting the incomming UNICODE name into OEM, and then // // running through an OEM splay tree of the open files. This // // works well when there are only OEM names on disk. // // // // The UNICODE name on disk can be VERY different from the short // // name in the DIRENT and not even representable in the OEM code // // page. Even if it were representable in OEM, it is possible // // that a case varient of the original UNICODE name would match // // a different OEM name, causing us to miss the Fcb in the // // prefix lookup phase. In these cases, we must put UNICODE // // name in the splay to guarentee that we find any case varient // // of the input UNICODE name. See the routine description of // // FatConstructNamesInFcb() for a detailed analysis of how we // // detect this case. // // // // The fundamental limitation we are imposing here is that if // // an Fcb exists for an open file, we MUST find it during the // // prefix stage. This is a basic premise of the create path // // in fastfat. In fact if we later find it gravelling through // // the disk (but not the splay tree), we will bug check if we // // try to add a duplicate entry to the splay tree (not to // // mention having two Fcbs). If we had some mechanism to deal // // with cases (and they would be rare) that we don't find the // // entry in the splay tree, but the Fcb is actually in there, // // then we could go to a single UNICODE splay tree. While // // this uses more pool for the splay tree, and makes string // // compares maybe take a bit as longer, it would eliminate the // // need for any NLS conversion during the prefix phase, so it // // might really be a net win. // // // // The current scheme was optimized for non-extended names // // (i.e. US names). As soon as you start using extended // // characters, then it is clearly a win as many code paths // // become active that would otherwise not be needed if we // // only had a single UNICODE splay tree. // // // // We may think about changing this someday. // //


// // 下面的两个条目将所有在此Dcb下打开的Fcbs按名称在一个Splay树中排序连接在一起。// //// // 我想解释一下为什么我们在当前的Fastfat架构中必须有(并且必须有)两个独立的Splay树。// // 我将提供一些关于如果我们想要一个单一的UNICODE树会有什么改变的见解。// //// // FAT的独特之处在于OEM和Unicode名称在磁盘上并排存在。几个不同的UNICODE名称// // 进入fastfat时可以匹配一个单独的OEM磁盘名称,并且真的没有办法列举出所有可能的// // UNICODE源字符串,可以映射到给定的OEM名称。这就说明了将输入的UNICODE名称转换// // 为OEM名称,然后通过一个OEM的Splay树来处理打开的文件的方式。当磁盘上只有OEM名称时,// // 这种方法很有效。// //// // 磁盘上的UNICODE名称可以与DIRENT中的短名称非常不同,甚至不能在OEM代码页中表示。// // 即使它可以在OEM中表示,也有可能原始UNICODE名称的大小写变体会与不同的OEM名称匹配,// // 导致我们在前缀查找阶段错过Fcb。在这些情况下,我们必须将UNICODE名称放入Splay中,// // 以确保我们找到输入UNICODE名称的任何大小写变体。请参阅FatConstructNamesInFcb()// // 的例程描述,详细分析了我们如何检测这种情况。// //// // 我们在这里施加的根本限制是,如果一个Fcb存在于打开的文件中,我们必须在前缀阶段// // 找到它。这是fastfat中创建路径的基本前提。事实上,如果我们稍后通过磁盘(而不是// // Splay树)找到它,如果尝试向Splay树添加重复条目,我们将引发bug检查(更不用说有两个// // Fcb了)。如果我们有一些机制来处理我们在Splay树中找不到条目但实际上Fcb确实在其中的// // 情况(尽管它们很少见),那么我们可以转向单个UNICODE的Splay树。虽然这会在Splay树// // 中使用更多的池,并且可能使字符串比较稍微变长,但它消除了前缀阶段中的任何NLS转换的需要,因此可能真的是一个净收益。//// 当前的方案针对非扩展名称(即美国名称)进行了优化。一旦开始使用扩展字符,那么它显然是一种优势,// 因为许多代码路径将变得活跃,否则在只有单个UNICODE的Splay树的情况下是不需要的。//// 也许有一天我们会考虑改变这一点。 // PRTL_SPLAY_LINKS RootOemNode; // PRTL_SPLAY_LINKS RootUnicodeNode;
// // // // The following field keeps track of free dirents, i.e., // // dirents that are either unallocated for deleted. // //// //// // 以下字段跟踪空闲的目录项,即未分配或已删除的目录项。// // // RTL_BITMAP FreeDirentBitmap;
// // // // Since the FCB specific part of this union is larger, use // // the slack here for an initial bitmap buffer. Currently // // there is enough space here for an 8K cluster. // //// //// // 由于此联合中 FCB 特定部分较大,因此在此处使用剩余空间作为初始位图缓冲区。当前这里有足够的空间容纳一个8K簇。// // // ULONG FreeDirentBitmapBuffer[1];
// } Dcb;
// // // // A File Control Block (Fcb) // //
// struct {
// // // // The following field is used by the filelock module // // to maintain current byte range locking information. // //// //// // 以下字段由 filelock 模块用于维护当前的字节范围锁定信息。// // // FILE_LOCK FileLock;
// // // // The following field is used by the oplock module // // to maintain current oplock information. // ////// // 以下字段由 oplock 模块用于维护当前的 oplock 信息。// // // OPLOCK Oplock;
// // // // This pointer is used to detect writes that eminated in the // // cache manager's lazywriter. It prevents lazy writer threads, // // who already have the Fcb shared, from trying to acquire it // // exclusive, and thus causing a deadlock. // //
// //// // 此指针用于检测源自缓存管理器的惰性写入的写操作。它防止已共享 Fcb 的惰性写入线程试图以独占方式获取它,从而导致死锁。// // // PVOID LazyWriteThread;
// } Fcb;
// } Specific;
// // // // The following field is used to verify that the Ea's for a file // // have not changed between calls to query for Ea's. It is compared // // with a similar field in a Ccb. // // // // IMPORTANT!! **** DO NOT MOVE THIS FIELD **** // // // // The slack space in the union above is computed from // // the field offset of the EaModificationCount. // //// //// // 以下字段用于验证文件的扩展属性(Ea)在查询 Ea 时未发生更改。它与 Ccb 中的类似字段进行比较。// //// // 重要提示!!**** 不要移动此字段 ****// //// // 上述联合体中的空闲空间是根据 EaModificationCount 字段的偏移量计算的。// // // ULONG EaModificationCount;
// // // // The following field is the fully qualified file name for this FCB/DCB // // starting from the root of the volume, and last file name in the // // fully qualified name. // //// // 以下字段是该 FCB/DCB 的完全限定文件名,从卷的根目录开始,以及完全限定名中的最后一个文件名。 // FILE_NAME_NODE ShortName;
// // // // The following field is only filled in if it is needed with the user's // // opened path // //// // 如果需要,以下字段将仅填充用户打开的路径 // UNICODE_STRING FullFileName;
// USHORT FinalNameLength;
// // // // To make life simpler we also keep in the Fcb/Dcb a current copy of // // the fat attribute byte for the file/directory. This field must // // also be updated when we create the Fcb, modify the File, or verify // // the Fcb // ////为了简化操作,我们还在Fcb/Dcb中保留了文件/目录的当前FAT属性字节的副本。当创建Fcb、修改文件或验证Fcb时,这个字段也必须进行更新。
// UCHAR DirentFatFlags;
// // // // The case preserved long filename // //// // 保留大小写的长文件名 // UNICODE_STRING ExactCaseLongName;
// // // // If the UNICODE Lfn is fully expressible in the system Oem code // // page, then we will store it in a prefix table, otherwise we will // // store the last UNICODE name in the Fcb. In both cases the name // // has been upcased. // // // // Note that we may need neither of these fields if an LFN was strict // // 8.3 or differed only in case. Indeed if there wasn't an LFN, we // // don't need them at all. // //// //// // 如果UNICODE Lfn完全可表示为系统Oem代码页中的字符,则我们将其存储在前缀表中,否则将将最后一个UNICODE名称存储在Fcb中。在这两种情况下,名称都已经被大写处理。// //// // 需要注意的是,如果LFN是严格的8.3格式,或者只是大小写不同,那么我们可能都不需要这些字段。实际上,如果没有LFN,我们根本不需要它们。// // // union {
// // // // This first field is present if FCB_STATE_HAS_OEM_LONG_NAME // // is set in the FcbState. // //// // 如果FcbState中设置了FCB_STATE_HAS_OEM_LONG_NAME,则存在此字段。 // FILE_NAME_NODE Oem;
// // // // This first field is present if FCB_STATE_HAS_UNICODE_LONG_NAME // // is set in the FcbState. // //// // 如果FcbState中设置了FCB_STATE_HAS_UNICODE_LONG_NAME,则存在此字段。 // FILE_NAME_NODE Unicode;
// } LongName;
// // // // Defragmentation / ReallocateOnWrite synchronization object. This // // is filled in by FatMoveFile() and affects the read and write paths. // ///// 碎片整理/重分配同步对象。由FatMoveFile()填充,影响读取和写入路径。 // PKEVENT MoveFileEvent;
// } FCB, *PFCB;

// Header:用于快速I/O的字段。// NonPaged:指向非分页池中的字段。// FirstClusterOfFile:文件分配链的头部。// ParentDcbLinks:用于在特定DCB的所有FCB队列上链接的队列。// ParentDcb:指向包含此FCB的父目录DCB的指针。// Vcb:指向包含此FCB的VCB的指针。// FcbState:FCB的内部状态和共享访问。// UncleanCount:已打开但尚未清理的文件对象数目。// OpenCount:已打开此文件/目录的文件对象数目。// NonCachedUncleanCount:以非缓存I/O打开的 "UncleanCount" 句柄数目。// DirentOffsetWithinDirectory:在目录中定位此FCB/Dcb的目录项的字节偏移量。// LfnOffsetWithinDirectory:与此文件关联的长文件名的起始偏移量。// CreationTime、LastAccessTime、LastWriteTime:用于验证目录项的时间戳。// ValidDataToDisk:要写入磁盘的有效数据。// Mcb:文件/目录的检索映射结构。// Specific:针对FCB和DCB的联合体,包含相应的字段。// EaModificationCount:用于验证文件的扩展属性是否在查询期间发生更改。// ShortName:FCB/DCB的完全限定文件名。// FullFileName:用户打开的路径的Unicode字符串。// DirentFatFlags:文件/目录的FAT属性字节的当前副本。// ExactCaseLongName:区分大小写的长文件名。// LongName:存储长文件名的结构体。// MoveFileEvent:用于碎片整理/重写分配的同步对象。


PCCB Ccb;
//typedef struct _CCB { // // // // Type and size of this record (must be FAT_NTC_CCB) // //
// NODE_TYPE_CODE NodeTypeCode; // 记录类型代码 // NODE_BYTE_SIZE NodeByteSize; // 记录字节大小
// // // // Define a 24bit wide field for Flags, but a UCHAR for Wild Cards Present // // since it is used so often. Line these up on byte boundaries for grins. // //
// ULONG Flags:24; // 标志位,占用24位 // BOOLEAN ContainsWildCards; // 是否包含通配符
// // // // Overlay a close context on the data of the CCB. The remaining // // fields are not useful during close, and we would like to avoid // // paying extra pool for it. // // // // // // 在CCB的数据上叠加一个关闭上下文。在关闭过程中,其余的字段是无用的,我们希望避免额外的内存池开销。 // //
// union { // struct {
// // // // Save the offset to start search from. // //
// VBO OffsetToStartSearchFrom; // 保存起始搜索偏移量
// // // // The query template is used to filter directory query requests. // // It originally is set to null and on the first call to NtQueryDirectory // // it is set to the input filename or "*" if the name is not supplied. // // All subsequent queries then use this template. // // // // The Oem structure is a union because if the name is wild, we store // // the arbitrary length string, while if the name is constant, we store // // the 8.3 representation for fast comparison. // ////// // 查询模板用于过滤目录查询请求。// // 最初设置为空,在第一次调用NtQueryDirectory时,// // 如果输入文件名存在,则设置为输入文件名,否则设置为"*"。// // 后续的查询都使用这个模板。// //// // Oem结构体是一个联合体,因为如果名称是通配符,我们会存储任意长度的字符串,// // 而如果名称是常量,我们会存储用于快速比较的8.3表示。// // // union {
// // // // If the template contains a wild card, use this. // //// //// // 如果查询模板包含通配符,使用此字段。// // // OEM_STRING Wild; // 通配符模板
// // // // If the name is constant, use this part. // //
// FAT8DOT3 Constant; // 常量名称
// } OemQueryTemplate;
// UNICODE_STRING UnicodeQueryTemplate; // Unicode查询模板
// // // // The field is compared with the similar field in the Fcb to determine // // if the Ea's for a file have been modified. // //// //// // 该字段与Fcb中的相似字段进行比较,以确定文件的扩展属性(Ea)是否已被修改。// // // ULONG EaModificationCount; // EA修改计数
// // // // The following field is used as an offset into the Eas for a // // particular file. This will be the offset for the next // // Ea to return. A value of 0xffffffff indicates that the // // Ea's are exhausted. // //// //// // 下面的字段用作特定文件的扩展属性(Ea)的偏移量。// // 这将是下一个要返回的扩展属性的偏移量。// // 值为0xffffffff表示扩展属性已经耗尽。// // // ULONG OffsetOfNextEaToReturn; // 下一个要返回的EA的偏移量
// };
// CLOSE_CONTEXT CloseContext; // 关闭上下文
// };//} CCB; PDCB ParentDcb;//typedef FCB DCB; PDCB FinalDcb = NULL;
// PVCB(Volume Control Block):卷控制块,用于表示文件系统的卷(卷是文件系统中的逻辑存储单元)。Vcb结构包含与特定卷相关的信息,如卷标、簇大小、簇位图等。
// PFCB(File Control Block):文件控制块,用于表示打开的文件对象。Fcb结构包含有关特定文件的信息,如文件大小、时间戳、文件属性等。
// PCCB(Context Control Block):上下文控制块,用于表示打开的文件的上下文信息。Ccb结构包含与文件相关的上下文数据,如当前文件指针位置、缓冲区等。
// PDCB(Directory Control Block):目录控制块,用于表示目录(文件夹)。Dcb结构包含与特定目录相关的信息,如目录的文件列表、子目录列表等。 UNICODE_STRING FinalName; UNICODE_STRING RemainingPart; UNICODE_STRING NextRemainingPart; UNICODE_STRING UpcasedFinalName; WCHAR UpcasedBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE];
OEM_STRING OemFinalName; UCHAR OemBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE*2];
BOOLEAN FreeOemBuffer; BOOLEAN FreeUpcasedBuffer;
PDIRENT Dirent; PBCB DirentBcb = NULL; ULONG LfnByteOffset; ULONG DirentByteOffset;
BOOLEAN PostIrp = FALSE; BOOLEAN OplockPostIrp = FALSE; BOOLEAN TrailingBackslash; BOOLEAN FirstLoop = TRUE;
CCB LocalCcb; UNICODE_STRING Lfn; WCHAR LfnBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE];
// // Get the current IRP stack location //
IrpSp = IoGetCurrentIrpStackLocation( Irp );//获取当前的irp
DebugTrace(+1, Dbg, "FatCommonCreate\n", 0 ); DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); DebugTrace( 0, Dbg, "->Flags = %08lx\n", Irp->Flags ); DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject ); DebugTrace( 0, Dbg, " ->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject ); DebugTrace( 0, Dbg, " ->FileName = %Z\n", &IrpSp->FileObject->FileName ); DebugTrace( 0, Dbg, "->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart ); DebugTrace( 0, Dbg, "->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart ); DebugTrace( 0, Dbg, "->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer ); DebugTrace( 0, Dbg, "->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); DebugTrace( 0, Dbg, "->Options = %08lx\n", IrpSp->Parameters.Create.Options ); DebugTrace( 0, Dbg, "->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes ); DebugTrace( 0, Dbg, "->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess ); DebugTrace( 0, Dbg, "->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength );
// // This is here because the Win32 layer can't avoid sending me double // beginning backslashes. //
if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) && (IrpSp->FileObject->FileName.Buffer[1] == L'\\') && (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) {
IrpSp->FileObject->FileName.Length -= sizeof(WCHAR);
RtlMoveMemory( &IrpSp->FileObject->FileName.Buffer[0], &IrpSp->FileObject->FileName.Buffer[1], IrpSp->FileObject->FileName.Length );
// // If there are still two beginning backslashes, the name is bogus. //
if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) && (IrpSp->FileObject->FileName.Buffer[1] == L'\\') && (IrpSp->FileObject->FileName.Buffer[0] == L'\\')) {
FatCompleteRequest( IrpContext, Irp, STATUS_OBJECT_NAME_INVALID );
DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_OBJECT_NAME_INVALID\n", 0); return STATUS_OBJECT_NAME_INVALID; } }
// // Reference our input parameters to make things easier //
ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL );
FileObject = IrpSp->FileObject; FileName = FileObject->FileName; RelatedFileObject = FileObject->RelatedFileObject; AllocationSize = Irp->Overlay.AllocationSize.LowPart; EaBuffer = Irp->AssociatedIrp.SystemBuffer; DesiredAccess = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess; Options = IrpSp->Parameters.Create.Options; FileAttributes = (UCHAR)(IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL); ShareAccess = IrpSp->Parameters.Create.ShareAccess; EaLength = IrpSp->Parameters.Create.EaLength;

// // Set up the file object's Vpb pointer in case anything happens. // This will allow us to get a reasonable pop-up. //
if ( RelatedFileObject != NULL ) { FileObject->Vpb = RelatedFileObject->Vpb; }
// // Force setting the archive bit in the attributes byte to follow OS/2, // & DOS semantics. Also mask out any extraneous bits, note that // we can't use the ATTRIBUTE_VALID_FLAGS constant because that has // the control and normal flags set. // // Delay setting ARCHIVE in case this is a directory: DavidGoe 2/16/95 //
FileAttributes &= (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE );
// // Locate the volume device object and Vcb that we are trying to access //
Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;// 首先,通过IoGetCurrentIrpStackLocation获取当前IRP堆栈位置的指针IrpSp,然后输出一些调试信息,包括相关的IRP和参数的值。
// 接下来,代码检查文件名是否以双斜杠开头。这段注释中提到的问题是,Windows Win32层有时无法避免发送带有双斜杠开头的文件名给文件系统驱动。如果检测到文件名以双斜杠开头,//代码会将第一个斜杠去除,并继续检查是否还有两个斜杠,如果是,则认为文件名无效,完成请求并返回STATUS_OBJECT_NAME_INVALID表示对象名无效。
// 然后,代码引用输入参数以便后续操作。例如,FileObject表示文件对象,FileName表示文件名,AllocationSize表示文件的分配大小,等等。
// 接着,代码设置文件对象的Vpb字段,以便在发生错误时能够获取适当的弹出窗口。
// 接下来,代码强制将文件属性中的存档位(Archive)设置为1,以符合OS/2和DOS的语义。此外,还将文件属性掩码为有效的属性标志,包括只读、隐藏、系统和存档。
// 最后,代码定位要访问的卷设备对象和Vcb(卷控制块),并将其赋值给变量Vcb,以便后续使用。///-------------------------------- // // Decipher Option flags and values //
// // If this is an open by fileid operation, just fail it explicitly. FAT's // source of fileids is not reversible for open operations. //
if (BooleanFlagOn( Options, FILE_OPEN_BY_FILE_ID )) {
FatCompleteRequest( IrpContext, Irp, STATUS_NOT_IMPLEMENTED ); return STATUS_NOT_IMPLEMENTED; }
DirectoryFile = BooleanFlagOn( Options, FILE_DIRECTORY_FILE ); NonDirectoryFile = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE ); SequentialOnly = BooleanFlagOn( Options, FILE_SEQUENTIAL_ONLY ); NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING ); NoEaKnowledge = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE ); DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE );
TemporaryFile = BooleanFlagOn( IrpSp->Parameters.Create.FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
CreateDisposition = (Options >> 24) & 0x000000ff;
IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ); OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY );
CreateDirectory = (BOOLEAN)(DirectoryFile && ((CreateDisposition == FILE_CREATE) || (CreateDisposition == FILE_OPEN_IF)));
OpenDirectory = (BOOLEAN)(DirectoryFile && ((CreateDisposition == FILE_OPEN) || (CreateDisposition == FILE_OPEN_IF)));

// // Make sure the input large integer is valid and that the dir/nondir // indicates a storage type we understand. //
if (Irp->Overlay.AllocationSize.HighPart != 0 || (DirectoryFile && NonDirectoryFile)) {
FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
DebugTrace(-1, Dbg, "FatCommonCreate -> STATUS_INVALID_PARAMETER\n", 0); return STATUS_INVALID_PARAMETER; }// 首先,代码检查是否是通过文件ID进行打开的操作,如果是,则返回STATUS_NOT_IMPLEMENTED,表示不支持通过文件ID打开文件的操作。
// 接下来,根据传入的选项参数进行标志位的设置,这些标志位包括:
// DirectoryFile: 是否是目录文件// NonDirectoryFile: 是否是非目录文件// SequentialOnly: 是否仅支持顺序访问// NoIntermediateBuffering: 是否禁用中间缓冲// NoEaKnowledge: 是否不需要EA(扩展属性)的相关信息// DeleteOnClose: 是否在关闭时删除文件// 然后,根据文件属性判断是否是临时文件。
// 接下来,解析Options参数中的CreateDisposition,确定文件的创建或打开方式。
// 然后,根据IrpSp->Flags中的标志位判断是否是分页文件或目标目录的打开操作。
// 最后,代码进行一些简单的验证。它检查输入的AllocationSize是否有效(高32位必须为0)以及DirectoryFile和NonDirectoryFile是否同时设置(两者互斥)。//如果验证失败,将返回STATUS_INVALID_PARAMETER表示参数无效。////---------------------------------- // // Acquire exclusive access to the vcb, and enqueue the Irp if // we didn't get it. //
if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
Iosb.Status = FatFsdPostRequest( IrpContext, Irp );
DebugTrace(-1, Dbg, "FatCommonCreate -> %08lx\n", Iosb.Status ); return Iosb.Status; }
// // Initialize the DirentBcb to null //
DirentBcb = NULL;
// // Initialize our temp strings with their stack buffers. //
OemFinalName.Length = 0; OemFinalName.MaximumLength = sizeof( OemBuffer); OemFinalName.Buffer = OemBuffer;
UpcasedFinalName.Length = 0; UpcasedFinalName.MaximumLength = sizeof( UpcasedBuffer); UpcasedFinalName.Buffer = UpcasedBuffer;
Lfn.Length = 0; Lfn.MaximumLength = sizeof( LfnBuffer); Lfn.Buffer = LfnBuffer;// 首先尝试获取卷控制块(Vcb)的排它访问权,通过调用FatAcquireExclusiveVcb函数。如果无法获取Vcb的排它访问权,说明当前无法处理IRP,因此将IRP提交到延迟队列中,并返回相应的状态。
// 接下来,对一些临时字符串进行初始化,这些字符串用于存储文件名的不同形式。具体而言:
// OemFinalName是一个ANSI字符串,用于存储转换为OEM字符集的文件名。// UpcasedFinalName是一个Unicode字符串,用于存储转换为大写形式的文件名。// Lfn是一个Unicode字符串,用于存储长文件名(Long File Name)。// 这些字符串都使用了栈上的缓冲区作为存储空间。 try {
// // Make sure the vcb is in a usable condition. This will raise // and error condition if the volume is unusable //
FatVerifyVcb( IrpContext, Vcb );
// // If the Vcb is locked then we cannot open another file //
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_LOCKED)) {
DebugTrace(0, Dbg, "Volume is locked\n", 0);
Status = STATUS_ACCESS_DENIED; if (Vcb->VcbCondition != VcbGood) {
Status = STATUS_VOLUME_DISMOUNTED; } try_return( Iosb.Status = Status ); }
// // Don't allow the DELETE_ON_CLOSE option if the volume is // write-protected. //
if (DeleteOnClose && FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
// // Set the real device for the pop-up info, and set the verify // bit in the device object, so that we will force a verify // in case the user put the correct media back in. //
IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, Vcb->Vpb->RealDevice );
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); }
// // If this is a fat32 volume, EA's are not supported. //
if (EaBuffer != NULL && FatIsFat32(Vcb)) {
try_return( Iosb.Status = STATUS_EAS_NOT_SUPPORTED ); }
// // Check if we are opening the volume and not a file/directory. // We are opening the volume if the name is empty and there // isn't a related file object. If there is a related file object // then it is the Vcb itself. //
if (FileName.Length == 0) {
PVCB DecodeVcb;
if (RelatedFileObject == NULL || FatDecodeFileObject( RelatedFileObject, &DecodeVcb, &Fcb, &Ccb ) == UserVolumeOpen) {
ASSERT( RelatedFileObject == NULL || Vcb == DecodeVcb );
// // Check if we were to open a directory //
if (DirectoryFile) {
DebugTrace(0, Dbg, "Cannot open volume as a directory\n", 0);
try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); }
// // Can't open the TargetDirectory of the DASD volume. //
if (OpenTargetDirectory) {
try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); }
DebugTrace(0, Dbg, "Opening the volume, Vcb = %08lx\n", Vcb);
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenVolume( IrpContext, FileObject, Vcb, DesiredAccess, ShareAccess, CreateDisposition );
Irp->IoStatus.Information = Iosb.Information; try_return( Iosb.Status ); } }// 首先,通过调用FatVerifyVcb函数,确保Vcb(卷控制块)处于可用状态。如果发现卷不可用,则会引发错误条件,并根据卷的状态设置相应的错误状态。
// 接下来,检查Vcb是否已被锁定。如果Vcb的状态标志中设置了VCB_STATE_FLAG_LOCKED,表示卷已被锁定,此时无法打开其他文件。如果发现卷已被锁定,则将返回适当的错误状态。
// 然后,检查是否允许使用DELETE_ON_CLOSE选项。如果Vcb的状态标志中设置了VCB_STATE_FLAG_WRITE_PROTECTED(表示卷为只读),且指定了DELETE_ON_CLOSE选项,则无法执行删除操作。//在此情况下,将设置硬错误和验证设备,以便在用户将正确的介质放回时触发验证,并引发相应的错误状态。
// 接下来,对于FAT32卷,不支持扩展属性(EA),因此如果EaBuffer不为空且卷是FAT32卷,则返回STATUS_EAS_NOT_SUPPORTED。
// 然后,检查是否正在打开卷而不是文件/目录。如果文件名长度为0且不存在相关的文件对象(RelatedFileObject为空),则表示正在打开卷。在这种情况下,//首先解码文件对象以获取相关的Vcb,并进行一些检查。如果指定了打开目录的选项,则返回STATUS_NOT_A_DIRECTORY;如果指定了打开目标目录的选项,//则返回STATUS_INVALID_PARAMETER。如果通过了这些检查,则调用FatOpenVolume函数打开卷。
// 最后,将Iosb结构中的信息(如打开的文件大小等)传递给Irp的IoStatus字段,并返回对应的Iosb.Status。

// // If there is a related file object then this is a relative open. // The related file object is the directory to start our search at. // Return an error if it is not a directory. //
if (RelatedFileObject != NULL) {
PVCB RelatedVcb; PDCB RelatedDcb; PCCB RelatedCcb; TYPE_OF_OPEN TypeOfOpen;
TypeOfOpen = FatDecodeFileObject( RelatedFileObject, &RelatedVcb, &RelatedDcb, &RelatedCcb );
if (TypeOfOpen != UserFileOpen && TypeOfOpen != UserDirectoryOpen) {
DebugTrace(0, Dbg, "Invalid related file object\n", 0);
try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND ); }
// // A relative open must be via a relative path. //
if (FileName.Length != 0 && FileName.Buffer[0] == L'\\') {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); }
// // Set up the file object's Vpb pointer in case anything happens. //
ASSERT( Vcb == RelatedVcb );
FileObject->Vpb = RelatedFileObject->Vpb;
// // Now verify the related Fcb so we don't get in trouble later // by assuming its in good shape. //
FatVerifyFcb( IrpContext, RelatedDcb );
ParentDcb = RelatedDcb;
} else {
// // This is not a relative open, so check if we're // opening the root dcb //
if ((FileName.Length == sizeof(WCHAR)) && (FileName.Buffer[0] == L'\\')) {
// // Check if we were not supposed to open a directory //
if (NonDirectoryFile) {
DebugTrace(0, Dbg, "Cannot open root directory as a file\n", 0);
try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); }
// // Can't open the TargetDirectory of the root directory. //
if (OpenTargetDirectory) {
try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); }
// // Not allowed to delete root directory. //
if (DeleteOnClose) {
try_return( Iosb.Status = STATUS_CANNOT_DELETE ); }
DebugTrace(0, Dbg, "Opening root dcb\n", 0);
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenRootDcb( IrpContext, FileObject, Vcb, DesiredAccess, ShareAccess, CreateDisposition );
Irp->IoStatus.Information = Iosb.Information; try_return( Iosb.Status ); }
// // Nope, we will be opening relative to the root directory. //
ParentDcb = Vcb->RootDcb; }////



// 如果存在相关的文件对象(RelatedFileObject不为空),则表示这是一个相对路径的打开操作。相关的文件对象是要开始搜索的目录。//如果相关的文件对象不是一个目录(既不是文件打开,也不是目录打开),则返回STATUS_OBJECT_PATH_NOT_FOUND错误。
// 接下来,检查文件名是否为空(FileName.Length != 0)且文件名的第一个字符为反斜杠(\)。如果是这种情况,//表示相对打开应该是通过相对路径进行的,因此返回STATUS_OBJECT_NAME_INVALID错误。
// 然后,设置文件对象的Vpb指针,以防发生任何意外情况。并调用FatVerifyFcb函数验证相关的DCB(目录控制块),以确保它处于良好状态。
// 如果没有相关的文件对象,即不是相对路径的打开操作,则检查是否正在打开根目录(通过检查文件名是否为单个反斜杠 \)。//如果是这种情况,则进行一些检查:如果指定了打开非目录文件的选项,则返回STATUS_FILE_IS_A_DIRECTORY错误;如果指定了打开目标目录的选项,//则返回STATUS_INVALID_PARAMETER错误;如果指定了DeleteOnClose选项,则返回STATUS_CANNOT_DELETE错误。然后,//调用FatOpenRootDcb函数打开根目录的DCB。
// 最后,设置ParentDcb变量为相应的目录控制块,用于后续的文件创建操作。

// // FatCommonCreate(): trailing backslash check //

if ((FileName.Length != 0) && (FileName.Buffer[FileName.Length/sizeof(WCHAR)-1] == L'\\')) {
FileName.Length -= sizeof(WCHAR); TrailingBackslash = TRUE;
} else {
TrailingBackslash = FALSE; }
// // Check for max path. We might want to tighten this down to DOS MAX_PATH // for maximal interchange with non-NT platforms, but for now defer to the // possibility of something depending on it. //
if (ParentDcb->FullFileName.Buffer == NULL) {
FatSetFullFileNameInFcb( IrpContext, ParentDcb ); }
if ((USHORT) (ParentDcb->FullFileName.Length + sizeof(WCHAR) + FileName.Length) <= FileName.Length) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); }//

// 首先,检查文件名的长度是否不为零,并且文件名的最后一个字符是否为反斜杠(\)。如果满足条件,则将文件名长度减去一个字符的大小(sizeof(WCHAR)),//并将TrailingBackslash标记设置为TRUE,表示存在尾部反斜杠。
// 接下来,检查父目录的完整文件名的缓冲区是否为NULL,如果是,则调用FatSetFullFileNameInFcb函数为父目录的DCB设置完整文件名。
// 然后,计算父目录的完整文件名长度加上一个字符大小和文件名长度的和,并将其转换为USHORT类型进行比较。如果长度超过了文件名长度,说明路径名过长,返回STATUS_OBJECT_NAME_INVALID错误。

// // We loop here until we land on an Fcb that is in a good // condition. This way we can reopen files that have stale handles // to files of the same name but are now different. //最后,通过一个循环,找到一个处于良好状态的FCB(文件控制块)。这样可以重新打开具有陈旧句柄的同名文件,而现在它们是不同的文件。
while ( TRUE ) {
Fcb = ParentDcb; RemainingPart = FileName;
// // Now walk down the Dcb tree looking for the longest prefix. // This one exit condition in the while() is to handle a // special case condition (relative NULL name open), the main // exit conditions are at the bottom of the loop. //
while (RemainingPart.Length != 0) {
PFCB NextFcb;
FsRtlDissectName( RemainingPart, &FinalName, &NextRemainingPart );
// // If RemainingPart starts with a backslash the name is // invalid. // Check for no more than 255 characters in FinalName //
if (((NextRemainingPart.Length != 0) && (NextRemainingPart.Buffer[0] == L'\\')) || (FinalName.Length > 255*sizeof(WCHAR))) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); }
// // Now, try to convert this one component into Oem and search // the splay tree. If it works then that's great, otherwise // we have to try with the UNICODE name instead. //
FatEnsureStringBufferEnough( &OemFinalName, FinalName.Length);
Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE );
if (NT_SUCCESS(Status)) {
NextFcb = FatFindFcb( IrpContext, &Fcb->Specific.Dcb.RootOemNode, (PSTRING)&OemFinalName, &FileNameOpenedDos );
} else {
NextFcb = NULL; OemFinalName.Length = 0;
if (Status != STATUS_UNMAPPABLE_CHARACTER) {
try_return( Iosb.Status = Status ); } }
// // If we didn't find anything searching the Oem space, we // have to try the Unicode space. To save cycles in the // common case that this tree is empty, we do a quick check // here. //
if ((NextFcb == NULL) && Fcb->Specific.Dcb.RootUnicodeNode) {
// // First downcase, then upcase the string, because this // is what happens when putting names into the tree (see // strucsup.c, FatConstructNamesInFcb()). //
FatEnsureStringBufferEnough( &UpcasedFinalName, FinalName.Length);
Status = RtlDowncaseUnicodeString(&UpcasedFinalName, &FinalName, FALSE ); ASSERT( NT_SUCCESS( Status ));
Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &UpcasedFinalName, FALSE ); ASSERT( NT_SUCCESS( Status ));
NextFcb = FatFindFcb( IrpContext, &Fcb->Specific.Dcb.RootUnicodeNode, (PSTRING)&UpcasedFinalName, &FileNameOpenedDos ); }
// // If we got back an Fcb then we consumed the FinalName // legitimately, so the remaining name is now RemainingPart. //
if (NextFcb != NULL) { Fcb = NextFcb; RemainingPart = NextRemainingPart; }
if ((NextFcb == NULL) || (NodeType(NextFcb) == FAT_NTC_FCB) || (NextRemainingPart.Length == 0)) {
break; } }
// // Remaining name cannot start with a backslash //
if (RemainingPart.Length && (RemainingPart.Buffer[0] == L'\\')) {
RemainingPart.Length -= sizeof(WCHAR); RemainingPart.Buffer += 1; }
// // Now verify that everybody up to the longest found prefix is valid. //
try {
FatVerifyFcb( IrpContext, Fcb );
} except( (GetExceptionCode() == STATUS_FILE_INVALID) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
FatResetExceptionState( IrpContext ); }
if ( Fcb->FcbCondition == FcbGood ) {
// // If we are trying to open a paging file and have happened // upon the DelayedCloseFcb, make it go away, and try again. //
if (IsPagingFile && FirstLoop && (NodeType(Fcb) == FAT_NTC_FCB) && (!IsListEmpty( &FatData.AsyncCloseList ) || !IsListEmpty( &FatData.DelayedCloseList ))) {
FatFspClose(Vcb);
FirstLoop = FALSE;
continue;
} else {
break; }
} else {
FatRemoveNames( IrpContext, Fcb ); } }

// 循环中逐级遍历文件路径,寻找最长的前缀匹配的文件控制块(FCB)。
// 首先,将Fcb设置为ParentDcb,RemainingPart设置为FileName。然后进入一个while循环,直到满足特定条件才会退出循环。
// 在循环中,首先使用FsRtlDissectName函数将RemainingPart分解为最后一个路径分量FinalName和剩余部分NextRemainingPart。
// 接下来,进行一些检查。如果NextRemainingPart不为空且以反斜杠(\)开头,或者FinalName的长度超过255个字符,就返回STATUS_OBJECT_NAME_INVALID错误。
// 然后,尝试将FinalName转换为OEM字符串,并在OEM节点的空间中搜索相应的FCB。如果成功找到,则将NextFcb设置为找到的FCB,并更新Fcb和RemainingPart。
// 如果OEM搜索未找到匹配的FCB,或者Fcb->Specific.Dcb.RootUnicodeNode不为空(即Unicode节点的空间不为空),则进行Unicode搜索。// 首先进行大小写转换,并在Unicode节点的空间中搜索相应的FCB。如果找到,则将NextFcb设置为找到的FCB,并更新Fcb和RemainingPart。
// 最后,检查RemainingPart是否以反斜杠开头,如果是,则将长度减去一个字符大小并将缓冲区指针向前移动一个字符。
// 接下来,通过FatVerifyFcb函数验证路径中每个级别的FCB的有效性。
// 如果Fcb的状态为FcbGood,表示找到了有效的FCB,可以退出循环。
// 如果尝试打开的是分页文件且是第一次循环,并且存在延迟关闭的FCB,那么将执行延迟关闭操作,并重新开始循环。
// 如果Fcb的状态不为FcbGood,则调用FatRemoveNames函数,将路径中已经访问的名称从FCB中移除。
// 整个过程将会逐级遍历路径,直到找到最长的前缀匹配的FCB或无法继续遍历为止。
ASSERT( Fcb->FcbCondition == FcbGood );
// // If there is already an Fcb for a paging file open and // it was not already opened as a paging file, we cannot // continue as it is too difficult to move a live Fcb to // non-paged pool. //
if (IsPagingFile) {
if (NodeType(Fcb) == FAT_NTC_FCB && !FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
try_return( Iosb.Status = STATUS_SHARING_VIOLATION ); }
// // Check for a system file. //
} else if (FlagOn( Fcb->FcbState, FCB_STATE_SYSTEM_FILE )) {
try_return( Iosb.Status = STATUS_ACCESS_DENIED ); }
// // If the longest prefix is pending delete (either the file or // some higher level directory), we cannot continue. //
if (FlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) {
try_return( Iosb.Status = STATUS_DELETE_PENDING ); }
// // Now that we've found the longest matching prefix we'll // check if there isn't any remaining part because that means // we've located an existing fcb/dcb to open and we can do the open // without going to the disk //
if (RemainingPart.Length == 0) {
// // First check if the user wanted to open the target directory // and if so then call the subroutine to finish the open. //
if (OpenTargetDirectory) {
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenTargetDirectory( IrpContext, FileObject, Fcb->ParentDcb, DesiredAccess, ShareAccess, TRUE ); Irp->IoStatus.Information = Iosb.Information; try_return( Iosb.Status ); }
// // We can open an existing fcb/dcb, now we only need to case // on which type to open. //
if (NodeType(Fcb) == FAT_NTC_DCB || NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
// // This is a directory we're opening up so check if // we were not to open a directory //
if (NonDirectoryFile) {
DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0);
try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); }
DebugTrace(0, Dbg, "Open existing dcb, Dcb = %08lx\n", Fcb);
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenExistingDcb( IrpContext, FileObject, Vcb, (PDCB)Fcb, DesiredAccess, ShareAccess, CreateDisposition, NoEaKnowledge, DeleteOnClose );
Irp->IoStatus.Information = Iosb.Information; try_return( Iosb.Status ); }
// // Check if we're trying to open an existing Fcb and that // the user didn't want to open a directory. Note that this // call might actually come back with status_pending because // the user wanted to supersede or overwrite the file and we // cannot block. If it is pending then we do not complete the // request, and we fall through the bottom to the code that // dispatches the request to the fsp. //
if (NodeType(Fcb) == FAT_NTC_FCB) {
// // Check if we were only to open a directory //
if (OpenDirectory) {
DebugTrace(0, Dbg, "Cannot open file as directory\n", 0);
try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); }
DebugTrace(0, Dbg, "Open existing fcb, Fcb = %08lx\n", Fcb);
if ( TrailingBackslash ) { try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); }
CollectCreateHitStatistics(Vcb);
Iosb = FatOpenExistingFcb( IrpContext, FileObject, Vcb, Fcb, DesiredAccess, ShareAccess, AllocationSize, EaBuffer, EaLength, FileAttributes, CreateDisposition, NoEaKnowledge, DeleteOnClose, FileNameOpenedDos, &OplockPostIrp );
if (Iosb.Status != STATUS_PENDING) {
// // Check if we need to set the cache support flag in // the file object //
if (NT_SUCCESS( Iosb.Status) && !NoIntermediateBuffering) {
FileObject->Flags |= FO_CACHE_SUPPORTED; }
Irp->IoStatus.Information = Iosb.Information;
} else {
DebugTrace(0, Dbg, "Enqueue Irp to FSP\n", 0);
PostIrp = TRUE; }
try_return( Iosb.Status ); }
// // Not and Fcb or a Dcb so we bug check //
FatBugCheck( NodeType(Fcb), (ULONG_PTR) Fcb, 0 ); }

// 在找到最长匹配的前缀后,检查文件控制块(FCB)的状态和执行相应的操作。
// 首先,使用断言语句ASSERT( Fcb->FcbCondition == FcbGood )检查FCB的状态是否为FcbGood。这是一个额外的检查,用于确保FCB的状态是良好的。
// 接下来,根据打开的是分页文件还是普通文件,执行不同的检查和操作。
// 如果打开的是分页文件(IsPagingFile为真),并且找到的FCB类型是FAT_NTC_FCB且没有设置FCB_STATE_PAGING_FILE标志,则返回STATUS_SHARING_VIOLATION错误,表示无法继续打开。
// 如果打开的不是分页文件,并且找到的FCB有FCB_STATE_SYSTEM_FILE标志,则返回STATUS_ACCESS_DENIED错误,表示无法访问系统文件。
// 接下来,检查找到的最长匹配前缀的FCB是否具有FCB_STATE_DELETE_ON_CLOSE标志。如果有,则返回STATUS_DELETE_PENDING错误,表示该文件或其某个上级目录已被标记为删除。
// 如果没有剩余路径(RemainingPart.Length == 0),则说明找到了一个现有的FCB或DCB,可以直接打开而无需访问磁盘。
// 如果OpenTargetDirectory为真,则调用FatOpenTargetDirectory子例程来完成打开操作。
// 如果找到的是DCB或RootDCB类型的FCB,则检查是否允许打开目录。如果NonDirectoryFile为真,则返回STATUS_FILE_IS_A_DIRECTORY错误,表示无法将目录作为文件打开。
// 如果找到的是FCB类型的FCB,则检查是否允许打开文件。如果OpenDirectory为真,则返回STATUS_NOT_A_DIRECTORY错误,表示无法将文件作为目录打开。
// 如果TrailingBackslash为真,则返回STATUS_OBJECT_NAME_INVALID错误,表示无效的对象名称。
// 最后,调用FatOpenExistingFcb函数来打开现有的FCB,并根据返回的状态执行相应的操作。如果操作不是挂起状态(Iosb.Status != STATUS_PENDING),// 则将结果信息和状态保存在Irp结构中。如果需要启用中间缓存,则将文件对象的标志设置为FO_CACHE_SUPPORTED。
// 如果返回的状态是挂起状态,表示需要将IRP排队到文件系统进程(FSP)中处理,设置PostIrp为真。
// 如果找到的FCB既不是DCB也不是FCB,则调用FatBugCheck函数引发系统错误,用于调试和错误分析。
// // There is more in the name to parse than we have in existing // fcbs/dcbs. So now make sure that fcb we got for the largest // matching prefix is really a dcb otherwise we can't go any // further //
if ((NodeType(Fcb) != FAT_NTC_DCB) && (NodeType(Fcb) != FAT_NTC_ROOT_DCB)) {
DebugTrace(0, Dbg, "Cannot open file as subdirectory, Fcb = %08lx\n", Fcb);
try_return( Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND ); }
// // Otherwise we continue on processing the Irp and allowing ourselves // to block for I/O as necessary. Find/create additional dcb's for // the one we're trying to open. We loop until either remaining part // is empty or we get a bad filename. When we exit FinalName is // the last name in the string we're after, and ParentDcb is the // parent directory that will contain the opened/created // file/directory. // // Make sure the rest of the name is valid in at least the LFN // character set (which just happens to be that of HPFS). // // If we are not in ChicagoMode, use FAT symantics. //
ParentDcb = Fcb; FirstLoop = TRUE;
while (TRUE) {
// // We do one little optimization here on the first itterration of // the loop since we know that we have already tried to convert // FinalOemName from the original UNICODE. //
if (FirstLoop) {
FirstLoop = FALSE; RemainingPart = NextRemainingPart; Status = OemFinalName.Length ? STATUS_SUCCESS : STATUS_UNMAPPABLE_CHARACTER;
} else {
// // Dissect the remaining part. //
DebugTrace(0, Dbg, "Dissecting the name %Z\n", &RemainingPart);
FsRtlDissectName( RemainingPart, &FinalName, &RemainingPart );
// // If RemainingPart starts with a backslash the name is // invalid. // Check for no more than 255 characters in FinalName //
if (((RemainingPart.Length != 0) && (RemainingPart.Buffer[0] == L'\\')) || (FinalName.Length > 255*sizeof(WCHAR))) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); }
// // Now, try to convert this one component into Oem. If it works // then that's great, otherwise we have to try with the UNICODE // name instead. //
FatEnsureStringBufferEnough( &OemFinalName, FinalName.Length);
Status = RtlUpcaseUnicodeStringToCountedOemString( &OemFinalName, &FinalName, FALSE ); }
if (NT_SUCCESS(Status)) {
// // We'll start by trying to locate the dirent for the name. Note // that we already know that there isn't an Fcb/Dcb for the file // otherwise we would have found it when we did our prefix lookup. //
if (FatIsNameShortOemValid( IrpContext, OemFinalName, FALSE, FALSE, FALSE )) {
FatStringTo8dot3( IrpContext, OemFinalName, &LocalCcb.OemQueryTemplate.Constant );
LocalCcb.Flags = 0;
} else {
LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE; }
} else {
LocalCcb.Flags = CCB_FLAG_SKIP_SHORT_NAME_COMPARE;
if (Status != STATUS_UNMAPPABLE_CHARACTER) {
try_return( Iosb.Status = Status ); } }
// // Now we know a lot about the final name, so do legal name // checking here. //
if (FatData.ChicagoMode) {
if (!FatIsNameLongUnicodeValid( IrpContext, &FinalName, FALSE, FALSE, FALSE )) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); }
} else {
if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); } }
DebugTrace(0, Dbg, "FinalName is %Z\n", &FinalName); DebugTrace(0, Dbg, "RemainingPart is %Z\n", &RemainingPart);
FatEnsureStringBufferEnough( &UpcasedFinalName, FinalName.Length);
if (!NT_SUCCESS(Status = RtlUpcaseUnicodeString( &UpcasedFinalName, &FinalName, FALSE))) {
try_return( Iosb.Status = Status ); }
LocalCcb.UnicodeQueryTemplate = UpcasedFinalName; LocalCcb.ContainsWildCards = FALSE;
Lfn.Length = 0;
FatLocateDirent( IrpContext, ParentDcb, &LocalCcb, 0, &Dirent, &DirentBcb, &DirentByteOffset, &FileNameOpenedDos, &Lfn); // // Remember we read this Dcb for error recovery. //
FinalDcb = ParentDcb;
// // If the remaining part is now empty then this is the last name // in the string and the one we want to open //
if (RemainingPart.Length == 0) { break; }
// // We didn't find a dirent, bail. //
if (Dirent == NULL) {
Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; try_return( Iosb.Status ); }
// // We now have a dirent, make sure it is a directory //
if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; try_return( Iosb.Status ); }
// // Compute the LfnByteOffset. //
LfnByteOffset = DirentByteOffset - FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT);
// // Create a dcb for the new directory //
ParentDcb = FatCreateDcb( IrpContext, Vcb, ParentDcb, LfnByteOffset, DirentByteOffset, Dirent, &Lfn );
// // Remember we created this Dcb for error recovery. //
FinalDcb = ParentDcb;
FatSetFullNameInFcb( IrpContext, ParentDcb, &FinalName ); }// 首先,代码检查最大匹配前缀对应的Fcb是否是一个目录控制块(DCB)。如果不是DCB,也不是根目录的DCB,那么无法继续打开文件或创建子目录,会返回错误状态码STATUS_OBJECT_PATH_NOT_FOUND。
// 如果Fcb是DCB或根目录的DCB,那么代码会继续处理Irp,并允许根据需要进行I/O阻塞。接下来的任务是找到或创建要打开/创建的目录的附加DCB。// 代码会循环执行,直到剩余部分为空或获取到无效的文件名。在退出循环时,FinalName是要打开/创建的字符串中的最后一个名称,而ParentDcb是包含所打开/创建文件/目录的父目录。
// 代码会检查剩余部分的名称在至少LFN(长文件名)字符集中是否有效(LFN字符集与HPFS字符集相同)。
// 根据ChicagoMode的设置,在合法名称检查方面会有所不同。如果在ChicagoMode中,会检查FinalName是否为有效的长文件名(Unicode格式)。如果不在ChicagoMode中,则要求FinalName为有效的短文件名(OEM格式)。
// 接下来,代码会对FinalName进行大写转换,并将结果存储在UpcasedFinalName中。
// 然后,代码通过调用FatLocateDirent函数来定位与FinalName匹配的目录项。这里已经确定在进行前缀查找时,没有找到对应的Fcb/Dcb。
// 如果剩余部分的长度为0,说明这是字符串中的最后一个名称,也是要打开的目标。
// 如果没有找到目录项(Dirent为空),则返回STATUS_OBJECT_PATH_NOT_FOUND错误。
// 确保找到的目录项是一个目录(通过检查Attributes属性)。
// 计算LfnByteOffset,该值用于创建新目录的DCB。
// 创建新目录的DCB(ParentDcb),并将其设置为下一次循环的父目录。
// 在最后,将FinalName设置为ParentDcb的完整路径名。


// // First check if the user wanted to open the target directory // and if so then call the subroutine to finish the open. //
if (OpenTargetDirectory) {
Iosb = FatOpenTargetDirectory( IrpContext, FileObject, ParentDcb, DesiredAccess, ShareAccess, Dirent ? TRUE : FALSE);
Irp->IoStatus.Information = Iosb.Information; try_return( Iosb.Status ); }
if (Dirent != NULL) {
// // Compute the LfnByteOffset. //
LfnByteOffset = DirentByteOffset - FAT_LFN_DIRENTS_NEEDED(&Lfn) * sizeof(LFN_DIRENT);
// // We were able to locate an existing dirent entry, so now // see if it is a directory that we're trying to open. //
if (FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
// // Make sure its okay to open a directory //
if (NonDirectoryFile) {
DebugTrace(0, Dbg, "Cannot open directory as a file\n", 0);
try_return( Iosb.Status = STATUS_FILE_IS_A_DIRECTORY ); }
DebugTrace(0, Dbg, "Open existing directory\n", 0);
Iosb = FatOpenExistingDirectory( IrpContext, FileObject, Vcb, ParentDcb, Dirent, LfnByteOffset, DirentByteOffset, &Lfn, DesiredAccess, ShareAccess, CreateDisposition, NoEaKnowledge, DeleteOnClose ); Irp->IoStatus.Information = Iosb.Information; try_return( Iosb.Status ); }
// // Otherwise we're trying to open and existing file, and we // need to check if the user only wanted to open a directory. //
if (OpenDirectory) {
DebugTrace(0, Dbg, "Cannot open file as directory\n", 0);
try_return( Iosb.Status = STATUS_NOT_A_DIRECTORY ); }
DebugTrace(0, Dbg, "Open existing file\n", 0);
if ( TrailingBackslash ) { try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); }
Iosb = FatOpenExistingFile( IrpContext, FileObject, Vcb, ParentDcb, Dirent, LfnByteOffset, DirentByteOffset, &Lfn, DesiredAccess, ShareAccess, AllocationSize, EaBuffer, EaLength, FileAttributes, CreateDisposition, IsPagingFile, NoEaKnowledge, DeleteOnClose, FileNameOpenedDos );
// // Check if we need to set the cache support flag in // the file object //
if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) {
FileObject->Flags |= FO_CACHE_SUPPORTED; }
Irp->IoStatus.Information = Iosb.Information; try_return( Iosb.Status ); }// 处理在已经定位到目标目录项的情况下,打开目标目录或目标文件的逻辑。
// 如果OpenTargetDirectory标志被设置,表示用户希望打开目标目录,那么调用FatOpenTargetDirectory子例程完成打开操作。
// 如果Dirent不为空,表示已经定位到一个现有的目录项。接下来,检查该目录项是否是一个目录。
// a. 如果是目录且NonDirectoryFile标志没有设置,表示要打开一个现有的目录。调用FatOpenExistingDirectory子例程完成打开操作。
// b. 如果是目录但NonDirectoryFile标志被设置,表示试图将目录作为文件打开,此时返回错误状态码STATUS_FILE_IS_A_DIRECTORY。
// c. 如果不是目录,表示要打开一个现有的文件。此时需要检查是否只希望打开目录(OpenDirectory标志被设置)。
// 如果OpenDirectory标志被设置,表示不允许将文件作为目录打开,返回错误状态码STATUS_NOT_A_DIRECTORY。
// 如果OpenDirectory标志没有设置,调用FatOpenExistingFile子例程完成打开操作。
// 最后,根据打开操作的结果更新Iosb结构和Irp的相关字段。

// // We can't locate a dirent so this is a new file. //
// // Now check to see if we wanted to only open an existing file. // And then case on whether we wanted to create a file or a directory. //
if ((CreateDisposition == FILE_OPEN) || (CreateDisposition == FILE_OVERWRITE)) {
DebugTrace( 0, Dbg, "Cannot open nonexisting file\n", 0);
try_return( Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND ); }
// // Skip a few cycles later if we know now that the Oem name is not // valid 8.3. //
if (FlagOn(LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE)) {
OemFinalName.Length = 0; }
// // Determine the granted access for this operation now. //
if (!NT_SUCCESS( Iosb.Status = FatCheckSystemSecurityAccess( IrpContext ))) {
try_return( Iosb ); }
if (CreateDirectory) {
DebugTrace(0, Dbg, "Create new directory\n", 0);
// // If this media is write protected, don't even try the create. //
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
// // Set the real device for the pop-up info, and set the verify // bit in the device object, so that we will force a verify // in case the user put the correct media back in. //

IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, Vcb->Vpb->RealDevice );
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); }
// // Don't allow people to create directories with the // temporary bit set. //
if (TemporaryFile) {
try_return( Iosb.Status = STATUS_INVALID_PARAMETER ); }
Iosb = FatCreateNewDirectory( IrpContext, FileObject, Vcb, ParentDcb, &OemFinalName, &FinalName, DesiredAccess, ShareAccess, EaBuffer, EaLength, FileAttributes, NoEaKnowledge, DeleteOnClose );
Irp->IoStatus.Information = Iosb.Information; try_return( Iosb.Status ); }
DebugTrace(0, Dbg, "Create new file\n", 0);
if ( TrailingBackslash ) {
try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); }
// // If this media is write protected, don't even try the create. //
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
// // Set the real device for the pop-up info, and set the verify // bit in the device object, so that we will force a verify // in case the user put the correct media back in. //

IoSetHardErrorOrVerifyDevice( IrpContext->OriginatingIrp, Vcb->Vpb->RealDevice );
SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME);
FatRaiseStatus( IrpContext, STATUS_MEDIA_WRITE_PROTECTED ); }
Iosb = FatCreateNewFile( IrpContext, FileObject, Vcb, ParentDcb, &OemFinalName, &FinalName, DesiredAccess, ShareAccess, AllocationSize, EaBuffer, EaLength, FileAttributes, &Lfn, IsPagingFile, NoEaKnowledge, DeleteOnClose, TemporaryFile );
// // Check if we need to set the cache support flag in // the file object //
if (NT_SUCCESS(Iosb.Status) && !NoIntermediateBuffering) {
FileObject->Flags |= FO_CACHE_SUPPORTED; }
Irp->IoStatus.Information = Iosb.Information;
try_exit: NOTHING;
// // This is a Beta Fix. Do this at a better place later. //
if (NT_SUCCESS(Iosb.Status) && !OpenTargetDirectory) {
PFCB LocalFcb;
// // If there is an Fcb/Dcb, set the long file name. //
LocalFcb = FileObject->FsContext;
if (LocalFcb && ((NodeType(LocalFcb) == FAT_NTC_FCB) || (NodeType(LocalFcb) == FAT_NTC_DCB)) && (LocalFcb->FullFileName.Buffer == NULL)) {
FatSetFullNameInFcb( IrpContext, LocalFcb, &FinalName ); } }
} finally {
DebugUnwind( FatCommonCreate );
// // There used to be a test here - the ASSERT replaces it. We will // never have begun enumerating directories if we post the IRP for // oplock reasons. //
ASSERT( !OplockPostIrp || DirentBcb == NULL );
FatUnpinBcb( IrpContext, DirentBcb );
// // If we are in an error path, check for any created subdir Dcbs that // have to be unwound. Don't whack the root directory. // // Note this will leave a branch of Dcbs dangling if the directory file // had not been built on the leaf (case: opening path which has an // element containing an invalid character name). //
if ((AbnormalTermination() || !NT_SUCCESS(Iosb.Status)) && (FinalDcb != NULL) && (NodeType(FinalDcb) == FAT_NTC_DCB) && IsListEmpty(&FinalDcb->Specific.Dcb.ParentDcbQueue) && (FinalDcb->OpenCount == 0) && (FinalDcb->Specific.Dcb.DirectoryFile != NULL)) {
PFILE_OBJECT DirectoryFileObject; ULONG SavedFlags;
// // Before doing the uninitialize, we have to unpin anything // that has been repinned, but disable writethrough first. We // disable raise from unpin-repin since we're already failing. //
SavedFlags = IrpContext->Flags;
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_RAISE | IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH );
FatUnpinRepinnedBcbs( IrpContext );
DirectoryFileObject = FinalDcb->Specific.Dcb.DirectoryFile;
FinalDcb->Specific.Dcb.DirectoryFile = NULL;
CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
ObDereferenceObject( DirectoryFileObject );
IrpContext->Flags = SavedFlags; }
if (AbnormalTermination()) {
FatReleaseVcb( IrpContext, Vcb ); }
// // Free up any string buffers we allocated //
FatFreeStringBuffer( &OemFinalName);
FatFreeStringBuffer( &UpcasedFinalName);
FatFreeStringBuffer( &Lfn); }// 处理无法定位到目标目录项的情况,即需要创建一个新文件或目录的逻辑。
// 首先检查是否只想打开一个现有文件,然后根据CreateDisposition的值分别处理文件和目录的创建:
// a. 如果CreateDisposition是FILE_OPEN或FILE_OVERWRITE,表示不允许打开不存在的文件,返回错误状态码STATUS_OBJECT_NAME_NOT_FOUND。
// 如果LocalCcb.Flags的CCB_FLAG_SKIP_SHORT_NAME_COMPARE标志被设置,表示不需要进行8.3格式的短文件名比较,将OemFinalName的长度设置为0。
// 调用FatCheckSystemSecurityAccess函数确定此操作的授权访问权限。
// 如果CreateDirectory标志被设置,表示要创建一个新目录。
// a. 如果存储介质是只读的(VCB_STATE_FLAG_WRITE_PROTECTED标志被设置),则不执行创建操作,引发状态码STATUS_MEDIA_WRITE_PROTECTED。
// b. 如果TemporaryFile标志被设置,表示不允许创建带有临时标志的目录,返回错误状态码STATUS_INVALID_PARAMETER。
// c. 调用FatCreateNewDirectory子例程创建新目录。
// 否则,表示要创建一个新文件。
// a. 如果TrailingBackslash标志被设置,表示文件名无效,返回错误状态码STATUS_OBJECT_NAME_INVALID。
// b. 如果存储介质是只读的,不执行创建操作,引发状态码STATUS_MEDIA_WRITE_PROTECTED。
// c. 调用FatCreateNewFile子例程创建新文件。
// 如果操作成功且NoIntermediateBuffering标志未设置,则设置文件对象的FO_CACHE_SUPPORTED标志,以指示支持缓存。
// 更新Irp的相关字段和Iosb结构。
// 在try_exit块中的Beta Fix代码段中进行额外的处理。如果存在Fcb/Dcb(文件/目录控制块),并且FullName.Buffer为空,则将FullName设置到Fcb中。
// finally块中进行清理和释放操作。
// a. 解除DirentBcb的锁定。
// b. 如果存在FinalDcb且不为空且不是根目录(FinalDcb是目标目录的父目录),并且没有打开的子目录,并且Specific.Dcb.DirectoryFile不为空,则进行一些额外的清理操作,包括解除映射缓存、取消引用DirectoryFileObject,并进行BCB的解锁。
// c. 如果发生异常终止,释放Vcb资源。
// d. 释放分配的字符串缓冲区。


// // The following code is only executed if we are exiting the // procedure through a normal termination. We complete the request // and if for any reason that bombs out then we need to unreference // and possibly delete the fcb and ccb. //
try {
if (PostIrp) {
// // If the Irp hasn't already been posted, do it now. //
if (!OplockPostIrp) {
Iosb.Status = FatFsdPostRequest( IrpContext, Irp ); }
} else {
FatUnpinRepinnedBcbs( IrpContext ); }
} finally {
DebugUnwind( FatCommonCreate-in-FatCompleteRequest );
if (AbnormalTermination()) {
PVCB LocalVcb; PFCB LocalFcb; PCCB LocalCcb2;
// // Unwind all of our counts. Note that if a write failed, then // the volume has been marked for verify, and all volume // structures will be cleaned up automatically. //
(VOID) FatDecodeFileObject( FileObject, &LocalVcb, &LocalFcb, &LocalCcb2 );
LocalFcb->UncleanCount -= 1; LocalFcb->OpenCount -= 1; LocalVcb->OpenFileCount -= 1;
if (IsFileObjectReadOnly(FileObject)) { LocalVcb->ReadOnlyCount -= 1; }
// // If we leafed out on a new Fcb we should get rid of it at this point. // // Since the object isn't being opened, we have to do all of the teardown // here. Our close path will not occur for this fileobject. Note this // will leave a branch of Dcbs dangling since we do it by hand and don't // chase to the root. //
if (LocalFcb->OpenCount == 0 && (NodeType( LocalFcb ) == FAT_NTC_FCB || IsListEmpty(&LocalFcb->Specific.Dcb.ParentDcbQueue))) {
ASSERT( NodeType( LocalFcb ) != FAT_NTC_ROOT_DCB );
if ( (NodeType( LocalFcb ) == FAT_NTC_DCB) && (LocalFcb->Specific.Dcb.DirectoryFile != NULL) ) {
FatSyncUninitializeCacheMap( IrpContext, LocalFcb->Specific.Dcb.DirectoryFile );
InterlockedDecrement( &LocalFcb->Specific.Dcb.DirectoryFileOpenCount ); FatSetFileObject( LocalFcb->Specific.Dcb.DirectoryFile, UnopenedFileObject, NULL, NULL );
ObDereferenceObject( LocalFcb->Specific.Dcb.DirectoryFile ); LocalFcb->Specific.Dcb.DirectoryFile = NULL; ExFreePool( FatAllocateCloseContext(Vcb)); }
FatDeleteFcb( IrpContext, LocalFcb ); }
FatDeleteCcb( IrpContext, LocalCcb2 );
FatReleaseVcb( IrpContext, LocalVcb );
} else {
FatReleaseVcb( IrpContext, Vcb );
if ( !PostIrp ) {
// // If this request is successful and the file was opened // for FILE_EXECUTE access, then set the FileObject bit. //
ASSERT( IrpSp->Parameters.Create.SecurityContext != NULL ); if (FlagOn( *DesiredAccess, FILE_EXECUTE )) {
SetFlag( FileObject->Flags, FO_FILE_FAST_IO_READ ); }
// // Lock volume in drive if we opened a paging file, allocating a // reserve MDL to guarantee paging file operations can always // go forward. //
if (IsPagingFile && NT_SUCCESS(Iosb.Status)) {
if (!FatReserveMdl) {
PMDL ReserveMdl = IoAllocateMdl( NULL, FAT_RESERVE_MDL_SIZE * PAGE_SIZE, TRUE, FALSE, NULL );
// // Stash the MDL, and if it turned out there was already one there // just free what we got. //
InterlockedCompareExchangePointer( &FatReserveMdl, ReserveMdl, NULL );
if (FatReserveMdl != ReserveMdl) {
IoFreeMdl( ReserveMdl ); } }
SetFlag(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE);
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
FatToggleMediaEjectDisable( IrpContext, Vcb, TRUE ); } }
// // Complete the request. //
FatCompleteRequest( IrpContext, Irp, Iosb.Status ); } }
DebugTrace(-1, Dbg, "FatCommonCreate -> %08lx\n", Iosb.Status); }
CollectCreateStatistics(Vcb, Iosb.Status);
return Iosb.Status;


// 用于处理在FAT文件系统中创建文件或目录的操作。
// 代码的作用是在正常终止的情况下完成请求,并进行必要的清理操作。
// try块开始执行,包含以下两种可能的路径:
// 如果PostIrp为真,则检查IRP是否已经被发送。如果没有发送,则立即发送IRP以进行异步处理。// 如果PostIrp为假,则调用FatUnpinRepinnedBcbs函数对缓存进行解除固定操作。// finally块开始执行,其中包含以下两种情况的处理逻辑:
// 如果程序异常终止,则执行清理操作:// 解码文件对象(FileObject),获取相关的卷控制块(LocalVcb)、文件控制块(LocalFcb)和文件控制块(LocalCcb2)。// 减少文件控制块(LocalFcb)的未清除计数(UncleanCount)、打开计数(OpenCount)和卷控制块(LocalVcb)的打开文件计数(OpenFileCount)。// 如果文件对象(FileObject)是只读的,减少卷控制块(LocalVcb)的只读计数(ReadOnlyCount)。// 如果文件控制块(LocalFcb)的打开计数(OpenCount)为零,并且是新的文件控制块(Fcb),或者其父级目录队列(ParentDcbQueue)为空,则删除该文件控制块(Fcb)。// 注意:此处将会删除文件控制块(Fcb)及其相关的目录控制块(Dcb),但不会沿着树状结构删除其它相关的目录控制块(Dcb)。// 如果程序正常终止,则执行以下操作:// 释放卷控制块(Vcb)。// 如果PostIrp为假,则执行以下操作:// 如果请求成功,并且文件以FILE_EXECUTE访问模式打开,则设置文件对象(FileObject)的相应标志位。// 如果打开的是分页文件,并且I/O操作状态为成功,则锁定驱动器上的卷,分配一个保留的内存描述符列表(MDL)来确保分页文件的操作能够进行。// 完成请求,调用FatCompleteRequest函数。// 最后,代码收集创建统计信息,然后返回Iosb.Status的值。}

文章来源: http://mp.weixin.qq.com/s?__biz=MzkwOTE5MDY5NA==&mid=2247488082&idx=1&sn=62cee5beb89edd2a928ae86613430e67&chksm=c13f211bf648a80d81c1074844b217af311410c1e43cde45877056e8f35fa73031acdd3497fc#rd
如有侵权请联系:admin#unsafe.sh