WinCE 编程实验(第11 章 档案系统)

来源:互联网 发布:ios客户端程序员招聘 编辑:程序博客网 时间:2024/06/03 07:11
 

11

档案系统

 

11.1储存管理器分层结构的建立及可安装档案系统的加载

 

对于任何新装入系统的外围储存设备来说,首先要做的就是将其纳入系统的管理,然后才谈得上对该设备的使用。从底层区块装置驱动程序的加载,到分区驱动程序的加载以及档案系统驱动程序的加载,最后得以将区块装置及其使用的各种驱动程序纳入设备管理器和储存管理器的管理当中,这本身是一个由下往上的程序。而透过统一的档案系统Win32 API接口定位到对应的档案系统驱动程序,再经由分区驱动程序或直接交给设备驱动程序完成最终的档案操作,这是一个由上往下的程序。这两个截然相反的程序中,前者是将外围储存设备纳入系统管理的程序,后者则是对外围储存设备的使用程序,对前一个程序的分析即为本章的重点。

 

当系统发现一个新的设备时,首先会由设备管理器负责为其加载对应的设备驱动程序,并在设备管理器中建立对应的数据结构记录与该设备相关的各种信息。之后如果发现新装入的设备是区块装置,还要由储存管理器为其加载档案系统驱动程序。这时,设备管理器会将一些必要的信息告知储存管理器,由它来完成后面的工作。储存管理器首先会根据注册表中的组态信息为区块装置加载分区驱动程序,分区驱动程序的主要作用就是定位区块装置上的分区。下面储存管理器会为设备加载所需的档案系统驱动程序。在这一程序中,储存管理器会为设备上的卷(volume)注册第一层目录的索引,将来用户透过全路径名对文件进行存取时就是由其中的一级目录名找到相应的档案系统。

这一程序大致如下:

1) 设备管理器负责加载区块装置的驱动程序。

2) 由该设备发出一个通知,告知储存管理器该设备的设备名及其GUID等信息。

3) 储存管理器根据组态信息为该设备加载分区驱动程序。

4) 储存管理器列举该设备上的所有分区。

5) 储存管理器为每个分区加载档案系统驱动程序。

 

下面先对这一程序中所使用到的数据结构做一下介绍,然后再将上述储存管理器分层结构的建立程序详细展开,以Ramdisk的安装程序为例看一看在实际的原始码中这一程序的实作。


 

11.1.1 几个重要的数据结构

 

每当系统中出现新的外围储存设备时,储存管理器都会为其建立一系列的数据结构用以维护与该设备有关的信息。这些数据结构包括CStoreDriverStateDSK等,它们都是用来描述一个设备的。对于设备上的分区,储存管理器中也有一套用于维护分区信息的数据结构,包括CPartitionPartStateVOL等。

 

CStore是一个类别,它描述了一个外围储存设备的一些基本信息,由储存管理器负责建立,其中一些成员变量的含义如下:

• m_szDeviceName:设备名称,由设备管理器告知。

• m_DeviceGuid:设备的GUID,也是由设备管理器告知的。

• m_szRootRegKey:此设备的组态信息在注册表中的位置。

• m_pPartDriver:设备所使用的分区驱动程序。

• m_pPartitionList:设备上的CPartition分区串行。

• m_dwStoreId:由分区管理器维护的与此设备对应的DriverState结构的指针。

• m_pNextStore:指向g_pStoreRoot队列中下一个CStore

DriverState由分区管理器负责建立并维护,记录了一些与分区管理器有关的设备细节信息,其中一些成员变量的含义如下:

• pPartState:设备上的PartState分区串行。

• pSearchState:指向SearchState结构组成的串行。

• diskInfo:设备参数,包括CHS值、每个扇区所含的字节数等等。

• snExtPartSector:设备上扩展分区的起始扇区。

• snExtPartEndSec:扩展分区的结束扇区。

C P a r t i t i o n也是一个类别,用于描述设备上的分区,由储存管理器负责建立并维护,其中一些成员变量的含义如下:

• m_dwPartitionId:由分区管理器维护的与此分区对应的PartState的句柄。

• m_dwStoreId:与此分区所在的设备对应的DriverState结构的指针。

• m_szPartitionName:分区的名称。

• m_szFileSys:此分区所使用的档案系统的名称。

• m_pDsk:指向由档案系统驱动程序负责维护的与此设备相关的DSK结构。

• m_hFSD:档案系统驱动程序对应的动态链接库的句柄。

• m_pNextPartition:指向同一设备上的下一个CPartition对象。

P a r t S t a t e是分区管理器为维护分区信息所使用的数据结构,与DriverState密切相关,其中一些成员变量的含义如下:

• cPartName:分区的名称。

• snStartSector:该分区的起始扇区号。

• snNumSectors:此分区所占用的扇区数,即分区大小。


 

• NextPartState:指向同一设备上的下一个PartState结构。

• pState:指向此分区所属设备的DriverState结构。

CPartDriver是一个类别,用于描述一个分区驱动程序,其成员变量m_hPartDriver记录了该分区驱动程序对应的动态链接库的句柄,其它的成员变量均为函数指针,与分区驱动程序的输出函数

一一对应。

 

以上这些数据结构的关系如图11 - 1所示:

11-1 几个重要数据结构的关系

 

下面再来看一下由档案系统驱动程序管理器负责建立并维护的FSDDSKVOL这三个数据结构。

 

FSD结构用来描述一个档案系统驱动程序或是一个过滤器,其中一些成员变量的含义如下:

• hFSD:档案系统驱动程序或过滤器对应的动态链接库句柄。

• pfnMountDisk:档案系统驱动程序的输出函数FSD_MountDisk的入口地址。

• pfnUnmountDisk:档案系统驱动程序的输出函数FSD_UnmountDisk的入口地址。

• pfnHookVolume:过滤器的输出函数FSD_HookVolume的入口地址。

• pfnUnhookVolume:过滤器的输出函数FSD_UnhookVolume的入口地址。

• szFileSysName:档案系统或过滤器的名称。

• szRegKey:档案系统或过滤器的相关信息在注册表中的位置。

• wsFSD:档案系统驱动程序输出函数的前置码(prefix)

• apfnAFSapfnFileapfnFind:记录了所有输出函数的入口地址。

 


 

 

每个档案系统驱动程序和过滤器除了对应一个FSD结构以外,还会对应一个DSK结构,其中一些成员变量的含义如下:

• pVo l:指向VOL结构组成的双向串行的开头结点。

• pFSD:指向对应的FSD结构。

• fdi :设备参数信息,包括CHS值、每个扇区所含的字节数等。

• pDeviceIoControlDeviceIoControl函数的入口地址。

 

VOL结构用于描述一个卷(volume),也就是一个分区,其中一些成员变量的含义如下:

• pDsk:指向此卷(volume)所使用的最上层档案系统或过滤器所对应的DSK结构。

• dwVolData:由FSDMGR_RegisterVolume提供的某一档案系统驱动程序中使用的VOLUME结构的指针。

• iAFS:由FSDMGR_RegisterVolume获得的AFS号。

 

11.1.2 情景分析

 

首先看一下Windows CE 与档案系统相关的原始码组织。在下面的说明中,%_WINCEROOT%代表安装WINCE400的根目录。

• %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/FSD:三种档案系统驱动程序的实作,包括FATRELFSD以及UDFS

• %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE:储存管理器的实作。

• %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/DOSPARTMSPart.dll的实作。

• %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/FSDMAIN:档案系统驱动程序管理器的实作。

• %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/FSDSERV:档案系统驱动程序管理器使用的一些服务例程及其提供的API

• %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREAPI:储存管理器提供的API的封装。

• %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREMGR:储存管理器的启动原始码, CStoreCPartition等类别的实作。

 

另外,与Ramdisk相关的原始码位于以下两个目录:

• %_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/BLOCK/RAMDISK/DRIVERBuild此目录下的源文件可以得到Ramdisk的驱动程序ramdisk.dll

• %_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/BLOCK/RAMDISK/LOADERBuild此目录下的源文件可以得到Ramdisk的安装程序ceramdrv.exe,该程序用于启动Ramdisk

 

下面就以Ramdisk的安装程序为例说明当系统中出现新的外围储存设备时,其设备驱动程序、分区驱动程序以及档案系统驱动程序是如何被装入系统的,以及系统是如何透过设备管理器和储存管理器来管理新发现的设备。

 

在系统的启动程序中会自动加载两个模块,一个是filesys.exe,与对象储存相关的基于RAM和基于ROM的档案系统的功能就是由该模块实作的;另一个是fsdmgr.dll,当新的外围储存设备装入系统时由它负责加载FAT这种可安装的档案系统。系统在装入fsdmgr.dll这一模块时会呼叫初始化函数Init,而建立储存管理器分层结构的入口点位于Init函数中的InitStorageManager。在InitStorageManager中,首先会呼叫CreateAPISetRegisterAPISet注册储存管理器对外提供的API集合,另外负责建立g_hPNPUpdateEvent这一事件。下面透过CreateMsgQueue建立了一个讯息队列g_hPNPQueue,由于msgopts.bReadAccess被设为TRUE,因此在Storage Manager这方面来讲只能对该讯息队列进行读取操作。之后是启动用于支持即插即用的执行绪PNPThread。在此执行绪中,首先由GetProcAddress得到RequestDeviceNotificationsStopDeviceNotifications这两个函数在coredll.dll中的入口地址,然后透过呼叫RequestDeviceNotifications告知设备管理器将发现新的区块设备的通告写入讯息队列g_hPNPQueue中。这些准备工作完成后PNPThread将进入一个无限循环,每隔一定时间对g_hPNPQueueg_hPNPUpdateEvent进行一次轮询。如果设备管理器发现新设备,会透过WriteMsgQueue向讯息队列中写入含有新设备信息(GUID,设备名等)的DEVDETAIL结构。这时,在PNPThread执行绪中就可以透过ReadMsgQueue将此信息读出,并呼叫MountStore来进行下面的加载工作。

 

***

%_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREMGR/storemain.cpp Line 608 ***

BOOL InitStorageManager()

{

DWORD dwThreadId=0;

int iAFS = INVALID_AFS;

MSGQUEUEOPTIONS msgopts;

 

iAFS = RegisterAFSName(L"StoreMgr");

if (iAFS != INVALID_AFS && GetLastError() == 0) {

g_hSTRMGRApi = CreateAPISet("PFSD", ARRAY_SIZE(apfnSTGMGRAPIs), (const PFNVOID

*) apfnSTGMGRAPIs, asigSTGMGRAPIs);

if (!RegisterAFSEx(iAFS, g_hSTRMGRApi, (DWORD) 1, AFS_VERSION ,AFS_FLAG_HIDDEN)) {

DEBUGMSGW(ZONE_INIT, (DBGTEXTW("STOREMGR:InitStoreMgrfailedregistering secondary volume/r/n")));

DeregisterAFSName(iAFS);

goto Fail;

}

} else {

goto Fail;

}

// 建立API集合

g_hFindStoreApi = CreateAPISet("FSTG", ARRAY_SIZE(apfnFindStoreAPIs), (const PFNVOID * )apfnFindStoreAPIs, asigFindStoreAPIs);

g_hFindPartApi = CreateAPISet("FPRT", ARRAY_SIZE(apfnFindPartAPIs), (const PFNVOID * )apfnFindPartAPIs, asigFindPartAPIs);

g_hStoreApi = CreateAPISet("STRG", ARRAY_SIZE(apfnSTGAPIs), (const PFNVOID *)apfnSTGAPIs, asigSTGAPIs);

g_hPNPUpdateEvent = CreateEvent( NULL, FALSE, FALSE, NULL);

// 建立讯息队列,储存管理器对该队列只能进行读取操作

msgopts.dwSize = sizeof(MSGQUEUEOPTIONS);

msgopts.dwFlags = 0;

msgopts.dwMaxMessages = 0; //?

msgopts.cbMaxMessage = sizeof(g_pPNPBuf);

msgopts.bReadAccess = TRUE;

g_hPNPQueue = CreateMsgQueue(NULL, &msgopts);

if (!g_hFindPartApi || !g_hFindStoreApi || !g_hStoreApi || !g_hPNPUpdateEvent || !g_hPNPQueue)

goto Fail;

 

// 注册API

RegisterAPISet(g_hFindStoreApi, HT_FIND | REGISTER_APISET_TYPE);

RegisterAPISet(g_hFindPartApi, HT_FIND | REGISTER_APISET_TYPE);

RegisterAPISet(g_hStoreApi, HT_FILE | REGISTER_APISET_TYPE);

InitializeCriticalSection(&g_csStoreMgr);

 

// 启动PNP执行绪,随时准备加载新发现的外围储存设备

if (g_hPNPThread = CreateThread( NULL, 0, PNPThread, NULL, 0, &dwThreadId)) {

} else {

goto Fail;

}

return TRUE;

Fail:

CloseAllGlobalHandles();

return FALSE;

}

***

%_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREMGR/storemain.cpp

 

Line 493 ***

DWORD PNPThread(LPVOID lParam)

{

extern const TCHAR *g_szSTORAGE_PATH; // = L"System//StorageManager";

extern const TCHAR *g_szReloadTimeOut; // = L"UnloadDelay";

DWORD dwFlags, dwSize;

// KEY hDevKey;

HANDLE hReg;

DEVDETAIL * pd = (DEVDETAIL *)g_pPNPBuf;

GUID guid = {0};

HANDLE pHandles[2];

TCHAR szGuid[MAX_PATH];

HMODULE hCoreDll;

DWORD dwTimeOut = INFINITE, dwTimeOutReset = DEFAULT_TIMEOUT_RESET;

PSTOPDEVICENOT pStopDeviceNotification = NULL;

PREQUESTDEVICENOT pRequestDeviceNotification = NULL;

pHandles[0] = g_hPNPQueue;

pHandles[1] = g_hPNPUpdateEvent;

 

HKEY hKey;

// 从注册表[HKEY_LOCAL_MACHINE/System/StorageManager]处读取

// PNPUnloadDelay的值

if (ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, g_szSTORAGE_PATH, 0, 0, hKey)) {

if (!FsdGetRegistryValue(hKey, g_szReloadTimeOut, &dwTimeOutReset)) {

dwTimeOutReset = DEFAULT_TIMEOUT_RESET;

       }

    }

DEBUGMSG(ZONE_INIT,( L"STOREMGR:Using PNP unload delay of %ld/r/n",dwTimeOutReset));

AutoLoadFileSystems();

 

// coredll.dll中得到RequestDeviceNotificationsStopDeviceNotifications两个函数

// 的入口地址

hCoreDll = (HMODULE)LoadLibrary(L"coredll.dll");

if (hCoreDll) {

pRequestDeviceNotification = (PREQUESTDEVICENOT)GetProcAddress( hCoreDll, L"RequestDeviceNotifications");

pStopDeviceNotification=(PSTOPDEVICENOT)GetProcAddress(hCoreDll, L"StopDeviceNotifications");

}

 


 

FreeLibrary( hCoreDll);

if (pRequestDeviceNotification && pStopDeviceNotification) {

DEBUGMSG( ZONE_INIT, (L"STOREMGR: PNPThread Created/r/n"));

while(!IsAPIReady(SH_DEVMGR_APIS)) {

Sleep(1000);

}

// 告知系统将新发现的区块装置的信息写入刚刚建立的讯息队列g_hPNPQueue

hReg = pRequestDeviceNotification(&BLOCK_DRIVER_GUID, g_hPNPQueue, TRUE);

while(TRUE) {

DWORD dwWaitCode;

dwWaitCode = WaitForMultipleObjects( 2, pHandles, FALSE, dwTimeOut);

if (dwWaitCode == WAIT_OBJECT_0) {

// 讯息队列有新的讯息写入,读取有关新设备的信息

if (ReadMsgQueue(g_hPNPQueue, pd, sizeof(g_pPNPBuf), &dwSize, INFINITE, &dwFlags)) {

FsdStringFromGuid(&pd->guidDevClass, szGuid);

DEBUGMSG(ZONE_INIT, (L"STOREMGR: Got a plug and play event %Class(%s) Attached=%s!!!/r/n", pd->szName, szGuid, pd->fAttached ? L"TRUE":L"FALSE"));

// 检查新发现的设备是否为区块装置

if (memcmp( &pd->guidDevClass, &BLOCK_DRIVER_GUID, sizeof(GUID)) == 0) {

// 如果新的区块装置的状态为Attached则继续后面的加载工作

if (pd->fAttached) {

// 将设备名及其GUID作为参数传给MountStore

MountStore( pd->szName, pd->guidDevClass);

⋯⋯ // 后面略去

 

对于Ramdisk来说,当运行其安装程序ceramdrv.exe之后,系统便会发现这块虚拟的外围储存设备并首先由设备管理器负责为其加载区块装置驱动程序ramdisk.dllRamdisk所使用的设备驱动程序等信息是由安装程序ceramdrv.exe填写进注册表的,见图11-2,其中dll的键值就是Ramdisk的设备驱动程序,而IClass的键值{A4E7EDDA-E575-4252-9D6B-4195D48BB865}即为CE中标识区块设备所用的GUID,它将作为参数之一传给MountStore。另外还有很重要的一个键是Profile,它的键值指明了Ramdisk的组态信息所在的位置,在后面还会提到这一点。

 

另外,在成功为其装入区块装置驱动程序之后,设备管理器会将Ramdisk作为已经启动(Active)的设备填写进注册表(见图11-3),并透过WriteMsgQueueRamdisk的设备名及其GUID等信息写入讯息队列。可以看到,与Ramdisk 有关的设备基本信息位于[HKEY_LOCAL_MACHINE/Drivers/Builtin/Ramdisk],也就是图11-2所示的注册表位置;另外,Name的键值即为Ramdisk的设备名称,也就是说DSK1:将作为参数之一传给MountStore

 


 

11-2 注册表中有关Ramdisk的信息

 

11-3 Ramdisk启动之后的注册表


 

进入MountStore之后,首先在由Storage Manager维护的以g_pStoreRoot为开头结点的串行中寻找是否已存在具有相同设备名的store。如果发现了具有相同设备名的匹配项目,并且该store的旗标位STORE_FLAG_DETCHED处于重导模式(说明设备可用),则表明该设备已经加载到系统中,直接返回。相反,如果在串行中未找到匹配项目,或虽然找到但是该store的旗标位STORE_FLAG_DETACHED处于已置位状态(说明设备不可用),则还要继续下面的工作。即以设备管理器告知的设备名及其GUID建立一个CStore对象,然后呼叫CStore::MountStore(BOOLbMount)完成实际的加载工作。如果以g_pStoreRoot为开头结点的串行中存在旗标位STORE_FLAG_DETACHED处于已置位状态的store,则参数bMountFALSE,否则为TRUE

 

CStore::MountStore函数中,首先呼叫OpenDisk。由于从设备管理器那里已经知道了设备名称,因此可以透过CreateFileW建立设备句柄,这之后还可以由DeviceIoControl得到有关该设备的更详细信息。在呼叫DeviceIoControl时如果dwIoControlCodeDISK_IOCTL_GETINFO,那么可以得到包含CHS等设备参数的DISK_INFO结构,并将其保存在Cstore的成员变量m_si中;如果dwIoControlCodeIOCTL_DISK_DEVICE_INFO,那么可以得到有关该设备的一些额外信息,这些信息组成了一个STORAGEDEVICEINFO结构,并且保存在CStore的成员变量m_sdi中。STORAGEDEVICEINFO结构中很重要的一项信息就是该设备的Profile在注册表中的位置,这决定了将从哪里得到有关该设备的组态信息。之后就可以呼叫GetStoreSettings,由注册表的Profile中读出该设备的这些组态信息。例如,是否支持AUTOMOUNTAUTOFORMAT以及AUTOPART、该设备所使用的预设档案系统、为该设备加载哪个partition driver等。得到这些信息后,首先建立一个CPartDriver对象,并透过LoadPartitionDriver加载从注册表中得知的该设备所使用的partition driver,另外LoadPartitionDriver还会完成刚刚加载的partition driver中的输出函数到CPartDriver中函数指针的映像。如果在注册表中未提供有关partition driver的加载信息,则系统使用预设的partition driverMSPart.dll)。下面呼叫的OpenStore被映像到刚刚载入的partition driverPD_OpenStore,它会根据由设备启动扇区读入的磁盘分区表建立并维护PartitionManager将用到的DriverState(代表设备)、PartState(代表设备上的分区)和SearchState(用于遍历(iterate)设备上的分区)等数据结构。该函数返回时, m_dwStoreId已被填写为在Partition Manager这一层上与设备相对应的DriverState结构的指针。之后的GetStoreInfo被映像为PD_GetStoreInfo,由于刚刚得到的m_dwStoreId是指向DriverState结构的指针,因此可以从Partition Manager保存的数据结构中读出这里所需的信息,并把这些信息组织在一个PD_STOREINFO结构中。接下来检查m_dwStoreId所指向的设备是否已经进行过格式化操作,如果该设备尚未被格式化并且前面由注册表读出的组态信息表明该设备支持AUTOFORMAT,则呼叫被映像为PD_FormatStoreFormatStore函数对该设备进行自动格式化。而后的GetPartitionCount透过PD_FindPartitionStartPD_FindPartitionNext以及PD_FindPartitionClose这三个函数的配合对该设备的分区进行遍历(iterate),由此统计出设备上的分区个数。如果统计结果为零,说明该设备尚未进行分区。这时如果前面由注册表读出的组态信息表明该设备支持AUTOPART,则以设备上可供利用的最大储存空间自动建立并格式化一个分区,这是透过呼叫CreatePartitionFormatPartition这两个函数完成的。最后一步是呼叫LoadExistingPartitions,它会像GetPartitionCount一样藉由所加载的partition driverPD_FindPartitionStartPD_FindPartitionNext以及PD_FindPartitionClose对该设备的所有分区进行巡访,但这次巡访的目的不是为了统计分区个数,而是呼叫LoadPartition依次载入每个分区。

 

*** %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREMGR/store.cpp

Line 322   ***

DWORD CStore::MountStore(BOOL bMount)

{

DWORD dwError = ERROR_GEN_FAILURE;

// 由设备名称DSK 1 :建立Ramdisk的设备识别码,并得到设备的相关信息

if(ERROR_SUCCESS == (dwError = OpenDisk()))

// 由注册表得到Ramdisk的组态信息

if (GetStoreSettings()) {

m_pPartDriver = new CPartDriver();

// 装入预设的分区驱动程序mspart.dll  

if (ERROR_SUCCESS == (dwError = m_pPartDriver-> LoadPartitionDriver (m_szPartDriver))) {

// mspart.dll中的PD_OpenStoreRamdiskPartition Manager建立

// 相应的数据结构

if (ERROR_SUCCESS == (dwError = m_pPartDriver- > OpenStore (

m_hDisk, & m_dwStoreId  ) ) ) {

DEBUGMSG( 1, (L" Opened the store hStore=%08X/r/n",

m_dwStoreId ) ) ;

m_si.cbSize = sizeof(PD_STOREINFO);

m_pPartDriver->GetStoreInfo( m_dwStoreId, &m_si);

DEBUGMSG( 1, ( L " NumSec = %ld BYTEsPerSec= %ld FreeSec = %ld

BiggestCreatable=%ld/r/n", (DWORD)m_si.snNumSectors, (DWORD)m_si.dwBytesPerSector, (DWORD)m_si.snFreeSectors, (DWORD)m_si.snBiggestPartCreatable));

// 检查Ramdisk是否已经进行过格式化操作,如果尚未进行过格式化

// 并且由注册表得知Ramdisk支持AUTOFORMAT,则对其进行自动// 格式化操作

if (ERROR_SUCCESS != m_pPartDriver->IsStoreFormatted(m_dwStoreId))

{ // TODO: Look for ERROR_BAD_FORMAT

if (m_dwFlags & STORE_ATTRIBUTE_AUTOFORMAT) {

// TODO: Check for failure

m_pPartDriver- > FormatStore( m_dwStoreId ) ;

m_pPartDriver->GetStoreInfo( m_dwStoreId, &m_si);

}

}

// 藉由分区驱动程序提供的PD_FindPartitionStart

// PD_FindPartitionNext PD_FindPartitionClose三个函数统计分区个//

GetPartitionCount() ;

// 如果统计结果为零,说明设备尚未分区。这时根据注册表中AutoPart

// 的键值决定是否自动分区

if (!m_dwPartCount) {

if (m_dwFlags & STORE_ATTRIBUTE_AUTOPART) {

PD_STOREINFO si;

si.cbSize = sizeof(PD_STOREINFO);

m_pPartDriver->GetStoreInfo(m_dwStoreId, &si);

if ( (ERROR_SUCCESS == m_pPartDriver->CreatePartition(m_dwStoreId, L"PART00", 0, si.snBiggestPartCreatable, TRUE)) &&

(ERROR_SUCCESS == m_pPartDriver -> FormatPartition

(m_dwStoreId, L"PART00", 0, TRUE))) {

m_pPartDriver -> GetStoreInfo( m_dwStoreId, &m_si ) ;

DEBUGMSG( 1, (L"NumSec=%ld BytesPerSec=%ld

FreeSec = %1dBiggestCreatable= %ld/r/n " , ( DWORD) m_si.snNumSectors, (DWORD) m_si.dwBytesPerSector,(DWORD)m_si.snFreeSectors,(DWORD)m_si.snBiggestPartCreatable ));

m_dwPartCount = 1;

}

}

}

// 呼叫LoadExistingPartitions完成后面的工作

LoadExistingPartitions ( bMount ) ;

m_dwFlags |= STORE_FLAG_MOUNTED;

}

}

}

}

return dwError;

}

*** %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREMGR/store.cpp Line 178 ***

DWORD CStore::OpenDisk()

{

DWORD dwError = ERROR_SUCCESS;

// 根据设备名建立设备识别码,首先尝试以可读可写方式建立

m_hDisk = CreateFileW(m_szDeviceName,

GENERIC_READ | GENERIC_WRITE,

0 ,

NULL, OPEN_EXISTING, 0, NULL);

if (m_hDisk == INVALID_HANDLE_VALUE) {

dwError = GetLastError();

// 如果尝试失败则以只读方式建立

if (dwError == ERROR_ACCESS_DENIED) {

dwError = ERROR_SUCCESS;

m_hDisk = CreateFileW(m_szDeviceName,

GENERIC_READ,

FILE_SHARE_READ ,

NULL, OPEN_EXISTING, 0, NULL);

if (m_hDisk != INVALID_HANDLE_VALUE) {

m_dwFlags |= STORE_ATTRIBUTE_READONLY;

} else {

dwError = GetLastError();

}

}

}

if (m_hDisk != INVALID_HANDLE_VALUE) {

DWORD dwRet;

// 得到含有C H S值的DISK _ INFO信息

if ( !::DeviceIoControl ( m_hDisk,DISK_IOCTL_GETINFO, & m_si ,

sizeof(DISK_INFO) , NULL, 0, & dwRet, NULL)) {

dwError = GetLastError();

if ( ( dwError ==ERROR_BAD_COMMAND) || ( dwError ==

ERROR_NOT_SUPPORTED ) ) {

memset( &m_si, 0, sizeof(DISK_INFO));

dwError = ERROR_SUCCESS;

}

}

if (dwError == ERROR_SUCCESS) {

// 得到STORAGEDEVICEINFO信息,其中m_sdi. szProfile指明了设备的

// 组态信息在注册表中的位置

if ( !::DeviceIoControl ( m_hDisk , IOCTL_DISK_DEVICE_INFO , &m_sdi,

sizeof (STORAGEDEVICEINFO ), NULL, 0, &dwRet, NULL)) {

DEBUGMSG( ZONE_INIT, (L"FSDMGR!CStore::OpenDisk(0x%08X) call to

IOCTL_DISK_DEVICE_INFO failed..filling info/r/n", this));

m_sdi.dwDeviceClass = STORAGE_DEVICE_CLASS_BLOCK;

m_sdi.dwDeviceFlags = STORAGE_DEVICE_FLAG_READWRITE;

m_sdi.dwDeviceType = STORAGE_DEVICE_TYPE_UNKNOWN;

wcscpy( m_sdi.szProfile, L"Default");

}

}

DEBUGMSG( ZONE_INIT, (L"FSDMGR!CStore::OpenDisk(0x%08X) DeviceInfo Class(0x%08X) Flags(0x%08X) Type(0x%08X) Profile(%s)/r/n",

this,

m_sdi.dwDeviceClass ,

m_sdi.dwDeviceFlags ,

m_sdi.dwDeviceType ,

m_sdi.szProfile ) ) ;

}

return dwError;

}

 

***%_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREMGR/store.cpp

Line 121 ***

BOOL CStore::GetStoreSettings()

{

HKEY hKeyStorage=NULL, hKeyProfile = NULL;

BOOL bRet = FALSE;

// hKeyStorage值为[ HKEY_LOCAL_MACHINE/System/StoageManager/Profiles ]

if (ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, g_szPROFILE_PATH, 0, 0, &hKeyStorage ) ) {

DUMPREGKEY(ZONE_INIT, g_szPROFILE_PATH, hKeyStorage);

// hKeyProfile值为

// [HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/RamDisk]

wsprintf( m_szRootRegKey, L"%s//%s", g_szPROFILE_PATH, m_sdi.szProfile);

if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_LOCAL_MACHINE, m_szRootRegKey, 0, 0, &hKeyProfile)) {

hKeyProfile = NULL;

} else {

DUMPREGKEY(ZONE_INIT, m_sdi.szProfile, hKeyProfile);

}

// 根据注册表中A U T O MOUNTAUTOFORMAT以及AUTOPART三者的值设

// m_dwFlags

if (!hKeyProfile ||  !FsdLoadFlag(hKeyProfile, g_szAUTO_MOUNT_STRING,

&m_dwFlags, STORE_ATTRIBUTE_AUTOMOUNT ) )

if (!FsdLoadFlag(hKeyStorage, g_szAUTO_MOUNT_STRING, &m_dwFlags,

STORE_ATTRIBUTR_AUTOMOUNT )  )

m_dwFlags |= STORE_ATTRIBUTE_AUTOMOUNT;

if (!hKeyProfile || !FsdLoadFlag(hKeyProfile, g_szAUTO_FORMAT_STRING,

&m_dwFlags, STORE_ATTRIBUTE_AUTOFORMAT))

Fsd_LoadFlag ( hKeyStorage , g_szAUTO_FORMAT_STRING , &m_dwFlags ,

STORE_ATTRIBUTE_ AUTOFORMAT ) ;

if (!hKeyProfile || !FsdLoadFlag(hKeyProfile, g_szAUTO_PART_STRING,

&m_dwFlags, STORE_ATTRIBUTE_AUTOPART))

Fsd_LoadFlag ( hKeyStorage , g_szAUTO_PART_STRING, &m_dwFlags ,

STORE_ATTRIBUTE_ AUTOPART ) ;

// 根据注册表得到Ramdisk所使用的文件系统驱动程序,最后使用的一级目录名

// 称以及储存设备的描述性名称

if (!hKeyProfile || !FsdGetRegistryString (hKeyProfg_szFILE_SYSTEM_STRING,m_szDefaultFileSystem,sizeof(m_szDefaultFileSystem)))

if (!FsdGetRegistryString(hKeyStorage, g_szFILE_SYSTEM_STRING,

m_szDefaultFileSystem, sizeof(m_szDefaultFileSystem ) ) )

wcscpy( m_szDefaultFileSystem, g_szDEFAULT_FILESYSTEM);

if ( ! hKeyProfile | | !FsdGetRegistryString ( hKeyProfg_szFOLDER_NAME_STRING, m_szFolderName, sizeof(m_szFolderName ) ) )

if (!FsdGetRegistryString(hKeyStorage, g_szFOLDER_NAME_STRING,

m_szFolderName, sizeof(m_szFolderName)))

wcscpy( m_szFolderName, g_szDEFAULT_FOLDER_NAME);

if ( ! hKeyProfile || ! FsdGetRegistryString ( hKeyProfg_szSTORE_NAME_STRING, m_szStoreName, sizeof(m_szStoreName)))

if ( ! FsdGetRegistryString( hKeyStorage , g_szSTORE_NAME_STRING,

m_szStoreName, sizeof(m_szStoreName)))

wcscpy( m_szStoreName, g_szDEFAULT_STORAGE_NAME);

}

// 从注册表中读取Ramdisk所使用的分区驱动程序名称

if (!GetPartitionDriver(hKeyStorage, hKeyProfile))

goto ExitFalse;

bRet = TRUE;

ExitFalse :

if (hKeyStorage) {

RegCloseKey( hKeyStorage);

}

if (hKeyProfile) {

RegCloseKey( hKeyProfile);

}

return bRet;

}

由图11-2可知,Profile键值为Ramdisk,正是这个值指出Ramdisk的组态信息在注册表的位置,即[HKEY_LOCAL_MACHINE/System/StoageManager/Profiles/Ramdisk],此处所

含的注册表信息如图11 - 4所示。

由图11 - 4可知,Ramdisk支持AutoformatAutoPartA u t o Mount三种操作,使用的文件系统驱动程序为FATFS,在后面注册时使用的一级目录名为Storage Card。另外对于此处没有给出的一些信息则要用到系统的预设组态,这些预设组态信息位于[HKEY_LOCAL_MACHINE/System/StoageManager/Profile],见图11 - 5。这里Ramdisk用到的惟一一项预设组态就是分区驱动程序mspart.dll

下面再来看看LoadPartition。首先是呼叫OpenPartition,藉由该函数得到由Partition Manager维护的代表该分区的PartState结构的指针。在这之后,建立一个CPartition对象并将其加入到CStorem_pPartitionList串行中,然后呼叫LoadPartition得到有关该分区的具体信息(汇总为一个PD_PARTINFO结构)。之后是一个判断语句,如果该store代表一个新发现的设备并且前面由

 

11-4 注册表中保存的Ramdisk的组态信息

11-5 储存管理器可能会用到的预设组态信息

 

 

 

注册表读出的组态信息表明该设备支持AUTOMOUNT,则呼叫MountPartition。其参数m_szRootRegKey为该设备的Profile在注册表中的位置,另一个参数则是预设使用的文件系统名称。在函数MountPartition中,首先由注册表读出有关分区的Mount选项以及所使用的文件系统等信息,然后装入相应文件系统驱动程序的动态连结库。最后填写一个FSDINTDATA数据结构,并呼叫InitEx。下面的工作就交由FSD Manager来完成了。

*** %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREMGR/store.cpp Line 291 ***

BOOL CStore::LoadPartition(LPCTSTR szPartitionName, BOOL bMount)

{

DWORD dwPartitionId;

// 得到由Partiton Manager负责维护的PartState结构的指针

if (ERROR_SUCCESS == m_pPartDriver->OpenPartition(m_dwStoreId,

szPartitionName,&dwPartitionId)) {

// 建立CPartition对象并将其加入到CStorem_pPartitionList串行中

CPartition *pPartition = new CPartition(m_dwStoreId, dwPartitionId,

m_pPartDriver, m_szFolderName);

AddPartition( pPartition);

// 得到有关该分区的具体信息

pPartition -> LoadPartition (szPartitionName ) ;

// 如果前面由注册表读出的组态信息表明该设备支持A U T O MOUNT,则呼叫

// CPartition的成员函数MountPartition完成后面的工作

if (bMount && (m_dwFlags & STORE_ATTRIBUTE_AUTOMOUNT)) {

if(pPartition->MountPartition(m_szRootRegKey,m_szDefaultFileSystem)){

m _ d w Mount C o u n t + + ;

}

}

return TRUE;

}

return FALSE;

}

 

*** %_WINCEROOT%/PRIVATE/WINCEOS/COREOS/STORAGE/STOREMGR/store.cppLine 291 ***

BOOL CPartition::MountPartition(LPCTSTR szRootRegKey, LPCTSTR szDefaultFileSystem)

{

extern const TCHAR *g_szPART_TABLE_STRING;

extern const TCHAR *g_szFILE_SYSTEM_MODULE_STRING;

extern const TCHAR *g_szMOUNT_FLAGS_STRING;

TCHAR szRegKey[MAX_PATH];

TCHAR szPartId[10];

HKEY hKey = NULL;

DWORD dwFlags = 0;

// 从注册表[HKEY_LOCAL_MACHINE/System/StoageManager/Profiles/Ramdisk]

// 处得到Mount F l a g s的值

wcscpy( m_szRootKey, szRootRegKey);

if (ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, m_szRootKey, 0, 0,

&hKey)) {

if(!FsdGetRegistryValue(hKey,g_szMOUNT_FLAGS_STRING,&dwFlags)) {

dwFlags = 0;

}

RegCloseKey( hKey);

}

wsprintf( szRegKey, L"%s//%s", m_szRootKey, g_szPART_TABLE_STRING);

 

// 决定Ramdisk所使用的文件系统驱动程序,即FATFS

wcscpy( m_szFileSys, szDefaultFileSystem);

if (ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, szRegKey,0, 0,&hKey)){

DUMPREGKEY(ZONE_INIT, szRegKey, hKey);

wsprintf(szPartId, L"%02X", m_pi.bPartType);

if ( ! FsdGetRegistryString( hKey , szPartId , m_szFileSys ,

sizeof(m_szFileSys))) {

wcscpy( m_szFileSys, szDefaultFileSystem);

}

}

//从注册表[HKEY_LOCAL_MACHINE/System/StorageManager/ FATFS]处得到FAT

// 文件系统驱动程序的模块名FATFSd.dll

if (wcslen(m_szFileSys)) {

GetFSDString ( g_szFILE_SYSTEM_MODULE_STRING , m_szModuleName,

sizeof ( m_szModuleName) ) ;

GetFSDString ( g_szFILE_SYSTEM_MODULE_STRING , m_szFriendlyName ,

sizeof ( m_szFriendlyName ) ) ;

if (m_szModuleName) {

// 装入FAT文件系统驱动程序FATFSd.dll

m_hFSD = LoadDriver( m_szModuleName);

// 填写FSD I N I T D A T A数据结构,呼叫InitEx

if (m_hFSD) {

FSDINITDATA fid;

fid.pIoControl = (PDEVICEIOCONTROL)PartitionIoControl;

fid.hDsk = (HANDLE)this;

fid.dwFlags = dwFlags;

wcscpy( fid.szFileSysName, m_szFileSys);

wcscpy( fid.szRegKey, szRootRegKey);

wcscpy( fid.szDiskName, m_szPartitionName);

fid.hFSD = m_hFSD;

fid.pNextDisk = NULL;

m_pDsk = InitEx( &fid);

}

}

}

if (m_pDsk) {

m_dwFlags |= PARTITION_ATTRIBUTE_MOUNTED;

} else {

if (m_hFSD) {

FreeLibrary( m_hFSD);

m_hFSD = NULL;

}

}

if (hKey) {

RegCloseKey(hKey) ;

}

return TRUE;

}

 

进入InitEx之后首先会呼叫FSDLoad,由其完成两项任务:

呼叫AllocFSD完成FSD结构的分配及初始化,并将其纳入文件系统驱动程序管理器的管理当中。其参数hFSD是装入文件系统驱动程序所对应的动态连结库后得到的识别码,有了这个识别码就可以藉由呼叫GetModuleFileName得到该动态连结库的名称。由于文件系统驱动程序所对应的动态连结库的名称与该文件系统驱动程序的输出函数所使用的前缀名相同,因此刚刚得到的动态连结库的名称也就是所有输出函数所使用的前缀名。下面藉由LocalAlloc申请分配一个FSD结构,并将其插入到文件系统驱动程序管理器负责维护的dlFSDList双向串行中。之后是透过呼叫GetFSDProcAddress检查hFSD对应的动态连结库是否带有FSD_MountDiskFSD_UnMountDisk这两个函数输出点,如果有则说明这是一个文件系统驱动程序;否则继续呼叫GetFSDProcAddress 检查该动态连结库是否带有FSD _ HookVolumeFSD_UnhookVolume这两个函数输出点,如果有则说明这是一个过滤器驱动程序,如果还未找到则将刚刚申请的FSD结构从dlFSDList双向串行中删除,并释放该结构所占用的空间。在通过了上述检查之后,由于前面已经知道了该文件系统驱动程序的输出函数所使用的前缀名,因此可以透过GetFSDProcArray得到该文件系统驱动程序和过滤器驱动程序的所有输出函数的入口地址。至此就完成了一个FSD结构的初始化工作,并且已经将其纳入文件系统驱动程序管理器负责维护的dlFSDList双向串行中。

      呼叫AllocDisk为该FSD分配并初始化一个DSK结构。DSK结构中的hDSK识别码由呼叫CreateFileW得到。在呼叫CreateFileW时首先试探以可读可写方式打开,如失败则以只读方式打开。由此也确定了DSK结构中的dwFlags是否具有DSK_READONL属性。之后透过LocalAlloc申请分配一个DSK结构并完成其成员变量的初始化。其中成员变量pFSD的值是

 

刚刚透过AllocFSD得到的,这样DSKFSD这两个结构之间就建立了连接。

*** PRIVATE/WINCEOS/COREOS/STORAGE/FSDMAIN/Fsdmain.cpp Line 155 ***

PDSK InitEx(FSDINITDATA *pInitData)

{

BOOL bSuccess = TRUE;

PDSK pDsk = FSDLoad(pInitData); // 呼叫FSDLoad建立的_DSK(设备)

// _ FSDFSD模块)结构

PDSK pDskNew = HookFilters( pDsk, pDsk->pFSD);//FSD之上挂接Filters

PFSD pFSD = pDsk->pFSD;                   //指向FSDLoad中建立的那个_FSD结构

 

// 如果_ FSD_DSK均建立成功

if (pFSD && pDsk) {

if (pFSD->pfnMountDisk) {

// 呼叫X X X _ Mount Disk把设备中的各个VOLUME注册进来

if (pFSD->pfnMountDisk(pDsk)) {

// 修改_DSK的状态

UnmarkDisk(pDsk, DSK_UNCERTAIN);

bSuccess = TRUE;

}

}

}

// 如果建立失败,释放前面申请的内存资源,略去. . .

// 返回_DSK结构的指针

return (pDsk);

}

 

*** PRIVATE/WINCEOS/COREOS/STORAGE/FSDMAIN/Fsdmain.cpp Line 127 ***

PDSK FSDLoad(FSDINITDATA * pInitData)

{

PFSD pFSD;

PDSK pDsk;

 

// 透过AllocFSD建立_ FSD结构

pFSD = AllocFSD((HMODULE)pInitData->hFSD);

// 透过AllocDisk建立DSK 结构

pDsk = AllocDisk(pFSD, pInitData->szDiskName, pInitData->hDsk,

pInitData->p IoControl ) ;

 

// 如果_DSK结构建立成功,继续设置

if (pDsk){

FSDMGR_GetDiskInfo(pDsk, &pDsk->fdi);        // 获取设备硬件方面的信息

//(容量,扇区大小等等)

pDsk->dwFlags |= pInitData->dwFlags << 16;   // 设置_DSK的属性

pDsk->pNextDisk = pInitData->pNextDisk; // 其实是NULL

pDsk->pPrevDisk = NULL;

}

// 如果_ FSD结构建立成功,继续设置

if (pFSD) {

// 拷贝名称(其实现在是NULL

wcscpy( pFSD->szFileSysName, pInitData->szFileSysName);

// 拷贝名称(其实现在是NULL

wcscpy( pFSD->szRegKey, pInitData->szRegKey);

}

// 返回DSK 结构的指针

return pDsk;

}

 

*** PRIVATE/WINCEOS/COREOS/STORAGE/FSDSERV/Fsdalloc.cpp Line 37 ***

PFSD AllocFSD(

HMODULE   h FSD // FSD模块的识别码

)

{

PFSD pFSD;

// 用于记录这个FSD模块中标准函数接口的前缀

WCHAR baseName[MAX_FSD_NAME_SIZE];

 

// 如果模块中FSD_CreateFileW 这个函数存在,那么前缀很简单,就是FSD_,这是

// 一种特殊情况

if (GetProcAddress(hFSD, L"FSD_CreateFileW")) {

wcscpy(baseName, L"FSD_");

} else {

// 否则就会比较麻烦才能找到这个前缀,略过不看. . .

}

// 函数前缀被存放在了b a s e N a m e

 

// 首先,为新建的_ FSD分配内存空间

pFSD = (PFSD)LocalAlloc(LPTR, sizeof(FSD));

 

if (pFSD) {

// 设定SIGFIELD(就是把后一个值赋给SIGFIELD

INITSIG(pFSD, FSD_SIG);

// 把新建的_ FSD结构,加入全局的双向串行,串行头是dlFSDList

AddListItem((PDLINK)&dlFSDList, (PDLINK)&pFSD->dlFSD);

// 设定_ FSD中模块识别码的属性

pFSD->hFSD = hFSD;

// b a s e N a m e拷贝到_ FSD中去

wcscpy( pFSD->wsFSD, baseName);

}

 

// 有了名称前缀,就能透过GetProcAddress获得模块中函数的指针

// 这里的GetFSDProcAddress本质上还是呼叫了GetProcAddress,只不过

// 先把后边的(如Mount Disk)和前缀合并起来,再去呼叫

if (pFSD) {

if (!pFSD->pfnMountDisk || !pFSD->pfnUnmountDisk) {

// _ FSD中的函数指针属性赋值

pFSD->pfnMountDisk = (PFNMOUNTDISK)GetFSDProcAddress ( p

TEXT ( " Mount Disk " ) ) ;

pFSD - > pfnUnMount Disk = (PFNMOUNTDISK) GetFSDProcAddress ( pFSD

TEXT("UnMountDisk")) ;

} 

}

 

// _ FSD结构中有三个函数指针的数组,而GetFSDProcArray就是给这个数组中的

// 函数指针赋值,其本质上还是去呼叫GetProcAddress,没有什么特别的地方

if ( GetFSDProcArray ( pFSD , pFSD - > apfnAFS , apfnAFSStubs ,

apwsAFSAPIs ,ARRAY_SIZE(apwsAFSAPIs)) &&

GetFSDProcArray ( pFSD , pFSD->apfnFile, apfnFileStubs, apwsFileAPIs , ARRAY_SIZE(apwsFileAPIs)) &&

GetFSDProcArray ( pFSD , pFSD->apfnFind, apfnFindStubs, apwsFindAPIs , ARRAY_SIZE(apwsFindAPIs))) {

pFSD -> pRefreshVolume = ( pRefreshVolume ) GetFSDProcAddress

L"RefreshVOLUME");

// 如果获取函数指针失败,透过D e AllocFSD释放_ FSD结构(并从串行中取出)

} else {

DeAllocFSD ( pFSD ) ;

pFSD = NULL;

}

 

// 返回新建立的_ FSD结构的指针(如果建立失败,返回NULL

return pFSD;

}

 

*** PRIVATE/WINCEOS/COREOS/STORAGE/FSDSERV/Fsdalloc.cpp Line 159 ***

PDSK AllocDisk(

PFSD pFSD,       // 这个设备所需要的FSD模块对应的_ FSD

// 结构指针

PCWSTR pwsDsk,   // 惟一标识的设备名称(比如说C O M 1 :

HANDLE hDsk, // InitEx中呼叫时传递的是

// INVALID_HANDLE_VALUE

PDEVICEIOCONTROLp IoControl // 用于IoControl的函数指针

)

{

DWORD dwFlags = DSK_NONE;

 

// 如果前面_ FSD建立失败,直接错误退出

if (pFSD == NULL)

return NULL;

 

// 透过设备的惟一名称,呼叫CreateFile打开设备

if (hDsk == INVALID_HANDLE_VALUE) {

hDsk = CreateFileW(

pwsDSK , // 设备的惟一标识名称

GENERIC_READ | GENERIC_WRITE, // 以读写模式打开

0 ,

NULL,

OPEN_EXISTING, // 设备是否存在

0 ,

NULL ) ;

 

// 如果成功,可以进行读写操作,设定dwFlag

if (hDsk != INVALID_HANDLE_VALUE)

// 本设备可以进行读写操作(而不是只读)

dwFlags &= ~DSK_READONLY;

// 否则的话,以只读模式打开设备

else if (GetLastError() == ERROR_ACCESS_DENIED) {

hDsk = CreateFileW(

pwsDSK ,

GENERIC_READ,

FILE_SHARE_READ ,      // 只读模式

NULL,

OPEN_EXISTING,

0,

NULL ) ;

 

// 如果成功,说明设备是只读的

if (hDsk != INVALID_HANDLE_VALUE)

// 设备只可以进行读操作

dwFlags |= DSK_READONLY;

}

pwsDsk = NULL;

}

// 如果读操作都不可以,直接错误退出

if (hDsk == INVALID_HANDLE_VALUE) {

return NULL;

}

 

// 申请_DSK结构需要的内存空间

PDSK pDsk = (PDSK)LocalAlloc(LPTR, sizeof(DSK));

 

// 接下来开始设置_DSK结构的各个属性

if (pDsk) {

INITSIG(pDsk, DSK_SIG);        // 设置SIGFIELD

pDsk->pFSD = pFSD;             // 指向前面建立的_FSD的结构

pDsk->pDeviceIoControl = pIOControl;  // 设定用于IoControl的函数指针

pDsk->pVol = NULL;             // 尚未向FSD Manager注册本设备

// 其中的文件系统卷,现在空

// 把本设备的标识名称拷贝到_DSK

if (pwsDsk) {

wcscpy( pDsk->wsDsk, pwsDsk);

} else {

wcscpy( pDsk->wsDsk, L"");

}

}

// 如果为_DSK分配空间失败,关闭前面打开的设备识别码(代码一定要严谨)

else {

if (hDsk) {

CloseHandle ( hDSK) ;

}

}

 

if (pDsk) {

pDsk->hDsk = hDsk;         // 设定设备的识别码

pDsk->dwFlags = dwFlags;   // 设定设备的属性(现在只有只读,或者读写两

// 种属性)

}

// 最后,返回新建立的_DSK结构的指针

return pDsk;

}

 

FSDLoad返回之后透过呼叫FSD MGR_GetDiskInfo以及FSDINTDATA结构中尚未用到的一些信息完成FSDDSK两个结构的剩余初始化工作,例如所使用的文件系统的名称、设备的组态信息在注册表中的位置等。在这之后, FSDLoad成功返回。

这之后是呼叫HookFilters从注册表中查找所有与该文件系统驱动程序相关的过滤器,并将它们纳入文件系统驱动程序管理器的管理之中。如前所述,有关文件系统过滤器的信息也是保存在注册表中的,文件系统驱动程序管理器需要到以下各处查找应加载的文件系统过滤器:

[HKEY_LOCAL_MACHINE / System/ StorageManager /AutoLoad / FileSystem/ Filters ]

[HKEY_LOCAL_MACHINE/System/StorageManager/Profiles/ProfileName/FileSystem/Filters]

[HKEY_LOCAL_MACHINE/System/StorageManager/FileSystem/Filters]

[ HKEY_LOCAL_MACHINE / System/ StorageManager / Filters ]

HookFilters的第一项工作就是到以上各处查找需要加载的过滤器驱动程序,并透过呼叫LoadFSD List将所有的过滤器驱动程序连结成一个单向队列。注册表中与每个过滤器相关的组态信息中都有一个Order,由它决定向文件系统驱动程序加载这些过滤器驱动程序时所采用的顺序。与此对应,LoadFSDList函数也有一个参数bReverse用于表明连结时所采用的顺序。如果为TRUE,则LoadFSDList在查找到过滤器驱动程序后将它们按递增排列。在收集了所有有关过滤器的信息

之后,下面要做的就是将所有的过滤器驱动程序一一连结到文件系统驱动程序上。这也是透过调用FSDLoad完成的,只不过前面加载的对象是文件系统驱动程序,而这次是一些过滤器驱动程序。由于每次都是从LoadFSDList建立的pFSDLoadList开头结点摘下一个过滤器,因此连接完成之后这些过滤器驱动程序的顺序便成了递减(REVERSE)。这一过程如图11-6、图11-7所示,所有的过滤

器驱动程序构成了过滤器驱动程序堆栈。

最后要做的是呼叫文件系统驱动程序的输出函数FSD_MountDisk,由其负责定位新设备上的所有卷(Volume),并呼叫FSDMGR_RegisterVolume在文件系统驱动程序管理器中对它们进行注册。在每个文件系统驱动程序中一般还会有一套由其自己定义的DSKVOLUME及其它一些结构,这些结构所代表的对象与文件系统驱动程序管理器中定义的DSKVOL等结构相同,但一般来说更为具体,以适应不同文件系统驱动程序的具体需要。FSD_MountDisk的主要作用就在于配合文件系统驱动程序管理器完成对其自身定义的这些结构的管理工作,以便今后使用。

11-6 呼叫LoadFSDList之后

11-7 DSK连结之后

 

FSD_MountDisk首先呼叫GetDiskInfo得到设备的相关信息,之后再由ReadWriteD isk读入该设备上的第一个扇区—即启动扇区的内容。由于启动扇区中含有分区信息表,因此可知道如何为该设备分配DSKVOLUME等结构以保存这些信息,并将它们管理起来。最后对各个卷以一级目录的形式在文件系统驱动程序管理器中进行注册。

注册用到就是下面要分析的FSDMGR_RegisterVol ume函数,它负责申请并初始化一个VOL结构,然后呼叫RegisterAFSEx以及RegisterAFSName按照文件系统驱动程序指定的名称对该卷进行注册。注册成功之后,文件系统驱动程序管理器就可以以目录的形式将这些卷显示出来了,并且使得应用程序可以藉由标准的文件系统A P I 函数对这些卷上的文件进行存取。

FSDMGR_RegisterVol ume函数的原型如下:

PVOL

FSD MGR_RegisterVOLUME (

PDSK pDSK ,             // 文件系统卷所在设备的_DSK结构指针

PWSTR pwsName,          // 希望把这个卷Mount到以此字符串为名的一级目录中

PVOLUME pVOLUME         // 具体的FSD中用于管理卷使用的数据结构指针

// 对于FAT FSD而言它就是_VOLUME结构的指针

)

前两个属性没什么好解释的,有疑问的是这里的第三个参数。事实上,作为文件系统驱动程序管理器,它并不需要知道关于每一个卷的详细信息(比如说其中的分区从哪个扇区开始,又在哪个扇区结束),它的工作仅仅是去告诉相应的FSD,由它管理的哪一个卷需要进行什么操作。

这些详细的信息对于文件系统驱动程序管理器是完全不必要的,也就不可能被放在相应的_VOL结构中。但对于具体的FSD,情况就不是这样了。这里的pVolume就指向了具体FSD中管理这个文件系统卷的数据结构(对于FAT FSD就是_VOLUME)。在FSDMGR_RegisterVolume中,所要做的就是简单地把这个指标赋给(将被建立的)_VOL结构中的dwVo l Data属性。

具体而言,这些入口是怎么被注册的呢?简单的想来,系统中应该维护一个列表,列表中记录了这些入口。一旦用户呼叫文件系统A P I,如CreateFile,必然会传给系统一个路径。系统先从路径名中分析出一级目录的名字,然后开始从列表中进行搜索,看这个一级目录是否是已经被登记的一个外围入口,如果是,就把操作权转让给FSD ManagerFSD Manager会进一步把操作权转给设备对应的FSD模块。

虽然这些注册入口的函数源码并没有公开,但是不难通过呼叫函数的原型大概猜测出其注册的方法。上面讲的那种入口在CE中称为A FS。一个AFS包括了这个入口的名称,还记录了是否有设备被Mount到了这个入口下,或者说是否有文件系统卷占用了这个入口。此外还会有一个序号(或者说索引)同每一个AFS一一对应。系统中预设进行预先登记的那个Storage Card对应的AFS

序号是OID_FIRST_AFS

AFS进行具体操作的函数有如下三个:

1) int GetAFSName(int iAFS, PWSTR pwsAFS, int cchMax) i AFS这个索引所指向的AFS是否已经被一个文件系统卷占用, p w s AFS会记录下这个入口的名字。如果返回值为零,就说明这个入口尚未被占用。

2) RegisterAFSName 建立一个AFS,其入口的名字就是此函数惟一的参数。返回值就是新建AFS一一对应的索引。

3) RegisterAFSEx 占用一个AFS入口。函数需要的参数包括这个被占用的AFS的索引(首先透过上面的RegisterAFSName来建立),以及占用这个入口的文件系统卷对应的_VOL结构指针等。

当一个文件系统卷进行Mount寻找(建立)AFS入口的时候,系统首先会从注册表中查找是否有已经设定好的对应这个设备的预设入口名称A FSDMGR_RegisterVol ume呼叫时那个pwsName指向了这个名称A,如果注册表中没有预设的名字,那么这个指标就是NULL)。然后会判断Storage Card这个预设的入口是否已经被占用,如果尚未被占用,就优先把卷Mount在这个入口中。否则判断是否提供了预设的入口( pwsName是否为NULL)。如果两个方向都被否定了,就比较麻烦了,需要自己起一个名字作为入口。

FSDMGR_RegisterVolume首先会呼叫AllocVolume为这个文件系统卷建立相应的_VOL结构。接下来,藉由一定的策略为这个卷找出一个入口名,最后把这个文件系统卷注册在这个入口AFS

中。具体的原始码如下:

*** PRIVATE/WINCEOS/COREOS/STORAGE/FSDSERV/Fsdserv.cpp Line 336 ***

PVOL FSDMGR_RegisterVolume(PDSK pDsk, PWSTR pwsName, PVOLUME pVolume)

{

PVOL pVol;

BOOL bSuccess = FALSE;

EnterCriticalSection(&csFSD);

 

// 如果已经为设备注册了文件系统卷,先藉由DeregisterVOLUME注销掉,再重新注册

if (pDsk->pVol) {

FSDMGR_Deregister VOLUME( PDSK - > pVOL ) ;

pDsk->pVol = NULL;

}

 

// 呼叫Al loc VOLUME建立(并初始化) _VOL结构

if (pVol = AllocVolume(pDsk, (DWORD)pVolume)) {

PFSD pFSD = pVol->pDsk->pFSD;

 

// 接下来的工作就是给这个新的卷注册一个AFS入口,首先需要找到合适的

// 入口名称

if (pVol->iAFS == INVALID_AFS) {

int cch;

int iAFS, iSuffix = 1;

WCHAR wsAFS[128];

 

// 首先藉由GetAFSNameStorage Card这个预设的入口是否已经被占用

cch = GetAFSName(OID_FIRST_AFS, wsAFS, ARRAY_SIZE(wsAFS));

 

// 如果没有给出可供使用的入口名( pwsName = NULL)而且Storage Card

// 已经被占用

if (pwsName == NULL && cch) {

// 后面会为这种最差的情况起一个入口名

iSuffix = 2;

pwsName = wsAFS;

}

 

// 如果没有给出可供使用的入口名(或者入口名就是Storage Card),而这个

// 预设的入口并没有被占用

if (((pwsName == NULL) || (wcscmp(pwsName, L"Storage Card")==0))

&& !cch){

// 那么直接呼叫RegisterAFSEx来占用这个入口就可以了

if (RegisterAFSEx(OID_FIRST_AFS, hAFSAPI, (DWORD)pVol,

AFS_VERSION,

( pDSK - > dwFlags >> 1 6 ) & ( AFS_ FLAG_ROOTFS |

AFS_FLAG_HIDEROM | AFS_FLAG_BOOTABLE))) {

// 入口的AFS索引就是OID_FIRST_AFS

pVol->iAFS = OID_FIRST_AFS;

// 把入口名储存在_VOL结构中

wcscpy( pVol->wsVol, wsAFS);

bSuccess = TRUE;

}

}

if (!bSuccess) {

// 如果上面的做法都不成功的话,就给Storage Card(或者给出的那

// 个名字)后面加上数字序号12. . .,如Storage Card1 Storage Card2

// 等等

wcscpy(wsAFS, pwsName[0] == '//'? pwsName+1 : pwsName);

cch = wcslen(wsAFS);

do {

// 不断尝试各个序号

if (iSuffix > 1) {

wsAFS[cch] = '0'+iSuffix;

wsAFS[cch+1] = 0;

}

// 看这个新的名字(末尾加了序号)对应的入口是否已经被占用

iAFS = RegisterAFSName(wsAFS);

// 如果尚未被使用,就可以藉由RegisterAFSEx来占用它,否则的

// 话继续循环(序号+ +

if (iAFS != INVALID_AFS && GetLastError() == 0) {

if (RegisterAFSEx(iAFS, hAFSAPI, (DWORD)pVol,

AFS_VERSION,

(pDsk->dwFlags >> 16) & (AFS_FLAG_ROOTFS |

AFS_FLAG_HIDEROM | AFS_FLAG_BOOTABLE))) {

// 把入口名储存在_VOL结构中

wcscpy( pVol->wsVol, wsAFS);

// AFS的索引储存在_VOL结构中

pVol->iAFS = iAFS;

break ;

}

}

} while (iSuffix++ < 9);

}

}

 

// 如果连序号都试过(Storage Card1Storage Card9),却都被占用了

//(这种情况应该很少见,毕竟C E不可能连接那么多设备)

if (pVol->iAFS == INVALID_AFS) {

// 注册(Mount)失败,释放前面申请的资源

D e Al loc VOLUME ( pVOL ) ;

pVol = NULL;

} else {

pVol->hNotifyHandle = NotifyCreateVolume(pVol->wsVol);

}

}

LeaveCriticalSection(&csFSD);

return pVol;

}

 

AllocVolume这个函数的目的同前面的AllocFSDAllocDisk非常类似。主要就是建立(并初始化)一个同文件系统卷相对应的_VOL结构。比起AllocFSDAllocDisk来要简单很多。首先会申请_VOL结构需要的内存空间,然后做简单的属性设置就可以了。具体的原始码如下:

*** PRIVATE/WINCEOS/COREOS/STORAGE/FSDSERV/Fsdalloc.cpp Line 298 ***

PVOL AllocVolume(

PDSK pDsk,          // 文件设备卷所属设备的_DSK结构指针

DWORD dwVolData     // 具体FSD中用于管理这个文件系统卷使用的数

// 据结构指针,它会被简单地赋给_VOL结构中的

// dwVolData属性

)

{

PVOL pVol;

if (pDsk == NULL)

return NULL;

 

// 首先,申请_VOL结构需要的内存空间

pVol = (PVOL)LocalAlloc(LPTR, sizeof(VOL));

 

// 申请成功的话,进行简单的初始化工作

if (pVol) {

INITSIG(pVol, VOL_SIG);                         // 设定SIGFIELD

I n i t List ( ( PDLINK ) &pVOL->dlHdlList); // 初始化_HDL结构的串行头

pDsk->pVol = pVol;                           // 设定_DSK_VOL指标

pVol->dwFlags = 0;

pVol->pDsk = pDsk;                           // 设定_VOL_DSK指标

pVol->iAFS = INVALID_AFS;         // 现在尚未被Mount,所以索引还没有意义

pVol->dwVolData = dwVolData;      // dwVOLData赋给_VOL中的相应属性

}

 

// 最后返回_VOL结构的指针

return pVol;

}

 

所有这些工作结束之后,Ramdisk就会以一级目录的形式展现出来,而这之后就可以藉由CreateFileReadFileWriteFile这些标准的文件系统Win32 API接口对Ramdisk进行文件操作。图11 - 8中的一级目录「储存卡」即为载入后的Ramdisk,这个一级目录名称正是图11 - 4所示的注册表中Folder的键值。当然,由于是中文版Windows CE的关系,Storage Card变成了储存卡。

11-8 Ramdisk安装成功之后以目录的形式展现在用户面前

11.2 FAT文件系统中CreateFile的具体流程:

 

11.2.1 实验环境

所有的除错工作均在Platform Builder 4.0下完成。目标机由Platform Builder自带的仿真器代替。首先,建立一个BSPEmulator的工程,选用设值为Internet Appliance。由于需要跟踪的是FAT FSD的操作,所以还需要一个储存外围。由于Internet Appliance的设置并没有提供相关外围

的驱动,为了方便起见,采用了上一节介绍的/PUBLIC/COMMON/OAK/DRIVERS /BLOCK/RAMDISK下的虚拟磁盘驱动,从R A M中虚拟出一块磁盘,并将其格式化为FAT。最后设备对应的路径为「 /储存卡/

希望跟踪的是FAT_CreateFileW这个函数,而这个函数对应最上层的系统呼叫是CreateFile,于是建立了一个应用程序,在程序中呼叫CreateFile。具体呼叫如下:

CreateFile ( L " / /储存卡//MYFILE.TXT",      //文件名为MYFILE . TXT

GENERIC_READ,                     //只读打开

FILE_SHARE_READ ,                 //允许读共享

NULL ,

CREATE_NEW ,                      //建立新的文件

FILE_ ATTRIBUTE_NORMAL,

NULL ) ;

 

11.2.2 参数介绍

1. PVOLUME pvol

一个指向_VOLUME的类型,标识FAT卷的信息。程序运行时CallStack如下:

FATFSD ! FAT_CreateFileW

FSDMGR ! FSDMGR_CreateFileW

COREDLL ! xxx_AFS_CreateFileW

FILESYS ! FS_CreateFileW

NK ! SC_CreateFileW

COREDLL ! xxx_CreateFileW

 

当发生系统呼叫到最终执行FAT_CreateFileW,中间经过COREDLLNKFILESYSFSDMGR这些程序的呼叫,而这个_VOLUME是由FSDMGR传过来的,也就是说, FAT卷的相关信息是从FSDMGR获得的。

关于_VOLUME的结构定义如下:

struct _VOLUME {

HVOL   v_hVOL ;                        //卷的相关信息

VOL_DLINK v_dlOpenVOLUMEs;            //磁盘上打开的卷的列表

S TM_DLINK    v_dlOpenStreams ;               //该卷上被打开的资料流的列表

#ifdef PATH_CACHING                   //如果对P A T H也进行CACHE

CCH_DLINK v_dlCaches ;                //对应的CACHE列表

#Endif

PDSK      v_PDSK;                     //该卷所在的磁盘的信息

PPARTINFO v_ppi ;                     //分区信息

DWORD     v_flags ;                   //卷的标志

signed char   v_VOLID ;                //文件系统标识,对于FAT0 x 0 1

BYTE      v_cCaches ;                 //当前Cache的入口

//

// 以下是需要在I n i t VOLUME时进行初始化的值

//

BYTE v_bMediaDesc ;                  //媒体的描述,从BPB中得到

BYTE v_bVerifyCount ;            //需要做「写后读」检查的数目

DWORD v_serialID ;                //卷的序列号boot sector中的卷标

BYTE v_label[OEMNAMESIZE+1] ;        // boot sector中的卷标

BYTE v_log2cbSec ;               // log2(bytes per sector)

BYTE  v_log2cblkSec;                  // log2(blocks per sector)

BYTE v_log2csecClus ;            // log2(sectors per cluster)

BYTE v_log2cblkClus ;            // log2(blocks per cluster)

DWORD v_secVOLBias ;                  //Sector的起始偏移值

DWORD v_secB l kBias ;            //block 0 的偏移值

DWORD v_blkClusBias ;                 //cluster 0的偏移值

DWORD v_cbClus ;                  //每个cluster的字节数

DWORD v_clusEOF ;                     // clusterEOF地址

DWORD v_clusMax ;                     // cluster数目的最大值

DWORD v_clusAl loc ;                  //被分配的最后一个cluster,没有为- 1

DWORD v_c clusFree ;                  //尚未使用的cluster,没有为- 1

DWORD v_c secFAT ;                // FATSector的数量

DWORD v_secEndFAT ;               //当前活动FAT中结束的Sector

DWORD v_secEndAl lFATs ;              //所有FAT结束的Sector

DWORD v_c secUsed ;               // FATroot使用的sectoer

DWORD v_c secTotal ;                  //卷上的Sector

#ifdef FAT32 //FAT3 2

DWORD v_secF S Info ;             //对应BIGFATBPB->BGBPB_ FSInfoSec

#Endif

UNPACKFN * v_unpack ;                //FATunpack函数指针

PACKFN        * v_pack ;                      //FATpack 函数指针

//

// 以上是需要在I n i t VOLUME时进行初始化的值

//

PDSTREAM v_pstmFAT;               //读写FAT所用的数据流

PDSTREAM v_pstmRoot ;         //读写根目录所用的数据流

PWSTR     v_pwsHostRoot ;          //系统对应的目录名称,若没有则为NULL

DWORD     v_cwsHostRoot ;          //上面目录名称的长度

CRITICAL_SECTION v_cs;         //卷的临界区

CRITICAL_SECTION v_csStms;     // OpenStreams列表使用的临界区

#ifdef PATH_CACHING                    //如果对PATH也进行CACHE

CRITICAL_SECTION v_csCaches;       // Cache列表使用的临界区

# Endi f

#ifdef DISK_CACHING

LPBYTE        v_pFATCacheBuffer;       //磁盘的Cache Buffer

LPDWORD   v_pFATCacheLookup ;         //Lookup buffer for Cache

DWORD     v_CacheSize ;               // Cache的大小

# Endif

DWORD     v_f l FATFS;                //每一个FAT卷的标识

};

2. 扇区(Sector)和簇(Cluster)概念比较

扇区(Sector)是对外围进行数据读写(或者说外围储存数据)的基本单位,每个扇区对应了一个惟一的实体扇号。藉由实体扇号,设备驱动程序就能够读取或者写入数据。此外,每一个扇区相对于盘卷的引导扇区,还有逻辑扇号。从概念上讲,逻辑扇号同实体扇号之间有一个隐藏扇区数的差别,但由于实际的单分区的设备,不存在隐藏扇区,所以两者是等价的。

FAT中的文件区被统一划分成了簇(Cluster)。从大小上看,每个簇对应的扇区个数必须是2的幂次方。从概念上讲,簇是文件储存的基本单位。每一个簇对应了FAT中的一个表项,它指出了这个簇的状态(是否被占用、是否是文件的最后一个簇、文件的下一个簇是什么等)。把一个簇在FAT中对应的表项的索引( FAT中第一个表项的索引为0,以此类推),称为这个簇的编号。由于FAT中的前两个簇项不对应任何的簇,所以文件区中的第一个簇编号为2

. HANDLE hProc 打开该文件的进程的识别码,本函数中没有使用。

. LPCWSTR lpFileName 文件的名称在本例中为: /储存卡/ MYFILE.TXT

. DWORD dwAccess 文件的存取方式,GENERIC_READGENERIC_WR I TE或是完全存取。在本例中为:GENERIC_READ

. DWORD dwShareMode允许共享的方式,FILE_SHARE_READFILE_SHA RE_WRITE或是完全共享。在本例中为: FILE_SHARE_READ

. LPSECURITY_ATTRIBUTES lpSecurityAttributes 在此被忽略,无用参数。在本例中为:NULL

. DWORD dwCreate 标识,当文件被建立或是被打开时的具体操作。在本例中为:CREAT E_NEW

. DWORD dwFlagsAndAttributes 对应CreateFileW中的dwFlagsAndATTRIBUTE s。在本例中为:FILE_ ATTRIBUTE_NORMAL

. HANDLE hTemplateFile 在此被忽略,无用参数。在本例中为:NULL

 

11.2.3 局部变数

. BYTE mode 文件打开的模式,根据dwShareModedwAccess得到,在后面传给CheckStreamSharing来检查数据流的共享模式。

. int flName 文件属性,根据dwCreat edwFlagsAndAttributes得到,在后面传给OpenName标识文件名的类型(egNAME_FILE, NAME_DIR)和文件属性(egATTR_DIRECTORY )

. HANDLE hFile 文件识别码,由FSDMGR _ CreateFileHAndle建立,为FAT_CreateFileW的返回值。

. PFHANDLE pfh = NULL FHANDLE类型的指标。供FSDMGR_CreateFile HAndle使用。并将其加到pstm里去。

. PDSTREAM pstm = NULL DSTREAM类型指标,对文件的具体操作均是对数据流的操作。

. DWORD dwError = ERROR_SUCCESS 出错信息,供除错输出时使用。

. BOOL bExists = FALSE 文件是否存在的标识。

 

11.2.4 过程跟踪

当从一个系统呼叫CreateFile,到最终写磁盘的操作,中间的过程可以用以下CAl l Stack表示:

FATFSD ! ReadWriteDisk 2

FATFSD ! ReadWriteDisk

FATFSD ! WriteVOLUME

FATFSD ! CommitBuffer

FATFSD ! CommitBufferSet

FATFSD ! CreateName

FATFSD ! OpenPath

FATFSD ! OpenName

FATFSD ! FAT_CreateFileW

FSDMGR ! FSDMGR_CreateFileW

COREDLL ! xxx_AFS_CreateFileW

FILESYS ! FS_CreateFileW

NK ! SC_CreateFileW

COREDLL ! xxx_CreateFileW

 

FAT_CreateFileW开始以下的工作由FAT FSD来完成,从FAT_CreateFileW开始的呼叫到CreateName即是对数据流的操作,接着是对Buffer的操作,最后是I/O操作。

FAT_CreateFileW函数中呼叫的其它函数如下:

 

FAT_CreateFileW

FATEnter

OpenName

CheckStreamSharing

FSDMGR_ CreateFile HAndl e

FILESYSTEMNOTIFICATION

FAT    Exit

 

本次分析的重点是OpenName的实现以及往下的OpenPath等操作,至于后来的CommitBufferWriteVolume以及最终的ReadWriteDisk,限于篇幅只是简要提到,如有兴趣,可参考其原始码。OpenName的呼叫实现了从StreamBufferCache到最终的I/O操作,也贯穿了整个的FAT存取结构。

具体分析如下:

1) 呼叫FATEnter

全域变量cFATThReads记录了当前存取该FAT卷的执行绪的数量,所以在执行FATEnter时首先

要将cFATThReads加一。

2) 呼叫BufEnter

全域变量cBufThReads记录了当前使用BufferCache的执行绪数量,当要使用BufferCache的时候,将cBufThReads减一。

限制使用BufferCache的执行绪数的原因在于,如果同时使用的执行绪过多,很可能所有的Buffer都被占用,那么后来的执行绪就无法透过FindBuffer获取自己需要的数据,执行绪的这次文件系统操作(用户的系统呼叫)就会失败。限制执行绪数目后,如果执行绪过多,即当cBufThReads < 0的时候阻塞,WaitForSingleObject(hevBufThreads, INFINITE),系统就让它们等待h e v B u f T h Reads这个事件,执行绪的系统呼叫就不会失败了。

3) 一系列的参数合法性判断和一些局部变量的初始化和赋值。

 

if(pvol->v_flags & (VOLF_UNMOUNTED | VOLF_FROZEN | VOLF_WRITELOCKED))

//判断当前卷是否可以被存取

{

dwError = ERROR_ACCESS_DENIED;

goto exit;

}

 

if (dwAccess & ~(GENERIC_READ | GENERIC_WRITE))

// dwAccess的合法性检查

goto invalidParm;

 

if ((BYTE)dwFlagsAndAttributes == FILE_ATTRIBUTE_NORMAL)

dwFlagsAndAttributes &= ~0xFF;// dwFlagsAndATTRIBUTE s清零

else {

dwFlagsAndAttributes &= ~FILE_ATTRIBUTE_NORMAL;

if ((BYTE)dwFlagsAndAttributes & ~ATTR_CHANGEABLE)

goto invalidParm;}

dwFlagsAndAttributes |= FILE_ATTRIBUTE_ARCHIVE;

//在本次呼叫中, dwFlagsAndATTRIBUTE s最后的值为FILE _ ATTRIBUTE _ A R C H I V E

 

if (dwShareMode & ~(FILE_SHARE_READ | FILE_SHARE_WRITE))

// dwShareMode的合法性检查

goto invalidParm;

 

flName = NAME_FILE | NAME_VOLUME;

switch (dwCreate) {

case CREATE_NEW:

flName |= NAME_NEW;

case CREATE_ALWAYS:

flName |= NAME_CREATE | NAME_TRUNCATE;

break ;

......

//以下省略,上面透过dwCreate得到flName

 

mode = (BYTE)(ShareToMode(dwShareMode) | AccessToMode(dwAccess));

flName |= (BYTE)(dwFlagsAndAttributes) | (mode << NAME_MODE_SHIFT);

//得到modeflName

 

4) 呼叫OpenName

. 呼叫OpenPath

PDSTREAM                    // 返回的数据流指针

OpenPath (

PVOLUME   pvol,         // 文件系统卷指针

PCWSTR        pwsPath,          // 路径名(若为/ A / B / C,返回B的数据流指针)

PCWSTR        * ppwsTail ,

int           * plen,

int           f l Name ,        // 打开属性

DWORD     clusFail

)

{

int           len,   flTmp;

PCWSTR        pws,   pwsOrig;

DWORD     dwError = ERROR_SUCCESS;

PDSTREAM pstm=NULL, pstmPrev = NULL ;

 

_try {

// 如果给出的路径名空,直接退出

if (*pwsPath == 0)

goto exit;

//获得_VOLUME中初始化时就打开的根目录对应的数据流并把数据流的引用次数+ +

pstm = OpenRoot(pvol);

if (!pstm) {

SetLastError (ERROR_ACCESS_DENIED ) ;

goto exit;

}

// 去掉路径名中开头的那个S l a s h

if (*pwsPath == TEXTW('//') || *pwsPath == TEXTW('/'))

+ +pwsPath ;

pwsOrig = pwsPath;

// 开始循环,对于/ A / B / C,直到打开B的数据流为止

do {

pws = pwsPath;

// 找到下一个目录的名称(到Slash为止)

while (*pwsPath && *pwsPath != TEXTW('//') && *pwsPath != TEXTW('/'))

++pwsPath;

//从两个指针中可以看出这个目录的长度

if (len = pwsPath - pws) {

if (len > MAX_LFN_LEN) {

dwError = ERROR_FILENAME_EXCED_RANGE;

break;

}

//如果这已经是最后一个元素了,那么现在的pstm就指向了需要的数据流,退//出即可

if (*pwsPath == 0)

break;

//否则如果NAME_DIR属性被设置,而路径名称类似于/ddd/jjj/rrr/,那么,打开//j j

 j的资料流就可以了,如果NAME_DIR没有被设置,就需要打开rrr对应的//数据流才满足要求

if (*(pwsPath+1) == 0 && (flName & NAME_DIR))

break ;

//设置呼叫OpenName的属性为NAME_DIR

flTmp = NAME_DIR;

//呼叫OpenName来获得这一层目录对应的数据流

pstm = OpenName(pstmPrev = pstm, pws, len, &flTmp);

 

//上一层目录对应的数据流不再需要了,关闭

CloseStream(pstmPrev);

//如果中间有一层目录打开失败,退出

if (!pstm)

break ;

}

//只要还有下一层目录,就需要循环下去

} while (*pwsPath++);

}

_except (EXCEPTION_EXECUTE_HANDLER) {

dwError = ERROR_INVALID_PARAMETER;

}

//如果打开失败,回传错误

if (len == 0 && pstmPrev != NULL)

dwError = ERROR_PATH_NOT_FOUND;

//进行错误处理(关闭前面的数据流等等)

if (dwError) {

SetLastError(dwError);

if (pstm) {

CloseStream( pstm ) ;

pstm = NULL;

}

}

*ppwsTail = pws;

*plen = len;

e x i t :

//回传数据流的指针

return pstm;

}

.具体的OpenName原始码如下:

PDSTREAM

OpenName(

DSTREAM      pstmDir,     //cwName非零的时候,这是档案所属的目录文件

//案的数据流,否则就是所属档案文件的指标

PCWSTR       pwsName,     //cwName非零的时候,这是纯粹的文件名,

//并不包括所在路径,否则就是完整的路径

int          cwName,      //零,或者是文件名的长度

int *     pflName      //打开的属性(不存在的时候是否该建立)

)

{

SHANDLE sh;

DIRINFO di;

WIN32_FIND_DATAW fd;

PDSTREAM pstmTmp = NULL;

PDSTREAM pstmRet = NULL;

int flName = *pflName;

DWORD dwError = ERROR_SUCCESS;

//如果cwName是零,就说明呼叫的时候,pstmDir没有给出所在档案夹的

//数据流,而是给出了所在档案文件的指标。相对的, pwsName是完整

//的路径名,因此需要透过OpenPath来打开档案所属的目录档案的数据流

if (cwName == 0) {

//呼叫OpenPath(假设传递的路径是/A/B/C,就会回传B的数据流指针)

pstmTmp = OpenPath((PVOLUME)pstmDir, pwsName, &pwsName,

&cwName,*pflName,UNKNOWN_CLUSTER ) ;

//如果呼叫OpenPath失败,错误退出

if (pstmTmp == NULL)

goto exit;

//现在pstmDir就是C的上层目录档案B的数据流指针了,接下来的任务就是在目录

//档案B中寻找C了(如果没找到,就可能产生新的目录项)

pstmDir = pstmTmp;

}

_try {

//接下来开始为呼叫FindNext做准备,设置_SHANDLE结构的各个属性

sh.sh_pstm = pstmDir;             // 指向目录档案对应的数据流

sh.sh_pos = 0;                    //搜寻的起始位置是开头(0

sh.sh_flags = SHF_BYNAME;         //搜寻的方法是透过文件名称

//如果flName指出档案不存在就建立,就给sh_flags加上SHF_CREATE

//这样,FindNext就知道DIRINFO中的信息是一定要填入的

if (flName & NAME_CREATE)

sh.sh_flags |= SHF_CREATE;

//把文件名拷贝到_SHANDLE中去

sh.sh_cwPattern = (WORD)cwName;

memcpy(sh.sh_awcPattern, pwsName, cwName*sizeof(WCHAR));

sh.sh_awcPattern[cwName] = 0;

}

_except (EXCEPTION_EXECUTE_HANDLER) {

dwError = ERROR_INVALID_PARAMETER;

goto exit;

}

//现在就可以呼叫FindNext函数了

dwError = FindNext(&sh, &di, &fd);

//如果FindNext失败,表示目录中不存在此档案

if (dwError) {

//如果需要建立,需要呼叫CreateName在目录档案中建立新的目录项

SetLastError(ERROR_SUCCESS) ;

if (flName & NAME_CREATE) {

if (dwError != ERROR_INVALID_NAME) {

//呼叫CreateName来建立新档案,并把FindNext设置的DIRINFO给它

dwError = CreateName(pstmDir, sh.sh_awcPattern, &di, NULL, flName);

flName &= ~(NAME_NEW | NAME_MODE_WRITE);

if (!dwError) {

*pflName |= NAME_CREATED;

}

}

}

else {

dwError = (flName & NAME_DIR? ERROR_PATH_NOT_FOUND : ERROR_FILE_NOT_FOUND);

}

}

//如果FindNext找到了所需的档案,接下来开始建立数据流

else {

if ((flName & NAME_CREATE) || (flName & NAME_TRUNCATE))

SetLastError ( ERROR_ALREADY_EXISTS ) ;

} 

//把档案的属性和打开的参数比较。比如说,如果目录项说明这是一个目录

//但要打开的却不是目录,而是数据文件文件,那就出错退出。同样,如果打开

//的是只读文件,却要求以可写模式打开,同样也就错误退出,等等

if (!dwError) {

if (flName & NAME_NEW) {

dwError = (di.di_pde->de_attr & ATTR_DIRECTORY) || (flName & NAME_DIR)?

ERROR_ALREADY_EXISTS : ERROR_FILE_EXISTS;

}

else if (!(di.di_pde->de_attr & ATTR_DIRECTORY) && (flName & NAME_DIR)) {

dwError = ERROR_PATH_NOT_FOUND;

}

else if ((di.di_pde->de_attr & ATTR_DIRECTORY) && !(flName & NAME_DIR)) {

dwError = ERROR_ACCESS_DENIED;

           }

else if ((di.di_pde->de_attr&ATTR_READ_ONLY)&&(flName&NAME_MODE_WRITE)){

dwError = ERROR_ACCESS_DENIED;

}

//一切满足要求

else {

//最后透过OpenStream来打开(建立)数据流,现在终于知道OpenStream

//需要的参数是从哪里来的了

pstmRet = OpenStream(pstmDir->s_pvol,

di.di_clusEntry ,

& di.di_sid,

pstmDir, &di, OPENSTREAM_CREATE);

if (!pstmRet)

dwError = ERROR_OUTOFMEMORY;

           }

ReleaseStreamBuffer(pstmDir, FALSE);

}

exit:

if (dwError)

SetLastError ( dwError ) ;

if (pstmTmp)

//最后关闭目录档案的数据流

CloseStream ( pstmTmp ) ;

//回传建立的(目标档案的)数据流

return pstmRet;

}

5) 根据回传的PDSTREAM pstm对流的共享性检查CheckStreamSharing

BOOL

CheckStreamSharing (

PDSTREAM  pstm, //被检查的资料流

int          mode   //需要对档案进行的操作模式

//读:FH_MODE_READ

//写:FH_MODE_WRITE

)

{

BYTE  bRequired = 0;

PFHANDLE pfh, pfhEnd;

ASSERT ( pstm ) ;

ASSERT ( OWNCRITICALSECTION ( &pstm->s_cs ) ) ;

//如果需要进行读的动作,那就需要所有的_FHANDLE都允许共享可读

if (mode & FH_MODE_READ)

bRequired |= FH_MODE_SHARE_READ;

//如果需要进行写的动作,那就需要所有的_FHANDLE都允许共享可写

if (mode & FH_MODE_WRITE)

bRequired |= FH_MODE_SHARE_WRITE;

pfh = pstm->s_dlOpenHandles.pfhNext;

pfhEnd = (PFHANDLE)&pstm->s_dlOpenHandles;

//接下来对数据流的_FHANDLE中的每一个进行检查

while (pfh != pfhEnd) {

//只要有一个不能满足要求就出错退出

if ((pfh->fh_mode & bRequired) != bRequired)

return FALSE;

pfh = pfh->fh_dlOpenHandles.pfhNext;

}

return TRUE;

}

6) 使用FSDMGR_CreateFileHandle建立最后回传的档案识别码hFile

7) 根据参数对pfh的一些运算子,并将其加到p s t m里去。

8) 呼叫FILESYSTEMNOTIFICATION通知系统,档案被建立。

9) 最后呼叫FATExitBuffExit( )cBufThreads加一,释放资源,并设置事件hevBufThreads,将cFATThreads加一。

 

原创粉丝点击