1 /* This is the MMURTL, MS-DOS Compatible (FAT) File system. */
\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
10 About MS-DOS disk formats and how MMURTL handles them.
\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
18 Physical (Absolute) Sector Numbers
\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
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
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
45 MMURTL Logical Block Address (LBA)
\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
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
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
68 MMURTL File System Initialization
\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
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
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
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
102 #define U32 unsigned long
\r
103 #define U16 unsigned int
\r
104 #define U8 unsigned char
\r
111 /*********** MMURTL Public Prototypes ***************/
\r
115 extern far AllocExch(long *pExchRet);
\r
116 extern far U32 GetTSSExch(U32 *pExchRet);
\r
117 extern far SpawnTask(char *pEntry,
\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
137 extern far long Respond(long dRqHndl, long dStatRet);
\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
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
150 extern far long TTYOut (char *pTextOut, long ddTextOut, long ddAttrib);
\r
151 extern far long GetNormVid(long *pNormVidRet);
\r
156 extern far U32 DeviceOp(U32 dDevice,
\r
162 extern far U32 DeviceStat(U32 dDevice,
\r
167 extern far U32 DeviceInit(U32 dDevNum,
\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
176 extern far U32 GetPath(long JobNum, char *pPathRet, long *pdcbPathRet);
\r
177 extern far U32 RegisterSvc(S8 *pName, U32 Exch);
\r
179 /* NEAR support for debugging */
\r
181 extern long xprintf(char *fmt, ...);
\r
182 extern U32 Dump(unsigned char *pb, long cb);
\r
184 /* File System error codes */
\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
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
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
218 #define ErcNewMedia 605 /* for floppy mounting from FDD */
\r
220 /**************** FAT Buffer control structure **********************/
\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
237 #define nFATBufs 17 /* 1 Static for floppies + 16 * 512 = 8192, 2 pages */
\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
248 static struct fattype Fat[nFATBufs]; /* 16 bytes * 17 */
\r
250 /* We read 3 sectors worth of floppy fat buf in cause cluster
\r
251 entries span sectors
\r
254 U8 FatBufA[1536]; /* floppy fat buffer */
\r
256 #define FATMOD 0x01
\r
257 #define FATLOCK 0x02
\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
268 static struct FCB {
\r
269 S8 Name[8]; /* From here to Filesize is copy of DirEnt */
\r
271 S8 Attr; /* from MS-DOS */
\r
272 U8 Resvd1[10]; /* ???????? */
\r
273 U16 Time; /* Only changed when created or updated */
\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
286 static struct FCB *paFCB; /* a pointer to array of allocated FCBs. */
\r
287 static struct FCB *pFCB; /* pointer to one FCB */
\r
289 /********************** File User Blocks **************************/
\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
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
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
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
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
330 /* Boot sector info (62 byte structure) */
\r
351 U8 FileSysType[8]; /* 62 bytes */
\r
353 static struct fsbtype fsb;
\r
356 /* Partition Table Entry info. 16 bytes */
\r
370 static struct partent partab[4]; /* 4 partition table entries 64 bytes */
\r
371 static U16 partsig;
\r
373 /* Bit definitions in attribute field for a directory entry */
\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
383 /* Directory Entry Record, 32 bytes */
\r
396 static struct dirstruct dirent;
\r
398 static struct dirstruct *pDirEnt; /* a pointer to a dir entry */
\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
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
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
425 /* raw sector buffer for all kinds of stuff */
\r
427 static U8 abRawSector[516];
\r
428 static U8 abTmpSector[516];
\r
429 static U8 abDirSectBuf[516];
\r
431 /* These arrays keep track of physical drive data (0-4). */
\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
442 static struct phydrv PDrvs[nPDrvs];
\r
444 /* This array of structures keeps track of logical drive data (A-J). */
\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
464 static struct ldrvtype Ldrv[nLDrvs];
\r
466 /* This is the Hard Disk Device Status record.
\r
467 It is peculiar to the HD Drvr */
\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
484 U8 fIntOnReset; /* Interrupt was received on HDC_RESET */
\r
490 U8 ResetStatByte; /* Status Byte immediately after RESET */
\r
492 U32 resvd1[2]; /* out to 64 bytes */
\r
495 static struct hddevtype HDDevStat;
\r
497 /* This is the Floppy Device Status record.
\r
498 It is peculiar to the FD Drvr */
\r
501 U32 erc; /* Last Error from device */
\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
514 U32 resvd4; /* 64 bytes total */
\r
517 static struct fdstattype FDDevStat;
\r
519 static long FSysStack[512]; /* 2048 byte stack for Fsys task */
\r
521 static long FSysExch;
\r
523 struct reqtype { /* 64 byte request block structure */
\r
544 static struct reqtype *pRQB;
\r
546 static char *fsysname = "FILESYSM";
\r
548 static unsigned long keycode; /* for testing */
\r
550 /*========================== BEGIN CODE ============================*/
\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
560 static void GetBSInfo(U32 d, U32 i)
\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
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
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
581 static U32 StatFloppy(U8 ld)
\r
585 /* Set gets status for the floppy type from the FDD and
\r
586 sets logical paramters for Ldrvs.
\r
589 Ldrv[0].DevNum= 10; /* Device Numbers for floppies */
\r
590 Ldrv[1].DevNum= 11;
\r
592 erc = DeviceStat(ld+10, &FDDevStat, 64, &i);
\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
600 Ldrv[ld].nHeads = FDDevStat.nHead;
\r
601 Ldrv[ld].nSecPerTrk = FDDevStat.nSectors;
\r
606 Ldrv[ld].DevNum = 0xff;
\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
621 static U32 read_PE(void)
\r
623 U32 erc, ercD12, ercD13, i, j;
\r
624 U8 fFound1, fFound2;
\r
626 fFound1 = 0; /* Have we found first valid partition on drive */
\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
633 for (i=2; i< nLDrvs; i++)
\r
634 { /* default to no logical hard drives */
\r
635 Ldrv[i].DevNum = 0xff;
\r
638 i = 2; /* first Logical Number for hard drives "C" */
\r
640 for (j=2; j<4; j++)
\r
641 { /* Array index Numbers for 2 physical hard Disks */
\r
643 erc = DeviceOp(j+10, 1, 0, 1, abRawSector); /* add 10 for Disk device nums */
\r
644 if (j==2) ercD12 = erc;
\r
649 CopyData(&abRawSector[0x01fe], &partsig, 2);
\r
651 /* It MUST have a partition table or we can't use it! */
\r
653 if (partsig != 0xAA55) return ErcNoParTable;
\r
655 CopyData(&abRawSector[0x01be], &partab[0].fBootable, 64);
\r
658 Dump(&partab[0].fBootable, 64);
\r
659 ReadKbd(&keycode, 1);
\r
662 if (partab[0].nSectorsTotal > 0)
\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
676 if (partab[1].nSectorsTotal > 0)
\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
688 if (partab[2].nSectorsTotal > 0)
\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
700 if (partab[3].nSectorsTotal > 0)
\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
712 if ((j==3) && (!fFound2))
\r
717 i++; /* if we had a valid partition go to next */
\r
722 if (ercD12) return ercD12; /* there may be no Device 13 */
\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
735 static U32 SetDriveGeometry(U32 d) /* d is the device number (12 or 13) */
\r
741 erc = DeviceStat(12, &HDDevStat, 64, &i);
\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
752 erc = DeviceStat(13, &HDDevStat, 64, &i);
\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
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
770 static U32 read_BS(U32 i)
\r
774 if (Ldrv[i].DevNum != 0xff)
\r
777 j = Ldrv[i].DevNum; /* j is MMURTL Device number */
\r
779 erc = DeviceOp(j, 1, Ldrv[i].LBA0, 1, abRawSector);
\r
781 if ((erc==ErcNewMedia) && (i<2))
\r
783 erc = DeviceOp(j, 1, Ldrv[i].LBA0, 1, abRawSector);
\r
786 CopyData(abRawSector, &fsb.Jmp, 62);
\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
804 } /* if valid logical device */
\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
816 U16 DDate, DTime, w;
\r
818 GetCMOSDate(&date);
\r
819 GetCMOSTime(&time);
\r
821 DDate = (((date >> 12) & 0x0f) * 10) + ((date >> 8) & 0x0f); /* day */
\r
822 w = (((date >> 20) & 0x0f) * 10) + ((date>>16) & 0x0f) + 2; /* month */
\r
824 w = (((date >> 28) & 0x0f) * 10) + ((date >> 24) & 0x0f); /* year */
\r
825 DDate |= (w + 1900 - 1980) << 9;
\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
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
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
851 /* update time in dir entry */
\r
852 GetFATTime(&paFCB[iFCB].Time, &paFCB[iFCB].Date);
\r
854 /* Read sector into a buffer */
\r
855 erc = DeviceOp(Ldrv[Drive].DevNum, 1, i, 1, abDirSectBuf);
\r
859 CopyData(&paFCB[iFCB], &abDirSectBuf[j], 32);
\r
860 erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, 1, abDirSectBuf);
\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
874 /* get some checks out of the way first */
\r
876 if (dHandle < 4) return ErcBadFileHandle;
\r
877 if (dHandle >= nFUBs) return ErcBadFileHandle;
\r
878 if (!paFUB[dHandle].Job) return ErcBadFileHandle;
\r
880 /* Looks like a valid handle */
\r
881 *iFCBRet = paFUB[dHandle]->iFCB;
\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
895 static U32 ClsToLBA(U16 Clstr, U8 Drive)
\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
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
911 ********************************************************/
\r
912 static U32 UpdateFAT(U32 iFAT)
\r
918 if (Fat[iFAT].fModLock & FATMOD)
\r
921 Drive = Fat[iFAT].Drive; /* What logical drive are we on? */
\r
922 i = Fat[iFAT].LBASect; /* Where to write it back */
\r
925 { /* This is the floppy buffer [0] */
\r
926 /* set up to write upto 3 sectors from the buffer */
\r
928 if (i+2 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)
\r
930 else if (i+1 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)
\r
938 erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, k, Fat[iFAT].pBuf);
\r
941 Fat[iFAT].fModLock &= ~FATMOD; /* Not modified anymore */
\r
942 if (Ldrv[Drive].nFATS > 1)
\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
948 i+= Ldrv[Drive].sFAT;
\r
949 erc = DeviceOp(Ldrv[Drive].DevNum, 2, i, k, Fat[iFAT].pBuf);
\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
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
982 static U32 FindFatSect(U8 Drive, U16 Clstr, U32 *piFatRecRet, U8 fLock)
\r
985 U32 first, oSector, erc, LRU, iLRU, iFound, Tick;
\r
988 if (Ldrv[Drive].fFAT16)
\r
991 MaxClstr = 0xff8; /* FAT12 */
\r
993 if (Clstr >= MaxClstr)
\r
998 return (ErcBadFATClstr);
\r
1001 GetTimerTick(&Tick);
\r
1003 erc = 0; /* default to no error */
\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
1010 if (Ldrv[Drive].fFAT16)
\r
1012 oSector = Clstr/256;
\r
1013 first = Clstr-(Clstr%256);
\r
1015 /* Set i to LBA of FAT sector we need by adding
\r
1016 offset to beginning of FAT
\r
1019 i = oSector + Ldrv[Drive].LBAFAT;
\r
1021 /* If FAT sector is out of range there's a BAD problem... */
\r
1023 if (i >= Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)
\r
1025 return (ErcBadFATClstr);
\r
1028 { /* Else we get it for them */
\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
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
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
1047 Fat[j].fModLock |= FATLOCK;
\r
1048 break; /* Already IN! */
\r
1051 if (Fat[j].LastUsed < LRU)
\r
1053 LRU = Fat[j].LastUsed;
\r
1058 if (iFound != 0xffffffff)
\r
1059 { /* Its already in memory */
\r
1060 Fat[j].LastUsed = Tick; /* update LRU */
\r
1063 { /* else put into oldest buffer */
\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
1071 if (Fat[j].fModLock & FATMOD)
\r
1072 erc = UpdateFAT(j);
\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
1086 /* This is for FAT12s */
\r
1090 oSector = (Clstr/1024) * 3; /* X3 cause we read 3 at a time */
\r
1091 first = Clstr-(Clstr%1024);
\r
1093 /* Set i to LBA of FAT sector we need by adding offset (oSector)
\r
1094 to beginning of FAT */
\r
1096 i = oSector + Ldrv[Drive].LBAFAT;
\r
1099 /* If FAT sector is out of range there's a BAD problem... */
\r
1101 if (i >= Ldrv[Drive].sFAT)
\r
1102 return (ErcBadFATClstr);
\r
1104 { /* Else we get it for them */
\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
1109 iFound = 0xffffffff;
\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
1118 Fat[0].fModLock |= FATLOCK;
\r
1122 if (iFound == 0xffffffff)
\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
1130 if (Fat[0].fModLock & FATMOD)
\r
1131 erc = UpdateFAT(0);
\r
1133 /* set up to read upto 3 sectors into buffer */
\r
1135 if (i+2 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)
\r
1137 else if (i+1 < Ldrv[Drive].sFAT + Ldrv[Drive].LBAFAT)
\r
1144 erc = DeviceOp(Ldrv[Drive].DevNum, 1, i, k, Fat[0].pBuf);
\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
1156 *piFatRecRet = j; /* Buffer that holds the sector(s) */
\r
1158 return (erc); /* Disk error Bad news */
\r
1161 /*********************************************
\r
1162 Returns the value found for this cluster
\r
1163 entry in a fat sector buffer. Values can be:
\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
1172 static U32 GetClstrValue(U16 Clstr, U8 Drive, U8 fLock,
\r
1173 U16 *pValRet, U32 *iFatBufRet)
\r
1175 U32 erc, oClstr, iFat;
\r
1176 U16 ClstrVal, *pClstr;
\r
1178 erc = FindFatSect(Drive, Clstr, &iFat, fLock);
\r
1186 pClstr = Fat[iFat].pBuf;
\r
1187 oClstr = Clstr - Fat[iFat].iClstrStart; /* offset into FatBuf */
\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
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
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
1205 ClstrVal &= 0xfff;
\r
1207 *pValRet= ClstrVal;
\r
1208 *iFatBufRet = iFat;
\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
1227 static U32 SetClstrValue(U16 Clstr, U16 NewClstrVal, U8 Drive, U32 *iFatBufRet)
\r
1229 U32 erc, oClstr, iFat;
\r
1230 U16 ClstrVal, *pClstr, ClstrSave;
\r
1232 erc = FindFatSect(Drive, Clstr, &iFat, 0);
\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
1246 /* FAT12 entries are 1.5 bytes long (remember??).
\r
1247 SAVE THE CORRECT NIBBLE OF THE ADJACENT CLUSTER!!
\r
1250 { /* a FAT12... */
\r
1251 pClstr += oClstr + (oClstr/2); /* 1.5 bytes in */
\r
1252 ClstrSave = *pClstr; /* We have 16 bits */
\r
1254 { /* Odd, must shift */
\r
1255 NewClstrVal <<= 4;
\r
1256 NewClstrVal &= 0xfff0;
\r
1257 ClstrVal = (ClstrSave & 0x0F) | NewClstrVal;
\r
1261 NewClstrVal &= 0x0fff;
\r
1262 ClstrVal = (ClstrSave & 0xf000) | NewClstrVal;
\r
1264 *pClstr = ClstrVal;
\r
1266 Fat[iFat].fModLock |= FATMOD;
\r
1267 *iFatBufRet = iFat;
\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
1281 static U32 NextFATClstr(U8 Drive, U16 Clstr, U16 *pNextClstrRet)
\r
1286 erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);
\r
1290 *pNextClstrRet = 0;
\r
1293 *pNextClstrRet = NextClstr;
\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
1312 static U32 ExtendClstrChain(U8 Drive, U16 LastClstr, U16 *pNextClstrRet)
\r
1315 U16 ClstrValue, MaxClstr, CrntClstr;
\r
1318 if (Ldrv[Drive].fFAT16)
\r
1319 MaxClstr = 0xfff8;
\r
1321 MaxClstr = 0xff8; /* FAT12 */
\r
1323 /* i is index to Fat with last sector of current chain */
\r
1325 erc = GetClstrValue(LastClstr, Drive, 1, &ClstrValue, &i);
\r
1328 *pNextClstrRet = 0;
\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
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
1344 CrntClstr = LastClstr;
\r
1348 ++CrntClstr; /* next cluster */
\r
1349 erc = GetClstrValue(CrntClstr, Drive, 0, &ClstrValue, &j);
\r
1352 *pNextClstrRet = 0;
\r
1353 Fat[i].fModLock &= ~FATLOCK; /* unlock previous lastclstr */
\r
1358 fFound = 1; /* found an empty one */
\r
1363 { /* CrntClstr is index to empty one */
\r
1365 /* Set the LastCluster to point to the new cluster found */
\r
1367 erc = SetClstrValue(LastClstr, CrntClstr, Drive, &k);
\r
1370 *pNextClstrRet = 0;
\r
1371 Fat[i].fModLock &= ~FATLOCK; /* unlock previous lastclstr */
\r
1374 Fat[k].fModLock &= ~FATLOCK; /* unlock it */
\r
1376 /* Set the newcluster to "end Cluster" chain value */
\r
1378 erc = SetClstrValue(CrntClstr, 0xFFFF, Drive, &j);
\r
1380 *pNextClstrRet = CrntClstr;
\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
1392 static U32 TruncClstrChain(U8 Drive, U16 Clstr)
\r
1395 U16 MaxClstr, NextClstr, CrntClstr;
\r
1397 if (Ldrv[Drive].fFAT16)
\r
1398 MaxClstr = 0xfff8;
\r
1400 MaxClstr = 0xff8; /* FAT12 */
\r
1402 /* i will be index to FatRec with last sector of current chain */
\r
1404 erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);
\r
1408 if (NextClstr >= MaxClstr)
\r
1409 { /* no need to truncate it */
\r
1410 return(0); /* It's already the end. */
\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
1418 erc = GetClstrValue(Clstr, Drive, 0, &NextClstr, &i);
\r
1421 erc = SetClstrValue(Clstr, 0xFFFF, Drive, &i); /* new end of chain */
\r
1425 while ((NextClstr) && (NextClstr < MaxClstr))
\r
1427 CrntClstr = NextClstr;
\r
1428 erc = GetClstrValue(CrntClstr, Drive, 0, &NextClstr, &i);
\r
1431 erc = SetClstrValue(CrntClstr, 0, Drive, &i); /* Free it up */
\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
1447 static U32 GetAbsoluteClstr(U32 dHandle, U32 dLFA,
\r
1448 U16 *pClstrRet, U32 *prLFARet)
\r
1450 U32 erc, iFCB, spc, bpc, rLFA;
\r
1451 U16 rClstrWant, rClstrNow, Clstr, MaxClstr;
\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
1459 if (Ldrv[Drive].fFAT16)
\r
1460 MaxClstr = 0xfff8;
\r
1462 MaxClstr = 0xff8; /* FAT12 */
\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
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
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
1481 rClstrWant = dLFA / bpc; /* Relative clstr they want */
\r
1482 rClstrNow = paFUB[dHandle]->LFAClstr / bpc; /* Rel 'Clstr' in FUB */
\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
1492 Clstr = paFUB[dHandle]->Clstr; /* No, start at current cluster */
\r
1493 rLFA = paFUB[dHandle]->LFAClstr; /* LFA of this cluster */
\r
1496 /* We need to run the cluster chain if rClstrNow < ClstrWant */
\r
1498 while ((rClstrNow < rClstrWant) && /* haven't reach it yet */
\r
1499 (Clstr < MaxClstr) && /* Not last cluster */
\r
1501 { /* A valid cluster */
\r
1502 erc = NextFATClstr(Drive, Clstr, &Clstr);
\r
1509 if (rClstrNow != rClstrWant) /* Cluster chain appears broken... */
\r
1510 return ErcBrokenFile;
\r
1512 *pClstrRet = Clstr;
\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
1527 static U32 SetFileSizeM(U32 dHandle, U32 dSize)
\r
1529 U32 erc, i, iFCB, rLFA, lfaEOF;
\r
1530 U32 CrntSize, nCrntClstrs, spc, bpc, nClstrsWant;
\r
1534 erc = ValidateHandle(dHandle, &iFCB);
\r
1537 if (!paFCB[iFCB]->Mode)
\r
1538 return ErcReadOnly;
\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
1544 /* Looks like it's valid to change the size */
\r
1546 CrntSize = paFCB[iFCB]->FileSize;
\r
1548 lfaEOF = CrntSize - 1;
\r
1552 if (CrntSize == dSize) /* No need to do anything! */
\r
1555 nCrntClstrs = CrntSize/bpc; /* nClusters currently */
\r
1560 nCrntClstrs = 1; /* ZERO length files have 1 Clstr! */
\r
1562 nClstrsWant = dSize/bpc; /* nClusters they we need */
\r
1567 nClstrsWant = 1; /* ZERO length files have 1 Clstr! */
\r
1570 if (nClstrsWant == nCrntClstrs)
\r
1573 else if (nClstrsWant > nCrntClstrs)
\r
1574 { /* Need to extend allocation */
\r
1576 /* get the last cluster in the file */
\r
1577 erc = GetAbsoluteClstr(dHandle, lfaEOF, &Clstr, &rLFA);
\r
1579 while ((!erc) && (i < nClstrsWant))
\r
1581 erc = ExtendClstrChain(Drive, Clstr, &Clstr);
\r
1585 else if (nClstrsWant < nCrntClstrs)
\r
1586 { /* Need to truncate it */
\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
1592 lfaEOF = dSize - 1;
\r
1596 erc = GetAbsoluteClstr(dHandle, lfaEOF, &Clstr, &rLFA);
\r
1598 erc = TruncClstrChain(Drive, Clstr);
\r
1600 /* Now we must ensure that the cluster helper is NOT
\r
1603 if (paFUB->LFAClstr >= dSize)
\r
1605 paFUB->LFAClstr = 0;
\r
1606 paFUB[dHandle]->Clstr = paFCB[iFCB]->StartClstr;
\r
1612 paFCB[iFCB]->FileSize = dSize;
\r
1613 paFCB[iFCB]->fMod = 1;
\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
1628 static U32 GetDirEnt(U8 *pName,
\r
1635 unsigned long sector, i, j, k, erc;
\r
1636 U8 fFound, fEnd, *pEnt, *pStart;
\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
1644 MaxClstr = 0xff8; /* FAT12 */
\r
1650 while ((!fFound) && (!fEnd))
\r
1651 { /* while there are valid entries */
\r
1653 { /* reached last dir sector of this cluster */
\r
1654 erc = NextFATClstr(Drive, Clstr, &Clstr);
\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
1669 erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector++, 1, abDirSectBuf);
\r
1673 ++i; /* next sector in cluster */
\r
1675 pEnt = &abDirSectBuf[0];
\r
1678 for (k=0; k<16; k++)
\r
1679 { /* 16 entries per sector */
\r
1681 { /* 0 in a DirEnt stops search */
\r
1686 if (CompareNCS(pEnt, pName, 11) == -1)
\r
1689 *pLBARet = sector-1; /* tell em what LBA of DirEnt */
\r
1690 *poEntRet = pEnt-pStart; /* Tell em offset in LBA */
\r
1693 pEnt+=32; /* 32 byte per entry */
\r
1701 else return (ErcNoSuchFile);
\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
1713 static U32 GetRootEnt(U8 *pName,
\r
1719 unsigned long i, j, k, erc;
\r
1720 U8 fFound, fEnd, *pEnt, *pStart;
\r
1722 i = Ldrv[Drive].LBARoot;
\r
1723 j = Ldrv[Drive].nRootDirEnt;
\r
1727 while ((j) && (!fFound) && (!fEnd))
\r
1728 { /* while there are valid entries */
\r
1730 erc = DeviceOp(Ldrv[Drive].DevNum, 1, i++, 1, abRawSector);
\r
1735 pEnt = abRawSector;
\r
1737 for (k=0; k<16; k++)
\r
1740 { /* 0 in a DirEnt stops search */
\r
1744 if (CompareNCS(pEnt, pName, 11) == -1)
\r
1747 *pLBARet = i-1; /* tell em what LBA of DirEnt */
\r
1748 *poEntRet = pEnt-pStart; /* Tell em offset in LBA */
\r
1751 --j; /* one less dir ent */
\r
1752 pEnt+=32; /* 32 byte per entry */
\r
1761 return (ErcNoSuchFile);
\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
1775 static void BuildSpec(char *pName,
\r
1784 if ((cbName) && (pName) && (pName[1] == ':'))
\r
1785 { /* Do NOT use path */
\r
1786 CopyData(pName, pDest, cbName);
\r
1789 /* use only drive and semicolon */
\r
1790 else if ((cbName) && (pName) && (pName[0] == 0x5C)) /* begins with backslash */
\r
1792 GetPath(iJob, pathtmp, &i);
\r
1793 pDest[0] = pathtmp[0];
\r
1794 pDest[1] = pathtmp[1];
\r
1796 CopyData(pName, &pDest[2], cbName);
\r
1800 { /* Use whole path as prefix */
\r
1802 GetPath(iJob, pDest, &i);
\r
1803 if ((cbName) && (pName))
\r
1805 if ((pName) && (cbName))
\r
1807 CopyData(pName, &pDest[i], cbName);
\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
1823 SpecDepth is set to the level of the last valid
\r
1824 11 character string (Specdepth is 0-6).
\r
1825 *****************************************************/
\r
1827 static U32 ParseName(U8 *pName, U32 cbName, U32 iJob)
\r
1829 unsigned long i, j, k, erc;
\r
1837 FillData(FileSpec, (7*11), ' '); /* Fill parse table with spaces */
\r
1839 if ((cbName) && (*pName == ' '))
\r
1840 erc = ErcBadFileSpec;
\r
1842 BuildSpec(pName, cbName, Spec, &cbSpec, iJob);
\r
1844 j = 0; /* index into crnt part of spec */
\r
1845 k = 0; /* index into crnt tree level */
\r
1847 for (i=0; i < cbSpec; i++)
\r
1849 switch (c = *pPart++)
\r
1851 case 0x5c : /* '\' separates dir or fname */
\r
1853 { /* if it's not the first one */
\r
1859 if ((j==1) && (k==0) && (FDrive==0))
\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
1866 else erc = ErcBadFileSpec;
\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
1872 case '>' : /* not allowed in spec */
\r
1884 erc = ErcBadFileSpec;
\r
1886 default : /* make chars upper */
\r
1888 erc = ErcBadFileSpec;
\r
1891 if (((c >= 'A') && (c <= 'Z')) ||
\r
1892 ((c >= 'a') && (c <= 'z')))
\r
1894 FileSpec[k][j] = c;
\r
1900 if (erc) break; /* bad news. Exit for loop */
\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
1914 static U32 GetDirSectorM(char *pPath,
\r
1923 U32 sector, i, j, k, erc, spc, level, iSect;
\r
1924 U16 MaxClstr, Clstr, rClstr;
\r
1925 U8 fFound, *pEnt, Drive;
\r
1927 if (cbRetMax > 512) /* WHOA Bub, 1 Sector at a time! */
\r
1930 erc = ParseName(pPath, cbPath, iJob);
\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
1939 if ((FDrive > 0x40) && (FDrive < 0x52)) /* A to J */
\r
1940 Drive = FDrive - 0x41; /* Make it 0-9 */
\r
1942 return(ErcNoSuchDrive);
\r
1946 StatFloppy(Drive);
\r
1947 erc= read_BS(Drive);
\r
1950 if (Ldrv[Drive].DevNum == 0xff)
\r
1951 return(ErcNoSuchDrive);
\r
1953 i = Ldrv[Drive].LBARoot;
\r
1954 j = Ldrv[Drive].nRootDirEnt;
\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
1961 /* Else we can give them the sector NOW */
\r
1962 erc = DeviceOp(Ldrv[Drive].DevNum, 1, i+SectNum, 1, abRawSector);
\r
1965 *LBARet = i+SectNum;
\r
1966 CopyData(abRawSector, pSectRet, cbRetMax);
\r
1971 /* We have to run the root for a dir name... */
\r
1974 while ((j) && (!fFound))
\r
1975 { /* while there are valid entries */
\r
1976 erc = DeviceOp(Ldrv[Drive].DevNum, 1, i++, 1, abRawSector);
\r
1979 pEnt = abRawSector; /* Point to first entry */
\r
1980 for (k=0; k<16; k++)
\r
1982 if (CompareNCS(pEnt, FileSpec[0], 11) == -1)
\r
1987 --j; /* one less dir ent */
\r
1988 pEnt+=32; /* 32 byte per entry */
\r
1992 return (ErcNoMatch);
\r
1994 pDirEnt = pEnt; /* Entry we just found in root was dir */
\r
1996 if (!(pDirEnt->Attr & DIRECTORY))
\r
1998 return(ErcNoSuchDir);
\r
2001 if (Ldrv[Drive].fFAT16)
\r
2002 MaxClstr = 0xfff8;
\r
2004 MaxClstr = 0xff8; /* FAT12 */
\r
2006 spc = Ldrv[Drive].SecPerClstr; /* How many sectors per cluster */
\r
2007 Clstr = pDirEnt->StartClstr;
\r
2009 level = 1; /* start at this directory+1, compare to FileSpec */
\r
2012 { /* looking for Dir */
\r
2014 if (FileSpec[level][0] == ' ')
\r
2015 { /* They want sector in this dir */
\r
2017 if (!(pDirEnt->Attr & DIRECTORY))
\r
2019 return(ErcNoSuchDir);
\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
2028 sector = ClsToLBA(Clstr, Drive); /* LBA of this clstr */
\r
2030 erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector, 1, abRawSector);
\r
2033 CopyData(abRawSector, pSectRet, cbRetMax);
\r
2035 *ClstrRet = Clstr;
\r
2040 { /* Else we must find this sub dir name */
\r
2042 sector = ClsToLBA(Clstr, Drive); /* sector of first dir sector */
\r
2047 { /* while there are valid entries */
\r
2050 { /* reached last dir sector of this cluster */
\r
2051 erc = NextFATClstr(Drive, Clstr, &Clstr);
\r
2054 if (Clstr >= MaxClstr) /* last sector */
\r
2055 return(ErcNoSuchFile); /* not found */
\r
2056 sector = ClsToLBA(Clstr, Drive); /* LBA of next sector */
\r
2063 erc = DeviceOp(Ldrv[Drive].DevNum, 1, sector++, 1, abRawSector);
\r
2066 i++; /* Next sector in this cluster */
\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
2076 pEnt+=32; /* 32 byte per entry */
\r
2079 pDirEnt = pEnt; /* Entry we just found */
\r
2080 Clstr = pDirEnt->StartClstr; /* Clstr @ start of dir entry */
\r
2082 ++level; /* next level of parsed filespec */
\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
2096 static U32 ReadBlockM(U32 dHandle,
\r
2101 U8 fFill) /* TRUE if filling a stream buffer */
\r
2103 U32 erc, j, LBA, iFCB, bpc, spc, nDone, rLFA, nLeft, nBlks;
\r
2104 U16 Clstr, MaxClstr, ClstrSav;
\r
2107 erc = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */
\r
2108 if (erc) return erc;
\r
2110 /* Certain FUB fields have different meanings in stream */
\r
2112 if ((paFUB[dHandle].fStream) && (!fFill))
\r
2115 return ErcStreamFile;
\r
2118 /* set these up in advance */
\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
2127 MaxClstr = 0xff8; /* FAT12 */
\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
2134 erc = GetAbsoluteClstr(dHandle, dLFA, &Clstr, &rLFA);
\r
2136 LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster */
\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
2146 We also set nleft which is how many sectors are left in the
\r
2147 current cluster we are reading from.
\r
2150 LBA += (dLFA/512) % spc;
\r
2151 nLeft = spc - ((dLFA/512) % spc);
\r
2154 while ((nBlks) && (!erc))
\r
2155 { /* while buffer isn't full and no error */
\r
2156 if (nBlks > nLeft)
\r
2160 paFUB[dHandle]->Clstr = Clstr; /* Save Current cluster */
\r
2161 paFUB[dHandle]->LFAClstr = rLFA; /* Save LFA for Clstr in FUB */
\r
2163 erc = DeviceOp(Ldrv[Drive].DevNum, 1, LBA, j, pBytesRet);
\r
2166 pBytesRet += j * 512; /* further into their buffer */
\r
2171 if ((nBlks) && (!nLeft))
\r
2172 { /* current cluster has none left */
\r
2175 erc = NextFATClstr(Drive, Clstr, &Clstr); /* next FAT cluster */
\r
2178 *pdBytesRet = nDone*512;
\r
2181 rLFA += bpc; /* Update rel LFA of new cluster*/
\r
2182 if (Clstr >= MaxClstr)
\r
2183 erc = ErcEOF; /* Last cluster */
\r
2185 erc = ErcBrokenFile; /* No good next cluster! */
\r
2186 LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster*/
\r
2189 *pdBytesRet = nDone*512;
\r
2191 return erc; /* WE'RE DONE, return the error (if any) */
\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
2205 static U32 WriteBlockM(U32 dHandle, char *pData, U32 nBytes,
\r
2206 U32 dLFA, U32 *pdnBytesRet)
\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
2213 erc = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */
\r
2214 if (erc) return erc;
\r
2216 nBlks = nBytes/512;
\r
2217 dLFA = (dLFA/512)*512; /* round LFA down to nearest sector */
\r
2219 if (!paFCB[iFCB]->Mode) /* Is it open in Modify?? */
\r
2220 return(ErcReadOnly);
\r
2222 i = (paFCB[iFCB]->FileSize/512); /* Set i nBlks in file max */
\r
2223 if (paFCB[iFCB]->FileSize%512)
\r
2226 j = (dLFA/512) + nBlks; /* blocks to write past dLFA*/
\r
2229 return(ErcBeyondEOF);
\r
2231 /* It seems OK to write the blocks out, so now let's DO IT! */
\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
2239 MaxClstr = 0xff8; /* FAT12 */
\r
2241 erc = GetAbsoluteClstr(dHandle, dLFA, &Clstr, &rLFA);
\r
2245 LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster */
\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
2259 LBA += (dLFA/512) % spc;
\r
2261 nLeft = spc - ((dLFA/512) % spc);
\r
2266 while ((nBlks) && (!erc))
\r
2267 { /* while blocks are left to write */
\r
2268 if (nBlks > nLeft)
\r
2272 paFUB[dHandle]->Clstr = Clstr; /* Save Current cluster */
\r
2273 paFUB[dHandle]->LFAClstr = rLFA; /* Save LFA for Clstr in FUB */
\r
2275 erc = DeviceOp(Ldrv[Drive].DevNum, 2, LBA, j, pData);
\r
2278 pData += (j * 512); /* Update address */
\r
2279 nDone += j; /* Total blocks done so far */
\r
2283 if ((nBlks) && (!nLeft))
\r
2284 { /* done with current cluster */
\r
2286 erc = NextFATClstr(Drive, Clstr, &Clstr); /* next FAT cluster */
\r
2289 rLFA += bpc; /* Update rel LFA of new cluster*/
\r
2290 if ((Clstr >= MaxClstr) && (nBlks)) /* Problem! */
\r
2292 erc = ErcBeyondEOF; /* Last cluster & they want more*/
\r
2294 if (!Clstr) erc = ErcBrokenFile; /* No good next cluster! */
\r
2295 LBA = ClsToLBA(Clstr, Drive); /* Get LBA of the target cluster*/
\r
2298 *pdnBytesRet = nDone * 512;
\r
2299 return erc; /* WE'RE DONE, return the error (if any) */
\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
2309 **********************************************************/
\r
2311 static U32 FillStreamBuff(U32 dHandle, U8 fInitial)
\r
2313 U32 erc, i, LFA, cLFA, LFABuf, iFCB;
\r
2319 /* Set these up in advance */
\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
2327 /* If the file was just opened we fill with LFA 0 */
\r
2331 erc = ReadBlockM(dHandle, pBuf, sBuf, 0, &i, TRUE);
\r
2332 paFUB[dHandle]->LFABuf = 0;
\r
2335 /* Else If the LFA is already in the buffer we just exit OK */
\r
2337 else if ((cLFA >= LFABuf) && (cLFA < (LFABuf + sBuf)))
\r
2342 /* ELSE We must figure out what starting LFA we want and fill the buffer */
\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
2351 if (erc == ErcEOF) /* We ignore this when filling the buffer */
\r
2353 return erc; /* WE'RE DONE, return the error (if any, except EOF) */
\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
2364 static U32 ReadBytesM(U32 dHandle, U8 *pBytesRet, U32 nBytes, U32 *pdReadRet)
\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
2372 erc = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */
\r
2373 if (erc) return erc;
\r
2375 /* Certain FUB fields have different meanings in stream type file */
\r
2377 if (!paFUB[dHandle]->fStream)
\r
2378 return ErcBlockFile;
\r
2380 cLFA = paFUB[dHandle]->CrntLFA; /* local cLFA */
\r
2381 fSize = paFCB[iFCB]->FileSize; /* local size */
\r
2383 /* check and see if we are at EOF */
\r
2385 if (cLFA >= fSize)
\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
2396 pBuf = paFUB[dHandle]->pBuf; /* Local ptr to buffer/size */
\r
2397 sBuf = paFUB[dHandle]->sBuf;
\r
2400 while ((nBytesDone < nBytes) &&
\r
2404 erc = FillStreamBuff(dHandle, 0); /* Fill the buff */
\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
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
2415 /* Calculate pointer to the next chunk out from stream buffer */
\r
2417 pOut = pBuf + (cLFA - paFUB[dHandle]->LFABuf);
\r
2419 /* Calc how many bytes we can get out of buffer (belongs to file) */
\r
2421 nBytesOut = lfaEOB - cLFA + 1;
\r
2422 if (nBytesOut > nBytes-nBytesDone)
\r
2423 nBytesOut = nBytes-nBytesDone;
\r
2425 /* Send bytes to pBytesRet */
\r
2427 CopyData(pOut, pBytesRet, nBytesOut);
\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
2435 *pdReadRet = nBytesDone; /* Tell em how many bytes they got */
\r
2437 if (cLFA == fSize)
\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
2453 static U32 FlushStreamBuff(U32 dHandle)
\r
2455 U32 erc, i, j, LFABuf, iFCB, size;
\r
2461 if (paFUB[dHandle]->fModified)
\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
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
2476 erc = WriteBlockM(dHandle, pBuf, i*512, LFABuf, &i);
\r
2477 paFUB[dHandle]->fModified = 0;
\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
2491 static U32 WriteBytesM(U32 dHandle, char *pData, U32 nBytes, U32 *nBytesRet)
\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
2499 erc = ValidateHandle(dHandle, &iFCB); /* Sets iFCB if OK */
\r
2500 if (erc) return erc;
\r
2502 /* Certain FUB fields have different meanings in stream type file */
\r
2504 if (!paFUB[dHandle]->fStream)
\r
2505 return(ErcBlockFile);
\r
2507 if (!paFCB[iFCB]->Mode) /* Is it open in Modify?? */
\r
2508 return(ErcReadOnly);
\r
2510 /* Set up local vars to values for current stream buffer.
\r
2511 These are in effect for this call on entry!
\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
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
2523 if (cLFA + nBytes > fSize)
\r
2524 { /* Must set file size first */
\r
2525 erc = SetFileSizeM(dHandle, cLFA + nBytes);
\r
2530 fSize = paFCB[iFCB]->FileSize; /* local size */
\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
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
2547 while ((nBytesDone < nBytes) &&
\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
2558 if ((cLFA > lfaEOB) ||
\r
2559 (cLFA < paFUB[dHandle]->LFABuf))
\r
2561 erc = FlushStreamBuff(dHandle);
\r
2566 erc = FillStreamBuff(dHandle, 0); /* Fill the buff */
\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
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
2582 /* Calc pointer to where next chunk goes in stream buffer */
\r
2584 pOut = pBuf + (cLFA - paFUB[dHandle]->LFABuf);
\r
2586 /* Calc how many bytes we can write to buffer. We must
\r
2587 ensure we don't exceed buffer size. */
\r
2589 nBytesOut = (lfaEOB + 1) - cLFA;
\r
2590 if (nBytesOut > nBytes-nBytesDone)
\r
2591 nBytesOut = nBytes-nBytesDone;
\r
2593 /* Get bytes from pData into stream buffer */
\r
2595 CopyData(pData, pOut, nBytesOut);
\r
2596 paFUB[dHandle]->fModified = 1;
\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
2604 *nBytesRet = nBytesDone; /* Tell em how many bytes we wrote */
\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
2616 static U32 GetFileSizeM(U32 dHandle, U32 *pdSizeRet)
\r
2619 erc = ValidateHandle(dHandle, &iFCB);
\r
2620 if (erc) return erc;
\r
2621 *pdSizeRet = paFCB[iFCB]->FileSize;
\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
2633 static U32 SetFileLFAM(U32 dHandle, S32 dLFA)
\r
2637 erc = ValidateHandle(dHandle, &iFCB);
\r
2638 if (erc) return erc;
\r
2640 if (!paFUB[dHandle]->fStream)
\r
2641 return ErcBlockFile;
\r
2643 if (paFCB[iFCB]->Mode) /* Modify mode - Flush will flush if needed */
\r
2644 erc = FlushStreamBuff(dHandle);
\r
2646 /* -1 = Set file ptr to EOF */
\r
2649 dLFA = paFCB[iFCB]->FileSize;
\r
2651 if (dLFA > paFCB[iFCB]->FileSize)
\r
2652 erc = ErcBeyondEOF;
\r
2656 paFUB[dHandle]->CrntLFA = dLFA; /* Set where we are in the file */
\r
2657 erc = FillStreamBuff(dHandle, 0);
\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
2671 static U32 GetFileLFAM(U32 dHandle, U32 *pdLFARet)
\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
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
2693 static U32 OpenFileM(U8 *pName,
\r
2698 U32 iJob) /* make use of iJob later!!! */
\r
2700 U32 erc, level, i, iFCB, iFUB, LBADirEnt, EntOffset;
\r
2702 U8 fFound, *pMem, Drive;
\r
2705 return ErcBadOpenMode;
\r
2707 level = 0; /* start at the root, compare to SpecDepth */
\r
2710 if (((cbName) && (*pName == ' ')) ||
\r
2712 return(ErcBadFileSpec);
\r
2715 erc = ParseName(pName, cbName, iJob);
\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
2723 if ((FDrive > 0x40) && (FDrive < 0x52)) /* A to J */
\r
2724 Drive = FDrive - 0x41; /* Make it 0-9 */
\r
2725 else erc = ErcNoSuchDrive;
\r
2727 if (Ldrv[Drive].DevNum == 0xff)
\r
2728 erc = ErcNoSuchDrive;
\r
2730 if ((Drive < 2) && (!erc))
\r
2732 StatFloppy(Drive);
\r
2733 erc= read_BS(Drive);
\r
2737 { /* Get Root dir entry */
\r
2738 erc = GetRootEnt(FileSpec[level],
\r
2744 if (erc == ErcNoSuchFile)
\r
2746 if (level == SpecDepth) erc = ErcNoSuchFile;
\r
2747 else erc = ErcNoSuchDir;
\r
2753 Clstr = pDirEnt->StartClstr; /* Clstr = beginning of file or dir */
\r
2755 while ((level < SpecDepth) && (!erc))
\r
2756 { /* looking for Dir, not file yet */
\r
2758 ++level; /* next level of parsed filespec */
\r
2760 erc = GetDirEnt(FileSpec[level],
\r
2766 if (erc == ErcNoSuchFile)
\r
2768 if (level == SpecDepth)
\r
2769 erc = ErcNoSuchFile;
\r
2770 else erc = ErcNoSuchDir;
\r
2775 Clstr = pDirEnt->StartClstr; /* Clstr @ start of dir entry */
\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
2786 /* If Attributes say it's not a file then ERROR */
\r
2788 if (pDirEnt->Attr & (VOLNAME | DIRECTORY))
\r
2789 return ErcNotAFile;
\r
2791 /* If ModeModify and File is readOnly then ERROR */
\r
2793 if ((Mode) && (pDirEnt->Attr & READONLY))
\r
2794 return ErcReadOnly;
\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
2804 while ((i<nFCBs) && (!fFound))
\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
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
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
2829 { /* It not already open. Find empty FCB */
\r
2831 while ((i<nFCBs) && (paFCB[i]->nUsers)) ++i; /* Find new FCB */
\r
2833 if (i==nFCBs) return ErcNoFreeFCB; /* Couldn't */
\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
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
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
2856 while ((i<nFUBs) && (paFUB[i]->Job)) ++i; /* Find new FUB */
\r
2859 pFCB->nUsers--; /* Make FCB correct */
\r
2860 return ErcNoFreeFUB; /* Couldn't */
\r
2864 /* If we got here, i is an index to a free FUB. */
\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
2878 { /* allocate/fill buffer and set rest of FUB */
\r
2880 erc = AllocOSPage(1, &pMem); /* Stream Buf is 4K */
\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
2889 paFUB[iFUB]->pBuf = pMem; /* Ptr to buffer if stream mode */
\r
2890 paFUB[iFUB]->sBuf = 4096; /* Size of buffer */
\r
2892 erc = FillStreamBuff(iFUB, 1); /* fInitial to TRUE */
\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
2903 *pdHandleRet = iFUB; /* File handle */
\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
2919 static U32 CloseFileM (U32 dHandle)
\r
2923 erc = ValidateHandle(dHandle, &iFCB);
\r
2924 if (erc) return erc;
\r
2926 if (paFCB[iFCB]->Mode)
\r
2927 { /* Modify mode */
\r
2928 if (paFUB[dHandle]->fStream)
\r
2930 erc = FlushStreamBuff(dHandle);
\r
2932 UpdateDirEnt(iFCB); /* ignore error */
\r
2935 if (paFUB[dHandle]->fStream)
\r
2936 DeAllocPage(paFUB[dHandle]->pBuf, 1); /* Free buffer */
\r
2938 /* This means the FS is screwed up. This shouldn't happen... */
\r
2939 if (!paFCB[iFCB]->nUsers)
\r
2942 paFCB[iFCB]->nUsers--;
\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
2948 paFUB[dHandle]->Job = 0;
\r
2950 /* This will write all modified fat sectors */
\r
2952 for (i=0; i<nFATBufs; i++)
\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
2964 static U32 CreateFileM(char *pName,
\r
2969 unsigned long dHandle, i, j, k, erc, LBA, spc;
\r
2972 char filename[12];
\r
2973 U16 CrntClstr, ClstrValue, iStart, DirClstr;
\r
2974 U8 fFound, Drive, fDir;
\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
2981 if (attrib & DIRECTORY)
\r
2989 erc = OpenFileM(pName, cbName, 0, 0, &dHandle, iJob);
\r
2994 CloseFileM(dHandle);
\r
2997 case ErcFileInUse:
\r
3000 case ErcNotAFile: /* It a directory... */
\r
3002 case ErcNoSuchFile:
\r
3003 { /* OK, this means we can try to create it! */
\r
3007 BuildSpec(pName, cbName, Path, &cbPath, iJob);
\r
3009 erc = ParseName(Path, cbPath, iJob);
\r
3013 /* FDrive was set up on Parse */
\r
3014 Drive = FDrive - 0x41; /* Make it 0-9 */
\r
3016 /* First we setup the filename from what was parsed out
\r
3017 during the Parse call. Then eliminate it from Path.
\r
3020 CopyData(FileSpec[SpecDepth], filename, 11); /* filename */
\r
3024 /* Hack the filename from Path so we can search the
\r
3025 directory path properly */
\r
3027 while ((cbPath) && (Path[cbPath-1] != 0x5C))
\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
3040 i = 0; /* i = sectornum */
\r
3041 while ((!fFound) && (!erc))
\r
3043 erc = GetDirSectorM(Path, cbPath, abTmpSector,
\r
3044 512, i++, &LBA, &DirClstr, iJob);
\r
3048 pDirEnt = abTmpSector;
\r
3051 if ((pDirEnt->Name[0] == 0xE5) ||
\r
3052 (!pDirEnt->Name[0]))
\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
3068 if ((erc == ErcNoMatch) && (!SpecDepth))
\r
3069 return (ErcRootFull);
\r
3071 else if (erc == ErcEOF)
\r
3072 { /* reach end of dir! */
\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
3079 FillData(abTmpSector, 512, 0);
\r
3080 spc = Ldrv[Drive].SecPerClstr; /* sectors per cluster */
\r
3081 erc = ExtendClstrChain(Drive, DirClstr, &DirClstr);
\r
3084 LBA = ClsToLBA(DirClstr, Drive);
\r
3088 while ((i--) && (!erc))
\r
3090 erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,
\r
3093 pDirEnt = abTmpSector; /* first entry in new sector */
\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
3104 if ((!erc) && (fFound))
\r
3105 { /* Let's DO IT! */
\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
3114 /* Find a fat buf already in memory for this drive */
\r
3115 /* One WILL be here! */
\r
3119 while ((k<nFATBufs) && (!CrntClstr))
\r
3121 if ((Drive == Fat[k].Drive) && (Fat[k].LastUsed))
\r
3122 CrntClstr = Fat[k].iClstrStart; /* valid cluster */
\r
3127 CrntClstr = 2; /* Can't find it so start at beginning */
\r
3129 iStart = CrntClstr; /* where we started looking for empties */
\r
3134 ++CrntClstr; /* next cluster */
\r
3135 if (CrntClstr == iStart)
\r
3136 return(ErcDiskFull);
\r
3138 erc = GetClstrValue(CrntClstr, Drive, 0, &ClstrValue, &j);
\r
3140 if ((!erc) && (!ClstrValue))
\r
3141 fFound = 1; /* found an empty one */
\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
3153 return(ErcDiskFull);
\r
3159 /* If we got here, we found an empty cluster */
\r
3161 CopyData(filename, pDirEnt, 11);
\r
3164 attrib & (READONLY | HIDDEN | SYSTEM | ARCHIVE);
\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
3173 erc = DeviceOp(Ldrv[Drive].DevNum, 2, LBA,
\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
3187 FillData(abTmpSector, 512, 0);
\r
3188 pDirEnt = abTmpSector; /* first entry in new sector */
\r
3190 /* do the current dir entry (.) */
\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
3198 /* do the previous current dir entry (.) */
\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
3207 spc = Ldrv[Drive].SecPerClstr; /* sectors per cluster */
\r
3208 LBA = ClsToLBA(CrntClstr, Drive);
\r
3211 /* Write this sector out to disk */
\r
3212 erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,
\r
3215 FillData(abTmpSector, 512, 0); /* zero the rest */
\r
3218 i = spc-1; /* less one cause we wrote the first */
\r
3219 while ((i--) && (!erc))
\r
3221 erc = DeviceOp(Ldrv[Drive].DevNum, 2, j++,
\r
3226 /* This will write all modified fat sectors */
\r
3228 for (i=0; i<nFATBufs; i++)
\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
3250 static U32 DeleteFileM(long *dHandle)
\r
3256 erc = ValidateHandle(dHandle, &iFCB);
\r
3257 if (erc) return erc;
\r
3259 Drive = paFCB[iFCB]->Ldrv; /* What logical drive are we on? */
\r
3261 if (!paFCB[iFCB]->Mode)
\r
3262 { /* Modify mode? */
\r
3263 CloseFileM(dHandle);
\r
3264 return ErcReadOnly;
\r
3266 if (paFUB[dHandle]->fStream)
\r
3267 DeAllocPage(paFUB[dHandle]->pBuf, 1); /* Free buffer */
\r
3269 iStart = paFCB[iFCB]->StartClstr;
\r
3272 erc = TruncClstrChain(Drive, iStart);
\r
3274 erc = SetClstrValue(iStart, 0, Drive, &i);
\r
3277 paFCB[iFCB]->Name[0] = 0xE5;
\r
3278 UpdateDirEnt(iFCB); /* ignore error */
\r
3280 /* This means the FS is screwed up. This shouldn't happen... */
\r
3281 if (!paFCB[iFCB]->nUsers)
\r
3284 paFCB[iFCB]->nUsers--;
\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
3290 paFUB[dHandle]->Job = 0;
\r
3292 /* This writes all modified fat sectors */
\r
3294 for (i=0; i<nFATBufs; i++)
\r
3300 /*** Rename File ***************************************
\r
3301 This is Rename File for the MMURTL FAT file system.
\r
3302 ********************************************************/
\r
3304 static U32 RenameFileM(char *pCrntName, long dcbCrntName,
\r
3305 char *pNewName, long dcbNewName, U32 iJob)
\r
3307 U32 dHandle, erc, erc1, iFCB;
\r
3309 erc = OpenFileM(pCrntName, dcbCrntName, 1, 0, &dHandle, iJob);
\r
3313 CopyData(FileSpec, FileSpec1, 77);
\r
3314 SpecDepth1 = SpecDepth;
\r
3315 erc = ParseName(pNewName, dcbNewName, iJob);
\r
3317 if ((FDrive1 != FDrive) || (SpecDepth1 != SpecDepth))
\r
3318 erc = ErcRenameDrv; /* No Rename across drives */
\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
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
3329 erc1 = CloseFileM(dHandle);
\r
3336 /*** Create Dir ***************************************
\r
3337 This is Create Directory for the MMURTL FAT file system.
\r
3338 ********************************************************/
\r
3340 static U32 CreateDirM(char *pPath, long cbPath, long iJob)
\r
3344 erc = CreateFileM(pPath, cbPath, DIRECTORY, iJob);
\r
3348 /*** Delete Directory ***********************************
\r
3349 This is Delete Directory for the MMURTL FAT file system.
\r
3350 ********************************************************/
\r
3352 static U32 DeleteDirM(char *pPath, long cbPath, long fAllFiles, long iJob)
\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
3366 static void FSysTask(void)
\r
3368 U32 FMsg[2], merc, erc, i;
\r
3373 erc = WaitMsg(FSysExch, FMsg);
\r
3377 pRQB = FMsg[0]; /* first DD in Msg is pointer to RQBlock */
\r
3379 switch (pRQB->ServiceCode)
\r
3381 case 0 : /* JobAbort - CLOSE ALL FILES FOR THIS JOB! */
\r
3385 if (paFUB[i]->Job == pRQB->dData0)
\r
3389 ++i; /* next FUB */
\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
3402 case 2 : /* CloseFile */
\r
3403 erc = CloseFileM(pRQB->dData0); /* Handle */
\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
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
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
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
3432 case 7 : /* GetFileLFA */
\r
3433 erc = GetFileLFAM(pRQB->dData0, /* Handle */
\r
3434 pRQB->pData1); /* pdLFARet */
\r
3436 case 8 : /* SetFileLFA */
\r
3437 erc = SetFileLFAM(pRQB->dData0, /* Handle */
\r
3438 pRQB->dData1); /* dNewLFA */
\r
3440 case 9 : /* GetFileSize */
\r
3441 erc = GetFileSizeM(pRQB->dData0, /* Handle */
\r
3442 pRQB->pData1); /* pdSizeRet */
\r
3444 case 10 : /* SetFileSize */
\r
3445 erc = SetFileSizeM(pRQB->dData0, /* Handle */
\r
3446 pRQB->dData1); /* dSize */
\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
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
3461 case 13 : /* DeleteFile */
\r
3462 erc = DeleteFileM(pRQB->dData0); /* Handle */
\r
3464 case 14 : /* CreateDirectory */
\r
3465 erc = CreateDirM(pRQB->pData1, /* pPath */
\r
3466 pRQB->cbData1, /* cbPath */
\r
3467 pRQB->RqOwnerJob); /* JobNum */
\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
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
3486 erc = ErcBadSvcCode;
\r
3490 merc = Respond(FMsg[0], erc);
\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
3502 U32 far _OpenFile(char *pFilename,
\r
3506 long *pdHandleRet)
\r
3508 long erc, exch, rqhndl, i, msg[2];
\r
3509 if (dcbFilename == 3)
\r
3511 if (CompareNCS(pFilename, "NUL" , 3) == -1)
\r
3516 else if (CompareNCS(pFilename, "KBD" , 3) == -1)
\r
3521 else if (CompareNCS(pFilename, "VID" , 3) == -1)
\r
3526 else if (CompareNCS(pFilename, "LPT" , 3) == -1)
\r
3528 erc = DeviceOp(3, 10, 0, 0, &i); /* 10=Open */
\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
3541 if (!erc) erc = WaitMsg(exch, msg);
\r
3542 if (erc) return(erc);
\r
3546 /*************************************/
\r
3547 U32 far _CloseFile(unsigned long dHandle)
\r
3549 long erc, exch, rqhndl, i, msg[2];
\r
3553 else if (dHandle == 3)
\r
3555 erc = DeviceOp(3, 11, 0, 0, &i); /* 11 = Close */
\r
3559 GetTSSExch(&exch);
\r
3560 erc = Request(fsysname, 2, exch, &rqhndl,
\r
3561 0, /* 0 Send ptr */
\r
3565 if (!erc) erc = WaitMsg(exch, msg);
\r
3566 if (erc) return(erc);
\r
3570 /*************************************/
\r
3571 U32 far _ReadBlock(long dHandle,
\r
3577 long erc, exch, rqhndl, msg[2], i;
\r
3579 return(ErcNotSupported);
\r
3580 GetTSSExch(&exch);
\r
3581 erc = Request(fsysname, 3, exch, &rqhndl,
\r
3582 1, /* 1 Send ptr */
\r
3585 dHandle, dLFA, 0);
\r
3586 if (!erc) erc = WaitMsg(exch, msg);
\r
3591 DeviceStat(10, &FDDevStat, 64, &i);
\r
3596 /*************************************/
\r
3597 U32 far _WriteBlock(long dHandle,
\r
3604 long erc, exch, rqhndl, msg[2];
\r
3606 return(ErcNotSupported);
\r
3607 GetTSSExch(&exch);
\r
3608 erc = Request(fsysname, 4, exch, &rqhndl,
\r
3609 1, /* 1 Send ptr */
\r
3612 dHandle, dLFA, 0);
\r
3613 if (!erc) erc = WaitMsg(exch, msg);
\r
3614 if (erc) return(erc);
\r
3618 /*************************************/
\r
3619 U32 far _ReadBytes(long dHandle,
\r
3622 long *pdnBytesRet)
\r
3624 long erc, exch, rqhndl, msg[2], i;
\r
3630 else if (dHandle == 1)
\r
3633 while (i < nBytes)
\r
3635 ReadKbd(*pDataRet++, 1);
\r
3641 else if ((dHandle == 2) || (dHandle == 3))
\r
3644 return(ErcWriteOnly);
\r
3647 GetTSSExch(&exch);
\r
3648 erc = Request(fsysname, 5, exch, &rqhndl,
\r
3649 1, /* 1 Send ptr */
\r
3653 if (!erc) erc = WaitMsg(exch, msg);
\r
3654 if (erc) return(erc);
\r
3658 /*************************************/
\r
3659 U32 far _WriteBytes(long dHandle,
\r
3662 long *pdnBytesRet)
\r
3665 long erc, exch, rqhndl, VidAttr, msg[2];
\r
3667 if (dHandle == 0)
\r
3669 *pdnBytesRet = nBytes;
\r
3672 else if (dHandle == 1)
\r
3675 return(ErcReadOnly);
\r
3677 else if (dHandle == 2)
\r
3679 GetNormVid(&VidAttr);
\r
3680 TTYOut(pData, nBytes, VidAttr);
\r
3681 *pdnBytesRet = nBytes;
\r
3684 else if (dHandle == 3)
\r
3686 erc = DeviceOp(3, 2, 0, nBytes, pData); /* 2 = CmdWriteRec */
\r
3690 GetTSSExch(&exch);
\r
3691 erc = Request(fsysname, 6, exch, &rqhndl,
\r
3692 1, /* 1 Send ptr */
\r
3696 if (!erc) erc = WaitMsg(exch, msg);
\r
3697 if (erc) return(erc);
\r
3701 /*************************************/
\r
3702 U32 far _GetFileLFA(long dHandle,
\r
3706 long erc, exch, rqhndl, msg[2];
\r
3710 GetTSSExch(&exch);
\r
3711 erc = Request(fsysname, 7, exch, &rqhndl,
\r
3712 0, /* 0 Send ptrs */
\r
3716 if (!erc) erc = WaitMsg(exch, msg);
\r
3717 if (erc) return(erc);
\r
3721 /*************************************/
\r
3722 U32 far _SetFileLFA(long dHandle,
\r
3725 long erc, exch, rqhndl, msg[2];
\r
3727 return(ErcNotSupported);
\r
3728 GetTSSExch(&exch);
\r
3729 erc = Request(fsysname, 8, exch, &rqhndl,
\r
3730 0, /* 0 Send ptrs */
\r
3733 dHandle, dNewLFA, 0);
\r
3734 if (!erc) erc = WaitMsg(exch, msg);
\r
3735 if (erc) return(erc);
\r
3739 /*************************************/
\r
3740 U32 far _GetFileSize(long dHandle,
\r
3743 long erc, exch, rqhndl, msg[2];
\r
3745 return(ErcNotSupported);
\r
3746 GetTSSExch(&exch);
\r
3747 erc = Request(fsysname, 9, exch, &rqhndl,
\r
3748 0, /* 0 Send ptrs */
\r
3752 if (!erc) erc = WaitMsg(exch, msg);
\r
3753 if (erc) return(erc);
\r
3757 /*************************************/
\r
3758 U32 far _SetFileSize(long dHandle,
\r
3761 long erc, exch, rqhndl, msg[2];
\r
3763 return(ErcNotSupported);
\r
3764 GetTSSExch(&exch);
\r
3765 erc = Request(fsysname, 10, exch, &rqhndl,
\r
3766 0, /* 0 Send ptrs */
\r
3769 dHandle, dSize, 0);
\r
3770 if (!erc) erc = WaitMsg(exch, msg);
\r
3771 if (erc) return(erc);
\r
3775 /*************************************/
\r
3776 U32 far _CreateFile(char *pFilename,
\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
3787 if (!erc) erc = WaitMsg(exch, msg);
\r
3788 if (erc) return(erc);
\r
3792 /*************************************/
\r
3793 U32 far _RenameFile(char *pCrntName,
\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
3805 if (!erc) erc = WaitMsg(exch, msg);
\r
3806 if (erc) return(erc);
\r
3810 /*************************************/
\r
3811 U32 far _DeleteFile(long dHandle)
\r
3813 long erc, exch, rqhndl, msg[2];
\r
3815 return(ErcNotSupported);
\r
3816 GetTSSExch(&exch);
\r
3817 erc = Request(fsysname, 13, exch, &rqhndl,
\r
3818 0, /* 0 Send ptrs */
\r
3822 if (!erc) erc = WaitMsg(exch, msg);
\r
3823 if (erc) return(erc);
\r
3827 /*************************************/
\r
3828 U32 far _CreateDir(char *pPath,
\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
3838 if (!erc) erc = WaitMsg(exch, msg);
\r
3839 if (erc) return(erc);
\r
3843 /*************************************/
\r
3844 U32 far _DeleteDir(char *pPath,
\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
3855 if (!erc) erc = WaitMsg(exch, msg);
\r
3856 if (erc) return(erc);
\r
3860 /*************************************/
\r
3861 U32 far _GetDirSector(char *pPathSpec,
\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
3874 if (!erc) erc = WaitMsg(exch, msg);
\r
3875 if (erc) return(erc);
\r
3879 /********************************************
\r
3880 Initialization Routine for the File system.
\r
3881 This is called the Monitor where any errors
\r
3883 **********************************************/
\r
3890 /* Allocate FAT buffers and initialize FAT related structures.
\r
3891 We will allocate 24Kb worth of buffers (6 Pages, 16 buffers).
\r
3894 Fat[0].pBuf = FatBufA; /* floppy fat buffers */
\r
3896 erc = AllocOSPage(2, &pMem);
\r
3898 if (!erc) /* hard disk fat buffers from allocated mem */
\r
3899 for (i=1; i<nFATBufs; i++)
\r
3901 Fat[i].pBuf = pMem; /* Make pBuf point to each buffer */
\r
3902 pMem += 512; /* Next buffer in allocated memory */
\r
3905 /* Allocate and initialize FCBs and FUBs. This is enough FCBs and
\r
3906 FUBS for 128 openfiles. (Good enough to start...)
\r
3910 erc = AllocOSPage(2, &paFCB); /* a pointer to allocated FCBs. */
\r
3912 FillData(paFCB, 8192, 0);
\r
3914 erc = AllocOSPage(1, &paFUB); /* a pointer to allocated FUBs. */
\r
3916 FillData(paFUB, 4096, 0);
\r
3918 if (!erc) erc = read_PE(); /* reads partition tables */
\r
3920 if (!erc) erc = SetDriveGeometry(12);
\r
3924 erc = SetDriveGeometry(13);
\r
3925 if (erc == 663) erc = 0; /* may be invalid drive */
\r
3931 /* read all logical drive boot sectors */
\r
3935 for (i=0; i< nLDrvs; i++)
\r
3937 if (Ldrv[i].DevNum != 0xff)
\r
3944 for (i=0; i<nLDrvs; i++)
\r
3945 if (Ldrv[i].DevNum != 0xff)
\r
3948 if (Ldrv[i].fFAT16)
\r
3950 xprintf("%c: Heads %d, Sec/Trk %d, Sec/Clstr %d, Dev %d, FAT%d \r\n",
\r
3953 Ldrv[i].nSecPerTrk,
\r
3954 Ldrv[i].SecPerClstr,
\r
3960 erc = AllocExch(&FSysExch);
\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
3967 erc = SpawnTask(&FSysTask, 5, 0, &FSysStack[511], 1);
\r
3970 erc = RegisterSvc(fsysname, FSysExch);
\r