--- /dev/null
+/* This is the MMURTL, MS-DOS Compatible (FAT) File system. */\r
+\r
+/*\r
+ MMURTL Operating System Source Code\r
+ Copyright 1991,1992,1993,1994 Richard A. Burgess\r
+ ALL RIGHTS RESERVED Version 1.0\r
+*/\r
+\r
+/*\r
+About MS-DOS disk formats and how MMURTL handles them.\r
+\r
+Physical Disk Layouts\r
+ From the disk controller's standpoint:\r
+ Cylinder numbers run from 0 to nMaxCyls-1.\r
+ Head numbers run from 0 to nMaxheads-1.\r
+ Sector numbers run from 1 to nMaxSectorsPerTrack.\r
+\r
+Physical (Absolute) Sector Numbers\r
+\r
+ Physical sector numbers (absolute) begin at Cyl 0, Head 0, Sector 1.\r
+ As the physical sector number rolls over (nMaxSectorsPerTrack+1),\r
+ the Head number is incremented which moves us to the next track\r
+ (same cylinder, next head). When the head number rolls over\r
+ (nMaxHeads is reached) the cylinder number is incremented.\r
+\r
+ Note: Track and cylinder are NOT interchangable terms in the\r
+ above text. If you have 6 heads on your drive, you have\r
+ 6 tracks per cylinder. This can be confusing because many\r
+ books and documents use the terms interchangably.\r
+ And you can, so long as you know that's what you're doing.\r
+\r
+Hidden Sectors\r
+\r
+ MS-DOS reserves a section of the physical hard disk. This area\r
+ is called the Hidden Sectors. This is usually the very first\r
+ track on the disk (begins at Cyl 0, head 0, Sector 1). The\r
+ partition tables are kept at the very end of the first sector in\r
+ this hidden area (offset 01BEh in the first sector to be exact).\r
+ The partition tables are 16 byte entries that describe\r
+ "logical" sections of the disk that can be treated as separate drives.\r
+ There are usually no "hidden sectors" on floppy disks, nor are there\r
+ any partition tables.\r
+\r
+\r
+MMURTL Logical Block Address (LBA)\r
+\r
+ MMURTL device drivers treat the entire disk as a single physical\r
+ drive. The MMURTL file system reads the partition tables,\r
+ then sets up the device driver to span the entire physical disk\r
+ as 0 to nMaxBlockNumbers-1. This is refered to as the Logical Block\r
+ Address (LBA) and is the value passed in to the DeviceOp call for\r
+ the MMURTL hard/floppy disk device drivers (LBAs are used with\r
+ all MMURTL devices).\r
+\r
+ Note: DO NOT confuse MMURTL's LBA for the sector number in an MS-DOS\r
+ logical drive. MMURTL calls these logical blocks because we still\r
+ have to convert them into physical cylinder, head and sector to\r
+ retrieve the data.\r
+\r
+MS-DOS Boot Sector\r
+\r
+ The first sector of an MS-DOS logical drive will be its boot\r
+ sector. Each of the MS-DOS logical partitions will have a boot\r
+ sector although only the first will be marked as bootable (if any are).\r
+ It's position on the disk is calculated from the partition table\r
+ information.\r
+\r
+MMURTL File System Initialization\r
+\r
+ The MMURTL-FAT file system reads the partition table and saves\r
+ the starting LBA and length of each of DOS logical disk that is\r
+ found. Armed with this information, MMURTL can access each of\r
+ the DOS logical disks as a separate disk drive. To maintain some\r
+ sanity, the MMURTL file system gives all of its logical drives\r
+ a letter just like MS-DOS. MMURTL supports two floppy drives\r
+ (A & B) and up to eight logical hard disk (C-J). All information\r
+ on the Logical Drives are kept in an array of records (Ldrvs).\r
+ This includes the logical letter to physical drive conversions.\r
+\r
+ Once we have the layout of each of the partitons, we read the boot\r
+ sector from the first DOS logical drive. The boot sector contains\r
+ several pieces of important information about the drive geometry\r
+ (numbers of heads, sectors per track, etc.), which are also placed\r
+ in the Logical Drive stuctures.\r
+\r
+ Once we have the drive geometry information we setup the MMURTL\r
+ device driver. This tells the device driver how many cylinders,\r
+ heads and sectors per track are on the physical disk.\r
+ Until this is done, the device driver assumes a minimum drive size\r
+ and you should only read the partition table (or boot sector if no\r
+ partition table is on the disk). This provides enough\r
+ information to do a DeviceInit call to set up proper drive\r
+ geometry.\r
+\r
+ If you were building a loadable file system to replace the one that\r
+ is included in MMURTL, you would call your routine to initialize\r
+ the file system very early in the main program block. You must\r
+ not service file system requests until this is done.\r
+\r
+*/\r
+\r
+#define U32 unsigned long\r
+#define U16 unsigned int\r
+#define U8 unsigned char\r
+#define S32 long\r
+#define S16 int\r
+#define S8 char\r
+#define TRUE 1\r
+#define FALSE 0\r
+\r
+/*********** MMURTL Public Prototypes ***************/\r
+\r
+/* From MKernel */\r
+\r
+extern far AllocExch(long *pExchRet);\r
+extern far U32 GetTSSExch(U32 *pExchRet);\r
+extern far SpawnTask(char *pEntry,\r
+ long dPriority,\r
+ long fDebug,\r
+ char *pStack,\r
+ long fOSCode);\r
+extern far long WaitMsg(long Exch, char *pMsgRet);\r
+extern far long CheckMsg(long Exch, char *pMsgRet);\r
+extern far long Request(unsigned char *pSvcName,\r
+ unsigned int wSvcCode,\r
+ unsigned long dRespExch,\r
+ unsigned long *pRqHndlRet,\r
+ unsigned long dnpSend,\r
+ unsigned char *pData1,\r
+ unsigned long dcbData1,\r
+ unsigned char *pData2,\r
+ unsigned long dcbData2,\r
+ unsigned long dData0,\r
+ unsigned long dData1,\r
+ unsigned long dData2);\r
+\r
+extern far long Respond(long dRqHndl, long dStatRet);\r
+\r
+/* From MData */\r
+extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes);\r
+extern far void FillData(U8 *pDest, U32 cBytes, U8 bFill);\r
+extern far long CompareNCS(U8 *pS1, U8 *pS2, U32 dSize);\r
+\r
+/* From MTimer.h */\r
+extern far long GetCMOSTime(long *pTimeRet);\r
+extern far long GetCMOSDate(long *pTimeRet);\r
+extern far long GetTimerTick(long *pTickRet);\r
+\r
+/* From MVid.h */\r
+extern far long TTYOut (char *pTextOut, long ddTextOut, long ddAttrib);\r
+extern far long GetNormVid(long *pNormVidRet);\r
+\r
+#include "MKbd.h"\r
+\r
+/* From MDevDrv */\r
+extern far U32 DeviceOp(U32 dDevice,\r
+ U32 dOpNum,\r
+ U32 dLBA,\r
+ U32 dnBlocks,\r
+ U8 *pData);\r
+\r
+extern far U32 DeviceStat(U32 dDevice,\r
+ S8 * pStatRet,\r
+ U32 dStatusMax,\r
+ U32 *pdSatusRet);\r
+\r
+extern far U32 DeviceInit(U32 dDevNum,\r
+ S8 *pInitData,\r
+ U32 sdInitData);\r
+\r
+/* From MMemory.h */\r
+extern far U32 AllocOSPage(U32 nPages, U8 *ppMemRet);\r
+extern far U32 DeAllocPage(U8 *pOrigMem, U32 nPages);\r
+\r
+/* From MJob.h */\r
+extern far U32 GetPath(long JobNum, char *pPathRet, long *pdcbPathRet);\r
+extern far U32 RegisterSvc(S8 *pName, U32 Exch);\r
+\r
+/* NEAR support for debugging */\r
+\r
+extern long xprintf(char *fmt, ...);\r
+extern U32 Dump(unsigned char *pb, long cb);\r
+\r
+/* File System error codes */\r
+\r
+#define ErcOK 0 /* Alls Well */\r
+#define ErcEOF 1 /* DUH... The END */\r
+#define ErcBadSvcCode 32 /* Service doesn't handle that code */\r
+\r
+#define ErcBadFileSpec 200 /* invalid file spec (not correct format)*/\r
+#define ErcNoSuchDrive 201 /* Try another letter bozo */\r
+#define ErcNotAFile 202 /* Open a directory?? NOT */\r
+#define ErcNoSuchFile 203 /* No can do! It ain't there...*/\r
+#define ErcNoSuchDir 204 /* Ain't no such dir... */\r
+#define ErcReadOnly 205 /* You can't modify it bubba */\r
+#define ErcNoFreeFCB 206 /* We're really hurtin... */\r
+#define ErcBadOpenMode 207 /* Say what? Mode??? */\r
+#define ErcFileInUse 208 /* File is open in an incompatible mode */\r
+#define ErcNoFreeFUB 209 /* Sorry, out of File User Blocks */\r
+#define ErcBadFileHandle 210 /* WHOAAA, bad handle buddy! */\r
+#define ErcBrokenFile 211 /* Cluster chain broken on file */\r
+#define ErcBadFCB 213 /* We got REAL problems... */\r
+#define ErcStreamFile 214 /* Operation not allowed on Stream File */\r
+#define ErcBlockFile 215 /* Operation not allowed on Block File */\r
+#define ErcBeyondEOF 217 /* SetLFA or Read/WriteBlock beyond EOF */\r
+#define ErcNoParTable 218 /* No partiton table found on disk!!! */\r
+#define ErcBadFATClstr 220 /* File system screwed up (or your disk) */\r
+#define ErcRenameDrv 222 /* They have tried to rename across Dir/Vol*/\r
+#define ErcRenameDir 223 /* They have tried to rename across Dir/Vol*/\r
+#define ErcNoMatch 224 /* No matching directory entry */\r
+\r
+#define ErcWriteOnly 225 /* Attempt to read write-only device */\r
+#define ErcDupName 226 /* Name exists as a file or dir already */\r
+#define ErcNotSupported 227 /* Not supported on this file */\r
+#define ErcRootFull 228 /* The Root Directory is Full */\r
+#define ErcDiskFull 230 /* No more free CLUSTERS!!! */\r
+\r
+#define ErcNewMedia 605 /* for floppy mounting from FDD */\r
+\r
+/**************** FAT Buffer control structure **********************/\r
+\r
+/*\r
+ The Fat structures are for keeping track of the FAT buffers.\r
+ We never want to have more than one copy of a FAT sector in\r
+ memory at one time, and we also never want to read one when\r
+ its already here (a waste of time)! We also keep track\r
+ of the last time it was used and deallocate the oldest (LRU -\r
+ Least Recently Used). Initially filling out the Fat control\r
+ structure is part of the file system initialization. If the\r
+ FAT sector we are in has been modified (data written to clusters\r
+ in it & FAT updated) we write it ASAP!\r
+ Each FAT buffer is 1 sector long, except the first one which\r
+ is 3 sectors for floppies (FAT12 types). This is because the\r
+ FAT12 entires span sectors!\r
+*/\r
+\r
+#define nFATBufs 17 /* 1 Static for floppies + 16 * 512 = 8192, 2 pages */\r
+\r
+static struct fattype { /* */\r
+ U8 *pBuf; /* points to beginning of fat buffer */\r
+ U32 LastUsed; /* Tick when last used (0 = Never) */\r
+ U32 LBASect; /* LBA of first FAT sect in buf (where it came from) */\r
+ U16 iClstrStart; /* Starting cluster for each buf */\r
+ U8 Drive; /* LDrive this FAT sector is from */\r
+ U8 fModLock; /* Bit 0 = Modified, bit 1 = Locked */\r
+ };\r
+\r
+static struct fattype Fat[nFATBufs]; /* 16 bytes * 17 */\r
+\r
+/* We read 3 sectors worth of floppy fat buf in cause cluster\r
+entries span sectors\r
+*/\r
+\r
+U8 FatBufA[1536]; /* floppy fat buffer */\r
+\r
+#define FATMOD 0x01\r
+#define FATLOCK 0x02\r
+\r
+/**************** File Contol Block Structures (FCBs) **********/\r
+/* One FCB is allocated and filled out for each file that is open.\r
+ The actual directory entry structure from the disk is embedded\r
+ in the FCB so it can be copied directly to/from the directory\r
+ sector on the disk.\r
+*/\r
+#define nFCBs 128\r
+#define sFCB 64\r
+\r
+static struct FCB {\r
+ S8 Name[8]; /* From here to Filesize is copy of DirEnt */\r
+ S8 Ext[3];\r
+ S8 Attr; /* from MS-DOS */\r
+ U8 Resvd1[10]; /* ???????? */\r
+ U16 Time; /* Only changed when created or updated */\r
+ U16 Date;\r
+ U16 StartClstr; /* At least one per file!! */\r
+ U32 FileSize; /* last entry in FAT Dir Ent (32 bytes) */\r
+ U32 LBADirSect; /* LBA of directory sector this is from */\r
+ U16 oSectDirEnt; /* Offset in sector for the dir entry */\r
+ U8 Ldrv; /* Logical drive this is on (A-J, 0-9) */\r
+ U8 Mode; /* 0 or 1 (Read or Modify). */\r
+ U8 nUsers; /* Active FUBs for this file (255 MAX). 0= Free FCB */\r
+ U8 fMod; /* This file was modified! */\r
+ U8 Resvd[22]; /* Out to 64 bytes */\r
+ };\r
+\r
+static struct FCB *paFCB; /* a pointer to array of allocated FCBs. */\r
+static struct FCB *pFCB; /* pointer to one FCB */\r
+\r
+/********************** File User Blocks **************************/\r
+\r
+/* Each user of an open file is assigned a FUB. The FUB number is the\r
+ filehandle (beginning with 3). ) 0, 1 & 2 are reserved for NUL,\r
+ KBD and VID devices.\r
+*/\r
+\r
+#define nFUBs 128\r
+#define sFUB 32\r
+\r
+/* The FUB contains information on a file related to a user's view\r
+ of the file. It is used to hold information on files opened\r
+ in stream and block mode. Three important fields in the FUB are:\r
+ LFABuf - LFA of first byte in buffer for a stream file.\r
+ Clstr - Clstr of last block read or stream fill.\r
+ LFAClstr - LFA of first byte in Clstr.\r
+\r
+ LFAClstr and Clstr give us a relative starting point when\r
+ reading a file from disk. If we didn't save this information\r
+ on the last access, we would have to "run" the cluster chain\r
+ everytime we wanted to read or access a file beyond the last\r
+ point we read. \r
+*/\r
+\r
+struct FUB {\r
+ U16 Job; /* User's Job Number. 0 if FUB is free. */\r
+ U16 iFCB; /* FCB number for this file (0 to nFCBs-1) */\r
+ U32 CrntLFA; /* Current Logical File Address (File Ptr) */\r
+ U8 *pBuf; /* Ptr to buffer if stream mode */\r
+ U32 sBuf; /* Size of buffer for Stream file in bytes */\r
+ U32 LFABuf; /* S-First LFA in Clstr Buffer */\r
+ U32 LFAClstr; /* LFA of Clstr (below). */\r
+ U16 Clstr; /* Last Cluster read */\r
+ U8 fModified; /* Data in buffer was modified */\r
+ U8 fStream; /* NonZero for STREAM mode */\r
+ U8 Rsvd[4]; /* Pad to 32 bytes */\r
+ };\r
+\r
+static struct FUB *paFUB; /* a pointer to allocated FUBs. Set up at init. */\r
+static struct FUB *pFUB; /* a pointer to allocated FUBs. Set up at init. */\r
+\r
+/* Boot sector info (62 byte structure) */\r
+struct fsbtype {\r
+ U8 Jmp[3];\r
+ U8 OEMname[8];\r
+ U16 bps;\r
+ U8 SecPerClstr;\r
+ U16 ResSectors;\r
+ U8 FATs;\r
+ U16 RootDirEnts;\r
+ U16 Sectors;\r
+ U8 Media;\r
+ U16 SecPerFAT;\r
+ U16 SecPerTrack;\r
+ U16 Heads;\r
+ U32 HiddenSecs;\r
+ U32 HugeSecs;\r
+ U8 DriveNum;\r
+ U8 Rsvd1;\r
+ U8 BootSig;\r
+ U32 VolID;\r
+ U8 VolLabel[11];\r
+ U8 FileSysType[8]; /* 62 bytes */\r
+ };\r
+static struct fsbtype fsb;\r
+\r
+\r
+/* Partition Table Entry info. 16 bytes */\r
+struct partent {\r
+ U8 fBootable;\r
+ U8 HeadStart;\r
+ U8 SecStart;\r
+ U8 CylStart;\r
+ U8 FATType;\r
+ U8 HeadEnd;\r
+ U8 SecEnd;\r
+ U8 CylEnd;\r
+ U32 nFirstSector;\r
+ U32 nSectorsTotal;\r
+ };\r
+\r
+static struct partent partab[4]; /* 4 partition table entries 64 bytes */\r
+static U16 partsig;\r
+\r
+/* Bit definitions in attribute field for a directory entry */\r
+\r
+#define ATTRNORM 0x00\r
+#define READONLY 0x01\r
+#define HIDDEN 0x02\r
+#define SYSTEM 0x04\r
+#define VOLNAME 0x08\r
+#define DIRECTORY 0x10\r
+#define ARCHIVE 0x20\r
+\r
+/* Directory Entry Record, 32 bytes */\r
+\r
+struct dirstruct {\r
+ U8 Name[8];\r
+ U8 Ext[3];\r
+ U8 Attr;\r
+ U8 Rsvd[10];\r
+ U16 Time;\r
+ U16 Date;\r
+ U16 StartClstr;\r
+ U32 FileSize;\r
+ };\r
+\r
+static struct dirstruct dirent;\r
+\r
+static struct dirstruct *pDirEnt; /* a pointer to a dir entry */\r
+\r
+/* When a file is opened, the filename is parsed into an array\r
+ to facilitate searching the directory tree. IN MS-DOS all\r
+ dir and file names are SPACE padded (20h). The FileSpec array\r
+ contains the fully parsed path of the file. For instance,\r
+ If you were to open "A:\Dog\Food\IsGood.txt" the FileSpec\r
+ array would look like this:\r
+ FileSpec[0] = "DOG "\r
+ FileSpec[1] = "FOOD "\r
+ FileSpec[2] = "ISGOOD TXT"\r
+ FileSpec[3][0] = NULL;\r
+ Note that the DOT is not inlcuded (it's not in the DOS directory\r
+ either), and the next unused FileSpec entry contain NULL in the\r
+ first byte. SpecDepth tells us how many directories deep the\r
+ name goes.\r
+*/\r
+\r
+static U8 FDrive /* Drive parsed from file operation */\r
+static U8 FileSpec[7][11]; /* Hierarchy from file spec parsing */\r
+static U8 SpecDepth; /* Depth of parse (0=Root File) */\r
+\r
+/* Used for Rename */\r
+static U8 FDrive1 /* Drive parsed from file operation */\r
+static U8 FileSpec1[7][11]; /* Hierarchy from file spec parsing */\r
+static U8 SpecDepth1; /* Depth of parse (0=Root File) */\r
+\r
+/* raw sector buffer for all kinds of stuff */\r
+\r
+static U8 abRawSector[516];\r
+static U8 abTmpSector[516];\r
+static U8 abDirSectBuf[516];\r
+\r
+/* These arrays keep track of physical drive data (0-4). */\r
+#define nPDrvs 4\r
+\r
+static struct phydrv {\r
+ U32 nHeads; /* heads per drives */\r
+ U32 nSecPerTrk; /* Sectors per track */\r
+ U16 BS1Cyl; /* Cyl of 1st boot sector on disk */\r
+ U8 BS1Head; /* Head of 1st boot sector on disk */\r
+ U8 BS1Sect; /* Sector of 1st boot sector on disk */\r
+ }\r
+\r
+static struct phydrv PDrvs[nPDrvs];\r
+\r
+/* This array of structures keeps track of logical drive data (A-J). */\r
+\r
+#define nLDrvs 10\r
+\r
+static struct ldrvtype {\r
+ U32 LBA0; /* lba for Start of LDrive (bootSect) */\r
+ U32 LBAData; /* lba for Start of Data Area */\r
+ U32 LBAMax; /* Max lba for logical drive */\r
+ U32 LBARoot; /* lba of the Root directory */\r
+ U32 LBAFAT; /* lba of first FAT */\r
+ U16 nHeads; /* Setup after boot sector is read */\r
+ U16 nSecPerTrk; /* Setup after boot sector is read */\r
+ U16 nRootDirEnt; /* Number of Root directory entries */\r
+ U16 sFAT; /* nSectors in a FAT */\r
+ U8 DevNum; /* Device Number for this ldrv FF = NONE */\r
+ U8 SecPerClstr; /* For each logical drive */\r
+ U8 nFATS; /* number of FATs */\r
+ U8 fFAT16; /* True for FAT16 else FAT12 */\r
+ };\r
+\r
+static struct ldrvtype Ldrv[nLDrvs];\r
+\r
+/* This is the Hard Disk Device Status record.\r
+ It is peculiar to the HD Drvr */\r
+\r
+struct hddevtype{\r
+ U32 erc;\r
+ U32 blocks_done;\r
+ U32 BlocksMax;\r
+ U8 fNewMedia;\r
+ U8 type_now; /* current fdisk_table for drive selected */\r
+ U8 resvd0[2]; /* padding for DWord align */\r
+ U32 nCyl; /* total physical cylinders (we really don't care) */\r
+ U32 nHead; /* total heads on device */\r
+ U32 nSectors; /* Sectors per track */\r
+ U32 nBPS; /* Number of bytes per sect. 32 bytes out to here.*/\r
+ U32 LastRecalErc0;\r
+ U32 LastSeekErc0;\r
+ U8 LastStatByte0;\r
+ U8 LastErcByte0;\r
+ U8 fIntOnReset; /* Interrupt was received on HDC_RESET */\r
+ U8 filler0;\r
+ U32 LastRecalErc1;\r
+ U32 LastSeekErc1;\r
+ U8 LastStatByte1;\r
+ U8 LastErcByte1;\r
+ U8 ResetStatByte; /* Status Byte immediately after RESET */\r
+ U8 filler1;\r
+ U32 resvd1[2]; /* out to 64 bytes */\r
+ };\r
+\r
+static struct hddevtype HDDevStat;\r
+\r
+/* This is the Floppy Device Status record.\r
+ It is peculiar to the FD Drvr */\r
+\r
+struct fdstattype{\r
+ U32 erc; /* Last Error from device */\r
+ U32 blocks_done;\r
+ U32 BlocksMax;\r
+ U8 fNewMedia;\r
+ U8 type_now; /* current fdisk_table for drive selected */\r
+ U8 resvd1[2]; /* padding for DWord align */\r
+ U32 nCyl; /* total physical cylinders */\r
+ U32 nHead; /* total heads on device */\r
+ U32 nSectors; /* Sectors per track */\r
+ U32 nBPS; /* Number of bytes per sect */\r
+ U8 params[16]; /* begin device specific fields */\r
+ U8 STATUS[8]; /* status returned from FDC (for user status) */\r
+ U32 resvd3;\r
+ U32 resvd4; /* 64 bytes total */\r
+ };\r
+\r
+static struct fdstattype FDDevStat;\r
+\r
+static long FSysStack[512]; /* 2048 byte stack for Fsys task */\r
+\r
+static long FSysExch;\r
+\r
+struct reqtype { /* 64 byte request block structure */\r
+ long ServiceExch;\r
+ long RespExch;\r
+ long RqOwnerJob;\r
+ long ServiceRoute;\r
+ char *pRqHndlRet;\r
+ long dData0;\r
+ long dData1;\r
+ long dData2;\r
+ int ServiceCode;\r
+ char npSend;\r
+ char npRecv;\r
+ char *pData1;\r
+ long cbData1;\r
+ char *pData2;\r
+ long cbData2;\r
+ long RQBRsvd1;\r
+ long RQBRsvd2;\r
+ long RQBRsvd3;\r
+ };\r
+\r
+static struct reqtype *pRQB;\r
+\r
+static char *fsysname = "FILESYSM";\r
+\r
+static unsigned long keycode; /* for testing */\r
+\r
+/*========================== BEGIN CODE ============================*/\r
+\r
+/************************************************\r
+ Called from read_PE, this gets the starting\r
+ cylinder, head and sector for the first boot\r
+ sector on a physical drive and stores it in the\r
+ phydrv array. d is the drive, i is the index\r
+ into the partition table we read in.\r
+*************************************************/\r
+\r
+static void GetBSInfo(U32 d, U32 i)\r
+{\r
+ PDrvs[d].BS1Head = partab[i].HeadStart;\r
+ PDrvs[d].BS1Sect = partab[i].SecStart;\r
+ PDrvs[d].BS1Cyl = partab[i].CylStart;\r
+\r
+ if (!i) \r
+ { /* primary partition info - use it for PDrv info */\r
+ PDrvs[d].nHeads = partab[i].HeadEnd;\r
+ PDrvs[d].nSecPerTrk = partab[i].nFirstSector & 0xff;\r
+ }\r
+}\r
+\r
+/** InitFloppy *********************************\r
+ This gets status from the floppy drive (device ld)\r
+ and sets the physical & logical drive parameters\r
+ for the type. It is called when the file system\r
+ is first initialized and when there has been\r
+ an error on the floppy.\r
+*************************************************/\r
+\r
+static U32 StatFloppy(U8 ld)\r
+{\r
+U32 erc, i;\r
+\r
+/* Set gets status for the floppy type from the FDD and\r
+ sets logical paramters for Ldrvs.\r
+*/\r
+\r
+Ldrv[0].DevNum= 10; /* Device Numbers for floppies */\r
+Ldrv[1].DevNum= 11;\r
+\r
+erc = DeviceStat(ld+10, &FDDevStat, 64, &i);\r
+if (!erc) \r
+{\r
+ PDrvs[ld].nHeads = FDDevStat.nHead;\r
+ PDrvs[ld].nSecPerTrk = FDDevStat.nSectors;\r
+ Ldrv[ld].LBA0 = 0; /* Floppy Boot Sector - always 0 */\r
+ Ldrv[ld].LBAMax= FDDevStat.BlocksMax-1; /* Max lba for logical drive 0 */\r
+\r
+ Ldrv[ld].nHeads = FDDevStat.nHead;\r
+ Ldrv[ld].nSecPerTrk = FDDevStat.nSectors;\r
+\r
+ erc = 0;\r
+}\r
+else\r
+ Ldrv[ld].DevNum = 0xff;\r
+\r
+ return erc;\r
+}\r
+\r
+/************************************************\r
+ Reads the partition table entries from hard\r
+ drives and sets up some of the the logical\r
+ drive array variables for hard Disks.\r
+ It also saves first cylinder, head and sector\r
+ of the first partiton on each physical drive\r
+ so we can get more info for the LDrv arrays\r
+ from the boot sector of that partition.\r
+*************************************************/\r
+\r
+static U32 read_PE(void)\r
+{\r
+U32 erc, ercD12, ercD13, i, j;\r
+U8 fFound1, fFound2;\r
+\r
+fFound1 = 0; /* Have we found first valid partition on drive */\r
+fFound2 = 0;\r
+\r
+/* Set defaults for 4 physical drives. This info will be set\r
+ correctly when the partition table and boot sectors are read.\r
+*/\r
+\r
+for (i=2; i< nLDrvs; i++) \r
+{ /* default to no logical hard drives */\r
+ Ldrv[i].DevNum = 0xff;\r
+}\r
+\r
+i = 2; /* first Logical Number for hard drives "C" */\r
+\r
+for (j=2; j<4; j++) \r
+{ /* Array index Numbers for 2 physical hard Disks */\r
+\r
+ erc = DeviceOp(j+10, 1, 0, 1, abRawSector); /* add 10 for Disk device nums */\r
+ if (j==2) ercD12 = erc;\r
+ else ercD13 = erc;\r
+\r
+ if (!erc) \r
+ {\r
+ CopyData(&abRawSector[0x01fe], &partsig, 2);\r
+\r
+ /* It MUST have a partition table or we can't use it! */\r
+\r
+ if (partsig != 0xAA55) return ErcNoParTable;\r
+\r
+ CopyData(&abRawSector[0x01be], &partab[0].fBootable, 64);\r
+\r
+/*\r
+ Dump(&partab[0].fBootable, 64);\r
+ ReadKbd(&keycode, 1);\r
+*/\r
+\r
+ if (partab[0].nSectorsTotal > 0) \r
+ {\r
+ Ldrv[i].LBA0 =partab[0].nFirstSector; /* lba for Start of LDrv (bootSect) */\r
+ Ldrv[i].LBAMax =partab[0].nSectorsTotal; /* Max lba for logical drive */\r
+ if (partab[0].FATType > 3)\r
+ Ldrv[i].fFAT16 = 1;\r
+ Ldrv[i].DevNum = j+10;\r
+ if ((j==2) && (!fFound1)) \r
+ { GetBSInfo(2, 0); fFound1=1; }\r
+ if ((j==3) && (!fFound2))\r
+ { GetBSInfo(3, 0); fFound2=1; }\r
+ i++; /* if valid partition go to next LDrv */\r
+ }\r
+\r
+ if (partab[1].nSectorsTotal > 0) \r
+ {\r
+ Ldrv[i].LBA0 = partab[1].nFirstSector;\r
+ Ldrv[i].LBAMax = partab[1].nSectorsTotal;\r
+ if (partab[1].FATType > 3)\r
+ Ldrv[i].fFAT16 = 1;\r
+ Ldrv[i].DevNum = j+10;\r
+ if ((j==2) && (!fFound1)) { GetBSInfo(2, 1); fFound1=1; }\r
+ if ((j==3) && (!fFound2)) { GetBSInfo(3, 1); fFound2=1; }\r
+ i++; /* if we had a valid partition go to next */\r
+ }\r
+\r
+ if (partab[2].nSectorsTotal > 0) \r
+ {\r
+ Ldrv[i].LBA0 = partab[2].nFirstSector;\r
+ Ldrv[i].LBAMax = partab[2].nSectorsTotal;\r
+ if (partab[2].FATType > 3)\r
+ Ldrv[i].fFAT16 = 1;\r
+ Ldrv[i].DevNum = j+10;\r
+ if ((j==2) && (!fFound1)) { GetBSInfo(2, 2); fFound1=1; }\r
+ if ((j==3) && (!fFound2)) { GetBSInfo(3, 2); fFound2=1; }\r
+ i++; /* if we had a valid partition go to next */\r
+ }\r
+\r
+ if (partab[3].nSectorsTotal > 0) \r
+ {\r
+ Ldrv[i].LBA0 = partab[3].nFirstSector;\r
+ Ldrv[i].LBAMax = partab[3].nSectorsTotal;\r
+ if (partab[3].FATType > 3)\r
+ Ldrv[i].fFAT16 = 1;\r
+ Ldrv[i].DevNum = j+10;\r
+ if ((j==2) && (!fFound1)) \r
+ { \r
+ GetBSInfo(2, 3); \r
+ fFound1=1; \r
+ }\r
+ if ((j==3) && (!fFound2)) \r
+ { \r
+ GetBSInfo(3, 3); \r
+ fFound2=1; \r
+ }\r
+ i++; /* if we had a valid partition go to next */\r
+ }\r
+ }\r
+ }\r
+\r
+ if (ercD12) return ercD12; /* there may be no Device 13 */\r
+ else return 0;\r
+}\r
+\r
+/********************************************************************\r
+ Reads in the first boot sector from each physical drive to get\r
+ drive geometry info not available in partition table. This includes\r
+ number of heads and sectors per track. Then we call DeviceInit\r
+ for each physical device to set its internal drive geometry.\r
+ This must be done before we even try to read the other boot sectors\r
+ if the disk has mulitple partitions (otherwise it fails).\r
+*********************************************************************/\r
+\r
+static U32 SetDriveGeometry(U32 d) /* d is the device number (12 or 13) */\r
+{\r
+U32 erc, i;\r
+\r
+ if (d==12) \r
+ {\r
+ erc = DeviceStat(12, &HDDevStat, 64, &i);\r
+ if (!erc) \r
+ {\r
+ HDDevStat.nHead = PDrvs[2].nHeads;\r
+ HDDevStat.nSectors = PDrvs[2].nSecPerTrk;\r
+ erc = DeviceInit(12, &HDDevStat, 64); /* Set up drive geometry */\r
+ }\r
+ }\r
+\r
+ if (d==13) \r
+ {\r
+ erc = DeviceStat(13, &HDDevStat, 64, &i);\r
+ if (!erc) \r
+ {\r
+ HDDevStat.nHead = PDrvs[3].nHeads;\r
+ HDDevStat.nSectors = PDrvs[3].nSecPerTrk;\r
+ erc = DeviceInit(13, &HDDevStat, 64); /* Set up drive geometry */\r
+ }\r
+ }\r
+\r
+return erc;\r
+}\r
+\r
+/********************************************************************\r
+ Read boot sector from logical drive (i) and sets up logical and\r
+ physical drive array variables for the FAT file system found on\r
+ the logical drive (described in the boot sector).\r
+*********************************************************************/\r
+\r
+static U32 read_BS(U32 i)\r
+{\r
+U32 erc, j;\r
+\r
+if (Ldrv[i].DevNum != 0xff) \r
+{\r
+\r
+ j = Ldrv[i].DevNum; /* j is MMURTL Device number */\r
+\r
+ erc = DeviceOp(j, 1, Ldrv[i].LBA0, 1, abRawSector);\r
+\r
+ if ((erc==ErcNewMedia) && (i<2)) \r
+ {\r
+ erc = DeviceOp(j, 1, Ldrv[i].LBA0, 1, abRawSector);\r
+ }\r
+\r
+ CopyData(abRawSector, &fsb.Jmp, 62);\r
+\r
+ if (erc==0) \r
+ {\r
+ Ldrv[i].LBARoot = fsb.ResSectors + Ldrv[i].LBA0 +\r
+ (fsb.FATs * fsb.SecPerFAT);\r
+ Ldrv[i].nRootDirEnt = fsb.RootDirEnts; /* n Root dir entries */\r
+ Ldrv[i].SecPerClstr = fsb.SecPerClstr;\r
+ Ldrv[i].nHeads = fsb.Heads;\r
+ Ldrv[i].nSecPerTrk = fsb.SecPerTrack;\r
+ Ldrv[i].sFAT = fsb.SecPerFAT; /* nSectors in a FAT */\r
+ Ldrv[i].nFATS = fsb.FATs; /* number of FATs */\r
+ Ldrv[i].LBAFAT = Ldrv[i].LBA0 + fsb.ResSectors;\r
+ Ldrv[i].LBAData = Ldrv[i].LBARoot + (fsb.RootDirEnts / 16);\r
+ if (fsb.FileSysType[4] == '2')\r
+ Ldrv[i].fFAT16 = 0;\r
+\r
+ } /* if erc */\r
+} /* if valid logical device */\r
+return 0;\r
+}\r
+\r
+/*******************************************************\r
+ This gets the CMOS date & time and converts it into the\r
+ format for the DOS FAT file system. This is two words\r
+ with bits representing Year/Mo/day & Hr/Min/SecDIV2.\r
+********************************************************/\r
+static void GetFATTime(U16 *pTimeRet, U16 *pDateRet)\r
+{\r
+U32 date, time;\r
+U16 DDate, DTime, w;\r
+\r
+ GetCMOSDate(&date);\r
+ GetCMOSTime(&time);\r
+ /* Do the date */\r
+ DDate = (((date >> 12) & 0x0f) * 10) + ((date >> 8) & 0x0f); /* day */\r
+ w = (((date >> 20) & 0x0f) * 10) + ((date>>16) & 0x0f) + 2; /* month */\r
+ DDate |= (w << 4);\r
+ w = (((date >> 28) & 0x0f) * 10) + ((date >> 24) & 0x0f); /* year */\r
+ DDate |= (w + 1900 - 1980) << 9;\r
+ /* Do the time */\r
+ DTime = (((((time >> 4) & 0x0f) * 10) + (time & 0x0f))/2); /* secs/2 */\r
+ w = (((time >> 12) & 0x0f) * 10) + ((time >> 8) & 0x0f);\r
+ DTime |= (w << 5); /* mins */\r
+ w = (((time >> 20) & 0x0f) * 10) + ((time >> 16) & 0x0f); /* hours */\r
+ DTime |= (w << 11);\r
+ *pTimeRet = DTime;\r
+ *pDateRet = DDate;\r
+}\r
+\r
+\r
+/*******************************************************\r
+ This updates a directory entry by reading in the\r
+ sector it came from and placing the modifed entry\r
+ into it then writing it back to disk. The date is\r
+ also updated at this time.\r
+********************************************************/\r
+static U32 UpdateDirEnt(U32 iFCB)\r
+{\r
+U32 erc, i, j;\r
+U8 Drive;\r
+ Drive = paFCB[iFCB]->Ldrv; /* What logical drive are we on? */\r
+ i = paFCB[iFCB]->LBADirSect; /* Sector on disk */\r
+ j = paFCB[iFCB]->oSectDirEnt; /* offset in sector */\r
+\r
+ /* update time in dir entry */\r
+ GetFATTime(&paFCB[iFCB].Time, &paFCB[iFCB].Date);\r
+\r
+ /* Read sector into a buffer */\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, i, 1, abDirSectBuf);\r
+\r
+ if (!erc) \r
+ {\r
+ CopyData(&paFCB[iFCB], &abDirSectBuf[j], 32);\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, 1, abDirSectBuf);\r
+ }\r
+ return erc;\r
+}\r
+\r
+\r
+/*******************************************************\r
+ Checks the validity of the a file handle and also\r
+ returns the index to the FCB if the handle is OK.\r
+ The function return OK (0) if handle is good, else\r
+ a proper error code is returned.\r
+********************************************************/\r
+static U32 ValidateHandle(U32 dHandle, U32 *iFCBRet)\r
+{\r
+ /* get some checks out of the way first */\r
+\r
+ if (dHandle < 4) return ErcBadFileHandle;\r
+ if (dHandle >= nFUBs) return ErcBadFileHandle;\r
+ if (!paFUB[dHandle].Job) return ErcBadFileHandle;\r
+\r
+ /* Looks like a valid handle */\r
+ *iFCBRet = paFUB[dHandle]->iFCB;\r
+ return 0;\r
+}\r
+\r
+/*********************************************\r
+ Returns absolute disk address for the\r
+ cluster number you specify. This gives us\r
+ the LBA of the first sector of data that\r
+ the cluster number represents.\r
+ The sector number is returned from the fucntion.\r
+ Uses: Ldrv[CrntDrv].LBAData\r
+ Ldrv[CrntDrv].SecPerClstr\r
+**********************************************/\r
+\r
+static U32 ClsToLBA(U16 Clstr, U8 Drive)\r
+{\r
+U32 LBA;\r
+\r
+ Clstr-=2; /* Minus 2 cause 0 and 1 are reserved clusters */\r
+ LBA = Ldrv[Drive].SecPerClstr * Clstr;\r
+ LBA += Ldrv[Drive].LBAData;\r
+ return LBA;\r
+}\r
+\r
+\r
+/*******************************************************\r
+ This writes out the specified FAT sector back into\r
+ the FAT. It also checks to see if there is more\r
+ than one copy of the fat and updates the second copy\r
+ if it exists.\r
+********************************************************/\r
+static U32 UpdateFAT(U32 iFAT)\r
+{\r
+U32 erc, i, k;\r
+U8 Drive;\r
+\r
+ erc = 0;\r
+ if (Fat[iFAT].fModLock & FATMOD)\r
+ { /* Modified?? */\r
+\r
+ Drive = Fat[iFAT].Drive; /* What logical drive are we on? */\r
+ i = Fat[iFAT].LBASect; /* Where to write it back */\r
+\r
+ if (!iFAT)\r
+ { /* This is the floppy buffer [0] */\r
+ /* set up to write upto 3 sectors from the buffer */\r
+\r
+ if (i+2 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
+ k = 3;\r
+ else if (i+1 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
+ k = 2;\r
+ else\r
+ k = 1;\r
+ }\r
+ else\r
+ k=1;\r
+\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, k, Fat[iFAT].pBuf);\r
+ if (!erc)\r
+ {\r
+ Fat[iFAT].fModLock &= ~FATMOD; /* Not modified anymore */\r
+ if (Ldrv[Drive].nFATS > 1) \r
+ { /* 2 FATS! */\r
+ /* if we have two FATS we must update the second fat\r
+ also. This will be located directly aftrer the first\r
+ FAT (by exactly LDrv.sFat sectors).\r
+ */\r
+ i+= Ldrv[Drive].sFAT;\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, k, Fat[iFAT].pBuf);\r
+ }\r
+ }\r
+ }\r
+ return erc;\r
+}\r
+\r
+/*******************************************************\r
+ Reads in the FAT sector that contains the Cluster we\r
+ specified into a FAT buffer if it isn't already in\r
+ one. The index to the FAT buffer is returned.\r
+ Returns Error if not in FAT.\r
+ Uses: Ldrv[LDrive].LBAFAT\r
+ Ldrv[LDrive].fFAT16\r
+ Ldrv[LDrive].DevNum\r
+ Each sector of the FAT contains 256 cluster entries\r
+ for FAT16 types. To find it, we Divide the cluster\r
+ number by the count of entries (256), and add this\r
+ to the beginning sector of the FAT. It is SOOO\r
+ important (for speed) to have the FAT sectors in\r
+ memory, that we allocate the FAT buffers on a Least\r
+ Recently Used (LRU) basis for hard disk drives.\r
+\r
+ It's more complicated for a FAT12 types (floppies)\r
+ because cluster entries span fat sectors (they have\r
+ an odd number of nibbles). For this reason, we have\r
+ one 3 sector fat buffer for fat12 devices (floppies).\r
+ We fill it with up to 3 sectors. This is because the\r
+ last entry may span the sectors and we must be able\r
+ to read it. There are 1024 cluster entries in\r
+ a FAT12 3 sector buffer.\r
+*******************************************************/\r
+\r
+static U32 FindFatSect(U8 Drive, U16 Clstr, U32 *piFatRecRet, U8 fLock)\r
+{\r
+U32 i, j, k;\r
+U32 first, oSector, erc, LRU, iLRU, iFound, Tick;\r
+U16 MaxClstr;\r
+\r
+ if (Ldrv[Drive].fFAT16)\r
+ MaxClstr = 0xfff8;\r
+ else\r
+ MaxClstr = 0xff8; /* FAT12 */\r
+\r
+ if (Clstr >= MaxClstr)\r
+ return(ErcEOF);\r
+\r
+ if (Clstr < 2)\r
+ {\r
+ return (ErcBadFATClstr);\r
+ }\r
+\r
+ GetTimerTick(&Tick);\r
+\r
+ erc = 0; /* default to no error */\r
+\r
+ /* Set oSector to offset of sector in FAT\r
+ There are 256 cluster entries in 1 sector of a FAT16,\r
+ and 1024 in a FAT12 (3 sectors)\r
+ */\r
+\r
+ if (Ldrv[Drive].fFAT16)\r
+ {\r
+ oSector = Clstr/256;\r
+ first = Clstr-(Clstr%256);\r
+\r
+ /* Set i to LBA of FAT sector we need by adding\r
+ offset to beginning of FAT\r
+ */\r
+\r
+ i = oSector + Ldrv[Drive].LBAFAT;\r
+\r
+ /* If FAT sector is out of range there's a BAD problem... */\r
+\r
+ if (i >= Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
+ {\r
+ return (ErcBadFATClstr);\r
+ }\r
+ else\r
+ { /* Else we get it for them */\r
+\r
+ /* Loop through the Fat bufs and see if its in one already. */\r
+ /* Save the index of the LRU in case it's not there. */\r
+ /* Set iFound to index of FatBuf (if found). */\r
+ /* Otherwise, Set up iLRU to indicate what the oldest buffer is */\r
+\r
+ iFound = 0xffffffff;\r
+ LRU = 0xffffffff; /* saves tick of oldest one so far */\r
+ iLRU = 1; /* default */\r
+ for (j=1; j<nFATBufs; j++)\r
+ {\r
+ if (Fat[j].LastUsed > 0)\r
+ { /* Valid ? (ever been used) */\r
+ if ((first == Fat[j].iClstrStart) &&\r
+ (Drive == Fat[j].Drive))\r
+ {\r
+ iFound = j;\r
+ if (fLock)\r
+ Fat[j].fModLock |= FATLOCK;\r
+ break; /* Already IN! */\r
+ }\r
+ }\r
+ if (Fat[j].LastUsed < LRU)\r
+ {\r
+ LRU = Fat[j].LastUsed;\r
+ iLRU = j;\r
+ }\r
+ }\r
+\r
+ if (iFound != 0xffffffff)\r
+ { /* Its already in memory */\r
+ Fat[j].LastUsed = Tick; /* update LRU */\r
+ }\r
+ else\r
+ { /* else put into oldest buffer */\r
+ j = iLRU;\r
+\r
+ /* Check to see if Fat[iLRU] is valid and has been\r
+ modified. If it is, write it out before we read\r
+ the next one into this buffer. This done by\r
+ calling UpdateFAT(iFatRec).\r
+ */\r
+ if (Fat[j].fModLock & FATMOD)\r
+ erc = UpdateFAT(j);\r
+\r
+ if (!erc)\r
+ {\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, i, 1, Fat[j].pBuf);\r
+ Fat[j].Drive = Drive; /* Update Drive */\r
+ Fat[j].LastUsed = Tick; /* update LRU */\r
+ Fat[j].iClstrStart = first; /* update first cluster num */\r
+ Fat[j].LBASect = i; /* LBA this FAt sect came from */\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /* This is for FAT12s */\r
+\r
+ else\r
+ {\r
+ oSector = (Clstr/1024) * 3; /* X3 cause we read 3 at a time */\r
+ first = Clstr-(Clstr%1024);\r
+\r
+ /* Set i to LBA of FAT sector we need by adding offset (oSector)\r
+ to beginning of FAT */\r
+\r
+ i = oSector + Ldrv[Drive].LBAFAT;\r
+ j = 0;\r
+\r
+ /* If FAT sector is out of range there's a BAD problem... */\r
+\r
+ if (i >= Ldrv[Drive].sFAT)\r
+ return (ErcBadFATClstr);\r
+ else\r
+ { /* Else we get it for them */\r
+\r
+ /* Check the single floppy fat buf and see if its already there. */\r
+ /* Set iFound to index of FatBuf (if found). */\r
+\r
+ iFound = 0xffffffff;\r
+\r
+ if (Fat[0].LastUsed > 0)\r
+ { /* Valid ? (nonzero means it's been used) */\r
+ if ((first == Fat[0].iClstrStart) &&\r
+ (Drive == Fat[0].Drive))\r
+ {\r
+ iFound = 0;\r
+ if (fLock)\r
+ Fat[0].fModLock |= FATLOCK;\r
+ }\r
+ }\r
+\r
+ if (iFound == 0xffffffff)\r
+ {\r
+ /* It's not the one we want or isn't there.\r
+ Check to see if Fat[0] is valid and has been\r
+ modified. If it is, write it out before we read\r
+ the one we want into the buffer. This done by\r
+ calling UpdateFAT(iFatRec).\r
+ */\r
+ if (Fat[0].fModLock & FATMOD)\r
+ erc = UpdateFAT(0);\r
+\r
+ /* set up to read upto 3 sectors into buffer */\r
+\r
+ if (i+2 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
+ k = 3;\r
+ else if (i+1 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
+ k = 2;\r
+ else\r
+ k = 1;\r
+\r
+ if (!erc)\r
+ {\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, i, k, Fat[0].pBuf);\r
+\r
+ Fat[0].Drive = Drive; /* Update Drive */\r
+ Fat[0].LastUsed = Tick; /* update LRU */\r
+ Fat[0].iClstrStart = first; /* update first cluster num */\r
+ Fat[0].LBASect = i; /* LBA this FAT sect came from */\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ *piFatRecRet = j; /* Buffer that holds the sector(s) */\r
+\r
+ return (erc); /* Disk error Bad news */\r
+}\r
+\r
+/*********************************************\r
+ Returns the value found for this cluster\r
+ entry in a fat sector buffer. Values can be:\r
+ FAT16 FAT12\r
+ Next entry in the chain (0002-FFF0 (002-FF0)\r
+ Last entry in chain (FFF8 )(FF8 )\r
+ Available cluster (0 )(0 )\r
+ Bad Cluster (FFF7 )(FF7 )\r
+ (other vlaues are reserved).\r
+**********************************************/\r
+\r
+static U32 GetClstrValue(U16 Clstr, U8 Drive, U8 fLock,\r
+ U16 *pValRet, U32 *iFatBufRet)\r
+{\r
+U32 erc, oClstr, iFat;\r
+U16 ClstrVal, *pClstr;\r
+\r
+ erc = FindFatSect(Drive, Clstr, &iFat, fLock);\r
+\r
+ if (erc)\r
+ {\r
+ *pValRet= 0;\r
+ return(erc);\r
+ }\r
+\r
+ pClstr = Fat[iFat].pBuf;\r
+ oClstr = Clstr - Fat[iFat].iClstrStart; /* offset into FatBuf */\r
+\r
+ if (Ldrv[Drive].fFAT16)\r
+ { /* if drive is FAT16 type */\r
+ pClstr += oClstr * 2; /* WORDS in */\r
+ ClstrVal = *pClstr;\r
+ }\r
+ /* FAT12 entries are 1.5 bytes long (what a pain).\r
+ This means we get the offset and see whether it\r
+ is an odd or even byte, then take the proper nibble\r
+ by ANDing or shifting.\r
+ */\r
+ else\r
+ { /* a FAT12... */\r
+ pClstr += oClstr + (oClstr/2); /* 1.5 bytes in */\r
+ ClstrVal = *pClstr; /* We have 16 bits */\r
+ if (Clstr & 1) /* Odd, must shift */\r
+ ClstrVal >>= 4;\r
+ ClstrVal &= 0xfff;\r
+ }\r
+ *pValRet= ClstrVal;\r
+ *iFatBufRet = iFat;\r
+\r
+ return(erc);\r
+}\r
+\r
+\r
+/*************************************************\r
+ Sets the value in Clstr to the value in\r
+ NextClstr which will be one of the following\r
+ values: FAT16 FAT12\r
+ Next entry in the chain (0002-FFEF (002-FEF)\r
+ Last entry in chain (FFFF )(FFF )\r
+ Available cluster (0 )(0 )\r
+ Bad Cluster (FFF7 )(FF7 )\r
+ (other vlaues are reserved).\r
+ This marks the associated fat buffer as modified.\r
+ This is the ONLY call that modifies a FAT buffer!\r
+**************************************************/\r
+\r
+static U32 SetClstrValue(U16 Clstr, U16 NewClstrVal, U8 Drive, U32 *iFatBufRet)\r
+{\r
+U32 erc, oClstr, iFat;\r
+U16 ClstrVal, *pClstr, ClstrSave;\r
+\r
+ erc = FindFatSect(Drive, Clstr, &iFat, 0);\r
+ if (erc)\r
+ {\r
+ *iFatBufRet = 0;\r
+ return(erc);\r
+ }\r
+\r
+ pClstr = Fat[iFat].pBuf;\r
+ oClstr = Clstr - Fat[iFat].iClstrStart; /* offset into FatBuf*/\r
+ if (Ldrv[Drive].fFAT16) \r
+ { /* if drive is FAT16 type */\r
+ pClstr += oClstr * 2; /* WORDS in */\r
+ *pClstr = NewClstrVal;\r
+ }\r
+ /* FAT12 entries are 1.5 bytes long (remember??).\r
+ SAVE THE CORRECT NIBBLE OF THE ADJACENT CLUSTER!!\r
+ */\r
+ else\r
+ { /* a FAT12... */\r
+ pClstr += oClstr + (oClstr/2); /* 1.5 bytes in */\r
+ ClstrSave = *pClstr; /* We have 16 bits */\r
+ if (Clstr & 1) \r
+ { /* Odd, must shift */\r
+ NewClstrVal <<= 4;\r
+ NewClstrVal &= 0xfff0;\r
+ ClstrVal = (ClstrSave & 0x0F) | NewClstrVal;\r
+ }\r
+ else \r
+ {\r
+ NewClstrVal &= 0x0fff;\r
+ ClstrVal = (ClstrSave & 0xf000) | NewClstrVal;\r
+ }\r
+ *pClstr = ClstrVal;\r
+ }\r
+ Fat[iFat].fModLock |= FATMOD;\r
+ *iFatBufRet = iFat;\r
+ return(erc);\r
+}\r
+\r
+\r
+/*************************************************\r
+ Read the FAT and get the cluster number for the\r
+ next cluster in the chain for the Clstr specifed.\r
+ This returns 0 and an error if failed.\r
+ 0 is an illegal cluster number.\r
+ Remember, the cluster you are on is actually the\r
+ number of the next cluster in a linked list!\r
+**************************************************/\r
+\r
+static U32 NextFATClstr(U8 Drive, U16 Clstr, U16 *pNextClstrRet)\r
+{\r
+U32 erc, i;\r
+U16 NextClstr;\r
+\r
+ erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);\r
+\r
+ if (erc)\r
+ {\r
+ *pNextClstrRet = 0;\r
+ return(erc);\r
+ }\r
+ *pNextClstrRet = NextClstr;\r
+ return(0);\r
+}\r
+\r
+/*************************************************\r
+ This allocates the next empty cluster on the disk\r
+ to the tail of the clstr that is passed in.\r
+ LastClstr is a valid cluster of a file or \r
+ directory (and MUST be the last one). \r
+ We error out if it isn't!\r
+ This returns 0 and an error if it fails.\r
+ Remember, the cluster you are on is actually the\r
+ number of the next cluster in a linked list!\r
+ This looks through the current and succesive\r
+ FAT sectors (if needed) to add to the file.\r
+ A cluster is available to allocate if it is\r
+ 0. This is strictly a first fit algorithm.\r
+**************************************************/\r
+\r
+static U32 ExtendClstrChain(U8 Drive, U16 LastClstr, U16 *pNextClstrRet)\r
+{\r
+U32 erc, i, j, k;\r
+U16 ClstrValue, MaxClstr, CrntClstr;\r
+U8 fFound;\r
+\r
+ if (Ldrv[Drive].fFAT16)\r
+ MaxClstr = 0xfff8;\r
+ else\r
+ MaxClstr = 0xff8; /* FAT12 */\r
+\r
+ /* i is index to Fat with last sector of current chain */\r
+\r
+ erc = GetClstrValue(LastClstr, Drive, 1, &ClstrValue, &i);\r
+ if (erc) \r
+ {\r
+ *pNextClstrRet = 0;\r
+ return(erc);\r
+ }\r
+\r
+ if (ClstrValue < MaxClstr) \r
+ { /* no need to extend it */\r
+ *pNextClstrRet = ClstrValue;\r
+ Fat[i].fModLock &= ~FATLOCK; /* unlock it */\r
+ return(0);\r
+ }\r
+\r
+ /* OK... now we have the Fat sector and the offset in the Fat\r
+ buf of the last cluster allocated to this file. Let's go\r
+ further into the buffer and try to get an empty one.\r
+ */\r
+\r
+ CrntClstr = LastClstr;\r
+ fFound = 0;\r
+ while (!fFound) \r
+ {\r
+ ++CrntClstr; /* next cluster */\r
+ erc = GetClstrValue(CrntClstr, Drive, 0, &ClstrValue, &j);\r
+ if (erc) \r
+ {\r
+ *pNextClstrRet = 0;\r
+ Fat[i].fModLock &= ~FATLOCK; /* unlock previous lastclstr */\r
+ return(erc);\r
+ }\r
+ if (!ClstrValue) \r
+ {\r
+ fFound = 1; /* found an empty one */\r
+ }\r
+ }\r
+\r
+ if (fFound) \r
+ { /* CrntClstr is index to empty one */\r
+\r
+ /* Set the LastCluster to point to the new cluster found */\r
+\r
+ erc = SetClstrValue(LastClstr, CrntClstr, Drive, &k);\r
+ if (erc) \r
+ {\r
+ *pNextClstrRet = 0;\r
+ Fat[i].fModLock &= ~FATLOCK; /* unlock previous lastclstr */\r
+ return(erc);\r
+ }\r
+ Fat[k].fModLock &= ~FATLOCK; /* unlock it */\r
+\r
+ /* Set the newcluster to "end Cluster" chain value */\r
+\r
+ erc = SetClstrValue(CrntClstr, 0xFFFF, Drive, &j);\r
+ }\r
+ *pNextClstrRet = CrntClstr;\r
+ return(erc);\r
+}\r
+\r
+/*************************************************\r
+ This truncates the file chain to the cluster\r
+ specified (makes it the last cluster).\r
+ This means we walk the rest of the chain setting\r
+ all the entries to 0 (so they can be reallocated).\r
+ This returns an error if failed.\r
+**************************************************/\r
+\r
+static U32 TruncClstrChain(U8 Drive, U16 Clstr)\r
+{\r
+U32 erc, i;\r
+U16 MaxClstr, NextClstr, CrntClstr;\r
+\r
+ if (Ldrv[Drive].fFAT16)\r
+ MaxClstr = 0xfff8;\r
+ else\r
+ MaxClstr = 0xff8; /* FAT12 */\r
+\r
+ /* i will be index to FatRec with last sector of current chain */\r
+\r
+ erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);\r
+ if (erc)\r
+ return(erc);\r
+\r
+ if (NextClstr >= MaxClstr)\r
+ { /* no need to truncate it */\r
+ return(0); /* It's already the end. */\r
+ }\r
+\r
+ /* OK... now we cut it off all the way down the chain.\r
+ We start by placing MaxClstr in the last sector and\r
+ then 0 in all entries to the end of the chain.\r
+ */\r
+\r
+ erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);\r
+ if (erc)\r
+ return(erc);\r
+ erc = SetClstrValue(Clstr, 0xFFFF, Drive, &i); /* new end of chain */\r
+ if (erc)\r
+ return(erc);\r
+\r
+ while ((NextClstr) && (NextClstr < MaxClstr)) \r
+ {\r
+ CrntClstr = NextClstr;\r
+ erc = GetClstrValue(CrntClstr, Drive, 0, &NextClstr, &i);\r
+ if (erc)\r
+ return(erc);\r
+ erc = SetClstrValue(CrntClstr, 0, Drive, &i); /* Free it up */\r
+ if (erc)\r
+ return(erc);\r
+ }\r
+\r
+ /* DONE! */\r
+ return(0);\r
+}\r
+\r
+/********************************************************\r
+ This finds the absolute cluster you want from the\r
+ LFA in a particular file. The file handle must already\r
+ be validated! It also returns the relative LFA of\r
+ the beginning of this cluster.\r
+*********************************************************/\r
+\r
+static U32 GetAbsoluteClstr(U32 dHandle, U32 dLFA,\r
+ U16 *pClstrRet, U32 *prLFARet)\r
+{\r
+U32 erc, iFCB, spc, bpc, rLFA;\r
+U16 rClstrWant, rClstrNow, Clstr, MaxClstr;\r
+U8 Drive;\r
+\r
+ iFCB = paFUB[dHandle]->iFCB;\r
+ Drive = paFCB[iFCB]->Ldrv; /* What logical drive are we on? */\r
+ spc = Ldrv[Drive].SecPerClstr; /* sectors per cluster */\r
+ bpc = spc * 512; /* bytes per cluster */\r
+\r
+ if (Ldrv[Drive].fFAT16)\r
+ MaxClstr = 0xfff8;\r
+ else\r
+ MaxClstr = 0xff8; /* FAT12 */\r
+\r
+/*\r
+ Calculate relative by dividing cluster size in bytes by dLFA.\r
+ If zero, we want the 1st cluster which is listed in the FCB.\r
+ If it is greater than zero, we have to "walk the FAT cluster\r
+ chain" until we reach the one we want, then read it in.\r
+\r
+ The FUB fields LFAClstr and Clstr store the file LFA of the last\r
+ cluster in this file that was read or written. This means if the\r
+ LFA is higher than the last read or written, we don't waste the\r
+ time reading the whole chain. We start from where we are.\r
+\r
+ The major difference is we may not be reading the first sector\r
+ in the cluster. We figure this out from the dLFA as compared to\r
+ LFAClstr.\r
+\r
+*/\r
+\r
+ rClstrWant = dLFA / bpc; /* Relative clstr they want */\r
+ rClstrNow = paFUB[dHandle]->LFAClstr / bpc; /* Rel 'Clstr' in FUB */\r
+\r
+ if (rClstrWant < rClstrNow)\r
+ { /* Is it earlier in the file? */\r
+ Clstr = paFCB[iFCB]->StartClstr; /* Yes, start at the beginning */\r
+ rClstrNow = 0;\r
+ rLFA = 0;\r
+ }\r
+ else\r
+ {\r
+ Clstr = paFUB[dHandle]->Clstr; /* No, start at current cluster */\r
+ rLFA = paFUB[dHandle]->LFAClstr; /* LFA of this cluster */\r
+ }\r
+\r
+ /* We need to run the cluster chain if rClstrNow < ClstrWant */\r
+\r
+ while ((rClstrNow < rClstrWant) && /* haven't reach it yet */\r
+ (Clstr < MaxClstr) && /* Not last cluster */\r
+ (Clstr))\r
+ { /* A valid cluster */\r
+ erc = NextFATClstr(Drive, Clstr, &Clstr);\r
+ if (erc)\r
+ return(erc);\r
+ ++rClstrNow;\r
+ rLFA += bpc;\r
+ }\r
+\r
+ if (rClstrNow != rClstrWant) /* Cluster chain appears broken... */\r
+ return ErcBrokenFile;\r
+\r
+ *pClstrRet = Clstr;\r
+ *prLFARet = rLFA;\r
+ return(0);\r
+}\r
+\r
+\r
+/*******************************************************\r
+ SetFileSize sets the FileSize entry in the FCB for\r
+ the handle specified. This means we will allocate\r
+ or deallocate clusters as necessary to satisfy this\r
+ request. The file MUST be open in MODE MODIFY.\r
+ This must be done before a file can be written\r
+ to beyond current FileSize (Block and Stream types).\r
+********************************************************/\r
+\r
+static U32 SetFileSizeM(U32 dHandle, U32 dSize)\r
+{\r
+U32 erc, i, iFCB, rLFA, lfaEOF;\r
+U32 CrntSize, nCrntClstrs, spc, bpc, nClstrsWant;\r
+U16 Clstr;\r
+U8 Drive;\r
+\r
+ erc = ValidateHandle(dHandle, &iFCB);\r
+ if (erc)\r
+ return erc;\r
+ if (!paFCB[iFCB]->Mode)\r
+ return ErcReadOnly;\r
+\r
+ Drive = paFCB[iFCB]->Ldrv; /* What logical drive are we on? */\r
+ spc = Ldrv[Drive].SecPerClstr; /* sectors per cluster */\r
+ bpc = spc * 512; /* bytes per cluster */\r
+\r
+ /* Looks like it's valid to change the size */\r
+\r
+ CrntSize = paFCB[iFCB]->FileSize;\r
+ if (CrntSize)\r
+ lfaEOF = CrntSize - 1;\r
+ else\r
+ lfaEOF = 0;\r
+\r
+ if (CrntSize == dSize) /* No need to do anything! */\r
+ return(0);\r
+\r
+ nCrntClstrs = CrntSize/bpc; /* nClusters currently */\r
+ if (CrntSize%bpc)\r
+ nCrntClstrs++;\r
+\r
+ if (!CrntSize)\r
+ nCrntClstrs = 1; /* ZERO length files have 1 Clstr! */\r
+\r
+ nClstrsWant = dSize/bpc; /* nClusters they we need */\r
+ if (dSize%bpc)\r
+ nClstrsWant++;\r
+\r
+ if (!dSize)\r
+ nClstrsWant = 1; /* ZERO length files have 1 Clstr! */\r
+\r
+\r
+ if (nClstrsWant == nCrntClstrs)\r
+ erc = 0;\r
+\r
+ else if (nClstrsWant > nCrntClstrs)\r
+ { /* Need to extend allocation */\r
+\r
+ /* get the last cluster in the file */\r
+ erc = GetAbsoluteClstr(dHandle, lfaEOF, &Clstr, &rLFA);\r
+ i = nCrntClstrs;\r
+ while ((!erc) && (i < nClstrsWant))\r
+ {\r
+ erc = ExtendClstrChain(Drive, Clstr, &Clstr);\r
+ i++;\r
+ }\r
+ }\r
+ else if (nClstrsWant < nCrntClstrs)\r
+ { /* Need to truncate it */\r
+\r
+ /* Get to cluster where it should be truncated. Set lfaEOF\r
+ to NEW lfaEOF to find where last valid cluster should be. */\r
+\r
+ if (dSize)\r
+ lfaEOF = dSize - 1;\r
+ else\r
+ lfaEOF = 0;\r
+\r
+ erc = GetAbsoluteClstr(dHandle, lfaEOF, &Clstr, &rLFA);\r
+ if (!erc)\r
+ erc = TruncClstrChain(Drive, Clstr);\r
+\r
+ /* Now we must ensure that the cluster helper is NOT\r
+ beyond EOF!\r
+ */\r
+ if (paFUB->LFAClstr >= dSize)\r
+ {\r
+ paFUB->LFAClstr = 0;\r
+ paFUB[dHandle]->Clstr = paFCB[iFCB]->StartClstr;\r
+ }\r
+\r
+ }\r
+ if (!erc)\r
+ {\r
+ paFCB[iFCB]->FileSize = dSize;\r
+ paFCB[iFCB]->fMod = 1;\r
+ }\r
+\r
+ return erc;\r
+}\r
+\r
+\r
+\r
+/*******************************************************************\r
+ This searches a directory beginning at Clstr for pName and returns\r
+ a pointer to the 32 byte dir entry which is in a temporary buffer.\r
+ If not found, returns NIL. When searching directories, if the\r
+ filename begins with a NULL the search need go no futher!\r
+********************************************************************/\r
+\r
+static U32 GetDirEnt(U8 *pName,\r
+ U8 Drive,\r
+ U16 Clstr,\r
+ U32 *pLBARet,\r
+ U32 *poEntRet,\r
+ U8 **pEntRet)\r
+{\r
+unsigned long sector, i, j, k, erc;\r
+U8 fFound, fEnd, *pEnt, *pStart;\r
+U16 MaxClstr;\r
+\r
+ j = Ldrv[Drive].SecPerClstr; /* How many sectors per cluster */\r
+ sector = ClsToLBA(Clstr, Drive); /* absolute sector of first dir sector */\r
+ if (Ldrv[Drive].fFAT16)\r
+ MaxClstr = 0xfff8;\r
+ else\r
+ MaxClstr = 0xff8; /* FAT12 */\r
+ i = 0;\r
+ fEnd=0;\r
+ fFound=0;\r
+\r
+ fFound= 0;\r
+ while ((!fFound) && (!fEnd)) \r
+ { /* while there are valid entries */\r
+ if (i==j) \r
+ { /* reached last dir sector of this cluster */\r
+ erc = NextFATClstr(Drive, Clstr, &Clstr);\r
+ if (!erc) \r
+ {\r
+ if (Clstr >= MaxClstr) /* last sector */\r
+ return(ErcNoSuchFile); /* not found */\r
+ sector = ClsToLBA(Clstr, Drive); /* LBA of next dir sector */\r
+ i=0;\r
+ }\r
+ else \r
+ {\r
+ *pEntRet = 0;\r
+ return(erc);\r
+ }\r
+ }\r
+\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector++, 1, abDirSectBuf);\r
+ if (erc)\r
+ return(erc);\r
+\r
+ ++i; /* next sector in cluster */\r
+\r
+ pEnt = &abDirSectBuf[0];\r
+ pStart = pEnt;\r
+\r
+ for (k=0; k<16; k++) \r
+ { /* 16 entries per sector */\r
+ if (*pEnt==0) \r
+ { /* 0 in a DirEnt stops search */\r
+ fEnd=1;\r
+ break;\r
+ }\r
+\r
+ if (CompareNCS(pEnt, pName, 11) == -1)\r
+ {\r
+ fFound=1;\r
+ *pLBARet = sector-1; /* tell em what LBA of DirEnt */\r
+ *poEntRet = pEnt-pStart; /* Tell em offset in LBA */\r
+ break;\r
+ }\r
+ pEnt+=32; /* 32 byte per entry */\r
+ }\r
+ }\r
+ if (fFound) \r
+ {\r
+ *pEntRet = pEnt;\r
+ return(0);\r
+ }\r
+ else return (ErcNoSuchFile);\r
+}\r
+\r
+\r
+/*****************************************************\r
+ This searches the ROOT directory for pName\r
+ and returns a pointer to the 32 byte entry which\r
+ is in a temporary buffer. If not found, returns NIL;\r
+ When searching directories, if the filename begins\r
+ with a NULL the search need go no futher!\r
+*****************************************************/\r
+\r
+static U32 GetRootEnt(U8 *pName,\r
+ U8 Drive,\r
+ U32 *pLBARet,\r
+ U32 *poEntRet,\r
+ U8 **pEntRet)\r
+{\r
+unsigned long i, j, k, erc;\r
+U8 fFound, fEnd, *pEnt, *pStart;\r
+\r
+ i = Ldrv[Drive].LBARoot;\r
+ j = Ldrv[Drive].nRootDirEnt;\r
+\r
+ fFound = 0;\r
+ fEnd = 0;\r
+ while ((j) && (!fFound) && (!fEnd)) \r
+ { /* while there are valid entries */\r
+\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, i++, 1, abRawSector);\r
+\r
+ if (erc)\r
+ return(erc);\r
+\r
+ pEnt = abRawSector;\r
+ pStart = pEnt;\r
+ for (k=0; k<16; k++) \r
+ {\r
+ if (*pEnt==0) \r
+ { /* 0 in a DirEnt stops search */\r
+ fEnd=1;\r
+ break;\r
+ }\r
+ if (CompareNCS(pEnt, pName, 11) == -1) \r
+ {\r
+ fFound=1;\r
+ *pLBARet = i-1; /* tell em what LBA of DirEnt */\r
+ *poEntRet = pEnt-pStart; /* Tell em offset in LBA */\r
+ break;\r
+ }\r
+ --j; /* one less dir ent */\r
+ pEnt+=32; /* 32 byte per entry */\r
+ }\r
+ }\r
+ if (fFound) \r
+ {\r
+ *pEntRet = pEnt;\r
+ return(0);\r
+ }\r
+ else\r
+ return (ErcNoSuchFile);\r
+}\r
+\r
+/********************************************\r
+RAB - This builds a full file specification from\r
+ pName and places it in pDest based on the\r
+ path from iJob. cbDestRet is set to size.\r
+ If the name starts with "DRIVE:"\r
+ then the path from the JCB is NOT used. Otherwise\r
+ the pName is cancatenated to the job's path.\r
+ If it begins with "\" only DRIVE: (first two\r
+ chars of path) are used.\r
+*********************************************/\r
+\r
+static void BuildSpec(char *pName,\r
+ long cbName,\r
+ char *pDest,\r
+ long *cbDestRet,\r
+ long iJob)\r
+{\r
+long i;\r
+char pathtmp[70];\r
+\r
+ if ((cbName) && (pName) && (pName[1] == ':'))\r
+ { /* Do NOT use path */\r
+ CopyData(pName, pDest, cbName);\r
+ i = cbName;\r
+ }\r
+ /* use only drive and semicolon */\r
+ else if ((cbName) && (pName) && (pName[0] == 0x5C)) /* begins with backslash */\r
+ {\r
+ GetPath(iJob, pathtmp, &i);\r
+ pDest[0] = pathtmp[0];\r
+ pDest[1] = pathtmp[1];\r
+ i = 2;\r
+ CopyData(pName, &pDest[2], cbName);\r
+ i += cbName;\r
+ }\r
+ else\r
+ { /* Use whole path as prefix */\r
+ i = 0;\r
+ GetPath(iJob, pDest, &i);\r
+ if ((cbName) && (pName))\r
+ {\r
+ if ((pName) && (cbName))\r
+ {\r
+ CopyData(pName, &pDest[i], cbName);\r
+ i += cbName;\r
+ }\r
+ }\r
+ }\r
+ *cbDestRet = i;\r
+}\r
+\r
+\r
+\r
+/*****************************************************\r
+ The parses out the path name into directories, filename,\r
+ and extension (Example):\r
+ C:\TEMP\TEST.TXT (with TEMP being a dir in the root).\r
+ This also uses the current path for the job to build\r
+ the filename.\r
+ SpecDepth is set to the level of the last valid\r
+ 11 character string (Specdepth is 0-6).\r
+*****************************************************/\r
+\r
+static U32 ParseName(U8 *pName, U32 cbName, U32 iJob)\r
+{\r
+unsigned long i, j, k, erc;\r
+U8 c, *pPart;\r
+char Spec[70];\r
+U32 cbSpec;\r
+\r
+ erc = 0;\r
+ FDrive = 0;\r
+\r
+ FillData(FileSpec, (7*11), ' '); /* Fill parse table with spaces */\r
+\r
+ if ((cbName) && (*pName == ' '))\r
+ erc = ErcBadFileSpec;\r
+\r
+ BuildSpec(pName, cbName, Spec, &cbSpec, iJob);\r
+\r
+ j = 0; /* index into crnt part of spec */\r
+ k = 0; /* index into crnt tree level */\r
+ pPart = Spec;\r
+ for (i=0; i < cbSpec; i++)\r
+ {\r
+ switch (c = *pPart++)\r
+ {\r
+ case 0x5c : /* '\' separates dir or fname */\r
+ if (j>0)\r
+ { /* if it's not the first one */\r
+ ++k;\r
+ j=0;\r
+ }\r
+ break;\r
+ case ':' :\r
+ if ((j==1) && (k==0) && (FDrive==0))\r
+ {\r
+ FDrive = FileSpec[0][0] & 0xdf; /* Make drive Upper*/\r
+ FileSpec[0][0] = ' ';\r
+ j=0; /* back to beginning of part */\r
+ k=0;\r
+ }\r
+ else erc = ErcBadFileSpec;\r
+ break;\r
+ case '.' : /* . can only appear once in dir or fname */\r
+ if (j>8) erc = ErcBadFileSpec;\r
+ else j=8; /* move to extension */\r
+ break;\r
+ case '>' : /* not allowed in spec */\r
+ case '<' :\r
+ case ',' :\r
+ case '+' :\r
+ case '|' :\r
+ case ']' :\r
+ case '[' :\r
+ case '+' :\r
+ case '=' :\r
+ case '@' :\r
+ case '*' :\r
+ case '?' :\r
+ erc = ErcBadFileSpec;\r
+ break;\r
+ default : /* make chars upper */\r
+ if (j>10)\r
+ erc = ErcBadFileSpec;\r
+ else\r
+ {\r
+ if (((c >= 'A') && (c <= 'Z')) ||\r
+ ((c >= 'a') && (c <= 'z')))\r
+ c &= 0xdf;\r
+ FileSpec[k][j] = c;\r
+ ++j;\r
+ }\r
+ break;\r
+ }\r
+\r
+ if (erc) break; /* bad news. Exit for loop */\r
+ }\r
+SpecDepth = k;\r
+return erc;\r
+}\r
+\r
+\r
+/*** Get Directory Sector *******************************\r
+ Gets a 512 byte directory sector in sequence number from\r
+ the directory path given. SectNum = 0 to nMax for Dir.\r
+ This also returns the LBA for sector itself for\r
+ internal users of this call.\r
+********************************************************/\r
+\r
+static U32 GetDirSectorM(char *pPath,\r
+ long cbPath,\r
+ char *pSectRet,\r
+ long cbRetMax,\r
+ long SectNum,\r
+ long *LBARet,\r
+ U16 *ClstrRet,\r
+ long iJob)\r
+{\r
+U32 sector, i, j, k, erc, spc, level, iSect;\r
+U16 MaxClstr, Clstr, rClstr;\r
+U8 fFound, *pEnt, Drive;\r
+\r
+ if (cbRetMax > 512) /* WHOA Bub, 1 Sector at a time! */\r
+ cbRetMax = 512;\r
+\r
+ erc = ParseName(pPath, cbPath, iJob);\r
+\r
+ /* The entire path has now been parsed out into an array of\r
+ arrays, each 11 bytes long that contain each directory name\r
+ in the path for the sector they want.\r
+ The first is always the root (entry 0).\r
+ The drive will be a letter in FDrive.\r
+ */\r
+\r
+ if ((FDrive > 0x40) && (FDrive < 0x52)) /* A to J */\r
+ Drive = FDrive - 0x41; /* Make it 0-9 */\r
+ else\r
+ return(ErcNoSuchDrive);\r
+\r
+ if (Drive < 2)\r
+ {\r
+ StatFloppy(Drive);\r
+ erc= read_BS(Drive);\r
+ }\r
+\r
+ if (Ldrv[Drive].DevNum == 0xff)\r
+ return(ErcNoSuchDrive);\r
+\r
+ i = Ldrv[Drive].LBARoot;\r
+ j = Ldrv[Drive].nRootDirEnt;\r
+\r
+ if (FileSpec[0][0] == ' ')\r
+ { /* They want sector in root */\r
+ if (SectNum > j/32) /* Beyond Root entries! */\r
+ return(ErcNoMatch);\r
+\r
+ /* Else we can give them the sector NOW */\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, i+SectNum, 1, abRawSector);\r
+ if (!erc) \r
+ {\r
+ *LBARet = i+SectNum;\r
+ CopyData(abRawSector, pSectRet, cbRetMax);\r
+ }\r
+ return(erc);\r
+ }\r
+\r
+ /* We have to run the root for a dir name... */\r
+\r
+ fFound = 0;\r
+ while ((j) && (!fFound))\r
+ { /* while there are valid entries */\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, i++, 1, abRawSector);\r
+ if (erc)\r
+ return(erc);\r
+ pEnt = abRawSector; /* Point to first entry */\r
+ for (k=0; k<16; k++) \r
+ {\r
+ if (CompareNCS(pEnt, FileSpec[0], 11) == -1)\r
+ {\r
+ fFound=1;\r
+ break;\r
+ }\r
+ --j; /* one less dir ent */\r
+ pEnt+=32; /* 32 byte per entry */\r
+ }\r
+ }\r
+ if (!fFound)\r
+ return (ErcNoMatch);\r
+\r
+ pDirEnt = pEnt; /* Entry we just found in root was dir */\r
+\r
+ if (!(pDirEnt->Attr & DIRECTORY))\r
+ {\r
+ return(ErcNoSuchDir);\r
+ }\r
+\r
+ if (Ldrv[Drive].fFAT16)\r
+ MaxClstr = 0xfff8;\r
+ else\r
+ MaxClstr = 0xff8; /* FAT12 */\r
+\r
+ spc = Ldrv[Drive].SecPerClstr; /* How many sectors per cluster */\r
+ Clstr = pDirEnt->StartClstr;\r
+\r
+ level = 1; /* start at this directory+1, compare to FileSpec */\r
+\r
+ while (!erc) \r
+ { /* looking for Dir */\r
+\r
+ if (FileSpec[level][0] == ' ')\r
+ { /* They want sector in this dir */\r
+\r
+ if (!(pDirEnt->Attr & DIRECTORY)) \r
+ {\r
+ return(ErcNoSuchDir);\r
+ }\r
+ rClstr = SectNum /spc; /* calc relative cluster from start clstr */\r
+ iSect = SectNum % spc; /* Add this to cluster start for sector */\r
+ sector = ClsToLBA(Clstr, Drive); /* sector of first dir sector */\r
+ while ((rClstr--) && (!erc))\r
+ erc = NextFATClstr(Drive, Clstr, &Clstr);\r
+ if (erc)\r
+ return(erc);\r
+ sector = ClsToLBA(Clstr, Drive); /* LBA of this clstr */\r
+ sector += iSect;\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector, 1, abRawSector);\r
+ if (!erc) \r
+ {\r
+ CopyData(abRawSector, pSectRet, cbRetMax);\r
+ *LBARet = sector;\r
+ *ClstrRet = Clstr;\r
+ }\r
+ return(erc);\r
+ }\r
+ else \r
+ { /* Else we must find this sub dir name */\r
+\r
+ sector = ClsToLBA(Clstr, Drive); /* sector of first dir sector */\r
+ fFound=0;\r
+ i = 0;\r
+\r
+ while (!fFound)\r
+ { /* while there are valid entries */\r
+\r
+ if (i==spc) \r
+ { /* reached last dir sector of this cluster */\r
+ erc = NextFATClstr(Drive, Clstr, &Clstr);\r
+ if (!erc) \r
+ {\r
+ if (Clstr >= MaxClstr) /* last sector */\r
+ return(ErcNoSuchFile); /* not found */\r
+ sector = ClsToLBA(Clstr, Drive); /* LBA of next sector */\r
+ i=0;\r
+ }\r
+ else\r
+ return(erc);\r
+ }\r
+\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector++, 1, abRawSector);\r
+ if (erc)\r
+ return(erc);\r
+ i++; /* Next sector in this cluster */\r
+\r
+ pEnt = &abRawSector[0];\r
+ for (k=0; k<16; k++)\r
+ { /* 16 entries per sector */\r
+ if (CompareNCS(pEnt, FileSpec[level], 11) == -1)\r
+ {\r
+ fFound=1;\r
+ break;\r
+ }\r
+ pEnt+=32; /* 32 byte per entry */\r
+ }\r
+ }\r
+ pDirEnt = pEnt; /* Entry we just found */\r
+ Clstr = pDirEnt->StartClstr; /* Clstr @ start of dir entry */\r
+ }\r
+ ++level; /* next level of parsed filespec */\r
+ }\r
+return (erc);\r
+}\r
+\r
+/*******************************************************\r
+ This is the BLOCK read for the MMURTL DOS file system.\r
+ It reads whole sectors and returns them to pBytesRet.\r
+ There are NO internal filesystem buffers for this call.\r
+ Data is returned directly to your buffer from the Disk.\r
+ This is the fastest method for reading a file.\r
+ This is also used internally to fill a stream buffer.\r
+********************************************************/\r
+\r
+static U32 ReadBlockM(U32 dHandle,\r
+ U8 *pBytesRet,\r
+ U32 nBytes,\r
+ U32 dLFA,\r
+ U32 *pdBytesRet,\r
+ U8 fFill) /* TRUE if filling a stream buffer */\r
+{\r
+U32 erc, j, LBA, iFCB, bpc, spc, nDone, rLFA, nLeft, nBlks;\r
+U16 Clstr, MaxClstr, ClstrSav;\r
+U8 Drive;\r
+\r
+ erc = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */\r
+ if (erc) return erc;\r
+\r
+ /* Certain FUB fields have different meanings in stream */\r
+\r
+ if ((paFUB[dHandle].fStream) && (!fFill))\r
+ {\r
+ *pdBytesRet = 0;\r
+ return ErcStreamFile;\r
+ }\r
+\r
+ /* set these up in advance */\r
+\r
+ nBlks = nBytes/512; /* nBytes MUST be multiple of 512 */\r
+ Drive = paFCB[iFCB]->Ldrv; /* What logical drive are we on? */\r
+ spc = Ldrv[Drive].SecPerClstr; /* sectors per cluster */\r
+ bpc = 512 * spc; /* Bytes per cluster */\r
+ if (Ldrv[Drive].fFAT16)\r
+ MaxClstr = 0xfff8;\r
+ else\r
+ MaxClstr = 0xff8; /* FAT12 */\r
+\r
+\r
+ /* Call to find the absolute cluster on the the logical disk,\r
+ and also the relative LFA of the cluster in question.\r
+ */\r
+\r
+ erc = GetAbsoluteClstr(dHandle, dLFA, &Clstr, &rLFA);\r
+\r
+ LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster */\r
+\r
+ /* Now LBA equals beginning of cluster that dLFA resides in.\r
+ We must see which sector in Clstr is the starting LBA. To do this\r
+ we MOD (dLFA in sectors) by (sectors per cluster) and\r
+ add the leftover amount (should be 0 to nSPC-1) to LBA before we\r
+ read. For example: if dLFA was 2560 and spc was 4, this\r
+ would be 5 % 4 = 1. We would add 1 to the LBA.\r
+ This is only done for the first read in the loop.\r
+\r
+ We also set nleft which is how many sectors are left in the\r
+ current cluster we are reading from.\r
+ */\r
+\r
+ LBA += (dLFA/512) % spc;\r
+ nLeft = spc - ((dLFA/512) % spc);\r
+ nDone = 0;\r
+\r
+ while ((nBlks) && (!erc))\r
+ { /* while buffer isn't full and no error */\r
+ if (nBlks > nLeft)\r
+ j = nLeft;\r
+ else j = nBlks;\r
+\r
+ paFUB[dHandle]->Clstr = Clstr; /* Save Current cluster */\r
+ paFUB[dHandle]->LFAClstr = rLFA; /* Save LFA for Clstr in FUB */\r
+\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 1, LBA, j, pBytesRet);\r
+ if (erc)\r
+ break;\r
+ pBytesRet += j * 512; /* further into their buffer */\r
+ nBlks -= j;\r
+ nLeft -= j;\r
+ nDone += j;\r
+\r
+ if ((nBlks) && (!nLeft))\r
+ { /* current cluster has none left */\r
+ nLeft = spc;\r
+ ClstrSav = Clstr;\r
+ erc = NextFATClstr(Drive, Clstr, &Clstr); /* next FAT cluster */\r
+ if (erc)\r
+ {\r
+ *pdBytesRet = nDone*512;\r
+ return(erc);\r
+ }\r
+ rLFA += bpc; /* Update rel LFA of new cluster*/\r
+ if (Clstr >= MaxClstr)\r
+ erc = ErcEOF; /* Last cluster */\r
+ if (!Clstr)\r
+ erc = ErcBrokenFile; /* No good next cluster! */\r
+ LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster*/\r
+ }\r
+ }\r
+ *pdBytesRet = nDone*512;\r
+\r
+ return erc; /* WE'RE DONE, return the error (if any) */\r
+}\r
+\r
+\r
+/*** Write Block ***************************************\r
+ This is the BLOCK write for the MMURTL FAT file system.\r
+ dLFA must be the LFA of a valid portion of the file and\r
+ nBlks must not extend beyond the last cluster allocated\r
+ to the file. IOW, you must call SetFileSize first.\r
+ In a block write dLFA is always written on\r
+ a sector boundry. We must make sure that the filesize\r
+ will accomidate the sectors we want to write!\r
+********************************************************/\r
+\r
+static U32 WriteBlockM(U32 dHandle, char *pData, U32 nBytes,\r
+ U32 dLFA, U32 *pdnBytesRet)\r
+{\r
+U32 erc, i, j, LBA, iFCB, bpc, spc, nDone, rLFA, nLeft, nBlks;\r
+U32 nLeft1, LBA1, nBlks1;\r
+U16 Clstr, MaxClstr;\r
+U8 Drive;\r
+\r
+ erc = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */\r
+ if (erc) return erc;\r
+\r
+ nBlks = nBytes/512;\r
+ dLFA = (dLFA/512)*512; /* round LFA down to nearest sector */\r
+\r
+ if (!paFCB[iFCB]->Mode) /* Is it open in Modify?? */\r
+ return(ErcReadOnly);\r
+\r
+ i = (paFCB[iFCB]->FileSize/512); /* Set i nBlks in file max */\r
+ if (paFCB[iFCB]->FileSize%512)\r
+ i++;\r
+\r
+ j = (dLFA/512) + nBlks; /* blocks to write past dLFA*/\r
+\r
+ if (j > i)\r
+ return(ErcBeyondEOF);\r
+\r
+ /* It seems OK to write the blocks out, so now let's DO IT! */\r
+\r
+ Drive = paFCB[iFCB]->Ldrv; /* What logical drive are we on? */\r
+ spc = Ldrv[Drive].SecPerClstr; /* sectors per cluster */\r
+ bpc = 512 * spc; /* Bytes per cluster */\r
+ if (Ldrv[Drive].fFAT16)\r
+ MaxClstr = 0xfff8;\r
+ else\r
+ MaxClstr = 0xff8; /* FAT12 */\r
+\r
+ erc = GetAbsoluteClstr(dHandle, dLFA, &Clstr, &rLFA);\r
+ if (erc)\r
+ return(erc);\r
+\r
+ LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster */\r
+\r
+ /* Now LBA equals beginning of cluster that dLFA resides in.\r
+ We must see which sector in Clstr is the starting LBA. To do this\r
+ we MOD (dLFA in sectors) by (sectors per cluster) and\r
+ add the leftover amount (should be 0 to nSPC-1) to LBA before we\r
+ write. For example: if dLFA was 2560 and spc was 4, this\r
+ would be 5 % 4 = 1. We would add 1 to the LBA.\r
+ This is only done for the first write in the loop.\r
+ We also set nleft which is how many sectors are left in the\r
+ current cluster we are writing to so we know when to\r
+ move to the next cluster.\r
+ */\r
+\r
+ LBA += (dLFA/512) % spc;\r
+ LBA1 = LBA;\r
+ nLeft = spc - ((dLFA/512) % spc);\r
+ nLeft1 = nLeft;\r
+ nBlks1 = nBlks;\r
+ nDone = 0;\r
+\r
+ while ((nBlks) && (!erc))\r
+ { /* while blocks are left to write */\r
+ if (nBlks > nLeft)\r
+ j = nLeft;\r
+ else j = nBlks;\r
+\r
+ paFUB[dHandle]->Clstr = Clstr; /* Save Current cluster */\r
+ paFUB[dHandle]->LFAClstr = rLFA; /* Save LFA for Clstr in FUB */\r
+\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 2, LBA, j, pData);\r
+ if (erc)\r
+ break;\r
+ pData += (j * 512); /* Update address */\r
+ nDone += j; /* Total blocks done so far */\r
+ nBlks -= j;\r
+ nLeft -= j;\r
+\r
+ if ((nBlks) && (!nLeft))\r
+ { /* done with current cluster */\r
+ nLeft = spc;\r
+ erc = NextFATClstr(Drive, Clstr, &Clstr); /* next FAT cluster */\r
+ if (erc)\r
+ return(erc);\r
+ rLFA += bpc; /* Update rel LFA of new cluster*/\r
+ if ((Clstr >= MaxClstr) && (nBlks)) /* Problem! */\r
+ {\r
+ erc = ErcBeyondEOF; /* Last cluster & they want more*/\r
+ }\r
+ if (!Clstr) erc = ErcBrokenFile; /* No good next cluster! */\r
+ LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster*/\r
+ }\r
+ }\r
+ *pdnBytesRet = nDone * 512;\r
+ return erc; /* WE'RE DONE, return the error (if any) */\r
+}\r
+\r
+\r
+/*********************************************************\r
+ Fills the stream buffer for the Current LFA in the FUB.\r
+ We simply figure out which relative LBA we want in the\r
+ buffer and call ReadBlockM to fill it. We then set\r
+ LFABuf in the FUB to show the LFA of the first byte\r
+ in the buffer.\r
+**********************************************************/\r
+\r
+static U32 FillStreamBuff(U32 dHandle, U8 fInitial)\r
+{\r
+U32 erc, i, LFA, cLFA, LFABuf, iFCB;\r
+U32 sBuf;\r
+U8 *pBuf;\r
+\r
+ erc = 0;\r
+\r
+ /* Set these up in advance */\r
+\r
+ cLFA = paFUB[dHandle]->CrntLFA; /* Find out where we want to be */\r
+ LFABuf = paFUB[dHandle]->LFABuf; /* LFA of first byte in buffer now */\r
+ pBuf = paFUB[dHandle]->pBuf; /* Local ptr to buffer */\r
+ sBuf = paFUB[dHandle]->sBuf; /* size of buffer */\r
+ iFCB = paFUB[dHandle]->iFCB; /* FCB for this FUB */\r
+\r
+ /* If the file was just opened we fill with LFA 0 */\r
+\r
+ if (fInitial)\r
+ {\r
+ erc = ReadBlockM(dHandle, pBuf, sBuf, 0, &i, TRUE);\r
+ paFUB[dHandle]->LFABuf = 0;\r
+ }\r
+\r
+ /* Else If the LFA is already in the buffer we just exit OK */\r
+\r
+ else if ((cLFA >= LFABuf) && (cLFA < (LFABuf + sBuf)))\r
+ {\r
+ erc = 0;\r
+ }\r
+\r
+ /* ELSE We must figure out what starting LFA we want and fill the buffer */\r
+\r
+ else\r
+ {\r
+ LFA = (cLFA/512) * 512; /* Round down to nearest sector */\r
+ erc = ReadBlockM(dHandle, pBuf, sBuf, LFA, &i, 1);\r
+ paFUB[dHandle]->LFABuf = LFA;\r
+ }\r
+\r
+ if (erc == ErcEOF) /* We ignore this when filling the buffer */\r
+ erc = 0;\r
+ return erc; /* WE'RE DONE, return the error (if any, except EOF) */\r
+}\r
+\r
+/*******************************************************\r
+ This is the STREAM read for the MMURTL FAT file system.\r
+ Used in conjunction with Get & Set FileLFA, you can\r
+ move to any portion of the file to read one or more bytes.\r
+ Data is buffered in Page sized chunks by the file\r
+ system. This is the easiest method for reading a file.\r
+********************************************************/\r
+\r
+static U32 ReadBytesM(U32 dHandle, U8 *pBytesRet, U32 nBytes, U32 *pdReadRet)\r
+{\r
+U32 erc, iFCB, sBuf, cLFA, fSize;\r
+U32 nBytesDone, /* Total read so far */\r
+ lfaEOB, /* LFA end of Buffer */\r
+ nBytesOut; /* Number of bytes to copy (this buffer full) */\r
+U8 *pOut, *pBuf; /* Next data to send caller from buffer. Local pbuf */\r
+\r
+ erc = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */\r
+ if (erc) return erc;\r
+\r
+ /* Certain FUB fields have different meanings in stream type file */\r
+\r
+ if (!paFUB[dHandle]->fStream)\r
+ return ErcBlockFile;\r
+\r
+ cLFA = paFUB[dHandle]->CrntLFA; /* local cLFA */\r
+ fSize = paFCB[iFCB]->FileSize; /* local size */\r
+\r
+ /* check and see if we are at EOF */\r
+\r
+ if (cLFA >= fSize)\r
+ { /* early out */\r
+ *pdReadRet = 0;\r
+ return ErcEOF;\r
+ }\r
+\r
+ /* We are not at EOF so we need to fill stream buff\r
+ and then calculate how many bytes we can give them\r
+ from this buffer full of data.\r
+ */\r
+\r
+ pBuf = paFUB[dHandle]->pBuf; /* Local ptr to buffer/size */\r
+ sBuf = paFUB[dHandle]->sBuf;\r
+ nBytesDone = 0;\r
+\r
+ while ((nBytesDone < nBytes) &&\r
+ (cLFA < fSize) &&\r
+ (!erc))\r
+ {\r
+ erc = FillStreamBuff(dHandle, 0); /* Fill the buff */\r
+\r
+ /* Find out the LFA at the end of the buffer since we\r
+ just filled it. It may be less than the end since\r
+ we may be near EOF.\r
+ */\r
+\r
+ lfaEOB = paFUB[dHandle]->LFABuf + sBuf -1; /* LFA at End of Buffer */\r
+ if (lfaEOB > fSize) /* Beyond EOF? */\r
+ lfaEOB = fSize - 1;\r
+\r
+ /* Calculate pointer to the next chunk out from stream buffer */\r
+\r
+ pOut = pBuf + (cLFA - paFUB[dHandle]->LFABuf);\r
+\r
+ /* Calc how many bytes we can get out of buffer (belongs to file) */\r
+\r
+ nBytesOut = lfaEOB - cLFA + 1;\r
+ if (nBytesOut > nBytes-nBytesDone)\r
+ nBytesOut = nBytes-nBytesDone;\r
+\r
+ /* Send bytes to pBytesRet */\r
+\r
+ CopyData(pOut, pBytesRet, nBytesOut);\r
+\r
+ pBytesRet += nBytesOut; /* Update pBytesRet */\r
+ nBytesDone += nBytesOut; /* Update nBytesDone */\r
+ cLFA += nBytesOut; /* update CrntLFA */\r
+ paFUB[dHandle]->CrntLFA = cLFA;\r
+ }\r
+\r
+ *pdReadRet = nBytesDone; /* Tell em how many bytes they got */\r
+ if (!erc)\r
+ if (cLFA == fSize)\r
+ erc = 1;\r
+\r
+ return erc;\r
+}\r
+\r
+/*********************************************************\r
+ Flushes the stream buffer if it has been modified.\r
+ We read the current LBA of the buffer and write it\r
+ to disk. Then reset the flag in the FCB.\r
+ This uses WriteBlockM. We ensure we do not call\r
+ WriteBlockM with more sectors than are allocated\r
+ to the file cause the buffer may extend past the\r
+ actual allocated amount of clusters!\r
+**********************************************************/\r
+\r
+static U32 FlushStreamBuff(U32 dHandle)\r
+{\r
+U32 erc, i, j, LFABuf, iFCB, size;\r
+U32 sBuf;\r
+U8 *pBuf;\r
+\r
+ erc = 0;\r
+\r
+ if (paFUB[dHandle]->fModified)\r
+ {\r
+ LFABuf = paFUB[dHandle]->LFABuf; /* LFA of first byte in buf now */\r
+ pBuf = paFUB[dHandle]->pBuf; /* Local ptr to buffer */\r
+ sBuf = paFUB[dHandle]->sBuf; /* size of buffer */\r
+ iFCB = paFUB[dHandle]->iFCB; /* to check filesize */\r
+ size = paFCB[iFCB]->FileSize;\r
+\r
+ i = (sBuf/512); /* Total blocks in buff */\r
+ j = (size-LFABuf)/512; /* Blocks in buf belonging to file */\r
+ if ((size-LFABuf)%512) /* Odd bytes, add one more block */\r
+ j++;\r
+ if (j < i)\r
+ i = j;\r
+\r
+ erc = WriteBlockM(dHandle, pBuf, i*512, LFABuf, &i);\r
+ paFUB[dHandle]->fModified = 0;\r
+ }\r
+ return erc;\r
+}\r
+\r
+\r
+/*** Write Bytes (Stream) ******************************\r
+ This is the STREAM write for the MMURTL FAT file system.\r
+ This uses FillStreamBuff to set up the buffer, and\r
+ FlushStreamBuff to write modified stream buffers.\r
+ This also calls SetFileSizeM to extend the\r
+ filelength when necessary (writing at EOF).\r
+********************************************************/\r
+\r
+static U32 WriteBytesM(U32 dHandle, char *pData, U32 nBytes, U32 *nBytesRet)\r
+{\r
+U32 erc, iFCB, sBuf, cLFA, fSize;\r
+U32 nBytesDone, /* Total written so far */\r
+ lfaEOB, /* LFA end of Buffer */\r
+ nBytesOut; /* Number of bytes to copy (this buffer full) */\r
+U8 *pOut, *pBuf; /* Next data to send caller from buffer. Local pbuf */\r
+\r
+ erc = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */\r
+ if (erc) return erc;\r
+\r
+ /* Certain FUB fields have different meanings in stream type file */\r
+\r
+ if (!paFUB[dHandle]->fStream)\r
+ return(ErcBlockFile);\r
+\r
+ if (!paFCB[iFCB]->Mode) /* Is it open in Modify?? */\r
+ return(ErcReadOnly);\r
+\r
+ /* Set up local vars to values for current stream buffer.\r
+ These are in effect for this call on entry!\r
+ */\r
+\r
+ pBuf = paFUB[dHandle]->pBuf; /* Local ptr to buffer/size */\r
+ sBuf = paFUB[dHandle]->sBuf;\r
+ cLFA = paFUB[dHandle]->CrntLFA; /* local cLFA */\r
+ fSize = paFCB[iFCB]->FileSize; /* local size */\r
+\r
+ /* check and see if we are at EOF or will go past it\r
+ when we write. If so, we make the file larger.\r
+ */\r
+\r
+ if (cLFA + nBytes > fSize)\r
+ { /* Must set file size first */\r
+ erc = SetFileSizeM(dHandle, cLFA + nBytes);\r
+ if (erc)\r
+ {\r
+ return(erc);\r
+ }\r
+ fSize = paFCB[iFCB]->FileSize; /* local size */\r
+ }\r
+\r
+ lfaEOB = paFUB[dHandle]->LFABuf + sBuf -1; /* LFA at End of Buffer */\r
+ if (lfaEOB > fSize) /* EOF before EOB? */\r
+ lfaEOB = fSize - 1;\r
+\r
+ /* Now we loop writing the bytes to the stream buffer\r
+ filling it with each new section of the file.\r
+ As this occurs we must call fillStreamBuff with\r
+ each new section of the file if not already in the\r
+ buff to ensure proper continuity of the file in case\r
+ we are overwriting existing sections of the file.\r
+ */\r
+\r
+ nBytesDone = 0;\r
+\r
+ while ((nBytesDone < nBytes) &&\r
+ (cLFA < fSize) &&\r
+ (!erc))\r
+ {\r
+ /* If the next byte to write goes outside the current buffer\r
+ (before or after it),\r
+ we call FlushStreamBuff which will write the current\r
+ buffer and reset the fModified flag (if needed),\r
+ then call FillStreamBuff for file continuity.\r
+ */\r
+\r
+ if ((cLFA > lfaEOB) ||\r
+ (cLFA < paFUB[dHandle]->LFABuf))\r
+ {\r
+ erc = FlushStreamBuff(dHandle);\r
+ if (erc)\r
+ {\r
+ return(erc);\r
+ }\r
+ erc = FillStreamBuff(dHandle, 0); /* Fill the buff */\r
+ if (erc)\r
+ {\r
+ return(erc);\r
+ }\r
+ }\r
+\r
+ /* Find out the LFA at the end of the buffer since we\r
+ just filled it. It may be less than the end since\r
+ we may be near EOF.\r
+ */\r
+\r
+ lfaEOB = paFUB[dHandle]->LFABuf + sBuf -1; /* LFA at End of Buffer */\r
+ if (lfaEOB > fSize) /* Beyond EOF? */\r
+ lfaEOB = fSize - 1;\r
+\r
+ /* Calc pointer to where next chunk goes in stream buffer */\r
+\r
+ pOut = pBuf + (cLFA - paFUB[dHandle]->LFABuf);\r
+\r
+ /* Calc how many bytes we can write to buffer. We must\r
+ ensure we don't exceed buffer size. */\r
+\r
+ nBytesOut = (lfaEOB + 1) - cLFA;\r
+ if (nBytesOut > nBytes-nBytesDone)\r
+ nBytesOut = nBytes-nBytesDone;\r
+\r
+ /* Get bytes from pData into stream buffer */\r
+\r
+ CopyData(pData, pOut, nBytesOut);\r
+ paFUB[dHandle]->fModified = 1;\r
+\r
+ pData += nBytesOut; /* Update pData pointer */\r
+ nBytesDone += nBytesOut; /* Update nBytesDone */\r
+ cLFA += nBytesOut; /* update CrntLFA */\r
+ paFUB[dHandle]->CrntLFA = cLFA;\r
+ }\r
+\r
+ *nBytesRet = nBytesDone; /* Tell em how many bytes we wrote */\r
+ return erc;\r
+}\r
+\r
+/*******************************************************\r
+ GetFileSize returns the FileSize entry from the FCB\r
+ for the handle specified. This means the file must be\r
+ OPEN. This call would NOT be used for a directory\r
+ listing function as you would have to open each file\r
+ to get the information. Use ReadDirSector instead.\r
+********************************************************/\r
+\r
+static U32 GetFileSizeM(U32 dHandle, U32 *pdSizeRet)\r
+{\r
+U32 erc, iFCB;\r
+ erc = ValidateHandle(dHandle, &iFCB);\r
+ if (erc) return erc;\r
+ *pdSizeRet = paFCB[iFCB]->FileSize;\r
+ return 0;\r
+}\r
+\r
+\r
+/*******************************************************\r
+ SetFileLFA sets a Stream mode file pointer to dLFA.\r
+ If attempted on a block mode file an error is returned.\r
+ -1 (0xffffffff) is equal to EOF. The Stream Buffer\r
+ is filled with the sectors that contains the LFA.\r
+********************************************************/\r
+\r
+static U32 SetFileLFAM(U32 dHandle, S32 dLFA)\r
+{\r
+U32 erc, iFCB;\r
+\r
+ erc = ValidateHandle(dHandle, &iFCB);\r
+ if (erc) return erc;\r
+\r
+ if (!paFUB[dHandle]->fStream)\r
+ return ErcBlockFile;\r
+\r
+ if (paFCB[iFCB]->Mode) /* Modify mode - Flush will flush if needed */\r
+ erc = FlushStreamBuff(dHandle);\r
+\r
+ /* -1 = Set file ptr to EOF */\r
+\r
+ if (dLFA == -1)\r
+ dLFA = paFCB[iFCB]->FileSize;\r
+\r
+ if (dLFA > paFCB[iFCB]->FileSize)\r
+ erc = ErcBeyondEOF;\r
+\r
+ if (!erc)\r
+ {\r
+ paFUB[dHandle]->CrntLFA = dLFA; /* Set where we are in the file */\r
+ erc = FillStreamBuff(dHandle, 0);\r
+ }\r
+\r
+ return erc;\r
+}\r
+\r
+\r
+/*******************************************************\r
+ GetFileLFA gets a Stream mode file pointer for caller\r
+ returning it to pdLFARet.\r
+ If attempted on a block mode file an error is returned.\r
+ EOF is returned as the FileSize.\r
+********************************************************/\r
+\r
+static U32 GetFileLFAM(U32 dHandle, U32 *pdLFARet)\r
+{\r
+U32 erc, iFCB;\r
+ erc = ValidateHandle(dHandle, &iFCB);\r
+ if (erc) return erc;\r
+ if (!paFUB[dHandle]->fStream)\r
+ return ErcBlockFile;\r
+ *pdLFARet = paFUB[dHandle]->CrntLFA;\r
+\r
+ return erc;\r
+}\r
+\r
+/*****************************************************\r
+ This calls parse to validate the filename and separate\r
+ it into directories and a filename. It then walks\r
+ up the tree until it either finds and opens the file,\r
+ or errors out. The handle that is returned is\r
+ 4 higher than the index to the FUB. This is becuase\r
+ 0, 1, 2 & 3 are reserved for NUL, KBD, VID, and LPT\r
+ which are only accesible from the blocking File calls.\r
+*****************************************************/\r
+\r
+static U32 OpenFileM(U8 *pName,\r
+ U32 cbName,\r
+ U8 Mode,\r
+ U8 fStream,\r
+ U32 *pdHandleRet,\r
+ U32 iJob) /* make use of iJob later!!! */\r
+{\r
+U32 erc, level, i, iFCB, iFUB, LBADirEnt, EntOffset;\r
+U16 Clstr;\r
+U8 fFound, *pMem, Drive;\r
+\r
+ if (Mode > 1)\r
+ return ErcBadOpenMode;\r
+\r
+ level = 0; /* start at the root, compare to SpecDepth */\r
+\r
+/* RAB B */\r
+ if (((cbName) && (*pName == ' ')) ||\r
+ (!cbName))\r
+ return(ErcBadFileSpec);\r
+/* RAB E */\r
+\r
+ erc = ParseName(pName, cbName, iJob);\r
+\r
+ /* The entire path has now been parsed out into an array of\r
+ arrays, each 11 bytes long that contain each directory name\r
+ up to and inlcuding the filename. The first is always\r
+ the root (entry 0). The drive will be a letter in FDrive.\r
+ */\r
+\r
+ if ((FDrive > 0x40) && (FDrive < 0x52)) /* A to J */\r
+ Drive = FDrive - 0x41; /* Make it 0-9 */\r
+ else erc = ErcNoSuchDrive;\r
+\r
+ if (Ldrv[Drive].DevNum == 0xff)\r
+ erc = ErcNoSuchDrive;\r
+\r
+ if ((Drive < 2) && (!erc))\r
+ {\r
+ StatFloppy(Drive);\r
+ erc= read_BS(Drive);\r
+ }\r
+\r
+ if (!erc)\r
+ { /* Get Root dir entry */\r
+ erc = GetRootEnt(FileSpec[level],\r
+ Drive,\r
+ &LBADirEnt,\r
+ &EntOffset,\r
+ &pDirEnt);\r
+\r
+ if (erc == ErcNoSuchFile)\r
+ {\r
+ if (level == SpecDepth) erc = ErcNoSuchFile;\r
+ else erc = ErcNoSuchDir;\r
+ }\r
+ if (erc)\r
+ return(erc);\r
+\r
+ if (!erc)\r
+ Clstr = pDirEnt->StartClstr; /* Clstr = beginning of file or dir */\r
+\r
+ while ((level < SpecDepth) && (!erc))\r
+ { /* looking for Dir, not file yet */\r
+\r
+ ++level; /* next level of parsed filespec */\r
+\r
+ erc = GetDirEnt(FileSpec[level],\r
+ Drive,\r
+ Clstr,\r
+ &LBADirEnt,\r
+ &EntOffset,\r
+ &pDirEnt);\r
+ if (erc == ErcNoSuchFile)\r
+ {\r
+ if (level == SpecDepth)\r
+ erc = ErcNoSuchFile;\r
+ else erc = ErcNoSuchDir;\r
+ }\r
+ else if (erc)\r
+ return(erc);\r
+ else\r
+ Clstr = pDirEnt->StartClstr; /* Clstr @ start of dir entry */\r
+ }\r
+\r
+ /* if we got here with no error we've got a file or a DIR.\r
+ If it's DIR then it's an error.\r
+ pDirEnt points to its directory entry, and Clstr\r
+ is the starting cluster of the file\r
+ */\r
+\r
+ if (!erc) \r
+ {\r
+ /* If Attributes say it's not a file then ERROR */\r
+\r
+ if (pDirEnt->Attr & (VOLNAME | DIRECTORY))\r
+ return ErcNotAFile;\r
+\r
+ /* If ModeModify and File is readOnly then ERROR */\r
+\r
+ if ((Mode) && (pDirEnt->Attr & READONLY))\r
+ return ErcReadOnly;\r
+\r
+ /* We check to see if it's already open by looking through the\r
+ valid FCBs to see if we have a Drive, StartClstr& name match.\r
+ If so, we must see if the modes are compatible.\r
+ A valid FCB is one where nUsers > 0.\r
+ */\r
+\r
+ fFound = 0;\r
+ i=0;\r
+ while ((i<nFCBs) && (!fFound)) \r
+ {\r
+\r
+ if ((paFCB[i]->nUsers) &&\r
+ (paFCB[i]->Ldrv == Drive) &&\r
+ (paFCB[i]->StartClstr == pDirEnt->StartClstr) &&\r
+ (CompareNCS(&paFCB[i],\r
+ FileSpec[SpecDepth], 11) == 0xffffffff))\r
+ fFound = 1;\r
+ else\r
+ ++i;\r
+ }\r
+\r
+ if (fFound)\r
+ { /* it's open already. i is index into FCBs */\r
+ if (paFCB[i]->Mode) /* it's open in Modify already */\r
+ return ErcFileInUse;\r
+ else\r
+ {\r
+ iFCB = i; /* Index to this FCB */\r
+ pFCB = &paFCB[i]; /* make pFCB point to FCB found */\r
+ pFCB->nUsers++; /* One more user */\r
+ }\r
+ }\r
+ else\r
+ { /* It not already open. Find empty FCB */\r
+ i = 0;\r
+ while ((i<nFCBs) && (paFCB[i]->nUsers)) ++i; /* Find new FCB */\r
+\r
+ if (i==nFCBs) return ErcNoFreeFCB; /* Couldn't */\r
+\r
+ /* i now indexes into FCBs for a free FCB. New we copy the\r
+ directory entry for the file into the FCB and set up\r
+ the other FCB values. */\r
+\r
+ iFCB = i; /* used to add an FUB */\r
+ pFCB = &paFCB[i]; /* make pFCB point to FCB found */\r
+ CopyData(pDirEnt, pFCB, 32); /* Copy Dir Ent into FCB */\r
+ pFCB->Ldrv = Drive; /* Set Drive */\r
+ pFCB->nUsers++; /* Now in use */\r
+ pFCB->Mode = Mode; /* Open mode */\r
+ pFCB->LBADirSect = LBADirEnt; /* So we know where it came from */\r
+ pFCB->oSectDirEnt = EntOffset; /* " " */\r
+ }\r
+\r
+ /* Now we have an FCB (either existing or we just built it).\r
+ Now add an FUB and fill in the info for the user\r
+ so we can return a handle to it. The Job fields is 0\r
+ for free FUBs.\r
+ */\r
+\r
+ i = 4;\r
+ while ((i<nFUBs) && (paFUB[i]->Job)) ++i; /* Find new FUB */\r
+ if (i==nFUBs) \r
+ {\r
+ pFCB->nUsers--; /* Make FCB correct */\r
+ return ErcNoFreeFUB; /* Couldn't */\r
+\r
+ }\r
+\r
+ /* If we got here, i is an index to a free FUB. */\r
+\r
+ iFUB = i;\r
+ paFUB[iFUB]->Job = iJob; /* Job Owner */\r
+ paFUB[iFUB]->iFCB = iFCB; /* Set index to FCB for this file */\r
+ paFUB[iFUB]->CrntLFA = 0; /* Current Logical File Address */\r
+ paFUB[iFUB]->fModified = 0; /* Stream buf was modified */\r
+ paFUB[iFUB]->fStream = fStream; /* NonZero for STREAM mode */\r
+ paFUB[iFUB]->Clstr = pDirEnt->StartClstr; /* Start Cluster */\r
+ paFUB[iFUB]->LFAClstr = 0; /* Rel LFA to 0 */\r
+ paFUB[iFUB]->LFABuf = 0; /* First LFA in Buffer */\r
+ paFUB[iFUB]->sBuf = 0; /* Default to No Buf */\r
+\r
+ if (fStream) \r
+ { /* allocate/fill buffer and set rest of FUB */\r
+\r
+ erc = AllocOSPage(1, &pMem); /* Stream Buf is 4K */\r
+\r
+ if (erc) \r
+ { /* No MEM left... Bummer */\r
+ pFCB->nUsers--; /* Return FCB to pool */\r
+ paFUB[iFUB]->Job = 0; /* Return FUB to pool */\r
+ return erc; /* Return Erc to user... */\r
+ }\r
+\r
+ paFUB[iFUB]->pBuf = pMem; /* Ptr to buffer if stream mode */\r
+ paFUB[iFUB]->sBuf = 4096; /* Size of buffer */\r
+\r
+ erc = FillStreamBuff(iFUB, 1); /* fInitial to TRUE */\r
+\r
+ if (erc) \r
+ {\r
+ pFCB->nUsers--; /* Return FCB to pool */\r
+ paFUB[iFUB]->Job = 0; /* Return FUB to pool */\r
+ DeAllocPage(pMem, 1); /* Free memory for buffer */\r
+ return erc; /* Return Erc to user... */\r
+ }\r
+ }\r
+\r
+ *pdHandleRet = iFUB; /* File handle */\r
+ }\r
+ }\r
+\r
+return erc;\r
+\r
+}\r
+\r
+\r
+/*******************************************************\r
+ CLOSE FILE for the MMURTL DOS file system. This finds\r
+ the FUB, checks to see if the buffer should be flushed\r
+ and deallocated (only for stream mode), invalidates\r
+ the FUB, and the FCB (if this is the last or only user).\r
+********************************************************/\r
+\r
+static U32 CloseFileM (U32 dHandle)\r
+{\r
+U32 erc, iFCB, i;\r
+\r
+ erc = ValidateHandle(dHandle, &iFCB);\r
+ if (erc) return erc;\r
+\r
+ if (paFCB[iFCB]->Mode)\r
+ { /* Modify mode */\r
+ if (paFUB[dHandle]->fStream)\r
+ {\r
+ erc = FlushStreamBuff(dHandle);\r
+ }\r
+ UpdateDirEnt(iFCB); /* ignore error */\r
+ }\r
+\r
+ if (paFUB[dHandle]->fStream)\r
+ DeAllocPage(paFUB[dHandle]->pBuf, 1); /* Free buffer */\r
+\r
+ /* This means the FS is screwed up. This shouldn't happen... */\r
+ if (!paFCB[iFCB]->nUsers)\r
+ erc = ErcBadFCB;\r
+ else\r
+ paFCB[iFCB]->nUsers--;\r
+\r
+ /* Now we should be able to close it and free the the FUB.\r
+ If the FCB.nUsers flips to 0 it will be free too\r
+ */\r
+\r
+ paFUB[dHandle]->Job = 0;\r
+\r
+ /* This will write all modified fat sectors */\r
+\r
+ for (i=0; i<nFATBufs; i++)\r
+ UpdateFAT(i);\r
+\r
+ return erc;\r
+}\r
+\r
+\r
+/*** Create File ***************************************\r
+ This is Create File for the MMURTL FAT file system.\r
+ This is also used internally to create directories.\r
+********************************************************/\r
+\r
+static U32 CreateFileM(char *pName,\r
+ long cbName,\r
+ long attrib,\r
+ long iJob)\r
+{\r
+unsigned long dHandle, i, j, k, erc, LBA, spc;\r
+char Path[70];\r
+long cbPath;\r
+char filename[12];\r
+U16 CrntClstr, ClstrValue, iStart, DirClstr;\r
+U8 fFound, Drive, fDir;\r
+\r
+ /* First we try to open it to see if it exists. If we get back\r
+ ErcOK or ErcFileInUse the name is already\r
+ in use as a file and we give them ErcDupName.\r
+ We return other errors as we find them.\r
+ */\r
+ if (attrib & DIRECTORY)\r
+ {\r
+ fDir = 1;\r
+ erc = 0;\r
+ }\r
+ else\r
+ fDir = 0;\r
+\r
+ erc = OpenFileM(pName, cbName, 0, 0, &dHandle, iJob);\r
+\r
+ switch (erc)\r
+ {\r
+ case ErcOK:\r
+ CloseFileM(dHandle);\r
+ erc = ErcDupName;\r
+ break;\r
+ case ErcFileInUse:\r
+ erc = ErcDupName;\r
+ break;\r
+ case ErcNotAFile: /* It a directory... */\r
+ break;\r
+ case ErcNoSuchFile:\r
+ { /* OK, this means we can try to create it! */\r
+\r
+ erc = 0;\r
+\r
+ BuildSpec(pName, cbName, Path, &cbPath, iJob);\r
+\r
+ erc = ParseName(Path, cbPath, iJob);\r
+ if (erc)\r
+ return(erc);\r
+\r
+ /* FDrive was set up on Parse */\r
+ Drive = FDrive - 0x41; /* Make it 0-9 */\r
+\r
+ /* First we setup the filename from what was parsed out\r
+ during the Parse call. Then eliminate it from Path.\r
+ */\r
+\r
+ CopyData(FileSpec[SpecDepth], filename, 11); /* filename */\r
+\r
+ filename[11] = 0;\r
+\r
+ /* Hack the filename from Path so we can search the\r
+ directory path properly */\r
+\r
+ while ((cbPath) && (Path[cbPath-1] != 0x5C)) \r
+ {\r
+ cbPath--;\r
+ }\r
+\r
+ /* Each directory sector has 16 32 byte entries.\r
+ We will now walk thru each sector until we find one\r
+ that is a deleted or empty entry.\r
+ A deleted entry has E5h as its first character in the name.\r
+ An unused entry has 00h as its first character in the name.\r
+ */\r
+\r
+ fFound = 0;\r
+ i = 0; /* i = sectornum */\r
+ while ((!fFound) && (!erc)) \r
+ {\r
+ erc = GetDirSectorM(Path, cbPath, abTmpSector,\r
+ 512, i++, &LBA, &DirClstr, iJob);\r
+ if (!erc) \r
+ {\r
+ k = 0;\r
+ pDirEnt = abTmpSector;\r
+ while (k<16) \r
+ {\r
+ if ((pDirEnt->Name[0] == 0xE5) ||\r
+ (!pDirEnt->Name[0]))\r
+ {\r
+ fFound = 1;\r
+ break;\r
+ }\r
+ pDirEnt += 32;\r
+ k++;\r
+ }\r
+ }\r
+ }\r
+ /* When we get here, we have either found an entry or\r
+ we have run out of sectors! If we run out of sectors\r
+ and this is the root, we error out (RootFull), otherwise\r
+ we extend the directory.\r
+ */\r
+\r
+ if ((erc == ErcNoMatch) && (!SpecDepth))\r
+ return (ErcRootFull);\r
+\r
+ else if (erc == ErcEOF)\r
+ { /* reach end of dir! */\r
+\r
+ /* We must now extend the cluster chain for this\r
+ directory entry. DirClstr holds the last\r
+ valid sector of the directory.\r
+ */\r
+\r
+ FillData(abTmpSector, 512, 0);\r
+ spc = Ldrv[Drive].SecPerClstr; /* sectors per cluster */\r
+ erc = ExtendClstrChain(Drive, DirClstr, &DirClstr);\r
+ if (erc)\r
+ return(erc);\r
+ LBA = ClsToLBA(DirClstr, Drive);\r
+ j = LBA;\r
+ i = spc;\r
+ erc = 0;\r
+ while ((i--) && (!erc))\r
+ {\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,\r
+ 1, abTmpSector);\r
+ }\r
+ pDirEnt = abTmpSector; /* first entry in new sector */\r
+ fFound = 1;\r
+\r
+ /* We now have a new cluster on the end of the\r
+ directory and it is all zeros!. The first sector\r
+ is pointed to by LBA and abTmpSector is still zeros\r
+ just as an new dir sector should be with pDirEnt\r
+ pointing to the first entry.\r
+ */\r
+ }\r
+\r
+ if ((!erc) && (fFound)) \r
+ { /* Let's DO IT! */\r
+\r
+ /* pDirEnt points to the entry we will use and\r
+ abTmpSector still has the entire sector in it,\r
+ so we find an empty clstr on the disk, allocate\r
+ to this file, fill in the rest of the dir\r
+ entry and we are almost done.\r
+ */\r
+\r
+ /* Find a fat buf already in memory for this drive */\r
+ /* One WILL be here! */\r
+\r
+ k = 0;\r
+ CrntClstr = 0;\r
+ while ((k<nFATBufs) && (!CrntClstr)) \r
+ {\r
+ if ((Drive == Fat[k].Drive) && (Fat[k].LastUsed))\r
+ CrntClstr = Fat[k].iClstrStart; /* valid cluster */\r
+ k++;\r
+ }\r
+\r
+ if (!CrntClstr)\r
+ CrntClstr = 2; /* Can't find it so start at beginning */\r
+\r
+ iStart = CrntClstr; /* where we started looking for empties */\r
+\r
+ fFound = 0;\r
+ while (!fFound) \r
+ {\r
+ ++CrntClstr; /* next cluster */\r
+ if (CrntClstr == iStart)\r
+ return(ErcDiskFull);\r
+\r
+ erc = GetClstrValue(CrntClstr, Drive, 0, &ClstrValue, &j);\r
+\r
+ if ((!erc) && (!ClstrValue))\r
+ fFound = 1; /* found an empty one */\r
+\r
+ else if (erc == ErcBadFATClstr) \r
+ { /* off the end */\r
+ /* we started AFTER beginning of disk so\r
+ we will go back and look for empties\r
+ from beginning to where we started.\r
+ */\r
+\r
+ if (iStart > 2)\r
+ CrntClstr = 2;\r
+ else\r
+ return(ErcDiskFull);\r
+ }\r
+ else if (erc)\r
+ return(erc);\r
+ }\r
+\r
+ /* If we got here, we found an empty cluster */\r
+\r
+ CopyData(filename, pDirEnt, 11);\r
+ if (!fDir)\r
+ pDirEnt->Attr =\r
+ attrib & (READONLY | HIDDEN | SYSTEM | ARCHIVE);\r
+ else\r
+ pDirEnt->Attr = attrib;\r
+ GetFATTime(&pDirEnt->Time, &pDirEnt->Date);\r
+ pDirEnt->StartClstr = CrntClstr;\r
+ pDirEnt->FileSize = 0;\r
+ erc = SetClstrValue(CrntClstr, 0xFFFF, Drive, &i);\r
+ /* Now we write the dir sector back to disk */\r
+ if (!erc)\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 2, LBA,\r
+ 1, abTmpSector);\r
+\r
+ /* If we were creating a directory, we must add\r
+ the two deafult directory entries . and ..\r
+ This is done by filling out abTmpSector as\r
+ the first sector of an empty directory and writing\r
+ it out to the allocated cluster.\r
+ We then zewro it out and write it to the rest\r
+ of the sectors in the new cluster.\r
+ */\r
+\r
+ if (fDir)\r
+ {\r
+ FillData(abTmpSector, 512, 0);\r
+ pDirEnt = abTmpSector; /* first entry in new sector */\r
+\r
+ /* do the current dir entry (.) */\r
+\r
+ CopyData(". ", pDirEnt, 11);\r
+ pDirEnt->Attr = DIRECTORY;\r
+ GetFATTime(&pDirEnt->Time, &pDirEnt->Date);\r
+ pDirEnt->StartClstr = CrntClstr;\r
+ pDirEnt->FileSize = 0;\r
+\r
+ /* do the previous current dir entry (.) */\r
+\r
+ pDirEnt += 32;\r
+ CopyData(".. ", pDirEnt, 11);\r
+ pDirEnt->Attr = DIRECTORY;\r
+ GetFATTime(&pDirEnt->Time, &pDirEnt->Date);\r
+ pDirEnt->StartClstr = DirClstr;\r
+ pDirEnt->FileSize = 0;\r
+\r
+ spc = Ldrv[Drive].SecPerClstr; /* sectors per cluster */\r
+ LBA = ClsToLBA(CrntClstr, Drive);\r
+ j = LBA;\r
+\r
+ /* Write this sector out to disk */\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,\r
+ 1, abTmpSector);\r
+\r
+ FillData(abTmpSector, 512, 0); /* zero the rest */\r
+\r
+ erc = 0;\r
+ i = spc-1; /* less one cause we wrote the first */\r
+ while ((i--) && (!erc))\r
+ {\r
+ erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,\r
+ 1, abTmpSector);\r
+ }\r
+\r
+ }\r
+ /* This will write all modified fat sectors */\r
+\r
+ for (i=0; i<nFATBufs; i++)\r
+ UpdateFAT(i);\r
+\r
+ return(erc);\r
+\r
+ }\r
+ else\r
+ return(erc);\r
+ }\r
+ default: ;\r
+\r
+ } /* switch */\r
+ return (erc);\r
+}\r
+\r
+/*** Delete File ***************************************\r
+ This is Delete File for the MMURTL FAT file system.\r
+ The file must be opened in mode modify which gives\r
+ the caller exclusive access. The file is closed\r
+ even if the Delete fails.\r
+********************************************************/\r
+\r
+static U32 DeleteFileM(long *dHandle)\r
+{\r
+U32 erc, iFCB, i;\r
+U16 iStart;\r
+U8 Drive;\r
+\r
+ erc = ValidateHandle(dHandle, &iFCB);\r
+ if (erc) return erc;\r
+\r
+ Drive = paFCB[iFCB]->Ldrv; /* What logical drive are we on? */\r
+\r
+ if (!paFCB[iFCB]->Mode) \r
+ { /* Modify mode? */\r
+ CloseFileM(dHandle);\r
+ return ErcReadOnly;\r
+ }\r
+ if (paFUB[dHandle]->fStream)\r
+ DeAllocPage(paFUB[dHandle]->pBuf, 1); /* Free buffer */\r
+\r
+ iStart = paFCB[iFCB]->StartClstr;\r
+ if (iStart) \r
+ {\r
+ erc = TruncClstrChain(Drive, iStart);\r
+ if (!erc)\r
+ erc = SetClstrValue(iStart, 0, Drive, &i);\r
+ }\r
+\r
+ paFCB[iFCB]->Name[0] = 0xE5;\r
+ UpdateDirEnt(iFCB); /* ignore error */\r
+\r
+ /* This means the FS is screwed up. This shouldn't happen... */\r
+ if (!paFCB[iFCB]->nUsers)\r
+ erc = ErcBadFCB;\r
+ else\r
+ paFCB[iFCB]->nUsers--;\r
+\r
+ /* Now we should be able to close it and free the the FUB.\r
+ If the FCB.nUsers flips to 0 it will be free too\r
+ */\r
+\r
+ paFUB[dHandle]->Job = 0;\r
+\r
+ /* This writes all modified fat sectors */\r
+\r
+ for (i=0; i<nFATBufs; i++)\r
+ UpdateFAT(i);\r
+\r
+ return erc;\r
+}\r
+\r
+/*** Rename File ***************************************\r
+ This is Rename File for the MMURTL FAT file system.\r
+********************************************************/\r
+\r
+static U32 RenameFileM(char *pCrntName, long dcbCrntName,\r
+ char *pNewName, long dcbNewName, U32 iJob)\r
+{\r
+U32 dHandle, erc, erc1, iFCB;\r
+\r
+ erc = OpenFileM(pCrntName, dcbCrntName, 1, 0, &dHandle, iJob);\r
+ if (!erc) \r
+ {\r
+ FDrive1 = FDrive;\r
+ CopyData(FileSpec, FileSpec1, 77);\r
+ SpecDepth1 = SpecDepth;\r
+ erc = ParseName(pNewName, dcbNewName, iJob);\r
+ if (!erc)\r
+ if ((FDrive1 != FDrive) || (SpecDepth1 != SpecDepth))\r
+ erc = ErcRenameDrv; /* No Rename across drives */\r
+ if (!erc)\r
+ if (SpecDepth) /* Compare upper tree */\r
+ if (CompareNCS(FileSpec, FileSpec1, SpecDepth * 11) != -1)\r
+ erc = ErcRenameDir; /* No Rename across dirs */\r
+ if (!erc) \r
+ { /* OK to rename */\r
+ iFCB = paFUB[dHandle]->iFCB; /* FCB for this FUB */\r
+ CopyData(FileSpec[SpecDepth], &paFCB[iFCB], 11);\r
+ erc = UpdateDirEnt(iFCB);\r
+ }\r
+ erc1 = CloseFileM(dHandle);\r
+ if (!erc)\r
+ erc = erc1;\r
+ }\r
+ return (erc);\r
+}\r
+\r
+/*** Create Dir ***************************************\r
+ This is Create Directory for the MMURTL FAT file system.\r
+********************************************************/\r
+\r
+static U32 CreateDirM(char *pPath, long cbPath, long iJob)\r
+{\r
+long erc;\r
+\r
+ erc = CreateFileM(pPath, cbPath, DIRECTORY, iJob);\r
+ return(erc);\r
+\r
+}\r
+/*** Delete Directory ***********************************\r
+ This is Delete Directory for the MMURTL FAT file system.\r
+********************************************************/\r
+\r
+static U32 DeleteDirM(char *pPath, long cbPath, long fAllFiles, long iJob)\r
+{\r
+ pPath = 0;\r
+ cbPath = 0;\r
+ fAllFiles = 0;\r
+ iJob = 0;\r
+}\r
+\r
+\r
+/*******************************************************\r
+ This is the File system task. All file system requests\r
+ end up here to be serviced.\r
+********************************************************/\r
+\r
+static void FSysTask(void)\r
+{\r
+U32 FMsg[2], merc, erc, i;\r
+U16 i16;\r
+\r
+while (1) \r
+{\r
+ erc = WaitMsg(FSysExch, FMsg);\r
+ if (!erc)\r
+ {\r
+\r
+ pRQB = FMsg[0]; /* first DD in Msg is pointer to RQBlock */\r
+\r
+ switch (pRQB->ServiceCode)\r
+ {\r
+ case 0 : /* JobAbort - CLOSE ALL FILES FOR THIS JOB! */\r
+ i = 4;\r
+ while (i<nFUBs)\r
+ {\r
+ if (paFUB[i]->Job == pRQB->dData0)\r
+ {\r
+ CloseFileM(i);\r
+ }\r
+ ++i; /* next FUB */\r
+ }\r
+\r
+ erc = 0;\r
+ break;\r
+ case 1 : /* OpenFile */\r
+ erc = OpenFileM(pRQB->pData1, /* pFilename */\r
+ pRQB->cbData1, /* dcbFilename */\r
+ pRQB->dData0, /* Mode */\r
+ pRQB->dData1, /* Type */\r
+ pRQB->pData2, /* pdHandleRet */\r
+ pRQB->RqOwnerJob); /* iJob Owner */\r
+ break;\r
+ case 2 : /* CloseFile */\r
+ erc = CloseFileM(pRQB->dData0); /* Handle */\r
+ break;\r
+ case 3 : /* ReadBlock */\r
+ erc = ReadBlockM(pRQB->dData0, /* Handle */\r
+ pRQB->pData1, /* pDataRet */\r
+ pRQB->cbData1, /* nByes */\r
+ pRQB->dData1, /* dLFA */\r
+ pRQB->pData2, /* pdnBytesRet */\r
+ 0); /* NOT internal */\r
+ break;\r
+ case 4 : /* WriteBlock */\r
+ erc = WriteBlockM(pRQB->dData0, /* Handle */\r
+ pRQB->pData1, /* pData */\r
+ pRQB->cbData1, /* nBytes */\r
+ pRQB->dData1, /* dLFA */\r
+ pRQB->pData2); /* pdBytesRet */\r
+ break;\r
+ case 5 : /* ReadBytes */\r
+ erc = ReadBytesM(pRQB->dData0, /* Handle */\r
+ pRQB->pData1, /* pDataRet */\r
+ pRQB->cbData1, /* nBytes */\r
+ pRQB->pData2); /* pdnBytesRet */\r
+ break;\r
+ case 6 : /* WriteBytes */\r
+ erc = WriteBytesM(pRQB->dData0, /* Handle */\r
+ pRQB->pData1, /* pData */\r
+ pRQB->cbData1, /* nBytes */\r
+ pRQB->pData2); /* pdnBytesRet */\r
+ break;\r
+ case 7 : /* GetFileLFA */\r
+ erc = GetFileLFAM(pRQB->dData0, /* Handle */\r
+ pRQB->pData1); /* pdLFARet */\r
+ break;\r
+ case 8 : /* SetFileLFA */\r
+ erc = SetFileLFAM(pRQB->dData0, /* Handle */\r
+ pRQB->dData1); /* dNewLFA */\r
+ break;\r
+ case 9 : /* GetFileSize */\r
+ erc = GetFileSizeM(pRQB->dData0, /* Handle */\r
+ pRQB->pData1); /* pdSizeRet */\r
+ break;\r
+ case 10 : /* SetFileSize */\r
+ erc = SetFileSizeM(pRQB->dData0, /* Handle */\r
+ pRQB->dData1); /* dSize */\r
+ break;\r
+ case 11 : /* CreateFile */\r
+ erc = CreateFileM(pRQB->pData1, /* pFilename */\r
+ pRQB->cbData1, /* cbFilename */\r
+ pRQB->dData0, /* Attributes */\r
+ pRQB->RqOwnerJob); /* iJob Owner */\r
+ break;\r
+ case 12 : /* RenameFile */\r
+ erc = RenameFileM(pRQB->pData1, /* pCrntName */\r
+ pRQB->cbData1, /* cbCrntName */\r
+ pRQB->pData2, /* pNewName */\r
+ pRQB->cbData2, /* dcbNewName */\r
+ pRQB->RqOwnerJob); /* JobNum */\r
+ break;\r
+ case 13 : /* DeleteFile */\r
+ erc = DeleteFileM(pRQB->dData0); /* Handle */\r
+ break;\r
+ case 14 : /* CreateDirectory */\r
+ erc = CreateDirM(pRQB->pData1, /* pPath */\r
+ pRQB->cbData1, /* cbPath */\r
+ pRQB->RqOwnerJob); /* JobNum */\r
+ break;\r
+ case 15 : /* DeleteDirectory */\r
+ erc = DeleteDirM(pRQB->pData1, /* pPath */\r
+ pRQB->cbData1, /* cbPath */\r
+ pRQB->dData0, /* fAllFiles */\r
+ pRQB->RqOwnerJob); /* JobNum */\r
+ break;\r
+ case 16 : /* GetDirSector */\r
+ erc = GetDirSectorM(pRQB->pData1, /* pPath */\r
+ pRQB->cbData1, /* cbPath */\r
+ pRQB->pData2, /* pSectRet */\r
+ pRQB->cbData2, /* cbRetMax */\r
+ pRQB->dData0, /* SectNum */\r
+ &i, /* for LBARet */\r
+ &i16, /* for DirClstr */\r
+ pRQB->RqOwnerJob); /* JobNum */\r
+ break;\r
+ default :\r
+ erc = ErcBadSvcCode;\r
+ break;\r
+ }\r
+\r
+ merc = Respond(FMsg[0], erc);\r
+\r
+ }\r
+} /* forever */\r
+}\r
+\r
+/***************** PUBLIC BLOCKING CALLS FOR FILESYSM *************\r
+ These calls query the TSS Exhange and use it to make Requests\r
+ to the file system service on the behalf of the caller.\r
+ These calls are fully reentrant! No static data!\r
+*******************************************************************/\r
+\r
+U32 far _OpenFile(char *pFilename,\r
+ long dcbFilename,\r
+ long Mode,\r
+ long Type,\r
+ long *pdHandleRet)\r
+{\r
+long erc, exch, rqhndl, i, msg[2];\r
+ if (dcbFilename == 3)\r
+ {\r
+ if (CompareNCS(pFilename, "NUL" , 3) == -1)\r
+ {\r
+ *pdHandleRet = 0;\r
+ return(0);\r
+ }\r
+ else if (CompareNCS(pFilename, "KBD" , 3) == -1)\r
+ {\r
+ *pdHandleRet = 1;\r
+ return(0);\r
+ }\r
+ else if (CompareNCS(pFilename, "VID" , 3) == -1)\r
+ {\r
+ *pdHandleRet = 2;\r
+ return(0);\r
+ }\r
+ else if (CompareNCS(pFilename, "LPT" , 3) == -1)\r
+ {\r
+ erc = DeviceOp(3, 10, 0, 0, &i); /* 10=Open */\r
+ if (!erc)\r
+ *pdHandleRet = 3;\r
+ return(erc);\r
+ }\r
+ }\r
+\r
+ GetTSSExch(&exch); /* No error will come back! */\r
+ erc = Request(fsysname, 1, exch, &rqhndl,\r
+ 1, /* 1 Send ptr */\r
+ pFilename, dcbFilename,\r
+ pdHandleRet, 4,\r
+ Mode, Type, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _CloseFile(unsigned long dHandle)\r
+{\r
+long erc, exch, rqhndl, i, msg[2];\r
+\r
+ if (dHandle < 3)\r
+ return(0);\r
+ else if (dHandle == 3)\r
+ {\r
+ erc = DeviceOp(3, 11, 0, 0, &i); /* 11 = Close */\r
+ return(erc);\r
+ }\r
+\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 2, exch, &rqhndl,\r
+ 0, /* 0 Send ptr */\r
+ 0, 0,\r
+ 0, 0,\r
+ dHandle, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _ReadBlock(long dHandle,\r
+ char *pDataRet,\r
+ long nBlks,\r
+ long dLFA,\r
+ long *pdnBlksRet)\r
+{\r
+long erc, exch, rqhndl, msg[2], i;\r
+ if (dHandle < 4)\r
+ return(ErcNotSupported);\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 3, exch, &rqhndl,\r
+ 1, /* 1 Send ptr */\r
+ pDataRet, nBlks,\r
+ pdnBlksRet, 4,\r
+ dHandle, dLFA, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc)\r
+ return(erc);\r
+ if(msg[1]) \r
+ {\r
+ DeviceStat(10, &FDDevStat, 64, &i);\r
+ }\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _WriteBlock(long dHandle,\r
+ char *pData,\r
+ long nBlks,\r
+ long dLFA,\r
+ long *pdnBlksRet)\r
+\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ if (dHandle < 4)\r
+ return(ErcNotSupported);\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 4, exch, &rqhndl,\r
+ 1, /* 1 Send ptr */\r
+ pData, nBlks,\r
+ pdnBlksRet, 4,\r
+ dHandle, dLFA, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _ReadBytes(long dHandle,\r
+ char *pDataRet,\r
+ long nBytes,\r
+ long *pdnBytesRet)\r
+{\r
+long erc, exch, rqhndl, msg[2], i;\r
+ if (dHandle == 0)\r
+ {\r
+ *pdnBytesRet = 0;\r
+ return(0);\r
+ }\r
+ else if (dHandle == 1)\r
+ {\r
+ i = 0;\r
+ while (i < nBytes)\r
+ {\r
+ ReadKbd(*pDataRet++, 1);\r
+ i++;\r
+ }\r
+ *pdnBytesRet = i;\r
+ return(0);\r
+ }\r
+ else if ((dHandle == 2) || (dHandle == 3))\r
+ {\r
+ *pdnBytesRet = 0;\r
+ return(ErcWriteOnly);\r
+ }\r
+\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 5, exch, &rqhndl,\r
+ 1, /* 1 Send ptr */\r
+ pDataRet, nBytes,\r
+ pdnBytesRet, 4,\r
+ dHandle, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _WriteBytes(long dHandle,\r
+ char *pData,\r
+ long nBytes,\r
+ long *pdnBytesRet)\r
+\r
+{\r
+long erc, exch, rqhndl, VidAttr, msg[2];\r
+\r
+ if (dHandle == 0) \r
+ {\r
+ *pdnBytesRet = nBytes;\r
+ return(0);\r
+ }\r
+ else if (dHandle == 1)\r
+ {\r
+ *pdnBytesRet = 0;\r
+ return(ErcReadOnly);\r
+ }\r
+ else if (dHandle == 2)\r
+ {\r
+ GetNormVid(&VidAttr);\r
+ TTYOut(pData, nBytes, VidAttr);\r
+ *pdnBytesRet = nBytes;\r
+ return(0);\r
+ }\r
+ else if (dHandle == 3)\r
+ {\r
+ erc = DeviceOp(3, 2, 0, nBytes, pData); /* 2 = CmdWriteRec */\r
+ return(erc);\r
+ }\r
+\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 6, exch, &rqhndl,\r
+ 1, /* 1 Send ptr */\r
+ pData, nBytes,\r
+ pdnBytesRet, 4,\r
+ dHandle, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _GetFileLFA(long dHandle,\r
+ long *pdLFARet)\r
+\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ if (dHandle < 4)\r
+ return(ErcEOF);\r
+\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 7, exch, &rqhndl,\r
+ 0, /* 0 Send ptrs */\r
+ pdLFARet, 4,\r
+ 0, 0,\r
+ dHandle, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _SetFileLFA(long dHandle,\r
+ long dNewLFA)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ if (dHandle < 4)\r
+ return(ErcNotSupported);\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 8, exch, &rqhndl,\r
+ 0, /* 0 Send ptrs */\r
+ 0, 0,\r
+ 0, 0,\r
+ dHandle, dNewLFA, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _GetFileSize(long dHandle,\r
+ long *pdSizeRet)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ if (dHandle < 4)\r
+ return(ErcNotSupported);\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 9, exch, &rqhndl,\r
+ 0, /* 0 Send ptrs */\r
+ pdSizeRet, 4,\r
+ 0, 0,\r
+ dHandle, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _SetFileSize(long dHandle,\r
+ long dSize)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ if (dHandle < 4)\r
+ return(ErcNotSupported);\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 10, exch, &rqhndl,\r
+ 0, /* 0 Send ptrs */\r
+ 0, 0,\r
+ 0, 0,\r
+ dHandle, dSize, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _CreateFile(char *pFilename,\r
+ long cbFilename,\r
+ long Attribute)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 11, exch, &rqhndl,\r
+ 1, /* 1 Send ptrs */\r
+ pFilename, cbFilename,\r
+ 0, 0,\r
+ Attribute, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _RenameFile(char *pCrntName,\r
+ long cbCrntName,\r
+ char *pNewName,\r
+ long cbNewName)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 12, exch, &rqhndl,\r
+ 2, /* 2 Send ptrs */\r
+ pCrntName, cbCrntName,\r
+ pNewName, cbNewName,\r
+ 0, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _DeleteFile(long dHandle)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ if (dHandle < 4)\r
+ return(ErcNotSupported);\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 13, exch, &rqhndl,\r
+ 0, /* 0 Send ptrs */\r
+ 0, 0,\r
+ 0, 0,\r
+ dHandle, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _CreateDir(char *pPath,\r
+ long cbPath)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 14, exch, &rqhndl,\r
+ 1, /* 1 Send ptrs */\r
+ pPath, cbPath,\r
+ 0, 0,\r
+ 0, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _DeleteDir(char *pPath,\r
+ long cbPath,\r
+ long fAllFiles)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 15, exch, &rqhndl,\r
+ 1, /* 1 Send ptrs */\r
+ pPath, cbPath,\r
+ 0, 0,\r
+ fAllFiles, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/*************************************/\r
+U32 far _GetDirSector(char *pPathSpec,\r
+ long cbPathSpec,\r
+ char *pEntRet,\r
+ long cbEntRet,\r
+ long SectNum)\r
+{\r
+long erc, exch, rqhndl, msg[2];\r
+ GetTSSExch(&exch);\r
+ erc = Request(fsysname, 16, exch, &rqhndl,\r
+ 1, /* 1 Send ptrs */\r
+ pPathSpec, cbPathSpec,\r
+ pEntRet, cbEntRet,\r
+ SectNum, 0, 0);\r
+ if (!erc) erc = WaitMsg(exch, msg);\r
+ if (erc) return(erc);\r
+ return(msg[1]);\r
+}\r
+\r
+/********************************************\r
+ Initialization Routine for the File system.\r
+ This is called the Monitor where any errors\r
+ will be reported.\r
+**********************************************/\r
+\r
+U32 InitFS(void)\r
+{\r
+U32 erc, i, j;\r
+U8 *pMem;\r
+\r
+ /* Allocate FAT buffers and initialize FAT related structures.\r
+ We will allocate 24Kb worth of buffers (6 Pages, 16 buffers).\r
+ */\r
+\r
+ Fat[0].pBuf = FatBufA; /* floppy fat buffers */\r
+\r
+ erc = AllocOSPage(2, &pMem);\r
+\r
+ if (!erc) /* hard disk fat buffers from allocated mem */\r
+ for (i=1; i<nFATBufs; i++)\r
+ {\r
+ Fat[i].pBuf = pMem; /* Make pBuf point to each buffer */\r
+ pMem += 512; /* Next buffer in allocated memory */\r
+ }\r
+\r
+ /* Allocate and initialize FCBs and FUBs. This is enough FCBs and\r
+ FUBS for 128 openfiles. (Good enough to start...)\r
+ */\r
+\r
+ if (!erc)\r
+ erc = AllocOSPage(2, &paFCB); /* a pointer to allocated FCBs. */\r
+ if (!erc)\r
+ FillData(paFCB, 8192, 0);\r
+ if (!erc)\r
+ erc = AllocOSPage(1, &paFUB); /* a pointer to allocated FUBs. */\r
+ if (!erc)\r
+ FillData(paFUB, 4096, 0);\r
+\r
+ if (!erc) erc = read_PE(); /* reads partition tables */\r
+\r
+ if (!erc) erc = SetDriveGeometry(12);\r
+\r
+ if (!erc) \r
+ {\r
+ erc = SetDriveGeometry(13);\r
+ if (erc == 663) erc = 0; /* may be invalid drive */\r
+ }\r
+\r
+ StatFloppy(0);\r
+ StatFloppy(1);\r
+\r
+ /* read all logical drive boot sectors */\r
+\r
+ if (!erc) \r
+ {\r
+ for (i=0; i< nLDrvs; i++) \r
+ {\r
+ if (Ldrv[i].DevNum != 0xff) \r
+ {\r
+ read_BS(i);\r
+ }\r
+ }\r
+ }\r
+\r
+ for (i=0; i<nLDrvs; i++)\r
+ if (Ldrv[i].DevNum != 0xff) \r
+ {\r
+ j=12;\r
+ if (Ldrv[i].fFAT16)\r
+ j=16;\r
+ xprintf("%c: Heads %d, Sec/Trk %d, Sec/Clstr %d, Dev %d, FAT%d \r\n",\r
+ i+0x41,\r
+ Ldrv[i].nHeads,\r
+ Ldrv[i].nSecPerTrk,\r
+ Ldrv[i].SecPerClstr,\r
+ Ldrv[i].DevNum,\r
+ j);\r
+ }\r
+\r
+ if (!erc)\r
+ erc = AllocExch(&FSysExch);\r
+\r
+ /* Start the filesystem task at a decently high priority (5).\r
+ This should be higher than the Monitor status task and even the\r
+ Keyboard. */\r
+\r
+ if (!erc)\r
+ erc = SpawnTask(&FSysTask, 5, 0, &FSysStack[511], 1);\r
+\r
+ if (!erc)\r
+ erc = RegisterSvc(fsysname, FSysExch);\r
+\r
+return erc;\r
+\r
+}\r