注只用于自己上班坐地铁看,不要转发。
NTSTATUS
FatFsdCreate (
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函数进行公共的创建操作。
// 如果发生异常,通过异常过滤器处理异常,并将异常代码转换为适当的错误状态。
// 在处理完毕后,离开文件系统。
// 返回处理结果的状态。
NTSTATUS
FatCommonCreate (
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的值。
}