]> pd.if.org Git - mmurtl/blob - ossource/fsys.c
autocommit for file dated 2003-12-29 17:36:54
[mmurtl] / ossource / fsys.c
1 /* This is the MMURTL, MS-DOS Compatible (FAT) File system.  */\r
2 \r
3 /*\r
4   MMURTL Operating System Source Code\r
5   Copyright 1991,1992,1993,1994 Richard A. Burgess\r
6   ALL RIGHTS RESERVED   Version 1.0\r
7 */\r
8 \r
9 /*\r
10 About MS-DOS disk formats and how MMURTL handles them.\r
11 \r
12 Physical Disk Layouts\r
13   From the disk controller's standpoint:\r
14           Cylinder numbers run from 0 to nMaxCyls-1.\r
15           Head numbers run from 0 to nMaxheads-1.\r
16           Sector numbers run from 1 to nMaxSectorsPerTrack.\r
17 \r
18 Physical (Absolute) Sector Numbers\r
19 \r
20   Physical sector numbers (absolute) begin at Cyl 0, Head 0, Sector 1.\r
21   As the physical sector number rolls over (nMaxSectorsPerTrack+1),\r
22   the Head number is incremented which moves us to the next track\r
23   (same cylinder, next head).  When the head number rolls over\r
24   (nMaxHeads is reached) the cylinder number is incremented.\r
25 \r
26   Note: Track and cylinder are NOT interchangable terms in the\r
27   above text.  If you have 6 heads on your drive, you have\r
28   6 tracks per cylinder.  This can be confusing because many\r
29   books and documents use the terms interchangably.\r
30   And you can, so long as you know that's what you're doing.\r
31 \r
32 Hidden Sectors\r
33 \r
34   MS-DOS reserves a section of the physical hard disk. This area\r
35   is called the Hidden Sectors.  This is usually the very first\r
36   track on the disk (begins at Cyl 0, head 0, Sector 1).  The\r
37   partition tables are kept at the very end of the first sector in\r
38   this hidden area (offset 01BEh in the first sector to be exact).\r
39   The partition tables are 16 byte entries that describe\r
40   "logical" sections of the disk that can be treated as separate drives.\r
41   There are usually no "hidden sectors" on floppy disks, nor are there\r
42   any partition tables.\r
43 \r
44 \r
45 MMURTL Logical Block Address (LBA)\r
46 \r
47   MMURTL device drivers treat the entire disk as a single physical\r
48   drive.  The MMURTL file system reads the partition tables,\r
49   then sets up the device driver to span the entire physical disk\r
50   as 0 to nMaxBlockNumbers-1.  This is refered to as the Logical Block\r
51   Address (LBA) and is the value passed in to the DeviceOp call for\r
52   the MMURTL hard/floppy disk device drivers (LBAs are used with\r
53   all MMURTL devices).\r
54 \r
55   Note: DO NOT confuse MMURTL's LBA for the sector number in an MS-DOS\r
56   logical drive.  MMURTL calls these logical blocks because we still\r
57   have to convert them into physical cylinder, head and sector to\r
58   retrieve the data.\r
59 \r
60 MS-DOS Boot Sector\r
61 \r
62   The first sector of an MS-DOS logical drive will be its boot\r
63   sector. Each of the MS-DOS logical partitions will have a boot\r
64   sector although only the first will be marked as bootable (if any are).\r
65   It's position on the disk is calculated from the partition table\r
66   information.\r
67 \r
68 MMURTL File System Initialization\r
69 \r
70   The MMURTL-FAT file system reads the partition table and saves\r
71   the starting LBA and length of each of DOS logical disk that is\r
72   found.  Armed with this information, MMURTL can access each of\r
73   the DOS logical disks as a separate disk drive. To maintain some\r
74   sanity, the MMURTL file system gives all of its logical drives\r
75   a letter just like MS-DOS.  MMURTL supports two floppy drives\r
76   (A & B) and up to eight logical hard disk (C-J).  All information\r
77   on the Logical Drives are kept in an array of records (Ldrvs).\r
78   This includes the logical letter to physical drive conversions.\r
79 \r
80   Once we have the layout of each of the partitons, we read the boot\r
81   sector from the first DOS logical drive.  The boot sector contains\r
82   several pieces of important information about the drive geometry\r
83   (numbers of heads, sectors per track, etc.), which are also placed\r
84   in the Logical Drive stuctures.\r
85 \r
86   Once we have the drive geometry information we setup the MMURTL\r
87   device driver.  This tells the device driver how many cylinders,\r
88   heads and sectors per track are on the physical disk.\r
89   Until this is done, the device driver assumes a minimum drive size\r
90   and you should only read the partition table (or boot sector if no\r
91   partition table is on the disk).  This provides enough\r
92   information to do a DeviceInit call to set up proper drive\r
93   geometry.\r
94 \r
95   If you were building a loadable file system to replace the one that\r
96   is included in MMURTL, you would call your routine to initialize\r
97   the file system very early in the main program block.  You must\r
98   not service file system requests until this is done.\r
99 \r
100 */\r
101 \r
102 #define U32 unsigned long\r
103 #define U16 unsigned int\r
104 #define U8  unsigned char\r
105 #define S32 long\r
106 #define S16 int\r
107 #define S8  char\r
108 #define TRUE 1\r
109 #define FALSE 0\r
110 \r
111 /*********** MMURTL Public Prototypes ***************/\r
112 \r
113 /* From MKernel */\r
114 \r
115 extern far AllocExch(long *pExchRet);\r
116 extern far U32 GetTSSExch(U32  *pExchRet);\r
117 extern far SpawnTask(char *pEntry,\r
118                              long dPriority,\r
119                      long fDebug,\r
120                      char *pStack,\r
121                              long fOSCode);\r
122 extern far long WaitMsg(long Exch, char *pMsgRet);\r
123 extern far long CheckMsg(long Exch, char *pMsgRet);\r
124 extern far long Request(unsigned char *pSvcName,\r
125                                                 unsigned int  wSvcCode,\r
126                                                 unsigned long dRespExch,\r
127                                                 unsigned long *pRqHndlRet,\r
128                                                 unsigned long dnpSend,\r
129                                                 unsigned char *pData1,\r
130                                                 unsigned long dcbData1,\r
131                                                 unsigned char *pData2,\r
132                                                 unsigned long dcbData2,\r
133                                                 unsigned long dData0,\r
134                                                 unsigned long dData1,\r
135                                                 unsigned long dData2);\r
136 \r
137 extern far long Respond(long dRqHndl, long dStatRet);\r
138 \r
139 /* From MData */\r
140 extern far void CopyData(U8 *pSource, U8 *pDestination, U32 dBytes);\r
141 extern far void FillData(U8 *pDest, U32 cBytes, U8 bFill);\r
142 extern far long CompareNCS(U8 *pS1, U8 *pS2, U32 dSize);\r
143 \r
144 /* From MTimer.h */\r
145 extern far long GetCMOSTime(long *pTimeRet);\r
146 extern far long GetCMOSDate(long *pTimeRet);\r
147 extern far long GetTimerTick(long *pTickRet);\r
148 \r
149 /* From MVid.h */\r
150 extern far long TTYOut (char *pTextOut, long ddTextOut, long ddAttrib);\r
151 extern far long GetNormVid(long *pNormVidRet);\r
152 \r
153 #include "MKbd.h"\r
154 \r
155 /* From MDevDrv */\r
156 extern far U32  DeviceOp(U32  dDevice,\r
157                                  U32  dOpNum,\r
158                                                  U32  dLBA,\r
159                                                  U32  dnBlocks,\r
160                                                  U8  *pData);\r
161 \r
162 extern far U32  DeviceStat(U32  dDevice,\r
163                                                    S8 * pStatRet,\r
164                                                    U32  dStatusMax,\r
165                                                    U32  *pdSatusRet);\r
166 \r
167 extern far U32  DeviceInit(U32  dDevNum,\r
168                                                    S8  *pInitData,\r
169                                                    U32   sdInitData);\r
170 \r
171 /* From MMemory.h */\r
172 extern far U32 AllocOSPage(U32 nPages, U8 *ppMemRet);\r
173 extern far U32 DeAllocPage(U8 *pOrigMem, U32 nPages);\r
174 \r
175 /* From MJob.h */\r
176 extern far U32 GetPath(long JobNum, char *pPathRet, long *pdcbPathRet);\r
177 extern far U32 RegisterSvc(S8 *pName, U32 Exch);\r
178 \r
179 /* NEAR support for debugging */\r
180 \r
181 extern long xprintf(char *fmt, ...);\r
182 extern U32 Dump(unsigned char *pb, long cb);\r
183 \r
184 /* File System error codes */\r
185 \r
186 #define ErcOK                    0              /* Alls Well */\r
187 #define ErcEOF                   1              /* DUH... The END */\r
188 #define ErcBadSvcCode    32             /* Service doesn't handle that code */\r
189 \r
190 #define ErcBadFileSpec   200    /* invalid file spec (not correct format)*/\r
191 #define ErcNoSuchDrive   201    /* Try another letter bozo */\r
192 #define ErcNotAFile              202    /* Open a directory?? NOT */\r
193 #define ErcNoSuchFile    203    /* No can do! It ain't there...*/\r
194 #define ErcNoSuchDir     204    /* Ain't no such dir... */\r
195 #define ErcReadOnly              205    /* You can't modify it bubba */\r
196 #define ErcNoFreeFCB     206    /* We're really hurtin... */\r
197 #define ErcBadOpenMode   207    /* Say what? Mode??? */\r
198 #define ErcFileInUse     208    /* File is open in an incompatible mode */\r
199 #define ErcNoFreeFUB     209    /* Sorry, out of File User Blocks */\r
200 #define ErcBadFileHandle 210    /* WHOAAA, bad handle buddy! */\r
201 #define ErcBrokenFile    211    /* Cluster chain broken on file */\r
202 #define ErcBadFCB                213    /* We got REAL problems... */\r
203 #define ErcStreamFile    214    /* Operation not allowed on Stream File */\r
204 #define ErcBlockFile     215    /* Operation not allowed on Block File */\r
205 #define ErcBeyondEOF     217    /* SetLFA or Read/WriteBlock beyond EOF */\r
206 #define ErcNoParTable    218    /* No partiton table found on disk!!! */\r
207 #define ErcBadFATClstr   220    /* File system screwed up (or your disk) */\r
208 #define ErcRenameDrv     222    /* They have tried to rename across Dir/Vol*/\r
209 #define ErcRenameDir     223    /* They have tried to rename across Dir/Vol*/\r
210 #define ErcNoMatch       224    /* No matching directory entry */\r
211 \r
212 #define ErcWriteOnly     225    /* Attempt to read write-only device */\r
213 #define ErcDupName               226    /* Name exists as a file or dir already */\r
214 #define ErcNotSupported  227    /* Not supported on this file  */\r
215 #define ErcRootFull              228    /* The Root Directory is Full  */\r
216 #define ErcDiskFull              230    /* No more free CLUSTERS!!!  */\r
217 \r
218 #define ErcNewMedia              605    /* for floppy mounting from FDD */\r
219 \r
220 /**************** FAT Buffer control structure **********************/\r
221 \r
222 /*\r
223    The Fat structures are for keeping track of the FAT buffers.\r
224    We never want to have more than one copy of a FAT sector in\r
225    memory at one time, and we also never want to read one when\r
226    its already here (a waste of time)!  We also keep track\r
227    of the last time it was used and deallocate the oldest (LRU -\r
228    Least Recently Used). Initially filling out the Fat control\r
229    structure is part of the file system initialization.  If the\r
230    FAT sector we are in has been modified (data written to clusters\r
231    in it & FAT updated) we write it ASAP!\r
232    Each FAT buffer is 1 sector long, except the first one which\r
233    is 3 sectors for floppies (FAT12 types). This is because the\r
234    FAT12 entires span sectors!\r
235 */\r
236 \r
237 #define nFATBufs 17             /* 1 Static for floppies + 16 * 512 = 8192, 2 pages */\r
238 \r
239 static struct fattype {                         /* */\r
240         U8  *pBuf;                      /* points to beginning of fat buffer  */\r
241         U32 LastUsed;           /* Tick when last used (0 = Never) */\r
242         U32 LBASect;            /* LBA of first FAT sect in buf (where it came from) */\r
243         U16 iClstrStart;        /* Starting cluster for each buf  */\r
244         U8  Drive;                      /* LDrive this FAT sector is from */\r
245         U8  fModLock;           /* Bit 0 = Modified, bit 1 = Locked  */\r
246         };\r
247 \r
248 static struct fattype Fat[nFATBufs];    /* 16 bytes * 17 */\r
249 \r
250 /* We read 3 sectors worth of floppy fat buf in cause cluster\r
251 entries span sectors\r
252 */\r
253 \r
254 U8 FatBufA[1536];  /* floppy fat buffer */\r
255 \r
256 #define FATMOD  0x01\r
257 #define FATLOCK 0x02\r
258 \r
259 /**************** File Contol Block Structures (FCBs) **********/\r
260 /* One FCB is allocated and filled out for each file that is open.\r
261    The actual directory entry structure from the disk is embedded\r
262    in the FCB so it can be copied directly to/from the directory\r
263    sector on the disk.\r
264 */\r
265 #define nFCBs 128\r
266 #define sFCB  64\r
267 \r
268 static struct FCB {\r
269         S8  Name[8];            /* From here to Filesize is copy of DirEnt */\r
270         S8  Ext[3];\r
271         S8  Attr;                       /* from MS-DOS */\r
272         U8  Resvd1[10];         /* ????????  */\r
273         U16 Time;                       /* Only changed when created or updated */\r
274         U16 Date;\r
275         U16 StartClstr;         /* At least one per file!! */\r
276         U32 FileSize;           /* last entry in FAT Dir Ent (32 bytes) */\r
277         U32 LBADirSect;         /* LBA of directory sector this is from */\r
278         U16 oSectDirEnt;        /* Offset in sector for the dir entry */\r
279         U8  Ldrv;                       /* Logical drive this is on (A-J, 0-9) */\r
280         U8  Mode;                       /* 0 or 1 (Read or Modify). */\r
281         U8  nUsers;                     /* Active FUBs for this file (255 MAX). 0= Free FCB */\r
282     U8  fMod;                   /* This file was modified! */\r
283         U8  Resvd[22];          /* Out to 64 bytes */\r
284         };\r
285 \r
286 static struct FCB *paFCB;               /* a pointer to array of allocated FCBs. */\r
287 static struct FCB *pFCB;                /* pointer to one FCB */\r
288 \r
289 /********************** File User Blocks **************************/\r
290 \r
291 /* Each user of an open file is assigned a FUB.  The FUB number is the\r
292    filehandle (beginning with 3). ) 0, 1 & 2 are reserved for NUL,\r
293    KBD and VID devices.\r
294 */\r
295 \r
296 #define nFUBs 128\r
297 #define sFUB  32\r
298 \r
299 /* The FUB contains information on a file related to a user's view\r
300    of the file.  It is used to hold information on files opened\r
301    in stream and block mode. Three important fields in the FUB are:\r
302    LFABuf - LFA of first byte in buffer for a stream file.\r
303    Clstr - Clstr of last block read or stream fill.\r
304    LFAClstr - LFA of first byte in Clstr.\r
305 \r
306    LFAClstr and Clstr give us a relative starting point when\r
307    reading a file from disk.  If we didn't save this information\r
308    on the last access, we would have to "run" the cluster chain\r
309    everytime we wanted to read or access a file beyond the last\r
310    point we read. \r
311 */\r
312 \r
313 struct FUB {\r
314         U16 Job;                        /* User's Job Number. 0 if FUB is free. */\r
315         U16 iFCB;                       /* FCB number for this file (0 to nFCBs-1) */\r
316         U32 CrntLFA;            /* Current Logical File Address (File Ptr) */\r
317         U8  *pBuf;                      /* Ptr to buffer if stream mode */\r
318         U32 sBuf;                       /* Size of buffer for Stream file in bytes */\r
319         U32     LFABuf;                 /* S-First LFA in Clstr Buffer */\r
320         U32 LFAClstr;           /* LFA of Clstr (below). */\r
321         U16 Clstr;                      /* Last Cluster read */\r
322         U8  fModified;          /* Data in buffer was modified */\r
323         U8  fStream;            /* NonZero for STREAM mode */\r
324         U8  Rsvd[4];            /* Pad to 32 bytes */\r
325         };\r
326 \r
327 static struct FUB *paFUB;               /* a pointer to allocated FUBs. Set up at init. */\r
328 static struct FUB *pFUB;                /* a pointer to allocated FUBs. Set up at init. */\r
329 \r
330 /* Boot sector info (62 byte structure) */\r
331 struct fsbtype {\r
332           U8  Jmp[3];\r
333           U8  OEMname[8];\r
334           U16 bps;\r
335           U8  SecPerClstr;\r
336           U16 ResSectors;\r
337           U8  FATs;\r
338           U16 RootDirEnts;\r
339           U16 Sectors;\r
340           U8  Media;\r
341           U16 SecPerFAT;\r
342           U16 SecPerTrack;\r
343           U16 Heads;\r
344           U32 HiddenSecs;\r
345           U32 HugeSecs;\r
346           U8  DriveNum;\r
347           U8  Rsvd1;\r
348           U8  BootSig;\r
349           U32 VolID;\r
350           U8  VolLabel[11];\r
351           U8  FileSysType[8];           /* 62 bytes */\r
352           };\r
353 static struct fsbtype  fsb;\r
354 \r
355 \r
356 /* Partition Table Entry info. 16 bytes */\r
357 struct partent {\r
358   U8  fBootable;\r
359   U8  HeadStart;\r
360   U8  SecStart;\r
361   U8  CylStart;\r
362   U8  FATType;\r
363   U8  HeadEnd;\r
364   U8  SecEnd;\r
365   U8  CylEnd;\r
366   U32 nFirstSector;\r
367   U32 nSectorsTotal;\r
368   };\r
369 \r
370 static struct partent partab[4];        /* 4 partition table entries 64 bytes */\r
371 static U16 partsig;\r
372 \r
373 /* Bit definitions in attribute field for a directory entry */\r
374 \r
375 #define ATTRNORM  0x00\r
376 #define READONLY  0x01\r
377 #define HIDDEN    0x02\r
378 #define SYSTEM    0x04\r
379 #define VOLNAME   0x08\r
380 #define DIRECTORY 0x10\r
381 #define ARCHIVE   0x20\r
382 \r
383 /* Directory Entry Record, 32 bytes */\r
384 \r
385 struct dirstruct {\r
386         U8  Name[8];\r
387         U8  Ext[3];\r
388         U8  Attr;\r
389         U8  Rsvd[10];\r
390         U16 Time;\r
391         U16 Date;\r
392         U16 StartClstr;\r
393         U32 FileSize;\r
394         };\r
395 \r
396 static struct dirstruct  dirent;\r
397 \r
398 static struct dirstruct *pDirEnt;               /* a pointer to a dir entry */\r
399 \r
400 /* When a file is opened, the filename is parsed into an array\r
401    to facilitate searching the directory tree.  IN MS-DOS all\r
402    dir and file names are SPACE padded (20h).  The FileSpec array\r
403    contains the fully parsed path of the file.  For instance,\r
404    If you were to open "A:\Dog\Food\IsGood.txt" the FileSpec\r
405    array would look like this:\r
406    FileSpec[0] = "DOG        "\r
407    FileSpec[1] = "FOOD       "\r
408    FileSpec[2] = "ISGOOD  TXT"\r
409    FileSpec[3][0] = NULL;\r
410    Note that the DOT is not inlcuded (it's not in the DOS directory\r
411    either), and the next unused FileSpec entry contain NULL in the\r
412    first byte.  SpecDepth tells us how many directories deep the\r
413    name goes.\r
414 */\r
415 \r
416 static U8 FDrive                                        /* Drive parsed from file operation */\r
417 static U8 FileSpec[7][11];                      /* Hierarchy from file spec parsing */\r
418 static U8 SpecDepth;                            /* Depth of parse (0=Root File) */\r
419 \r
420 /* Used for Rename */\r
421 static U8 FDrive1                                       /* Drive parsed from file operation */\r
422 static U8 FileSpec1[7][11];             /* Hierarchy from file spec parsing */\r
423 static U8 SpecDepth1;                           /* Depth of parse (0=Root File) */\r
424 \r
425 /* raw sector buffer for all kinds of stuff */\r
426 \r
427 static U8  abRawSector[516];\r
428 static U8  abTmpSector[516];\r
429 static U8  abDirSectBuf[516];\r
430 \r
431 /* These arrays keep track of physical drive data (0-4). */\r
432 #define nPDrvs 4\r
433 \r
434 static struct phydrv {\r
435         U32 nHeads;             /* heads per drives   */\r
436         U32 nSecPerTrk;         /* Sectors per track  */\r
437         U16 BS1Cyl;                     /* Cyl of 1st boot sector on disk */\r
438         U8  BS1Head;            /* Head of 1st boot sector on disk */\r
439         U8  BS1Sect;            /* Sector of 1st boot sector on disk */\r
440         }\r
441 \r
442 static struct phydrv  PDrvs[nPDrvs];\r
443 \r
444 /* This array of structures keeps track of logical drive data (A-J). */\r
445 \r
446 #define nLDrvs 10\r
447 \r
448 static struct ldrvtype {\r
449         U32 LBA0;                       /* lba for Start of LDrive (bootSect) */\r
450         U32 LBAData;            /* lba for Start of Data Area */\r
451         U32 LBAMax;                     /* Max lba for logical drive */\r
452         U32 LBARoot;            /* lba of the Root directory */\r
453         U32 LBAFAT;                     /* lba of first FAT */\r
454         U16 nHeads;             /* Setup after boot sector is read */\r
455         U16 nSecPerTrk;         /* Setup after boot sector is read */\r
456         U16 nRootDirEnt;        /* Number of Root directory entries */\r
457         U16 sFAT;                       /* nSectors in a FAT */\r
458         U8  DevNum;                     /* Device Number for this ldrv FF = NONE */\r
459         U8  SecPerClstr;        /* For each logical drive */\r
460         U8  nFATS;                      /* number of FATs */\r
461         U8  fFAT16;                     /* True for FAT16 else FAT12 */\r
462         };\r
463 \r
464 static struct ldrvtype  Ldrv[nLDrvs];\r
465 \r
466 /* This is the Hard Disk Device Status record.\r
467    It is peculiar to the HD Drvr */\r
468 \r
469 struct hddevtype{\r
470   U32 erc;\r
471   U32 blocks_done;\r
472   U32 BlocksMax;\r
473   U8  fNewMedia;\r
474   U8  type_now;         /* current fdisk_table for drive selected */\r
475   U8  resvd0[2];        /* padding for DWord align  */\r
476   U32 nCyl;                     /* total physical cylinders (we really don't care) */\r
477   U32 nHead;            /* total heads on device    */\r
478   U32 nSectors;         /* Sectors per track        */\r
479   U32 nBPS;                     /* Number of bytes per sect.  32 bytes out to here.*/\r
480   U32 LastRecalErc0;\r
481   U32 LastSeekErc0;\r
482   U8  LastStatByte0;\r
483   U8  LastErcByte0;\r
484   U8  fIntOnReset;      /* Interrupt was received on HDC_RESET */\r
485   U8  filler0;\r
486   U32 LastRecalErc1;\r
487   U32 LastSeekErc1;\r
488   U8  LastStatByte1;\r
489   U8  LastErcByte1;\r
490   U8  ResetStatByte;    /* Status Byte immediately after RESET */\r
491   U8  filler1;\r
492   U32 resvd1[2];        /* out to 64 bytes */\r
493   };\r
494 \r
495 static struct hddevtype   HDDevStat;\r
496 \r
497 /* This is the Floppy Device Status record.\r
498    It is peculiar to the FD Drvr */\r
499 \r
500 struct fdstattype{\r
501   U32 erc;                      /* Last Error from device */\r
502   U32 blocks_done;\r
503   U32 BlocksMax;\r
504   U8 fNewMedia;\r
505   U8 type_now;          /* current fdisk_table for drive selected */\r
506   U8 resvd1[2];         /* padding for DWord align  */\r
507   U32 nCyl;                     /* total physical cylinders */\r
508   U32 nHead;            /* total heads on device    */\r
509   U32 nSectors;         /* Sectors per track        */\r
510   U32 nBPS;                     /* Number of bytes per sect */\r
511   U8 params[16];        /* begin device specific fields */\r
512   U8 STATUS[8];         /* status returned from FDC (for user status) */\r
513   U32 resvd3;\r
514   U32 resvd4;           /* 64 bytes total */\r
515   };\r
516 \r
517 static struct fdstattype  FDDevStat;\r
518 \r
519 static long FSysStack[512];     /* 2048 byte stack for Fsys task */\r
520 \r
521 static long FSysExch;\r
522 \r
523 struct reqtype {                        /* 64 byte request block structure */\r
524         long ServiceExch;\r
525         long RespExch;\r
526         long RqOwnerJob;\r
527         long ServiceRoute;\r
528         char *pRqHndlRet;\r
529         long dData0;\r
530         long dData1;\r
531         long dData2;\r
532         int  ServiceCode;\r
533         char npSend;\r
534         char npRecv;\r
535         char *pData1;\r
536         long cbData1;\r
537         char *pData2;\r
538         long cbData2;\r
539         long RQBRsvd1;\r
540         long RQBRsvd2;\r
541         long RQBRsvd3;\r
542         };\r
543 \r
544 static struct reqtype *pRQB;\r
545 \r
546 static char *fsysname = "FILESYSM";\r
547 \r
548 static unsigned long keycode;                   /* for testing */\r
549 \r
550 /*========================== BEGIN CODE ============================*/\r
551 \r
552 /************************************************\r
553  Called from read_PE, this gets the starting\r
554  cylinder, head and sector for the first boot\r
555  sector on a physical drive and stores it in the\r
556  phydrv array.  d is the drive, i is the index\r
557  into the partition table we read in.\r
558 *************************************************/\r
559 \r
560 static void GetBSInfo(U32 d, U32 i)\r
561 {\r
562  PDrvs[d].BS1Head = partab[i].HeadStart;\r
563  PDrvs[d].BS1Sect = partab[i].SecStart;\r
564  PDrvs[d].BS1Cyl  = partab[i].CylStart;\r
565 \r
566  if (!i) \r
567  {              /* primary partition info - use it for PDrv info */\r
568          PDrvs[d].nHeads = partab[i].HeadEnd;\r
569          PDrvs[d].nSecPerTrk = partab[i].nFirstSector & 0xff;\r
570  }\r
571 }\r
572 \r
573 /** InitFloppy *********************************\r
574  This gets status from the floppy drive (device ld)\r
575  and sets the physical & logical drive parameters\r
576  for the type. It is called when the file system\r
577  is first initialized and when there has been\r
578  an error on the floppy.\r
579 *************************************************/\r
580 \r
581 static U32 StatFloppy(U8 ld)\r
582 {\r
583 U32 erc, i;\r
584 \r
585 /* Set gets status for the floppy type from the FDD and\r
586    sets logical paramters for Ldrvs.\r
587 */\r
588 \r
589 Ldrv[0].DevNum= 10;             /* Device Numbers for floppies */\r
590 Ldrv[1].DevNum= 11;\r
591 \r
592 erc = DeviceStat(ld+10, &FDDevStat, 64, &i);\r
593 if (!erc) \r
594 {\r
595         PDrvs[ld].nHeads = FDDevStat.nHead;\r
596         PDrvs[ld].nSecPerTrk = FDDevStat.nSectors;\r
597         Ldrv[ld].LBA0 = 0;              /* Floppy Boot Sector - always 0 */\r
598         Ldrv[ld].LBAMax= FDDevStat.BlocksMax-1; /* Max lba for logical drive 0 */\r
599 \r
600         Ldrv[ld].nHeads = FDDevStat.nHead;\r
601         Ldrv[ld].nSecPerTrk = FDDevStat.nSectors;\r
602 \r
603     erc = 0;\r
604 }\r
605 else\r
606    Ldrv[ld].DevNum = 0xff;\r
607 \r
608  return erc;\r
609 }\r
610 \r
611 /************************************************\r
612  Reads the partition table entries from hard\r
613  drives and sets up some of the the logical\r
614  drive array variables for hard Disks.\r
615  It also saves first cylinder, head and sector\r
616  of the first partiton on each physical drive\r
617  so we can get more info for the LDrv arrays\r
618  from the boot sector of that partition.\r
619 *************************************************/\r
620 \r
621 static U32 read_PE(void)\r
622 {\r
623 U32 erc, ercD12, ercD13, i, j;\r
624 U8 fFound1, fFound2;\r
625 \r
626 fFound1 = 0;            /* Have we found first valid partition on drive */\r
627 fFound2 = 0;\r
628 \r
629 /* Set defaults for 4 physical drives. This info will be set\r
630    correctly when the partition table and boot sectors are read.\r
631 */\r
632 \r
633 for (i=2; i< nLDrvs; i++) \r
634 {       /* default to no logical hard drives */\r
635    Ldrv[i].DevNum = 0xff;\r
636 }\r
637 \r
638 i = 2;          /* first Logical Number for hard drives "C" */\r
639 \r
640 for (j=2; j<4; j++) \r
641 {       /* Array index Numbers for 2 physical hard Disks */\r
642 \r
643   erc = DeviceOp(j+10, 1, 0, 1, abRawSector); /* add 10 for Disk device nums */\r
644   if (j==2) ercD12 = erc;\r
645   else ercD13 = erc;\r
646 \r
647   if (!erc) \r
648   {\r
649     CopyData(&abRawSector[0x01fe], &partsig, 2);\r
650 \r
651         /* It MUST have a partition table or we can't use it! */\r
652 \r
653         if (partsig != 0xAA55) return ErcNoParTable;\r
654 \r
655     CopyData(&abRawSector[0x01be], &partab[0].fBootable, 64);\r
656 \r
657 /*\r
658          Dump(&partab[0].fBootable, 64);\r
659          ReadKbd(&keycode, 1);\r
660 */\r
661 \r
662     if (partab[0].nSectorsTotal > 0) \r
663     {\r
664      Ldrv[i].LBA0 =partab[0].nFirstSector;      /* lba for Start of LDrv (bootSect) */\r
665      Ldrv[i].LBAMax =partab[0].nSectorsTotal;   /* Max lba for logical drive */\r
666          if (partab[0].FATType > 3)\r
667         Ldrv[i].fFAT16 = 1;\r
668      Ldrv[i].DevNum = j+10;\r
669      if ((j==2) && (!fFound1)) \r
670      { GetBSInfo(2, 0); fFound1=1; }\r
671        if ((j==3) && (!fFound2))\r
672      { GetBSInfo(3, 0); fFound2=1; }\r
673        i++;                                     /* if valid partition go to next LDrv */\r
674      }\r
675 \r
676     if (partab[1].nSectorsTotal > 0) \r
677     {\r
678      Ldrv[i].LBA0   = partab[1].nFirstSector;\r
679      Ldrv[i].LBAMax = partab[1].nSectorsTotal;\r
680          if (partab[1].FATType > 3)\r
681         Ldrv[i].fFAT16 = 1;\r
682      Ldrv[i].DevNum = j+10;\r
683      if ((j==2) && (!fFound1)) { GetBSInfo(2, 1); fFound1=1; }\r
684      if ((j==3) && (!fFound2)) { GetBSInfo(3, 1); fFound2=1; }\r
685      i++;                                       /* if we had a valid partition go to next */\r
686     }\r
687 \r
688     if (partab[2].nSectorsTotal > 0) \r
689     {\r
690      Ldrv[i].LBA0   = partab[2].nFirstSector;\r
691      Ldrv[i].LBAMax = partab[2].nSectorsTotal;\r
692          if (partab[2].FATType > 3)\r
693         Ldrv[i].fFAT16 = 1;\r
694      Ldrv[i].DevNum = j+10;\r
695      if ((j==2) && (!fFound1)) { GetBSInfo(2, 2); fFound1=1; }\r
696      if ((j==3) && (!fFound2)) { GetBSInfo(3, 2); fFound2=1; }\r
697      i++;                                       /* if we had a valid partition go to next */\r
698     }\r
699 \r
700     if (partab[3].nSectorsTotal > 0) \r
701     {\r
702      Ldrv[i].LBA0   = partab[3].nFirstSector;\r
703      Ldrv[i].LBAMax = partab[3].nSectorsTotal;\r
704          if (partab[3].FATType > 3)\r
705         Ldrv[i].fFAT16 = 1;\r
706      Ldrv[i].DevNum = j+10;\r
707      if ((j==2) && (!fFound1)) \r
708      { \r
709         GetBSInfo(2, 3); \r
710         fFound1=1; \r
711      }\r
712      if ((j==3) && (!fFound2)) \r
713      { \r
714         GetBSInfo(3, 3); \r
715         fFound2=1; \r
716      }\r
717      i++;                                       /* if we had a valid partition go to next */\r
718      }\r
719     }\r
720   }\r
721 \r
722  if (ercD12) return ercD12;             /* there may be no Device 13 */\r
723  else return 0;\r
724 }\r
725 \r
726 /********************************************************************\r
727   Reads in the first boot sector from each physical drive to get\r
728   drive geometry info not available in partition table.  This includes\r
729   number of heads and sectors per track.  Then we call DeviceInit\r
730   for each physical device to set its internal drive geometry.\r
731   This must be done before we even try to read the other boot sectors\r
732   if the disk has mulitple partitions (otherwise it fails).\r
733 *********************************************************************/\r
734 \r
735 static U32 SetDriveGeometry(U32 d)              /* d is the device number (12 or 13) */\r
736 {\r
737 U32 erc, i;\r
738 \r
739   if (d==12) \r
740   {\r
741         erc =  DeviceStat(12, &HDDevStat, 64, &i);\r
742         if (!erc) \r
743         {\r
744           HDDevStat.nHead = PDrvs[2].nHeads;\r
745           HDDevStat.nSectors = PDrvs[2].nSecPerTrk;\r
746       erc = DeviceInit(12, &HDDevStat, 64); /* Set up drive geometry */\r
747     }\r
748   }\r
749 \r
750   if (d==13) \r
751   {\r
752         erc =  DeviceStat(13, &HDDevStat, 64, &i);\r
753         if (!erc) \r
754         {\r
755           HDDevStat.nHead = PDrvs[3].nHeads;\r
756           HDDevStat.nSectors = PDrvs[3].nSecPerTrk;\r
757       erc = DeviceInit(13, &HDDevStat, 64); /* Set up drive geometry */\r
758     }\r
759   }\r
760 \r
761 return erc;\r
762 }\r
763 \r
764 /********************************************************************\r
765   Read boot sector from logical drive (i) and sets up logical and\r
766   physical drive array variables for the FAT file system found on\r
767   the logical drive (described in the boot sector).\r
768 *********************************************************************/\r
769 \r
770 static U32 read_BS(U32 i)\r
771 {\r
772 U32 erc, j;\r
773 \r
774 if (Ldrv[i].DevNum != 0xff) \r
775 {\r
776 \r
777     j = Ldrv[i].DevNum;                 /* j is MMURTL Device number */\r
778 \r
779         erc = DeviceOp(j, 1, Ldrv[i].LBA0, 1, abRawSector);\r
780 \r
781         if ((erc==ErcNewMedia) && (i<2)) \r
782         {\r
783                 erc = DeviceOp(j, 1, Ldrv[i].LBA0, 1, abRawSector);\r
784         }\r
785 \r
786         CopyData(abRawSector, &fsb.Jmp, 62);\r
787 \r
788     if (erc==0) \r
789     {\r
790        Ldrv[i].LBARoot     = fsb.ResSectors + Ldrv[i].LBA0 +\r
791                             (fsb.FATs * fsb.SecPerFAT);\r
792        Ldrv[i].nRootDirEnt = fsb.RootDirEnts;   /* n Root dir entries */\r
793        Ldrv[i].SecPerClstr = fsb.SecPerClstr;\r
794        Ldrv[i].nHeads      = fsb.Heads;\r
795        Ldrv[i].nSecPerTrk  = fsb.SecPerTrack;\r
796        Ldrv[i].sFAT        = fsb.SecPerFAT;             /* nSectors in a FAT */\r
797        Ldrv[i].nFATS       = fsb.FATs;                  /* number of FATs */\r
798        Ldrv[i].LBAFAT      = Ldrv[i].LBA0 + fsb.ResSectors;\r
799        Ldrv[i].LBAData     = Ldrv[i].LBARoot + (fsb.RootDirEnts / 16);\r
800            if (fsb.FileSysType[4] == '2')\r
801          Ldrv[i].fFAT16 = 0;\r
802 \r
803     } /* if erc */\r
804 } /* if valid logical device */\r
805 return 0;\r
806 }\r
807 \r
808 /*******************************************************\r
809   This gets the CMOS date & time and converts it into the\r
810   format for the DOS FAT file system. This is two words\r
811   with bits representing Year/Mo/day & Hr/Min/SecDIV2.\r
812 ********************************************************/\r
813 static void GetFATTime(U16 *pTimeRet, U16 *pDateRet)\r
814 {\r
815 U32 date, time;\r
816 U16 DDate, DTime, w;\r
817 \r
818         GetCMOSDate(&date);\r
819         GetCMOSTime(&time);\r
820         /* Do the date */\r
821         DDate = (((date >> 12) & 0x0f) * 10) + ((date >> 8) & 0x0f); /* day */\r
822         w = (((date >> 20) & 0x0f) * 10) + ((date>>16) & 0x0f) + 2;      /* month */\r
823         DDate |= (w << 4);\r
824         w = (((date >> 28) & 0x0f) * 10) + ((date >> 24)  & 0x0f);       /* year */\r
825         DDate |= (w + 1900 - 1980) << 9;\r
826         /* Do the time */\r
827         DTime = (((((time >> 4) & 0x0f) * 10) + (time & 0x0f))/2);      /* secs/2 */\r
828         w = (((time >> 12) & 0x0f) * 10) + ((time >> 8) & 0x0f);\r
829         DTime |= (w << 5);                                                                                      /* mins */\r
830         w = (((time >> 20) & 0x0f) * 10) + ((time >> 16) & 0x0f);       /* hours */\r
831         DTime |= (w << 11);\r
832         *pTimeRet = DTime;\r
833         *pDateRet = DDate;\r
834 }\r
835 \r
836 \r
837 /*******************************************************\r
838   This updates a directory entry by reading in the\r
839   sector it came from and placing the modifed entry\r
840   into it then writing it back to disk.  The date is\r
841   also updated at this time.\r
842 ********************************************************/\r
843 static U32 UpdateDirEnt(U32 iFCB)\r
844 {\r
845 U32 erc, i, j;\r
846 U8 Drive;\r
847         Drive = paFCB[iFCB]->Ldrv;              /* What logical drive are we on? */\r
848         i = paFCB[iFCB]->LBADirSect;    /* Sector on disk */\r
849         j = paFCB[iFCB]->oSectDirEnt;   /* offset in sector */\r
850 \r
851         /* update time in dir entry */\r
852         GetFATTime(&paFCB[iFCB].Time, &paFCB[iFCB].Date);\r
853 \r
854         /* Read sector into a buffer */\r
855         erc = DeviceOp(Ldrv[Drive].DevNum, 1, i, 1, abDirSectBuf);\r
856 \r
857         if (!erc) \r
858         {\r
859             CopyData(&paFCB[iFCB], &abDirSectBuf[j], 32);\r
860                 erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, 1, abDirSectBuf);\r
861         }\r
862   return erc;\r
863 }\r
864 \r
865 \r
866 /*******************************************************\r
867   Checks the validity of the a file handle and also\r
868   returns the index to the FCB if the handle is OK.\r
869   The function return OK (0) if handle is good, else\r
870   a proper error code is returned.\r
871 ********************************************************/\r
872 static U32 ValidateHandle(U32 dHandle, U32 *iFCBRet)\r
873 {\r
874   /* get some checks out of the way first */\r
875 \r
876   if (dHandle < 4) return ErcBadFileHandle;\r
877   if (dHandle >= nFUBs) return ErcBadFileHandle;\r
878   if (!paFUB[dHandle].Job) return ErcBadFileHandle;\r
879 \r
880   /* Looks like a valid handle */\r
881   *iFCBRet = paFUB[dHandle]->iFCB;\r
882   return 0;\r
883 }\r
884 \r
885 /*********************************************\r
886   Returns absolute disk address for the\r
887   cluster number you specify. This gives us\r
888   the LBA of the first sector of data that\r
889   the cluster number represents.\r
890   The sector number is returned from the fucntion.\r
891   Uses: Ldrv[CrntDrv].LBAData\r
892                 Ldrv[CrntDrv].SecPerClstr\r
893 **********************************************/\r
894 \r
895 static U32 ClsToLBA(U16 Clstr, U8 Drive)\r
896 {\r
897 U32 LBA;\r
898 \r
899  Clstr-=2;              /* Minus 2 cause 0 and 1 are reserved clusters */\r
900  LBA = Ldrv[Drive].SecPerClstr * Clstr;\r
901  LBA += Ldrv[Drive].LBAData;\r
902  return LBA;\r
903 }\r
904 \r
905 \r
906 /*******************************************************\r
907   This writes out the specified FAT sector back into\r
908   the FAT. It also checks to see if there is more\r
909   than one copy of the fat and updates the second copy\r
910   if it exists.\r
911 ********************************************************/\r
912 static U32 UpdateFAT(U32 iFAT)\r
913 {\r
914 U32 erc, i, k;\r
915 U8 Drive;\r
916 \r
917   erc = 0;\r
918   if (Fat[iFAT].fModLock & FATMOD)\r
919   {     /* Modified?? */\r
920 \r
921         Drive = Fat[iFAT].Drive;                /* What logical drive are we on? */\r
922     i = Fat[iFAT].LBASect;                              /* Where to write it back */\r
923 \r
924         if (!iFAT)\r
925         {                       /* This is the floppy buffer [0] */\r
926                 /* set up to write upto 3 sectors from the buffer */\r
927 \r
928                 if (i+2 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
929                         k = 3;\r
930                 else if (i+1 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
931                         k = 2;\r
932                 else\r
933                         k = 1;\r
934         }\r
935         else\r
936                 k=1;\r
937 \r
938         erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, k, Fat[iFAT].pBuf);\r
939         if (!erc)\r
940         {\r
941                 Fat[iFAT].fModLock &= ~FATMOD;  /* Not modified anymore */\r
942                 if (Ldrv[Drive].nFATS > 1)  \r
943                 {       /* 2 FATS! */\r
944                         /* if we have two FATS we must update the second fat\r
945                         also. This will be located directly aftrer the first\r
946                         FAT (by exactly LDrv.sFat sectors).\r
947                         */\r
948                         i+= Ldrv[Drive].sFAT;\r
949                         erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, k, Fat[iFAT].pBuf);\r
950                 }\r
951         }\r
952   }\r
953   return erc;\r
954 }\r
955 \r
956 /*******************************************************\r
957  Reads in the FAT sector that contains the Cluster we\r
958  specified into a FAT buffer if it isn't already in\r
959  one.  The index to the FAT buffer is returned.\r
960  Returns Error if not in FAT.\r
961  Uses: Ldrv[LDrive].LBAFAT\r
962            Ldrv[LDrive].fFAT16\r
963            Ldrv[LDrive].DevNum\r
964  Each sector of the FAT contains 256 cluster entries\r
965  for FAT16 types. To find it, we  Divide the cluster\r
966  number by the count of entries (256), and add this\r
967  to the beginning sector of the FAT.  It is SOOO\r
968  important (for speed) to have the FAT sectors in\r
969  memory, that we allocate the FAT buffers on a Least\r
970  Recently Used (LRU) basis for hard disk drives.\r
971 \r
972  It's more complicated for a FAT12 types (floppies)\r
973  because cluster entries span fat sectors (they have\r
974  an odd number of nibbles). For this reason, we have\r
975  one 3 sector fat buffer for fat12 devices (floppies).\r
976  We fill it with up to 3 sectors. This is because the\r
977  last entry may span the sectors and we must be able\r
978  to read it.  There are 1024 cluster entries in\r
979  a FAT12 3 sector buffer.\r
980 *******************************************************/\r
981 \r
982 static U32 FindFatSect(U8 Drive, U16 Clstr, U32 *piFatRecRet, U8 fLock)\r
983 {\r
984 U32 i, j, k;\r
985 U32 first, oSector, erc, LRU, iLRU, iFound, Tick;\r
986 U16 MaxClstr;\r
987 \r
988   if (Ldrv[Drive].fFAT16)\r
989         MaxClstr = 0xfff8;\r
990   else\r
991         MaxClstr = 0xff8;       /* FAT12 */\r
992 \r
993  if (Clstr >= MaxClstr)\r
994         return(ErcEOF);\r
995 \r
996  if (Clstr < 2)\r
997  {\r
998         return (ErcBadFATClstr);\r
999  }\r
1000 \r
1001  GetTimerTick(&Tick);\r
1002 \r
1003  erc = 0;               /* default to no error */\r
1004 \r
1005  /* Set oSector to offset of sector in FAT\r
1006     There are 256 cluster entries in 1 sector of a FAT16,\r
1007     and 1024 in a FAT12 (3 sectors)\r
1008  */\r
1009 \r
1010  if (Ldrv[Drive].fFAT16)\r
1011  {\r
1012         oSector = Clstr/256;\r
1013         first = Clstr-(Clstr%256);\r
1014 \r
1015         /* Set i to LBA of FAT sector we need by adding\r
1016            offset to beginning of FAT\r
1017         */\r
1018 \r
1019         i = oSector + Ldrv[Drive].LBAFAT;\r
1020 \r
1021         /* If FAT sector is out of range there's a BAD problem... */\r
1022 \r
1023         if (i >= Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
1024         {\r
1025                 return (ErcBadFATClstr);\r
1026         }\r
1027         else\r
1028         {   /* Else we get it for them */\r
1029 \r
1030                 /* Loop through the Fat bufs and see if its in one already. */\r
1031                 /* Save the index of the LRU in case it's not there. */\r
1032                 /* Set iFound to index of FatBuf (if found). */\r
1033                 /* Otherwise, Set up iLRU to indicate what the oldest buffer is  */\r
1034 \r
1035                 iFound = 0xffffffff;\r
1036                 LRU = 0xffffffff;       /* saves tick of oldest one so far */\r
1037                 iLRU = 1;                       /* default */\r
1038                 for (j=1; j<nFATBufs; j++)\r
1039                 {\r
1040                         if (Fat[j].LastUsed > 0)\r
1041                         {               /* Valid ? (ever been used) */\r
1042                                 if ((first == Fat[j].iClstrStart) &&\r
1043                                         (Drive == Fat[j].Drive))\r
1044                                         {\r
1045                                                 iFound = j;\r
1046                                                 if (fLock)\r
1047                                     Fat[j].fModLock |= FATLOCK;\r
1048                                                 break;          /* Already IN! */\r
1049                                         }\r
1050                         }\r
1051                         if (Fat[j].LastUsed < LRU)\r
1052                         {\r
1053                                 LRU = Fat[j].LastUsed;\r
1054                                 iLRU = j;\r
1055                         }\r
1056                 }\r
1057 \r
1058                 if (iFound != 0xffffffff)\r
1059                 {                                                               /* Its already in memory */\r
1060                 Fat[j].LastUsed = Tick;         /* update LRU */\r
1061                 }\r
1062                 else\r
1063                 {                                               /* else put into oldest buffer */\r
1064                         j = iLRU;\r
1065 \r
1066                         /* Check to see if Fat[iLRU] is valid and has been\r
1067                            modified. If it is, write it out before we read\r
1068                            the next one into this buffer. This done by\r
1069                            calling UpdateFAT(iFatRec).\r
1070                         */\r
1071                 if (Fat[j].fModLock & FATMOD)\r
1072                         erc = UpdateFAT(j);\r
1073 \r
1074                         if (!erc)\r
1075                         {\r
1076                                 erc = DeviceOp(Ldrv[Drive].DevNum, 1, i, 1, Fat[j].pBuf);\r
1077                                 Fat[j].Drive = Drive;                   /* Update Drive */\r
1078                     Fat[j].LastUsed = Tick;                     /* update LRU */\r
1079                         Fat[j].iClstrStart = first;             /* update first cluster num */\r
1080                         Fat[j].LBASect = i;                             /* LBA this FAt sect came from */\r
1081                         }\r
1082                 }\r
1083         }\r
1084  }\r
1085 \r
1086         /* This is for FAT12s */\r
1087 \r
1088  else\r
1089  {\r
1090         oSector = (Clstr/1024) * 3;      /* X3 cause we read 3 at a time */\r
1091         first = Clstr-(Clstr%1024);\r
1092 \r
1093         /* Set i to LBA of FAT sector we need by adding offset (oSector)\r
1094            to beginning of FAT */\r
1095 \r
1096         i = oSector + Ldrv[Drive].LBAFAT;\r
1097         j = 0;\r
1098 \r
1099         /* If FAT sector is out of range there's a BAD problem... */\r
1100 \r
1101         if (i >= Ldrv[Drive].sFAT)\r
1102                 return (ErcBadFATClstr);\r
1103         else\r
1104         {   /* Else we get it for them */\r
1105 \r
1106                 /* Check the single floppy fat buf and see if its already there. */\r
1107                 /* Set iFound to index of FatBuf (if found). */\r
1108 \r
1109                 iFound = 0xffffffff;\r
1110 \r
1111                 if (Fat[0].LastUsed > 0)\r
1112                 {               /* Valid ? (nonzero means it's been used) */\r
1113                         if ((first == Fat[0].iClstrStart) &&\r
1114                                 (Drive == Fat[0].Drive))\r
1115                         {\r
1116                                 iFound = 0;\r
1117                                 if (fLock)\r
1118                                         Fat[0].fModLock |= FATLOCK;\r
1119                         }\r
1120                 }\r
1121 \r
1122                 if (iFound == 0xffffffff)\r
1123                 {\r
1124                         /* It's not the one we want or isn't there.\r
1125                            Check to see if Fat[0] is valid and has been\r
1126                            modified. If it is, write it out before we read\r
1127                            the one we want into the buffer. This done by\r
1128                            calling UpdateFAT(iFatRec).\r
1129                         */\r
1130                         if (Fat[0].fModLock & FATMOD)\r
1131                                 erc = UpdateFAT(0);\r
1132 \r
1133                         /* set up to read upto 3 sectors into buffer */\r
1134 \r
1135                         if (i+2 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
1136                                 k = 3;\r
1137                         else if (i+1 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)\r
1138                                 k = 2;\r
1139                         else\r
1140                                 k = 1;\r
1141 \r
1142                         if (!erc)\r
1143                         {\r
1144                                 erc = DeviceOp(Ldrv[Drive].DevNum, 1, i, k, Fat[0].pBuf);\r
1145 \r
1146                                 Fat[0].Drive = Drive;                   /* Update Drive */\r
1147                         Fat[0].LastUsed = Tick;                 /* update LRU */\r
1148                         Fat[0].iClstrStart = first;             /* update first cluster num */\r
1149                         Fat[0].LBASect = i;             /* LBA this FAT sect came from */\r
1150                         }\r
1151                 }\r
1152         }\r
1153  }\r
1154 \r
1155 \r
1156  *piFatRecRet = j;  /* Buffer that holds the sector(s) */\r
1157 \r
1158  return (erc);          /* Disk error Bad news */\r
1159 }\r
1160 \r
1161 /*********************************************\r
1162   Returns the value found for this cluster\r
1163   entry in a fat sector buffer.  Values can be:\r
1164                               FAT16     FAT12\r
1165         Next entry in the chain (0002-FFF0 (002-FF0)\r
1166         Last entry in chain     (FFF8     )(FF8    )\r
1167         Available cluster       (0        )(0      )\r
1168         Bad Cluster             (FFF7     )(FF7    )\r
1169         (other vlaues are reserved).\r
1170 **********************************************/\r
1171 \r
1172 static U32 GetClstrValue(U16 Clstr, U8 Drive, U8 fLock,\r
1173                          U16 *pValRet, U32 *iFatBufRet)\r
1174 {\r
1175 U32 erc, oClstr, iFat;\r
1176 U16 ClstrVal, *pClstr;\r
1177 \r
1178         erc = FindFatSect(Drive, Clstr, &iFat, fLock);\r
1179 \r
1180         if (erc)\r
1181         {\r
1182                 *pValRet= 0;\r
1183                 return(erc);\r
1184         }\r
1185 \r
1186         pClstr = Fat[iFat].pBuf;\r
1187         oClstr = Clstr - Fat[iFat].iClstrStart;    /* offset into FatBuf */\r
1188 \r
1189         if (Ldrv[Drive].fFAT16)\r
1190         {       /* if drive is FAT16 type */\r
1191                 pClstr += oClstr * 2;                                   /* WORDS in */\r
1192                 ClstrVal = *pClstr;\r
1193         }\r
1194                 /* FAT12 entries are 1.5 bytes long (what a pain).\r
1195                    This means we get the offset and see whether it\r
1196                    is an odd or even byte, then take the proper nibble\r
1197                    by ANDing or shifting.\r
1198                 */\r
1199         else\r
1200         {                                               /* a FAT12... */\r
1201                 pClstr += oClstr + (oClstr/2);                  /* 1.5 bytes in */\r
1202                 ClstrVal = *pClstr;                                     /* We have 16 bits */\r
1203                 if (Clstr & 1)                                  /* Odd, must shift */\r
1204                         ClstrVal >>= 4;\r
1205                 ClstrVal &= 0xfff;\r
1206         }\r
1207         *pValRet= ClstrVal;\r
1208     *iFatBufRet = iFat;\r
1209 \r
1210         return(erc);\r
1211 }\r
1212 \r
1213 \r
1214 /*************************************************\r
1215   Sets the value in Clstr to the value in\r
1216   NextClstr which will be one of the following\r
1217   values:                     FAT16     FAT12\r
1218         Next entry in the chain (0002-FFEF (002-FEF)\r
1219         Last entry in chain     (FFFF     )(FFF    )\r
1220         Available cluster       (0        )(0      )\r
1221         Bad Cluster             (FFF7     )(FF7    )\r
1222         (other vlaues are reserved).\r
1223   This marks the associated fat buffer as modified.\r
1224   This is the ONLY call that modifies a FAT buffer!\r
1225 **************************************************/\r
1226 \r
1227 static U32 SetClstrValue(U16 Clstr, U16 NewClstrVal, U8 Drive, U32 *iFatBufRet)\r
1228 {\r
1229 U32 erc, oClstr, iFat;\r
1230 U16 ClstrVal, *pClstr, ClstrSave;\r
1231 \r
1232         erc = FindFatSect(Drive, Clstr, &iFat, 0);\r
1233         if (erc)\r
1234         {\r
1235                 *iFatBufRet = 0;\r
1236                 return(erc);\r
1237         }\r
1238 \r
1239         pClstr = Fat[iFat].pBuf;\r
1240         oClstr = Clstr - Fat[iFat].iClstrStart;    /* offset into FatBuf*/\r
1241         if (Ldrv[Drive].fFAT16) \r
1242         {       /* if drive is FAT16 type */\r
1243                 pClstr += oClstr * 2;                                   /* WORDS in */\r
1244                 *pClstr = NewClstrVal;\r
1245         }\r
1246                 /* FAT12 entries are 1.5 bytes long (remember??).\r
1247                    SAVE THE CORRECT NIBBLE OF THE ADJACENT CLUSTER!!\r
1248                 */\r
1249         else\r
1250         {                                               /* a FAT12... */\r
1251                 pClstr += oClstr + (oClstr/2);                  /* 1.5 bytes in */\r
1252                 ClstrSave = *pClstr;                                            /* We have 16 bits */\r
1253                 if (Clstr & 1) \r
1254                 {                                       /* Odd, must shift */\r
1255                         NewClstrVal <<= 4;\r
1256                         NewClstrVal &= 0xfff0;\r
1257                         ClstrVal = (ClstrSave & 0x0F) | NewClstrVal;\r
1258                 }\r
1259                 else \r
1260                 {\r
1261                         NewClstrVal &= 0x0fff;\r
1262                         ClstrVal = (ClstrSave & 0xf000) | NewClstrVal;\r
1263                 }\r
1264                 *pClstr = ClstrVal;\r
1265         }\r
1266     Fat[iFat].fModLock |= FATMOD;\r
1267     *iFatBufRet = iFat;\r
1268         return(erc);\r
1269 }\r
1270 \r
1271 \r
1272 /*************************************************\r
1273  Read the FAT and get the cluster number for the\r
1274  next cluster in the chain for the Clstr specifed.\r
1275  This returns 0 and an error if failed.\r
1276  0 is an illegal cluster number.\r
1277  Remember, the cluster you are on is actually the\r
1278  number of the next cluster in a linked list!\r
1279 **************************************************/\r
1280 \r
1281 static U32 NextFATClstr(U8 Drive, U16 Clstr, U16 *pNextClstrRet)\r
1282 {\r
1283 U32 erc, i;\r
1284 U16 NextClstr;\r
1285 \r
1286         erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);\r
1287 \r
1288         if (erc)\r
1289         {\r
1290                 *pNextClstrRet = 0;\r
1291                 return(erc);\r
1292         }\r
1293         *pNextClstrRet = NextClstr;\r
1294         return(0);\r
1295 }\r
1296 \r
1297 /*************************************************\r
1298  This allocates the next empty cluster on the disk\r
1299  to the tail of the clstr that is passed in.\r
1300  LastClstr is a valid cluster of a file or \r
1301  directory (and MUST be the last one).  \r
1302  We error out if it isn't!\r
1303  This returns 0 and an error if it fails.\r
1304  Remember, the cluster you are on is actually the\r
1305  number of the next cluster in a linked list!\r
1306  This looks through the current and succesive\r
1307  FAT sectors (if needed) to add to the file.\r
1308  A cluster is available to allocate if it is\r
1309  0.  This is strictly a first fit algorithm.\r
1310 **************************************************/\r
1311 \r
1312 static U32 ExtendClstrChain(U8 Drive, U16 LastClstr, U16 *pNextClstrRet)\r
1313 {\r
1314 U32 erc, i, j, k;\r
1315 U16 ClstrValue, MaxClstr, CrntClstr;\r
1316 U8 fFound;\r
1317 \r
1318         if (Ldrv[Drive].fFAT16)\r
1319                 MaxClstr = 0xfff8;\r
1320         else\r
1321                 MaxClstr = 0xff8;       /* FAT12 */\r
1322 \r
1323         /* i is index to Fat with last sector of current chain */\r
1324 \r
1325         erc = GetClstrValue(LastClstr, Drive, 1, &ClstrValue, &i);\r
1326         if (erc) \r
1327         {\r
1328                 *pNextClstrRet = 0;\r
1329                 return(erc);\r
1330         }\r
1331 \r
1332         if (ClstrValue < MaxClstr) \r
1333         {               /* no need to extend it */\r
1334         *pNextClstrRet = ClstrValue;\r
1335         Fat[i].fModLock &= ~FATLOCK;            /* unlock it */\r
1336                 return(0);\r
1337         }\r
1338 \r
1339         /* OK... now we have the Fat sector and the offset in the Fat\r
1340         buf of the last cluster allocated to this file.  Let's go\r
1341         further into the buffer and try to get an empty one.\r
1342         */\r
1343 \r
1344         CrntClstr = LastClstr;\r
1345         fFound = 0;\r
1346         while (!fFound) \r
1347         {\r
1348                 ++CrntClstr;            /* next cluster */\r
1349                 erc = GetClstrValue(CrntClstr, Drive, 0, &ClstrValue, &j);\r
1350                 if (erc) \r
1351                 {\r
1352                         *pNextClstrRet = 0;\r
1353                 Fat[i].fModLock &= ~FATLOCK;    /* unlock previous lastclstr */\r
1354                         return(erc);\r
1355                 }\r
1356                 if (!ClstrValue) \r
1357                 {\r
1358                         fFound = 1;     /* found an empty one */\r
1359                 }\r
1360         }\r
1361 \r
1362         if (fFound) \r
1363         {       /* CrntClstr is index to empty one */\r
1364 \r
1365                 /* Set the LastCluster to point to the new cluster found */\r
1366 \r
1367                 erc = SetClstrValue(LastClstr, CrntClstr, Drive, &k);\r
1368                 if (erc) \r
1369                 {\r
1370                         *pNextClstrRet = 0;\r
1371                 Fat[i].fModLock &= ~FATLOCK;    /* unlock previous lastclstr */\r
1372                         return(erc);\r
1373                 }\r
1374         Fat[k].fModLock &= ~FATLOCK;            /* unlock it */\r
1375 \r
1376                 /* Set the newcluster to "end Cluster" chain value */\r
1377 \r
1378                 erc = SetClstrValue(CrntClstr, 0xFFFF, Drive, &j);\r
1379         }\r
1380         *pNextClstrRet = CrntClstr;\r
1381         return(erc);\r
1382 }\r
1383 \r
1384 /*************************************************\r
1385  This truncates the file chain to the cluster\r
1386  specified (makes it the last cluster).\r
1387  This means we walk the rest of the chain setting\r
1388  all the entries to 0 (so they can be reallocated).\r
1389  This returns an error if failed.\r
1390 **************************************************/\r
1391 \r
1392 static U32 TruncClstrChain(U8 Drive, U16 Clstr)\r
1393 {\r
1394 U32 erc, i;\r
1395 U16 MaxClstr, NextClstr, CrntClstr;\r
1396 \r
1397         if (Ldrv[Drive].fFAT16)\r
1398                 MaxClstr = 0xfff8;\r
1399         else\r
1400                 MaxClstr = 0xff8;       /* FAT12 */\r
1401 \r
1402         /* i will be index to FatRec with last sector of current chain */\r
1403 \r
1404         erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);\r
1405         if (erc)\r
1406                 return(erc);\r
1407 \r
1408         if (NextClstr >= MaxClstr)\r
1409         {               /* no need to truncate it */\r
1410                 return(0);                                              /* It's already the end. */\r
1411         }\r
1412 \r
1413         /* OK... now we cut it off all the way down the chain.\r
1414         We start by placing MaxClstr in the last sector and\r
1415         then 0 in all entries to the end of the chain.\r
1416         */\r
1417 \r
1418         erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);\r
1419         if (erc)\r
1420                 return(erc);\r
1421         erc = SetClstrValue(Clstr, 0xFFFF, Drive, &i);  /* new end of chain */\r
1422         if (erc)\r
1423                 return(erc);\r
1424 \r
1425         while ((NextClstr) && (NextClstr < MaxClstr)) \r
1426         {\r
1427                 CrntClstr = NextClstr;\r
1428                 erc = GetClstrValue(CrntClstr, Drive, 0, &NextClstr, &i);\r
1429                 if (erc)\r
1430                         return(erc);\r
1431                 erc = SetClstrValue(CrntClstr, 0, Drive, &i);  /* Free it up */\r
1432                 if (erc)\r
1433                         return(erc);\r
1434         }\r
1435 \r
1436         /* DONE! */\r
1437         return(0);\r
1438 }\r
1439 \r
1440 /********************************************************\r
1441   This finds the absolute cluster you want from the\r
1442   LFA in a particular file. The file handle must already\r
1443   be validated!  It also returns the relative LFA of\r
1444   the beginning of this cluster.\r
1445 *********************************************************/\r
1446 \r
1447 static U32 GetAbsoluteClstr(U32 dHandle, U32 dLFA,\r
1448                                                         U16 *pClstrRet, U32 *prLFARet)\r
1449 {\r
1450 U32 erc, iFCB, spc, bpc, rLFA;\r
1451 U16 rClstrWant, rClstrNow, Clstr, MaxClstr;\r
1452 U8 Drive;\r
1453 \r
1454   iFCB = paFUB[dHandle]->iFCB;\r
1455   Drive = paFCB[iFCB]->Ldrv;            /* What logical drive are we on? */\r
1456   spc = Ldrv[Drive].SecPerClstr;        /* sectors per cluster */\r
1457   bpc = spc * 512;                                      /* bytes per cluster */\r
1458 \r
1459  if (Ldrv[Drive].fFAT16)\r
1460         MaxClstr = 0xfff8;\r
1461  else\r
1462         MaxClstr = 0xff8;       /* FAT12 */\r
1463 \r
1464 /*\r
1465   Calculate relative by dividing cluster size in bytes by dLFA.\r
1466   If zero, we want the 1st cluster which is listed in the FCB.\r
1467   If it is greater than zero, we have to "walk the FAT cluster\r
1468   chain" until we reach the one we want, then read it in.\r
1469 \r
1470   The FUB fields LFAClstr and Clstr store the file LFA of the last\r
1471   cluster in this file that was read or written.  This means if the\r
1472   LFA is higher than the last read or written, we don't waste the\r
1473   time reading the whole chain. We start from where we are.\r
1474 \r
1475   The major difference is we may not be reading the first sector\r
1476   in the cluster. We figure this out from the dLFA as compared to\r
1477   LFAClstr.\r
1478 \r
1479 */\r
1480 \r
1481   rClstrWant = dLFA / bpc;                                      /* Relative clstr they want */\r
1482   rClstrNow = paFUB[dHandle]->LFAClstr / bpc;   /* Rel 'Clstr' in FUB */\r
1483 \r
1484   if (rClstrWant < rClstrNow)\r
1485   {                     /* Is it earlier in the file? */\r
1486         Clstr = paFCB[iFCB]->StartClstr;                /* Yes, start at the beginning */\r
1487         rClstrNow = 0;\r
1488         rLFA = 0;\r
1489   }\r
1490   else\r
1491   {\r
1492         Clstr = paFUB[dHandle]->Clstr;                  /* No, start at current cluster */\r
1493         rLFA = paFUB[dHandle]->LFAClstr;                /* LFA of this cluster */\r
1494   }\r
1495 \r
1496   /* We need to run the cluster chain if rClstrNow < ClstrWant */\r
1497 \r
1498   while ((rClstrNow < rClstrWant) &&    /* haven't reach it yet */\r
1499          (Clstr < MaxClstr) &&                  /* Not last cluster */\r
1500          (Clstr))\r
1501  {                                      /* A valid cluster */\r
1502           erc = NextFATClstr(Drive, Clstr, &Clstr);\r
1503           if (erc)\r
1504                 return(erc);\r
1505           ++rClstrNow;\r
1506           rLFA += bpc;\r
1507   }\r
1508 \r
1509   if (rClstrNow != rClstrWant)                  /* Cluster chain appears broken... */\r
1510     return ErcBrokenFile;\r
1511 \r
1512   *pClstrRet = Clstr;\r
1513   *prLFARet = rLFA;\r
1514   return(0);\r
1515 }\r
1516 \r
1517 \r
1518 /*******************************************************\r
1519   SetFileSize sets the FileSize entry in the FCB for\r
1520   the handle specified.  This means we will allocate\r
1521   or deallocate clusters as necessary to satisfy this\r
1522   request. The file MUST be open in MODE MODIFY.\r
1523   This must be done before a file can be written\r
1524   to beyond current FileSize (Block and Stream types).\r
1525 ********************************************************/\r
1526 \r
1527 static U32 SetFileSizeM(U32 dHandle, U32 dSize)\r
1528 {\r
1529 U32 erc, i, iFCB, rLFA, lfaEOF;\r
1530 U32 CrntSize, nCrntClstrs, spc, bpc, nClstrsWant;\r
1531 U16 Clstr;\r
1532 U8 Drive;\r
1533 \r
1534   erc = ValidateHandle(dHandle, &iFCB);\r
1535   if (erc)\r
1536         return erc;\r
1537   if (!paFCB[iFCB]->Mode)\r
1538         return ErcReadOnly;\r
1539 \r
1540   Drive = paFCB[iFCB]->Ldrv;            /* What logical drive are we on? */\r
1541   spc = Ldrv[Drive].SecPerClstr;        /* sectors per cluster */\r
1542   bpc = spc * 512;                                      /* bytes per cluster */\r
1543 \r
1544   /* Looks like it's valid to change the size */\r
1545 \r
1546   CrntSize = paFCB[iFCB]->FileSize;\r
1547   if (CrntSize)\r
1548           lfaEOF = CrntSize - 1;\r
1549   else\r
1550           lfaEOF = 0;\r
1551 \r
1552   if (CrntSize == dSize)                /* No need to do anything! */\r
1553         return(0);\r
1554 \r
1555   nCrntClstrs = CrntSize/bpc;   /* nClusters currently  */\r
1556   if (CrntSize%bpc)\r
1557         nCrntClstrs++;\r
1558 \r
1559   if (!CrntSize)\r
1560         nCrntClstrs = 1;                        /* ZERO length files have 1 Clstr! */\r
1561 \r
1562   nClstrsWant = dSize/bpc;      /* nClusters they we need  */\r
1563   if (dSize%bpc)\r
1564         nClstrsWant++;\r
1565 \r
1566   if (!dSize)\r
1567         nClstrsWant = 1;                        /* ZERO length files have 1 Clstr! */\r
1568 \r
1569 \r
1570   if (nClstrsWant == nCrntClstrs)\r
1571         erc = 0;\r
1572 \r
1573   else if (nClstrsWant > nCrntClstrs)\r
1574   {     /* Need to extend allocation */\r
1575 \r
1576         /* get the last cluster in the file */\r
1577         erc = GetAbsoluteClstr(dHandle, lfaEOF, &Clstr, &rLFA);\r
1578         i = nCrntClstrs;\r
1579         while ((!erc) && (i < nClstrsWant))\r
1580         {\r
1581                         erc = ExtendClstrChain(Drive, Clstr, &Clstr);\r
1582                         i++;\r
1583         }\r
1584   }\r
1585   else if (nClstrsWant < nCrntClstrs)\r
1586   { /* Need to truncate it */\r
1587 \r
1588         /* Get to cluster where it should be truncated. Set lfaEOF\r
1589         to NEW lfaEOF to find where last valid cluster should be. */\r
1590 \r
1591         if (dSize)\r
1592                 lfaEOF = dSize - 1;\r
1593         else\r
1594           lfaEOF = 0;\r
1595 \r
1596         erc = GetAbsoluteClstr(dHandle, lfaEOF, &Clstr, &rLFA);\r
1597         if (!erc)\r
1598                 erc = TruncClstrChain(Drive, Clstr);\r
1599 \r
1600         /* Now we must ensure that the cluster helper is NOT\r
1601         beyond EOF!\r
1602         */\r
1603     if (paFUB->LFAClstr >= dSize)\r
1604     {\r
1605         paFUB->LFAClstr = 0;\r
1606         paFUB[dHandle]->Clstr = paFCB[iFCB]->StartClstr;\r
1607         }\r
1608 \r
1609   }\r
1610   if (!erc)\r
1611   {\r
1612         paFCB[iFCB]->FileSize = dSize;\r
1613         paFCB[iFCB]->fMod = 1;\r
1614   }\r
1615 \r
1616   return erc;\r
1617 }\r
1618 \r
1619 \r
1620 \r
1621 /*******************************************************************\r
1622  This searches a directory beginning at Clstr for pName and returns\r
1623  a pointer to the 32 byte dir entry which is in a temporary buffer.\r
1624  If not found, returns NIL. When searching directories, if the\r
1625  filename begins with a NULL the search need go no futher!\r
1626 ********************************************************************/\r
1627 \r
1628 static U32 GetDirEnt(U8  *pName,\r
1629               U8  Drive,\r
1630               U16 Clstr,\r
1631               U32 *pLBARet,\r
1632               U32 *poEntRet,\r
1633               U8  **pEntRet)\r
1634 {\r
1635 unsigned long sector, i, j, k, erc;\r
1636 U8 fFound, fEnd, *pEnt, *pStart;\r
1637 U16 MaxClstr;\r
1638 \r
1639  j = Ldrv[Drive].SecPerClstr;           /* How many sectors per cluster */\r
1640  sector = ClsToLBA(Clstr, Drive);       /* absolute sector of first dir sector */\r
1641  if (Ldrv[Drive].fFAT16)\r
1642         MaxClstr = 0xfff8;\r
1643  else\r
1644         MaxClstr = 0xff8;       /* FAT12 */\r
1645  i = 0;\r
1646  fEnd=0;\r
1647  fFound=0;\r
1648 \r
1649   fFound= 0;\r
1650   while ((!fFound) && (!fEnd)) \r
1651   {             /* while there are valid entries */\r
1652         if (i==j) \r
1653         {               /* reached last dir sector of this cluster */\r
1654                 erc = NextFATClstr(Drive, Clstr, &Clstr);\r
1655                 if (!erc) \r
1656                 {\r
1657                         if (Clstr >= MaxClstr)                          /* last sector */\r
1658                                 return(ErcNoSuchFile);                  /* not found */\r
1659                         sector = ClsToLBA(Clstr, Drive);        /* LBA of next dir sector */\r
1660                         i=0;\r
1661                 }\r
1662                 else \r
1663                 {\r
1664                         *pEntRet = 0;\r
1665                         return(erc);\r
1666                 }\r
1667         }\r
1668 \r
1669         erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector++, 1, abDirSectBuf);\r
1670         if (erc)\r
1671                 return(erc);\r
1672 \r
1673         ++i;            /* next sector in cluster */\r
1674 \r
1675     pEnt = &abDirSectBuf[0];\r
1676     pStart = pEnt;\r
1677 \r
1678         for (k=0; k<16; k++) \r
1679         {               /* 16 entries per sector */\r
1680                 if (*pEnt==0) \r
1681                 {                       /* 0 in a DirEnt stops search */\r
1682                         fEnd=1;\r
1683                         break;\r
1684                 }\r
1685 \r
1686                 if (CompareNCS(pEnt, pName, 11) == -1)\r
1687                 {\r
1688                         fFound=1;\r
1689             *pLBARet = sector-1;                /* tell em what LBA of DirEnt */\r
1690             *poEntRet = pEnt-pStart;    /* Tell em offset in LBA */\r
1691                         break;\r
1692                 }\r
1693                 pEnt+=32;       /* 32 byte per entry */\r
1694                 }\r
1695         }\r
1696   if (fFound) \r
1697   {\r
1698         *pEntRet = pEnt;\r
1699         return(0);\r
1700   }\r
1701   else return (ErcNoSuchFile);\r
1702 }\r
1703 \r
1704 \r
1705 /*****************************************************\r
1706  This searches the ROOT directory for pName\r
1707  and returns a pointer to the 32 byte entry which\r
1708  is in a temporary buffer. If not found, returns NIL;\r
1709  When searching directories, if the filename begins\r
1710  with a NULL the search need go no futher!\r
1711 *****************************************************/\r
1712 \r
1713 static U32 GetRootEnt(U8  *pName,\r
1714                U8  Drive,\r
1715                U32 *pLBARet,\r
1716                U32 *poEntRet,\r
1717                U8  **pEntRet)\r
1718 {\r
1719 unsigned long i, j, k, erc;\r
1720 U8 fFound, fEnd, *pEnt, *pStart;\r
1721 \r
1722  i = Ldrv[Drive].LBARoot;\r
1723  j = Ldrv[Drive].nRootDirEnt;\r
1724 \r
1725  fFound = 0;\r
1726  fEnd = 0;\r
1727  while ((j) && (!fFound) && (!fEnd)) \r
1728  {      /* while there are valid entries */\r
1729 \r
1730         erc = DeviceOp(Ldrv[Drive].DevNum, 1, i++, 1, abRawSector);\r
1731 \r
1732         if (erc)\r
1733                 return(erc);\r
1734 \r
1735     pEnt = abRawSector;\r
1736     pStart = pEnt;\r
1737         for (k=0; k<16; k++) \r
1738         {\r
1739                 if (*pEnt==0) \r
1740                 {                       /* 0 in a DirEnt stops search */\r
1741                         fEnd=1;\r
1742                         break;\r
1743                 }\r
1744                 if (CompareNCS(pEnt, pName, 11) == -1) \r
1745                 {\r
1746                         fFound=1;\r
1747             *pLBARet = i-1;                              /* tell em what LBA of DirEnt */\r
1748             *poEntRet = pEnt-pStart;     /* Tell em offset in LBA */\r
1749                         break;\r
1750                 }\r
1751                 --j;            /* one less dir ent */\r
1752                 pEnt+=32;       /* 32 byte per entry */\r
1753         }\r
1754  }\r
1755  if (fFound) \r
1756  {\r
1757         *pEntRet = pEnt;\r
1758         return(0);\r
1759  }\r
1760  else\r
1761         return (ErcNoSuchFile);\r
1762 }\r
1763 \r
1764 /********************************************\r
1765 RAB -  This builds a full file specification from\r
1766   pName and places it in pDest based on the\r
1767   path from iJob. cbDestRet is set to size.\r
1768   If the name starts with "DRIVE:"\r
1769   then the path from the JCB is NOT used. Otherwise\r
1770   the pName is cancatenated to the job's path.\r
1771   If it begins with "\" only DRIVE: (first two\r
1772   chars of path) are used.\r
1773 *********************************************/\r
1774 \r
1775 static void BuildSpec(char *pName,\r
1776                            long cbName,\r
1777                            char *pDest,\r
1778                            long *cbDestRet,\r
1779                            long iJob)\r
1780 {\r
1781 long i;\r
1782 char pathtmp[70];\r
1783 \r
1784  if ((cbName) && (pName) && (pName[1] == ':'))\r
1785  {              /* Do NOT use path */\r
1786          CopyData(pName, pDest, cbName);\r
1787          i = cbName;\r
1788  }\r
1789     /* use only drive and semicolon */\r
1790  else if ((cbName) && (pName) && (pName[0] == 0x5C)) /* begins with backslash */\r
1791  {\r
1792         GetPath(iJob, pathtmp, &i);\r
1793         pDest[0] = pathtmp[0];\r
1794         pDest[1] = pathtmp[1];\r
1795         i = 2;\r
1796         CopyData(pName, &pDest[2], cbName);\r
1797         i += cbName;\r
1798  }\r
1799  else\r
1800  {                                                              /* Use whole path as prefix */\r
1801         i = 0;\r
1802         GetPath(iJob, pDest, &i);\r
1803         if ((cbName) && (pName))\r
1804         {\r
1805                 if ((pName) && (cbName))\r
1806                 {\r
1807                         CopyData(pName, &pDest[i], cbName);\r
1808                         i += cbName;\r
1809                 }\r
1810         }\r
1811  }\r
1812  *cbDestRet = i;\r
1813 }\r
1814 \r
1815 \r
1816 \r
1817 /*****************************************************\r
1818  The parses out the path name into directories, filename,\r
1819  and extension  (Example):\r
1820  C:\TEMP\TEST.TXT  (with TEMP being a dir in the root).\r
1821  This also uses the current path for the job to build\r
1822  the filename.\r
1823  SpecDepth is set to the level of the last valid\r
1824  11 character string (Specdepth is 0-6).\r
1825 *****************************************************/\r
1826 \r
1827 static U32 ParseName(U8 *pName, U32 cbName, U32 iJob)\r
1828 {\r
1829 unsigned long i, j, k, erc;\r
1830 U8 c, *pPart;\r
1831 char Spec[70];\r
1832 U32 cbSpec;\r
1833 \r
1834  erc = 0;\r
1835  FDrive = 0;\r
1836 \r
1837  FillData(FileSpec, (7*11), ' ');               /* Fill parse table with spaces */\r
1838 \r
1839  if ((cbName) && (*pName == ' '))\r
1840         erc = ErcBadFileSpec;\r
1841 \r
1842  BuildSpec(pName, cbName, Spec, &cbSpec, iJob);\r
1843 \r
1844  j = 0;         /* index into crnt part of spec */\r
1845  k = 0;         /* index into crnt tree level   */\r
1846  pPart = Spec;\r
1847  for (i=0; i < cbSpec; i++)\r
1848  {\r
1849         switch (c = *pPart++)\r
1850         {\r
1851                 case 0x5c  :    /* '\' separates dir or fname */\r
1852                         if (j>0)\r
1853                         {  /* if it's not the first one */\r
1854                                 ++k;\r
1855                                 j=0;\r
1856                         }\r
1857                         break;\r
1858                 case ':' :\r
1859                         if ((j==1) && (k==0) && (FDrive==0))\r
1860                         {\r
1861                                 FDrive = FileSpec[0][0] & 0xdf;  /* Make drive Upper*/\r
1862                 FileSpec[0][0] = ' ';\r
1863                                 j=0;                    /* back to beginning of part */\r
1864                                 k=0;\r
1865                         }\r
1866                         else erc = ErcBadFileSpec;\r
1867                         break;\r
1868                 case '.'  :                     /* . can only appear once in dir or fname */\r
1869                         if (j>8) erc = ErcBadFileSpec;\r
1870                         else j=8;                                               /* move to extension */\r
1871                         break;\r
1872                 case '>'  :                                                     /* not allowed in spec */\r
1873                 case '<'  :\r
1874                 case ','  :\r
1875                 case '+'  :\r
1876                 case '|'  :\r
1877                 case ']'  :\r
1878                 case '['  :\r
1879                 case '+'  :\r
1880                 case '='  :\r
1881                 case '@'  :\r
1882                 case '*'  :\r
1883                 case '?'  :\r
1884                          erc = ErcBadFileSpec;\r
1885                         break;\r
1886                 default   :                                                     /* make chars upper */\r
1887                         if (j>10)\r
1888                                 erc = ErcBadFileSpec;\r
1889                         else\r
1890                         {\r
1891                                 if (((c >= 'A') && (c <= 'Z')) ||\r
1892                                     ((c >= 'a') && (c <= 'z')))\r
1893                                            c &= 0xdf;\r
1894                     FileSpec[k][j] = c;\r
1895                     ++j;\r
1896                 }\r
1897                         break;\r
1898                 }\r
1899 \r
1900         if (erc) break;         /* bad news. Exit for loop */\r
1901         }\r
1902 SpecDepth = k;\r
1903 return erc;\r
1904 }\r
1905 \r
1906 \r
1907 /*** Get Directory Sector *******************************\r
1908  Gets a 512 byte directory sector in sequence number from\r
1909  the directory path given. SectNum = 0 to nMax for Dir.\r
1910  This also returns the LBA for sector itself for\r
1911  internal users of this call.\r
1912 ********************************************************/\r
1913 \r
1914 static U32 GetDirSectorM(char *pPath,\r
1915                                  long cbPath,\r
1916                                  char *pSectRet,\r
1917                                  long cbRetMax,\r
1918                                  long SectNum,\r
1919                                  long *LBARet,\r
1920                                  U16  *ClstrRet,\r
1921                                  long iJob)\r
1922 {\r
1923 U32 sector, i, j, k, erc, spc, level, iSect;\r
1924 U16 MaxClstr, Clstr, rClstr;\r
1925 U8  fFound, *pEnt, Drive;\r
1926 \r
1927   if (cbRetMax > 512)   /* WHOA Bub, 1 Sector at a time! */\r
1928         cbRetMax = 512;\r
1929 \r
1930   erc = ParseName(pPath, cbPath, iJob);\r
1931 \r
1932         /* The entire path has now been parsed out into an array of\r
1933            arrays, each 11 bytes long that contain each directory name\r
1934            in the path for the sector they want.\r
1935            The first is always the root (entry 0).\r
1936            The drive will be a letter in FDrive.\r
1937         */\r
1938 \r
1939   if ((FDrive > 0x40) && (FDrive < 0x52))               /* A to J */\r
1940         Drive = FDrive - 0x41;                                          /* Make it 0-9 */\r
1941   else\r
1942         return(ErcNoSuchDrive);\r
1943 \r
1944   if (Drive < 2)\r
1945   {\r
1946         StatFloppy(Drive);\r
1947         erc= read_BS(Drive);\r
1948   }\r
1949 \r
1950   if (Ldrv[Drive].DevNum == 0xff)\r
1951         return(ErcNoSuchDrive);\r
1952 \r
1953   i = Ldrv[Drive].LBARoot;\r
1954   j = Ldrv[Drive].nRootDirEnt;\r
1955 \r
1956   if (FileSpec[0][0] == ' ')\r
1957   {             /* They want sector in root */\r
1958         if (SectNum > j/32)                             /* Beyond Root entries! */\r
1959                 return(ErcNoMatch);\r
1960 \r
1961         /* Else we can give them the sector NOW */\r
1962         erc = DeviceOp(Ldrv[Drive].DevNum, 1, i+SectNum, 1, abRawSector);\r
1963         if (!erc) \r
1964         {\r
1965                 *LBARet = i+SectNum;\r
1966                 CopyData(abRawSector, pSectRet, cbRetMax);\r
1967         }\r
1968         return(erc);\r
1969   }\r
1970 \r
1971         /* We have to run the root for a dir name... */\r
1972 \r
1973   fFound = 0;\r
1974   while ((j) && (!fFound))\r
1975   {     /* while there are valid entries */\r
1976         erc = DeviceOp(Ldrv[Drive].DevNum, 1, i++, 1, abRawSector);\r
1977         if (erc)\r
1978                 return(erc);\r
1979     pEnt = abRawSector;         /* Point to first entry */\r
1980         for (k=0; k<16; k++) \r
1981         {\r
1982                 if (CompareNCS(pEnt, FileSpec[0], 11) == -1)\r
1983                 {\r
1984                         fFound=1;\r
1985                         break;\r
1986                 }\r
1987                 --j;            /* one less dir ent */\r
1988                 pEnt+=32;       /* 32 byte per entry */\r
1989         }\r
1990  }\r
1991  if (!fFound)\r
1992         return (ErcNoMatch);\r
1993 \r
1994   pDirEnt = pEnt;               /* Entry we just found in root was dir */\r
1995 \r
1996   if (!(pDirEnt->Attr & DIRECTORY))\r
1997   {\r
1998         return(ErcNoSuchDir);\r
1999   }\r
2000 \r
2001   if (Ldrv[Drive].fFAT16)\r
2002         MaxClstr = 0xfff8;\r
2003   else\r
2004         MaxClstr = 0xff8;       /* FAT12 */\r
2005 \r
2006   spc = Ldrv[Drive].SecPerClstr;        /* How many sectors per cluster */\r
2007   Clstr = pDirEnt->StartClstr;\r
2008 \r
2009   level = 1;    /* start at this directory+1, compare to FileSpec */\r
2010 \r
2011   while (!erc) \r
2012   {     /* looking for Dir */\r
2013 \r
2014         if (FileSpec[level][0] == ' ')\r
2015         { /* They want sector in this dir */\r
2016 \r
2017                 if (!(pDirEnt->Attr & DIRECTORY))  \r
2018                 {\r
2019                         return(ErcNoSuchDir);\r
2020                 }\r
2021                 rClstr = SectNum /spc;  /* calc relative cluster from start clstr */\r
2022                 iSect  = SectNum % spc;  /* Add this to cluster start for sector */\r
2023                 sector = ClsToLBA(Clstr, Drive);        /* sector of first dir sector */\r
2024                 while ((rClstr--) && (!erc))\r
2025                         erc = NextFATClstr(Drive, Clstr, &Clstr);\r
2026                 if (erc)\r
2027                         return(erc);\r
2028                 sector = ClsToLBA(Clstr, Drive);  /* LBA of this clstr */\r
2029                 sector += iSect;\r
2030                 erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector, 1, abRawSector);\r
2031                 if (!erc) \r
2032                 {\r
2033                         CopyData(abRawSector, pSectRet, cbRetMax);\r
2034                         *LBARet = sector;\r
2035                         *ClstrRet = Clstr;\r
2036                 }\r
2037                 return(erc);\r
2038         }\r
2039         else \r
2040         {  /* Else we must find this sub dir name */\r
2041 \r
2042                 sector = ClsToLBA(Clstr, Drive);        /* sector of first dir sector */\r
2043                 fFound=0;\r
2044                 i = 0;\r
2045 \r
2046                 while (!fFound)\r
2047                 {               /* while there are valid entries */\r
2048 \r
2049                         if (i==spc) \r
2050                         {               /* reached last dir sector of this cluster */\r
2051                                 erc = NextFATClstr(Drive, Clstr, &Clstr);\r
2052                                 if (!erc) \r
2053                                 {\r
2054                                         if (Clstr >= MaxClstr)                   /* last sector */\r
2055                                                 return(ErcNoSuchFile);           /* not found */\r
2056                                         sector = ClsToLBA(Clstr, Drive); /* LBA of next sector */\r
2057                                         i=0;\r
2058                                 }\r
2059                                 else\r
2060                                         return(erc);\r
2061                         }\r
2062 \r
2063                         erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector++, 1, abRawSector);\r
2064                         if (erc)\r
2065                                 return(erc);\r
2066                         i++;    /* Next sector in this cluster */\r
2067 \r
2068                     pEnt = &abRawSector[0];\r
2069                         for (k=0; k<16; k++)\r
2070                         {               /* 16 entries per sector */\r
2071                                 if (CompareNCS(pEnt, FileSpec[level], 11) == -1)\r
2072                                 {\r
2073                                         fFound=1;\r
2074                                         break;\r
2075                                 }\r
2076                                 pEnt+=32;       /* 32 byte per entry */\r
2077                         }\r
2078                 }\r
2079                 pDirEnt = pEnt;                           /* Entry we just found */\r
2080             Clstr = pDirEnt->StartClstr;  /* Clstr @ start of dir entry */\r
2081         }\r
2082         ++level;                /* next level of parsed filespec */\r
2083   }\r
2084 return (erc);\r
2085 }\r
2086 \r
2087 /*******************************************************\r
2088  This is the BLOCK read for the MMURTL DOS file system.\r
2089  It reads whole sectors and returns them to pBytesRet.\r
2090  There are NO internal filesystem buffers for this call.\r
2091  Data is returned directly to your buffer from the Disk.\r
2092  This is the fastest method for reading a file.\r
2093  This is also used internally to fill a stream buffer.\r
2094 ********************************************************/\r
2095 \r
2096 static U32 ReadBlockM(U32 dHandle,\r
2097                U8  *pBytesRet,\r
2098                U32 nBytes,\r
2099                U32 dLFA,\r
2100                U32 *pdBytesRet,\r
2101                U8  fFill)                       /* TRUE if filling a stream buffer */\r
2102 {\r
2103 U32 erc, j, LBA, iFCB, bpc, spc, nDone, rLFA, nLeft, nBlks;\r
2104 U16 Clstr, MaxClstr, ClstrSav;\r
2105 U8 Drive;\r
2106 \r
2107   erc = ValidateHandle(dHandle, &iFCB);         /* Sets iFCB if OK */\r
2108   if (erc) return erc;\r
2109 \r
2110   /* Certain FUB fields have different meanings in stream */\r
2111 \r
2112   if ((paFUB[dHandle].fStream) && (!fFill))\r
2113   {\r
2114         *pdBytesRet = 0;\r
2115         return ErcStreamFile;\r
2116   }\r
2117 \r
2118   /* set these up in advance */\r
2119 \r
2120   nBlks = nBytes/512;                                   /* nBytes MUST be multiple of 512 */\r
2121   Drive = paFCB[iFCB]->Ldrv;                    /* What logical drive are we on? */\r
2122   spc = Ldrv[Drive].SecPerClstr;                /* sectors per cluster */\r
2123   bpc = 512 * spc;                      /* Bytes per cluster */\r
2124   if (Ldrv[Drive].fFAT16)\r
2125         MaxClstr = 0xfff8;\r
2126   else\r
2127         MaxClstr = 0xff8;       /* FAT12 */\r
2128 \r
2129 \r
2130   /* Call to find the absolute cluster on the the logical disk,\r
2131      and also the relative LFA of the cluster in question.\r
2132   */\r
2133 \r
2134   erc = GetAbsoluteClstr(dHandle, dLFA, &Clstr, &rLFA);\r
2135 \r
2136   LBA = ClsToLBA(Clstr, Drive);         /* Get LBA of the target cluster */\r
2137 \r
2138   /* Now LBA equals beginning of cluster that dLFA resides in.\r
2139      We must see which sector in Clstr is the starting LBA.  To do this\r
2140      we MOD (dLFA in sectors) by (sectors per cluster) and\r
2141      add the leftover amount (should be 0 to nSPC-1) to LBA before we\r
2142      read.  For example: if dLFA was 2560 and spc was 4, this\r
2143      would be 5 % 4 = 1.  We would add 1 to the LBA.\r
2144      This is only done for the first read in the loop.\r
2145 \r
2146      We also set nleft which is how many sectors are left in the\r
2147      current cluster we are reading from.\r
2148   */\r
2149 \r
2150   LBA += (dLFA/512) % spc;\r
2151   nLeft = spc - ((dLFA/512) % spc);\r
2152   nDone = 0;\r
2153 \r
2154   while ((nBlks) && (!erc))\r
2155   {     /* while buffer isn't full and no error */\r
2156         if (nBlks > nLeft)\r
2157                 j = nLeft;\r
2158         else j = nBlks;\r
2159 \r
2160         paFUB[dHandle]->Clstr = Clstr;                  /* Save Current cluster */\r
2161         paFUB[dHandle]->LFAClstr = rLFA;                /* Save LFA for Clstr in FUB */\r
2162 \r
2163         erc = DeviceOp(Ldrv[Drive].DevNum, 1, LBA, j, pBytesRet);\r
2164         if (erc)\r
2165                 break;\r
2166         pBytesRet += j * 512;   /* further into their buffer */\r
2167         nBlks -= j;\r
2168         nLeft -= j;\r
2169         nDone += j;\r
2170 \r
2171         if ((nBlks) && (!nLeft))\r
2172         {               /* current cluster has none left */\r
2173                 nLeft = spc;\r
2174                 ClstrSav = Clstr;\r
2175                 erc = NextFATClstr(Drive, Clstr, &Clstr);       /* next FAT cluster */\r
2176                 if (erc)\r
2177                 {\r
2178                         *pdBytesRet = nDone*512;\r
2179                         return(erc);\r
2180                 }\r
2181                 rLFA += bpc;                                             /* Update rel LFA of new cluster*/\r
2182             if (Clstr >= MaxClstr)\r
2183                 erc = ErcEOF; /* Last cluster */\r
2184             if (!Clstr)\r
2185                 erc = ErcBrokenFile;     /* No good next cluster! */\r
2186                 LBA = ClsToLBA(Clstr, Drive);            /* Get LBA of the target cluster*/\r
2187         }\r
2188   }\r
2189   *pdBytesRet = nDone*512;\r
2190 \r
2191   return erc;           /* WE'RE DONE, return the error (if any) */\r
2192 }\r
2193 \r
2194 \r
2195 /*** Write Block ***************************************\r
2196  This is the BLOCK write for the MMURTL FAT file system.\r
2197  dLFA must be the LFA of a valid portion of the file and\r
2198  nBlks must not extend beyond the last cluster allocated\r
2199  to the file. IOW, you must call SetFileSize first.\r
2200  In a block write dLFA is always written on\r
2201  a sector boundry. We must make sure that the filesize\r
2202  will accomidate the sectors we want to write!\r
2203 ********************************************************/\r
2204 \r
2205 static U32 WriteBlockM(U32 dHandle, char *pData, U32 nBytes,\r
2206                 U32 dLFA, U32 *pdnBytesRet)\r
2207 {\r
2208 U32 erc, i, j, LBA, iFCB, bpc, spc, nDone, rLFA, nLeft, nBlks;\r
2209 U32 nLeft1, LBA1, nBlks1;\r
2210 U16 Clstr, MaxClstr;\r
2211 U8 Drive;\r
2212 \r
2213   erc = ValidateHandle(dHandle, &iFCB);         /* Sets iFCB if OK */\r
2214   if (erc) return erc;\r
2215 \r
2216   nBlks = nBytes/512;\r
2217   dLFA = (dLFA/512)*512;                /* round LFA down to nearest sector */\r
2218 \r
2219   if (!paFCB[iFCB]->Mode)               /* Is it open in Modify?? */\r
2220         return(ErcReadOnly);\r
2221 \r
2222   i = (paFCB[iFCB]->FileSize/512);      /* Set i nBlks in file max */\r
2223   if (paFCB[iFCB]->FileSize%512)\r
2224         i++;\r
2225 \r
2226   j = (dLFA/512) + nBlks;       /* blocks to write past dLFA*/\r
2227 \r
2228   if (j > i)\r
2229         return(ErcBeyondEOF);\r
2230 \r
2231   /* It seems OK to write the blocks out, so now let's DO IT! */\r
2232 \r
2233   Drive = paFCB[iFCB]->Ldrv;                    /* What logical drive are we on? */\r
2234   spc = Ldrv[Drive].SecPerClstr;                /* sectors per cluster */\r
2235   bpc = 512 * spc;                      /* Bytes per cluster */\r
2236   if (Ldrv[Drive].fFAT16)\r
2237         MaxClstr = 0xfff8;\r
2238   else\r
2239         MaxClstr = 0xff8;       /* FAT12 */\r
2240 \r
2241   erc = GetAbsoluteClstr(dHandle, dLFA, &Clstr, &rLFA);\r
2242   if (erc)\r
2243         return(erc);\r
2244 \r
2245   LBA = ClsToLBA(Clstr, Drive);         /* Get LBA of the target cluster */\r
2246 \r
2247   /* Now LBA equals beginning of cluster that dLFA resides in.\r
2248      We must see which sector in Clstr is the starting LBA.  To do this\r
2249      we MOD (dLFA in sectors) by (sectors per cluster) and\r
2250      add the leftover amount (should be 0 to nSPC-1) to LBA before we\r
2251      write.  For example: if dLFA was 2560 and spc was 4, this\r
2252      would be 5 % 4 = 1.  We would add 1 to the LBA.\r
2253      This is only done for the first write in the loop.\r
2254      We also set nleft which is how many sectors are left in the\r
2255      current cluster we are writing to so we know when to\r
2256      move to the next cluster.\r
2257   */\r
2258 \r
2259   LBA += (dLFA/512) % spc;\r
2260   LBA1 = LBA;\r
2261   nLeft = spc - ((dLFA/512) % spc);\r
2262   nLeft1 = nLeft;\r
2263   nBlks1 = nBlks;\r
2264   nDone = 0;\r
2265 \r
2266   while ((nBlks) && (!erc))\r
2267   {     /* while blocks are left to write */\r
2268         if (nBlks > nLeft)\r
2269                 j = nLeft;\r
2270         else j = nBlks;\r
2271 \r
2272         paFUB[dHandle]->Clstr = Clstr;                  /* Save Current cluster */\r
2273         paFUB[dHandle]->LFAClstr = rLFA;                /* Save LFA for Clstr in FUB */\r
2274 \r
2275         erc = DeviceOp(Ldrv[Drive].DevNum, 2, LBA, j, pData);\r
2276         if (erc)\r
2277                 break;\r
2278         pData += (j * 512);     /* Update address */\r
2279         nDone += j;                     /* Total blocks done so far */\r
2280         nBlks -= j;\r
2281         nLeft -= j;\r
2282 \r
2283         if ((nBlks) && (!nLeft))\r
2284         {               /* done with current cluster */\r
2285                 nLeft = spc;\r
2286                 erc = NextFATClstr(Drive, Clstr, &Clstr);       /* next FAT cluster */\r
2287                 if (erc)\r
2288                         return(erc);\r
2289                 rLFA += bpc;                                             /* Update rel LFA of new cluster*/\r
2290             if ((Clstr >= MaxClstr) && (nBlks))  /* Problem! */\r
2291             {\r
2292                 erc = ErcBeyondEOF;                      /* Last cluster & they want more*/\r
2293                 }\r
2294             if (!Clstr) erc = ErcBrokenFile;     /* No good next cluster! */\r
2295                 LBA = ClsToLBA(Clstr, Drive);            /* Get LBA of the target cluster*/\r
2296         }\r
2297   }\r
2298   *pdnBytesRet = nDone * 512;\r
2299   return erc;           /* WE'RE DONE, return the error (if any) */\r
2300 }\r
2301 \r
2302 \r
2303 /*********************************************************\r
2304  Fills the stream buffer for the Current LFA in the FUB.\r
2305  We simply figure out which relative LBA we want in the\r
2306  buffer and call ReadBlockM to fill it.  We then set\r
2307  LFABuf in the FUB to show the LFA of the first byte\r
2308  in the buffer.\r
2309 **********************************************************/\r
2310 \r
2311 static U32 FillStreamBuff(U32 dHandle, U8 fInitial)\r
2312 {\r
2313 U32 erc, i, LFA, cLFA, LFABuf, iFCB;\r
2314 U32 sBuf;\r
2315 U8  *pBuf;\r
2316 \r
2317   erc = 0;\r
2318 \r
2319   /* Set these up in advance */\r
2320 \r
2321   cLFA =  paFUB[dHandle]->CrntLFA;              /* Find out where we want to be */\r
2322   LFABuf = paFUB[dHandle]->LFABuf;              /* LFA of first byte in buffer now */\r
2323   pBuf = paFUB[dHandle]->pBuf;                  /* Local ptr to buffer */\r
2324   sBuf = paFUB[dHandle]->sBuf;                  /* size of buffer */\r
2325   iFCB = paFUB[dHandle]->iFCB;                  /* FCB for this FUB */\r
2326 \r
2327         /*      If the file was just opened we fill with LFA 0 */\r
2328 \r
2329   if (fInitial)\r
2330   {\r
2331         erc = ReadBlockM(dHandle, pBuf, sBuf, 0, &i, TRUE);\r
2332     paFUB[dHandle]->LFABuf = 0;\r
2333   }\r
2334 \r
2335         /* Else If the LFA is already in the buffer we just exit OK */\r
2336 \r
2337   else if ((cLFA >= LFABuf) && (cLFA < (LFABuf + sBuf)))\r
2338   {\r
2339         erc = 0;\r
2340   }\r
2341 \r
2342         /* ELSE We must figure out what starting LFA we want and fill the buffer */\r
2343 \r
2344   else\r
2345   {\r
2346         LFA = (cLFA/512) * 512;         /* Round down to nearest sector */\r
2347         erc = ReadBlockM(dHandle, pBuf, sBuf, LFA, &i, 1);\r
2348     paFUB[dHandle]->LFABuf = LFA;\r
2349   }\r
2350 \r
2351   if (erc == ErcEOF)            /* We ignore this when filling the buffer */\r
2352         erc = 0;\r
2353   return erc;           /* WE'RE DONE, return the error (if any, except EOF) */\r
2354 }\r
2355 \r
2356 /*******************************************************\r
2357  This is the STREAM read for the MMURTL FAT file system.\r
2358  Used in conjunction with Get & Set FileLFA, you can\r
2359  move to any portion of the file to read one or more bytes.\r
2360  Data is buffered in Page sized chunks by the file\r
2361  system. This is the easiest method for reading a file.\r
2362 ********************************************************/\r
2363 \r
2364 static U32 ReadBytesM(U32 dHandle, U8 *pBytesRet, U32 nBytes, U32 *pdReadRet)\r
2365 {\r
2366 U32 erc, iFCB, sBuf, cLFA, fSize;\r
2367 U32 nBytesDone,         /* Total read so far */\r
2368         lfaEOB,                 /* LFA end of Buffer */\r
2369         nBytesOut;              /* Number of bytes to copy (this buffer full) */\r
2370 U8  *pOut, *pBuf;       /* Next data to send caller from buffer. Local pbuf */\r
2371 \r
2372   erc = ValidateHandle(dHandle, &iFCB);         /* Sets iFCB if OK */\r
2373   if (erc) return erc;\r
2374 \r
2375   /* Certain FUB fields have different meanings in stream type file */\r
2376 \r
2377   if (!paFUB[dHandle]->fStream)\r
2378         return ErcBlockFile;\r
2379 \r
2380   cLFA = paFUB[dHandle]->CrntLFA;                       /* local cLFA */\r
2381   fSize = paFCB[iFCB]->FileSize;                        /* local size */\r
2382 \r
2383   /* check and see if we are at EOF */\r
2384 \r
2385   if (cLFA >= fSize)\r
2386   {                                             /* early out */\r
2387     *pdReadRet = 0;\r
2388         return ErcEOF;\r
2389   }\r
2390 \r
2391   /* We are not at EOF so we need to fill stream buff\r
2392      and then calculate how many bytes we can give them\r
2393      from this buffer full of data.\r
2394   */\r
2395 \r
2396   pBuf = paFUB[dHandle]->pBuf;                  /* Local ptr to buffer/size */\r
2397   sBuf = paFUB[dHandle]->sBuf;\r
2398   nBytesDone = 0;\r
2399 \r
2400   while ((nBytesDone < nBytes) &&\r
2401                 (cLFA < fSize) &&\r
2402                 (!erc))\r
2403   {\r
2404         erc = FillStreamBuff(dHandle, 0);                               /* Fill the buff */\r
2405 \r
2406         /* Find out the LFA at the end of the buffer since we\r
2407            just filled it.  It may be less than the end since\r
2408            we may be near EOF.\r
2409         */\r
2410 \r
2411     lfaEOB = paFUB[dHandle]->LFABuf + sBuf -1;  /* LFA at End of Buffer */\r
2412     if (lfaEOB > fSize)                                                 /* Beyond EOF? */\r
2413         lfaEOB = fSize - 1;\r
2414 \r
2415         /* Calculate pointer to the next chunk out from stream buffer */\r
2416 \r
2417         pOut = pBuf + (cLFA - paFUB[dHandle]->LFABuf);\r
2418 \r
2419         /* Calc how many bytes we can get out of buffer (belongs to file) */\r
2420 \r
2421         nBytesOut = lfaEOB - cLFA + 1;\r
2422         if (nBytesOut > nBytes-nBytesDone)\r
2423                 nBytesOut = nBytes-nBytesDone;\r
2424 \r
2425         /* Send bytes to pBytesRet */\r
2426 \r
2427         CopyData(pOut, pBytesRet, nBytesOut);\r
2428 \r
2429         pBytesRet += nBytesOut;         /* Update pBytesRet */\r
2430         nBytesDone += nBytesOut;                /* Update nBytesDone */\r
2431         cLFA += nBytesOut;                              /* update CrntLFA */\r
2432         paFUB[dHandle]->CrntLFA = cLFA;\r
2433   }\r
2434 \r
2435   *pdReadRet = nBytesDone;                      /* Tell em how many bytes they got */\r
2436   if (!erc)\r
2437         if (cLFA == fSize)\r
2438                 erc = 1;\r
2439 \r
2440   return erc;\r
2441 }\r
2442 \r
2443 /*********************************************************\r
2444  Flushes the stream buffer if it has been modified.\r
2445  We read the current LBA of the buffer and write it\r
2446  to disk. Then reset the flag in the FCB.\r
2447  This uses WriteBlockM.  We ensure we do not call\r
2448  WriteBlockM with more sectors than are allocated\r
2449  to the file cause the buffer may extend past the\r
2450  actual allocated amount of clusters!\r
2451 **********************************************************/\r
2452 \r
2453 static U32 FlushStreamBuff(U32 dHandle)\r
2454 {\r
2455 U32 erc, i, j, LFABuf, iFCB, size;\r
2456 U32 sBuf;\r
2457 U8  *pBuf;\r
2458 \r
2459   erc = 0;\r
2460 \r
2461   if (paFUB[dHandle]->fModified)\r
2462   {\r
2463           LFABuf = paFUB[dHandle]->LFABuf;              /* LFA of first byte in buf now */\r
2464           pBuf = paFUB[dHandle]->pBuf;                  /* Local ptr to buffer */\r
2465           sBuf = paFUB[dHandle]->sBuf;                  /* size of buffer */\r
2466           iFCB = paFUB[dHandle]->iFCB;                  /* to check filesize */\r
2467           size = paFCB[iFCB]->FileSize;\r
2468 \r
2469           i = (sBuf/512);                       /* Total blocks in buff */\r
2470           j = (size-LFABuf)/512;        /* Blocks in buf belonging to file */\r
2471           if ((size-LFABuf)%512)        /* Odd bytes, add one more block */\r
2472                 j++;\r
2473           if (j < i)\r
2474                 i = j;\r
2475 \r
2476           erc = WriteBlockM(dHandle, pBuf, i*512, LFABuf, &i);\r
2477       paFUB[dHandle]->fModified = 0;\r
2478   }\r
2479   return erc;\r
2480 }\r
2481 \r
2482 \r
2483 /*** Write Bytes (Stream) ******************************\r
2484  This is the STREAM write for the MMURTL FAT file system.\r
2485  This uses FillStreamBuff to set up the buffer, and\r
2486  FlushStreamBuff to write modified stream buffers.\r
2487  This also calls SetFileSizeM to extend the\r
2488  filelength when necessary (writing at EOF).\r
2489 ********************************************************/\r
2490 \r
2491 static U32 WriteBytesM(U32 dHandle, char *pData, U32 nBytes, U32 *nBytesRet)\r
2492 {\r
2493 U32 erc, iFCB, sBuf, cLFA, fSize;\r
2494 U32 nBytesDone,         /* Total written so far */\r
2495         lfaEOB,                 /* LFA end of Buffer */\r
2496         nBytesOut;              /* Number of bytes to copy (this buffer full) */\r
2497 U8  *pOut, *pBuf;       /* Next data to send caller from buffer. Local pbuf */\r
2498 \r
2499         erc = ValidateHandle(dHandle, &iFCB);           /* Sets iFCB if OK */\r
2500         if (erc) return erc;\r
2501 \r
2502         /* Certain FUB fields have different meanings in stream type file */\r
2503 \r
2504         if (!paFUB[dHandle]->fStream)\r
2505                 return(ErcBlockFile);\r
2506 \r
2507         if (!paFCB[iFCB]->Mode)         /* Is it open in Modify?? */\r
2508                 return(ErcReadOnly);\r
2509 \r
2510         /* Set up local vars to values for current stream buffer.\r
2511            These are in effect for this call on entry!\r
2512         */\r
2513 \r
2514         pBuf = paFUB[dHandle]->pBuf;            /* Local ptr to buffer/size */\r
2515         sBuf = paFUB[dHandle]->sBuf;\r
2516         cLFA = paFUB[dHandle]->CrntLFA;         /* local cLFA */\r
2517         fSize = paFCB[iFCB]->FileSize;          /* local size */\r
2518 \r
2519         /* check and see if we are at EOF or will go past it\r
2520         when we write. If so, we make the file larger.\r
2521         */\r
2522 \r
2523         if (cLFA + nBytes > fSize)\r
2524         {               /* Must set file size first */\r
2525                 erc = SetFileSizeM(dHandle, cLFA + nBytes);\r
2526                 if (erc)\r
2527                 {\r
2528                         return(erc);\r
2529                 }\r
2530                 fSize = paFCB[iFCB]->FileSize;                  /* local size */\r
2531         }\r
2532 \r
2533         lfaEOB = paFUB[dHandle]->LFABuf + sBuf -1;      /* LFA at End of Buffer */\r
2534     if (lfaEOB > fSize)                                                 /* EOF before EOB? */\r
2535         lfaEOB = fSize - 1;\r
2536 \r
2537         /* Now we loop writing the bytes to the stream buffer\r
2538      filling it with each new section of the file.\r
2539      As this occurs we must call fillStreamBuff with\r
2540      each new section of the file if not already in the\r
2541      buff to ensure proper continuity of the file in case\r
2542      we are overwriting existing sections of the file.\r
2543         */\r
2544 \r
2545         nBytesDone = 0;\r
2546 \r
2547         while ((nBytesDone < nBytes) &&\r
2548                    (cLFA < fSize) &&\r
2549                    (!erc))\r
2550         {\r
2551                 /* If the next byte to write goes outside the current buffer\r
2552                 (before or after it),\r
2553                 we call FlushStreamBuff which will write the current\r
2554                 buffer and reset the fModified flag (if needed),\r
2555                 then call FillStreamBuff for file continuity.\r
2556                 */\r
2557 \r
2558                 if ((cLFA > lfaEOB) ||\r
2559                         (cLFA < paFUB[dHandle]->LFABuf))\r
2560                 {\r
2561                         erc = FlushStreamBuff(dHandle);\r
2562                                 if (erc)\r
2563                                 {\r
2564                                         return(erc);\r
2565                                 }\r
2566                         erc = FillStreamBuff(dHandle, 0);       /* Fill the buff */\r
2567                                 if (erc)\r
2568                                 {\r
2569                                         return(erc);\r
2570                                 }\r
2571                 }\r
2572 \r
2573                 /* Find out the LFA at the end of the buffer since we\r
2574                    just filled it.  It may be less than the end since\r
2575                    we may be near EOF.\r
2576                 */\r
2577 \r
2578             lfaEOB = paFUB[dHandle]->LFABuf + sBuf -1;  /* LFA at End of Buffer */\r
2579             if (lfaEOB > fSize)                                                 /* Beyond EOF? */\r
2580                 lfaEOB = fSize - 1;\r
2581 \r
2582                 /* Calc pointer to where next chunk goes in stream buffer */\r
2583 \r
2584                 pOut = pBuf + (cLFA - paFUB[dHandle]->LFABuf);\r
2585 \r
2586                 /* Calc how many bytes we can write to buffer. We must\r
2587                 ensure we don't exceed buffer size. */\r
2588 \r
2589                 nBytesOut = (lfaEOB + 1) - cLFA;\r
2590                 if (nBytesOut > nBytes-nBytesDone)\r
2591                         nBytesOut = nBytes-nBytesDone;\r
2592 \r
2593                 /* Get bytes from pData into stream buffer */\r
2594 \r
2595                 CopyData(pData, pOut, nBytesOut);\r
2596                 paFUB[dHandle]->fModified = 1;\r
2597 \r
2598                 pData += nBytesOut;                     /* Update pData pointer */\r
2599                 nBytesDone += nBytesOut;                /* Update nBytesDone */\r
2600                 cLFA += nBytesOut;                              /* update CrntLFA */\r
2601                 paFUB[dHandle]->CrntLFA = cLFA;\r
2602         }\r
2603 \r
2604         *nBytesRet = nBytesDone;                        /* Tell em how many bytes we wrote */\r
2605         return erc;\r
2606 }\r
2607 \r
2608 /*******************************************************\r
2609   GetFileSize returns the FileSize entry from the FCB\r
2610   for the handle specified.  This means the file must be\r
2611   OPEN.  This call would NOT be used for a directory\r
2612   listing function as you would have to open each file\r
2613   to get the information.  Use ReadDirSector instead.\r
2614 ********************************************************/\r
2615 \r
2616 static U32 GetFileSizeM(U32 dHandle, U32 *pdSizeRet)\r
2617 {\r
2618 U32 erc, iFCB;\r
2619   erc = ValidateHandle(dHandle, &iFCB);\r
2620   if (erc) return erc;\r
2621   *pdSizeRet = paFCB[iFCB]->FileSize;\r
2622   return 0;\r
2623 }\r
2624 \r
2625 \r
2626 /*******************************************************\r
2627   SetFileLFA sets a Stream mode file pointer to dLFA.\r
2628   If attempted on a block mode file an error is returned.\r
2629   -1 (0xffffffff) is equal to EOF.  The Stream Buffer\r
2630   is filled with the sectors that contains the LFA.\r
2631 ********************************************************/\r
2632 \r
2633 static U32 SetFileLFAM(U32 dHandle, S32 dLFA)\r
2634 {\r
2635 U32 erc, iFCB;\r
2636 \r
2637   erc = ValidateHandle(dHandle, &iFCB);\r
2638   if (erc) return erc;\r
2639 \r
2640   if (!paFUB[dHandle]->fStream)\r
2641         return ErcBlockFile;\r
2642 \r
2643   if (paFCB[iFCB]->Mode)        /* Modify mode - Flush will flush if needed */\r
2644                 erc = FlushStreamBuff(dHandle);\r
2645 \r
2646   /* -1 = Set file ptr to EOF */\r
2647 \r
2648   if (dLFA == -1)\r
2649         dLFA = paFCB[iFCB]->FileSize;\r
2650 \r
2651   if (dLFA > paFCB[iFCB]->FileSize)\r
2652     erc = ErcBeyondEOF;\r
2653 \r
2654   if (!erc)\r
2655   {\r
2656           paFUB[dHandle]->CrntLFA = dLFA;       /* Set where we are in the file */\r
2657           erc = FillStreamBuff(dHandle, 0);\r
2658   }\r
2659 \r
2660   return erc;\r
2661 }\r
2662 \r
2663 \r
2664 /*******************************************************\r
2665   GetFileLFA gets a Stream mode file pointer for caller\r
2666   returning it to pdLFARet.\r
2667   If attempted on a block mode file an error is returned.\r
2668   EOF is returned as the FileSize.\r
2669 ********************************************************/\r
2670 \r
2671 static U32 GetFileLFAM(U32 dHandle, U32 *pdLFARet)\r
2672 {\r
2673 U32 erc, iFCB;\r
2674   erc = ValidateHandle(dHandle, &iFCB);\r
2675   if (erc) return erc;\r
2676   if (!paFUB[dHandle]->fStream)\r
2677         return ErcBlockFile;\r
2678   *pdLFARet = paFUB[dHandle]->CrntLFA;\r
2679 \r
2680   return erc;\r
2681 }\r
2682 \r
2683 /*****************************************************\r
2684  This calls parse to validate the filename and separate\r
2685  it into directories and a filename.  It then walks\r
2686  up the tree until it either finds and opens the file,\r
2687  or errors out.  The handle that is returned is\r
2688  4 higher than the index to the FUB.  This is becuase\r
2689  0, 1, 2 & 3 are reserved for NUL, KBD, VID, and LPT\r
2690  which are  only accesible from the blocking File calls.\r
2691 *****************************************************/\r
2692 \r
2693 static U32 OpenFileM(U8 *pName,\r
2694                           U32 cbName,\r
2695                           U8 Mode,\r
2696                           U8 fStream,\r
2697                           U32 *pdHandleRet,\r
2698                           U32 iJob)                     /* make use of iJob later!!! */\r
2699 {\r
2700 U32 erc, level, i, iFCB, iFUB, LBADirEnt, EntOffset;\r
2701 U16 Clstr;\r
2702 U8 fFound, *pMem, Drive;\r
2703 \r
2704   if (Mode > 1)\r
2705         return ErcBadOpenMode;\r
2706 \r
2707   level = 0;    /* start at the root, compare to SpecDepth */\r
2708 \r
2709 /* RAB B */\r
2710  if (((cbName) && (*pName == ' ')) ||\r
2711      (!cbName))\r
2712         return(ErcBadFileSpec);\r
2713 /* RAB E */\r
2714 \r
2715   erc = ParseName(pName, cbName, iJob);\r
2716 \r
2717         /* The entire path has now been parsed out into an array of\r
2718            arrays, each 11 bytes long that contain each directory name\r
2719            up to and inlcuding the filename.  The first is always\r
2720            the root (entry 0). The drive will be a letter in FDrive.\r
2721         */\r
2722 \r
2723   if ((FDrive > 0x40) && (FDrive < 0x52))       /* A to J */\r
2724         Drive = FDrive - 0x41;                                                  /* Make it 0-9 */\r
2725   else erc = ErcNoSuchDrive;\r
2726 \r
2727   if (Ldrv[Drive].DevNum == 0xff)\r
2728     erc = ErcNoSuchDrive;\r
2729 \r
2730   if ((Drive < 2) && (!erc))\r
2731   {\r
2732         StatFloppy(Drive);\r
2733         erc= read_BS(Drive);\r
2734   }\r
2735 \r
2736   if (!erc)\r
2737   {                                     /* Get Root dir entry */\r
2738         erc = GetRootEnt(FileSpec[level],\r
2739                                          Drive,\r
2740                                          &LBADirEnt,\r
2741                                          &EntOffset,\r
2742                                          &pDirEnt);\r
2743 \r
2744         if (erc == ErcNoSuchFile)\r
2745         {\r
2746                 if (level == SpecDepth) erc = ErcNoSuchFile;\r
2747                 else erc = ErcNoSuchDir;\r
2748         }\r
2749         if (erc)\r
2750                 return(erc);\r
2751 \r
2752         if (!erc)\r
2753             Clstr = pDirEnt->StartClstr;  /* Clstr = beginning of file or dir */\r
2754 \r
2755         while ((level < SpecDepth) && (!erc))\r
2756         {       /* looking for Dir, not file yet */\r
2757 \r
2758                 ++level;                                        /* next level of parsed filespec */\r
2759 \r
2760                 erc = GetDirEnt(FileSpec[level],\r
2761                                                 Drive,\r
2762                                                 Clstr,\r
2763                                                 &LBADirEnt,\r
2764                                                 &EntOffset,\r
2765                                                 &pDirEnt);\r
2766                 if (erc == ErcNoSuchFile)\r
2767                 {\r
2768                         if (level == SpecDepth)\r
2769                                 erc = ErcNoSuchFile;\r
2770                         else erc = ErcNoSuchDir;\r
2771                 }\r
2772                 else if (erc)\r
2773                         return(erc);\r
2774                 else\r
2775                     Clstr = pDirEnt->StartClstr;  /* Clstr @ start of dir entry */\r
2776         }\r
2777 \r
2778         /* if we got here with no error we've got a file or a DIR.\r
2779            If it's DIR then it's an error.\r
2780            pDirEnt points to its directory entry, and Clstr\r
2781            is the starting cluster of the file\r
2782         */\r
2783 \r
2784         if (!erc) \r
2785         {\r
2786                 /* If Attributes say it's not a file then ERROR */\r
2787 \r
2788                 if (pDirEnt->Attr & (VOLNAME | DIRECTORY))\r
2789                         return ErcNotAFile;\r
2790 \r
2791                 /* If ModeModify and File is readOnly then ERROR */\r
2792 \r
2793                 if ((Mode) && (pDirEnt->Attr & READONLY))\r
2794                         return ErcReadOnly;\r
2795 \r
2796         /* We check to see if it's already open by looking through the\r
2797            valid FCBs to see if we have a Drive, StartClstr& name match.\r
2798            If so, we must see if the modes are compatible.\r
2799            A valid FCB is one where nUsers > 0.\r
2800         */\r
2801 \r
2802         fFound = 0;\r
2803         i=0;\r
2804         while ((i<nFCBs) && (!fFound)) \r
2805         {\r
2806 \r
2807                 if ((paFCB[i]->nUsers) &&\r
2808                         (paFCB[i]->Ldrv == Drive) &&\r
2809                     (paFCB[i]->StartClstr == pDirEnt->StartClstr) &&\r
2810                     (CompareNCS(&paFCB[i],\r
2811                                         FileSpec[SpecDepth], 11) == 0xffffffff))\r
2812                          fFound = 1;\r
2813                 else\r
2814                     ++i;\r
2815         }\r
2816 \r
2817         if (fFound)\r
2818         {                       /* it's open already.  i is index into FCBs  */\r
2819                 if (paFCB[i]->Mode)             /* it's open in Modify already */\r
2820                         return ErcFileInUse;\r
2821                 else\r
2822                 {\r
2823                         iFCB = i;                       /* Index to this FCB */\r
2824                         pFCB = &paFCB[i];       /* make pFCB point to FCB found */\r
2825                         pFCB->nUsers++;         /* One more user */\r
2826                 }\r
2827         }\r
2828         else\r
2829         {                                       /* It not already open. Find empty FCB */\r
2830                 i = 0;\r
2831                 while ((i<nFCBs) && (paFCB[i]->nUsers)) ++i;    /* Find new FCB */\r
2832 \r
2833                 if (i==nFCBs) return ErcNoFreeFCB;                      /* Couldn't */\r
2834 \r
2835                 /* i now indexes into FCBs for a free FCB.  New we copy the\r
2836                    directory entry for the file into the FCB and set up\r
2837                    the other FCB values. */\r
2838 \r
2839                 iFCB = i;                                               /* used to add an FUB */\r
2840                 pFCB = &paFCB[i];                               /* make pFCB point to FCB found */\r
2841                 CopyData(pDirEnt, pFCB, 32);    /* Copy Dir Ent into FCB */\r
2842                 pFCB->Ldrv = Drive;                             /* Set Drive */\r
2843                 pFCB->nUsers++;                                 /* Now in use */\r
2844                 pFCB->Mode = Mode;                              /* Open mode */\r
2845                 pFCB->LBADirSect =      LBADirEnt;      /* So we know where it came from */\r
2846                 pFCB->oSectDirEnt = EntOffset;  /* "  "  */\r
2847         }\r
2848 \r
2849         /* Now we have an FCB (either existing or we just built it).\r
2850            Now add an FUB and fill in the info for the user\r
2851            so we can return a handle to it.  The Job fields is 0\r
2852            for free FUBs.\r
2853         */\r
2854 \r
2855         i = 4;\r
2856         while ((i<nFUBs) && (paFUB[i]->Job)) ++i;       /* Find new FUB */\r
2857         if (i==nFUBs) \r
2858         {\r
2859                 pFCB->nUsers--;                                 /* Make FCB correct */\r
2860                 return ErcNoFreeFUB;                    /* Couldn't */\r
2861 \r
2862         }\r
2863 \r
2864         /* If we got here, i is an index to a free FUB. */\r
2865 \r
2866         iFUB = i;\r
2867         paFUB[iFUB]->Job = iJob;                /* Job Owner */\r
2868         paFUB[iFUB]->iFCB = iFCB;               /* Set index to FCB for this file */\r
2869         paFUB[iFUB]->CrntLFA = 0;               /* Current Logical File Address */\r
2870         paFUB[iFUB]->fModified = 0;             /* Stream buf was modified */\r
2871         paFUB[iFUB]->fStream = fStream; /* NonZero for STREAM mode */\r
2872         paFUB[iFUB]->Clstr = pDirEnt->StartClstr;       /* Start Cluster */\r
2873         paFUB[iFUB]->LFAClstr = 0;              /* Rel LFA to 0 */\r
2874         paFUB[iFUB]->LFABuf = 0;                /* First LFA in Buffer */\r
2875         paFUB[iFUB]->sBuf = 0;                  /* Default to No Buf */\r
2876 \r
2877         if (fStream) \r
2878         {               /* allocate/fill buffer and set rest of FUB */\r
2879 \r
2880                 erc = AllocOSPage(1, &pMem);            /* Stream Buf is 4K */\r
2881 \r
2882             if (erc) \r
2883             {                                                   /* No MEM left... Bummer */\r
2884                         pFCB->nUsers--;                                 /* Return FCB to pool */\r
2885                         paFUB[iFUB]->Job = 0;                   /* Return FUB to pool */\r
2886                         return erc;                                             /* Return Erc to user... */\r
2887                 }\r
2888 \r
2889                 paFUB[iFUB]->pBuf = pMem;               /* Ptr to buffer if stream mode */\r
2890                 paFUB[iFUB]->sBuf = 4096;               /* Size of buffer */\r
2891 \r
2892                 erc = FillStreamBuff(iFUB, 1);  /* fInitial to TRUE */\r
2893 \r
2894                 if (erc) \r
2895                 {\r
2896                         pFCB->nUsers--;                         /* Return FCB to pool */\r
2897                         paFUB[iFUB]->Job = 0;           /* Return FUB to pool */\r
2898                         DeAllocPage(pMem, 1);           /* Free memory for buffer */\r
2899                         return erc;                                     /* Return Erc to user... */\r
2900                 }\r
2901         }\r
2902 \r
2903     *pdHandleRet = iFUB;                /* File handle */\r
2904         }\r
2905   }\r
2906 \r
2907 return erc;\r
2908 \r
2909 }\r
2910 \r
2911 \r
2912 /*******************************************************\r
2913   CLOSE FILE for the MMURTL DOS file system. This finds\r
2914   the FUB, checks to see if the buffer should be flushed\r
2915   and deallocated (only for stream mode), invalidates\r
2916   the FUB, and the FCB (if this is the last or only user).\r
2917 ********************************************************/\r
2918 \r
2919 static U32 CloseFileM (U32 dHandle)\r
2920 {\r
2921 U32 erc, iFCB, i;\r
2922 \r
2923         erc = ValidateHandle(dHandle, &iFCB);\r
2924         if (erc) return erc;\r
2925 \r
2926         if (paFCB[iFCB]->Mode)\r
2927         {  /* Modify mode */\r
2928                 if (paFUB[dHandle]->fStream)\r
2929                 {\r
2930                         erc = FlushStreamBuff(dHandle);\r
2931                 }\r
2932                 UpdateDirEnt(iFCB);                     /* ignore error */\r
2933         }\r
2934 \r
2935         if (paFUB[dHandle]->fStream)\r
2936                 DeAllocPage(paFUB[dHandle]->pBuf, 1);   /* Free buffer */\r
2937 \r
2938         /* This means the FS is screwed up. This shouldn't happen... */\r
2939         if (!paFCB[iFCB]->nUsers)\r
2940                 erc = ErcBadFCB;\r
2941         else\r
2942                 paFCB[iFCB]->nUsers--;\r
2943 \r
2944           /* Now we should be able to close it and free the the FUB.\r
2945          If the FCB.nUsers flips to 0 it will be free too\r
2946           */\r
2947 \r
2948         paFUB[dHandle]->Job = 0;\r
2949 \r
2950         /* This will write all modified fat sectors */\r
2951 \r
2952         for (i=0; i<nFATBufs; i++)\r
2953                         UpdateFAT(i);\r
2954 \r
2955   return erc;\r
2956 }\r
2957 \r
2958 \r
2959 /*** Create File ***************************************\r
2960  This is Create File for the MMURTL FAT file system.\r
2961  This is also used internally to create directories.\r
2962 ********************************************************/\r
2963 \r
2964 static U32 CreateFileM(char *pName,\r
2965                                 long cbName,\r
2966                                 long attrib,\r
2967                                 long iJob)\r
2968 {\r
2969 unsigned long dHandle, i, j, k, erc, LBA, spc;\r
2970 char Path[70];\r
2971 long cbPath;\r
2972 char filename[12];\r
2973 U16 CrntClstr, ClstrValue, iStart, DirClstr;\r
2974 U8 fFound, Drive, fDir;\r
2975 \r
2976         /* First we try to open it to see if it exists. If we get back\r
2977                 ErcOK or ErcFileInUse the name is already\r
2978                 in use as a file and we give them ErcDupName.\r
2979                 We return other errors as we find them.\r
2980         */\r
2981         if (attrib & DIRECTORY)\r
2982         {\r
2983                 fDir = 1;\r
2984                 erc = 0;\r
2985         }\r
2986         else\r
2987                 fDir = 0;\r
2988 \r
2989         erc = OpenFileM(pName, cbName, 0, 0, &dHandle, iJob);\r
2990 \r
2991         switch (erc)\r
2992         {\r
2993         case ErcOK:\r
2994                 CloseFileM(dHandle);\r
2995                 erc = ErcDupName;\r
2996                 break;\r
2997         case ErcFileInUse:\r
2998                 erc = ErcDupName;\r
2999                 break;\r
3000         case ErcNotAFile:       /* It a directory... */\r
3001                 break;\r
3002         case ErcNoSuchFile:\r
3003         {       /* OK, this means we can try to create it! */\r
3004 \r
3005                 erc = 0;\r
3006 \r
3007                 BuildSpec(pName, cbName, Path, &cbPath, iJob);\r
3008 \r
3009                 erc = ParseName(Path, cbPath, iJob);\r
3010                 if (erc)\r
3011                         return(erc);\r
3012 \r
3013                 /* FDrive was set up on Parse */\r
3014                 Drive = FDrive - 0x41;                  /* Make it 0-9 */\r
3015 \r
3016                 /* First we setup the filename from what was parsed out\r
3017                 during the Parse call. Then eliminate it from Path.\r
3018                 */\r
3019 \r
3020                 CopyData(FileSpec[SpecDepth], filename, 11); /* filename */\r
3021 \r
3022                 filename[11] = 0;\r
3023 \r
3024                 /* Hack the filename from Path so we can search the\r
3025                 directory path properly */\r
3026 \r
3027                 while ((cbPath) && (Path[cbPath-1] != 0x5C)) \r
3028                 {\r
3029                         cbPath--;\r
3030                 }\r
3031 \r
3032                 /* Each directory sector has 16 32 byte entries.\r
3033                 We will now walk thru each sector until we find one\r
3034                 that is a deleted or empty entry.\r
3035                 A deleted entry has E5h as its first character in the name.\r
3036                 An unused entry has 00h as its first character in the name.\r
3037                 */\r
3038 \r
3039                 fFound = 0;\r
3040                 i = 0;                                                  /* i = sectornum */\r
3041                 while ((!fFound) && (!erc)) \r
3042                 {\r
3043                         erc = GetDirSectorM(Path, cbPath, abTmpSector,\r
3044                                                                 512, i++, &LBA, &DirClstr, iJob);\r
3045                         if (!erc) \r
3046                         {\r
3047                                 k = 0;\r
3048                 pDirEnt = abTmpSector;\r
3049                                 while (k<16) \r
3050                                 {\r
3051                                         if ((pDirEnt->Name[0] == 0xE5) ||\r
3052                             (!pDirEnt->Name[0]))\r
3053                             {\r
3054                                 fFound = 1;\r
3055                                 break;\r
3056                                         }\r
3057                                         pDirEnt += 32;\r
3058                                         k++;\r
3059                                 }\r
3060                         }\r
3061                 }\r
3062                 /* When we get here, we have either found an entry or\r
3063                 we have run out of sectors!  If we run out of sectors\r
3064                 and this is the root, we error out (RootFull), otherwise\r
3065                 we extend the directory.\r
3066                 */\r
3067 \r
3068                 if ((erc == ErcNoMatch) && (!SpecDepth))\r
3069                         return (ErcRootFull);\r
3070 \r
3071         else if (erc == ErcEOF)\r
3072         {               /* reach end of dir! */\r
3073 \r
3074                         /* We must now extend the cluster chain for this\r
3075                            directory entry. DirClstr holds the last\r
3076                            valid sector of the directory.\r
3077                         */\r
3078 \r
3079                   FillData(abTmpSector, 512, 0);\r
3080                   spc = Ldrv[Drive].SecPerClstr;                /* sectors per cluster */\r
3081                   erc = ExtendClstrChain(Drive, DirClstr, &DirClstr);\r
3082                   if (erc)\r
3083                         return(erc);\r
3084                   LBA = ClsToLBA(DirClstr, Drive);\r
3085                   j = LBA;\r
3086                   i = spc;\r
3087                   erc = 0;\r
3088                   while ((i--) && (!erc))\r
3089                   {\r
3090                         erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,\r
3091                                                    1, abTmpSector);\r
3092                   }\r
3093                   pDirEnt = abTmpSector;  /* first entry in new sector */\r
3094                   fFound = 1;\r
3095 \r
3096                         /* We now have a new cluster on the end of the\r
3097                         directory and it is all zeros!. The first sector\r
3098                         is pointed to by LBA and abTmpSector is still zeros\r
3099                         just as an new dir sector should be with pDirEnt\r
3100                         pointing to the first entry.\r
3101                         */\r
3102                 }\r
3103 \r
3104                 if ((!erc) && (fFound)) \r
3105                 {               /* Let's DO IT! */\r
3106 \r
3107                         /* pDirEnt points to the entry we will use and\r
3108                            abTmpSector still has the entire sector in it,\r
3109                            so we find an empty clstr on the disk, allocate\r
3110                            to this file, fill in the rest of the dir\r
3111                            entry and we are almost done.\r
3112                         */\r
3113 \r
3114                         /* Find a fat buf already in memory for this drive */\r
3115                         /* One WILL be here! */\r
3116 \r
3117                         k = 0;\r
3118                         CrntClstr = 0;\r
3119                         while ((k<nFATBufs) && (!CrntClstr)) \r
3120                         {\r
3121                                 if ((Drive == Fat[k].Drive) && (Fat[k].LastUsed))\r
3122                     CrntClstr = Fat[k].iClstrStart;     /* valid cluster */\r
3123                                 k++;\r
3124                         }\r
3125 \r
3126                         if (!CrntClstr)\r
3127                                 CrntClstr = 2;          /* Can't find it so start at beginning */\r
3128 \r
3129                         iStart = CrntClstr;             /* where we started looking for empties */\r
3130 \r
3131                         fFound = 0;\r
3132                         while (!fFound) \r
3133                         {\r
3134                                 ++CrntClstr;            /* next cluster */\r
3135                                 if (CrntClstr == iStart)\r
3136                                         return(ErcDiskFull);\r
3137 \r
3138                                 erc = GetClstrValue(CrntClstr, Drive, 0, &ClstrValue, &j);\r
3139 \r
3140                                 if ((!erc) && (!ClstrValue))\r
3141                                                 fFound = 1;     /* found an empty one */\r
3142 \r
3143                                 else if (erc == ErcBadFATClstr) \r
3144                                 { /* off the end */\r
3145                                                 /* we started AFTER beginning of disk so\r
3146                                                   we will go back and look for empties\r
3147                                                   from beginning to where we started.\r
3148                                                 */\r
3149 \r
3150                                         if (iStart > 2)\r
3151                                                 CrntClstr = 2;\r
3152                                         else\r
3153                                                 return(ErcDiskFull);\r
3154                                 }\r
3155                                 else if (erc)\r
3156                                                 return(erc);\r
3157                         }\r
3158 \r
3159                         /* If we got here, we found an empty cluster */\r
3160 \r
3161             CopyData(filename, pDirEnt, 11);\r
3162                         if (!fDir)\r
3163                                 pDirEnt->Attr =\r
3164                                         attrib & (READONLY | HIDDEN | SYSTEM | ARCHIVE);\r
3165                         else\r
3166                 pDirEnt->Attr = attrib;\r
3167                         GetFATTime(&pDirEnt->Time, &pDirEnt->Date);\r
3168                         pDirEnt->StartClstr = CrntClstr;\r
3169                         pDirEnt->FileSize = 0;\r
3170                         erc = SetClstrValue(CrntClstr, 0xFFFF, Drive, &i);\r
3171                         /* Now we write the dir sector back to disk */\r
3172                         if (!erc)\r
3173                                 erc = DeviceOp(Ldrv[Drive].DevNum, 2, LBA,\r
3174                                                                 1, abTmpSector);\r
3175 \r
3176                         /* If we were creating a directory, we must add\r
3177                         the two deafult directory entries . and ..\r
3178                         This is done by filling out abTmpSector as\r
3179                         the first sector of an empty directory and writing\r
3180                         it out to the allocated cluster.\r
3181                         We then zewro it out and write it to the rest\r
3182                         of the sectors in the new cluster.\r
3183                         */\r
3184 \r
3185                         if (fDir)\r
3186                         {\r
3187                           FillData(abTmpSector, 512, 0);\r
3188                           pDirEnt = abTmpSector;  /* first entry in new sector */\r
3189 \r
3190                                 /* do the current dir entry (.) */\r
3191 \r
3192                           CopyData(".          ", pDirEnt, 11);\r
3193                           pDirEnt->Attr = DIRECTORY;\r
3194                           GetFATTime(&pDirEnt->Time, &pDirEnt->Date);\r
3195                           pDirEnt->StartClstr = CrntClstr;\r
3196                           pDirEnt->FileSize = 0;\r
3197 \r
3198                                 /* do the previous current dir entry (.) */\r
3199 \r
3200                           pDirEnt += 32;\r
3201                           CopyData("..         ", pDirEnt, 11);\r
3202                           pDirEnt->Attr = DIRECTORY;\r
3203                           GetFATTime(&pDirEnt->Time, &pDirEnt->Date);\r
3204                           pDirEnt->StartClstr = DirClstr;\r
3205                           pDirEnt->FileSize = 0;\r
3206 \r
3207                           spc = Ldrv[Drive].SecPerClstr;                /* sectors per cluster */\r
3208                           LBA = ClsToLBA(CrntClstr, Drive);\r
3209                           j = LBA;\r
3210 \r
3211                           /* Write this sector out to disk */\r
3212                           erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,\r
3213                                                          1, abTmpSector);\r
3214 \r
3215                           FillData(abTmpSector, 512, 0); /* zero the rest */\r
3216 \r
3217                           erc = 0;\r
3218                           i = spc-1;    /* less one cause we wrote the first */\r
3219                           while ((i--) && (!erc))\r
3220                           {\r
3221                                 erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,\r
3222                                                            1, abTmpSector);\r
3223                           }\r
3224 \r
3225                         }\r
3226                         /* This will write all modified fat sectors */\r
3227 \r
3228                         for (i=0; i<nFATBufs; i++)\r
3229                                 UpdateFAT(i);\r
3230 \r
3231                         return(erc);\r
3232 \r
3233                 }\r
3234                 else\r
3235                         return(erc);\r
3236         }\r
3237         default: ;\r
3238 \r
3239         } /* switch */\r
3240         return (erc);\r
3241 }\r
3242 \r
3243 /*** Delete File ***************************************\r
3244  This is Delete File for the MMURTL FAT file system.\r
3245  The file must be opened in mode modify which gives\r
3246  the caller exclusive access. The file is closed\r
3247  even if the Delete fails.\r
3248 ********************************************************/\r
3249 \r
3250 static U32 DeleteFileM(long *dHandle)\r
3251 {\r
3252 U32 erc, iFCB, i;\r
3253 U16 iStart;\r
3254 U8 Drive;\r
3255 \r
3256         erc = ValidateHandle(dHandle, &iFCB);\r
3257         if (erc) return erc;\r
3258 \r
3259         Drive = paFCB[iFCB]->Ldrv;                      /* What logical drive are we on? */\r
3260 \r
3261         if (!paFCB[iFCB]->Mode) \r
3262         {  /* Modify mode? */\r
3263                 CloseFileM(dHandle);\r
3264                 return ErcReadOnly;\r
3265         }\r
3266         if (paFUB[dHandle]->fStream)\r
3267                 DeAllocPage(paFUB[dHandle]->pBuf, 1);   /* Free buffer */\r
3268 \r
3269         iStart = paFCB[iFCB]->StartClstr;\r
3270         if (iStart) \r
3271         {\r
3272                 erc = TruncClstrChain(Drive, iStart);\r
3273                 if (!erc)\r
3274                         erc = SetClstrValue(iStart, 0, Drive, &i);\r
3275         }\r
3276 \r
3277         paFCB[iFCB]->Name[0] = 0xE5;\r
3278         UpdateDirEnt(iFCB);                     /* ignore error */\r
3279 \r
3280         /* This means the FS is screwed up. This shouldn't happen... */\r
3281         if (!paFCB[iFCB]->nUsers)\r
3282                 erc = ErcBadFCB;\r
3283         else\r
3284                 paFCB[iFCB]->nUsers--;\r
3285 \r
3286           /* Now we should be able to close it and free the the FUB.\r
3287          If the FCB.nUsers flips to 0 it will be free too\r
3288           */\r
3289 \r
3290         paFUB[dHandle]->Job = 0;\r
3291 \r
3292         /* This writes all modified fat sectors */\r
3293 \r
3294         for (i=0; i<nFATBufs; i++)\r
3295                         UpdateFAT(i);\r
3296 \r
3297         return erc;\r
3298 }\r
3299 \r
3300 /*** Rename File ***************************************\r
3301  This is Rename File for the MMURTL FAT file system.\r
3302 ********************************************************/\r
3303 \r
3304 static U32 RenameFileM(char *pCrntName, long dcbCrntName,\r
3305                 char *pNewName, long dcbNewName, U32 iJob)\r
3306 {\r
3307 U32 dHandle, erc, erc1, iFCB;\r
3308 \r
3309         erc = OpenFileM(pCrntName, dcbCrntName, 1, 0, &dHandle, iJob);\r
3310         if (!erc) \r
3311         {\r
3312                 FDrive1 = FDrive;\r
3313                 CopyData(FileSpec, FileSpec1, 77);\r
3314                 SpecDepth1 = SpecDepth;\r
3315                 erc = ParseName(pNewName, dcbNewName, iJob);\r
3316                 if (!erc)\r
3317                         if ((FDrive1 != FDrive) || (SpecDepth1 != SpecDepth))\r
3318                                 erc = ErcRenameDrv;                     /* No Rename across drives */\r
3319                 if (!erc)\r
3320                         if (SpecDepth)          /* Compare upper tree */\r
3321                                 if (CompareNCS(FileSpec, FileSpec1, SpecDepth * 11) != -1)\r
3322                                         erc = ErcRenameDir;             /* No Rename across dirs */\r
3323                 if (!erc) \r
3324                 { /* OK to rename */\r
3325                         iFCB = paFUB[dHandle]->iFCB;            /* FCB for this FUB */\r
3326                         CopyData(FileSpec[SpecDepth], &paFCB[iFCB], 11);\r
3327                         erc = UpdateDirEnt(iFCB);\r
3328                 }\r
3329                 erc1 = CloseFileM(dHandle);\r
3330                 if (!erc)\r
3331                         erc = erc1;\r
3332         }\r
3333         return (erc);\r
3334 }\r
3335 \r
3336 /*** Create Dir ***************************************\r
3337  This is Create Directory for the MMURTL FAT file system.\r
3338 ********************************************************/\r
3339 \r
3340 static U32      CreateDirM(char *pPath, long cbPath, long iJob)\r
3341 {\r
3342 long erc;\r
3343 \r
3344   erc = CreateFileM(pPath, cbPath, DIRECTORY, iJob);\r
3345   return(erc);\r
3346 \r
3347 }\r
3348 /*** Delete Directory ***********************************\r
3349  This is Delete Directory for the MMURTL FAT file system.\r
3350 ********************************************************/\r
3351 \r
3352 static U32      DeleteDirM(char *pPath, long cbPath, long fAllFiles, long iJob)\r
3353 {\r
3354         pPath = 0;\r
3355         cbPath = 0;\r
3356         fAllFiles = 0;\r
3357         iJob = 0;\r
3358 }\r
3359 \r
3360 \r
3361 /*******************************************************\r
3362  This is the File system task. All file system requests\r
3363  end up here to be serviced.\r
3364 ********************************************************/\r
3365 \r
3366 static void FSysTask(void)\r
3367 {\r
3368 U32 FMsg[2], merc, erc, i;\r
3369 U16 i16;\r
3370 \r
3371 while (1) \r
3372 {\r
3373   erc = WaitMsg(FSysExch, FMsg);\r
3374   if (!erc)\r
3375   {\r
3376 \r
3377         pRQB = FMsg[0];         /* first DD in Msg is pointer to RQBlock */\r
3378 \r
3379         switch (pRQB->ServiceCode)\r
3380         {\r
3381                 case 0 :                /* JobAbort - CLOSE ALL FILES FOR THIS JOB! */\r
3382                         i = 4;\r
3383                         while (i<nFUBs)\r
3384                         {\r
3385                     if (paFUB[i]->Job == pRQB->dData0)\r
3386                     {\r
3387                                         CloseFileM(i);\r
3388                                 }\r
3389                                 ++i;    /* next FUB */\r
3390                         }\r
3391 \r
3392                         erc = 0;\r
3393                         break;\r
3394                 case 1 :                /* OpenFile */\r
3395                         erc = OpenFileM(pRQB->pData1,       /* pFilename */\r
3396                                         pRQB->cbData1,      /* dcbFilename */\r
3397                                                     pRQB->dData0,       /* Mode */\r
3398                                                     pRQB->dData1,       /* Type */\r
3399                                                     pRQB->pData2,       /* pdHandleRet */\r
3400                                                     pRQB->RqOwnerJob);  /* iJob Owner */\r
3401                         break;\r
3402                 case 2 :                /* CloseFile */\r
3403                         erc = CloseFileM(pRQB->dData0);     /* Handle */\r
3404                         break;\r
3405                 case 3 :                /* ReadBlock */\r
3406                         erc = ReadBlockM(pRQB->dData0,       /* Handle */\r
3407                                          pRQB->pData1,       /* pDataRet */\r
3408                                          pRQB->cbData1,      /* nByes */\r
3409                                                      pRQB->dData1,       /* dLFA */\r
3410                                                      pRQB->pData2,       /* pdnBytesRet */\r
3411                                                      0);                 /* NOT internal */\r
3412                         break;\r
3413                 case 4 :                /* WriteBlock */\r
3414                         erc = WriteBlockM(pRQB->dData0,      /* Handle */\r
3415                                           pRQB->pData1,      /* pData */\r
3416                                           pRQB->cbData1,     /* nBytes */\r
3417                                                       pRQB->dData1,      /* dLFA */\r
3418                                                       pRQB->pData2);     /* pdBytesRet */\r
3419                         break;\r
3420                 case 5 :                /* ReadBytes */\r
3421                         erc = ReadBytesM(pRQB->dData0,       /* Handle */\r
3422                                          pRQB->pData1,       /* pDataRet */\r
3423                                          pRQB->cbData1,      /* nBytes */\r
3424                                                      pRQB->pData2);      /* pdnBytesRet */\r
3425                         break;\r
3426                 case 6 :                /* WriteBytes */\r
3427                         erc = WriteBytesM(pRQB->dData0,      /* Handle */\r
3428                                           pRQB->pData1,      /* pData */\r
3429                                           pRQB->cbData1,     /* nBytes */\r
3430                                                       pRQB->pData2);     /* pdnBytesRet */\r
3431                         break;\r
3432                 case 7 :                /* GetFileLFA */\r
3433                         erc = GetFileLFAM(pRQB->dData0,      /* Handle */\r
3434                                           pRQB->pData1);     /* pdLFARet */\r
3435                         break;\r
3436                 case 8 :                /* SetFileLFA */\r
3437                         erc = SetFileLFAM(pRQB->dData0,      /* Handle */\r
3438                                          pRQB->dData1);     /* dNewLFA */\r
3439                         break;\r
3440                 case 9 :                /* GetFileSize */\r
3441                         erc = GetFileSizeM(pRQB->dData0,     /* Handle */\r
3442                                           pRQB->pData1);    /* pdSizeRet */\r
3443                         break;\r
3444                 case 10 :               /* SetFileSize */\r
3445                         erc = SetFileSizeM(pRQB->dData0,     /* Handle */\r
3446                                            pRQB->dData1);    /*  dSize */\r
3447                         break;\r
3448                 case 11 :               /* CreateFile */\r
3449                         erc = CreateFileM(pRQB->pData1,      /* pFilename  */\r
3450                                           pRQB->cbData1,     /* cbFilename */\r
3451                                           pRQB->dData0,      /* Attributes */\r
3452                                                           pRQB->RqOwnerJob); /* iJob Owner */\r
3453                         break;\r
3454                 case 12 :               /* RenameFile */\r
3455                         erc = RenameFileM(pRQB->pData1,      /* pCrntName  */\r
3456                                           pRQB->cbData1,     /* cbCrntName */\r
3457                                           pRQB->pData2,      /* pNewName */\r
3458                                           pRQB->cbData2,     /* dcbNewName */\r
3459                                           pRQB->RqOwnerJob); /* JobNum */\r
3460                         break;\r
3461                 case 13 :               /* DeleteFile */\r
3462                         erc = DeleteFileM(pRQB->dData0);     /* Handle  */\r
3463                         break;\r
3464                 case 14 :               /* CreateDirectory */\r
3465                         erc = CreateDirM(pRQB->pData1,      /* pPath */\r
3466                                          pRQB->cbData1,     /* cbPath */\r
3467                                          pRQB->RqOwnerJob); /* JobNum */\r
3468                         break;\r
3469                 case 15 :               /* DeleteDirectory */\r
3470                         erc = DeleteDirM(pRQB->pData1,      /* pPath */\r
3471                                          pRQB->cbData1,     /* cbPath */\r
3472                                          pRQB->dData0,      /* fAllFiles */\r
3473                                          pRQB->RqOwnerJob); /* JobNum */\r
3474                         break;\r
3475                 case 16 :               /* GetDirSector */\r
3476                         erc = GetDirSectorM(pRQB->pData1,      /* pPath    */\r
3477                                             pRQB->cbData1,     /* cbPath   */\r
3478                                                             pRQB->pData2,      /* pSectRet */\r
3479                                             pRQB->cbData2,     /* cbRetMax */\r
3480                                             pRQB->dData0,      /* SectNum */\r
3481                                             &i,                            /* for LBARet */\r
3482                                             &i16,                          /* for DirClstr */\r
3483                                             pRQB->RqOwnerJob); /* JobNum   */\r
3484                         break;\r
3485                 default :\r
3486                         erc = ErcBadSvcCode;\r
3487                         break;\r
3488         }\r
3489 \r
3490         merc = Respond(FMsg[0], erc);\r
3491 \r
3492   }\r
3493 }               /* forever */\r
3494 }\r
3495 \r
3496 /***************** PUBLIC BLOCKING CALLS FOR FILESYSM *************\r
3497  These calls query the TSS Exhange and use it to make Requests\r
3498  to the file system service on the behalf of the caller.\r
3499  These calls are fully reentrant! No static data!\r
3500 *******************************************************************/\r
3501 \r
3502 U32 far _OpenFile(char *pFilename,\r
3503                              long dcbFilename,\r
3504                                  long Mode,\r
3505                                  long Type,\r
3506                                  long *pdHandleRet)\r
3507 {\r
3508 long erc, exch, rqhndl, i, msg[2];\r
3509         if (dcbFilename == 3)\r
3510         {\r
3511                 if (CompareNCS(pFilename, "NUL" , 3) == -1)\r
3512                 {\r
3513             *pdHandleRet = 0;\r
3514             return(0);\r
3515                 }\r
3516                 else if (CompareNCS(pFilename, "KBD" , 3) == -1)\r
3517                 {\r
3518             *pdHandleRet = 1;\r
3519             return(0);\r
3520                 }\r
3521                 else if (CompareNCS(pFilename, "VID" , 3) == -1)\r
3522                 {\r
3523             *pdHandleRet = 2;\r
3524             return(0);\r
3525                 }\r
3526                 else if (CompareNCS(pFilename, "LPT" , 3) == -1)\r
3527                 {\r
3528                         erc = DeviceOp(3, 10, 0,    0,  &i);   /* 10=Open */\r
3529                         if (!erc)\r
3530                     *pdHandleRet = 3;\r
3531             return(erc);\r
3532                 }\r
3533         }\r
3534 \r
3535         GetTSSExch(&exch);              /* No error will come back! */\r
3536     erc = Request(fsysname, 1, exch, &rqhndl,\r
3537                   1,                                                    /* 1 Send ptr */\r
3538                   pFilename, dcbFilename,\r
3539                   pdHandleRet, 4,\r
3540                   Mode, Type, 0);\r
3541         if (!erc) erc = WaitMsg(exch, msg);\r
3542         if (erc) return(erc);\r
3543         return(msg[1]);\r
3544 }\r
3545 \r
3546 /*************************************/\r
3547 U32 far _CloseFile(unsigned long dHandle)\r
3548 {\r
3549 long erc, exch, rqhndl, i, msg[2];\r
3550 \r
3551         if (dHandle < 3)\r
3552            return(0);\r
3553         else if (dHandle == 3)\r
3554         {\r
3555                 erc = DeviceOp(3,  11,   0,   0,  &i);    /* 11 = Close */\r
3556                 return(erc);\r
3557         }\r
3558 \r
3559         GetTSSExch(&exch);\r
3560     erc = Request(fsysname, 2, exch, &rqhndl,\r
3561                    0,                                                   /* 0 Send ptr */\r
3562                    0, 0,\r
3563                    0, 0,\r
3564                    dHandle, 0, 0);\r
3565         if (!erc) erc = WaitMsg(exch, msg);\r
3566         if (erc) return(erc);\r
3567         return(msg[1]);\r
3568 }\r
3569 \r
3570 /*************************************/\r
3571 U32 far _ReadBlock(long dHandle,\r
3572                   char *pDataRet,\r
3573                   long nBlks,\r
3574                                   long dLFA,\r
3575                                   long *pdnBlksRet)\r
3576 {\r
3577 long erc, exch, rqhndl, msg[2], i;\r
3578         if (dHandle < 4)\r
3579                 return(ErcNotSupported);\r
3580         GetTSSExch(&exch);\r
3581     erc = Request(fsysname, 3, exch, &rqhndl,\r
3582                   1,                                                    /* 1 Send ptr */\r
3583                   pDataRet, nBlks,\r
3584                   pdnBlksRet, 4,\r
3585                   dHandle, dLFA, 0);\r
3586         if (!erc) erc = WaitMsg(exch, msg);\r
3587         if (erc)\r
3588                 return(erc);\r
3589         if(msg[1]) \r
3590         {\r
3591                 DeviceStat(10, &FDDevStat, 64, &i);\r
3592         }\r
3593         return(msg[1]);\r
3594 }\r
3595 \r
3596 /*************************************/\r
3597 U32 far _WriteBlock(long dHandle,\r
3598                    char *pData,\r
3599                    long nBlks,\r
3600                    long dLFA,\r
3601                    long *pdnBlksRet)\r
3602 \r
3603 {\r
3604 long erc, exch, rqhndl, msg[2];\r
3605         if (dHandle < 4)\r
3606                 return(ErcNotSupported);\r
3607         GetTSSExch(&exch);\r
3608     erc = Request(fsysname, 4, exch, &rqhndl,\r
3609                    1,                                                   /* 1 Send ptr */\r
3610                    pData, nBlks,\r
3611                    pdnBlksRet, 4,\r
3612                    dHandle, dLFA, 0);\r
3613         if (!erc) erc = WaitMsg(exch, msg);\r
3614         if (erc) return(erc);\r
3615         return(msg[1]);\r
3616 }\r
3617 \r
3618 /*************************************/\r
3619 U32 far _ReadBytes(long dHandle,\r
3620                                   char *pDataRet,\r
3621                                   long nBytes,\r
3622                                   long *pdnBytesRet)\r
3623 {\r
3624 long erc, exch, rqhndl, msg[2], i;\r
3625         if (dHandle == 0)\r
3626         {\r
3627         *pdnBytesRet = 0;\r
3628                 return(0);\r
3629         }\r
3630         else if (dHandle == 1)\r
3631         {\r
3632                 i = 0;\r
3633                 while (i < nBytes)\r
3634                 {\r
3635                         ReadKbd(*pDataRet++, 1);\r
3636                         i++;\r
3637                 }\r
3638             *pdnBytesRet = i;\r
3639                 return(0);\r
3640         }\r
3641         else if ((dHandle == 2) || (dHandle == 3))\r
3642         {\r
3643         *pdnBytesRet = 0;\r
3644                 return(ErcWriteOnly);\r
3645         }\r
3646 \r
3647         GetTSSExch(&exch);\r
3648     erc = Request(fsysname, 5, exch, &rqhndl,\r
3649                    1,                                                   /* 1 Send ptr */\r
3650                    pDataRet, nBytes,\r
3651                    pdnBytesRet, 4,\r
3652                    dHandle, 0, 0);\r
3653         if (!erc) erc = WaitMsg(exch, msg);\r
3654         if (erc) return(erc);\r
3655         return(msg[1]);\r
3656 }\r
3657 \r
3658 /*************************************/\r
3659 U32 far _WriteBytes(long dHandle,\r
3660                    char *pData,\r
3661                    long nBytes,\r
3662                    long *pdnBytesRet)\r
3663 \r
3664 {\r
3665 long erc, exch, rqhndl, VidAttr, msg[2];\r
3666 \r
3667         if (dHandle == 0) \r
3668         {\r
3669         *pdnBytesRet = nBytes;\r
3670                 return(0);\r
3671         }\r
3672         else if (dHandle == 1)\r
3673         {\r
3674         *pdnBytesRet = 0;\r
3675                 return(ErcReadOnly);\r
3676         }\r
3677         else if (dHandle == 2)\r
3678         {\r
3679                 GetNormVid(&VidAttr);\r
3680                 TTYOut(pData, nBytes, VidAttr);\r
3681             *pdnBytesRet = nBytes;\r
3682                 return(0);\r
3683         }\r
3684         else if (dHandle == 3)\r
3685         {\r
3686                 erc = DeviceOp(3, 2, 0,  nBytes, pData);   /* 2 = CmdWriteRec */\r
3687                 return(erc);\r
3688         }\r
3689 \r
3690         GetTSSExch(&exch);\r
3691     erc = Request(fsysname, 6, exch, &rqhndl,\r
3692                    1,                                                   /* 1 Send ptr */\r
3693                    pData, nBytes,\r
3694                    pdnBytesRet, 4,\r
3695                    dHandle, 0, 0);\r
3696         if (!erc) erc = WaitMsg(exch, msg);\r
3697         if (erc) return(erc);\r
3698         return(msg[1]);\r
3699 }\r
3700 \r
3701 /*************************************/\r
3702 U32 far  _GetFileLFA(long dHandle,\r
3703                     long *pdLFARet)\r
3704 \r
3705 {\r
3706 long erc, exch, rqhndl, msg[2];\r
3707         if (dHandle < 4)\r
3708                 return(ErcEOF);\r
3709 \r
3710         GetTSSExch(&exch);\r
3711     erc = Request(fsysname, 7, exch, &rqhndl,\r
3712                    0,                                                   /* 0 Send ptrs */\r
3713                    pdLFARet, 4,\r
3714                    0, 0,\r
3715                    dHandle, 0, 0);\r
3716         if (!erc) erc = WaitMsg(exch, msg);\r
3717         if (erc) return(erc);\r
3718         return(msg[1]);\r
3719 }\r
3720 \r
3721 /*************************************/\r
3722 U32 far _SetFileLFA(long dHandle,\r
3723                    long dNewLFA)\r
3724 {\r
3725 long erc, exch, rqhndl, msg[2];\r
3726         if (dHandle < 4)\r
3727                 return(ErcNotSupported);\r
3728         GetTSSExch(&exch);\r
3729     erc = Request(fsysname, 8, exch, &rqhndl,\r
3730                    0,                                                   /* 0 Send ptrs */\r
3731                    0, 0,\r
3732                    0, 0,\r
3733                    dHandle, dNewLFA, 0);\r
3734         if (!erc) erc = WaitMsg(exch, msg);\r
3735         if (erc) return(erc);\r
3736         return(msg[1]);\r
3737 }\r
3738 \r
3739 /*************************************/\r
3740 U32 far _GetFileSize(long dHandle,\r
3741                     long *pdSizeRet)\r
3742 {\r
3743 long erc, exch, rqhndl, msg[2];\r
3744         if (dHandle < 4)\r
3745                 return(ErcNotSupported);\r
3746         GetTSSExch(&exch);\r
3747     erc = Request(fsysname, 9, exch, &rqhndl,\r
3748                    0,                                                   /* 0 Send ptrs */\r
3749                    pdSizeRet, 4,\r
3750                    0, 0,\r
3751                    dHandle, 0, 0);\r
3752         if (!erc) erc = WaitMsg(exch, msg);\r
3753         if (erc) return(erc);\r
3754         return(msg[1]);\r
3755 }\r
3756 \r
3757 /*************************************/\r
3758 U32 far _SetFileSize(long dHandle,\r
3759                     long dSize)\r
3760 {\r
3761 long erc, exch, rqhndl, msg[2];\r
3762         if (dHandle < 4)\r
3763                 return(ErcNotSupported);\r
3764         GetTSSExch(&exch);\r
3765     erc = Request(fsysname, 10, exch, &rqhndl,\r
3766                    0,                                                   /* 0 Send ptrs */\r
3767                    0, 0,\r
3768                    0, 0,\r
3769                    dHandle, dSize, 0);\r
3770         if (!erc) erc = WaitMsg(exch, msg);\r
3771         if (erc) return(erc);\r
3772         return(msg[1]);\r
3773 }\r
3774 \r
3775 /*************************************/\r
3776 U32 far _CreateFile(char *pFilename,\r
3777                                    long cbFilename,\r
3778                                    long Attribute)\r
3779 {\r
3780 long erc, exch, rqhndl, msg[2];\r
3781         GetTSSExch(&exch);\r
3782     erc = Request(fsysname, 11, exch, &rqhndl,\r
3783                    1,                                                   /* 1 Send ptrs */\r
3784                    pFilename, cbFilename,\r
3785                    0, 0,\r
3786                    Attribute, 0, 0);\r
3787         if (!erc) erc = WaitMsg(exch, msg);\r
3788         if (erc) return(erc);\r
3789         return(msg[1]);\r
3790 }\r
3791 \r
3792 /*************************************/\r
3793 U32 far _RenameFile(char *pCrntName,\r
3794                                    long cbCrntName,\r
3795                                    char *pNewName,\r
3796                                    long cbNewName)\r
3797 {\r
3798 long erc, exch, rqhndl, msg[2];\r
3799         GetTSSExch(&exch);\r
3800     erc = Request(fsysname, 12, exch, &rqhndl,\r
3801                    2,                                                   /* 2 Send ptrs */\r
3802                    pCrntName, cbCrntName,\r
3803                    pNewName, cbNewName,\r
3804                    0, 0, 0);\r
3805         if (!erc) erc = WaitMsg(exch, msg);\r
3806         if (erc) return(erc);\r
3807         return(msg[1]);\r
3808 }\r
3809 \r
3810 /*************************************/\r
3811 U32 far _DeleteFile(long dHandle)\r
3812 {\r
3813 long erc, exch, rqhndl, msg[2];\r
3814         if (dHandle < 4)\r
3815                 return(ErcNotSupported);\r
3816         GetTSSExch(&exch);\r
3817     erc = Request(fsysname, 13, exch, &rqhndl,\r
3818                    0,                                                   /* 0 Send ptrs */\r
3819                    0, 0,\r
3820                    0, 0,\r
3821                    dHandle, 0, 0);\r
3822         if (!erc) erc = WaitMsg(exch, msg);\r
3823         if (erc) return(erc);\r
3824         return(msg[1]);\r
3825 }\r
3826 \r
3827 /*************************************/\r
3828 U32 far _CreateDir(char *pPath,\r
3829                                    long cbPath)\r
3830 {\r
3831 long erc, exch, rqhndl, msg[2];\r
3832         GetTSSExch(&exch);\r
3833     erc = Request(fsysname, 14, exch, &rqhndl,\r
3834                    1,                                                   /* 1 Send ptrs */\r
3835                    pPath, cbPath,\r
3836                    0, 0,\r
3837                    0, 0, 0);\r
3838         if (!erc) erc = WaitMsg(exch, msg);\r
3839         if (erc) return(erc);\r
3840         return(msg[1]);\r
3841 }\r
3842 \r
3843 /*************************************/\r
3844 U32 far _DeleteDir(char *pPath,\r
3845                                    long cbPath,\r
3846                                    long fAllFiles)\r
3847 {\r
3848 long erc, exch, rqhndl, msg[2];\r
3849         GetTSSExch(&exch);\r
3850     erc = Request(fsysname, 15, exch, &rqhndl,\r
3851                    1,                                                   /* 1 Send ptrs */\r
3852                    pPath, cbPath,\r
3853                    0, 0,\r
3854                    fAllFiles, 0, 0);\r
3855         if (!erc) erc = WaitMsg(exch, msg);\r
3856         if (erc) return(erc);\r
3857         return(msg[1]);\r
3858 }\r
3859 \r
3860 /*************************************/\r
3861 U32 far _GetDirSector(char *pPathSpec,\r
3862                                      long cbPathSpec,\r
3863                                          char *pEntRet,\r
3864                                      long cbEntRet,\r
3865                                      long SectNum)\r
3866 {\r
3867 long erc, exch, rqhndl, msg[2];\r
3868         GetTSSExch(&exch);\r
3869     erc = Request(fsysname, 16, exch, &rqhndl,\r
3870                    1,                                                   /* 1 Send ptrs */\r
3871                    pPathSpec, cbPathSpec,\r
3872                    pEntRet, cbEntRet,\r
3873                    SectNum, 0, 0);\r
3874         if (!erc) erc = WaitMsg(exch, msg);\r
3875         if (erc) return(erc);\r
3876         return(msg[1]);\r
3877 }\r
3878 \r
3879 /********************************************\r
3880  Initialization Routine for the File system.\r
3881  This is called the Monitor where any errors\r
3882  will be reported.\r
3883 **********************************************/\r
3884 \r
3885 U32 InitFS(void)\r
3886 {\r
3887 U32 erc, i, j;\r
3888 U8 *pMem;\r
3889 \r
3890   /* Allocate FAT buffers and initialize FAT related structures.\r
3891      We will allocate 24Kb worth of buffers (6 Pages, 16 buffers).\r
3892    */\r
3893 \r
3894   Fat[0].pBuf = FatBufA;  /* floppy fat buffers */\r
3895 \r
3896   erc = AllocOSPage(2, &pMem);\r
3897 \r
3898   if (!erc)                              /* hard disk fat buffers from allocated mem */\r
3899     for (i=1; i<nFATBufs; i++)\r
3900     {\r
3901           Fat[i].pBuf = pMem;                   /* Make pBuf point to each buffer */\r
3902           pMem += 512;                                  /* Next buffer in allocated memory */\r
3903         }\r
3904 \r
3905   /* Allocate and initialize FCBs and FUBs. This is enough FCBs and\r
3906      FUBS for 128 openfiles.  (Good enough to start...)\r
3907   */\r
3908 \r
3909   if (!erc)\r
3910           erc = AllocOSPage(2, &paFCB); /* a pointer to allocated FCBs. */\r
3911   if (!erc)\r
3912           FillData(paFCB, 8192, 0);\r
3913   if (!erc)\r
3914           erc = AllocOSPage(1, &paFUB); /* a pointer to allocated FUBs. */\r
3915   if (!erc)\r
3916           FillData(paFUB, 4096, 0);\r
3917 \r
3918   if (!erc)     erc = read_PE();        /* reads partition tables */\r
3919 \r
3920   if (!erc)     erc = SetDriveGeometry(12);\r
3921 \r
3922   if (!erc)     \r
3923   {\r
3924         erc = SetDriveGeometry(13);\r
3925     if (erc == 663) erc = 0;            /* may be invalid drive */\r
3926   }\r
3927 \r
3928   StatFloppy(0);\r
3929   StatFloppy(1);\r
3930 \r
3931         /* read all logical drive boot sectors */\r
3932 \r
3933   if (!erc) \r
3934   {\r
3935         for (i=0; i< nLDrvs; i++) \r
3936         {\r
3937           if (Ldrv[i].DevNum != 0xff) \r
3938           {\r
3939                         read_BS(i);\r
3940           }\r
3941         }\r
3942   }\r
3943 \r
3944   for (i=0; i<nLDrvs; i++)\r
3945         if (Ldrv[i].DevNum != 0xff) \r
3946         {\r
3947         j=12;\r
3948           if (Ldrv[i].fFAT16)\r
3949                   j=16;\r
3950           xprintf("%c: Heads %d, Sec/Trk %d, Sec/Clstr %d, Dev %d, FAT%d \r\n",\r
3951                         i+0x41,\r
3952                         Ldrv[i].nHeads,\r
3953                         Ldrv[i].nSecPerTrk,\r
3954                         Ldrv[i].SecPerClstr,\r
3955                         Ldrv[i].DevNum,\r
3956                         j);\r
3957         }\r
3958 \r
3959   if (!erc)\r
3960           erc = AllocExch(&FSysExch);\r
3961 \r
3962         /* Start the filesystem task at a decently high priority (5).\r
3963         This should be higher than the Monitor status task and even the\r
3964         Keyboard. */\r
3965 \r
3966   if (!erc)\r
3967         erc = SpawnTask(&FSysTask, 5, 0, &FSysStack[511], 1);\r
3968 \r
3969   if (!erc)\r
3970    erc = RegisterSvc(fsysname, FSysExch);\r
3971 \r
3972 return erc;\r
3973 \r
3974 }\r