ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

fat.c
Go to the documentation of this file.
00001 /*
00002  *  FreeLoader
00003  *  Copyright (C) 1998-2003  Brian Palmer  <brianp@sginet.com>
00004  *  Copyright (C) 2009       Hervé Poussineau
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00019  */
00020 
00021 #include <freeldr.h>
00022 
00023 #define NDEBUG
00024 #include <debug.h>
00025 
00026 DBG_DEFAULT_CHANNEL(FILESYSTEM);
00027 
00028 ULONG   FatDetermineFatType(PFAT_BOOTSECTOR FatBootSector, ULONGLONG PartitionSectorCount);
00029 PVOID   FatBufferDirectory(PFAT_VOLUME_INFO Volume, ULONG DirectoryStartCluster, ULONG* EntryCountPointer, BOOLEAN RootDirectory);
00030 BOOLEAN FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG EntryCount, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer);
00031 LONG FatLookupFile(PFAT_VOLUME_INFO Volume, PCSTR FileName, ULONG DeviceId, PFAT_FILE_INFO FatFileInfoPointer);
00032 void    FatParseShortFileName(PCHAR Buffer, PDIRENTRY DirEntry);
00033 BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, ULONG Cluster, ULONG* ClusterPointer);
00034 ULONG   FatCountClustersInChain(PFAT_VOLUME_INFO Volume, ULONG StartCluster);
00035 ULONG*  FatGetClusterChainArray(PFAT_VOLUME_INFO Volume, ULONG StartCluster);
00036 BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, ULONG StartClusterNumber, ULONG NumberOfClusters, PVOID Buffer);
00037 BOOLEAN FatReadPartialCluster(PFAT_VOLUME_INFO Volume, ULONG ClusterNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer);
00038 BOOLEAN FatReadVolumeSectors(PFAT_VOLUME_INFO Volume, ULONG SectorNumber, ULONG SectorCount, PVOID Buffer);
00039 
00040 typedef struct _FAT_VOLUME_INFO
00041 {
00042     ULONG BytesPerSector; /* Number of bytes per sector */
00043     ULONG SectorsPerCluster; /* Number of sectors per cluster */
00044     ULONG FatSectorStart; /* Starting sector of 1st FAT table */
00045     ULONG ActiveFatSectorStart; /* Starting sector of active FAT table */
00046     ULONG NumberOfFats; /* Number of FAT tables */
00047     ULONG SectorsPerFat; /* Sectors per FAT table */
00048     ULONG RootDirSectorStart; /* Starting sector of the root directory (non-fat32) */
00049     ULONG RootDirSectors; /* Number of sectors of the root directory (non-fat32) */
00050     ULONG RootDirStartCluster; /* Starting cluster number of the root directory (fat32 only) */
00051     ULONG DataSectorStart; /* Starting sector of the data area */
00052     ULONG FatType; /* FAT12, FAT16, FAT32, FATX16 or FATX32 */
00053     ULONG DeviceId;
00054 } FAT_VOLUME_INFO;
00055 
00056 PFAT_VOLUME_INFO FatVolumes[MAX_FDS];
00057 
00058 VOID FatSwapFatBootSector(PFAT_BOOTSECTOR Obj)
00059 {
00060     SW(Obj, BytesPerSector);
00061     SW(Obj, ReservedSectors);
00062     SW(Obj, RootDirEntries);
00063     SW(Obj, TotalSectors);
00064     SW(Obj, SectorsPerFat);
00065     SW(Obj, SectorsPerTrack);
00066     SW(Obj, NumberOfHeads);
00067     SD(Obj, HiddenSectors);
00068     SD(Obj, TotalSectorsBig);
00069     SD(Obj, VolumeSerialNumber);
00070     SW(Obj, BootSectorMagic);
00071 }
00072 
00073 VOID FatSwapFat32BootSector(PFAT32_BOOTSECTOR Obj)
00074 {
00075     SW(Obj, BytesPerSector);
00076     SW(Obj, ReservedSectors);
00077     SW(Obj, RootDirEntries);
00078     SW(Obj, TotalSectors);
00079     SW(Obj, SectorsPerFat);
00080     SW(Obj, NumberOfHeads);
00081     SD(Obj, HiddenSectors);
00082     SD(Obj, TotalSectorsBig);
00083     SD(Obj, SectorsPerFatBig);
00084     SW(Obj, ExtendedFlags);
00085     SW(Obj, FileSystemVersion);
00086     SD(Obj, RootDirStartCluster);
00087     SW(Obj, FsInfo);
00088     SW(Obj, BackupBootSector);
00089     SD(Obj, VolumeSerialNumber);
00090     SW(Obj, BootSectorMagic);
00091 }
00092 
00093 VOID FatSwapFatXBootSector(PFATX_BOOTSECTOR Obj)
00094 {
00095     SD(Obj, VolumeSerialNumber);
00096     SD(Obj, SectorsPerCluster);
00097     SW(Obj, NumberOfFats);
00098 }
00099 
00100 VOID FatSwapDirEntry(PDIRENTRY Obj)
00101 {
00102     SW(Obj, CreateTime);
00103     SW(Obj, CreateDate);
00104     SW(Obj, LastAccessDate);
00105     SW(Obj, ClusterHigh);
00106     SW(Obj, Time);
00107     SW(Obj, Date);
00108     SW(Obj, ClusterLow);
00109     SD(Obj, Size);
00110 }
00111 
00112 VOID FatSwapLFNDirEntry(PLFN_DIRENTRY Obj)
00113 {
00114     int i;
00115     SW(Obj, StartCluster);
00116     for(i = 0; i < 5; i++)
00117         Obj->Name0_4[i] = SWAPW(Obj->Name0_4[i]);
00118     for(i = 0; i < 6; i++)
00119         Obj->Name5_10[i] = SWAPW(Obj->Name5_10[i]);
00120     for(i = 0; i < 2; i++)
00121         Obj->Name11_12[i] = SWAPW(Obj->Name11_12[i]);
00122 }
00123 
00124 VOID FatSwapFatXDirEntry(PFATX_DIRENTRY Obj)
00125 {
00126     SD(Obj, StartCluster);
00127     SD(Obj, Size);
00128     SW(Obj, Time);
00129     SW(Obj, Date);
00130     SW(Obj, CreateTime);
00131     SW(Obj, CreateDate);
00132     SW(Obj, LastAccessTime);
00133     SW(Obj, LastAccessDate);
00134 }
00135 
00136 BOOLEAN FatOpenVolume(PFAT_VOLUME_INFO Volume, PFAT_BOOTSECTOR BootSector, ULONGLONG PartitionSectorCount)
00137 {
00138     char ErrMsg[80];
00139     ULONG FatSize;
00140     PFAT_BOOTSECTOR FatVolumeBootSector;
00141     PFAT32_BOOTSECTOR Fat32VolumeBootSector;
00142     PFATX_BOOTSECTOR FatXVolumeBootSector;
00143 
00144     TRACE("FatOpenVolume() DeviceId = %d\n", Volume->DeviceId);
00145 
00146     //
00147     // Allocate the memory to hold the boot sector
00148     //
00149     FatVolumeBootSector = (PFAT_BOOTSECTOR)BootSector;
00150     Fat32VolumeBootSector = (PFAT32_BOOTSECTOR)BootSector;
00151     FatXVolumeBootSector = (PFATX_BOOTSECTOR)BootSector;
00152 
00153     // Get the FAT type
00154     Volume->FatType = FatDetermineFatType(FatVolumeBootSector, PartitionSectorCount);
00155 
00156     // Dump boot sector (and swap it for big endian systems)
00157     TRACE("Dumping boot sector:\n");
00158     if (ISFATX(Volume->FatType))
00159     {
00160         FatSwapFatXBootSector(FatXVolumeBootSector);
00161         TRACE("sizeof(FATX_BOOTSECTOR) = 0x%x.\n", sizeof(FATX_BOOTSECTOR));
00162 
00163         TRACE("FileSystemType: %c%c%c%c.\n", FatXVolumeBootSector->FileSystemType[0], FatXVolumeBootSector->FileSystemType[1], FatXVolumeBootSector->FileSystemType[2], FatXVolumeBootSector->FileSystemType[3]);
00164         TRACE("VolumeSerialNumber: 0x%x\n", FatXVolumeBootSector->VolumeSerialNumber);
00165         TRACE("SectorsPerCluster: %d\n", FatXVolumeBootSector->SectorsPerCluster);
00166         TRACE("NumberOfFats: %d\n", FatXVolumeBootSector->NumberOfFats);
00167         TRACE("Unknown: 0x%x\n", FatXVolumeBootSector->Unknown);
00168 
00169         TRACE("FatType %s\n", Volume->FatType == FATX16 ? "FATX16" : "FATX32");
00170 
00171     }
00172     else if (Volume->FatType == FAT32)
00173     {
00174         FatSwapFat32BootSector(Fat32VolumeBootSector);
00175         TRACE("sizeof(FAT32_BOOTSECTOR) = 0x%x.\n", sizeof(FAT32_BOOTSECTOR));
00176 
00177         TRACE("JumpBoot: 0x%x 0x%x 0x%x\n", Fat32VolumeBootSector->JumpBoot[0], Fat32VolumeBootSector->JumpBoot[1], Fat32VolumeBootSector->JumpBoot[2]);
00178         TRACE("OemName: %c%c%c%c%c%c%c%c\n", Fat32VolumeBootSector->OemName[0], Fat32VolumeBootSector->OemName[1], Fat32VolumeBootSector->OemName[2], Fat32VolumeBootSector->OemName[3], Fat32VolumeBootSector->OemName[4], Fat32VolumeBootSector->OemName[5], Fat32VolumeBootSector->OemName[6], Fat32VolumeBootSector->OemName[7]);
00179         TRACE("BytesPerSector: %d\n", Fat32VolumeBootSector->BytesPerSector);
00180         TRACE("SectorsPerCluster: %d\n", Fat32VolumeBootSector->SectorsPerCluster);
00181         TRACE("ReservedSectors: %d\n", Fat32VolumeBootSector->ReservedSectors);
00182         TRACE("NumberOfFats: %d\n", Fat32VolumeBootSector->NumberOfFats);
00183         TRACE("RootDirEntries: %d\n", Fat32VolumeBootSector->RootDirEntries);
00184         TRACE("TotalSectors: %d\n", Fat32VolumeBootSector->TotalSectors);
00185         TRACE("MediaDescriptor: 0x%x\n", Fat32VolumeBootSector->MediaDescriptor);
00186         TRACE("SectorsPerFat: %d\n", Fat32VolumeBootSector->SectorsPerFat);
00187         TRACE("SectorsPerTrack: %d\n", Fat32VolumeBootSector->SectorsPerTrack);
00188         TRACE("NumberOfHeads: %d\n", Fat32VolumeBootSector->NumberOfHeads);
00189         TRACE("HiddenSectors: %d\n", Fat32VolumeBootSector->HiddenSectors);
00190         TRACE("TotalSectorsBig: %d\n", Fat32VolumeBootSector->TotalSectorsBig);
00191         TRACE("SectorsPerFatBig: %d\n", Fat32VolumeBootSector->SectorsPerFatBig);
00192         TRACE("ExtendedFlags: 0x%x\n", Fat32VolumeBootSector->ExtendedFlags);
00193         TRACE("FileSystemVersion: 0x%x\n", Fat32VolumeBootSector->FileSystemVersion);
00194         TRACE("RootDirStartCluster: %d\n", Fat32VolumeBootSector->RootDirStartCluster);
00195         TRACE("FsInfo: %d\n", Fat32VolumeBootSector->FsInfo);
00196         TRACE("BackupBootSector: %d\n", Fat32VolumeBootSector->BackupBootSector);
00197         TRACE("Reserved: 0x%x\n", Fat32VolumeBootSector->Reserved);
00198         TRACE("DriveNumber: 0x%x\n", Fat32VolumeBootSector->DriveNumber);
00199         TRACE("Reserved1: 0x%x\n", Fat32VolumeBootSector->Reserved1);
00200         TRACE("BootSignature: 0x%x\n", Fat32VolumeBootSector->BootSignature);
00201         TRACE("VolumeSerialNumber: 0x%x\n", Fat32VolumeBootSector->VolumeSerialNumber);
00202         TRACE("VolumeLabel: %c%c%c%c%c%c%c%c%c%c%c\n", Fat32VolumeBootSector->VolumeLabel[0], Fat32VolumeBootSector->VolumeLabel[1], Fat32VolumeBootSector->VolumeLabel[2], Fat32VolumeBootSector->VolumeLabel[3], Fat32VolumeBootSector->VolumeLabel[4], Fat32VolumeBootSector->VolumeLabel[5], Fat32VolumeBootSector->VolumeLabel[6], Fat32VolumeBootSector->VolumeLabel[7], Fat32VolumeBootSector->VolumeLabel[8], Fat32VolumeBootSector->VolumeLabel[9], Fat32VolumeBootSector->VolumeLabel[10]);
00203         TRACE("FileSystemType: %c%c%c%c%c%c%c%c\n", Fat32VolumeBootSector->FileSystemType[0], Fat32VolumeBootSector->FileSystemType[1], Fat32VolumeBootSector->FileSystemType[2], Fat32VolumeBootSector->FileSystemType[3], Fat32VolumeBootSector->FileSystemType[4], Fat32VolumeBootSector->FileSystemType[5], Fat32VolumeBootSector->FileSystemType[6], Fat32VolumeBootSector->FileSystemType[7]);
00204         TRACE("BootSectorMagic: 0x%x\n", Fat32VolumeBootSector->BootSectorMagic);
00205     }
00206     else
00207     {
00208         FatSwapFatBootSector(FatVolumeBootSector);
00209         TRACE("sizeof(FAT_BOOTSECTOR) = 0x%x.\n", sizeof(FAT_BOOTSECTOR));
00210 
00211         TRACE("JumpBoot: 0x%x 0x%x 0x%x\n", FatVolumeBootSector->JumpBoot[0], FatVolumeBootSector->JumpBoot[1], FatVolumeBootSector->JumpBoot[2]);
00212         TRACE("OemName: %c%c%c%c%c%c%c%c\n", FatVolumeBootSector->OemName[0], FatVolumeBootSector->OemName[1], FatVolumeBootSector->OemName[2], FatVolumeBootSector->OemName[3], FatVolumeBootSector->OemName[4], FatVolumeBootSector->OemName[5], FatVolumeBootSector->OemName[6], FatVolumeBootSector->OemName[7]);
00213         TRACE("BytesPerSector: %d\n", FatVolumeBootSector->BytesPerSector);
00214         TRACE("SectorsPerCluster: %d\n", FatVolumeBootSector->SectorsPerCluster);
00215         TRACE("ReservedSectors: %d\n", FatVolumeBootSector->ReservedSectors);
00216         TRACE("NumberOfFats: %d\n", FatVolumeBootSector->NumberOfFats);
00217         TRACE("RootDirEntries: %d\n", FatVolumeBootSector->RootDirEntries);
00218         TRACE("TotalSectors: %d\n", FatVolumeBootSector->TotalSectors);
00219         TRACE("MediaDescriptor: 0x%x\n", FatVolumeBootSector->MediaDescriptor);
00220         TRACE("SectorsPerFat: %d\n", FatVolumeBootSector->SectorsPerFat);
00221         TRACE("SectorsPerTrack: %d\n", FatVolumeBootSector->SectorsPerTrack);
00222         TRACE("NumberOfHeads: %d\n", FatVolumeBootSector->NumberOfHeads);
00223         TRACE("HiddenSectors: %d\n", FatVolumeBootSector->HiddenSectors);
00224         TRACE("TotalSectorsBig: %d\n", FatVolumeBootSector->TotalSectorsBig);
00225         TRACE("DriveNumber: 0x%x\n", FatVolumeBootSector->DriveNumber);
00226         TRACE("Reserved1: 0x%x\n", FatVolumeBootSector->Reserved1);
00227         TRACE("BootSignature: 0x%x\n", FatVolumeBootSector->BootSignature);
00228         TRACE("VolumeSerialNumber: 0x%x\n", FatVolumeBootSector->VolumeSerialNumber);
00229         TRACE("VolumeLabel: %c%c%c%c%c%c%c%c%c%c%c\n", FatVolumeBootSector->VolumeLabel[0], FatVolumeBootSector->VolumeLabel[1], FatVolumeBootSector->VolumeLabel[2], FatVolumeBootSector->VolumeLabel[3], FatVolumeBootSector->VolumeLabel[4], FatVolumeBootSector->VolumeLabel[5], FatVolumeBootSector->VolumeLabel[6], FatVolumeBootSector->VolumeLabel[7], FatVolumeBootSector->VolumeLabel[8], FatVolumeBootSector->VolumeLabel[9], FatVolumeBootSector->VolumeLabel[10]);
00230         TRACE("FileSystemType: %c%c%c%c%c%c%c%c\n", FatVolumeBootSector->FileSystemType[0], FatVolumeBootSector->FileSystemType[1], FatVolumeBootSector->FileSystemType[2], FatVolumeBootSector->FileSystemType[3], FatVolumeBootSector->FileSystemType[4], FatVolumeBootSector->FileSystemType[5], FatVolumeBootSector->FileSystemType[6], FatVolumeBootSector->FileSystemType[7]);
00231         TRACE("BootSectorMagic: 0x%x\n", FatVolumeBootSector->BootSectorMagic);
00232     }
00233 
00234     //
00235     // Check the boot sector magic
00236     //
00237     if (! ISFATX(Volume->FatType) && FatVolumeBootSector->BootSectorMagic != 0xaa55)
00238     {
00239         sprintf(ErrMsg, "Invalid boot sector magic (expected 0xaa55 found 0x%x)",
00240                 FatVolumeBootSector->BootSectorMagic);
00241         FileSystemError(ErrMsg);
00242         return FALSE;
00243     }
00244 
00245     //
00246     // Check the FAT cluster size
00247     // We do not support clusters bigger than 64k
00248     //
00249     if ((ISFATX(Volume->FatType) && 64 * 1024 < FatXVolumeBootSector->SectorsPerCluster * 512) ||
00250        (! ISFATX(Volume->FatType) && 64 * 1024 < FatVolumeBootSector->SectorsPerCluster * FatVolumeBootSector->BytesPerSector))
00251     {
00252         FileSystemError("This file system has cluster sizes bigger than 64k.\nFreeLoader does not support this.");
00253         return FALSE;
00254     }
00255 
00256     //
00257     // Get the sectors per FAT,
00258     // root directory starting sector,
00259     // and data sector start
00260     //
00261     if (ISFATX(Volume->FatType))
00262     {
00263         Volume->BytesPerSector = 512;
00264         Volume->SectorsPerCluster = SWAPD(FatXVolumeBootSector->SectorsPerCluster);
00265         Volume->FatSectorStart = (4096 / Volume->BytesPerSector);
00266         Volume->ActiveFatSectorStart = Volume->FatSectorStart;
00267         Volume->NumberOfFats = 1;
00268         FatSize = (ULONG)(PartitionSectorCount / Volume->SectorsPerCluster *
00269                   (Volume->FatType == FATX16 ? 2 : 4));
00270         Volume->SectorsPerFat = (((FatSize + 4095) / 4096) * 4096) / Volume->BytesPerSector;
00271 
00272         Volume->RootDirSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
00273         Volume->RootDirSectors = FatXVolumeBootSector->SectorsPerCluster;
00274 
00275         Volume->DataSectorStart = Volume->RootDirSectorStart + Volume->RootDirSectors;
00276     }
00277     else if (Volume->FatType != FAT32)
00278     {
00279         Volume->BytesPerSector = FatVolumeBootSector->BytesPerSector;
00280         Volume->SectorsPerCluster = FatVolumeBootSector->SectorsPerCluster;
00281         Volume->FatSectorStart = FatVolumeBootSector->ReservedSectors;
00282         Volume->ActiveFatSectorStart = Volume->FatSectorStart;
00283         Volume->NumberOfFats = FatVolumeBootSector->NumberOfFats;
00284         Volume->SectorsPerFat = FatVolumeBootSector->SectorsPerFat;
00285 
00286         Volume->RootDirSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
00287         Volume->RootDirSectors = ((FatVolumeBootSector->RootDirEntries * 32) + (Volume->BytesPerSector - 1)) / Volume->BytesPerSector;
00288 
00289         Volume->DataSectorStart = Volume->RootDirSectorStart + Volume->RootDirSectors;
00290     }
00291     else
00292     {
00293         Volume->BytesPerSector = Fat32VolumeBootSector->BytesPerSector;
00294         Volume->SectorsPerCluster = Fat32VolumeBootSector->SectorsPerCluster;
00295         Volume->FatSectorStart = Fat32VolumeBootSector->ReservedSectors;
00296         Volume->ActiveFatSectorStart = Volume->FatSectorStart +
00297                                        ((Fat32VolumeBootSector->ExtendedFlags & 0x80) ? ((Fat32VolumeBootSector->ExtendedFlags & 0x0f) * Fat32VolumeBootSector->SectorsPerFatBig) : 0);
00298         Volume->NumberOfFats = Fat32VolumeBootSector->NumberOfFats;
00299         Volume->SectorsPerFat = Fat32VolumeBootSector->SectorsPerFatBig;
00300 
00301         Volume->RootDirStartCluster = Fat32VolumeBootSector->RootDirStartCluster;
00302         Volume->DataSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
00303 
00304         //
00305         // Check version
00306         // we only work with version 0
00307         //
00308         if (Fat32VolumeBootSector->FileSystemVersion != 0)
00309         {
00310             FileSystemError("FreeLoader is too old to work with this FAT32 filesystem.\nPlease update FreeLoader.");
00311             return FALSE;
00312         }
00313     }
00314 
00315     return TRUE;
00316 }
00317 
00318 ULONG FatDetermineFatType(PFAT_BOOTSECTOR FatBootSector, ULONGLONG PartitionSectorCount)
00319 {
00320     ULONG           RootDirSectors;
00321     ULONG           DataSectorCount;
00322     ULONG           SectorsPerFat;
00323     ULONG           TotalSectors;
00324     ULONG           CountOfClusters;
00325     PFAT32_BOOTSECTOR   Fat32BootSector = (PFAT32_BOOTSECTOR)FatBootSector;
00326     PFATX_BOOTSECTOR    FatXBootSector = (PFATX_BOOTSECTOR)FatBootSector;
00327 
00328     if (0 == strncmp(FatXBootSector->FileSystemType, "FATX", 4))
00329     {
00330         CountOfClusters = (ULONG)(PartitionSectorCount / FatXBootSector->SectorsPerCluster);
00331         if (CountOfClusters < 65525)
00332         {
00333             /* Volume is FATX16 */
00334             return FATX16;
00335         }
00336         else
00337         {
00338             /* Volume is FAT32 */
00339             return FATX32;
00340         }
00341     }
00342     else
00343     {
00344         RootDirSectors = ((SWAPW(FatBootSector->RootDirEntries) * 32) + (SWAPW(FatBootSector->BytesPerSector) - 1)) / SWAPW(FatBootSector->BytesPerSector);
00345         SectorsPerFat = SWAPW(FatBootSector->SectorsPerFat) ? SWAPW(FatBootSector->SectorsPerFat) : SWAPD(Fat32BootSector->SectorsPerFatBig);
00346         TotalSectors = SWAPW(FatBootSector->TotalSectors) ? SWAPW(FatBootSector->TotalSectors) : SWAPD(FatBootSector->TotalSectorsBig);
00347         DataSectorCount = TotalSectors - (SWAPW(FatBootSector->ReservedSectors) + (FatBootSector->NumberOfFats * SectorsPerFat) + RootDirSectors);
00348 
00349 //mjl
00350         if (FatBootSector->SectorsPerCluster == 0)
00351             CountOfClusters = 0;
00352         else
00353             CountOfClusters = DataSectorCount / FatBootSector->SectorsPerCluster;
00354 
00355         if (CountOfClusters < 4085)
00356         {
00357             /* Volume is FAT12 */
00358             return FAT12;
00359         }
00360         else if (CountOfClusters < 65525)
00361         {
00362             /* Volume is FAT16 */
00363             return FAT16;
00364         }
00365         else
00366         {
00367             /* Volume is FAT32 */
00368             return FAT32;
00369         }
00370     }
00371 }
00372 
00373 typedef struct _DIRECTORY_BUFFER
00374 {
00375     LIST_ENTRY Link;
00376     PVOID Volume;
00377     ULONG DirectoryStartCluster;
00378     ULONG DirectorySize;
00379     UCHAR Data[];
00380 } DIRECTORY_BUFFER, *PDIRECTORY_BUFFER;
00381 
00382 LIST_ENTRY DirectoryBufferListHead = {&DirectoryBufferListHead, &DirectoryBufferListHead};
00383 
00384 PVOID FatBufferDirectory(PFAT_VOLUME_INFO Volume, ULONG DirectoryStartCluster, ULONG *DirectorySize, BOOLEAN RootDirectory)
00385 {
00386     PDIRECTORY_BUFFER DirectoryBuffer;
00387     PLIST_ENTRY Entry;
00388 
00389     TRACE("FatBufferDirectory() DirectoryStartCluster = %d RootDirectory = %s\n", DirectoryStartCluster, (RootDirectory ? "TRUE" : "FALSE"));
00390 
00391     /*
00392      * For FAT32, the root directory is nothing special. We can treat it the same
00393      * as a subdirectory.
00394      */
00395     if (RootDirectory && Volume->FatType == FAT32)
00396     {
00397         DirectoryStartCluster = Volume->RootDirStartCluster;
00398         RootDirectory = FALSE;
00399     }
00400 
00401     /* Search the list for a match */
00402     for (Entry = DirectoryBufferListHead.Flink;
00403          Entry != &DirectoryBufferListHead;
00404          Entry = Entry->Flink)
00405     {
00406         DirectoryBuffer = CONTAINING_RECORD(Entry, DIRECTORY_BUFFER, Link);
00407 
00408         /* Check if it matches */
00409         if ((DirectoryBuffer->Volume == Volume) &&
00410             (DirectoryBuffer->DirectoryStartCluster == DirectoryStartCluster))
00411         {
00412             TRACE("Found cached buffer\n");
00413             *DirectorySize = DirectoryBuffer->DirectorySize;
00414             return DirectoryBuffer->Data;
00415         }
00416     }
00417 
00418     //
00419     // Calculate the size of the directory
00420     //
00421     if (RootDirectory)
00422     {
00423         *DirectorySize = Volume->RootDirSectors * Volume->BytesPerSector;
00424     }
00425     else
00426     {
00427         *DirectorySize = FatCountClustersInChain(Volume, DirectoryStartCluster) * Volume->SectorsPerCluster * Volume->BytesPerSector;
00428     }
00429 
00430     //
00431     // Attempt to allocate memory for directory buffer
00432     //
00433     TRACE("Trying to allocate (DirectorySize) %d bytes.\n", *DirectorySize);
00434     DirectoryBuffer = MmHeapAlloc(*DirectorySize + sizeof(DIRECTORY_BUFFER));
00435 
00436     if (DirectoryBuffer == NULL)
00437     {
00438         return NULL;
00439     }
00440 
00441     //
00442     // Now read directory contents into DirectoryBuffer
00443     //
00444     if (RootDirectory)
00445     {
00446         if (!FatReadVolumeSectors(Volume, Volume->RootDirSectorStart, Volume->RootDirSectors, DirectoryBuffer->Data))
00447         {
00448             MmHeapFree(DirectoryBuffer);
00449             return NULL;
00450         }
00451     }
00452     else
00453     {
00454         if (!FatReadClusterChain(Volume, DirectoryStartCluster, 0xFFFFFFFF, DirectoryBuffer->Data))
00455         {
00456             MmHeapFree(DirectoryBuffer);
00457             return NULL;
00458         }
00459     }
00460 
00461     /* Enqueue it in the list */
00462     DirectoryBuffer->Volume = Volume;
00463     DirectoryBuffer->DirectoryStartCluster = DirectoryStartCluster;
00464     DirectoryBuffer->DirectorySize = *DirectorySize;
00465     InsertTailList(&DirectoryBufferListHead, &DirectoryBuffer->Link);
00466 
00467     return DirectoryBuffer->Data;
00468 }
00469 
00470 BOOLEAN FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG DirectorySize, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer)
00471 {
00472     ULONG       EntryCount;
00473     ULONG       CurrentEntry;
00474     CHAR        LfnNameBuffer[265];
00475     CHAR        ShortNameBuffer[20];
00476     ULONG       StartCluster;
00477     DIRENTRY        OurDirEntry;
00478     LFN_DIRENTRY    OurLfnDirEntry;
00479     PDIRENTRY   DirEntry = &OurDirEntry;
00480     PLFN_DIRENTRY   LfnDirEntry = &OurLfnDirEntry;
00481 
00482     EntryCount = DirectorySize / sizeof(DIRENTRY);
00483 
00484     TRACE("FatSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer, EntryCount, FileName);
00485 
00486     memset(ShortNameBuffer, 0, 13 * sizeof(CHAR));
00487     memset(LfnNameBuffer, 0, 261 * sizeof(CHAR));
00488 
00489     for (CurrentEntry=0; CurrentEntry<EntryCount; CurrentEntry++, DirectoryBuffer = ((PDIRENTRY)DirectoryBuffer)+1)
00490     {
00491         OurLfnDirEntry = *((PLFN_DIRENTRY) DirectoryBuffer);
00492         FatSwapLFNDirEntry(LfnDirEntry);
00493         OurDirEntry = *((PDIRENTRY) DirectoryBuffer);
00494         FatSwapDirEntry(DirEntry);
00495 
00496         //TRACE("Dumping directory entry %d:\n", CurrentEntry);
00497         //DbgDumpBuffer(DPRINT_FILESYSTEM, DirEntry, sizeof(DIRENTRY));
00498 
00499         //
00500         // Check if this is the last file in the directory
00501         // If DirEntry[0] == 0x00 then that means all the
00502         // entries after this one are unused. If this is the
00503         // last entry then we didn't find the file in this directory.
00504         //
00505         if (DirEntry->FileName[0] == '\0')
00506         {
00507             return FALSE;
00508         }
00509 
00510         //
00511         // Check if this is a deleted entry or not
00512         //
00513         if (DirEntry->FileName[0] == '\xE5')
00514         {
00515             memset(ShortNameBuffer, 0, 13 * sizeof(CHAR));
00516             memset(LfnNameBuffer, 0, 261 * sizeof(CHAR));
00517             continue;
00518         }
00519 
00520         //
00521         // Check if this is a LFN entry
00522         // If so it needs special handling
00523         //
00524         if (DirEntry->Attr == ATTR_LONG_NAME)
00525         {
00526             //
00527             // Check to see if this is a deleted LFN entry, if so continue
00528             //
00529             if (LfnDirEntry->SequenceNumber & 0x80)
00530             {
00531                 continue;
00532             }
00533 
00534             //
00535             // Mask off high two bits of sequence number
00536             // and make the sequence number zero-based
00537             //
00538             LfnDirEntry->SequenceNumber &= 0x3F;
00539             LfnDirEntry->SequenceNumber--;
00540 
00541             //
00542             // Get all 13 LFN entry characters
00543             //
00544             if (LfnDirEntry->Name0_4[0] != 0xFFFF)
00545             {
00546                 LfnNameBuffer[0 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[0];
00547             }
00548             if (LfnDirEntry->Name0_4[1] != 0xFFFF)
00549             {
00550                 LfnNameBuffer[1 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[1];
00551             }
00552             if (LfnDirEntry->Name0_4[2] != 0xFFFF)
00553             {
00554                 LfnNameBuffer[2 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[2];
00555             }
00556             if (LfnDirEntry->Name0_4[3] != 0xFFFF)
00557             {
00558                 LfnNameBuffer[3 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[3];
00559             }
00560             if (LfnDirEntry->Name0_4[4] != 0xFFFF)
00561             {
00562                 LfnNameBuffer[4 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[4];
00563             }
00564             if (LfnDirEntry->Name5_10[0] != 0xFFFF)
00565             {
00566                 LfnNameBuffer[5 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[0];
00567             }
00568             if (LfnDirEntry->Name5_10[1] != 0xFFFF)
00569             {
00570                 LfnNameBuffer[6 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[1];
00571             }
00572             if (LfnDirEntry->Name5_10[2] != 0xFFFF)
00573             {
00574                 LfnNameBuffer[7 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[2];
00575             }
00576             if (LfnDirEntry->Name5_10[3] != 0xFFFF)
00577             {
00578                 LfnNameBuffer[8 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[3];
00579             }
00580             if (LfnDirEntry->Name5_10[4] != 0xFFFF)
00581             {
00582                 LfnNameBuffer[9 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[4];
00583             }
00584             if (LfnDirEntry->Name5_10[5] != 0xFFFF)
00585             {
00586                 LfnNameBuffer[10 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[5];
00587             }
00588             if (LfnDirEntry->Name11_12[0] != 0xFFFF)
00589             {
00590                 LfnNameBuffer[11 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name11_12[0];
00591             }
00592             if (LfnDirEntry->Name11_12[1] != 0xFFFF)
00593             {
00594                 LfnNameBuffer[12 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name11_12[1];
00595             }
00596 
00597             //TRACE("Dumping long name buffer:\n");
00598             //DbgDumpBuffer(DPRINT_FILESYSTEM, LfnNameBuffer, 260);
00599 
00600             continue;
00601         }
00602 
00603         //
00604         // Check for the volume label attribute
00605         // and skip over this entry if found
00606         //
00607         if (DirEntry->Attr & ATTR_VOLUMENAME)
00608         {
00609             memset(ShortNameBuffer, 0, 13 * sizeof(UCHAR));
00610             memset(LfnNameBuffer, 0, 261 * sizeof(UCHAR));
00611             continue;
00612         }
00613 
00614         //
00615         // If we get here then we've found a short file name
00616         // entry and LfnNameBuffer contains the long file
00617         // name or zeroes. All we have to do now is see if the
00618         // file name matches either the short or long file name
00619         // and fill in the FAT_FILE_INFO structure if it does
00620         // or zero our buffers and continue looking.
00621         //
00622 
00623         //
00624         // Get short file name
00625         //
00626         FatParseShortFileName(ShortNameBuffer, DirEntry);
00627 
00628         //TRACE("Entry: %d LFN = %s\n", CurrentEntry, LfnNameBuffer);
00629         //TRACE("Entry: %d DOS name = %s\n", CurrentEntry, ShortNameBuffer);
00630 
00631         //
00632         // See if the file name matches either the short or long name
00633         //
00634         if (((strlen(FileName) == strlen(LfnNameBuffer)) && (_stricmp(FileName, LfnNameBuffer) == 0)) ||
00635             ((strlen(FileName) == strlen(ShortNameBuffer)) && (_stricmp(FileName, ShortNameBuffer) == 0)))      {
00636             //
00637             // We found the entry, now fill in the FAT_FILE_INFO struct
00638             //
00639             FatFileInfoPointer->Attributes = DirEntry->Attr;
00640             FatFileInfoPointer->FileSize = DirEntry->Size;
00641             FatFileInfoPointer->FilePointer = 0;
00642 
00643             TRACE("MSDOS Directory Entry:\n");
00644             TRACE("FileName[11] = %c%c%c%c%c%c%c%c%c%c%c\n", DirEntry->FileName[0], DirEntry->FileName[1], DirEntry->FileName[2], DirEntry->FileName[3], DirEntry->FileName[4], DirEntry->FileName[5], DirEntry->FileName[6], DirEntry->FileName[7], DirEntry->FileName[8], DirEntry->FileName[9], DirEntry->FileName[10]);
00645             TRACE("Attr = 0x%x\n", DirEntry->Attr);
00646             TRACE("ReservedNT = 0x%x\n", DirEntry->ReservedNT);
00647             TRACE("TimeInTenths = %d\n", DirEntry->TimeInTenths);
00648             TRACE("CreateTime = %d\n", DirEntry->CreateTime);
00649             TRACE("CreateDate = %d\n", DirEntry->CreateDate);
00650             TRACE("LastAccessDate = %d\n", DirEntry->LastAccessDate);
00651             TRACE("ClusterHigh = 0x%x\n", DirEntry->ClusterHigh);
00652             TRACE("Time = %d\n", DirEntry->Time);
00653             TRACE("Date = %d\n", DirEntry->Date);
00654             TRACE("ClusterLow = 0x%x\n", DirEntry->ClusterLow);
00655             TRACE("Size = %d\n", DirEntry->Size);
00656 
00657             //
00658             // Get the cluster chain
00659             //
00660             StartCluster = ((ULONG)DirEntry->ClusterHigh << 16) + DirEntry->ClusterLow;
00661             TRACE("StartCluster = 0x%x\n", StartCluster);
00662             FatFileInfoPointer->FileFatChain = FatGetClusterChainArray(Volume, StartCluster);
00663 
00664             //
00665             // See if memory allocation failed
00666             //
00667             if (FatFileInfoPointer->FileFatChain == NULL)
00668             {
00669                 return FALSE;
00670             }
00671 
00672             return TRUE;
00673         }
00674 
00675         //
00676         // Nope, no match - zero buffers and continue looking
00677         //
00678         memset(ShortNameBuffer, 0, 13 * sizeof(UCHAR));
00679         memset(LfnNameBuffer, 0, 261 * sizeof(UCHAR));
00680         continue;
00681     }
00682 
00683     return FALSE;
00684 }
00685 
00686 static BOOLEAN FatXSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG DirectorySize, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer)
00687 {
00688     ULONG       EntryCount;
00689     ULONG       CurrentEntry;
00690     SIZE_T      FileNameLen;
00691     FATX_DIRENTRY   OurDirEntry;
00692     PFATX_DIRENTRY  DirEntry = &OurDirEntry;
00693 
00694     EntryCount = DirectorySize / sizeof(FATX_DIRENTRY);
00695 
00696     TRACE("FatXSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer, EntryCount, FileName);
00697 
00698     FileNameLen = strlen(FileName);
00699 
00700     for (CurrentEntry = 0; CurrentEntry < EntryCount; CurrentEntry++, DirectoryBuffer = ((PFATX_DIRENTRY)DirectoryBuffer)+1)
00701     {
00702         OurDirEntry = *(PFATX_DIRENTRY) DirectoryBuffer;
00703         FatSwapFatXDirEntry(&OurDirEntry);
00704         if (0xff == DirEntry->FileNameSize)
00705         {
00706             break;
00707         }
00708         if (0xe5 == DirEntry->FileNameSize)
00709         {
00710             continue;
00711         }
00712         if (FileNameLen == DirEntry->FileNameSize &&
00713             0 == _strnicmp(FileName, DirEntry->FileName, FileNameLen))
00714         {
00715             /*
00716              * We found the entry, now fill in the FAT_FILE_INFO struct
00717              */
00718             FatFileInfoPointer->FileSize = DirEntry->Size;
00719             FatFileInfoPointer->FilePointer = 0;
00720 
00721             TRACE("FATX Directory Entry:\n");
00722             TRACE("FileNameSize = %d\n", DirEntry->FileNameSize);
00723             TRACE("Attr = 0x%x\n", DirEntry->Attr);
00724             TRACE("StartCluster = 0x%x\n", DirEntry->StartCluster);
00725             TRACE("Size = %d\n", DirEntry->Size);
00726             TRACE("Time = %d\n", DirEntry->Time);
00727             TRACE("Date = %d\n", DirEntry->Date);
00728             TRACE("CreateTime = %d\n", DirEntry->CreateTime);
00729             TRACE("CreateDate = %d\n", DirEntry->CreateDate);
00730             TRACE("LastAccessTime = %d\n", DirEntry->LastAccessTime);
00731             TRACE("LastAccessDate = %d\n", DirEntry->LastAccessDate);
00732 
00733             /*
00734              * Get the cluster chain
00735              */
00736             FatFileInfoPointer->FileFatChain = FatGetClusterChainArray(Volume, DirEntry->StartCluster);
00737 
00738             /*
00739              * See if memory allocation failed
00740              */
00741             if (NULL == FatFileInfoPointer->FileFatChain)
00742             {
00743                 return FALSE;
00744             }
00745 
00746             return TRUE;
00747         }
00748     }
00749 
00750     return FALSE;
00751 }
00752 
00753 /*
00754  * FatLookupFile()
00755  * This function searches the file system for the
00756  * specified filename and fills in an FAT_FILE_INFO structure
00757  * with info describing the file, etc. returns ARC error code
00758  */
00759 LONG FatLookupFile(PFAT_VOLUME_INFO Volume, PCSTR FileName, ULONG DeviceId, PFAT_FILE_INFO FatFileInfoPointer)
00760 {
00761     UINT32      i;
00762     ULONG       NumberOfPathParts;
00763     CHAR        PathPart[261];
00764     PVOID       DirectoryBuffer;
00765     ULONG       DirectoryStartCluster = 0;
00766     ULONG       DirectorySize;
00767     FAT_FILE_INFO   FatFileInfo;
00768 
00769     TRACE("FatLookupFile() FileName = %s\n", FileName);
00770 
00771     memset(FatFileInfoPointer, 0, sizeof(FAT_FILE_INFO));
00772 
00773     //
00774     // Figure out how many sub-directories we are nested in
00775     //
00776     NumberOfPathParts = FsGetNumPathParts(FileName);
00777 
00778     //
00779     // Loop once for each part
00780     //
00781     for (i=0; i<NumberOfPathParts; i++)
00782     {
00783         //
00784         // Get first path part
00785         //
00786         FsGetFirstNameFromPath(PathPart, FileName);
00787 
00788         //
00789         // Advance to the next part of the path
00790         //
00791         for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
00792         {
00793         }
00794         FileName++;
00795 
00796         //
00797         // Buffer the directory contents
00798         //
00799         DirectoryBuffer = FatBufferDirectory(Volume, DirectoryStartCluster, &DirectorySize, (i == 0) );
00800         if (DirectoryBuffer == NULL)
00801         {
00802             return ENOMEM;
00803         }
00804 
00805         //
00806         // Search for file name in directory
00807         //
00808         if (ISFATX(Volume->FatType))
00809         {
00810             if (!FatXSearchDirectoryBufferForFile(Volume, DirectoryBuffer, DirectorySize, PathPart, &FatFileInfo))
00811             {
00812                 return ENOENT;
00813             }
00814         }
00815         else
00816         {
00817             if (!FatSearchDirectoryBufferForFile(Volume, DirectoryBuffer, DirectorySize, PathPart, &FatFileInfo))
00818             {
00819                 return ENOENT;
00820             }
00821         }
00822 
00823         //
00824         // If we have another sub-directory to go then
00825         // grab the start cluster and free the fat chain array
00826         //
00827         if ((i+1) < NumberOfPathParts)
00828         {
00829             //
00830             // Check if current entry is a directory
00831             //
00832             if (!(FatFileInfo.Attributes & ATTR_DIRECTORY))
00833             {
00834                 MmHeapFree(FatFileInfo.FileFatChain);
00835                 return ENOTDIR;
00836             }
00837             DirectoryStartCluster = FatFileInfo.FileFatChain[0];
00838             MmHeapFree(FatFileInfo.FileFatChain);
00839             FatFileInfo.FileFatChain = NULL;
00840         }
00841     }
00842 
00843     memcpy(FatFileInfoPointer, &FatFileInfo, sizeof(FAT_FILE_INFO));
00844 
00845     return ESUCCESS;
00846 }
00847 
00848 /*
00849  * FatParseFileName()
00850  * This function parses a directory entry name which
00851  * is in the form of "FILE   EXT" and puts it in Buffer
00852  * in the form of "file.ext"
00853  */
00854 void FatParseShortFileName(PCHAR Buffer, PDIRENTRY DirEntry)
00855 {
00856     ULONG       Idx;
00857 
00858     Idx = 0;
00859     RtlZeroMemory(Buffer, 13);
00860 
00861     //
00862     // Fixup first character
00863     //
00864     if (DirEntry->FileName[0] == 0x05)
00865     {
00866         DirEntry->FileName[0] = 0xE5;
00867     }
00868 
00869     //
00870     // Get the file name
00871     //
00872     while (Idx < 8)
00873     {
00874         if (DirEntry->FileName[Idx] == ' ')
00875         {
00876             break;
00877         }
00878 
00879         Buffer[Idx] = DirEntry->FileName[Idx];
00880         Idx++;
00881     }
00882 
00883     //
00884     // Get extension
00885     //
00886     if ((DirEntry->FileName[8] != ' '))
00887     {
00888         Buffer[Idx++] = '.';
00889         Buffer[Idx++] = (DirEntry->FileName[8] == ' ') ? '\0' : DirEntry->FileName[8];
00890         Buffer[Idx++] = (DirEntry->FileName[9] == ' ') ? '\0' : DirEntry->FileName[9];
00891         Buffer[Idx++] = (DirEntry->FileName[10] == ' ') ? '\0' : DirEntry->FileName[10];
00892     }
00893 
00894     //TRACE("FatParseShortFileName() ShortName = %s\n", Buffer);
00895 }
00896 
00897 /*
00898  * FatGetFatEntry()
00899  * returns the Fat entry for a given cluster number
00900  */
00901 BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, ULONG Cluster, ULONG* ClusterPointer)
00902 {
00903     ULONG       fat = 0;
00904     UINT32      FatOffset;
00905     UINT32      ThisFatSecNum;
00906     UINT32      ThisFatEntOffset;
00907 
00908     //TRACE("FatGetFatEntry() Retrieving FAT entry for cluster %d.\n", Cluster);
00909 
00910     switch(Volume->FatType)
00911     {
00912     case FAT12:
00913 
00914         FatOffset = Cluster + (Cluster / 2);
00915         ThisFatSecNum = Volume->ActiveFatSectorStart + (FatOffset / Volume->BytesPerSector);
00916         ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
00917 
00918         TRACE("FatOffset: %d\n", FatOffset);
00919         TRACE("ThisFatSecNum: %d\n", ThisFatSecNum);
00920         TRACE("ThisFatEntOffset: %d\n", ThisFatEntOffset);
00921 
00922         if (ThisFatEntOffset == (Volume->BytesPerSector - 1))
00923         {
00924             if (!FatReadVolumeSectors(Volume, ThisFatSecNum, 2, (PVOID)FILESYSBUFFER))
00925             {
00926                 return FALSE;
00927             }
00928         }
00929         else
00930         {
00931             if (!FatReadVolumeSectors(Volume, ThisFatSecNum, 1, (PVOID)FILESYSBUFFER))
00932             {
00933                 return FALSE;
00934             }
00935         }
00936 
00937         fat = *((USHORT *) ((ULONG_PTR)FILESYSBUFFER + ThisFatEntOffset));
00938         fat = SWAPW(fat);
00939         if (Cluster & 0x0001)
00940             fat = fat >> 4; /* Cluster number is ODD */
00941         else
00942             fat = fat & 0x0FFF; /* Cluster number is EVEN */
00943 
00944         break;
00945 
00946     case FAT16:
00947     case FATX16:
00948 
00949         FatOffset = (Cluster * 2);
00950         ThisFatSecNum = Volume->ActiveFatSectorStart + (FatOffset / Volume->BytesPerSector);
00951         ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
00952 
00953         if (!FatReadVolumeSectors(Volume, ThisFatSecNum, 1, (PVOID)FILESYSBUFFER))
00954         {
00955             return FALSE;
00956         }
00957 
00958         fat = *((USHORT *) ((ULONG_PTR)FILESYSBUFFER + ThisFatEntOffset));
00959         fat = SWAPW(fat);
00960 
00961         break;
00962 
00963     case FAT32:
00964     case FATX32:
00965 
00966         FatOffset = (Cluster * 4);
00967         ThisFatSecNum = Volume->ActiveFatSectorStart + (FatOffset / Volume->BytesPerSector);
00968         ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
00969 
00970         if (!FatReadVolumeSectors(Volume, ThisFatSecNum, 1, (PVOID)FILESYSBUFFER))
00971         {
00972             return FALSE;
00973         }
00974 
00975         // Get the fat entry
00976         fat = (*((ULONG *) ((ULONG_PTR)FILESYSBUFFER + ThisFatEntOffset))) & 0x0FFFFFFF;
00977         fat = SWAPD(fat);
00978 
00979         break;
00980 
00981     default:
00982         TRACE("Unknown FAT type %d\n", Volume->FatType);
00983         return FALSE;
00984 
00985     }
00986 
00987     //TRACE("FAT entry is 0x%x.\n", fat);
00988 
00989     *ClusterPointer = fat;
00990 
00991     return TRUE;
00992 }
00993 
00994 ULONG FatCountClustersInChain(PFAT_VOLUME_INFO Volume, ULONG StartCluster)
00995 {
00996     ULONG   ClusterCount = 0;
00997 
00998     TRACE("FatCountClustersInChain() StartCluster = %d\n", StartCluster);
00999 
01000     while (1)
01001     {
01002         //
01003         // If end of chain then break out of our cluster counting loop
01004         //
01005         if (((Volume->FatType == FAT12) && (StartCluster >= 0xff8)) ||
01006             ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartCluster >= 0xfff8)) ||
01007             ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartCluster >= 0x0ffffff8)))
01008         {
01009             break;
01010         }
01011 
01012         //
01013         // Increment count
01014         //
01015         ClusterCount++;
01016 
01017         //
01018         // Get next cluster
01019         //
01020         if (!FatGetFatEntry(Volume, StartCluster, &StartCluster))
01021         {
01022             return 0;
01023         }
01024     }
01025 
01026     TRACE("FatCountClustersInChain() ClusterCount = %d\n", ClusterCount);
01027 
01028     return ClusterCount;
01029 }
01030 
01031 ULONG* FatGetClusterChainArray(PFAT_VOLUME_INFO Volume, ULONG StartCluster)
01032 {
01033     ULONG   ClusterCount;
01034     ULONG       ArraySize;
01035     ULONG*  ArrayPointer;
01036     ULONG       Idx;
01037 
01038     TRACE("FatGetClusterChainArray() StartCluster = %d\n", StartCluster);
01039 
01040     ClusterCount = FatCountClustersInChain(Volume, StartCluster) + 1; // Lets get the 0x0ffffff8 on the end of the array
01041     ArraySize = ClusterCount * sizeof(ULONG);
01042 
01043     //
01044     // Allocate array memory
01045     //
01046     ArrayPointer = MmHeapAlloc(ArraySize);
01047 
01048     if (ArrayPointer == NULL)
01049     {
01050         return NULL;
01051     }
01052 
01053     //
01054     // Loop through and set array values
01055     //
01056     for (Idx=0; Idx<ClusterCount; Idx++)
01057     {
01058         //
01059         // Set current cluster
01060         //
01061         ArrayPointer[Idx] = StartCluster;
01062 
01063         //
01064         // Don't try to get next cluster for last cluster
01065         //
01066         if (((Volume->FatType == FAT12) && (StartCluster >= 0xff8)) ||
01067             ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartCluster >= 0xfff8)) ||
01068             ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartCluster >= 0x0ffffff8)))
01069         {
01070             Idx++;
01071             break;
01072         }
01073 
01074         //
01075         // Get next cluster
01076         //
01077         if (!FatGetFatEntry(Volume, StartCluster, &StartCluster))
01078         {
01079             MmHeapFree(ArrayPointer);
01080             return NULL;
01081         }
01082     }
01083 
01084     return ArrayPointer;
01085 }
01086 
01087 /*
01088  * FatReadClusterChain()
01089  * Reads the specified clusters into memory
01090  */
01091 BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, ULONG StartClusterNumber, ULONG NumberOfClusters, PVOID Buffer)
01092 {
01093     ULONG       ClusterStartSector;
01094 
01095     TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
01096 
01097     while (NumberOfClusters > 0)
01098     {
01099 
01100         //TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
01101         //
01102         // Calculate starting sector for cluster
01103         //
01104         ClusterStartSector = ((StartClusterNumber - 2) * Volume->SectorsPerCluster) + Volume->DataSectorStart;
01105 
01106         //
01107         // Read cluster into memory
01108         //
01109         if (!FatReadVolumeSectors(Volume, ClusterStartSector, Volume->SectorsPerCluster, (PVOID)FILESYSBUFFER))
01110         {
01111             return FALSE;
01112         }
01113 
01114         memcpy(Buffer, (PVOID)FILESYSBUFFER, Volume->SectorsPerCluster * Volume->BytesPerSector);
01115 
01116         //
01117         // Decrement count of clusters left to read
01118         //
01119         NumberOfClusters--;
01120 
01121         //
01122         // Increment buffer address by cluster size
01123         //
01124         Buffer = (PVOID)((ULONG_PTR)Buffer + (Volume->SectorsPerCluster * Volume->BytesPerSector));
01125 
01126         //
01127         // Get next cluster
01128         //
01129         if (!FatGetFatEntry(Volume, StartClusterNumber, &StartClusterNumber))
01130         {
01131             return FALSE;
01132         }
01133 
01134         //
01135         // If end of chain then break out of our cluster reading loop
01136         //
01137         if (((Volume->FatType == FAT12) && (StartClusterNumber >= 0xff8)) ||
01138             ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartClusterNumber >= 0xfff8)) ||
01139             ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartClusterNumber >= 0x0ffffff8)))
01140         {
01141             break;
01142         }
01143     }
01144 
01145     return TRUE;
01146 }
01147 
01148 /*
01149  * FatReadPartialCluster()
01150  * Reads part of a cluster into memory
01151  */
01152 BOOLEAN FatReadPartialCluster(PFAT_VOLUME_INFO Volume, ULONG ClusterNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer)
01153 {
01154     ULONG       ClusterStartSector;
01155 
01156     //TRACE("FatReadPartialCluster() ClusterNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", ClusterNumber, StartingOffset, Length, Buffer);
01157 
01158     ClusterStartSector = ((ClusterNumber - 2) * Volume->SectorsPerCluster) + Volume->DataSectorStart;
01159 
01160     if (!FatReadVolumeSectors(Volume, ClusterStartSector, Volume->SectorsPerCluster, (PVOID)FILESYSBUFFER))
01161     {
01162         return FALSE;
01163     }
01164 
01165     memcpy(Buffer, (PVOID)((ULONG_PTR)FILESYSBUFFER + StartingOffset), Length);
01166 
01167     return TRUE;
01168 }
01169 
01170 /*
01171  * FatReadFile()
01172  * Reads BytesToRead from open file and
01173  * returns the number of bytes read in BytesRead
01174  */
01175 BOOLEAN FatReadFile(PFAT_FILE_INFO FatFileInfo, ULONG BytesToRead, ULONG* BytesRead, PVOID Buffer)
01176 {
01177     PFAT_VOLUME_INFO Volume = FatFileInfo->Volume;
01178     ULONG           ClusterNumber;
01179     ULONG           OffsetInCluster;
01180     ULONG           LengthInCluster;
01181     ULONG           NumberOfClusters;
01182     ULONG           BytesPerCluster;
01183 
01184     TRACE("FatReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead, Buffer);
01185 
01186     if (BytesRead != NULL)
01187     {
01188         *BytesRead = 0;
01189     }
01190 
01191     //
01192     // If they are trying to read past the
01193     // end of the file then return success
01194     // with BytesRead == 0
01195     //
01196     if (FatFileInfo->FilePointer >= FatFileInfo->FileSize)
01197     {
01198         return TRUE;
01199     }
01200 
01201     //
01202     // If they are trying to read more than there is to read
01203     // then adjust the amount to read
01204     //
01205     if ((FatFileInfo->FilePointer + BytesToRead) > FatFileInfo->FileSize)
01206     {
01207         BytesToRead = (FatFileInfo->FileSize - FatFileInfo->FilePointer);
01208     }
01209 
01210     //
01211     // Ok, now we have to perform at most 3 calculations
01212     // I'll draw you a picture (using nifty ASCII art):
01213     //
01214     // CurrentFilePointer -+
01215     //                     |
01216     //    +----------------+
01217     //    |
01218     // +-----------+-----------+-----------+-----------+
01219     // | Cluster 1 | Cluster 2 | Cluster 3 | Cluster 4 |
01220     // +-----------+-----------+-----------+-----------+
01221     //    |                                    |
01222     //    +---------------+--------------------+
01223     //                    |
01224     // BytesToRead -------+
01225     //
01226     // 1 - The first calculation (and read) will align
01227     //     the file pointer with the next cluster.
01228     //     boundary (if we are supposed to read that much)
01229     // 2 - The next calculation (and read) will read
01230     //     in all the full clusters that the requested
01231     //     amount of data would cover (in this case
01232     //     clusters 2 & 3).
01233     // 3 - The last calculation (and read) would read
01234     //     in the remainder of the data requested out of
01235     //     the last cluster.
01236     //
01237 
01238     BytesPerCluster = Volume->SectorsPerCluster * Volume->BytesPerSector;
01239 
01240     //
01241     // Only do the first read if we
01242     // aren't aligned on a cluster boundary
01243     //
01244     if (FatFileInfo->FilePointer % BytesPerCluster)
01245     {
01246         //
01247         // Do the math for our first read
01248         //
01249         ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
01250         ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
01251         OffsetInCluster = (FatFileInfo->FilePointer % BytesPerCluster);
01252         LengthInCluster = (BytesToRead > (BytesPerCluster - OffsetInCluster)) ? (BytesPerCluster - OffsetInCluster) : BytesToRead;
01253 
01254         //
01255         // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
01256         //
01257         if (!FatReadPartialCluster(Volume, ClusterNumber, OffsetInCluster, LengthInCluster, Buffer))
01258         {
01259             return FALSE;
01260         }
01261         if (BytesRead != NULL)
01262         {
01263             *BytesRead += LengthInCluster;
01264         }
01265         BytesToRead -= LengthInCluster;
01266         FatFileInfo->FilePointer += LengthInCluster;
01267         Buffer = (PVOID)((ULONG_PTR)Buffer + LengthInCluster);
01268     }
01269 
01270     //
01271     // Do the math for our second read (if any data left)
01272     //
01273     if (BytesToRead > 0)
01274     {
01275         //
01276         // Determine how many full clusters we need to read
01277         //
01278         NumberOfClusters = (BytesToRead / BytesPerCluster);
01279 
01280         if (NumberOfClusters > 0)
01281         {
01282             ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
01283             ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
01284 
01285             //
01286             // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
01287             //
01288             if (!FatReadClusterChain(Volume, ClusterNumber, NumberOfClusters, Buffer))
01289             {
01290                 return FALSE;
01291             }
01292             if (BytesRead != NULL)
01293             {
01294                 *BytesRead += (NumberOfClusters * BytesPerCluster);
01295             }
01296             BytesToRead -= (NumberOfClusters * BytesPerCluster);
01297             FatFileInfo->FilePointer += (NumberOfClusters * BytesPerCluster);
01298             Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfClusters * BytesPerCluster));
01299         }
01300     }
01301 
01302     //
01303     // Do the math for our third read (if any data left)
01304     //
01305     if (BytesToRead > 0)
01306     {
01307         ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
01308         ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
01309 
01310         //
01311         // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
01312         //
01313         if (!FatReadPartialCluster(Volume, ClusterNumber, 0, BytesToRead, Buffer))
01314         {
01315             return FALSE;
01316         }
01317         if (BytesRead != NULL)
01318         {
01319             *BytesRead += BytesToRead;
01320         }
01321         FatFileInfo->FilePointer += BytesToRead;
01322         BytesToRead -= BytesToRead;
01323         Buffer = (PVOID)((ULONG_PTR)Buffer + BytesToRead);
01324     }
01325 
01326     return TRUE;
01327 }
01328 
01329 BOOLEAN FatReadVolumeSectors(PFAT_VOLUME_INFO Volume, ULONG SectorNumber, ULONG SectorCount, PVOID Buffer)
01330 {
01331     LARGE_INTEGER Position;
01332     ULONG Count;
01333     LONG ret;
01334 
01335     //TRACE("FatReadVolumeSectors(): SectorNumber %d, SectorCount %d, Buffer %p\n",
01336     //    SectorNumber, SectorCount, Buffer);
01337 
01338     //
01339     // Seek to right position
01340     //
01341     Position.QuadPart = SectorNumber * 512;
01342     ret = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
01343     if (ret != ESUCCESS)
01344     {
01345         TRACE("FatReadVolumeSectors() Failed to seek\n");
01346         return FALSE;
01347     }
01348 
01349     //
01350     // Read data
01351     //
01352     ret = ArcRead(Volume->DeviceId, Buffer, SectorCount * 512, &Count);
01353     if (ret != ESUCCESS || Count != SectorCount * 512)
01354     {
01355         TRACE("FatReadVolumeSectors() Failed to read\n");
01356         return FALSE;
01357     }
01358 
01359     // Return success
01360     return TRUE;
01361 }
01362 
01363 LONG FatClose(ULONG FileId)
01364 {
01365     PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
01366 
01367     if (FileHandle->FileFatChain) MmHeapFree(FileHandle->FileFatChain);
01368     MmHeapFree(FileHandle);
01369 
01370     return ESUCCESS;
01371 }
01372 
01373 LONG FatGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
01374 {
01375     PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
01376 
01377     RtlZeroMemory(Information, sizeof(FILEINFORMATION));
01378     Information->EndingAddress.LowPart = FileHandle->FileSize;
01379     Information->CurrentAddress.LowPart = FileHandle->FilePointer;
01380 
01381     TRACE("FatGetFileInformation() FileSize = %d\n",
01382         Information->EndingAddress.LowPart);
01383     TRACE("FatGetFileInformation() FilePointer = %d\n",
01384         Information->CurrentAddress.LowPart);
01385 
01386     return ESUCCESS;
01387 }
01388 
01389 LONG FatOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
01390 {
01391     PFAT_VOLUME_INFO FatVolume;
01392     FAT_FILE_INFO TempFileInfo;
01393     PFAT_FILE_INFO FileHandle;
01394     ULONG DeviceId;
01395     BOOLEAN IsDirectory;
01396     LONG ret;
01397 
01398     if (OpenMode != OpenReadOnly && OpenMode != OpenDirectory)
01399         return EACCES;
01400 
01401     DeviceId = FsGetDeviceId(*FileId);
01402     FatVolume = FatVolumes[DeviceId];
01403 
01404     TRACE("FatOpen() FileName = %s\n", Path);
01405 
01406     RtlZeroMemory(&TempFileInfo, sizeof(TempFileInfo));
01407     ret = FatLookupFile(FatVolume, Path, DeviceId, &TempFileInfo);
01408     if (ret != ESUCCESS)
01409         return ENOENT;
01410 
01411     //
01412     // Check if caller opened what he expected (dir vs file)
01413     //
01414     IsDirectory = (TempFileInfo.Attributes & ATTR_DIRECTORY) != 0;
01415     if (IsDirectory && OpenMode != OpenDirectory)
01416         return EISDIR;
01417     else if (!IsDirectory && OpenMode != OpenReadOnly)
01418         return ENOTDIR;
01419 
01420     FileHandle = MmHeapAlloc(sizeof(FAT_FILE_INFO));
01421     if (!FileHandle)
01422         return ENOMEM;
01423 
01424     RtlCopyMemory(FileHandle, &TempFileInfo, sizeof(FAT_FILE_INFO));
01425     FileHandle->Volume = FatVolume;
01426 
01427     FsSetDeviceSpecific(*FileId, FileHandle);
01428     return ESUCCESS;
01429 }
01430 
01431 LONG FatRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
01432 {
01433     PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
01434     BOOLEAN ret;
01435 
01436     //
01437     // Call old read method
01438     //
01439     ret = FatReadFile(FileHandle, N, Count, Buffer);
01440 
01441     //
01442     // Check for success
01443     //
01444     if (ret)
01445         return ESUCCESS;
01446     else
01447         return EIO;
01448 }
01449 
01450 LONG FatSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
01451 {
01452     PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
01453 
01454     TRACE("FatSeek() NewFilePointer = %lu\n", Position->LowPart);
01455 
01456     if (SeekMode != SeekAbsolute)
01457         return EINVAL;
01458     if (Position->HighPart != 0)
01459         return EINVAL;
01460     if (Position->LowPart >= FileHandle->FileSize)
01461         return EINVAL;
01462 
01463     FileHandle->FilePointer = Position->LowPart;
01464     return ESUCCESS;
01465 }
01466 
01467 const DEVVTBL FatFuncTable =
01468 {
01469     FatClose,
01470     FatGetFileInformation,
01471     FatOpen,
01472     FatRead,
01473     FatSeek,
01474     L"fastfat",
01475 };
01476 
01477 const DEVVTBL* FatMount(ULONG DeviceId)
01478 {
01479     PFAT_VOLUME_INFO Volume;
01480     UCHAR Buffer[512];
01481     PFAT_BOOTSECTOR BootSector = (PFAT_BOOTSECTOR)Buffer;
01482     PFAT32_BOOTSECTOR BootSector32 = (PFAT32_BOOTSECTOR)Buffer;
01483     PFATX_BOOTSECTOR BootSectorX = (PFATX_BOOTSECTOR)Buffer;
01484     FILEINFORMATION FileInformation;
01485     LARGE_INTEGER Position;
01486     ULONG Count;
01487     ULARGE_INTEGER SectorCount;
01488     LONG ret;
01489 
01490     //
01491     // Allocate data for volume information
01492     //
01493     Volume = MmHeapAlloc(sizeof(FAT_VOLUME_INFO));
01494     if (!Volume)
01495         return NULL;
01496     RtlZeroMemory(Volume, sizeof(FAT_VOLUME_INFO));
01497 
01498     //
01499     // Read the BootSector
01500     //
01501     Position.HighPart = 0;
01502     Position.LowPart = 0;
01503     ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
01504     if (ret != ESUCCESS)
01505     {
01506         MmHeapFree(Volume);
01507         return NULL;
01508     }
01509     ret = ArcRead(DeviceId, Buffer, sizeof(Buffer), &Count);
01510     if (ret != ESUCCESS || Count != sizeof(Buffer))
01511     {
01512         MmHeapFree(Volume);
01513         return NULL;
01514     }
01515 
01516     //
01517     // Check if BootSector is valid. If no, return early
01518     //
01519     if (!RtlEqualMemory(BootSector->FileSystemType, "FAT12   ", 8) &&
01520         !RtlEqualMemory(BootSector->FileSystemType, "FAT16   ", 8) &&
01521         !RtlEqualMemory(BootSector32->FileSystemType, "FAT32   ", 8) &&
01522         !RtlEqualMemory(BootSectorX->FileSystemType, "FATX", 4))
01523     {
01524         MmHeapFree(Volume);
01525         return NULL;
01526     }
01527 
01528     //
01529     // Determine sector count
01530     //
01531     ret = ArcGetFileInformation(DeviceId, &FileInformation);
01532     if (ret != ESUCCESS)
01533     {
01534         MmHeapFree(Volume);
01535         return NULL;
01536     }
01537     SectorCount.HighPart = FileInformation.EndingAddress.HighPart;
01538     SectorCount.LowPart = FileInformation.EndingAddress.LowPart;
01539     SectorCount.QuadPart /= SECTOR_SIZE;
01540 
01541     //
01542     // Keep device id
01543     //
01544     Volume->DeviceId = DeviceId;
01545 
01546     //
01547     // Really open the volume
01548     //
01549     if (!FatOpenVolume(Volume, BootSector, SectorCount.QuadPart))
01550     {
01551         MmHeapFree(Volume);
01552         return NULL;
01553     }
01554 
01555     //
01556     // Remember FAT volume information
01557     //
01558     FatVolumes[DeviceId] = Volume;
01559 
01560     //
01561     // Return success
01562     //
01563     return &FatFuncTable;
01564 }

Generated on Sat May 26 2012 04:17:57 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.