Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

114KB

Developing Your Own 32-Bit Operating System — Chapter-27: File System Code

  • Source: /library/Developing Your Own 32-Bit Operating System/source-file.pdf
  • PDF pages: 613–682
  • Pages without text: 613

File System Code

I
592 Part PV ■ The Operating System Source Code The MMURTL FAT file system is truly no great accomplishment. Reverse engineering pro- vides some satisfaction, but never the amount that an original design would. There are better file systems to be designed than the one that came with a 1 5-year-old operat- ing system, but none are as widespread as the MS-DOS FAT file system. How MMURTL Handles FAT The physical disk layout, as seen from the disk controller's standpoint, is as follows: Cylinder numbers run from 0 to nMaxCyls-1. Head numbers run from 0 to nMaxheads— 1. Sector numbers run from 1 to nMaxSectorsPerTrack. Physical (Absolute) Sector Numbers Physical sector numbers (absolute) begin at Cylinder 0, Head 0, Sector 1. As the physical sec- tor number rolls over (nMaxSectorsPerTrack+1), the head number is incremented, which moves you to the next track (same cylinder, next head). When the head number rolls over (nMaxHeads is reached), the cylinder number is incremented. Track and cylinder ‘are nor interchangeable terms in what I have said here. If you have six heads on your drive, you have six tracks per cylinder. This can be confusing because many books and docu- ments use these terms interchangeably. You can too, so long as you know that's what you're doing. Hidden Sectors MS-DOS reserves a section of the physical hard disk. This area is called the hidden sectors. This area is usually the very first track on the disk (beginning at Cylinder 0, Head 0, Sector 1). The partition tables are kept at the very end of the first sector in this hidden area (offset 01 BEh in the first sector, to be exact). The partition tables are 16-byte entries describing “logical” sections of the disk that can be treated as separate drives. Usually there are no “hidden sectors” on floppy disks, nor are there any par- tition tables. Logical Block Address (LBA) MMURTL device drivers treat the entire disk as a single physical drive. The MMURTL file system reads the partition tables, then sets up the device driver to span the entire physical disk as 0 to nMaxBlockNumbers-1. A block number is referred to as the Logical Block Address (LBA) and is the value passed in to the DeviceOp call for the MMURTL hard/floppy disk device driv- ers (LBAs are used with all MMURTL devices).

Chapter 27 ■ File System Code Do not confuse MMURTL's LBA for the sector number in an MS-DOS logical drive. MMURTL refers to these as “logical blocks” because you still have to convert them into physi- cal cylinder, head, and sector to retrieve the data. MS-DOS Boot Sector The first sector of an MS-DOS logical drive is its boot sector. Each of the MS-DOS logical parti- tions will have a boot sector, although only the first will be marked as bootable (if any are). Its position on the disk is calculated from the partition table information. |
593 File System Initialization The MMURTL- FAT file system reads the partition table and saves the starting LBA and length of each of DOS logical disk that is found. Armed with this information, MMURTL can access each of the DOS logical disks as a separate disk drive. To maintain some sanity, the MMURTL file system gives all of its logical drives a letter, just as does MS-DOS. MMURTL supports two floppy drives (A and B) and up to eight logical hard disks (C through J). All information on the logical drives are kept in an array of records (Ldrvs). This includes the logical-letter-to-physical-drive conversions. After you have the layout of each of the partitions, you read the boot sector from the first DOS logical drive. The boot sector contains several pieces of important information about the drive geometry (numbers of heads, sectors per track, and so on), which are also placed in the logical drive structures. After you have the drive geometry information, you set up the MMURTL device driver. This tells the device driver how many cylinders, heads, and sectors per track are on the physical disk. Until this is done, the device driver assumes a minimum drive size, and you should only read the partition table (or boot sector, if no partition table is on the disk). This provides enough information to do a Device I nit call to set up proper drive geometry. If you were building a loadable file system to replace the one that's included in MMURTL, you would call your routine to initialize the file system very early in the main program block. You must not service file system requests until this is done. File System Listing The file system implementation in Listing 27. 1 has ample comments to describe the purpose of each function. An important concept to note is that the file system itself is a separate task that runs at a relatively high priority of 5.

Part W ■ The Operating System Source Code At the very end of Listing 27.1, you will find a function called InitFS( ), which is called from the Monitor to initialize the file system. All resources are allocated in this function before we spawn the new file system task and register the service with the operating system. Listing 27.1 . MS-DOS FAT-compatible file system code. #define U32 unsigned long #define U16 unsigned int #define U8 unsigned char #define S32 long #define S16 int #define S8 char #define TRUE 1 #define FALSE 0 /*********** MMURTL Public Prototypes ***************/ /* From MKernel */ extern far AllocExch(long *pExchRet); extern far U32 GetTSSExch(U32 *pExchRet); extern far SpawnTask(char *pEntry, long dPriority, long f Debug, char *pStack, long fOSCode) ; extern far long WaitMsg(long Exch, char *pMsgRet); extern far long CheckMsg(long Exch, char *pMsgRet); extern far long Request (unsigned char *pSvcName, unsigned int wSvcCode, unsigned long dRespExch, unsigned long *pRqHndlRet, unsigned long dnpSend, unsigned char *pData1 , unsigned long dcbDatal , unsigned char *pData2, unsigned long dcbData2, unsigned long dData0, unsigned long dDatal , unsigned long dData2); extern far long Respond(long dRqHndl, long dStatRet); /* From MData */ extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes); extern far void FillData(U8 *pDest, U32 cBytes, U8 bFill); extern far long CompareNCS(U8 *pS1 , U8 *pS2, U32 dSize) ; /* From MTimer.h */ extern far long GetCMOSTime(long *pTimeRet); extern far long GetCMOSDate(long *pTimeRet); extern far long GetTimerTick(long *pTickRet); /* From MVid.h */ extern far long TTYOut (char *pTextOut, long ddTextOut, long ddAttrib); extern far long GetNormVid(long *pNormVidRet ) ;

Chapter 27 ■ File System Code H #include “MKbd.h” /* From MDevDrv */ extern far U32 Device0p(U32 dDevice, U32 dOpNum, U32 dLBA, U32 dnBlocks, U8 *pData); extern far U32 DeviceStat(U32 dDevice, S8 * pStatRet, U32 dStatusMax, U32 *pdSatusRet); extern far U32 DeviceInit(U32 dDevNum, S8 *pInitData, U32 sdlnitData); /* From MMemory.h */ extern far U32 AllocOSPage(U32 nPages, U8 *ppMemRet); extern far U32 DeAllocPage(U8 *pOrigMem, U32 nPages); /* From MJob.h */ extern far U32 GetPath(long JobNum, char *pPathRet, long *pdcbPathRet) extern far U32 RegisterSvc(S8 *pName, U32 Exch); /* NEAR support for debugging */ extern long xprintf(char *fmt, ...)>’ extern U32 Dump(unsigned char *pb, long cb) ; /* File System error codes */ #define ErcOK #define ErcEOF #define ErcBadSvcCode #def ine #def ine #def ine #define #define #define #def ine #define #define #define #def ine #define #def ine #def ine #define #define #define #define #define #def ine ErcBadFileSpec ErcNoSuchDrive ErcNotAFile ErcNoSuchFile ErcNoSuchDir ErcReadOnly ErcNoFreeFCB ErcBadOpenMode ErcFilelnUse ErcNoFreeFUB ErcBadFileHandle 210 1 32 200 201 202 203 204 205 206 207 208 209 ErcBrokenFile ErcBadFCB ErcStreamFile ErcBlockFile ErcBeyondEOF ErcNoParTable ErcBadFATClstr ErcRenameDrv ErcRenameDir 211 213 214 215 217 218 220 222 223 Alls Well */ DUH. . . The END */ Service doesn't handle that code */ invalid file spec (not correct format)*/ Try another letter bozo */ Open a directory?? NOT */ No can do! It ain't there...*/ Ain't no such dir... */ You can't modify it bubba */ We're really hurtin... */ Say what? Mode??? */ File is open in an incompatible mode */ Sorry, out of File User Blocks */ WHOAAA, bad handle buddy! */ Cluster chain broken on file */ We got REAL problems... */ Operation not allowed on Stream File */ Operation not allowed on Block File */ SetLFA or Read/WriteBlock beyond EOF */ No partiton table found on disk!!! */ File system screwed up (or your disk) */ They have tried to rename across Dir/Vol*/ They have tried to rename across Dir/Vol*/

\5% Part PV ■ The Operating System Source Code Listing 27.1. continued #define ErcNoMatch #define ErcWriteOnly #define ErcDupName #define ErcNotSupported #define ErcRootFull #define ErcDiskFull 225 / 226 / 227 / 228 / 230 / /* No matching directory entry */ Attempt to read write-only device */ Name exists as a file or dir already */ Not supported on this file */ The Root Directory is Full */ No more free CLUSTERS!!! */ #define ErcNewMedia 605 /* for floppy mounting from FDD */ /**************** FAT Buffer control structure **********************/ The Fat structures are for keeping track of the FAT buffers. We never want to have more than one copy of a FAT sector in memory at one time, and we also never want to read one when it's already here (a waste of time)! We also keep track of the last time it was used and deallocate the oldest (LRU - Least Recently Used). Initially filling out the Fat control structure is part of the file system initialization. If the FAT sector we are in has been modified (data written to clusters in it & FAT updated) we write it ASAP! Each FAT buffer is 1 sector long, except the first one which is 3 sectors for floppies (FAT12 types). This is because the FAT12 entries span sectors! #define nFATBufs 17 1 Static for floppies 512 = 8192, 2 pages */ static struct fattype { U8 *pBuf ; / U32 LastUsed; / U32 LBASect; / U16 iClstrStart; / U8 Drive; / U8 }; fModLock; / /* */ points to beginning of fat buffer */ /* Tick when last used (0 = Never) */ LBA of first FAT sect in buf (where it came from) */ Starting cluster for each buf */ /* LDrive this FAT sector is from */ Bit 0 = Modified, bit 1 = Locked */ static struct fattype Fat[nFATBufs] ; /* 16 bytes * 17 */ /* We read 3 sectors worth of floppy fat buf in because cluster entries span sectors U8 FatBufA(1536]; /* floppy fat buffer */ #define FATMOD 0x01 tfdefine FATLOCK 0x02 /*****.********** File contol Block Structures (FCBs) **********/ /* One FCB is allocated and filled out for each file that is open. The actual directory entry structure from the disk is embedded in the FCB so it can be copied directly to/from the directory sector on the disk. */

Chapter 27 H File System Code #define nFCBs 128 #define sFCB 64 static struct FCB { S8 Name[8]; S8 Ext[3]; S8 Attr; U8 Resvd1[10]; U16 Time; U16 Date; U16 StartClstr; U32 FileSize; U32 LBADirSect; U16 oSectDirEnt; U8 Ldrv; U8 Mode; U8 nUsers; U8 fMod; U8 Resvd[22]; }; /* From here to Filesize is copy of DirEnt */ /* from MS-DOS */ /* ???????? */ /* Only changed when created or updated */ /* At least one per file! last entry in FAT Dir Ent (32 bytes) */ LBA of directory sector this is from */ /* Offset in sector for the dir entry r /’ /’ I* /’ Logical drive this is on (A-J, 0-9) 0 or 1 (Read or Modify). */ Active FUBs for this file (255 MAX) This file was modified! */ Out to 64 bytes */ Free FCB */ static struct FCB static struct FCB ‘paFCB; ‘pFCB; a pointer to array of allocated FCBs. */ pointer to one FCB */ File User Blocks /* Each user of an open file is assigned a FUB. The FUB number is the filehandle (beginning with 3). ) 0, 1 & 2 are reserved for NUL, KBD and VID devices. */ #define nFUBs 128 #define sFUB 32 /* The FUB contains information on a file related to a user's view of the file. It is used to hold information on files opened in stream and block mode. Three important fields in the FUB are: LFABuf - LFA of first byte in buffer for a stream file. Clstr - Clstr of last block read or stream fill. LFAClstr - LFA of first byte in Clstr. LFAClstr and Clstr give us a relative starting point when reading a file from disk. If we didn't save this information on the last access, we would have to “run” the cluster chain everytime we wanted to read or access a file beyond the last point we read. ruct FUB { U16 Job; /* U16 iFCB; /* U32 CrntLFA; /* U8 *pBuf; /* U32 sBuf; /* U32 LFABuf; /* U32 LFAClstr; /* U16 Clstr; /* User's Job Number. 0 if FUB is free. */ FCB number for this file (0 to nFCBs-1) Current Logical File Address (File Ptr) Ptr to buffer if stream mode */ Size of buffer for Stream file in bytes S-First LFA in Clstr Buffer */ LFA of Clstr (below). */ Last Cluster read */

I cqo Part TV ■ The Operating System Source Code Listing 27.1. continued U8 f Modified; / U8 fStream; / U8 Rsvd[4]; / }; Data in buffer was modified */ NonZero for STREAM mode */ Pad to 32 bytes */ static struct FUB paFUB; / a pointer to allocated FUBs. Set up at init. */ static struct FUB pFUB; / a pointer to allocated FUBs. Set up at init. / / Boot sector info (62 byte structure) / struct fsbtype { U8 Jmp[3]; U8 0EMname[8]; U16 bps; U8 SecPerClstr; U16 ResSectors; U8 FATs; U16 RootDirEnts; U16 Sectors; U8 Media; U16 SecPerFAT; U16 SecPerTrack; U16 Heads; U32 HiddenSecs; U32 HugeSecs; U8 DriveNum; U8 Rsvdl; U8 BootSig; U32 VolID; U8 VolLabel[H]; U8 FileSysType[8]; / 62 bytes / }; static struct fsbtype fsb; / Partition Table Entry info. 16 bytes / struct partent { U8 f Bootable; U8 HeadStart; U8 SecStart; U8 CylStart; U8 FATType; U8 HeadEnd; U8 SecEnd; U8 CylEnd; U32 nFirstSector; U32 }; nSectorsTotal static struct partent partab[4); / 4 partition table entries 64 bytes / static U16 partsig; / Bit definitions in attribute field for a directory entry */ #define ATTRNORM 0x00 #define READONLY 0x01 fldefine HIDDEN 0x02

Chapter 27 ■ File System Code H #define SYSTEM 0x04 #define VOLNAME 0x08 #define DIRECTORY 0x10 #define ARCHIVE 0x20 /* Directory Entry Record, 32 bytes */ struct dirstruct { U8 Name[8]; U8 Ext[3]; U8 Attr; U8 Rsvd[10]; U16 Time; U16 Date; U16 StartClstr; U32 FileSize; }; static struct dirstruct dirent; static struct dirstruct *pDirEnt; /* a pointer to a dir entry */ /* When a file is opened, the filename is parsed into an array to facilitate searching the directory tree. IN MS-DOS all dir and file names are SPACE padded (20h). The FileSpec array contains the fully parsed path of the file. For instance, If you were to open “A:\Dog\Food\IsGood.txt” the FileSpec array would look like this: FileSpec [0] = “DOG FileSpec[1] = “FOOD FileSpec[2] = “ISGOOD TXT” FileSpec[3][0] = NULL; Note that the DOT is not inlcuded (it's not in the DOS directory either), and the next unused FileSpec entry contains NULL in the first byte. SpecDepth tells us how many directories deep the name goes. static U8 FDrive /* Drive parsed from file operation */ static U8 FileSpec[7] [11 ] ; /* Hierarchy from file spec parsing */ static U8 SpecDepth; /* Depth of parse (0=Root File) */ /* Used for Rename */ static U8 FDrivel /* Drive parsed from file operation */ static U8 FileSped [7] [11 ] ; /* Hierarchy from file spec parsing */ static U8 SpecDepthl ; /* Depth of parse (0=Root File) */ /* raw sector buffer for all kinds of stuff */ static U8 abRawSector[516] ; static U8 abTmpSector[516] ; static U8 abDirSectBuf [516] ; /* These arrays keep track of physical drive data (0-4). */ #define nPDrvs 4 static struct phydrv { U32 nHeads; /* heads per drives */

600 I Part PV ■ The Operating System Source Code Listing 27.1. continued U32 nSecPerTrk; U16 BSICyl; U8 BSIHead; U8 BSISect; } /* Sectors per track */ /* Cyl of 1st boot sector on disk */ /* Head of 1st boot sector on disk */ /* Sector of 1st boot sector on disk */ static struct phydrv PDrvsfnPDrvs] ; /* This array of structures keeps track of logical drive data (A-J). */ #define nLDrvs 10 static struct ldrvtype { U32 LBA0; /* U32 LBAData; /* U32 LBAMax; /* U32 LBARoot; /* U32 LBAFAT; /* U16 nHeads; /* U16 nSecPerTrk; /* U16 nRootDirEnt; /* U16 SFAT; /* U8 DevNum; /* U8 SecPerClstr; /* U8 nFATS; /* U8 fFAT16; }; /* lba for Start of LDrive (bootSect) */ lba for Start of Data Area */ Max lba for logical drive */ lba of the Root directory */ lba of first FAT */ Setup after boot sector is read */ Setup after boot sector is read */ Number of Root directory entries */ nSectors in a FAT */ Device Number for this ldrv FF = NONE */ For each logical drive */ number of FATs */ True for FAT16 else FAT12 */ static struct ldrvtype Ldrv[nLDrvs] ; /* This is the Hard Disk Device Status record. It is peculiar to the HD Drvr */ struct hddevtype{ U32 ere; U32 blocks_done; U32 BlocksMax; U8 fNewMedia; U8 type_now; U8 resvd0[2]; U32 nCyl; U32 nHead; U32 nSectors; U32 nBPS; U32 LastRecalErc0; U32 LastSeekErc0; U8 LastStatByte0; U8 LastErcByte0; U8 flntOnReset; U8 filler©; U32 LastRecalErd ; U32 LastSeekErd; U8 LastStatBytel ; U8 LastErcBytel ; U8 ResetStatByte; U8 fillerl; current fdisk_table for drive selected */ padding for DWord align */ total physical cylinders (we really don't care) •/ total heads on device */ Sectors per track */ Number of bytes per sect. 32 bytes out to here.*/ /* Interrupt was received on HDC_RESET Status Byte immediately after RESET */

Chapter 27 File System Code H U32 resvd1[2]

; /* out to 64 bytes */ static struct hddevtype HDDevStat; /* This is the Floppy Device Status record. It is peculiar to the FD Drvr */ struct fdstattype{ U32 ere; U32 blocks_done; U32 BlocksMax; U8 fNewMedia; U8 type_now; U8 resvd1[2]; U32 nCyl; U32 nHead; U32 nSectors; U32 nBPS; U8 params[16] ; U8 STATUS[ 8]; U32 resvd3; U32 resvd4; }; /* Last Error from device */ /* current fdisk_table for drive selected */ /* padding for DWord align /* total physical cylinders /* total heads on device /* Sectors per track /* Number of bytes per sect /* begin device specific fields */ /* status returned from FDC (for user status] /* 64 bytes total */ static struct fdstattype FDDevStat; static long FSysStack[512] ; /* 2048 byte stack for Fsys task */ static long FSysExch; struct reqtype { long ServiceExch; long RespExch; long RqOwnerJob; long ServiceRoute; char *pRqHndlRet; long dDataO; long dDatal ; long dData2; int ServiceCode; char npSend; char npRecv; char *pData1 ; long cbDatal ; char *pData2; long cbData2; long RQBRsvdl ; long RQBRsvd2; long RQBRsvd3; }; /* 64 byte request block structure */ static struct reqtype *pRQB; static char *fsysname = “FILESYSM’ static unsigned long keycode; /* for testing */

602 Part TV ■ The Operating System Source Code Listing 27.1. continued BEGIN CODE Called from read_PE, this gets the starting cylinder, head and sector for the first boot sector on a physical drive and stores it in the phydrv array, d is the drive, i is the index into the partition table we read in. static void GetBSInfo(U32 d, U32 i) { PDrvs[d] .BSlHead = partab[i] .HeadStart; PDrvs[d] .BSISect = partab[i] .SecStart; PDrvs[d].BS1Cyl = partab[i] .CylStart; if (!i) { /* primary partition info - use it for PDrv info */ PDrvs[d] .nHeads = partab[i] .HeadEnd; PDrvs[d] .nSecPerTrk = partab[i] .nFirstSector & 0xff; } } /** InitFloppy ********************************* This gets status from the floppy drive (device Id) and sets the physical & logical drive parameters for the type. It is called when the file system is first initialized and when there has been an error on the floppy. static U32 StatFloppy(U8 Id) { U32 ere, i; /* Set gets status for the floppy type from the FDD and sets logical paramters for Ldrvs. Ldrv[0] .DevNum= 10; Ldrv[1 ] .DevNum= 1 1 ; Device Numbers for floppies ere = DeviceStat(ld+10, &FDDevStat, 64, &i); if (!erc) { PDrvs[ld] .nHeads = FDDevStat .nHead ; PDrvsfld] .nSecPerTrk = FDDevStat . nSectors; Ldrvfld] .LBA0 =0; /* Floppy Boot Sector - always 0 */ Ldrv[ld] .LBAMax= FDDevStat .BlocksMax -1 ; /* Max lba for logical drive 0 */ Ldrv[ Id] .nHeads = FDDevStat .nHead; Ldrv[ld] .nSecPerTrk ■ FDDevStat .nSectors; ere = 0; } else Ldrv[ld] .DevNun 0xff

Chapter 27 ■ File System Code return ere; } Reads the partition table entries from hard drives and sets up some of the the logical drive array variables for hard Disks. It also saves first cylinder, head and sector of the first partiton on each physical drive so we can get more info for the LDrv arrays from the boot sector of that partition. static U32 read_PE(void) { U32 ere, ercD12, ercD13, i, j; U8 fFoundl , fFound2; fFoundl = 0; /* Have we found first valid partition on drive */ fFound2 = 0; /* Set defaults for 4 physical drives. This info will be set correctly when the partition table and boot sectors are read. */ for (i=2; i< nLDrvs; i++) { /* default to no logical hard drives */ Ldrv[i] .DevNum = 0xff ; } i = 2; /* first Logical Number for hard drives “C” */ for (j=2; j<4; j++) { /* Array index Numbers for 2 physical hard Disks */ ere = DeviceOp( j+10, 1, 0, 1, abRawSector) ; /* add 10 for Disk device nums */ if (j==2) ercD12 = ere; else ercD13 = ere; if (!erc) { CopyData(&abRawSector[0x01fe] , &partsig, 2); /* It MUST have a partition table or we can't use it! */ if (partsig != 0xAA55) return ErcNoParTable; CopyData(&abRawSector[0x01be] , &partab[0] .fBootable, 64); Dump(&partab[0] .fBootable, 64) ReadKbd(&keycode, 1); if (partab[0) .nSectorsTotal > 0) < Ldrv[i].LBA0 =partab[0] .nFirstSector; /* lba for Start of LDrv (bootSect) */

|7qT| Part TV ■ The Operating System Source Code Listing 27.1. continued Ldrv[i] .LBAMax =partab[0] .nSectorsTotal; /* Max lba for logical drive */ Ldrv[i] .LBAMax =partab[0] .nSectorsTotal; /* Max lba for logical drive */ if (partab[0] .FATType > 3) Ldrv[i].fFAT16 = 1; Ldrv[i] .DevNum = j+10; if ((j==2) && (IfFoundl)) { GetBSInfo(2, 0); fFound1=1; } if ((j==3) && (!fFound2)) { GetBSInfo(3, 0); f Found2=1 ; } i++; /* if valid partition go to next LDrv */ } if (partabfl] .nSectorsTotal > 0) { Ldrv[i].LBA0 = partabt 1 ] .nFirstSector; Ldrv[ij .LBAMax = partab[1 ] .nSectorsTotal; if (partab[1]. FATType > 3) Ldrv[i].fFAT16 = 1; Ldrv[i] .DevNum = j+10; if ((j==2) && (IfFoundl)) { GetBSInfo(2, 1); fFound1=1; } if ((j==3) && (!fFound2)) { GetBSInfo(3, 1); f Found2=1 ; } i++; /* if we had a valid partition go to next */ } if (partab[2] .nSectorsTotal > 0) { Ldrv[i].LBA0 = partab[2] .nFirstSector; Ldrv[i] .LBAMax = partab[2] .nSectorsTotal; if (partab[ 2]. FATType > 3) Ldrv[i].fFAT16 = 1; Ldrv[i] .DevNum = j+10; if ((j==2) && (IfFoundl)) { GetBSInfo(2, 2); fFound1=1; } if ((j==3) && (!fFound2)) { GetBSInfo(3, 2); f Found2=1 ; } i++; /* if we had a valid partition go to next */ } if (partab[3] .nSectorsTotal > 0) { Ldrv[i].LBA0 = partab[3] . nFirstSector; Ldrv[i] .LBAMax = partab[3] .nSectorsTotal; if (partab[ 3] .FATType > 3) Ldrv[i].fFATl6 = 1; Ldrv[i] .DevNum = j+10; if ((j==2) && (IfFoundl)) { GetBSInfo(2, 3); fFound1=1; } if ((j==3) && (!fFound2)) { GetBSInfo(3, 3); fFound2=1; } i++; /* if we had a valid partition go to next */ } } }

Chapter 27 ■ File System Code if (ercD12) return ercD12; /* Device 13 may not exist */ else return 0; } Reads in the first boot sector from each physical drive to get drive geometry info not available in partition table. This includes number of heads and sectors per track. Then we call Devicelnit for each physical device to set its internal drive geometry. This must be done before we even try to read the other boot sectors if the disk has multiple partitions (otherwise it fails). static U32 SetDriveGeometry(U32 d) /* d is the device number (12 or 13) */ { U32 ere, i; if (d==12) { ere = DeviceStat(12, &HDDevStat, 64, &i); if (!erc) { HDDevStat.nHead = PDrvs[2] .nHeads; HDDevStat.nSectors = PDrvs[2] .nSecPerTrk; ere = Devicelnit(12, &HDDevStat, 64); /* Set up drive geometry */ } } if (d==13) { ere = DeviceStat (13, &HDDevStat, 64, &i) ; if (!erc) { HDDevStat.nHead = PDrvs[3] .nHeads; HDDevStat.nSectors = PDrvs[3] .nSecPerTrk; ere = Devicelnit(13, &HDDevStat, 64); /* Set up drive geometry */ } } return ere; } Read boot sector from logical drive (i) and sets up logical and physical drive array variables for the FAT file system found on the logical drive (described in the boot sector) . static U32 read_BS(U32 i) { U32 ere, j ; if (Ldrv[i] .DevNum != 0xff) { ] = Ldrv[i] .DevNum; /* j is MMURTL Device number */

I
606
Part PV ■ The Operating System Source Code
Listing 27.1. continued
ere = DeviceOp(j, 1, Ldrv[i] .LBA0, 1, abRawSector) ;
if ((erc==ErcNewMedia) && (i<2))
{
ere = DeviceOp(j, 1, Ldrv[i] .LBA0, 1, abRawSector);
}
CopyData( abRawSector, &fsb.Jmp, 62);
if (erc==0)
{
Ldrv[i].LBARoot
Ldrv[i]
Ldrv[i]
Ldrv[i]
Ldrv[i]
Ldrv[i]
Ldrv[i]
Ldrv[i]
Ldrv[i]
if (fsb
Ldrv[
. nRootDirEnt =
.SecPerClstr =
. nHeads
. nSecPerTrk -
.SFAT
.nFATS
. LBAFAT
.LBAData
. FileSysTypeH
i] .fFAT16 = 0;
fsb.ResSectors + Ldrv[i].LBA0 +
(fsb. FATs * fsb.SecPerFAT);
fsb.RootDirEnts; /* n Root dir entries */
fsb. SecPerClstr;
fsb. Heads;
fsb.SecPerTrack;
fsb.SecPerFAT; /* nSectors in a FAT */ fsb. FATs; /* number of FATs */
Ldrv[i].LBA0 + fsb.ResSectors; ■ Ldrv[i] .LBARoot + (fsb.RootDirEnts / 16); ] == ‘2’) } /* if ere */ } /* if valid logical device */ return 0; } l*i This gets the CMOS date & time and converts it into the format for the DOS FAT file system. This is two words with bits representing Year/Mo/day & Hr/Min/SecDIV2. static void GetFATTime(U16 *pTimeRet, U16 { U32 date, time; U16 DDate, DTime, w; ‘pDateRet) GetCMOSDate(&date); GetCMOSTime(&time) ; /* Do the date */ DDate = (((date » 12) & 0x0f) * w = (((date » 20) & 0x0f) * 10) DDate |= (w « 4) ; w = (((date » 28) & 0x0f) * 10) DDate != (w + 1900 - 1980) « 9; /* Do the time */ DTime = (((((time » 4) & 0x0f) w = (((time » 12) & 0x0f) * 10) DTime = (w « 5) ; w = (((time » 20) & 0x0f) * 10) DTime |= (w « 11 ) ; •pTimeRet = DTime; *pDateRet = DDate; I) + ((date » 8) & 0x0f) ((date»16) & 0x0f) + 2; ( (date » 24) & 0x0f ) ;
    • (time & 0x0f))/2) • ((time » 8) & 0x0f); ((time » 16) & 0x0f ); /* day */ /* month */ /* year */ secs/2 */ mins */ hours */

Chapter 27 ■ File System Code I /-q-t This updates a directory entry by reading in the sector it came from and placing the modifed entry into it then writing it back to disk. The date is also updated at this time. static U32 UpdateDirEnt(U32 iFCB) { U32 ere, i, j ; U8 Drive; Drive = paFCB[iFCB] ->Ldrv; /* What logical drive are we on? */ i = paFCB[iFCB] ->LBADirSect; /* Sector on disk */ j = paFCB[iFCB] ->oSectDirEnt; /* offset in sector */ /* update time in dir entry */ GetFATTime(&paFCB[iFCB] .Time, &paFCB[iFCB] .Date) ; /* Read sector into a buffer */ ere = DeviceOp(Ldrv[Drive] .DevNum, 1, i, 1, abDirSectBuf ) ; if (!erc) { CopyData(&paFCB[iFCB], &abDirSectBuf [ j ] , 32); ere = DeviceOp(Ldrv[Drive] .DevNum, 2, i, 1, abDirSectBuf); } return ere; } /♦♦♦A************************************************* Checks the validity of the a file handle and also returns the index to the FCB if the handle is OK. The function returns OK (0) if handle is good, else a proper error code is returned. static U32 ValidateHandle(U32 dHandle, U32 *iFCBRet) { /* get some checks out of the way first */ if (dHandle < 4) return ErcBadFileHandle; if (dHandle >= nFUBs) return ErcBadFileHandle; if ( !paFUB[dHandle] .Job) return ErcBadFileHandle; /* Looks like a valid handle */ *iFCBRet = paFUB[dHandle] ->iFCB; return 0; } Returns absolute disk address for the cluster number you specify. This gives us the LBA of the first sector of data that the cluster number represents. The sector number is returned from the function. Uses: Ldrv[CrntDrv] .LBAData Ldrv[CrntDrv] .SecPerClstr

/-qo I Part PV ■ The Operating System Source Code Listing 27.1. continued static U32 ClsToLBA(U16 Clstr, U8 Drive) { U32 LBA; Clstr-=2; /* Minus 2 cause 0 and 1 are reserved clusters */ LBA = Ldrv[Drive] .SecPerClstr * Clstr; LBA += Ldrv [ Drive ] .LBAData; return LBA; } This writes out the specified FAT sector back into the FAT. It also checks to see if there is more than one copy of the fat and updates the second copy if it exists. Static U32 UpdateFAT(U32 iFAT) { U32 ere, i, k; U8 Drive; ere = 0; if ( Fat [ iFAT] .fModLock & FATMOD) { /* Modified?? */ Drive = Fat [iFAT] .Drive; /* What logical drive are we on? */ i = Fat [ iFAT ] .LBASect; /* Where to write it back */ if (!iFAT) { /* This is the floppy buffer [0] */ /* set up to write up to 3 sectors from the buffer */ if (i+2 < Ldrv [ Drive ] .sFAT + Ld rv[ Drive ] .LBAFAT) k = 3; else if (i+1 < Ldrv [ Drive] .sFAT + Ldrv [Drive] .LBAFAT) k = 2; else k = 1; } else k=1; ere = DeviceOp(Ldrv[Drive] .DevNum, 2, i, k, Fat [ iFAT] .pBuf) ; if (lerc) { Fat [iFAT]. fModLock &= -FATMOD; /* Not modified anymore */ if (Ldrv [Drive] .nFATS > 1) { /* 2 FATS! */ /* if we have two FATS we must update the second fat also. This will be located directly aftrer the first FAT (by exactly LDrv.sFat sectors). */ i+= Ldrv[Drive] .sFAT; ere = DeviceOp(Ldrv[Drive] .DevNum, 2, i, k, Fat [ iFAT ] .pBuf) } }

Chapter 27 H File System Code 609 } return ere; } Reads in the FAT sector that contains the Cluster we specified into a FAT buffer if it isn't already in one. The index to the FAT buffer is returned. Returns Error if not in FAT. Uses: Ldrv[LDrive] .LBAFAT Ldrv[LDrive] .fFAT16 Ldrv[LDrive] .DevNum Each sector of the FAT contains 256 cluster entries for FAT16 types. To find it, we Divide the cluster number by the count of entries (256), and add this to the beginning sector of the FAT. It is S000 important (for speed) to have the FAT sectors in memory, that we allocate the FAT buffers on a Least Recently Used (LRU) basis for hard disk drives. It's more complicated for a FAT12 types (floppies) because cluster entries span fat sectors (they have an odd number of nibbles). For this reason, we have one 3 sector fat buffer for fat12 devices (floppies). We fill it with up to 3 sectors. This is because the last entry may span the sectors and we must be able to read it. There are 1024 cluster entries in a FAT12 3 sector buffer. static U32 FindFatSect(U8 Drive, U16 Clstr, U32 piFatRecRet , U8 fLock) { U32 i, j, k; U32 first, oSector, ere, LRU, iLRU, iFound, Tick; U16 MaxClstr; if (Ldrv[Drive].fFAT16) MaxClstr = 0xfff8; else MaxClstr = 0xff8; / FAT12 / if (Clstr >= MaxClstr) return(ErcEOF) ; if (Clstr < 2) { return (ErcBadFATClstr) ; } GetTimerTick(&Tick) ; ere = 0; / default to no error / / Set oSector to offset of sector in FAT There are 256 cluster entries in 1 sector of a FAT16, and 1024 in a FAT12 (3 sectors) */

H Part PV ■ The Operating System Source Code Listing 27.1. continued if (Ldrv[Drive].fFAT16) { oSector = Clstr/256; first = Clstr-(Clstr%256); /* Set i to LBA of FAT sector we need by adding offset to beginning of FAT */ i = oSector + Ldrv [ Drive ] .LBAFAT; /* If FAT sector is out of range there's a BAD problem... */ if (i >= Ldrv [ Drive] .sFAT + Ldrv [Drive] .LBAFAT) { return (ErcBadFATClstr) ; } else { /* Else we get it for them */ /* Loop through the Fat bufs and see if it's in one already. */ /* Save the index of the LRU in case it's not there. */ /* Set iFound to index of FatBuf (if found). */ /* Otherwise, Set up iLRU to indicate what the oldest buffer is */ iFound = Oxffffffff; LRU = 0xffffffff; /* saves tick of oldest one so far */ iLRU =1; /* default */ for (j=1; j<nFATBufs; j++) { if (Fat[j].LastUsed > 0) { /* Valid ? (ever been used) */ if ((first == Fat[j] .iClstrStart) && (Drive == Fat[j ] .Drive) ) { iFound = j ; if (fLock) Fat[j].fModLock J= FATLOCK; break; /* Already IN! */ } } if (Fat[j].LastUsed < LRU) { LRU = Fat[j].LastUsed; iLRU = j; } } if (iFound != Oxffffffff) { /* Its already in memory */ Fat[j].LastUsed ■ Tick; /* update LRU */ } else { /* else put into oldest buffer */ j = iLRU; /* Check to see if Fat [iLRU] is valid and has been

Chapter 27 ■ File System Code modified. If it is, write it out before we read the next one into this buffer. This done by calling UpdateFAT(iFatRec) . / if (Fat[j).fModLock & FATMOD) ere = UpdateFAT( j ) ; if (!erc) { ere = DeviceOp(Ldrv[Drive] .DevNum, 1, i, 1, Fat [ j ] .pBuf ) ; Fat[j]. Drive = Drive; / Update Drive / Fat[j].LastUsed = Tick; / update LRU / Fat[ j ] .iClstrStart = first; / update first cluster num / Fat[ j ] .LBASect = i; / LBA this FAt sect came from / } } / This is for FAT12s / else { oSector = (Clstr/1024) * 3; / X3 cause we read 3 at a time / first = Clstr-(Clstr%1024); / Set i to LBA of FAT sector we need by adding offset (oSector) to beginning of FAT / i = oSector + Ldrv [ Drive ] .LBAFAT; j = 0; / If FAT sector is out of range there's a BAD problem... / if (i >= Ldrv[Drive] .sFAT) return (ErcBadFATClstr) ; else { / Else we get it for them / / Check the single floppy fat buf and see if it's already there. / / Set iFound to index of FatBuf (if found). / iFound = 0xffffffff; if (Fat[0].LastUsed > 0) { / Valid ? (nonzero means it's been used) */ if ((first == Fat[0] . iClstrStart) && (Drive == Fat[0] .Drive) ) { iFound = 0; if (fLock) Fat[0].fModLock [= FATLOCK; } if (iFound == 0xffffffff) { 0

612 Part TV ■ The Operating System Source Code Listing 27.1. continued /* It's not the one we want or isn't there. Check to see if Fat[0] is valid and has been modified. If it is, write it out before we read the one we want into the buffer. This done by calling UpdateFAT(iFatRec) . */ if (Fat[0].fModLock & FATMOD) ere = UpdateFAT(0) ; /* set up to read upto 3 sectors into buffer */ if (i+2 < Ldrv [ Drive ] .sFAT + Ldrv [ Drive ] .LBAFAT) k = 3; else if (i+1 < Ldrv [ Drive ] .sFAT + Ldrv [Drive] .LBAFAT) k = 2; else k = 1; if (!erc) { ere = DeviceOp(Ldrv[Drive] .DevNum, 1, i, k, Fat[0] .pBuf ) ; Fat[0]. Drive = Drive; /* Update Drive */ Fat[0].LastUsed = Tick; /* update LRU */ Fat[0] .iClstrStart = first; /* update first cluster num Fat[0] .LBASect = i; /* LBA this FAT sect came from */ } } *piFatRecRet = j; /* Buffer that holds the sector(s) */ return (ere); /* Disk error Bad news */ } Returns the value found for this cluster entry in a fat sector buffer. Values can be: FAT16 FAT12 Next entry in the chain (0002-FFF0 (002-FF0) Last entry in chain (FFF8 )(FF8 ) Available cluster (0 )(0 ) Bad Cluster (FFF7 )(FF7 ) (other values are reserved). static U32 GetClstrValue(U16 Clstr, U8 Drive, U8 fLock, U16 *pValRet, U32 *iFatBufRet) { U32 ere, oClstr, iFat; U16 ClstrVal, *pClstr; ere = FindFatSect (Drive, Clstr, &iFat , fLock); if (ere)

Chapter 27 H File System Code |
613 pValRet= 0; return(erc) ; pClstr = Fat[iFat].pBuf ; oClstr = Clstr - Fat[iFat] .iClstrStart ; / offset into FatBuf / if (Ldrv[Drive].fFAT16) { / if drive is FAT16 type / pClstr += oClstr * 2; / WORDS in / ClstrVal = pClstr; } / FAT12 entries are 1.5 bytes long (what a pain). This means we get the offset and see whether it is an odd or even byte, then take the proper nibble by ANDing or shifting. / else { / a FAT12... / pClstr += oClstr + (oClstr/2); / 1.5 bytes in / ClstrVal = pClstr; / We have 16 bits / if (Clstr & 1) / Odd, must shift / ClstrVal »= 4; ClstrVal &= 0xfff; } pValRet= ClstrVal; iFatBufRet = iFat; return(erc) ; k******************************************* Sets the value in Clstr to the value in NextClstr which will be one of the following values: FAT16 FAT12 Next entry in the chain (0002-FFEF (002-FEF) Last entry in chain (FFFF )(FFF ) Available cluster (0 )(0 ) Bad Cluster (FFF7 )(FF7 ) (other vlaues are reserved). This marks the associated fat buffer as modified. This is the ONLY call that modifies a FAT buffer! static U32 SetClstrValue(U16 Clstr, U16 NewClstrVal, U8 Drive, U32 *iFatBufRet) { U32 ere, oClstr, iFat; U16 ClstrVal, *pClstr, ClstrSave; ere = FindFatSect(Drive, Clstr, &iFat, 0); if (ere) { *iFatBufRet = 0; return(erc) ; }

I
614 Part PV ■ The Operating System Source Code Listing 27.1. continued pClstr = Fat[iFat] .pBuf ; oClstr = Clstr - Fat[iFat] .iClstrStart; /* offset into FatBuf*/ if (Ldrv[Drive].fFAT16) { /* if drive is FAT16 type */ pClstr += oClstr * 2; /* WORDS in */ *pClstr = NewClstrVal; } /* FAT12 entries are 1.5 bytes long (remember??). SAVE THE CORRECT NIBBLE OF THE ADJACENT CLUSTER!! */ else {
/*
a FAT12. ..
*/ pClstr += oClstr + (oClstr/2); /* 1.5 bytes in */ ClstrSave = *pClstr; /* We have 16 bits */ if (Clstr & 1) { /* Odd, must shift */ NewClstrVal «= 4; NewClstrVal &= 0xfff0; ClstrVal = (ClstrSave & 0x0F) | NewClstrVal; } else { NewClstrVal &= 0x0fff; ClstrVal = (ClstrSave & 0xf000) J NewClstrVal; } *pClstr = ClstrVal; } Fat[iFat] .fModLock j= FATMOD; *iFatBufRet = iFat; return(erc) ; Read the FAT and get the cluster number for the next cluster in the chain for the Clstr specifed. This returns 0 and an error if failed. 0 is an illegal cluster number. Remember, the cluster you are on is actually the number of the next cluster in a linked list! static U32 NextFATClstr(U8 Drive, U16 Clstr, U16 *pNextClstrRet ) { U32 ere, ij U16 NextClstr; ere = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i); if (ere) { •pNextClstrRet = 0; return(erc) ; } *pNextClstrRet ■ NextClstr; return(0) ;

Chapter 27 ■ File System Code |
615 This allocates the next empty cluster on the disk to the tail of the clstr that is passed in. LastClstr is a valid cluster of a file or directory (and MUST be the last one). We error out if it isn't! This returns 0 and an error if it fails. Remember, the cluster you are on is actually the number of the next cluster in a linked list! This looks through the current and successive FAT sectors (if needed) to add to the file. A cluster is available to allocate if it is 0. This is strictly a first fit algorithm. static U32 ExtendClstrChain(U8 Drive, U16 LastClstr, U16 pNextClstrRet) { U32 ere, i, j , k; U16 ClstrValue, MaxClstr, CrntClstr; U8 f Found; if (Ldrv[Drive].fFAT16) MaxClstr = 0xfff8; else MaxClstr = 0xff8; / FAT12 / / i is index to Fat with last sector of current chain */ ere = GetClstrValue(LastClstr, Drive, 1, &ClstrValue, &i); if (ere) pNextClstrRet return(erc) ; if (ClstrValue < MaxClstr) { / no need to extend it */ pNextClstrRet = ClstrValue; Fat[i].fModLock &= -FATLOCK; / unlock it / return(0) ; } / OK... now we have the Fat sector and the offset in the Fat buf of the last cluster allocated to this file. Let's go further into the buffer and try to get an empty one. / CrntClstr = LastClstr; fFound = 0; while (IfFound) { ++CrntClstr; / next cluster */ ere = GetClstrValue(CrntClstr, Drive, 0, &ClstrValue, &j ) ; if (ere) { *pNextClstrRet = 0;

/-I,’ Part PV ■ The Operating System Source Code Listing 27.1. continued Fat[i] .fModLock &= -FATLOCK; /* unlock previous lastclstr */ return(erc) ; } if (IClstrValue) { f Found =1; /* found an empty one */ if (fFound) { /* CrntClstr is index to empty one */ /* Set the LastCluster to point to the new cluster found */ ere = SetClstrValue(LastClstr, CrntClstr, Drive, &k); if (ere) { *pNextClstrRet = 0; Fat[i] .fModLock &= -FATLOCK; /* unlock previous lastclstr */ return(erc) ; } Fat[k]. fModLock &= -FATLOCK; /* unlock it */ /* Set the newcluster to “end Cluster” chain value */ ere = SetClstrValue(CrntClstr, 0xFFFF, Drive, &j ) ; } *pNextClstrRet = CrntClstr; return(erc) ; } This truncates the file chain to the cluster specified (makes it the last cluster). This means we walk the rest of the chain setting all the entries to 0 (so they can be reallocated). This returns an error if failed. static U32 TruncClstrChain(U8 Drive, U16 Clstr) { U32 ere, i; U16 MaxClstr, NextClstr, CrntClstr; if (Ldrv[Drive].fFAT16) MaxClstr = 0xfff8; else MaxClstr = 0xff8; /* FAT12 */ /* i will be index to FatRec with last sector of current chain */ ere = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i); if (ere) return(erc) ; if (NextClstr >= MaxClstr) { /* no need to truncate it */

Chapter 27 ■ File System Code E return(0); /* It's already the end. */ } /* OK... now we cut it off all the way down the chain. We start by placing MaxClstr in the last sector and then 0 in all entries to the end of the chain. */ ere = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i); if (ere) return(erc) ; ere = SetClstrValue(Clstr, 0xFFFF, Drive, &i) ; /* new end of chain */ if (ere) return(erc) ; while ((NextClstr) && (NextClstr < MaxClstr)) { CrntClstr = NextClstr; ere = GetClstrValue(CrntClstr, Drive, 0, &NextClstr, &i); if (ere) return(erc) ; ere = SetClstrValue(CrntClstr, 0, Drive, &i); /* Free it up */ if (ere) return(erc) ; } /* DONE! */ return(0) ; } This finds the absolute cluster you want from the LFA in a particular file. The file handle must already be validated! It also returns the relative LFA of the beginning of this cluster. static U32 GetAbsoluteClstr(U32 dHandle, U32 dLFA, U16 *pClstrRet, U32 *prLFARet) { U32 ere, iFCB, spc, bpc, rLFA; U16 rClstrWant, rClstrNow, Clstr, MaxClstr; U8 Drive; iFCB = paFUB [ dHandle ]->iFCB; Drive = paFCB[iFCB] ->Ldrv; /* What logical drive are we on? */ spc = Ldrv[Drive] .SecPerClstr; /* sectors per cluster */ bpc = spc * 512; /* bytes per cluster */ if (Ldrv[Drive].fFAT16) MaxClstr = 0xfff8; else MaxClstr = 0xff8; /* FAT12 */ /* Calculate
relative
by
dividing
cluster
size
in
bytes
by
dLFA. If
zero,
we
want
the
1st
cluster
which
is
listed
in
the
FCB.

Part PV ■ The Operating System Source Code Listing 27.1. continued If it is greater than zero, we have to “walk the FAT cluster chain” until we reach the one we want, then read it in. The FUB fields LFAClstr and Clstr store the file LFA of the last cluster in this file that was read or written. This means if the LFA is higher than the last read or written, we don't waste the time reading the whole chain. We start from where we are. The major difference is we may not be reading the first sector in the cluster. We figure this out from the dLFA as compared to LFAClstr. 7 rClstrWant = dLFA / bpc; rClstrNow = paFUB[dHandle] ->LFAClstr / bpc; /* Relative clstr they want */ /* Rel ‘Clstr’ in FUB */ if (rClstrWant < rClstrNow) { /* Is it earlier in the file? Clstr = paFCB[iFCB]->StartClstr; rClstrNow = 0; rLFA = 0; } else { Clstr = paFUB[dHandle]->Clstr; rLFA = paFUB[dHandle] ->LFAClstr; } /* Yes, start at the beginning */ /* No, start at current cluster */ /* LFA of this cluster */ /* We need to run the cluster chain if rClstrNow < ClstrWant */ while ((rClstrNow < rClstrWant) && (Clstr < MaxClstr) && (Clstr)) { /* A valid cluster */ ere = NextFATClstr(Drive, Clstr, &Clstr) if (ere) return(erc) ; ++rClstrNow; rLFA += bpc; } /* haven't reach it yet */ /* Not last cluster */ if (rClstrNow != rClstrWant; return ErcBrokenFile; *pClstrRet = Clstr; *prLFARet = rLFA; return(0) ; Cluster chain appears broken. SetFileSize sets the FileSize entry in the FCB for the handle specified. This means we will allocate or deallocate clusters as necessary to satisfy this request. The file MUST be open in MODE MODIFY. This must be done before a file can be written to beyond current FileSize (Block and Stream types).

Chapter 27 ■ File System Code 619 static U32 SetFileSizeM(U32 dHandle, U32 dSize) { U32 ere, i, iFCB, rLFA; U32 CrntSize, nCrntClstrs, spc, bpc, nClstrsWant; U16 Clstr; U8 Drive; ere = ValidateHandle(dHandle, &iFCB); if (ere) return ere; if (!paFCB[iFCB]->Mode) return ErcReadOnly; Drive = paFCB[iFCB] ->Ldrv; spc = Ldrv[Drive] .SecPerClstr; bpc = spc * 512; /* What logical drive are we on? */ /* sectors per cluster */ /* bytes per cluster */ /* Looks like it's valid to change the size */ CrntSize = paFCB[iFCB] ->FileSize; if (CrntSize return(0) ; dSize) nCrntClstrs = CrntSize/bpc; if (CrntSize%bpc) nCrntClstrs++: /* No need to do anything! /* nClusters currently */ if (ICrntSize) nCrntClstrs : nClstrsWant = dSize/bpc; if (dSize%bpc) nClstrsWant++; /* ZERO length files have 1 Clstr! */ /* nClusters they we need */ if (idSize) nClstrsWant /* ZERO length files have 1 Clstr! */ if (nClstrsWant == nCrntClstrs) ere = 0; else if (nClstrsWant > nCrntClstrs) { /* Need to extend allocation */ /* get the last cluster in the file */ ere = GetAbsoluteClstr(dHandle, CrntSize, &Clstr, &rLFA) ; i = nCrntClstrs; while ((!erc) && (i < nClstrsWant)) { ere = ExtendClstrChain(Drive, Clstr, &Clstr); i++; } } else if (nClstrsWant < nCrntClstrs) { /* Need to truncate it */

H Part IV The Operating System Source Code Listing 27.1. continued /* Get to cluster where it should be truncated */ ere = GetAbsoluteClstr(dHandle, dSize, &Clstr, &rLFA) if (!erc) ere = TruncClstrChain(Drive, Clstr); /* Now we must ensure that the cluster helper is NOT beyond EOF! */ if (paFUB->LFAClstr > dSize) { paFUB->LFAClstr = 0; paFUB[dHandle] ->Clstr = paFCB[iFCB] ->StartClstr; } } if (!erc) { paFCB[iFCB] ->FileSize = dSize; paFCB[iFCB]->fMod = 1; } return ere; This searches a directory beginning at Clstr for pName and returns a pointer to the 32 byte dir entry which is in a temporary buffer. If not found, returns NIL. When searching directories, if the filename begins with a NULL the search need go no further! static U32 GetDirEnt(U8 *pName, U8 Drive, U16 Clstr, U32 *pLBARet, U32 *poEntRet, U8 **pEntRet) { unsigned long sector, i, j, k, ere; U8 fFound, fEnd, *pEnt, *pStart; U16 MaxClstr; j = Ldrv[Drive] .SecPerClstr; sector = ClsToLBA(Clstr, Drive); if (Ldrv[Drive].fFAT16) MaxClstr = 0xfff8; else MaxClstr = 0xff8; /* FAT12 i = 0; fEnd=0; fFound=0; /* How many sectors per cluster */ /* absolute sector of first dir sector fFound= 0; while ((ifFound) && (IfEnd)) { /* while there are valid entries */

Chapter 27 U File System Code H if (i==j) { /* reached last dir sector of this cluster */ ere = NextFATClstr(Drive, Clstr, &Clstr); if (!erc) { if (Clstr >= MaxClstr) /* last sector */ retum(ErcNoSuchFile) ; /* not found */ sector = ClsToLBA(Clstr, Drive); /* LBA of next dir sector */ i=0; } else { *pEntRet = 0; return(erc) ; } } ere = DeviceOp (Ldrv[ Drive ] .DevNum, 1, sector++, 1, abDirSectBuf ) ; if (ere) return(erc) ; ++i; /* next sector in cluster */ pEnt = &abDirSectBuf [0] ; pStart = pEnt; for (k=0; k<16; k++) { /* 16 entries per sector */ if (*pEnt==0) { /* 0 in a DirEnt stops search */ fEnd=1; break; } if (CompareNCS(pEnt, pName, 11) == -1) { f Found=1 ; ♦pLBARet = sector- 1; /* tell em what LBA of DirEnt */ *poEntRet = pEnt-pStart; /* Tell em offset in LBA */ break; } pEnt+=32; /* 32 byte per entry */ } } if (fFound) { *pEntRet = pEnt; return(0) ; } else return (ErcNoSuchFile) ; This searches the ROOT directory for pName and returns a pointer to the 32 byte entry which is in a temporary buffer. If not found, returns NIL;

s-jy Part TV ■ The Operating System Source Code Listing 27.1 . continued When searching directories, if the filename begins with a NULL the search need go no futher! static U32 GetRootEnt(U8 *pName, U8 Drive, U32 *pLBARet, U32 *poEntRet, U8 **pEntRet) { unsigned long i, j, k, ere; U8 fFound, fEnd, *pEnt, pStart; i = Ldrv [ Drive ] .LBARoot; j = Ldrv[Drive] .nRootDirEnt; fFound = 0; fEnd = 0; while ((j) && (IfFound) && (!fEnd)) { / while there are valid entries */ ere = DeviceOp ( Ldrv [ Drive ] .DevNum, 1, i++, 1, abRawSector) ; if (ere) return(erc) ; pEnt = abRawSector; pStart = pEnt; for (k=0; k<16; k++) { if (pEnt==0) { / 0 in a DirEnt stops search */ fEnd=1; break; } if (CompareNCS(pEnt, pName, 11) == -1) { f Found=1 ; pLBARet = 1-1 j / tell em what LBA of DirEnt */ poEntRet = pEnt-pStart; / Tell em offset in LBA */ break; }

    • j ; /* one less dir ent */ pEnt+=32; /* 32 byte per entry */ } } if (fFound) { *pEntRet = pEnt; return(0) ; } else return (ErcNoSuchFile) ; }

Chapter 27 ■ File System Code I 623 This builds a full file specification from pName and places it in pDest based on the path from iJob. cbDestRet is set to size. static void BuildSpec(char *pName, long cbName, char *pDest, long cbDestRet, long iJob) { long i; if ((cbName) && (pName) && (pName[1] == ‘:')) { / Do NOT use path / CopyData(pName, pDest, cbName); i = cbName; } else { / Use path as prefix */ i = 0; GetPath(iJob, pDest, &i); if ((cbName) && (pName)) { CopyData(pName, &pDest[i], cbName); i += cbName; } } ♦cbDestRet = i; } The parses out the path name into directories, filename, and extension (Example): C:\TEMP\TEST.TXT (with TEMP being a dir in the root). This also uses the current path for the job to build the filename. If the name starts with \ or “DRIVE:” then the path from the JCB is NOT used. Otherwise the pName is concatenated to the job's path. SpecDepth is set to the level of the last valid 11 character string (0-6). static U32 ParseName(U8 pName, U32 cbName, U32 iJob) { unsigned long i, j, k, ere; U8 c, pPart; char Spec[70] ; U32 cbSpec; ere = 0; FDrive = 0; FillData(FileSpec, (711), ' ‘); / Fill parse table with spaces */

624 I Part TV ■ The Operating System Source Code Listing 27.1. continued BuildSpec(pName, cbName, Spec, &cbSpec, iJob); j = 0; /* index into crnt part of spec */ k = 0; /* index into crnt tree level */ pPart = Spec; for (i=0; i < cbSpec; i++) { switch (c = *pPart++) { case 0x5c : /* ' \ ' separates dir or fname */ if (j>0) { /* if it's not the first one */ ++k; } break; case ' : ' : if ((j==1) && (k==0) && (FDrive==0)) { FDrive = FileSpec[0] [0] & 0xdf; /* Make drive Upper*/ FileSpec[0][0] = ' ' ; j=0; /* back to beginning of part */ k=0; } else ere = ErcBadFileSpec; break; case ‘.’ : /* . can only appear once in dir or fname */ if (j>8) ere = ErcBadFileSpec; else j=8; /* move to extension */ break; case

/* not allowed in spec */ case < case , case + case | case ] case [ case + case

case @ case * case ‘?’ ere = ErcBadFileSpec; break; default /* make chars upper */ if (j>10) ere = ErcBadFileSpec; else { if (((c >= ‘A’) && (c <= ‘Z’)) | ; ((c >= ‘a’) && (c <= ‘z’))) c 4= 0xdf; FileSpec[k) [ j ] = c; } b ^e 3k:

Chapter 27 ■ File System Code if (ere) break; /* bad news. Exit for loop } SpecDepth = k; return ere; } /*** Get Directory Sector ******************************* Gets a 512 byte directory sector in sequence number from the directory path given. SectNum = 0 to nMax for Dir. This also returns the LBA for sector itself for internal users of this call. static U32 GetDirSectorM(char *pPath, long cbPath, char *pSectRet, long cbRetMax, long SectNum, long *LBARet, U16 *ClstrRet, long iJob) { U32 sector, i, j, k, ere, spc, level, iSect; U16 MaxClstr, Clstr, rClstr; U8 fFound, *pEnt, Drive; if (cbRetMax > 512) /* WHOA Bub, 1 Sector at a time! */ CbRetMax =512; ere = ParseName(pPath, cbPath, iJob); /* The entire path has now been parsed out into an array of arrays, each 11 bytes long that contain each directory name in the path for the sector they want. The first is always the root (entry 0). The drive will be a letter in FDrive. */ if ((FDrive > 0x40) && (FDrive < 0x52)) /* A to J */ Drive = FDrive - 0x41; /* Make it 0-9 */ else return(ErcNoSuchDrive) ; if (Drive < 2) { StatFloppy(Drive) ; erc= readBS(Drive) ; } if (Ldrv[Drive] .DevNum == 0xff) return(ErcNoSuchDrive) ; i = Ldrv [ Drive ] .LBARoot; j = Ldrv[Drive] .nRootDirEnt;

I
626 Part PV ■ The Operating System Source Code Listing 27.1. continued if (FileSpec[0][0] == ' ' ) { /* They want sector in root */ if (SectNum > j/32) /* Beyond Root entries! */ return(ErcNoMatch) ; /* Else we can give them the sector NOW */ ere = DeviceOp (Ldrv[ Drive ] .DevNum, 1, i+SectNum, 1, abRawSector) ; if (!erc) { *LBARet = i+SectNum; CopyData(abRawSector, pSectRet, cbRetMax); } return(erc) ; } /* We have to run the root for a dir name... */ fFound = 0; while ((j) && (!f Found)) { /* while there are valid entries */ ere = DeviceOp(Ldrv[Drive] .DevNum, 1, i++, 1, abRawSector); if (ere) return(erc) ; pEnt = abRawSector; /* Point to first entry */ for (k=0; k<16; k++) { if (CompareNCS(pEnt, FileSpec[0], 11) == -1) { f Found=1 ; break; }

    • j ; /* one less dir ent */ pEnt+=32; /* 32 byte per entry */ } } if (IfFound) return (ErcNoMatch) ; pDirEnt = pEnt; /* Entry we just found in root was dir */ if (!(pDirEnt->Attr & DIRECTORY)) { return(ErcNoSuchDir) ; } if (Ldrv[Drive].fFAT16) MaxClstr = 0xfff8; else MaxClstr = 0xff8; /* FAT12 */ spc = Ldrv[Drive] .SecPerClstr; /* How many sectors per cluster */ Clstr = pDirEnt->StartClstr; level =1; /* start at this directory+1 , compare to FileSpec */ while (!erc) { /* looking for Dir */

Chapter 27 ■ File System Code R if (FileSpec[level][0] == ' ' ) { /* They want sector in this dir */ if (!(pDirEnt->Attr & DIRECTORY)) { return(ErcNoSuchDir) ; } rClstr = SectNum /spc; /* calc relative cluster from start clstr */ iSect = SectNum % spc; /* Add this to cluster start for sector */ sector = ClsToLBA(Clstr, Drive); /* sector of first dir sector */ while ((rClstr--) && (!erc)) ere = NextFATClstr(Drive, Clstr, &Clstr); if (ere) return(erc) ; sector = ClsToLBA(Clstr, Drive); /* LBA of this clstr */ sector += iSect; ere = DeviceOp(Ldrv[Drive] .DevNum, 1, sector, 1, abRawSector) ; if (!erc) { CopyData(abRawSector, pSectRet, cbRetMax); *LBARet = sector; *ClstrRet = Clstr; } return(erc) ; } else { /* Else we must find this sub dir name */ sector = ClsToLBA(Clstr, Drive); /* sector of first dir sector */ fFound=0; while (IfFound) { /* while there are valid entries */ if (i==spc) { /* reached last dir sector of this cluster */ ere = NextFATClstr(Drive, Clstr, &Clstr); if (!erc) { if (Clstr >= MaxClstr) /* last sector */ return(ErcNoSuchFile) ; /* not found */ sector = ClsToLBA(Clstr, Drive); /* LBA of next sector */ i=0; } else return(erc) ; } ere = DeviceOp(Ldrv[Drive] .DevNum, 1, sector++, 1, abRawSector); if (ere) return(erc) ; i++; /* Next sector in this cluster */ pEnt = &abRawSector[0] ; for (k=0; k<16; k++) { /* 16 entries per sector */

H Part PV ■ The Operating System Source Code Listing 27.1. continued if (CompareNCS(pEnt, FileSpec[level] , 11)== { f Found=1 ; break; } pEnt+=32; /* 32 byte per entry */ } pDirEnt = pEnt; /* Entry we just found */ Clstr = pDirEnt->StartClstr; /* Clstr @ start of dir entry */ } ++level; /* next level of parsed filespec */ } return (ere); } This is the BLOCK read for the MMURTL DOS file system. It reads whole sectors and returns them to pBytesRet. There are NO internal filesystem buffers for this call. Data is returned directly to your buffer from the Disk. This is the fastest method for reading a file. This is also used internally to fill a stream buffer. static U32 ReadBlockM(U32 dHandle, U8 *pBytesRet, U32 nBytes, U32 dLFA, U32 *pdBytesRet, U8 fFill) /* TRUE if filling a stream buffer */ { U32 ere, j, LBA, iFCB, bpc, spc, nDone, rLFA, nLeft, nBlks; U16 Clstr, MaxClstr, ClstrSav; U8 Drive; ere = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */ if (ere) return ere; /* Certain FUB fields have different meanings in stream */ if ((paFUB[dHandle] .fStream) && (If Fill)) { *pdBytesRet = 0; return ErcStreamFile; } /* set these up in advance */ nBlks = nBytes/512; /* nBytes MUST be multiple of 512 */ Drive = paFCB[iFCB] ->Ldrv; /* What logical drive are we on? */ spc = Ldrv[Drive] .SecPerClstr; /* sectors per cluster */ bpc ■ 512 * spc; /* Bytes per cluster */ if (Ldrv[Drive].fFAT16) MaxClstr = 0xfff8; else MaxClstr = 0xff8; /* FAT12 •/

Chapter 27 ■ File System Code I* Call to find the absolute cluster on the the logical disk and also the relative LFA of the cluster in question. ere = GetAbsoluteClstr(dHandle, dLFA, &Clstr, &rLFA) ; LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster */ /* Now LBA equals beginning of cluster that dLFA resides in. We must see which sector in Clstr is the starting LBA. To do this we MOD (dLFA in sectors) by (sectors per cluster) and add the leftover amount (should be 0 to nSPC-1) to LBA before we read. For example: if dLFA was 2560 and spc was 4, this would be 5 % 4 = 1 . We would add 1 to the LBA. This is only done for the first read in the loop. We also set nleft which is how many sectors are left in the current cluster we are reading from. */ LBA += (dLFA/512) % spc; nLeft = spc - ((dLFA/512) % spc); nDone = 0; while ((nBlks) && (lerc)) { /* while buffer isn't full and no error */ if (nBlks > nLeft) j = nLeft; else j = nBlks; paFUB[dHandle]->Clstr = Clstr; paFUB[dHandle]->LFAClstr = rLFA; /* Save Current cluster */ /* Save LFA for Clstr in FUB */ ere = DeviceOp(Ldrv[Drive] .DevNum, 1, LBA, j, pBytesRet); if (ere) break; pBytesRet += j * 512; /* further into their buffer */ nBlks -= nLeft -= nDone += if ((nBlks) && (InLeft)) { /* current cluster has none left */ nLeft = spc; ClstrSav = Clstr; ere = NextFATClstr(Drive, Clstr, &Clstr); /* next FAT cluster */ if (ere) { *pdBytesRet = nDone*512; return(erc) ; } rLFA += bpc; /* Update rel LFA of new cluster*/ if (Clstr >= MaxClstr) ere = ErcEOF; /* Last cluster */ if (IClstr) ere = ErcBrokenFile; /* No good next cluster! */

I /-oq I Part PV ■ The Operating System Source Code Listing 27.1 . continued LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster*/ } } *pdBytesRet = nDone*5l2; return ere; /* WE'RE DONE, return the error (if any) */ } /*** Write Block *************************************** This is the BLOCK write for the MMURTL FAT file system. dLFA must be the LFA of a valid portion of the file and nBlks must not extend beyond the last cluster allocated to the file, in other words, you must call SetFileSize first. In a block write dLFA is always written on a sector boundry. We must make sure that the filesize will accomodate the sectors we want to write! static U32 WriteBlockM(U32 dHandle, char *pData, U32 nBytes, U32 dLFA, U32 *pdnBytesRet) { U32 ere, i, j, LBA, iFCB, bpc, spc, nDone, rLFA, nLeft, nBlks; U16 Clstr, MaxClstr; U8 Drive; ere = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */ if (ere) return ere; nBlks = nBytes/512; dLFA = (dLFA/512)*512; /* round LFA down to nearest sector */ if (!paFCB[iFCB]->Mode) /* Is it open in Modify?? */ return(ErcReadOnly) ; i = (paFCB[iFCB]->FileSize/5l2); /* Set i nBlks in file max */ if (paFCB[iFCB]->FileSize%512) i++; j = (dLFA/512) + nBlks; /* blocks to write past dLFA*/ if (j > i) return(ErcBeyondEOF) ; /* It seems OK to write the blocks out, so now let's DO IT! */ Drive = paFCB[iFCB] ->Ldrv; /* What logical drive are we on? */ spc = Ldrv[Drive] .SecPerClstr; /* sectors per cluster */ bpc = 512 * spc; /* Bytes per cluster */ if (Ldrv[Drive].fFATl6) MaxClstr = 0xfff8; else MaxClstr = 0xff8; /* FAT12 */ ere = GetAbsoluteClstr(dHandle, dLFA, &Clstr, &rLFA) ; if (ere)

(Chapter 27 ■ File System Code return(erc) ; LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster */ /* Now LBA equals beginning of cluster that dLFA resides in. We must see which sector in Clstr is the starting LBA. To do this we MOD (dLFA in sectors) by (sectors per cluster) and add the leftover amount (should be 0 to nSPC-1) to LBA before we write. For example: if dLFA was 2560 and spc was 4, this would be 5 % 4 = 1 . We would add 1 to the LBA. This is only done for the first write in the loop. We also set nleft which is how many sectors are left in the current cluster we are writing to. */ LBA += (dLFA/512) % spc; nLeft = spc - (dLFA/512) % spc; nDone = 0; while ((nBlks) && (!erc)) { /* while blocks are left to write */ if (nBlks > nLeft) j = nLeft; else j = nBlks; paFUB[dHandle] ->Clstr = Clstr; /* Save Current cluster */ paFUB[dHandle]->LFAClstr = rLFA; /* Save LFA for Clstr in FUB */ ere = DeviceOp(Ldrv[Drive] .DevNum, 2, LBA, j, pData); if (ere) break; pData += (j * 512); /* Update address */ nDone += nBlks -= nLeft -= /* Total blocks done so far */ if ((nBlks) && (InLeft)) { /* done with current cluster */ nLeft = spc; ere = NextFATClstr(Drive, Clstr, &Clstr); /* next FAT cluster */ if (ere) return(erc) ; rLFA += bpc; /* Update relative LFA of new cluster*/ if ((Clstr >= MaxClstr) && (nBlks)) /* Problem! */ ere = ErcBeyondEOF; /* Last cluster & they want more*/ if (!Clstr) ere = ErcBrokenFile; /* No good next cluster! */ LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster*/ } } *pdnBytesRet = nDone * 512; return ere; /* WE'RE DONE, return the error (if any) */ } Fills the stream buffer for the Current LFA in the FUB. We simply figure out which relative LBA we want in the

0 Part TV ■ The Operating System Source Code Listing 27.1 . continued buffer and call ReadBlockM to fill it. We then set LFABuf in the FUB to show the LFA of the first byte in the buffer. static U32 FillStreamBuff (U32 dHandle, U8 f Initial) { U32 ere, i, LFA, cLFA, LFABuf, iFCB; U32 sBuf; U8 pBuf; ere = 0; / Set these up in advance / cLFA = paFUB[dHandle] ->CrntLFA; / Find out where we want to be / LFABuf = paFUB[dHandle]->LFABuf ; / LFA of first byte in buffer now / pBuf = paFUB[dHandle] ->pBuf ; / Local ptr to buffer / sBuf = paFUB[ dHandle] ->sBuf ; / size of buffer / iFCB = paFUB[dHandle]->iFCB; / FCB for this FUB / / If the file was just opened we fill with LFA 0 / if (flnitial) { ere = ReadBlockM(dHandle, pBuf, sBuf, 0, &i, TRUE); } / Else If the LFA is already in the buffer we just exit OK / else if ((cLFA >= LFABuf) && (CLFA < (LFABuf + sBuf))) { ere = 0; } / ELSE We must figure out what starting LFA we want and fill the buffer ' else { LFA = (cLFA/512) * 512; /* Round down to nearest sector */ ere = ReadBlockM(dHandle, pBuf, sBuf, LFA, &i, 1); paFUB[dHandle] ->LFABuf = LFA; } if (ere == ErcEOF) /* We ignore this when filling the buffer */ ere = 0; return ere; /* WE'RE DONE, return the error (if any, except EOF) */ } This is the STREAM read for the MMURTL FAT file system. Used in conjunction with Get & Set FileLFA, you can move to any portion of the file to read one or more bytes. Data is buffered in Page sized chunks by the file system. This is the easiest method for reading a file. ft*******************************************************/

Chapter 27 ■ File System Code H static U32 ReadBytesM(U32 dHandle, U8 *pBytesRet, U32 nBytes, U32 pdReadRet) { U32 ere, iFCB, sBuf, cLFA, fSize; U32 nBytesDone, / Total read so far / lfaEOB, / LFA end of Buffer / nBytesOut; / Number of bytes to copy (this buffer full) */ U8 *pOut, pBuf; / Next data to send caller from buffer. Local pbuf / ere = ValidateHandle(dHandle, &iFCB); / Sets iFCB if OK / if (ere) return ere; / Certain FUB fields have different meanings in stream type file / if (!paFUB[dHandle]->f Stream) return ErcBlockFile; cLFA = paFUB[dHandle]->CrntLFA; / local cLFA / fSize = paFCB[iFCB] ->FileSize; / local size / / check and see if we are at EOF / if (cLFA >= fSize) { / early out / ♦pdReadRet = 0; return ErcEOF; } / We are not at EOF so we need to fill stream buff and then calculate how many bytes we can give them from this buffer full of data. / pBuf = paFUB[dHandle] ->pBuf ; / Local ptr to buffer/size / sBuf = paFUB[dHandle] ->sBuf ; nBytesDone = 0; while ((nBytesDone < nBytes) && (cLFA < fSize) && (!erc)) { ere = FillStreamBuff (dHandle, 0); / Fill the buff / / Find out the LFA at the end of the buffer since we just filled it. It may be less than the end since we may be near EOF. / lfaEOB = paFUB[dHandle]->LFABuf + sBuf -1; / LFA at End of Buffer / if (lfaEOB > fSize) / Beyond EOF? / lfaEOB = fSize - 1; / Calculate pointer to the next chunk out from stream buffer / pOut = pBuf + (cLFA - paFUB [ dHandle ]->LFABuf ); / Calc how many bytes we can get out of buffer (belongs to file) */

[634 Part PV ■ The Operating System Source Code Listing 27.1 . continued nBytesOut = If aEOB - cLFA + 1 ; if (nBytesOut > nBytes-nBytesDone) nBytesOut = nBytes-nBytesDone; /* Send bytes to pBytesRet */ CopyData(pOut, pBytesRet, nBytesOut); pBytesRet += nBytesOut; /* Update pBytesRet */ nBytesDone += nBytesOut; /* Update nBytesDone */ cLFA += nBytesOut; /* update CrntLFA */ paFUBfdHandle] ->CrntLFA = cLFA; *pdReadRet = nBytesDone; /* Tell em how many bytes they got */ if (!erc) if (cLFA == fSize) ere = 1 ; return ere; Flushes the stream buffer if it has been modified. We read the current LFA of the buffer and write it to disk. Then reset the flag in the FOB. This uses WriteBlockM. We ensure we do not call WriteBlockM with more sectors than are allocated to the file cause the buffer may extend past the actual allocated amount of clusters! static U32 FlushStreamBuff (U32 dHandle) { U32 ere, i, j, LFABuf, iFCB, size; U32 sBuf; U8 *pBuf; if (paFUB[dHandle] ->fModified) { LFABuf = paFUB[dHandle] ->LFABuf ; /* LFA of first byte in buf now */ pBuf = paFUB[dHandle] ->pBuf ; /* Local ptr to buffer */ sBuf = paFUB[dHandle] ->sBuf ; /* size of buffer */ iFCB = paFUB[dHandle] ->iFCB; /* to check filesize */ size = paFCB[iFCB] ->FileSize; i = (sBuf/512); /* Total blocks in buff */ j = (size-LFABuf )/512; /* Blocks in buf belonging to file */ if ( (size-LFABuf )%512) /* Odd bytes, add one more block */ if (j < i) i = i;

Chapter 27 ■ File System Code H ere = WriteBlockM(dHandle, pBuf, i512, LFABuf, &i) paFUBfdHandle] ->fModified = 0; } return ere; } /** Write Bytes (Stream) ****************************** This is the STREAM write for the MMURTL FAT file system. This uses FillStreamBuff to set up the buffer, and FlushStreamBuff to write modified stream buffers. This also calls SetFileSizeM to extend the filelength when necessary (writing at EOF). ***************.******.*********************************/ static U32 WriteBytesM(U32 dHandle, char *pData, U32 nBytes, U32 *nBytesRet) { U32 ere, iFCB, sBuf, cLFA, fSize, OrigSize; U32 nBytesDone, /* Total written so far */ lfaEOB, /* LFA end of Buffer */ nBytesOut; /* Number of bytes to copy (this buffer full) */ U8 *pOut, *pBuf; /* Next data to send caller from buffer. Local pbuf */ ere = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */ if (ere) return ere; /* Certain FUB fields have different meanings in stream type file */ if (!paFUB[dHandle]->fStream) return(ErcBlockFile) ; if ( !paFCB[iFCB] ->Mode) /* Is it open in Modify?? */ return(ErcReadOnly) ; cLFA = paFUBfdHandle] ->CrntlFA; /* local cLFA */ fSize = paFCB[iFCB] ->FileSize; /* local size */ /* check and see if we are at EOF or will go past it when we write */ if (cLFA + nBytes > fSize) { /* Must set file size first */ OrigSize = fSize; ere = SetFileSizeM(dHandle, cLFA + nBytes); if (ere) return(erc) ; fSize = paFCB[iFCB] ->FileSize; /* local size */ Now we loop writing the bytes to the stream buffer filling it with each new section of the file. As this occurs we must call f illStreamBuff with each new section of the file if not already in the buff to ensure proper continuity of the file in case we are overwriting existing sections of the file.

(( \ Part IV ■ The Operating System Source Code Listing 27.1. continued pBuf = paFUB[dHandle] ->pBuf ; / Local ptr to buffer/size / sBuf = paFUB[dHandle]->sBuf ; nBytesDone = 0; while ((nBytesDone < nBytes) && (cLFA < fSize) && (!erc)) { / First call FlushStreamBuff which will write the current buffer and reset the fModified flag (if needed). / ere = FlushStreamBuff (dHandle) ; if (ere) return(erc) ; if (cLFA < OrigSize) ere = FillStreamBuff (dHandle, 0); / Fill the buff / else paFUB [ dHandle ]->LFABuf= (cLFA/512) * 512; / Just set Buff LFA ■ I* Find out the LFA at the end of the buffer since we just filled it. It may be less than the end since we may be near EOF. */ lfaEOB = paFUB[dHandle] ->LFABuf + sBuf -1; /* LFA at End of Buffer *i if (lfaEOB > fSize) /* Beyond EOF? */ lfaEOB = fSize - 1; /* Calc pointer to where next chunk goes in stream buffer */ pOut = pBuf + (cLFA - paFUB[dHandle]->LFABuf); /* Calc how many bytes we can write to buffer */ nBytesOut = lfaEOB - cLFA + 1 ; if (nBytesOut > nBytes-nBytesDone) nBytesOut = nBytes-nBytesDone; /* Get bytes from pData into stream buffer */ CopyData(pData, pOut, nBytesOut); paFUB[dHandle] ->fModified = 1; pData += nBytesOut; /* Update pData pointer */ nBytesDone += nBytesOut; /* Update nBytesDone */ cLFA += nBytesOut; /* update CrntLFA */ paFUB(dHandle) ->CrntLFA = cLFA; } •nBytesRet = nBytesDone; /* Tell em how many bytes we wrote */ return ere; }

Chapter 27 ■ File System Code R GetFileSize returns the FileSize entry from the FCB for the handle specified. This means the file must be OPEN. This call would NOT be used for a directory listing function as you would have to open each file to get the information. Use ReadDirSector instead. static U32 GetFileSizeM(U32 dHandle, U32 *pdSizeRet) { U32 ere, iFCB; ere = ValidateHandle(dHandle, &iFCB); if (ere) return ere; pdSizeRet = paFCB[iFCB] ->FileSize; return 0; } SetFileLFA sets a Stream mode file pointer to dLFA. If attempted on a block mode file an error is returned. -1 (Oxffffffff) is equal to EOF. The Stream Buffer is filled with the sectors that contains the LFA. static U32 SetFilel_FAM(U32 dHandle, S32 dLFA) { U32 ere, iFCB; ere = ValidateHandle(dHandle, &iFCB); if (ere) return ere; if (!paFUB[dHandle]->fStream) return ErcBlockFile; if (paFCB[iFCB] ->Mode) / Modify mode - Flush will flush if needed / ere = FlushStreamBuff (dHandle) ; / -1 = Set file ptr to EOF / if (dLFA == -1) dLFA = paFCB[iFCB]->FileSize; if (dLFA > paFCB[iFCB]->FileSize) ere = ErcBeyondEOF; if (!erc) { paFUB[dHandle] ->CrntLFA = dLFA; / Find out where we are in the file */ ere = FillStreamBuff (dHandle, 0); } return ere:

638] Part PV ■ The Operating System Source Code Listing 27.1 . continued GetFileLFA gets a Stream mode file pointer for caller returning it to pdLFARet. If attempted on a block mode file an error is returned. EOF is returned as the FileSize. static U32 GetFileLFAM(U32 dHandle, U32 *pdLFARet; { U32 ere, iFCB; ere = ValidateHandle (dHandle, &iFCB); if (ere) return ere; if ( !paFUB[dHandle] ->fStream) return ErcBlockFile; *pdLFARet = paFUB[dHandle]->CrntLFA; return ere; This calls parse to validate the filename and separate it into directories and a filename. It then walks up the tree until it either finds and opens the file, or errors out. The handle that is returned is 4 higher than the index to the FUB. This is because 0, 1, 2 & 3 are reserved for NUL, KBD, VID, and LPT which are only accessible from the blocking File calls. static U32 0penFileM(U8 *pName, U32 cbName, U8 Mode, U8 fStream, U32 pdHandleRet, U32 iJob) / make use of iJob later!!! */ { U32 ere, level, i, iFCB, iFUB, LBADirEnt, EntOffset; U16 Clstr; U8 fFound, pMem, Drive; if (Mode > 1) return ErcBadOpenMode; level = 0; / start at the root, compare to SpecDepth / ere = ParseName(pName, cbName, iJob); / The entire path has now been parsed out into an array of arrays, each 11 bytes long that contain each directory name up to and including the filename. The first is always the root (entry 0). The drive will be a letter in FDrive. / if ((FDrive > 0x40) && (FDrive < 0x52)) / A to J / Drive ■ FDrive - 0x41; I Make it 0-9 •/ else ere = ErcNoSuchDrive;

Chapter 27 ■ File System (.ode if ( Ldrv[ Drive ] .DevNum == 0xff) ere = ErcNoSuchDrive; if (Drive < 2) { StatFloppy(Drive) ; erc= read_BS ( Drive ) ; } if (!erc) { /* Get Root dir entry */ ere = GetRootEnt(FileSpec[level] , Drive, &LBADirEnt, &EntOffset, &pDirEnt) ; if (ere == ErcNoSuchFile) { if (level == SpecDepth) ere = ErcNoSuchFile; else ere = ErcNoSuchDir; } if (ere) return(erc) ; if (!erc) Clstr = pDirEnt->StartClstr; /* Clstr = beginning of file or dir */ while ((level < SpecDepth) && (!erc)) { /* looking for Dir, not file yet */ ++level; /* next level of parsed filespec */ ere = GetDirEnt(FileSpec[level] , Drive, Clstr, &LBADirEnt, &EntOffset, &pDirEnt) ; if (ere == ErcNoSuchFile) { if (level == SpecDepth) ere = ErcNoSuchFile; else ere = ErcNoSuchDir; } else if (ere) return(erc) ; else Clstr = pDirEnt->StartClstr; /* Clstr @ start of dir entry */ } /* if we got here with no error we've got a file or a DIR. If it's DIR then it's an error. pDirEnt points to its directory entry, and Clstr is the starting cluster of the file */ if (!erc)

/- /n P<zrf TV ■ The Operating System Source Code Listing 27.1 . continued { /* If Attributes say it's not a file then ERROR */ if (pDirEnt->Attr & (VOLNAME | DIRECTORY)) return ErcNotAFile; /* If ModeModify and File is readonly then ERROR */ if ((Mode) && (pDirEnt->Attr & READONLY)) return ErcReadOnly; /* We check to see if it's already open by looking through the valid FCBs to see if we have a Drive, StartClstr& name match. If so, we must see if the modes are compatible. A valid FCB is one where nUsers > 0. */ fFound = 0; i=0; while ((KnFCBs) && (!f Found)) { if ((paFCB[i]->nUsers) && (paFCB[i] ->Ldrv == Drive) && (paFCB[i] ->StartClstr == pDirEnt->StartClstr) && (CompareNCS(&paFCB[i] , FileSpec[SpecDepth], 11) == 0xffffffff ) ) fFound = 1 ; else ++i; } if (fFound) { /* it's open already, i is index into FCBs */ if (paFCB[i] ->Mode) /* it's open in Modify already */ return ErcFilelnUse; else { iFCB = i; /* Index to this FCB */ pFCB = &paFCB[i]; /* make pFCB point to FCB found */ pFCB->nUsers++; /* One more user */ } } else { /* It not already open. Find empty FCB */ i = 0; while ((i<nFCBs) && (paFCB[ i) ->nUsers) ) ++i; /* Find new FCB */ if (i==nFCBs) return ErcNoFreeFCB; /* Couldn't */ /* i now indexes into FCBs for a free FCB. New we copy the directory entry for the file into the FCB and set up the other FCB values. */ iFCB = i; /* used to add an FUB */ pFCB = &paFCB[i]; /* make pFCB point to FCB found */ CopyData(pDirEnt, pFCB, 32); /* Copy Dir Ent into FCB */

Chapter 27 File System Code pFCB->Ldrv = Drive; pFCB->nUsers++; pFCB->Mode = Mode; pFCB->LBADirSect = pFCB->oSectDirEnt • /* Set Drive */ /* Now in use */ /* Open mode */ LBADirEnt; /* So we know where it came from */ EntOffset; /* " " */ /* Now we have an FCB (either existing or we just built it) Now add an FUB and fill in the info for the user so we can return a handle to it. The Job fields is 0 for free FUBs. */ while ((KnFUBs) && (paFUB[i] ->Job) ) ++i if (i==nFUBs) { pFCB->nUsers- - ; li return ErcNoFreeFUB; /’ /* Find new FUB */ Make FCB correct */ Couldn't */ /* If we got here, i is an index to a free FUB. Job Owner */ Set index to FCB for this file */ iFUB = i; paFUB[iFUB]->Job = iJob; paFUB[iFUB]->iFCB = iFCB; paFUB[iFUB]->CrntLFA = 0; paFUB[ iFUB] ->f Modified = 0; paFUB[iFUB] ->fStream = fStream; paFUB[iFUB]->Clstr = pDirEnt paFUB[iFUB]->LFAClStr = 0; paFUB[iFUB]->LFABuf = 0; paFUB[iFUB]->sBuf = 0; if (fStream) { /* allocate/fill buffer and set rest of FUB */ ere = AllocOSPage(l , &pMem); /* Stream Buf is 4K */ /* Current Logical File Address */ /* Stream buf was modified */ /* NonZero for STREAM mode */ StartClstr; /* Start Cluster */ /* Rel LFA to 0 */ /* First LFA in Buffer */ /* Default to No Buf */ if (ere) { pFCB->nUsers- - ; paFUB[iFUB] ->Job = 0; return ere; } paFUB[iFUB]->pBuf = pMem; paFUB[iFUB]->sBuf = 4096; ere = FillStreamBuff (iFUB, 1] if (ere) { pFCB->nUsers- - ; paFUB[iFUB] ->Job = 0; DeAllocPage ( pMem , 1 ) ; /* No MEM left... Bummer */ /* Return FCB to pool */ /* Return FUB to pool */ /* Return Ere to user... */ /* Ptr to buffer if stream mode /* Size of buffer */ flnitial to TRUE /* Return FCB to pool */ /* Return FUB to pool */ /* Free memory for buffer */

I
642 Part PV ■ The Operating System Source Code Listing 27.1 . continued return ere; /* Return Ere to user.. } } *pdHandleRet = iFUB; /* File handle */ } } return ere; } CLOSE FILE for the MMURTL DOS file system. This finds the FUB, checks to see if the buffer should be flushed and deallocated (only for stream mode), invalidates the FUB, and the FCB (if this is the last or only user). static U32 CloseFileM (U32 dHandle) { U32 ere, iFCB, i; ere = ValidateHandle(dHandle, &iFCB); if (ere) return ere; if (paFCB[iFCB]->Mode) { /* Modify mode */ if (paFUB[dHandle]->fStream) { ere = FlushStreamBuff (dHandle) ; } UpdateDirEnt(iFCB) ; /* ignore error */ } if (paFUB[dHandle]->fStream) DeAllocPage(paFUB[dHandle] ->pBuf , 1); /* Free buffer */ /* This means the FS is screwed up. This shouldn't happen... ' if (!paFCB[iFCB]->nUsers) ere ■ ErcBadFCB; else paFCB(iFCB] ->nUsers- - ; /* Now we should be able to close it and free the FUB. If the FCB.nUsers flips to 0 it will be free too */ paFUB(dHandle) ->Job = 0; /* This will write all modified fat sectors */ for (i=0; i<nFATBufs; i++) UpdateFAT(i); return ere; }

Chapter 27 ■ File System Code 643 /*** Create File *********************************** This is Create File for the MMURTL FAT file system. This is also used internally to create directories. static U32 CreateFileM(char *pName, long cbName, long attrib, long iJob) { unsigned long dHandle, i, j, k, ere, LBA, spc; char Path[70]; leng cbPath; char filename! 12] ; U16 CrntClstr, ClstrValue, iStart, DirClstr; U8 fFound, Drive, fDir; /* First we try to open it to see if it exists. If we get back ErcOK or ErcFilelnUse the name is already in use as a file and we give them ErcDupName. We return other errors as we find them. */ if (attrib & DIRECTORY) { fDir = 1; ere = 0; } else fDir = 0; ere = OpenFileM(pName, cbName, 0, 0, &dHandle, iJob); switch (ere) { case ErcOK: CloseFileM(dHandle); ere = ErcDupName; break; case ErcFilelnUse: ere = ErcDupName; break; case ErcNotAFile: /* It a directory... */ break; case ErcNoSuchFile: { /* OK, this means we can try to create it! */ ere = 0; BuildSpec(pName, cbName, Path, &cbPath, iJob); ere = ParseName(Path, cbPath, iJob); if (ere) return(erc) ; /* FDrive was set up on Parse */ Drive = FDrive - 0x41; /* Make it 0-9 */

saa Part PV ■ The Operating System Source Code Listing 27.1 . continued /* First we setup the filename from what was parsed out during the Parse call. Then eliminate it from Path. *l CopyData(FileSpec[SpecDepth] , filename, 11); /* filename */ filename[H ] = 0; /* Hack the filename from Path so we can search the directory path properly */ while ((cbPath) && (Path[cbPath-1 ] != 0x5C)) { cbPath--; } /* Each directory sector has 16 32 byte entries. We will now walk thru each sector until we find one that is a deleted or empty entry. A deleted entry has E5h as its first character in the name. An unused entry has 00h as its first character in the name. */ fFound = 0; i = 0; /* i = sectornum */ while ((IfFound) && (Sere)) { ere = GetDirSectorM(Path, cbPath, abTmpSector, 512, i++, &LBA, &DirClstr, iJob) ; if (!erc) { k = 0; pDirEnt = abTmpSector; while (k<l6) { if ((pDirEnt->Name[0] == 0xE5) !| (!pDirEnt->Name[0])) { fFound = 1 ; break; } pDirEnt += 32; k++; } } } /* When we get here, we have either found an entry or we have run out of sectors! If we run out of sectors and this is the root, we error out (RootFull), otherwise we extend the directory. */ if ((ere == ErcNoMatch) && ( ISpecDepth) ) return (ErcRootFull) ; else if (ere == ErcEOF) { /* reach end of dir! */

Chapter 27 U File System Code |643 /* We must now extend the cluster chain for this directory entry. DirClstr holds the last valid sector of the directory. */ FillData(abTmpSector, 512, 0); spc ■ Ldrv[Drive] .SecPerClstr; /* sectors per cluster */ ere = ExtendClstrChain(Drive, DirClstr, &DirClstr); if (ere) return(erc) ; LBA = ClsToLBA(DirClstr, Drive); j = LBA; i = spc; ere = 0; while ((!•■) && (lerc)) { ere = DeviceOp(Ldrv[Drive] .DevNum, 2, j++, 1, abTmpSector) ; } pDirEnt = abTmpSector; /* first entry in new sector */ f Found = 1 ; /* We now have a new cluster on the end of the directory and it is all zeros!. The first sector is pointed to by LBA and abTmpSector is still zeros just as an new dir sector should be with pDirEnt pointing to the first entry. */ if ((!erc) && (f Found)) { /* Let's DO IT! */ /* pDirEnt points to the entry we will use and abTmpSector still has the entire sector in it, so we find an empty clstr on the disk, allocate to this file, fill in the rest of the dir entry and we are almost done. /* Find a fat buf already in memory for this drive */ /* One WILL be here! */ CrntClstr = 0; while ((k<nFATBufs) && ( !CrntClstr) ) { if ((Drive == Fat[k] .Drive) && (Fat[k] .LastUsed) ) CrntClstr = Fat[k] .iClstrStart ; /* valid cluster */ k++; } if (ICrntClstr) CrntClstr = 2; /* Can't find it so start at beginning */ iStart = CrntClstr; /* where we started looking for empties */

£ / /- Part PV ■ The Operating System Source Code Listing 27.1. continued fFound = 0; while (!f Found) { ++CrntClstr; /* next cluster */ if (CrntClstr == iStart) return(ErcDiskFull) ; ere = GetClstrValue(CrntClstr, Drive, 0, &ClstrValue, &j ) if ((!erc) && (IClstrValue)) fFound =1; /* found an empty one */ else if (ere == ErcBadFATClstr) { /* off the end */ /* we started AFTER beginning of disk so we will go back and look for empties from beginning to where we started. */ if (iStart > 2) CrntClstr = 2; else return(ErcDiskFull) ; } else if (ere) return(erc) ; } /* If we got here, we found an empty cluster */ CopyData(f ilename, pDirEnt, 11); if (IfDir) pDirEnt->Attr = attrib & (READONLY [ HIDDEN \ SYSTEM \ ARCHIVE); else pDirEnt->Attr = attrib; GetFATTime(&pDirEnt->Time, SpDirEnt ->Date) ; pDirEnt->StartClstr = CrntClstr; pDirEnt->FileSize = 0; ere = SetClstrValue(CrntClstr, 0xFFFF, Drive, &i); /* Now we write the dir sector back to disk */ if (!erc) ere = DeviceOp(Ldrv[Drive] .DevNum, 2, LBA, 1, abTmpSector) ; /* If we were creating a directory, we must add the two default directory entries . and . . This is done by filling out abTmpSector as the first sector of an empty directory and writing it out to the allocated cluster. We then zero it out and write it to the rest of the sectors in the new cluster. */ if (fDir) { FillData(abTmpSector, 512, 0);

Chapter 27 ■ File System Code H pDirEnt = abTmpSector; /* first entry in new sector */ /* do the current dir entry (.) */ CopyData(". “, pDirEnt, 11); pDirEnt->Attr = DIRECTORY; GetFATTime(&pDirEnt->Time, &pDirEnt ->Date) ; pDirEnt ->StartClstr = CrntClstr; pDirEnt ->FileSize = 0; /* do the previous current dir entry (.) */ pDirEnt += 32; CopyData(".. “, pDirEnt, 11); pDirEnt ->Attr = DIRECTORY; GetFATTime(&pDirEnt->Time, &pDirEnt->Date) ; pDirEnt ->StartClstr = DirClstr; pDirEnt ->FileSize = 0; spc = Ldrv[Drive] .SecPerClstr; /* sectors per cluster */ LBA = ClsToLBA(CrntClstr, Drive); j = LBA; /* Write this sector out to disk */ ere = DeviceOp(Ldrv[Drive] .DevNum, 2, j++, 1, abTmpSector); FillData( abTmpSector, 512, 0); /* zero the rest */ ere = 0; i = spc-1; /* less one cause we wrote the first */ while ((i--) && (!erc)) { ere = DeviceOp(Ldrv[Drive] .DevNum, 2, j++, 1, abTmpSector); } } /* This will write all modified fat sectors */ for (i=0; KnFATBufs; i++) UpdateFAT(i); return(erc) ; } else return(erc) ; } default: ; } /* switch */ return (ere); } /*** Delete File *************************************** This is Delete File for the MMURTL FAT file system.

I
648 Part IV ■ The Operating System Source Code Listing 27.1. continued The file must be opened in mode modify which gives the caller exclusive access. The file is closed even if the Delete fails. static U32 DeleteFileM(long dHandle) { U32 ere, iFCB, i; U16 iStart; U8 Drive; ere = ValidateHandle(dHandle, &iFCB); if (ere) return ere; Drive = paFCB[iFCB] ->Ldrv; / What logical drive are we on’ if (!paFCB[iFCB]->Mode) { /* Modify mode? */ CloseFileM(dHandle); return ErcReadOnly; } if (paFUB[dHandle] ->fStream) DeAllocPage(paFUB[dHandle]->pBuf , 1); /* Free buffer */ iStart = paFCB[iFCB] ->StartClstr; if (iStart) { ere = TruncClstrChain(Drive, iStart); if (!erc) ere = SetClstrValue( iStart, 0, Drive, &i); } paFCB[iFCB]->Name[0] = 0xE5; UpdateDirEnt(iFCB) ; /* ignore error */ /* This means the FS is screwed up. This shouldn't happen... */ if (!paFCB[iFCB]->nUsers) ere = ErcBadFCB; else paFCB[iFCB] ->nUsers- - ; /* Now we should be able to close it and free the the FUB. If the FCB.nUsers flips to 0 it will be free too */ paFUB[dHandle] ->Job = 0; /* This writes all modified fat sectors */ for (i=0; i<nFATBufs; i++) UpdateFAT(i); return ere;

Chapter 27 ■ File System Code /*** Rename File *********************************** This is Rename File for the MMURTL FAT file system. static U32 RenameFileM(char *pCrntName, long dcbCrntName, char *pNewName, long dcbNewName, U32 iJob) { U32 dHandle, ere, erd , iFCB; ere = OpenFileM(pCrntName, dcbCrntName, 1, 0, &dHandle, iJob) ; if (!erc) { FDrivel = FDrive; CopyData(FileSpec, FileSped, 77); SpecDepthl = SpecDepth; ere = ParseName(pNewName, dcbNewName, iJob); if (!erc) if ((FDrivel != FDrive) || (SpecDepthl != SpecDepth)) ere = ErcRenameDrv; /* No Rename across drives */ if (!erc) if (SpecDepth) /* Compare upper tree */ if (CompareNCS(FileSpec, FileSped, SpecDepth * 11) != -1) ere = ErcRenameDir; /* No Rename across dirs */ if (!erc) { /* OK to rename */ iFCB = paFUB[dHandle]->iFCB; /* FCB for this FUB */ CopyData(FileSpec[SpecDepth] , &paFCB[iFCB] , 11); ere = UpdateDirEnt (iFCB) ; } erd = CloseFileM(dHandle) ; if (!erc) ere = erd ; } return (ere);

  • Create Dir *************************************** This is Create Directory for the MMURTL FAT file system. static U32 CreateDirM(char *pPath, long cbPath, long iJob) ong ere; ere = CreateFileM(pPath, cbPath, DIRECTORY, iJob); return(erc) ; *** Delete Directory *********************************** This is Delete Directory for the MMURTL FAT file system. static U32 DeleteDirM(char *pPath, long cbPath, long fAHFiles, long iJob) { pPath = 0; cbPath = 0; fAHFiles = 0:

Part IV The Operating System Source Code Listing 27.1. continued This is the File system task. All file system requests end up here to be serviced. static void FSysTask(void) { U32 FMsg[2], mere, ere, U16 i16; while (1) { ere = WaitMsg(FSysExch, FMsg); if (!erc) { pRQB = FMsg[0]; /* first DD in Msg is pointer to RQBlock */ switch (pRQB->ServiceCode) { case 0 : /* JobAbort - CLOSE Al .L FILES FOR THIS JOB! i = 4; while (i<nFUBs) \ if (paFUB[i]->Job == pRQB->dData0) CloseFileM(i); } ++i; /* next FUB */ } ere = 0; break; case 1 : /* OpenFile */ ere = OpenFileM(pRQB->pDatal , /* pFilename */ pRQB->cbData1 , /* dcbFilename */ pRQB->dData0, /* Mode */ pRQB->dDatal , /* Type */ pRQB->pData2, /* pdHandleRet */ pRQB->RqOwnerJob) /* iJob Owner */ break; case 2 : /* CloseFile */ ere = CloseFileM(pRQB->dData0) ; /* Handle */ break; case 3 : /* ReadBlock */ ere = ReadBlockM(pRQB->dData0, /* Handle */ pRQB->pDatal , /* pDataRet */ pRQB->cbDatal , /* nByes */ pRQB->dData1 , /* dLFA */ pRQB->pData2, /* pdnBytesRet */ 0); /* NOT internal */ break;

Chapter 27 B File System Code case 4 ere break case 5 : ere = break case 6 : ere = break case 7 : ere = break case 8 : ere = break case 9 : ere = break case 10 : ere = break case 11 : ere = break case 12 : ere = break: case 13 : ere = break; case 14 : ere = /* WriteBlock */ WriteBlockM ( pRQB - >dData0 , pRQB->pData1 , pRQB->cbData1 , pRQB->dData1 , pRQB->pData2) ; /* ReadBytes */ ReadBytesM ( pRQB - >dData0 , pRQB->pData1 , pRQB->cbData1 , pRQB->pData2) ; /* WriteBytes */ WriteBytesM(pRQB->dData0, pRQB->pData1 , pRQB->cbData1 , pRQB->pData2) ; /* GetFileLFA */ Get FileLFANI ( pRQB - >dDat a0 , pRQB->pData1 ) ; /* SetFileLFA */ SetFileLFAM ( pRQB - >dData0 , pRQB->dData1 ) ; /* GetFileSize */ GetFileSizeM ( pRQB - >dData0 , pRQB->pData1 ) ; /* SetFileSize */ Set FileSizeM ( pRQB - >dData0 , pRQB->dData1); /* CreateFile */ CreateFileM(pRQB->pData1 , pRQB->cbData1 , pRQB->dData0, pRQB->RqOwnerJob) /* RenameFile */ RenameFileM(pRQB->pData1 , pRQB->cbData1 , pRQB->pData2, pRQB->cbData2, pRQB->RqOwnerJob) /* DeleteFile */ DeleteFileM(pRQB->dData0) ; /* CreateDirectory */ CreateDirM(pRQB->pData1 , pRQB->cbData1 , pRQB->RqOwnerJob) ; /* Handle */ /* pData */ /* nBytes */ /* dLFA */ /* pdBytesRet */ /* Handle */ /* pDataRet */ /* nBytes */ /* pdnBytesRet */ /* Handle */ /* pData */ /* nBytes */ /* pdnBytesRet */ /* Handle */ /* pdLFARet */ /* Handle */ /* dNewLFA */ /* Handle */ /* pdSizeRet */ /* Handle */ /* dSize */ /* pFilename */ /* cbFilename */ /* Attributes */ /* iJob Owner */ /* pCrntName */ /* cbCrntName */ /* pNewName */ /* dcbNewName */ /* JobNum */ /* Handle /* pPath */ /* cbPath */ /* JobNum */

H Part PV ■ The Operating System Source Code Listing 27.1. continued case 15 : /* DeleteDirectory */ ere = DeleteDirM(pRQB->pData1 , /* pPath */ pRQB->cbData1 , /* cbPath */ pRQB->dData0, /* fAHFiles */ pRQB->RqOwnerJob) ; /* JobNum */ break; case 16 : /* GetDirSector */ ere = GetDirSectorM(pRQB->pData1 , /* pPath */ pRQB->cbData1 , /* cbPath */ pRQB->pData2, /* pSectRet */ pRQB->cbData2, /* cbRetMax */ pRQB->dData0, /* SectNum */ &i, /* for LBARet */ &116, /* for DirClstr */ pRQB->RqOwnerJob) ; /* JobNum */ break; default : ere = ErcBadSvcCode; break; } mere = Respond (FMsg[0] , ere); } } \ 1* forever */ /***************** public BLOCKING CALLS FOR FILESYSM ********* These calls query the TSS Exhange and use it to make Requests to the file system service on the behalf of the caller. These calls are fully reentrant! No static data! U32 far _OpenFile(char *pFilename, long dcbFilename, long Mode, long Type, long *pdHandleRet) { long ere, exch, rqhndl, i, msg[2]; if (dcbFilename == 3) { Lf (CompareNCS(pFilename, “NUL’ 3) *pdHandleRet = 0; return(0) ; else if (CompareNCS(pFilename, “KBD” , 3) •pdHandleRet = 1 ; return(0) ; else if (CompareNCS(pFilename, “VID” , 3) •pdHandleRet = 2; return(0) ;

Chapter 27 ■ File System Code 653 else if (CompareNCS(pFilename, { ere = Device0p(3, 10, 0, if (!erc) ♦pdHandleRet = 3; return(erc) ; } ‘LPT” , 3) == -1) 0, &i); /* 10=Open */ GetTSSExch(&exch) ; /* No error will come back! */ ere = Request(fsysname, 1, exch, &rqhndl, 1 , /* 1 Send ptr */ pFilename, dcbFilename, pdHandleRet, 4, Mode, Type, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; U32 far _CloseFile(unsigned long dHandle) { long ere, exch, rqhndl, i, msg[2]; if (dHandle < 3) return(0) ; else if (dHandle == 3) { ere = Device0p(3, 11, 0, 0, &i) return(erc) ; } GetTSSExch(&exch) ; ere = Request (fsysname, 2, exch, &rqhndl, Close */ /* 0 Send ptr */ dHandle, 0, 0); if (!erc) ere = WaitMsg(exch, msg) if (ere) return(erc); return(msg[1 ] ) ; U32 far _ReadBlock(long dHandle, char *pDataRet, long nBlks, long dLFA, long *pdnBlksRet) { long ere, exch, rqhndl, msg[2], i; if (dHandle < 4) return(ErcNotSupported) ; GetTSSExch(&exch); ere = Request (fsysname, 3, exch, &rqhndl, 1, /* 1 Send ptr */

(cla\ Part F/ ■ The Operating System Source Code Listing 27.1. continued pDataRet, nBlks, pdnBlksRet, 4, dHandle, dLFA, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc) ; if(msg[1]) { DeviceStat(l0, &FDDevStat, 64, &i) ; } return(msg[1 ] ) ; U32 far _WriteBlock(long dHandle, char *pData, long nBlks, long dLFA, long pdnBlksRet) { long ere, exch, rqhndl, msg[2]; if (dHandle < 4) return(ErcNotSupported) ; GetTSSExch(&exch); ere = Request (fsysname, 4, exch, &rqhndl, 1, / 1 Send ptr */ pData. nBlks, pdnBlksRet, 4, dHandle, dLFA, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; } U32 far _ReadBytes(long dHandle, char *pDataRet, long nBytes, long *pdnBytesRet) { long ere, exch, rqhndl, msg[2], i; if (dHandle == 0) { *pdnBytesRet = 0; return(0) ; } else if (dHandle == 1) { i = 0; while (i < nBytes) { ReadKbd(*pDataRet++, 1); i++; } •pdnBytesRet = i; return(0) ;

Chapter 27 ■ File System Code B else if ( (dHandle { pdnBytesRet = 0; return (ErcWriteOnly) } 2) ; ; (dHandle 3) GetTSSExch(&exch) ; ere = Request(fsysname, 5, exch, &rqhndl, 1 , pDataRet, nBytes, pdnBytesRet, 4, dHandle, 0, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; / 1 Send ptr / r****** l U32 far _WriteBytes(long dHandle, char *pData, long nBytes, long *pdnBytesRet) { long ere, exch, rqhndl, VidAttr, msg[2] if (dHandle == 0) *pdnBytesRet = nBytes; return(0) ; else if (dHandle else if (dHandle D *pdnBytesRet = 0; return(ErcReadOnly) 2) GetNormVid(&VidAttr); TTYOut(pData, nBytes, VidAttr] *pdnBytesRet = nBytes; return(0) ; else if (dHandle == 3) ere = Device0p(3, 2, return(erc) ; nBytes, pData); /* 2 = CmdWriteRec */ GetTSSExch(&exch); ere = Request(fsysname, 6, exch, &rqhndl, 1 , pData, nBytes, pdnBytesRet, 4, dHandle, 0, 0); if (!erc) ere = WaitMsg(exch, msg); /* 1 Send ptr */

ser Part F/ ■ The Operating System Source Code Listing 27.1. continued if (ere) return(erc); return(msg[1 ]) ; } U32 far _GetFileLFA(long dHandle, long pdLFARet) { long ere, exch, rqhndl, msg[2]; if (dHandle < 4) return(ErcEOF) ; GetTSSExch(&exch); ere = Request(fsysname, 7, exch, &rqhndl, 0, / 0 Send ptrs / pdLFARet, 4, 0, 0, dHandle, 0, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1]) ; } U32 far _SetFileLFA(long dHandle, long dNewLFA) { long ere, exch, rqhndl, msg[2]; if (dHandle < 4) return(ErcNotSupported) ; GetTSSExch(&exch); ere = Request(fsysname, 8, exch, &rqhndl, 0, / 0 Send ptrs */ dHandle, dNewLFA, 0) if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; U32 far _GetFileSize(long dHandle, long *pdSizeRet) { long ere, exch, rqhndl, msg[2]; if (dHandle < 4) return(ErcNotSupported) ; GetTSSExch(&exch); ere = Request (fsysname, 9, exch, &rqhndl, 0, pdSizeRet, 4, 0, 0, dHandle, 0, 0); 0 Send ptrs

Chapter 27 ■ File System Code B if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; } U32 far _SetFileSize(long dHandle, long dSize) { long ere, exch, rqhndl, msg[2]; if (dHandle < 4) return(ErcNotSupported) ; GetTSSExch(&exch); ere = Request (fsysname, 10, exch, &rqhndl, 0, /* 0 Send ptrs */ dHandle, dSize, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; U32 far _CreateFile(char *pFilename, long cbFilename, long Attribute) { long ere, exch, rqhndl, msg[2]; GetTSSExch(&exch); ere = Request(f sysname, 11, exch, &rqhndl, 1, /* 1 Send ptrs */ pFilename, cbFilename, 0, 0, Attribute, 0, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1]) ; } U32 far _RenameFile(char *pCrntName, long cbCrntName, char *pNewName, long cbNewName) { long ere, exch, rqhndl, msg[2]; GetTSSExch(&exch); ere = Request(f sysname, 12, exch, &rqhndl, 2, /* 2 Send ptrs */ pCrntName, cbCrntName, pNewName, cbNewName, 0, 0, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; }

/en Part TV ■ The Operating System Source Code Listing 27.1. continued /♦it***********************************/ U32 far _DeleteFile(long dHandle) { long ere, exch, rqhndl, msg[2]; if (dHandle < 4) return(ErcNotSupported) ; GetTSSExch(&exch) ; ere = Request (fsysname, 13, exch, &rqhndl, 0, /* 0 Send ptrs */ dHandle, 0, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; U32 far _CreateDir(char *pPath, long cbPath) { long ere, exch, rqhndl, msg[2]; GetTSSExch(&exch) ; ere = Request(fsysname, 14, exch, &rqhndl, 1 , /* 1 Send ptrs */ pPath, cbPath, 0, 0, 0, 0, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; } U32 far _DeleteDir(char *pPath, long cbPath, long fAHFiles) { long ere, exch, rqhndl, msg[2]; GetTSSExch(&exch); ere = Request (fsysname, 15, exch, &rqhndl, 1 , /* 1 Send ptrs */ pPath, cbPath, 0, 0, fAHFiles, 0, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; } /A************************************/ U32 far _GetDirSector(char *pPathSpec, long cbPathSpec, char *pEntRet, long cbEntRet, long SectNum) {

Chapter 27 ■ File System Code long ere, exch, rqhndl, msg[2]; GetTSSExch(&exch); ere = Request (fsysname, 16, exch, &rqhndl, 1 , /* 1 Send ptrs */ pPathSpec, cbPathSpec, pEntRet, cbEntRet, SectNum, 0, 0); if (!erc) ere = WaitMsg(exch, msg); if (ere) return(erc); return(msg[1 ] ) ; } Initialization Routine for the File system. This is called by the Monitor where any errors will be reported. U32 InitFS(void) { U32 ere, i, j ; U8 *pMem; /* Allocate FAT buffers and initialize FAT related structures. We will allocate 24Kb worth of buffers (6 Pages, 16 buffers). */ Fat[0].pBuf = FatBufA; /* floppy fat buffers */ ere = AllocOSPage(2, &pMem); if (!erc) /* hard disk fat buffers from allocated mem */ for (1=1; i<nFATBufs; i++) { Fat[i].pBuf = pMem; /* Make pBuf point to each buffer */ pMem += 512; /* Next buffer in allocated memory */ } /* Allocate and initialize FCBs and FUBs. This is enough FCBs and FUBS for 128 openfiles. (Good enough to start...) */ if (!erc) ere = AllocOSPage(2, &paFCB) ; /* a pointer to allocated FCBs. */ if (!erc) FillData(paFCB, 8192, 0); if (!erc) ere = AllocOSPage(1 , &paFUB); /* a pointer to allocated FUBs. */ if (!erc) FillData(paFUB, 4096, 0); if (!erc) ere = read_PE(); /* reads partition tables */ if (!erc) ere = SetDriveGeometry(12) ; if (!erc) {

Part FV ■ The Operating System Source Code Listing 27.1. continued ere = SetDriveGeometry(13) ; if (ere == 663) ere = 0; /* may be invalid drive */ } StatFloppy(0); StatFloppy(l); /* read all logical drive boot sectors */ if (!erc) { for (i=0; i< nLDrvs; i++) { if (Ldrv[i] .DevNum != 0xff) { read_BS(i) ; } } } for (i=0; i<nLDrvs; i++) if (Ldrv[i] .DevNum != 0xff) { 1-12; if (Ldrv[i] .fFAT16) j = 16; xprintf("%c: Heads %d, Sec/Trk %d, Sec/Clstr %d, Dev %d, FAT%d \r\n” i+0x41 , Ldrv[i] .nHeads, Ldrv[i] .nSecPerTrk, Ldrv[i] .SecPerClstr, Ldrv[i] .DevNum, j); } if (!erc) ere = AllocExch(&FSysExch) ; /* Start the filesystem task at a decently high priority (5). This should be higher than the Monitor status task and even the Keyboard. */ if (!erc) ere = SpawnTask(&FSysTask, 5, 0, &FSysStack[51 1 ] , 1); if (lerc) ere = RegisterSvc(fsysname, FSysExch); return ere; }

Powered by TurnKey Linux.