fatlite.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:104k
源码类别:

VxWorks

开发平台:

C/C++

  1. /*
  2.  * $Log:   P:/user/amir/lite/vcs/fatlite.c_v  $
  3.  *
  4.  *    Rev 1.46   06 Oct 1997 19:44:58   danig
  5.  * COPY2
  6.  *
  7.  *    Rev 1.45   06 Oct 1997 10:28:54   danig
  8.  * Check that drive handle < noOfDrives
  9.  *
  10.  *    Rev 1.44   29 Sep 1997 16:00:50   danig
  11.  * Take the no. of hidden sectors from partition table in getBPB
  12.  *
  13.  *    Rev 1.43   29 Sep 1997 14:20:16   danig
  14.  * flExitSocket & check findSector() return value
  15.  *
  16.  *    Rev 1.43   28 Sep 1997 19:08:02   danig
  17.  * flExitSocket & check findSector return value
  18.  *
  19.  *    Rev 1.42   10 Sep 1997 17:51:40   danig
  20.  * Got rid of generic names  &
  21.  * big-endian + unaligned in replaceFATsector
  22.  *
  23.  *    Rev 1.41   04 Sep 1997 18:29:08   danig
  24.  * Debug messages
  25.  *
  26.  *    Rev 1.40   02 Sep 1997 15:29:30   danig
  27.  * Fixed flushBuffer for SINGLE_BUFFER
  28.  *
  29.  *    Rev 1.39   21 Aug 1997 14:06:20   unknown
  30.  * Unaligned4
  31.  *
  32.  *    Rev 1.38   19 Aug 1997 20:05:38   danig
  33.  * Andray's changes
  34.  *
  35.  *    Rev 1.37   17 Aug 1997 17:32:32   danig
  36.  * Mutual exclusive mounting
  37.  *
  38.  *    Rev 1.37   17 Aug 1997 16:51:08   danig
  39.  * Mutual exclusive mounting
  40.  *
  41.  *    Rev 1.36   14 Aug 1997 15:49:46   danig
  42.  * Change in low level routines
  43.  *
  44.  *    Rev 1.35   04 Aug 1997 13:26:30   danig
  45.  * Low level API & no irHandle in formatVolume
  46.  *
  47.  *    Rev 1.34   27 Jul 1997 14:35:58   danig
  48.  * FAR -> FAR0 in splitjoinFile
  49.  *
  50.  *    Rev 1.33   27 Jul 1997 11:59:24   amirban
  51.  * Socket.volNo initialization
  52.  *
  53.  *    Rev 1.32   27 Jul 1997 10:12:32   amirban
  54.  * replaceFATSector and name changes
  55.  *
  56.  *    Rev 1.30   21 Jul 1997 20:01:50   danig
  57.  * Compilation issues
  58.  *
  59.  *    Rev 1.29   20 Jul 1997 18:44:10   danig
  60.  *
  61.  *    Rev 1.28   20 Jul 1997 18:34:50   danig
  62.  * Fixed findFile with SET_ATTRIBUTES bug
  63.  *
  64.  *    Rev 1.27   20 Jul 1997 17:16:46   amirban
  65.  * No watchDogTimer
  66.  *
  67.  *    Rev 1.26   07 Jul 1997 15:21:02   amirban
  68.  * Ver 2.0
  69.  *
  70.  *    Rev 1.24   29 May 1997 14:59:00   amirban
  71.  * Dismount closes files on one drive only
  72.  *
  73.  *    Rev 1.23   28 May 1997 18:51:28   amirban
  74.  * setBusy(OFF) always
  75.  *
  76.  *    Rev 1.22   20 Apr 1997 10:48:50   danig
  77.  * Fixed bug: fsFindFirstFile when subdirectories are undefined.
  78.  *
  79.  *    Rev 1.21   09 Apr 1997 17:35:42   amirban
  80.  * Partition table redefined
  81.  *
  82.  *    Rev 1.20   31 Mar 1997 18:02:38   amirban
  83.  * BPB redefined
  84.  *
  85.  v    Rev 1.19   03 Nov 1996 17:40:14   amirban
  86.  * Correction for single-buffer
  87.  *
  88.  *    Rev 1.17   21 Oct 1996 18:10:20   amirban
  89.  * Defragment I/F change
  90.  *
  91.  *    Rev 1.16   17 Oct 1996 18:56:36   danig
  92.  * added big-endian to splitFile and joinFile
  93.  *
  94.  *    Rev 1.15   17 Oct 1996 16:21:46   danig
  95.  * Audio features: splitFile, joinFile
  96.  *
  97.  *    Rev 1.14   17 Oct 1996 12:39:08   amirban
  98.  * Buffer FAT sectors
  99.  *
  100.  *    Rev 1.13   03 Oct 1996 11:56:24   amirban
  101.  * New Big-Endian
  102.  *
  103.  *    Rev 1.12   09 Sep 1996 11:38:52   amirban
  104.  * Introduce sectorNotFound errors
  105.  *
  106.  *    Rev 1.11   29 Aug 1996 14:16:48   amirban
  107.  * Warnings
  108.  *
  109.  *    Rev 1.10   20 Aug 1996 13:22:52   amirban
  110.  * fsGetBPB
  111.  *
  112.  *    Rev 1.9   12 Aug 1996 15:46:26   amirban
  113.  * Use setBusy instead of socketSetBusy
  114.  *
  115.  *    Rev 1.8   31 Jul 1996 14:29:58   amirban
  116.  * Background stuff, and current file size in fsFindFile
  117.  *
  118.  *    Rev 1.7   14 Jul 1996 16:48:12   amirban
  119.  * Format params
  120.  *
  121.  *    Rev 1.6   01 Jul 1996 15:41:26   amirban
  122.  * new fsInit and modified fsGetDiskInfo
  123.  *
  124.  *    Rev 1.5   16 Jun 1996 14:04:06   amirban
  125.  * Format does not need mounting
  126.  *
  127.  *    Rev 1.4   09 Jun 1996 18:15:42   amirban
  128.  * Added fsExit, and VFAT rename mode
  129.  *
  130.  *    Rev 1.3   03 Jun 1996 16:26:24   amirban
  131.  * Separated fsParsePath, and rename-file change
  132.  *
  133.  *    Rev 1.2   19 May 1996 19:31:46   amirban
  134.  * Got rid of aliases in IOreq, and better fsParsePath
  135.  *
  136.  *    Rev 1.1   12 May 1996 20:05:26   amirban
  137.  * Changes to findFile
  138.  *
  139.  *    Rev 1.0   20 Mar 1996 13:33:06   amirban
  140.  * Initial revision.
  141.  */
  142. /************************************************************************/
  143. /*                                                                      */
  144. /* FAT-FTL Lite Software Development Kit */
  145. /* Copyright (C) M-Systems Ltd. 1995-1996 */
  146. /* */
  147. /************************************************************************/
  148. #include "fltl.h"
  149. #include "flsocket.h"
  150. #include "dosformt.h"
  151. #include "backgrnd.h"
  152. #include "flbuffer.h"
  153. #include "stdcomp.h"
  154. #include "fatlite.h"
  155. unsigned long flMsecCounter = 0;
  156. /* Volume flag definitions */
  157. #define VOLUME_LOW_LVL_MOUNTED  1       /* volume is mounted for low level operations */
  158. #define VOLUME_MOUNTED 2 /* Volume is mounted */
  159. #define VOLUME_12BIT_FAT 4 /* Volume uses 12-bit FAT */
  160. #define VOLUME_ABS_MOUNTED 8 /* Volume is mounted for abs calls */
  161. typedef struct {
  162.   char flags; /* See description above */
  163.   unsigned sectorsPerCluster; /* Cluster size in sectors */
  164.   unsigned maxCluster; /* highest cluster no. */
  165.   unsigned bytesPerCluster; /* Bytes per cluster */
  166.   unsigned bootSectorNo; /* Sector no. of DOS boot sector */
  167.   unsigned firstFATSectorNo; /* Sector no. of 1st FAT */
  168.   unsigned secondFATSectorNo; /* Sector no. of 2nd FAT */
  169.   unsigned numberOfFATS; /* number of FAT copies */
  170.   unsigned sectorsPerFAT; /* Sectors per FAT copy */
  171.   unsigned rootDirectorySectorNo; /* Sector no. of root directory */
  172.   unsigned sectorsInRootDirectory; /* No. of sectors in root directory */
  173.   unsigned firstDataSectorNo; /* 1st cluster sector no. */
  174.   unsigned allocationRover; /* rover pointer for allocation */
  175. #ifndef SINGLE_BUFFER
  176.   #if FILES > 0
  177.   FLBuffer volBuffer; /* Define a sector buffer */
  178.   #endif
  179.   FLMutex volExecInProgress;
  180. #endif
  181. /* #ifdef LOW_LEVEL */
  182.   FLFlash  flash; /* flash structure for low level operations */
  183. /* #endif */
  184.   TL tl; /* Translation layer methods */
  185.   FLSocket *socket; /* Pointer to socket */
  186. } Volume;
  187. static FLBoolean initDone = FALSE; /* Initialization already done */
  188. static Volume  vols[DRIVES];
  189. typedef struct {
  190.   long currentPosition; /* current byte offset in file */
  191. #define ownerDirCluster currentPosition /* 1st cluster of owner directory */
  192.   long  fileSize; /* file size in bytes */
  193.   SectorNo directorySector; /* sector of directory containing file */
  194.   unsigned      currentCluster; /* cluster of current position */
  195.   unsigned char directoryIndex; /* entry no. in directory sector */
  196.   unsigned char flags; /* See description below */
  197.   Volume * fileVol; /* Drive of file */
  198. } File;
  199. /* File flag definitions */
  200. #define FILE_MODIFIED 4 /* File was modified */
  201. #define FILE_IS_OPEN 8 /* File entry is used */
  202. #define FILE_IS_DIRECTORY    0x10 /* File is a directory */
  203. #define FILE_IS_ROOT_DIR     0x20 /* File is root directory */
  204. #define FILE_READ_ONLY      0x40 /* Writes not allowed */
  205. #define FILE_MUST_OPEN       0x80 /* Create file if not found */
  206. #if FILES > 0
  207. static File  fileTable[FILES]; /* the file table */
  208. #endif /* FILES > 0 */
  209. #ifdef SINGLE_BUFFER
  210. FLBuffer buffer;
  211. FLMutex execInProgress;
  212. #else
  213. #define buffer (vol.volBuffer)
  214. #define execInProgress (vol.volExecInProgress)
  215. #endif
  216. #define directory ((DirectoryEntry *) buffer.data)
  217. /*----------------------------------------------------------------------*/
  218. /*                  s e t B u s y */
  219. /* */
  220. /* Notifies the start and end of a file-system operation. */
  221. /* */
  222. /* Parameters:                                                          */
  223. /* vol : Pointer identifying drive */
  224. /*      state : TFFS_ON (1) = operation entry */
  225. /*   TFFS_OFF(0) = operation exit */
  226. /*                                                                      */
  227. /*----------------------------------------------------------------------*/
  228. static FLStatus setBusy(Volume vol, FLBoolean state)
  229. {
  230.   if (state == TFFS_ON) {
  231.     if (!flTakeMutex(&execInProgress,1))
  232.       return flDriveNotAvailable;
  233.     flSocketSetBusy(vol.socket,TFFS_ON);
  234.     flNeedVcc(vol.socket);
  235.     if (vol.flags & VOLUME_ABS_MOUNTED)
  236.       vol.tl.tlSetBusy(vol.tl.rec,TFFS_ON);
  237.   }
  238.   else {
  239.     if (vol.flags & VOLUME_ABS_MOUNTED)
  240.       vol.tl.tlSetBusy(vol.tl.rec,TFFS_OFF);
  241.     flDontNeedVcc(vol.socket);
  242.     flSocketSetBusy(vol.socket,TFFS_OFF);
  243.     flFreeMutex(&execInProgress);
  244.   }
  245.   return flOK;
  246. }
  247. /*----------------------------------------------------------------------*/
  248. /*           f i n d S e c t o r */
  249. /* */
  250. /* Locates a sector in the buffer or maps it */
  251. /*                                                                      */
  252. /* Parameters:                                                          */
  253. /* vol : Pointer identifying drive */
  254. /* sectorNo : Sector no. to locate */
  255. /*                                                                      */
  256. /*----------------------------------------------------------------------*/
  257. static const void FAR0 *findSector(Volume vol, SectorNo sectorNo)
  258. {
  259. CardAddress *physAddress =0;
  260.   return
  261. #if FILES > 0
  262.  (sectorNo == buffer.sectorNo && &vol == buffer.owner) ?
  263.  buffer.data :
  264. #endif
  265.  vol.tl.mapSector(vol.tl.rec,sectorNo, physAddress);
  266. }
  267. #if FILES > 0
  268. static FLStatus closeFile(File *file); /* forward */
  269. static FLStatus flushBuffer(Volume vol); /* forward */
  270. #endif
  271. /*----------------------------------------------------------------------*/
  272. /*       d i s m o u n t V o l u m e */
  273. /* */
  274. /* Dismounts the volume, closing all files. */
  275. /* This call is not normally necessary, unless it is known the volume   */
  276. /* will soon be removed. */
  277. /* */
  278. /* Parameters:                                                          */
  279. /* vol : Pointer identifying drive */
  280. /*                                                                      */
  281. /* Returns:                                                             */
  282. /* FLStatus : 0 on success, otherwise failed                */
  283. /*----------------------------------------------------------------------*/
  284. static FLStatus dismountVolume(Volume vol)
  285. {
  286. #if FILES > 0
  287.   int i;
  288. #endif
  289.   if (vol.flags & VOLUME_ABS_MOUNTED) {
  290.     FLStatus status = flOK;
  291. #ifndef FIXED_MEDIA
  292.     status = flMediaCheck(vol.socket);
  293. #endif
  294.     if (status != flOK)
  295.       vol.flags = 0;
  296. #if FILES > 0
  297.     else {
  298.       checkStatus(flushBuffer(&vol));
  299.     }
  300.     /* Close or discard all files and make them available */
  301.     for (i = 0; i < FILES; i++)
  302.       if (fileTable[i].fileVol == &vol)
  303. if (vol.flags & VOLUME_MOUNTED)
  304.   closeFile(&fileTable[i]);
  305. else
  306.   fileTable[i].flags = 0;
  307.     vol.tl.dismount(vol.tl.rec);
  308.     buffer.sectorNo = UNASSIGNED_SECTOR; /* Current sector no. (none) */
  309.     buffer.dirty = buffer.checkPoint = FALSE;
  310. #endif /* FILES > 0 */
  311.   }
  312.   vol.flags = 0; /* mark volume unmounted */
  313.   return flOK;
  314. }
  315. /*----------------------------------------------------------------------*/
  316. /*         m o u n t V o l u m e */
  317. /* */
  318. /* Mounts the Flash volume. */
  319. /* */
  320. /* In case the inserted volume has changed, or on the first access to   */
  321. /* the file system, it should be mounted before file operations can be  */
  322. /* done on it. */
  323. /* Mounting a volume has the effect of discarding all open files (the   */
  324. /* files cannot be properly closed since the original volume is gone),  */
  325. /* and turning off the media-change indication to allow file processing */
  326. /* calls. */
  327. /* */
  328. /* The volume automatically becomes unmounted if it is removed or       */
  329. /* changed. */
  330. /*                                                                      */
  331. /* Parameters:                                                          */
  332. /* vol : Pointer identifying drive */
  333. /*                                                                      */
  334. /* Returns:                                                             */
  335. /* FLStatus : 0 on success, otherwise failed                */
  336. /*----------------------------------------------------------------------*/
  337. static FLStatus mountVolume(Volume vol)
  338. {
  339.   SectorNo noOfSectors;
  340.   PartitionTable FAR0 *partitionTable;
  341.   DOSBootSector FAR0 *bootSector;
  342.   checkStatus(dismountVolume(&vol));
  343.   checkStatus(flMount(&vol - vols,&vol.tl,&vol.flash)); /* Try to mount translation layer */
  344.   /* Read in paritition table */
  345.   partitionTable = (PartitionTable FAR0 *) findSector(&vol,0);
  346.   if(partitionTable == NULL)
  347.     return flSectorNotFound;
  348.   /* *BUG* *BUG* Doesn't work if SECTOR_SIZE < 512 */
  349.   if (LE2(partitionTable->signature) == PARTITION_SIGNATURE &&
  350.       partitionTable->type != 0)
  351.     vol.bootSectorNo =
  352. (unsigned) UNAL4(partitionTable->startingSectorOfPartition);
  353.   else
  354.     vol.bootSectorNo = 0; /* If partition table is undetected,
  355.    assume sector 0 is DOS boot block */
  356.   vol.firstFATSectorNo = vol.secondFATSectorNo = 0; /* Disable FAT monitoring */
  357.   vol.flags |= VOLUME_ABS_MOUNTED;  /* Enough to do abs operations */
  358.   bootSector = (DOSBootSector FAR0 *) findSector(&vol,vol.bootSectorNo);
  359.   if(bootSector == NULL)
  360.     return flSectorNotFound;
  361.   /* Do the customary sanity checks */
  362.   if (!(bootSector->bpb.jumpInstruction[0] == 0xe9 ||
  363. (bootSector->bpb.jumpInstruction[0] == 0xeb &&
  364.  bootSector->bpb.jumpInstruction[2] == 0x90))) {
  365.   #ifdef DEBUG_PRINT
  366.     DEBUG_PRINT("Debug: did not recognize format.n");
  367.   #endif
  368.     return flNonFATformat;
  369.   }
  370.   /* See if we handle this sector size */
  371.   if (UNAL2(bootSector->bpb.bytesPerSector) != SECTOR_SIZE)
  372.     return flFormatNotSupported;
  373.   vol.sectorsPerCluster = bootSector->bpb.sectorsPerCluster;
  374.   vol.numberOfFATS = bootSector->bpb.noOfFATS;
  375.   vol.sectorsPerFAT = LE2(bootSector->bpb.sectorsPerFAT);
  376.   vol.firstFATSectorNo = vol.bootSectorNo +
  377.     LE2(bootSector->bpb.reservedSectors);
  378.   vol.secondFATSectorNo = vol.firstFATSectorNo +
  379.      LE2(bootSector->bpb.sectorsPerFAT);
  380.   vol.rootDirectorySectorNo = vol.firstFATSectorNo +
  381.    bootSector->bpb.noOfFATS * LE2(bootSector->bpb.sectorsPerFAT);
  382.   vol.sectorsInRootDirectory =
  383. (UNAL2(bootSector->bpb.rootDirectoryEntries) * DIRECTORY_ENTRY_SIZE - 1) /
  384. SECTOR_SIZE + 1;
  385.   vol.firstDataSectorNo = vol.rootDirectorySectorNo +
  386.      vol.sectorsInRootDirectory;
  387.   noOfSectors = UNAL2(bootSector->bpb.totalSectorsInVolumeDOS3);
  388.   if (noOfSectors == 0)
  389.     noOfSectors = (SectorNo) LE4(bootSector->bpb.totalSectorsInVolume);
  390.   vol.maxCluster = (unsigned) ((noOfSectors - vol.firstDataSectorNo) /
  391. vol.sectorsPerCluster) + 1;
  392.   if (vol.maxCluster < 4085) {
  393. #ifdef FAT_12BIT
  394.     vol.flags |= VOLUME_12BIT_FAT; /* 12-bit FAT */
  395. #else
  396. #ifdef DEBUG_PRINT
  397.     DEBUG_PRINT("Debug: FAT_12BIT must be defined.n");
  398. #endif
  399.     return flFormatNotSupported;
  400. #endif
  401.   }
  402.   vol.bytesPerCluster = vol.sectorsPerCluster * SECTOR_SIZE;
  403.   vol.allocationRover = 2; /* Set rover at first cluster */
  404.   vol.flags |= VOLUME_MOUNTED; /* That's it */
  405.   return flOK;
  406. }
  407. #if defined(DEFRAGMENT_VOLUME) || defined(SINGLE_BUFFER)
  408. /*----------------------------------------------------------------------*/
  409. /*         d e f r a g m e n t V o l u m e */
  410. /* */
  411. /* Performs a general defragmentation and recycling of non-writable */
  412. /* Flash areas, to achieve optimal write speed. */
  413. /*                                                                      */
  414. /* NOTE: The required number of sectors (in irLength) may be changed    */
  415. /* (from another execution thread) while defragmentation is active. In  */
  416. /* particular, the defragmentation may be cut short after it began by */
  417. /* modifying the irLength field to 0. */
  418. /* */
  419. /* Parameters:                                                          */
  420. /* ioreq->irLength : Minimum number of sectors to make available   */
  421. /*   for writes. */
  422. /*                                                                      */
  423. /* Returns:                                                             */
  424. /* ioreq->irLength : Actual number of sectors available for writes */
  425. /* FLStatus : 0 on success, otherwise failed                */
  426. /*----------------------------------------------------------------------*/
  427. static FLStatus defragmentVolume(Volume vol, IOreq FAR2 *ioreq)
  428. {
  429.   return vol.tl.defragment(vol.tl.rec,&ioreq->irLength);
  430. }
  431. #endif /* DEFRAGMENT_VOLUME */
  432. #ifdef FORMAT_VOLUME
  433. /*----------------------------------------------------------------------*/
  434. /*       f l F o r m a t V o l u m e */
  435. /* */
  436. /* Formats a volume, writing a new and empty file-system. All existing  */
  437. /* data is destroyed. Optionally, a low-level FTL formatting is also    */
  438. /* done. */
  439. /* Formatting leaves the volume in the dismounted state, so that a */
  440. /* flMountVolume call is necessary after it. */
  441. /*                                                                      */
  442. /* Parameters:                                                          */
  443. /* irHandle : Drive number (0, 1, ...) */
  444. /* irFlags : NO_FTL_FORMAT: Do FAT formatting only */
  445. /*   FTL_FORMAT: Do FTL & FAT formatting           */
  446. /*   FTL_FORMAT_IF_NEEDED: Do FTL formatting only */
  447. /*       if current format is invalid */
  448. /* irData : Address of FormatParams structure to use */
  449. /*                                                                      */
  450. /* Returns:                                                             */
  451. /* FLStatus : 0 on success, otherwise failed                */
  452. /*----------------------------------------------------------------------*/
  453. static FLStatus formatVolume(Volume vol, IOreq FAR2 *ioreq)
  454. {
  455.   FormatParams FAR1 *formatParams = (FormatParams FAR1 *) ioreq->irData;
  456.   checkStatus(dismountVolume(&vol));
  457.   if (ioreq->irFlags == FTL_FORMAT) {
  458.     checkStatus(flFormat(&vol - vols,formatParams));
  459.   }
  460.   else {
  461.     FLStatus status = flMount(&vol - vols,&vol.tl,&vol.flash);
  462.     if ((status == flUnknownMedia || status == flBadFormat) &&
  463. ioreq->irFlags == FTL_FORMAT_IF_NEEDED)
  464.       status = flFormat(&vol - vols,formatParams);
  465.     if (status != flOK)
  466.       return status;
  467.   }
  468.   checkStatus(flMount(&vol - vols,&vol.tl,&vol.flash));
  469.   checkStatus(flDosFormat(&vol.tl,formatParams));
  470.   return flOK;
  471. }
  472. #endif /* FORMAT_VOLUME */
  473. #ifdef ABS_READ_WRITE
  474. /*----------------------------------------------------------------------*/
  475. /*              a b s R e a d  */
  476. /* */
  477. /* Reads absolute sectors by sector no. */
  478. /* */
  479. /* Parameters:                                                          */
  480. /* irHandle : Drive number (0, 1, ...) */
  481. /*      irData : Address of user buffer to read into */
  482. /* irSectorNo : First sector no. to read (sector 0 is the */
  483. /*   DOS boot sector). */
  484. /* irSectorCount : Number of consectutive sectors to read */
  485. /*                                                                      */
  486. /* Returns:                                                             */
  487. /* FLStatus : 0 on success, otherwise failed                */
  488. /* irSectorCount : Number of sectors actually read */
  489. /*----------------------------------------------------------------------*/
  490. static FLStatus absRead(Volume vol, IOreq FAR2 *ioreq)
  491. {
  492.   char FAR1 *userBuffer = (char FAR1 *) ioreq->irData;
  493.   SectorNo currSector = vol.bootSectorNo + ioreq->irSectorNo;
  494.   unsigned sectorCount = ioreq->irSectorCount;
  495.   for (ioreq->irSectorCount = 0;
  496.        ioreq->irSectorCount < sectorCount;
  497.        ioreq->irSectorCount++, currSector++, userBuffer += SECTOR_SIZE) {
  498.     const void FAR0 *mappedSector = findSector(&vol,currSector);
  499.     if (mappedSector)
  500.       tffscpy(userBuffer,mappedSector,SECTOR_SIZE);
  501.     else
  502.       tffsset(userBuffer,0,SECTOR_SIZE);
  503.   }
  504.   return flOK;
  505. }
  506. /*----------------------------------------------------------------------*/
  507. /*       r e p l a c e F A T s e c t o r  */
  508. /* */
  509. /* Monitors sector deletions in the FAT. */
  510. /* */
  511. /* When a FAT block is about to be written by an absolute write, this   */
  512. /* routine will first scan whether any sectors are being logically */
  513. /* deleted by this FAT update, and if so, it will delete-sector them */
  514. /* before the actual FAT update takes place. */
  515. /* */
  516. /* Parameters:                                                          */
  517. /* vol : Pointer identifying drive */
  518. /* sectorNo : FAT Sector no. about to be written */
  519. /*      newFATsector : Address of FAT sector about to be written */
  520. /*                                                                      */
  521. /* Returns:                                                             */
  522. /* FLStatus : 0 on success, otherwise failed                */
  523. /*----------------------------------------------------------------------*/
  524. FLStatus replaceFATsector(Volume vol,
  525. SectorNo sectorNo,
  526. const char FAR1 *newFATsector)
  527. {
  528.   const char FAR0 *oldFATsector = (const char FAR0 *) findSector(&vol,sectorNo);
  529. #ifdef FAT_12BIT
  530.   unsigned FAThalfBytes = vol.flags & VOLUME_12BIT_FAT ? 3 : 4;
  531.   unsigned firstCluster =
  532. (FAThalfBytes == 3) ?
  533. (((unsigned) (sectorNo - vol.firstFATSectorNo) * (2 * SECTOR_SIZE) + 2) / 3) :
  534. ((unsigned) (sectorNo - vol.firstFATSectorNo) * (SECTOR_SIZE / 2));
  535.   SectorNo firstSector =
  536. ((SectorNo) firstCluster - 2) * vol.sectorsPerCluster + vol.firstDataSectorNo;
  537.   unsigned int halfByteOffset =
  538. (firstCluster * FAThalfBytes) & (2 * SECTOR_SIZE - 1);
  539.   if (oldFATsector == NULL)
  540.     return flOK;
  541.   /* Find if any clusters were logically deleted, and if so, delete them */
  542.   /* NOTE: We are skipping over 12-bit FAT entries which span more than  */
  543.   /*       one sector. Nobody's perfect anyway.                          */
  544.   for (; halfByteOffset < (2 * SECTOR_SIZE - 2);
  545.        firstSector += vol.sectorsPerCluster, halfByteOffset += FAThalfBytes) {
  546.     unsigned short oldFATentry, newFATentry;
  547. #ifdef TFFS_BIG_ENDIAN
  548.     oldFATentry = LE2(*(LEushort FAR0 *)(oldFATsector + (halfByteOffset / 2)));
  549.     newFATentry = LE2(*(LEushort FAR1 *)(newFATsector + (halfByteOffset / 2)));
  550. #else
  551.     oldFATentry = UNAL2(*(Unaligned FAR0 *)(oldFATsector + (halfByteOffset / 2)));
  552.     newFATentry = UNAL2(*(Unaligned FAR1 *)(newFATsector + (halfByteOffset / 2)));
  553. #endif /* TFFS_BIG_ENDIAN */
  554.     if (halfByteOffset & 1) {
  555.       oldFATentry >>= 4;
  556.       newFATentry >>= 4;
  557.     }
  558.     else if (FAThalfBytes == 3) {
  559.       oldFATentry &= 0xfff;
  560.       newFATentry &= 0xfff;
  561.     }
  562. #else
  563.   unsigned firstCluster =
  564. ((unsigned) (sectorNo - vol.firstFATSectorNo) * (SECTOR_SIZE / 2));
  565.   SectorNo firstSector =
  566. ((SectorNo) firstCluster - 2) * vol.sectorsPerCluster + vol.firstDataSectorNo;
  567.   unsigned int byteOffset;
  568.   if (oldFATsector == NULL)
  569.     return flOK;
  570.   /* Find if any clusters were logically deleted, and if so, delete them */
  571.   for (byteOffset = 0; byteOffset < SECTOR_SIZE;
  572.        firstSector += vol.sectorsPerCluster, byteOffset += 2) {
  573.     unsigned short oldFATentry = LE2(*(LEushort FAR0 *)(oldFATsector + byteOffset));
  574.     unsigned short newFATentry = LE2(*(LEushort FAR1 *)(newFATsector + byteOffset));
  575. #endif
  576.     if (oldFATentry != FAT_FREE && newFATentry == FAT_FREE)
  577.       checkStatus(vol.tl.deleteSector(vol.tl.rec,firstSector,vol.sectorsPerCluster));
  578.     /* make sure sector is still there */
  579.     oldFATsector = (const char FAR0 *) findSector(&vol,sectorNo);
  580.   }
  581.   return flOK;
  582. }
  583. /*----------------------------------------------------------------------*/
  584. /*                  d i s a b l e F A T m o n i t o r                   */
  585. /*                                                                      */
  586. /* Turns off FAT monitor.                                               */
  587. /*                                                                      */
  588. /* Parameters:                                                          */
  589. /*      vol             : Pointer identifying drive                     */
  590. /*                                                                      */
  591. /* Returns:                                                             */
  592. /*      always flOK                                                     */
  593. /*----------------------------------------------------------------------*/
  594. static FLStatus disableFATmonitor(Volume vol)
  595. {
  596.   vol.firstFATSectorNo = vol.secondFATSectorNo = 0; /* Disable FAT monitoring */
  597.   return flOK;
  598. }
  599. /*----------------------------------------------------------------------*/
  600. /*            a b s W r i t e  */
  601. /* */
  602. /* Writes absolute sectors by sector no. */
  603. /* */
  604. /* Parameters:                                                          */
  605. /* irHandle : Drive number (0, 1, ...) */
  606. /*      irData : Address of user buffer to write from */
  607. /* irSectorNo : First sector no. to write (sector 0 is the */
  608. /*   DOS boot sector). */
  609. /* irSectorCount : Number of consectutive sectors to write */
  610. /*                                                                      */
  611. /* Returns:                                                             */
  612. /* FLStatus : 0 on success, otherwise failed                */
  613. /* irSectorCount : Number of sectors actually written */
  614. /*----------------------------------------------------------------------*/
  615. static FLStatus absWrite(Volume vol, IOreq FAR2 *ioreq)
  616. {
  617.   char FAR1 *userBuffer = (char FAR1 *) ioreq->irData;
  618.   SectorNo currSector = vol.bootSectorNo + ioreq->irSectorNo;
  619.   unsigned sectorCount = ioreq->irSectorCount;
  620.   if (currSector < vol.secondFATSectorNo &&
  621.       currSector + sectorCount > vol.firstFATSectorNo) {
  622.     unsigned iSector;
  623.     for (iSector = 0; iSector < sectorCount;
  624.  iSector++, currSector++, userBuffer += SECTOR_SIZE) {
  625.       if (currSector >= vol.firstFATSectorNo &&
  626.   currSector < vol.secondFATSectorNo)
  627. replaceFATsector(&vol,currSector,userBuffer);
  628.     }
  629.     userBuffer = (char FAR1 *) ioreq->irData;
  630.     currSector = vol.bootSectorNo + ioreq->irSectorNo;
  631.   }
  632.   for (ioreq->irSectorCount = 0;
  633.        ioreq->irSectorCount < sectorCount;
  634.        ioreq->irSectorCount++, currSector++, userBuffer += SECTOR_SIZE) {
  635.     checkStatus(vol.tl.writeSector(vol.tl.rec,currSector,userBuffer));
  636.   }
  637.   return flOK;
  638. }
  639. /*----------------------------------------------------------------------*/
  640. /*           a b s D e l e t e  */
  641. /* */
  642. /* Marks absolute sectors by sector no. as deleted. */
  643. /* */
  644. /* Parameters:                                                          */
  645. /* irHandle : Drive number (0, 1, ...) */
  646. /* irSectorNo : First sector no. to write (sector 0 is the */
  647. /*   DOS boot sector). */
  648. /* irSectorCount : Number of consectutive sectors to delete */
  649. /*                                                                      */
  650. /* Returns:                                                             */
  651. /* FLStatus : 0 on success, otherwise failed                */
  652. /*----------------------------------------------------------------------*/
  653. static FLStatus absDelete(Volume vol, IOreq FAR2 *ioreq)
  654. {
  655.   return vol.tl.deleteSector(vol.tl.rec,vol.bootSectorNo + ioreq->irSectorNo,ioreq->irSectorCount);
  656. }
  657. /*----------------------------------------------------------------------*/
  658. /*              g e t B P B  */
  659. /* */
  660. /* Reads the BIOS Parameter Block from the boot sector */
  661. /* */
  662. /* Parameters:                                                          */
  663. /* irHandle : Drive number (0, 1, ...) */
  664. /*      irData : Address of user buffer to read BPB into */
  665. /*                                                                      */
  666. /* Returns:                                                             */
  667. /* FLStatus : 0 on success, otherwise failed                */
  668. /*----------------------------------------------------------------------*/
  669. static FLStatus getBPB(Volume vol, IOreq FAR2 *ioreq)
  670. {
  671.   BPB FAR1 *userBPB = (BPB FAR1 *) ioreq->irData;
  672.   DOSBootSector FAR0 *bootSector;
  673.   unsigned long noOfHiddenSectors;
  674.   PartitionTable FAR0 *partitionTable;
  675.   bootSector = (DOSBootSector FAR0 *) findSector(&vol,vol.bootSectorNo);
  676.   if(bootSector == NULL)
  677.     return flSectorNotFound;
  678.   *userBPB = bootSector->bpb;
  679. #if FALSE
  680.   tffscpy (userBPB, &bootSector->bpb, sizeof(BPB));
  681. #endif /* FALSE */
  682.   /* take the number of hidden sectors from the partition table in case
  683.      we don't have DOS format BPB */
  684.   partitionTable = (PartitionTable FAR0 *) findSector(&vol,0);
  685.   if(partitionTable == NULL)
  686.     return flSectorNotFound;
  687.   if (LE2(partitionTable->signature) == PARTITION_SIGNATURE &&
  688.       partitionTable->type != 0) {
  689.     noOfHiddenSectors = UNAL4(partitionTable->startingSectorOfPartition);
  690.     toLE4(userBPB->noOfHiddenSectors, noOfHiddenSectors);
  691.   }
  692.   return flOK;
  693. }
  694. #endif /* ABS_READ_WRITE */
  695. #ifdef LOW_LEVEL
  696. /*----------------------------------------------------------------------*/
  697. /*              m o u n t L o w L e v e l  */
  698. /* */
  699. /* Mount a volume for low level operations. If a low level routine is   */
  700. /* called and the volume is not mounted for low level operations, this */
  701. /* routine is called atomatically.  */
  702. /* */
  703. /* Parameters: */
  704. /* vol : Pointer identifying drive */
  705. /*                                                                      */
  706. /* Returns:                                                             */
  707. /* FLStatus : 0 on success, otherwise failed                */
  708. /*----------------------------------------------------------------------*/
  709. static FLStatus mountLowLevel(Volume vol)
  710. {
  711.   checkStatus(flIdentifyFlash(vol.socket,&vol.flash));
  712.   vol.flash.setPowerOnCallback(&vol.flash);
  713.   vol.flags |= VOLUME_LOW_LVL_MOUNTED;
  714.   return flOK;
  715. }
  716. /*----------------------------------------------------------------------*/
  717. /*              d i s m o u n t L o w L e v e l  */
  718. /* */
  719. /* Dismount the volume for low level operations. */
  720. /* */
  721. /* Parameters: */
  722. /* vol : Pointer identifying drive */
  723. /*                                                                      */
  724. /*----------------------------------------------------------------------*/
  725. static void dismountLowLevel(Volume vol)
  726. {
  727.   /* mark the volume as unmounted for low level operations, untouch other flags */
  728.   vol.flags &= ~VOLUME_LOW_LVL_MOUNTED;
  729. }
  730. /*----------------------------------------------------------------------*/
  731. /*              g e t P h y s i c a l I n f o  */
  732. /* */
  733. /* Get physical information of the media. The information includes */
  734. /* JEDEC ID, unit size and media size. */
  735. /* */
  736. /* Parameters: */
  737. /* vol : Pointer identifying drive */
  738. /*      irData : Address of user buffer to read physical */
  739. /*   information into. */
  740. /*                                                                      */
  741. /* Returns:                                                             */
  742. /* FLStatus : 0 on success, otherwise failed                */
  743. /*----------------------------------------------------------------------*/
  744. static FLStatus getPhysicalInfo(Volume vol, IOreq FAR2 *ioreq)
  745. {
  746.   PhysicalInfo FAR2 *physicalInfo = (PhysicalInfo FAR2 *)ioreq->irData;
  747.   physicalInfo->type = vol.flash.type;
  748.   physicalInfo->unitSize = vol.flash.erasableBlockSize;
  749.   physicalInfo->mediaSize = vol.flash.chipSize * vol.flash.noOfChips;
  750.   return flOK;
  751. }
  752. /*----------------------------------------------------------------------*/
  753. /*              p h y s i c a l R e a d   */
  754. /* */
  755. /* Read from a physical address. */
  756. /* */
  757. /* Parameters: */
  758. /* vol : Pointer identifying drive */
  759. /* irAddress : Physical address to read from. */
  760. /* irByteCount : Number of bytes to read. */
  761. /*      irData : Address of user buffer to read into. */
  762. /*                                                                      */
  763. /* Returns:                                                             */
  764. /* FLStatus : 0 on success, otherwise failed                */
  765. /*----------------------------------------------------------------------*/
  766. static FLStatus physicalRead(Volume vol, IOreq FAR2 *ioreq)
  767. {
  768.   /* check that we are reading whithin the media boundaries */
  769.   if (ioreq->irAddress + (long)ioreq->irByteCount > vol.flash.chipSize *
  770.     vol.flash.noOfChips)
  771.     return flBadParameter;
  772.   /* We don't read accross a unit boundary */
  773.   if ((long)ioreq->irByteCount > vol.flash.erasableBlockSize -
  774.    (ioreq->irAddress % vol.flash.erasableBlockSize))
  775.     return flBadParameter;
  776.   vol.flash.read(&vol.flash, ioreq->irAddress, ioreq->irData,
  777.  ioreq->irByteCount, 0);
  778.   return flOK;
  779. }
  780. /*----------------------------------------------------------------------*/
  781. /*              p h y s i c a l W r i t e   */
  782. /* */
  783. /* Write to a physical address. */
  784. /* */
  785. /* Parameters: */
  786. /* vol : Pointer identifying drive */
  787. /* irAddress : Physical address to write to. */
  788. /* irByteCount : Number of bytes to write. */
  789. /*      irData : Address of user buffer to write from. */
  790. /*                                                                      */
  791. /* Returns:                                                             */
  792. /* FLStatus : 0 on success, otherwise failed                */
  793. /*----------------------------------------------------------------------*/
  794. static FLStatus physicalWrite(Volume vol, IOreq FAR2 *ioreq)
  795. {
  796.   /* check that we are writing whithin the media boundaries */
  797.   if (ioreq->irAddress + (long)ioreq->irByteCount > vol.flash.chipSize *
  798.        vol.flash.noOfChips)
  799.     return flBadParameter;
  800.   /* We don't write accross a unit boundary */
  801.   if ((long)ioreq->irByteCount > vol.flash.erasableBlockSize -
  802.    (ioreq->irAddress % vol.flash.erasableBlockSize))
  803.     return flBadParameter;
  804.   checkStatus(vol.flash.write(&vol.flash, ioreq->irAddress, ioreq->irData,
  805.       ioreq->irByteCount, 0));
  806.   return flOK;
  807. }
  808. /*----------------------------------------------------------------------*/
  809. /*              p h y s i c a l E r a s e   */
  810. /* */
  811. /* Erase physical units. */
  812. /* */
  813. /* Parameters: */
  814. /* vol : Pointer identifying drive */
  815. /* irUnitNo : First unit to erase. */
  816. /* irUnitCount : Number of units to erase. */
  817. /*                                                                      */
  818. /* Returns:                                                             */
  819. /* FLStatus : 0 on success, otherwise failed                */
  820. /*----------------------------------------------------------------------*/
  821. static FLStatus physicalErase(Volume vol, IOreq FAR2 *ioreq)
  822. {
  823.   if (ioreq->irUnitNo + (long)ioreq->irUnitCount > vol.flash.chipSize * vol.flash.noOfChips /
  824.        vol.flash.erasableBlockSize)
  825.     return flBadParameter;
  826.   checkStatus(vol.flash.erase(&vol.flash, ioreq->irUnitNo, ioreq->irUnitCount));
  827.   return flOK;
  828. }
  829. #endif /* LOW_LEVEL */
  830. #if FILES > 0
  831. /*----------------------------------------------------------------------*/
  832. /*          f l u s h B u f f e r */
  833. /* */
  834. /* Writes the buffer contents if it is dirty.                           */
  835. /* */
  836. /* If this is a FAT sector, all FAT copies are written. */
  837. /*                                                                      */
  838. /* Parameters:                                                          */
  839. /* vol : Pointer identifying drive */
  840. /*                                                                      */
  841. /* Returns:                                                             */
  842. /* FLStatus : 0 on success, otherwise failed                */
  843. /*----------------------------------------------------------------------*/
  844. static FLStatus flushBuffer(Volume vol)
  845. {
  846.   if (buffer.dirty) {
  847.     FLStatus status;
  848.     unsigned i;
  849. #ifdef SINGLE_BUFFER
  850.     Volume *bufferOwner = (Volume *) buffer.owner;
  851.     if (&vol != bufferOwner) {
  852.       flFreeMutex(&execInProgress);
  853.       checkStatus(setBusy(bufferOwner,TFFS_ON));
  854.     }
  855. #else
  856.     Volume *bufferOwner = &vol;
  857. #endif
  858.     status = (*bufferOwner).tl.writeSector((*bufferOwner).tl.rec, buffer.sectorNo,
  859.    buffer.data);
  860.     if (status == flOK) {
  861.       if (buffer.sectorNo >= (*bufferOwner).firstFATSectorNo &&
  862.   buffer.sectorNo < (*bufferOwner).secondFATSectorNo)
  863. for (i = 1; i < (*bufferOwner).numberOfFATS; i++)
  864.   checkStatus((*bufferOwner).tl.writeSector((*bufferOwner).tl.rec,
  865.     buffer.sectorNo + i * (*bufferOwner).sectorsPerFAT,
  866.     buffer.data));
  867.     }
  868.     else
  869.       buffer.sectorNo = UNASSIGNED_SECTOR;
  870. #ifdef SINGLE_BUFFER
  871.     if (&vol != bufferOwner) {
  872.       setBusy(bufferOwner,TFFS_OFF);
  873.       if (!flTakeMutex(&execInProgress,1))
  874. return flGeneralFailure;
  875.     }
  876. #endif
  877.     buffer.dirty = buffer.checkPoint = FALSE;
  878.     return status;
  879.   }
  880.   else
  881.     return flOK;
  882. }
  883. /*----------------------------------------------------------------------*/
  884. /*         u p d a t e S e c t o r */
  885. /* */
  886. /* Prepares a sector for update in the buffer */
  887. /*                                                                      */
  888. /* Parameters:                                                          */
  889. /* vol : Pointer identifying drive */
  890. /* sectorNo : Sector no. to read */
  891. /* read : Whether to initialize buffer by reading, or */
  892. /*                        clearing */
  893. /*                                                                      */
  894. /* Returns:                                                             */
  895. /* FLStatus : 0 on success, otherwise failed                */
  896. /*----------------------------------------------------------------------*/
  897. static FLStatus updateSector(Volume vol, SectorNo sectorNo, FLBoolean read)
  898. {
  899.   if (sectorNo != buffer.sectorNo || &vol != buffer.owner) {
  900.     const void FAR0 *mappedSector;
  901.     checkStatus(flushBuffer(&vol));
  902. #ifdef SINGLE_BUFFER
  903.   }
  904.   if (!buffer.dirty) {
  905.     long sectorsNeeded = 20;
  906.     checkStatus(vol.tl.defragment(vol.tl.rec,&sectorsNeeded));
  907.   }
  908.   if (sectorNo != buffer.sectorNo || &vol != buffer.owner) {
  909.     const void FAR0 *mappedSector;
  910. #endif
  911.     buffer.sectorNo = sectorNo;
  912.     buffer.owner = &vol;
  913.     if (read) {
  914.       mappedSector = vol.tl.mapSector(vol.tl.rec,sectorNo);
  915.       if (mappedSector)
  916. tffscpy(buffer.data,mappedSector,SECTOR_SIZE);
  917.       else
  918. return flSectorNotFound;
  919.     }
  920.     else
  921.       tffsset(buffer.data,0,SECTOR_SIZE);
  922.   }
  923.   buffer.dirty = TRUE;
  924.   return flOK;
  925. }
  926. /*----------------------------------------------------------------------*/
  927. /*    f i r s t S e c t o r O f C l u s t e r */
  928. /* */
  929. /* Get sector no. corresponding to cluster no. */
  930. /*                                                                      */
  931. /* Parameters:                                                          */
  932. /* vol : Pointer identifying drive */
  933. /* cluster : Cluster no.                                   */
  934. /*                                                                      */
  935. /* Returns:                                                             */
  936. /* first sector no. of cluster */
  937. /*----------------------------------------------------------------------*/
  938. static SectorNo firstSectorOfCluster(Volume vol, unsigned cluster)
  939. {
  940.   return (SectorNo) (cluster - 2) * vol.sectorsPerCluster +
  941.  vol.firstDataSectorNo;
  942. }
  943. /*----------------------------------------------------------------------*/
  944. /*        g e t D i r E n t r y     */
  945. /* */
  946. /* Get a read-only copy of a directory entry. */
  947. /*                                                                      */
  948. /* Parameters:                                                          */
  949. /* vol : Pointer identifying drive */
  950. /* file : File belonging to directory entry */
  951. /*                                                                      */
  952. /* Returns:                                                             */
  953. /* dirEntry : Pointer to directory entry */
  954. /*----------------------------------------------------------------------*/
  955. static const DirectoryEntry FAR0 *getDirEntry(File *file)
  956. {
  957.   return (DirectoryEntry FAR0 *) findSector(file->fileVol,file->directorySector) +
  958.  file->directoryIndex;
  959. }
  960. /*----------------------------------------------------------------------*/
  961. /*   g e t D i r E n t r y F o r U p d a t e */
  962. /* */
  963. /* Read a directory sector into the sector buffer and point to an */
  964. /* entry, with the intention of modifying it. */
  965. /* The buffer will be flushed on operation exit. */
  966. /*                                                                      */
  967. /* Parameters:                                                          */
  968. /* vol : Pointer identifying drive */
  969. /* file : File belonging to directory entry */
  970. /*                                                                      */
  971. /* Returns:                                                             */
  972. /* FLStatus : 0 on success, otherwise failed                */
  973. /* dirEntry : Pointer to directory entry in buffer */
  974. /*----------------------------------------------------------------------*/
  975. static FLStatus getDirEntryForUpdate(File *file, DirectoryEntry * *dirEntry)
  976. {
  977.   Volume vol = file->fileVol;
  978.   checkStatus(updateSector(file->fileVol,file->directorySector,TRUE));
  979.   *dirEntry = directory + file->directoryIndex;
  980.   buffer.checkPoint = TRUE;
  981.   return flOK;
  982. }
  983. /*----------------------------------------------------------------------*/
  984. /*   s e t C u r r e n t D a t e T i m e                   */
  985. /* */
  986. /* Set current time/date in directory entry                             */
  987. /*                                                                      */
  988. /* Parameters:                                                          */
  989. /* dirEntry : Pointer to directory entry                    */
  990. /*                                                                      */
  991. /*----------------------------------------------------------------------*/
  992. static void setCurrentDateTime(DirectoryEntry *dirEntry)
  993. {
  994.   toLE2(dirEntry->updateTime,flCurrentTime());
  995.   toLE2(dirEntry->updateDate,flCurrentDate());
  996. }
  997. /*----------------------------------------------------------------------*/
  998. /*         g e t F A T e n t r y */
  999. /* */
  1000. /* Get an entry from the FAT. The 1st FAT copy is used. */
  1001. /*                                                                      */
  1002. /* Parameters:                                                          */
  1003. /* vol : Pointer identifying drive */
  1004. /* cluster : Cluster no. of enrty. */
  1005. /*                                                                      */
  1006. /* Returns:                                                             */
  1007. /* Value of FAT entry. */
  1008. /*----------------------------------------------------------------------*/
  1009. static unsigned getFATentry(Volume vol, unsigned cluster)
  1010. {
  1011.   LEushort FAR0 *fat16Sector;
  1012.   unsigned fatSectorNo = vol.firstFATSectorNo;
  1013. #ifdef FAT_12BIT
  1014.   if (vol.flags & VOLUME_12BIT_FAT)
  1015.     fatSectorNo += (cluster * 3) >> (SECTOR_SIZE_BITS + 1);
  1016.   else
  1017. #endif
  1018.     fatSectorNo += cluster >> (SECTOR_SIZE_BITS - 1);
  1019. #ifndef SINGLE_BUFFER
  1020.   if (!buffer.dirty) {
  1021.     /* If the buffer is free, use it to store this FAT sector */
  1022.     updateSector(&vol,fatSectorNo,TRUE);
  1023.     buffer.dirty = FALSE;
  1024.   }
  1025. #endif
  1026.   fat16Sector = (LEushort FAR0 *) findSector(&vol,fatSectorNo);
  1027. #ifdef FAT_12BIT
  1028.   if (vol.flags & VOLUME_12BIT_FAT) {
  1029.     unsigned char FAR0 *fat12Sector = (unsigned char FAR0 *) fat16Sector;
  1030.     unsigned halfByteOffset = (cluster * 3) & (SECTOR_SIZE * 2 - 1);
  1031.     unsigned char firstByte = fat12Sector[halfByteOffset / 2];
  1032.     halfByteOffset += 2;
  1033.     if (halfByteOffset >= SECTOR_SIZE * 2) {
  1034.       /* Entry continues on the next sector. What a mess */
  1035.       halfByteOffset -= SECTOR_SIZE * 2;
  1036.       fat12Sector = (unsigned char FAR0 *) findSector(&vol,fatSectorNo + 1);
  1037.     }
  1038.     if (halfByteOffset & 1)
  1039.       return ((firstByte & 0xf0) >> 4) + (fat12Sector[halfByteOffset / 2] << 4);
  1040.     else
  1041.       return firstByte + ((fat12Sector[halfByteOffset / 2] & 0xf) << 8);
  1042.   }
  1043.   else
  1044. #endif
  1045.     return LE2(fat16Sector[cluster & (SECTOR_SIZE / 2 - 1)]);
  1046. }
  1047. /*----------------------------------------------------------------------*/
  1048. /*          s e t F A T e n t r y */
  1049. /* */
  1050. /* Writes a new value to a given FAT cluster entry.                     */
  1051. /*                                                                      */
  1052. /* Parameters:                                                          */
  1053. /* vol : Pointer identifying drive */
  1054. /* cluster : Cluster no. of enrty. */
  1055. /* entry : New value of FAT entry. */
  1056. /*                                                                      */
  1057. /* Returns:                                                             */
  1058. /* FLStatus : 0 on success, otherwise failed                */
  1059. /*----------------------------------------------------------------------*/
  1060. static FLStatus setFATentry(Volume vol, unsigned cluster, unsigned entry)
  1061. {
  1062.   LEushort *fat16Sector;
  1063.   unsigned fatSectorNo = vol.firstFATSectorNo;
  1064. #ifdef FAT_12BIT
  1065.   if (vol.flags & VOLUME_12BIT_FAT)
  1066.     fatSectorNo += (cluster * 3) >> (SECTOR_SIZE_BITS + 1);
  1067.   else
  1068. #endif
  1069.     fatSectorNo += cluster >> (SECTOR_SIZE_BITS - 1);
  1070.   checkStatus(updateSector(&vol,fatSectorNo,TRUE));
  1071.   fat16Sector = (LEushort *) buffer.data;
  1072. #ifdef FAT_12BIT
  1073.   if (vol.flags & VOLUME_12BIT_FAT) {
  1074.     unsigned char *fat12Sector = (unsigned char *) buffer.data;
  1075.     unsigned halfByteOffset = (cluster * 3) & (SECTOR_SIZE * 2 - 1);
  1076.     if (halfByteOffset & 1) {
  1077.       fat12Sector[halfByteOffset / 2] &= 0xf;
  1078.       fat12Sector[halfByteOffset / 2] |= (entry & 0xf) << 4;
  1079.     }
  1080.     else
  1081.       fat12Sector[halfByteOffset / 2] = entry;
  1082.     halfByteOffset += 2;
  1083.     if (halfByteOffset >= SECTOR_SIZE * 2) {
  1084.       /* Entry continues on the next sector. What a mess */
  1085.       halfByteOffset -= SECTOR_SIZE * 2;
  1086.       fatSectorNo++;
  1087.       checkStatus(updateSector(&vol,fatSectorNo,TRUE));
  1088.     }
  1089.     if (halfByteOffset & 1)
  1090.       fat12Sector[halfByteOffset / 2] = entry >> 4;
  1091.     else {
  1092.       fat12Sector[halfByteOffset / 2] &= 0xf0;
  1093.       fat12Sector[halfByteOffset / 2] |= (entry & 0x0f00) >> 8;
  1094.     }
  1095.   }
  1096.   else
  1097. #endif
  1098.     toLE2(fat16Sector[cluster & (SECTOR_SIZE / 2 - 1)],entry);
  1099.   return flOK;
  1100. }
  1101. /*----------------------------------------------------------------------*/
  1102. /*         a l l o c a t e C l u s t e r */
  1103. /* */
  1104. /* Allocates a new cluster for a file and adds it to a FAT chain or     */
  1105. /* marks it in a directory entry. */
  1106. /*                                                                      */
  1107. /* Parameters:                                                          */
  1108. /* vol : Pointer identifying drive */
  1109. /* file : File to extend. It should be positioned at    */
  1110. /*   end-of-file. */
  1111. /*                                                                      */
  1112. /* Returns:                                                             */
  1113. /* FLStatus : 0 on success, otherwise failed                */
  1114. /*----------------------------------------------------------------------*/
  1115. static FLStatus allocateCluster(File *file)
  1116. {
  1117.   Volume vol = file->fileVol;
  1118.   unsigned originalRover;
  1119.   if (file->flags & FILE_READ_ONLY)
  1120.     return flNoWriteAccess;
  1121.   /* Look for a free cluster. Start at the allocation rover */
  1122.   originalRover = vol.allocationRover;
  1123.   do {
  1124.     vol.allocationRover++;
  1125.     if (vol.allocationRover > vol.maxCluster)
  1126.       vol.allocationRover = 2; /* wraparound to start of volume */
  1127.     if (vol.allocationRover == originalRover)
  1128.       return flNoSpaceInVolume;
  1129.   } while (getFATentry(&vol,vol.allocationRover) != FAT_FREE);
  1130.   /* Found a free cluster. Mark it as an end of chain */
  1131.   checkStatus(setFATentry(&vol,vol.allocationRover,FAT_LAST_CLUSTER));
  1132.   /* Mark previous cluster or directory to point to it */
  1133.   if (file->currentCluster == 0) {
  1134.     DirectoryEntry *dirEntry;
  1135.     checkStatus(getDirEntryForUpdate(file,&dirEntry));
  1136.     toLE2(dirEntry->startingCluster,vol.allocationRover);
  1137.     setCurrentDateTime(dirEntry);
  1138.   }
  1139.   else
  1140.     checkStatus(setFATentry(&vol,file->currentCluster,vol.allocationRover));
  1141.   /* Set our new current cluster */
  1142.   file->currentCluster = vol.allocationRover;
  1143.   return flOK;
  1144. }
  1145. /*----------------------------------------------------------------------*/
  1146. /*      g e t S e c t o r A n d O f f s e t */
  1147. /* */
  1148. /* Based on the current position of a file, gets a sector number and    */
  1149. /* offset in the sector that marks the file's current position. */
  1150. /* If the position is at the end-of-file, and the file is opened for    */
  1151. /* write, this routine will extend the file by allocating a new cluster.*/
  1152. /*                                                                      */
  1153. /* Parameters:                                                          */
  1154. /* vol : Pointer identifying drive */
  1155. /*                                                                      */
  1156. /* Returns:                                                             */
  1157. /* FLStatus : 0 on success, otherwise failed                */
  1158. /*----------------------------------------------------------------------*/
  1159. static FLStatus getSectorAndOffset(File *file,
  1160.  SectorNo *sectorNo,
  1161.  unsigned *offsetInSector)
  1162. {
  1163.   Volume vol = file->fileVol;
  1164.   unsigned offsetInCluster =
  1165.       (unsigned) file->currentPosition & (vol.bytesPerCluster - 1);
  1166.   if (offsetInCluster == 0) { /* This cluster is finished. Get next */
  1167.     if (file->flags & FILE_IS_ROOT_DIR) {
  1168.       if (file->currentPosition >= file->fileSize)
  1169. return flRootDirectoryFull;
  1170.     }
  1171.     else {
  1172.       if (file->currentPosition >= file->fileSize) {
  1173. checkStatus(allocateCluster(file));
  1174.       }
  1175.       else {
  1176. unsigned nextCluster;
  1177. if (file->currentCluster == 0)
  1178.   nextCluster = LE2(getDirEntry(file)->startingCluster);
  1179. else
  1180.   nextCluster = getFATentry(&vol,file->currentCluster);
  1181. if (nextCluster < 2 || nextCluster > vol.maxCluster)
  1182.   /* We have a bad file size, or the FAT is bad */
  1183.   return flInvalidFATchain;
  1184. file->currentCluster = nextCluster;
  1185.       }
  1186.     }
  1187.   }
  1188.   *offsetInSector = offsetInCluster & (SECTOR_SIZE - 1);
  1189.   if (file->flags & FILE_IS_ROOT_DIR)
  1190.     *sectorNo = vol.rootDirectorySectorNo +
  1191.       (SectorNo) (file->currentPosition >> SECTOR_SIZE_BITS);
  1192.   else
  1193.     *sectorNo = firstSectorOfCluster(&vol,file->currentCluster) +
  1194.       (SectorNo) (offsetInCluster >> SECTOR_SIZE_BITS);
  1195.   return flOK;
  1196. }
  1197. /*----------------------------------------------------------------------*/
  1198. /*         c l o s e F i l e */
  1199. /* */
  1200. /* Closes an open file, records file size and dates in directory and    */
  1201. /* releases file handle. */
  1202. /*                                                                      */
  1203. /* Parameters:                                                          */
  1204. /* vol : Pointer identifying drive */
  1205. /* file : File to close.                 */
  1206. /*                                                                      */
  1207. /* Returns:                                                             */
  1208. /* FLStatus : 0 on success, otherwise failed                */
  1209. /*----------------------------------------------------------------------*/
  1210. static FLStatus closeFile(File *file)
  1211. {
  1212.   if ((file->flags & FILE_MODIFIED) && !(file->flags & FILE_IS_ROOT_DIR)) {
  1213.     DirectoryEntry *dirEntry;
  1214.     checkStatus(getDirEntryForUpdate(file,&dirEntry));
  1215.     dirEntry->attributes |= ATTR_ARCHIVE;
  1216.     if (!(file->flags & FILE_IS_DIRECTORY))
  1217.       toLE4(dirEntry->fileSize,file->fileSize);
  1218.     setCurrentDateTime(dirEntry);
  1219.   }
  1220.   file->flags = 0; /* no longer open */
  1221.   return flOK;
  1222. }
  1223. #ifdef SUB_DIRECTORY
  1224. /*----------------------------------------------------------------------*/
  1225. /*         e x t e n d D i r e c t o r y */
  1226. /* */
  1227. /* Extends a directory, writing empty entries and the mandatory '.' and */
  1228. /* '..' entries. */
  1229. /*                                                                      */
  1230. /* Parameters:                                                          */
  1231. /* vol : Pointer identifying drive */
  1232. /* file : Directory file to extend. On entry,         */
  1233. /*   currentPosition == fileSize. On exit, fileSize*/
  1234. /*   is updated. */
  1235. /* ownerDir : Cluster no. of owner directory */
  1236. /*                                                                      */
  1237. /* Returns:                                                             */
  1238. /* FLStatus : 0 on success, otherwise failed                */
  1239. /*----------------------------------------------------------------------*/
  1240. static FLStatus extendDirectory(File *file, unsigned ownerDir)
  1241. {
  1242.   Volume vol = file->fileVol;
  1243.   unsigned i;
  1244.   SectorNo sectorOfDir;
  1245.   unsigned offsetInSector;
  1246.   /* Assuming the current position is at the end-of-file, this will     */
  1247.   /* extend the directory. */
  1248.   checkStatus(getSectorAndOffset(file,&sectorOfDir,&offsetInSector));
  1249.   for (i = 0; i < vol.sectorsPerCluster; i++) {
  1250.     /* Write binary zeroes to indicate never-used entries */
  1251.     checkStatus(updateSector(&vol,sectorOfDir + i,FALSE));
  1252.     buffer.checkPoint = TRUE;
  1253.     if (file->currentPosition == 0 && i == 0) {
  1254.       /* Add the mandatory . and .. entries */
  1255.       tffscpy(directory[0].name,".          ",sizeof directory[0].name);
  1256.       directory[0].attributes = ATTR_ARCHIVE | ATTR_DIRECTORY;
  1257.       toLE2(directory[0].startingCluster,file->currentCluster);
  1258.       toLE4(directory[0].fileSize,0);
  1259.       setCurrentDateTime(&directory[0]);
  1260.       tffscpy(&directory[1],&directory[0],sizeof directory[0]);
  1261.       directory[1].name[1] = '.'; /* change . to .. */
  1262.       toLE2(directory[1].startingCluster,ownerDir);
  1263.     }
  1264.     file->fileSize += SECTOR_SIZE;
  1265.   }
  1266.   /* Update the parent directory by closing the file */
  1267.   file->flags |= FILE_MODIFIED;
  1268.   return closeFile(file);
  1269. }
  1270. #endif /* SUB_DIRECTORY */
  1271. /*----------------------------------------------------------------------*/
  1272. /*         f i n d D i r E n t r y */
  1273. /* */
  1274. /* Finds a directory entry by path-name, or finds an available directory*/
  1275. /* entry if the file does not exist. */
  1276. /* Most fields necessary for opening a file are set by this routine. */
  1277. /*                                                                      */
  1278. /* Parameters:                                                          */
  1279. /* vol : Pointer identifying drive */
  1280. /* path : path to find */
  1281. /*      file            : File in which to record directory information.*/
  1282. /*                        Specific fields on entry: */
  1283. /*     flags: if FILE_MUST_OPEN = 1, directory  */
  1284. /*    will be extended if necessary. */
  1285. /*   on exit: */
  1286. /*     flags: FILE_IS_DIRECTORY and             */
  1287. /*    FILE_IS_ROOT_DIR set if true. */
  1288. /*     fileSize: Set for non-directory files.   */
  1289. /*                          currentCluster: Set to 0 (unknown) */
  1290. /*     ownerDirCluster: Set to 1st cluster of      */
  1291. /*       owning directory. */
  1292. /*     directorySector: Sector of directory. If 0  */
  1293. /*       entry not found and directory full*/
  1294. /*     directoryEntry: Entry # in directory sector */
  1295. /*     currentPosition: not set by this routine.   */
  1296. /* */
  1297. /* Returns:                                                             */
  1298. /* FLStatus : 0 on success and file found */
  1299. /*   flFileNotFound on success and file not found */
  1300. /*   otherwise failed. */
  1301. /*----------------------------------------------------------------------*/
  1302. static FLStatus findDirEntry(Volume vol, FLSimplePath FAR1 *path, File *file)
  1303. {
  1304.   File scanFile; /* Internal file of search */
  1305.   unsigned dirBackPointer = 0; /* 1st cluster of previous directory */
  1306.   FLStatus status = flOK; /* root directory exists */
  1307.   file->flags |= (FILE_IS_ROOT_DIR | FILE_IS_DIRECTORY);
  1308.   file->fileSize = (long) (vol.sectorsInRootDirectory) << SECTOR_SIZE_BITS;
  1309.   file->fileVol = &vol;
  1310. #ifdef SUB_DIRECTORY
  1311.   for (; path->name[0]; path++) /* while we have another path segment */
  1312. #else
  1313.   if (path->name[0])    /* search the root directory */
  1314. #endif
  1315.   {
  1316.     status = flFileNotFound; /* not yet */
  1317.     if (!(file->flags & FILE_IS_DIRECTORY))
  1318.       return flPathNotFound;  /* if we don't have a directory,
  1319.        we have no business here */
  1320.     scanFile = *file;     /* the previous file found becomes the scan file */
  1321.     scanFile.currentPosition = 0;
  1322.     file->directorySector = 0; /* indicate no entry found yet */
  1323.     file->flags &= ~(FILE_IS_ROOT_DIR | FILE_IS_DIRECTORY);
  1324.     file->ownerDirCluster = dirBackPointer;
  1325.     file->fileSize = 0;
  1326.     file->currentCluster = 0;
  1327.     /* Scan directory */
  1328.     while (scanFile.currentPosition < scanFile.fileSize) {
  1329.       int i;
  1330.       DirectoryEntry FAR0 *dirEntry;
  1331.       SectorNo sectorOfDir;
  1332.       unsigned offsetInSector;
  1333.       FLStatus readStatus = getSectorAndOffset(&scanFile,&sectorOfDir,&offsetInSector);
  1334.       if (readStatus == flInvalidFATchain) {
  1335. scanFile.fileSize = scanFile.currentPosition; /* now we know */
  1336. break; /* we ran into the end of the directory file */
  1337.       }
  1338.       else if (readStatus != flOK)
  1339. return readStatus;
  1340.       dirEntry = (DirectoryEntry FAR0 *) findSector(&vol,sectorOfDir);
  1341.       if (dirEntry == NULL)
  1342. return flSectorNotFound;
  1343.       scanFile.currentPosition += SECTOR_SIZE;
  1344.       for (i = 0; i < DIRECTORY_ENTRIES_PER_SECTOR; i++, dirEntry++) {
  1345. if (tffscmp(path,dirEntry->name,sizeof dirEntry->name) == 0 &&
  1346.     !(dirEntry->attributes & ATTR_VOL_LABEL)) {
  1347.   /* Found a match */
  1348.   file->directorySector = sectorOfDir;
  1349.   file->directoryIndex = i;
  1350.   file->fileSize = LE4(dirEntry->fileSize);
  1351.   if (dirEntry->attributes & ATTR_DIRECTORY) {
  1352.     file->flags |= FILE_IS_DIRECTORY;
  1353.     file->fileSize = 0x7fffffffl;
  1354.     /* Infinite. Directories don't have a recorded size */
  1355.   }
  1356.   if (dirEntry->attributes & ATTR_READ_ONLY)
  1357.     file->flags |= FILE_READ_ONLY;
  1358.   dirBackPointer = LE2(dirEntry->startingCluster);
  1359.   status = flOK;
  1360.   goto endOfPathSegment;
  1361. }
  1362. else if (dirEntry->name[0] == NEVER_USED_DIR_ENTRY ||
  1363.  dirEntry->name[0] == DELETED_DIR_ENTRY) {
  1364.   /* Found a free entry. Remember it in case we don't find a match */
  1365.   if (file->directorySector == 0) {
  1366.     file->directorySector = sectorOfDir;
  1367.     file->directoryIndex = i;
  1368.   }
  1369.   if (dirEntry->name[0] == NEVER_USED_DIR_ENTRY) /* end of dir */
  1370.     goto endOfPathSegment;
  1371. }
  1372.       }
  1373.     }
  1374. endOfPathSegment:
  1375.     ;
  1376.   }
  1377.   if (status == flFileNotFound && (file->flags & FILE_MUST_OPEN) &&
  1378.       file->directorySector == 0) {
  1379.     /* We did not find a place in the directory for this new entry. The */
  1380.     /* directory should be extended. 'scanFile' refers to the directory */
  1381.     /* to extend, and the current pointer is at its end */
  1382. #ifdef SUB_DIRECTORY
  1383.     checkStatus(extendDirectory(&scanFile,(unsigned) file->ownerDirCluster));
  1384.     file->directorySector = firstSectorOfCluster(&vol,scanFile.currentCluster);
  1385.     file->directoryIndex = 0;       /* Has to be. This is a new cluster */
  1386. #else
  1387.     status = flRootDirectoryFull;
  1388. #endif
  1389.   }
  1390.   return status;
  1391. }
  1392. /*----------------------------------------------------------------------*/
  1393. /*         r e a d F i l e */
  1394. /* */
  1395. /* Reads from the current position in the file to the user-buffer. */
  1396. /* Parameters:                                                          */
  1397. /*      file : File to read. */
  1398. /*      ioreq->irData : Address of user buffer */
  1399. /* ioreq->irLength : Number of bytes to read. If the read extends  */
  1400. /*   beyond the end-of-file, the read is truncated */
  1401. /*   at the end-of-file. */
  1402. /*                                                                      */
  1403. /* Returns:                                                             */
  1404. /* FLStatus : 0 on success, otherwise failed                */
  1405. /* ioreq->irLength : Actual number of bytes read */
  1406. /*----------------------------------------------------------------------*/
  1407. static FLStatus readFile(File *file,IOreq FAR2 *ioreq)
  1408. {
  1409.   Volume vol = file->fileVol;
  1410.   char FAR1 *userData = (char FAR1 *) ioreq->irData;   /* user buffer address */
  1411.   unsigned long stillToRead = ioreq->irLength;
  1412.   unsigned long remainingInFile = file->fileSize - file->currentPosition;
  1413.   ioreq->irLength = 0; /* read so far */
  1414.   /* Should we return an end of file status ? */
  1415.   if (stillToRead > remainingInFile)
  1416.     stillToRead = (unsigned) remainingInFile;
  1417.   while (stillToRead > 0) {
  1418.     SectorNo sectorToRead;
  1419.     unsigned offsetInSector;
  1420.     unsigned readThisTime;
  1421.     const char FAR0 *sector;
  1422.     checkStatus(getSectorAndOffset(file,&sectorToRead,&offsetInSector));
  1423.     sector = (const char FAR0 *) findSector(&vol,sectorToRead);
  1424.     readThisTime = SECTOR_SIZE - offsetInSector;
  1425.     if (readThisTime > stillToRead)
  1426.       readThisTime = (unsigned) stillToRead;
  1427.     if (sector)
  1428.       tffscpy(userData,sector + offsetInSector,readThisTime);
  1429.     else
  1430.       return flSectorNotFound; /* Sector does not exist */
  1431.     stillToRead -= readThisTime;
  1432.     ioreq->irLength += readThisTime;
  1433.     userData += readThisTime;
  1434.     file->currentPosition += readThisTime;
  1435.   }
  1436.   return flOK;
  1437. }
  1438. /*----------------------------------------------------------------------*/
  1439. /*  f l F i n d N e x t F i l e */
  1440. /*                                                                      */
  1441. /* See the description of 'flFindFirstFile'. */
  1442. /*                                                                      */
  1443. /* Parameters:                                                          */
  1444. /* irHandle : File handle returned by flFindFirstFile. */
  1445. /* irData : Address of user buffer to receive a */
  1446. /*   DirectoryEntry structure */
  1447. /*                                                                      */
  1448. /* Returns:                                                             */
  1449. /* FLStatus : 0 on success, otherwise failed                */
  1450. /*----------------------------------------------------------------------*/
  1451. static FLStatus findNextFile(File *file, IOreq FAR2 *ioreq)
  1452. {
  1453.   DirectoryEntry FAR1 *irDirEntry = (DirectoryEntry FAR1 *) ioreq->irData;
  1454.   /* Do we have a directory ? */
  1455.   if (!(file->flags & FILE_IS_DIRECTORY))
  1456.     return flNotADirectory;
  1457.   ioreq->irLength = DIRECTORY_ENTRY_SIZE;
  1458.   do {
  1459.     readFile(file,ioreq);
  1460.     if (ioreq->irLength != DIRECTORY_ENTRY_SIZE ||
  1461. irDirEntry->name[0] == NEVER_USED_DIR_ENTRY) {
  1462.       checkStatus(closeFile(file));
  1463.       return flNoMoreFiles;
  1464.     }
  1465.   } while (irDirEntry->name[0] == DELETED_DIR_ENTRY ||
  1466.    (irDirEntry->attributes & ATTR_VOL_LABEL));
  1467.   return flOK;
  1468. }
  1469. /*----------------------------------------------------------------------*/
  1470. /*          d e l e t e F i l e */
  1471. /* */
  1472. /* Deletes a file or directory.                                         */
  1473. /*                                                                      */
  1474. /* Parameters:                                                          */
  1475. /* ioreq->irPath : path of file to delete */
  1476. /* isDirectory : 0 = delete file, other = delete directory */
  1477. /*                                                                      */
  1478. /* Returns:                                                             */
  1479. /* FLStatus : 0 on success, otherwise failed                */
  1480. /*----------------------------------------------------------------------*/
  1481. static FLStatus deleteFile(Volume vol, IOreq FAR2 *ioreq, FLBoolean isDirectory)
  1482. {
  1483.   File file; /* Our private file */
  1484.   DirectoryEntry *dirEntry;
  1485.   file.flags = 0;
  1486.   checkStatus(findDirEntry(&vol,ioreq->irPath,&file));
  1487.   if (file.flags & FILE_READ_ONLY)
  1488.     return flNoWriteAccess;
  1489.   if (isDirectory) {
  1490.     DirectoryEntry fileFindInfo;
  1491.     ioreq->irData = &fileFindInfo;
  1492.     if (!(file.flags & FILE_IS_DIRECTORY))
  1493.       return flNotADirectory;
  1494.     /* Verify that directory is empty */
  1495.     file.currentPosition = 0;
  1496.     for (;;) {
  1497.       FLStatus status = findNextFile(&file,ioreq);
  1498.       if (status == flNoMoreFiles)
  1499. break;
  1500.       if (status != flOK)
  1501. return status;
  1502.       if (!((fileFindInfo.attributes & ATTR_DIRECTORY) &&
  1503.     (tffscmp(fileFindInfo.name,".          ",sizeof fileFindInfo.name) == 0 ||
  1504.      tffscmp(fileFindInfo.name,"..         ",sizeof fileFindInfo.name) == 0)))
  1505. return flDirectoryNotEmpty;
  1506.     }
  1507.   }
  1508.   else {
  1509.     /* Did we find a directory ? */
  1510.     if (file.flags & FILE_IS_DIRECTORY)
  1511.       return flFileIsADirectory;
  1512.   }
  1513.   /* Mark directory entry deleted */
  1514.   checkStatus(getDirEntryForUpdate(&file,&dirEntry));
  1515.   dirEntry->name[0] = DELETED_DIR_ENTRY;
  1516.   /* Delete FAT entries */
  1517.   file.currentPosition = 0;
  1518.   file.currentCluster = LE2(dirEntry->startingCluster);
  1519.   while (file.currentPosition < file.fileSize) {
  1520.     unsigned nextCluster;
  1521.     if (file.currentCluster < 2 || file.currentCluster > vol.maxCluster)
  1522.       /* We have a bad file size, or the FAT is bad */
  1523.       return isDirectory ? flOK : flInvalidFATchain;
  1524.     nextCluster = getFATentry(&vol,file.currentCluster);
  1525.     /* mark FAT free */
  1526.     checkStatus(setFATentry(&vol,file.currentCluster,FAT_FREE));
  1527.     buffer.checkPoint = TRUE;
  1528.     /* mark sectors free */
  1529.     checkStatus(vol.tl.deleteSector(vol.tl.rec,
  1530.     firstSectorOfCluster(&vol,file.currentCluster),
  1531.     vol.sectorsPerCluster));
  1532.     file.currentPosition += vol.bytesPerCluster;
  1533.     file.currentCluster = nextCluster;
  1534.   }
  1535.   return flOK;
  1536. }
  1537. /*----------------------------------------------------------------------*/
  1538. /*      s e t N a m e I n D i r E n t r y */
  1539. /* */
  1540. /* Sets the file name in a directory entry from a path name             */
  1541. /*                                                                      */
  1542. /* Parameters:                                                          */
  1543. /* dirEntry : directory entry */
  1544. /* path : path the last segment of which is the name */
  1545. /*                                                                      */
  1546. /*----------------------------------------------------------------------*/
  1547. static void setNameInDirEntry(DirectoryEntry *dirEntry, FLSimplePath FAR1 *path)
  1548. {
  1549.   FLSimplePath FAR1 *lastSegment;
  1550.   for (lastSegment = path; /* Find null terminator */
  1551.        lastSegment->name[0];
  1552.        lastSegment++);
  1553.   tffscpy(dirEntry->name,--lastSegment,sizeof dirEntry->name);
  1554. }
  1555. /*----------------------------------------------------------------------*/
  1556. /*          o p e n F i l e */
  1557. /* */
  1558. /* Opens an existing file or creates a new file. Creates a file handle  */
  1559. /* for further file processing. */
  1560. /*                                                                      */
  1561. /* Parameters:                                                          */
  1562. /* ioreq->irFlags : Access and action options, defined below */
  1563. /* ioreq->irPath : path of file to open              */
  1564. /*                                                                      */
  1565. /* Returns:                                                             */
  1566. /* FLStatus : 0 on success, otherwise failed                */
  1567. /* ioreq->irHandle : New file handle for open file                 */
  1568. /*                                                                      */
  1569. /*----------------------------------------------------------------------*/
  1570. static FLStatus openFile(Volume vol, IOreq FAR2 *ioreq)
  1571. {
  1572.   int i;
  1573.   FLStatus status;
  1574.   /* Look for an available file */
  1575.   File *file = fileTable;
  1576.   for (i = 0; i < FILES && (file->flags & FILE_IS_OPEN); i++, file++);
  1577.   if (i >= FILES)
  1578.     return flTooManyOpenFiles;
  1579.   file->fileVol = &vol;
  1580.   ioreq->irHandle = i; /* return file handle */
  1581.   /* Delete file if exists and required */
  1582.   if (ioreq->irFlags & ACCESS_CREATE) {
  1583.     FLStatus status = deleteFile(&vol,ioreq,FALSE);
  1584.     if (status != flOK && status != flFileNotFound)
  1585.       return status;
  1586.   }
  1587.   /* Find the path */
  1588.   if (ioreq->irFlags & ACCESS_CREATE)
  1589.     file->flags |= FILE_MUST_OPEN;
  1590.   status =  findDirEntry(file->fileVol,ioreq->irPath,file);
  1591.   if (status != flOK &&
  1592.       (status != flFileNotFound || !(ioreq->irFlags & ACCESS_CREATE)))
  1593.     return status;
  1594.   /* Did we find a directory ? */
  1595.   if (file->flags & FILE_IS_DIRECTORY)
  1596.     return flFileIsADirectory;
  1597.   /* Create file if required */
  1598.   if (ioreq->irFlags & ACCESS_CREATE) {
  1599.     DirectoryEntry *dirEntry;
  1600.     checkStatus(getDirEntryForUpdate(file,&dirEntry));
  1601.     setNameInDirEntry(dirEntry,ioreq->irPath);
  1602.     dirEntry->attributes = ATTR_ARCHIVE;
  1603.     toLE2(dirEntry->startingCluster,0);
  1604.     toLE4(dirEntry->fileSize,0);
  1605.     setCurrentDateTime(dirEntry);
  1606.   }
  1607.   if (!(ioreq->irFlags & ACCESS_READ_WRITE))
  1608.     file->flags |= FILE_READ_ONLY;
  1609.   file->currentPosition = 0; /* located at file beginning */
  1610.   file->flags |= FILE_IS_OPEN; /* this file now officially open */
  1611.   return flOK;
  1612. }
  1613. /*----------------------------------------------------------------------*/
  1614. /*          w r i t e F i l e */
  1615. /* */
  1616. /* Writes from the current position in the file from the user-buffer.   */
  1617. /*                                                                      */
  1618. /* Parameters:                                                          */
  1619. /* file : File to write. */
  1620. /*      ioreq->irData : Address of user buffer */
  1621. /* ioreq->irLength : Number of bytes to write. */
  1622. /*                                                                      */
  1623. /* Returns:                                                             */
  1624. /* FLStatus : 0 on success, otherwise failed                */
  1625. /* ioreq->irLength : Actual number of bytes written */
  1626. /*----------------------------------------------------------------------*/
  1627. static FLStatus writeFile(File *file, IOreq FAR2 *ioreq)
  1628. {
  1629.   Volume vol = file->fileVol;
  1630.   char FAR1 *userData = (char FAR1 *) ioreq->irData;   /* user buffer address */
  1631.   unsigned long stillToWrite = ioreq->irLength;
  1632.   if (file->flags & FILE_READ_ONLY)
  1633.     return flNoWriteAccess;
  1634.   file->flags |= FILE_MODIFIED;
  1635.   ioreq->irLength = 0; /* written so far */
  1636.   while (stillToWrite > 0) {
  1637.     SectorNo sectorToWrite; /* She's got a sector to write, but she don't care */
  1638.     unsigned offsetInSector;
  1639.     unsigned writeThisTime;
  1640.     checkStatus(getSectorAndOffset(file,&sectorToWrite,&offsetInSector));
  1641.     if (stillToWrite < SECTOR_SIZE || offsetInSector > 0) {
  1642.       /* Not on full sector boundary */
  1643.       checkStatus(updateSector(&vol,sectorToWrite,file->currentPosition < file->fileSize));
  1644.       if (file->flags & FILE_IS_DIRECTORY)
  1645. buffer.checkPoint = TRUE;
  1646.       writeThisTime = SECTOR_SIZE - offsetInSector;
  1647.       if (writeThisTime > stillToWrite)
  1648. writeThisTime = (unsigned) stillToWrite;
  1649.       tffscpy(buffer.data + offsetInSector,userData,writeThisTime);
  1650.     }
  1651.     else {
  1652.       if (sectorToWrite == buffer.sectorNo && &vol == buffer.owner) {
  1653. buffer.sectorNo = UNASSIGNED_SECTOR; /* no longer valid */
  1654. buffer.dirty = buffer.checkPoint = FALSE;
  1655.       }
  1656. #ifdef SINGLE_BUFFER
  1657.       if (buffer.dirty) {
  1658. long int freeWritableSectors = 0;
  1659. vol.tl.defragment(vol.tl.rec,&freeWritableSectors);
  1660. /* No defragmentation, just tell us how much space left */
  1661. if (freeWritableSectors < 20)
  1662.   /* Prevent garbage collection while the buffer is dirty */
  1663.   checkStatus(flushBuffer(&vol));
  1664.       }
  1665. #endif
  1666.       checkStatus(vol.tl.writeSector(vol.tl.rec,sectorToWrite,userData));
  1667.       writeThisTime = SECTOR_SIZE;
  1668.     }
  1669.     stillToWrite -= writeThisTime;
  1670.     ioreq->irLength += writeThisTime;
  1671.     userData += writeThisTime;
  1672.     file->currentPosition += writeThisTime;
  1673.     if (file->currentPosition > file->fileSize)
  1674.       file->fileSize = file->currentPosition;
  1675.   }
  1676.   return flOK;
  1677. }
  1678. /*----------------------------------------------------------------------*/
  1679. /*           s e e k F i l e */
  1680. /* */
  1681. /* Sets the current position in the file, relative to file start, end or*/
  1682. /* current position. */
  1683. /* Note: This function will not move the file pointer beyond the */
  1684. /* beginning or end of file, so the actual file position may be */
  1685. /* different from the required. The actual position is indicated on     */
  1686. /* return. */
  1687. /*                                                                      */
  1688. /* Parameters:                                                          */
  1689. /* file : File to set position.                         */
  1690. /*      ioreq->irLength : Offset to set position. */
  1691. /* ioreq->irFlags : Method code */
  1692. /*   SEEK_START: absolute offset from start of file  */
  1693. /*   SEEK_CURR:  signed offset from current position */
  1694. /*   SEEK_END:   signed offset from end of file    */
  1695. /*                                                                      */
  1696. /* Returns:                                                             */
  1697. /* FLStatus : 0 on success, otherwise failed                */
  1698. /* ioreq->irLength : Actual absolute offset from start of file */
  1699. /*----------------------------------------------------------------------*/
  1700. static FLStatus seekFile(File *file, IOreq FAR2 *ioreq)
  1701. {
  1702.   Volume vol = file->fileVol;
  1703.   long int seekPosition = ioreq->irLength;
  1704.   switch (ioreq->irFlags) {
  1705.     case SEEK_START:
  1706.       break;
  1707.     case SEEK_CURR:
  1708.       seekPosition += file->currentPosition;
  1709.       break;
  1710.     case SEEK_END:
  1711.       seekPosition += file->fileSize;
  1712.       break;
  1713.     default:
  1714.       return flBadParameter;
  1715.   }
  1716.   if (seekPosition < 0)
  1717.     seekPosition = 0;
  1718.   if (seekPosition > file->fileSize)
  1719.     seekPosition = file->fileSize;
  1720.   /* now set the position ... */
  1721.   if (seekPosition < file->currentPosition) {
  1722.     file->currentCluster = 0;
  1723.     file->currentPosition = 0;
  1724.   }
  1725.   while (file->currentPosition < seekPosition) {
  1726.     SectorNo sectorNo;
  1727.     unsigned offsetInSector;
  1728.     checkStatus(getSectorAndOffset(file,&sectorNo,&offsetInSector));
  1729.     file->currentPosition += vol.bytesPerCluster;
  1730.     file->currentPosition &= - (long) (vol.bytesPerCluster);
  1731.   }
  1732.   ioreq->irLength = file->currentPosition = seekPosition;
  1733.   return flOK;
  1734. }
  1735. /*----------------------------------------------------------------------*/
  1736. /*           f l F i n d F i l e */
  1737. /*                                                                      */
  1738. /* Finds a file entry in a directory, optionally modifying the file     */
  1739. /* time/date and/or attributes.                                         */
  1740. /* Files may be found by handle no. provided they are open, or by name. */
  1741. /* Only the Hidden, System or Read-only attributes may be modified. */
  1742. /* Entries may be found for any existing file or directory other than   */
  1743. /* the root. A DirectoryEntry structure describing the file is copied   */
  1744. /* to a user buffer. */
  1745. /*                                                                      */
  1746. /* The DirectoryEntry structure is defined in dosformt.h */
  1747. /*                                                                      */
  1748. /* Parameters:                                                          */
  1749. /* irHandle : If by name: Drive number (0, 1, ...) */
  1750. /*   else      : Handle of open file */
  1751. /* irPath : If by name: Specifies a file or directory path*/
  1752. /* irFlags : Options flags */
  1753. /*   FIND_BY_HANDLE: Find open file by handle.  */
  1754. /*   Default is access by path.    */
  1755. /*                        SET_DATETIME: Update time/date from buffer */
  1756. /*   SET_ATTRIBUTES: Update attributes from buffer */
  1757. /* irDirEntry : Address of user buffer to receive a */
  1758. /*   DirectoryEntry structure */
  1759. /*                                                                      */
  1760. /* Returns:                                                             */
  1761. /* irLength : Modified */
  1762. /* FLStatus : 0 on success, otherwise failed                */
  1763. /*----------------------------------------------------------------------*/
  1764. static FLStatus findFile(Volume vol, File *file, IOreq FAR2 *ioreq)
  1765. {
  1766.   File tFile; /* temporary file for searches */
  1767.   if (ioreq->irFlags & FIND_BY_HANDLE)
  1768.     tFile = *file;
  1769.   else {
  1770.     tFile.flags = 0;
  1771.     checkStatus(findDirEntry(&vol,ioreq->irPath,&tFile));
  1772.   }
  1773.   if (tFile.flags & FILE_IS_ROOT_DIR)
  1774.     if (ioreq->irFlags & (SET_DATETIME | SET_ATTRIBUTES))
  1775.       return flPathIsRootDirectory;
  1776.     else {
  1777.       DirectoryEntry FAR1 *irDirEntry = (DirectoryEntry FAR1 *) ioreq->irData;
  1778.       tffsset(irDirEntry,0,sizeof(DirectoryEntry));
  1779.       irDirEntry->attributes = ATTR_DIRECTORY;
  1780.       return flOK;
  1781.     }
  1782.   if (ioreq->irFlags & (SET_DATETIME | SET_ATTRIBUTES)) {
  1783.     DirectoryEntry FAR1 *irDirEntry = (DirectoryEntry FAR1 *) ioreq->irData;
  1784.     DirectoryEntry *dirEntry;
  1785.     checkStatus(getDirEntryForUpdate(&tFile,&dirEntry));
  1786.     if (ioreq->irFlags & SET_DATETIME) {
  1787.       COPY2(dirEntry->updateDate,irDirEntry->updateDate);
  1788.       COPY2(dirEntry->updateTime,irDirEntry->updateTime);
  1789.     }
  1790.     if (ioreq->irFlags & SET_ATTRIBUTES)
  1791.       dirEntry->attributes = irDirEntry->attributes &
  1792.      (ATTR_ARCHIVE | ATTR_HIDDEN | ATTR_READ_ONLY | ATTR_SYSTEM);
  1793.     tffscpy(irDirEntry, dirEntry, sizeof(DirectoryEntry));
  1794.   }
  1795.   else {
  1796.     tffscpy(ioreq->irData,getDirEntry(&tFile),sizeof(DirectoryEntry));
  1797.     if (ioreq->irFlags & FIND_BY_HANDLE)
  1798.       toLE4(((DirectoryEntry FAR1 *) (ioreq->irData))->fileSize, tFile.fileSize);
  1799.   }
  1800.   return flOK;
  1801. }
  1802. /*----------------------------------------------------------------------*/
  1803. /*  f l F i n d F i r s t F i l e */
  1804. /*                                                                      */
  1805. /* Finds the first file entry in a directory. */
  1806. /* This function is used in combination with the flFindNextFile call,   */
  1807. /* which returns the remaining file entries in a directory sequentially.*/
  1808. /* Entries are returned according to the unsorted directory order. */
  1809. /* flFindFirstFile creates a file handle, which is returned by it. Calls*/
  1810. /* to flFindNextFile will provide this file handle. When flFindNextFile */
  1811. /* returns 'noMoreEntries', the file handle is automatically closed.    */
  1812. /* Alternatively the file handle can be closed by a 'closeFile' call    */
  1813. /* before actually reaching the end of directory. */
  1814. /* A DirectoryEntry structure is copied to the user buffer describing   */
  1815. /* each file found. This structure is defined in dosformt.h. */
  1816. /*                                                                      */
  1817. /* Parameters:                                                          */
  1818. /* irHandle : Drive number (0, 1, ...) */
  1819. /* irPath : Specifies a directory path */
  1820. /* irData : Address of user buffer to receive a */
  1821. /*   DirectoryEntry structure */
  1822. /*                                                                      */
  1823. /* Returns:                                                             */
  1824. /* irHandle : File handle to use for subsequent operations. */
  1825. /* FLStatus : 0 on success, otherwise failed                */
  1826. /*----------------------------------------------------------------------*/
  1827. static FLStatus findFirstFile(Volume vol, IOreq FAR2 *ioreq)
  1828. {
  1829.   int i;
  1830.   /* Look for an available file */
  1831.   File *file = fileTable;
  1832.   for (i = 0; i < FILES && (file->flags & FILE_IS_OPEN); i++, file++);
  1833.   if (i >= FILES)
  1834.     return flTooManyOpenFiles;
  1835.   file->fileVol = &vol;
  1836.   ioreq->irHandle = i; /* return file handle */
  1837.   /* Find the path */
  1838.   checkStatus(findDirEntry(file->fileVol,ioreq->irPath,file));
  1839.   file->currentPosition = 0; /* located at file beginning */
  1840.   file->flags |= FILE_IS_OPEN | FILE_READ_ONLY; /* this file now officially open */
  1841.   return findNextFile(file,ioreq);
  1842. }
  1843. /*----------------------------------------------------------------------*/
  1844. /*          g e t D i s k I n f o */
  1845. /* */
  1846. /* Returns general allocation information. */
  1847. /* */
  1848. /* The bytes/sector, sector/cluster, total cluster and free cluster */
  1849. /* information are returned into a DiskInfo structure. */
  1850. /*                                                                      */
  1851. /* Parameters:                                                          */
  1852. /* ioreq->irData : Address of DiskInfo structure                 */
  1853. /*                                                                      */
  1854. /* Returns:                                                             */
  1855. /* FLStatus : 0 on success, otherwise failed                */
  1856. /*----------------------------------------------------------------------*/
  1857. static FLStatus getDiskInfo(Volume vol, IOreq FAR2 *ioreq)
  1858. {
  1859.   unsigned i;
  1860.   DiskInfo FAR1 *diskInfo = (DiskInfo FAR1 *) ioreq->irData;
  1861.   diskInfo->bytesPerSector = SECTOR_SIZE;
  1862.   diskInfo->sectorsPerCluster = vol.sectorsPerCluster;
  1863.   diskInfo->totalClusters = vol.maxCluster - 1;
  1864.   diskInfo->freeClusters = 0; /* let's count them */
  1865.   for (i = 2; i <= vol.maxCluster; i++)
  1866.     if (getFATentry(&vol,i) == 0)
  1867.       diskInfo->freeClusters++;
  1868.   return flOK;
  1869. }
  1870. #ifdef RENAME_FILE
  1871. /*----------------------------------------------------------------------*/
  1872. /*         r e n a m e F i l e */
  1873. /* */
  1874. /* Renames a file to another name. */
  1875. /* */
  1876. /* Parameters:                                                          */
  1877. /* ioreq->irPath : path of existing file */
  1878. /*      ioreq->irData : path of new name. */
  1879. /*                                                                      */
  1880. /* Returns:                                                             */
  1881. /* FLStatus : 0 on success, otherwise failed                */
  1882. /*----------------------------------------------------------------------*/
  1883. static FLStatus renameFile(Volume vol, IOreq FAR2 *ioreq)
  1884. {
  1885.   File file, file2; /* temporary files for searches */
  1886.   DirectoryEntry *dirEntry, *dirEntry2;
  1887.   FLStatus status;
  1888.   FLSimplePath FAR1 *irPath2 = (FLSimplePath FAR1 *) ioreq->irData;
  1889.   file.flags = 0;
  1890.   checkStatus(findDirEntry(&vol,ioreq->irPath,&file));
  1891.   file2.flags = FILE_MUST_OPEN;
  1892.   status = findDirEntry(file.fileVol,irPath2,&file2);
  1893.   if (status != flFileNotFound)
  1894.     return status == flOK ? flFileAlreadyExists : status;
  1895. #ifndef VFAT_COMPATIBILITY
  1896.   if (file.ownerDirCluster == file2.ownerDirCluster) { /* Same directory */
  1897.     /* Change name in directory entry */
  1898.     checkStatus(getDirEntryForUpdate(&file,&dirEntry));
  1899.     setNameInDirEntry(dirEntry,irPath2);
  1900.   }
  1901.   else
  1902. #endif
  1903.   { /* not same directory */
  1904.     /* Write new directory entry */
  1905.     checkStatus(getDirEntryForUpdate(&file2,&dirEntry2));
  1906.     *dirEntry2 = *getDirEntry(&file);
  1907.     setNameInDirEntry(dirEntry2,irPath2);
  1908.     /* Delete original entry */
  1909.     checkStatus(getDirEntryForUpdate(&file,&dirEntry));
  1910.     dirEntry->name[0] = DELETED_DIR_ENTRY;
  1911.   }
  1912.   return flOK;
  1913. }
  1914. #endif /* RENAME_FILE */
  1915. #ifdef SUB_DIRECTORY
  1916. /*----------------------------------------------------------------------*/
  1917. /*              m a k e D i r */
  1918. /* */
  1919. /* Creates a new directory. */
  1920. /*                                                                      */
  1921. /* Parameters:                                                          */
  1922. /* ioreq->irPath : path of new directory. */
  1923. /*                                                                      */
  1924. /* Returns:                                                             */
  1925. /* FLStatus : 0 on success, otherwise failed                */
  1926. /*----------------------------------------------------------------------*/
  1927. static FLStatus makeDir(Volume vol, IOreq FAR2 *ioreq)
  1928. {
  1929.   File file; /* temporary file for searches */
  1930.   unsigned dirBackPointer;
  1931.   DirectoryEntry *dirEntry;
  1932.   FLStatus status;
  1933.   file.flags = FILE_MUST_OPEN;
  1934.   status = findDirEntry(&vol,ioreq->irPath,&file);
  1935.   if (status != flFileNotFound)
  1936.     return status == flOK ? flFileAlreadyExists : status;
  1937.   /* Create the directory entry for the new dir */
  1938.   checkStatus(getDirEntryForUpdate(&file,&dirEntry));
  1939.   setNameInDirEntry(dirEntry,ioreq->irPath);
  1940.   dirEntry->attributes = ATTR_ARCHIVE | ATTR_DIRECTORY;
  1941.   toLE2(dirEntry->startingCluster,0);
  1942.   toLE4(dirEntry->fileSize,0);
  1943.   setCurrentDateTime(dirEntry);
  1944.   /* Remember the back pointer to owning directory for the ".." entry */
  1945.   dirBackPointer = (unsigned) file.ownerDirCluster;
  1946.   file.flags |= FILE_IS_DIRECTORY;
  1947.   file.currentPosition = 0;
  1948.   file.fileSize = 0;
  1949.   return extendDirectory(&file,dirBackPointer);
  1950. }
  1951. #endif /* SUB_DIRECTORY */
  1952. #ifdef SPLIT_JOIN_FILE
  1953. /*------------------------------------------------------------------------*/
  1954. /*           j o i n F i l e                                 */
  1955. /*                                                                        */
  1956. /* joins two files. If the end of the first file is on a cluster          */
  1957. /* boundary, the files will be joined there. Otherwise, the data in       */
  1958. /* the second file from the beginning until the offset that is equal to   */
  1959. /* the offset in cluster of the end of the first file will be lost. The   */
  1960. /* rest of the second file will be joined to the first file at the end of */
  1961. /* the first file. On exit, the first file is the expanded file and the   */
  1962. /* second file is deleted.                                                */
  1963. /* Note: The second file will be open by this function, it is advised to  */
  1964. /*  close it before calling this function in order to avoid          */
  1965. /*  inconsistencies.                                                 */
  1966. /*                                                                        */
  1967. /* Parameters:                                                            */
  1968. /* file            : file to join to.                                */
  1969. /* irPath          : Path name of the file to be joined.             */
  1970. /*                                                                        */
  1971. /* Returns:                                                               */
  1972. /* FLStatus        : 0 on success, otherwise failed.                 */
  1973. /*                                                                        */
  1974. /*------------------------------------------------------------------------*/
  1975. static FLStatus joinFile (File *file, IOreq FAR2 *ioreq)
  1976. {
  1977.   Volume vol = file->fileVol;
  1978.   File joinedFile;
  1979.   DirectoryEntry *joinedDirEntry;
  1980.   unsigned offsetInCluster = file->fileSize % vol.bytesPerCluster;
  1981.   if (file->flags & FILE_READ_ONLY)
  1982.     return flNoWriteAccess;
  1983.   if (file->flags & FILE_IS_DIRECTORY)
  1984.     return flFileIsADirectory;
  1985.   /* open the joined file. */
  1986.   joinedFile.flags = 0;
  1987.   checkStatus(findDirEntry(file->fileVol,ioreq->irPath,&joinedFile));
  1988.   joinedFile.currentPosition = 0;
  1989.   /* Check if the two files are the same file. */
  1990.   if (file->directorySector == joinedFile.directorySector &&
  1991.       file->directoryIndex == joinedFile.directoryIndex)
  1992.     return flBadFileHandle;
  1993.   file->flags |= FILE_MODIFIED;
  1994.   if (joinedFile.fileSize > offsetInCluster) { /* the joined file extends
  1995.    beyond file's end of file.*/
  1996.     unsigned lastCluster, nextCluster, firstCluster;
  1997.     /* get the first cluster of the joined file. */
  1998.     firstCluster = LE2(getDirEntry(&joinedFile)->startingCluster);
  1999.     if (file->fileSize) {  /* the file is not empty.*/
  2000.       /* find the last cluster of file by following the FAT chain.*/
  2001.       if (file->currentCluster == 0)     /* start from the first cluster.*/
  2002. nextCluster = LE2(getDirEntry(file)->startingCluster);
  2003.       else                               /* start from the current cluster.*/
  2004. nextCluster = file->currentCluster;
  2005.       /* follow the FAT chain.*/
  2006.       while (nextCluster != FAT_LAST_CLUSTER) {
  2007. if (nextCluster < 2 || nextCluster > vol.maxCluster)
  2008.   return flInvalidFATchain;
  2009. lastCluster = nextCluster;
  2010. nextCluster = getFATentry(&vol,lastCluster);
  2011.       }
  2012.     }
  2013.     else                   /* the file is empty. */
  2014.       lastCluster = 0;
  2015.     if (offsetInCluster) {      /* join in the middle of a cluster.*/
  2016.       SectorNo sectorNo, joinedSectorNo, tempSectorNo;
  2017.       unsigned offset, joinedOffset, numOfSectors = 1, i;
  2018.       const char FAR0 *startCopy;
  2019.       /* get the sector and offset of the end of the file.*/
  2020.       file->currentPosition = file->fileSize;
  2021.       file->currentCluster = lastCluster;
  2022.       checkStatus(getSectorAndOffset(file, &sectorNo, &offset));
  2023.       /* load the sector of the end of the file to the buffer.*/
  2024.       checkStatus(updateSector(&vol, sectorNo, TRUE));
  2025.       /*  copy the second part of the first cluster of the joined file
  2026.   to the end of the last cluster of the original file.*/
  2027.       /* first set the current position of the joined file.*/
  2028.       joinedFile.currentPosition = offsetInCluster;
  2029.       joinedFile.currentCluster = firstCluster;
  2030.       /* get the relevant sector in the joined file.*/
  2031.       checkStatus(getSectorAndOffset(&joinedFile, &joinedSectorNo, &joinedOffset));
  2032.       /* map sector and offset.*/
  2033.       startCopy = (const char FAR0 *) findSector(&vol,joinedSectorNo) + joinedOffset;
  2034.       if (startCopy == NULL)
  2035. return flSectorNotFound;
  2036.       /* copy.*/
  2037.       tffscpy(buffer.data + offset, startCopy, SECTOR_SIZE - offset);
  2038.       checkStatus(flushBuffer(&vol));
  2039.       /* find how many sectors should still be copied (the number of sectors
  2040.  until the end of the current cluster).*/
  2041.       tempSectorNo = firstSectorOfCluster(&vol,lastCluster);
  2042.       while(tempSectorNo != sectorNo) {
  2043. tempSectorNo++;
  2044. numOfSectors++;
  2045.       }
  2046.       /* copy the rest of the sectors in the current cluster.
  2047.  this is done by loading a sector from the joined file to the buffer,
  2048.  changing the sectoNo of the buffer to the relevant sector in file
  2049.  and then flushing the buffer.*/
  2050.       sectorNo++;
  2051.       joinedSectorNo++;
  2052.       for(i = 0; i < vol.sectorsPerCluster - numOfSectors; i++) {
  2053. checkStatus(updateSector(&vol,joinedSectorNo, TRUE));
  2054. buffer.sectorNo = sectorNo;
  2055. checkStatus(flushBuffer(&vol));
  2056. sectorNo++;
  2057. joinedSectorNo++;
  2058.       }
  2059.       /* adjust the FAT chain.*/
  2060.       checkStatus(setFATentry(&vol,
  2061.       lastCluster,
  2062.       getFATentry(&vol,firstCluster)));
  2063.       /* mark the first cluster of the joined file as free */
  2064.       checkStatus(setFATentry(&vol,firstCluster,FAT_FREE));
  2065.       buffer.checkPoint = TRUE;
  2066.       /* mark sectors free */
  2067.       checkStatus(vol.tl.deleteSector(vol.tl.rec,firstSectorOfCluster(&vol,firstCluster),
  2068.       vol.sectorsPerCluster));
  2069.     }
  2070.     else {    /* join on a cluster boundary.*/
  2071.       if (lastCluster) {      /* file is not empty. */
  2072. checkStatus(setFATentry(&vol,lastCluster, firstCluster));
  2073.       }
  2074.       else {                  /* file is empty.*/
  2075. DirectoryEntry *dirEntry;
  2076. checkStatus(getDirEntryForUpdate(file, &dirEntry));
  2077. toLE2(dirEntry->startingCluster, firstCluster);
  2078.         setCurrentDateTime(dirEntry);
  2079.       }
  2080.     }
  2081.     /*adjust the size of the expanded file.*/
  2082.     file->fileSize += joinedFile.fileSize - offsetInCluster;
  2083.   }
  2084.   /* mark the directory entry of the joined file as deleted.*/
  2085.   checkStatus(getDirEntryForUpdate(&joinedFile, &joinedDirEntry));
  2086.   joinedDirEntry->name[0] = DELETED_DIR_ENTRY;
  2087.   return flOK;
  2088. }
  2089. /*------------------------------------------------------------------------*/
  2090. /*       s p l i t F i l e                                   */
  2091. /*                                                                        */
  2092. /* Splits the file into two files. The original file contains the first   */
  2093. /* part, and a new file (which is created for that purpose) contains      */
  2094. /* the second part. If the current position is on a cluster               */
  2095. /* boundary, the file will be split at the current position. Otherwise,   */
  2096. /* the cluster of the current position is duplicated, one copy is the     */
  2097. /* first cluster of the new file, and the other is the last cluster of the*/
  2098. /* original file, which now ends at the current position.                 */
  2099. /*                                                                        */
  2100. /* Parameters:                                                            */
  2101. /* file            : file to split.                                  */
  2102. /* irPath          : Path name of the new file.                      */
  2103. /*                                                                        */
  2104. /* Returns:                                                               */
  2105. /* irHandle        : handle of the new file.                         */
  2106. /* FLStatus        : 0 on success, otherwise failed.                 */
  2107. /*                                                                        */
  2108. /*------------------------------------------------------------------------*/
  2109. static FLStatus splitFile (File *file, IOreq FAR2 *ioreq)
  2110. {
  2111.   Volume vol = file->fileVol;
  2112.   File *newFile, dummyFile;
  2113.   IOreq ioreq2;
  2114.   FLStatus status;
  2115.   if (file->flags & FILE_READ_ONLY)
  2116.     return flNoWriteAccess;
  2117.   if (file->flags & FILE_IS_DIRECTORY)
  2118.     return flFileIsADirectory;
  2119.   /* check if the path of the new file already exists.*/
  2120.   dummyFile.flags = 0;
  2121.   status = findDirEntry(&vol,ioreq->irPath,&dummyFile);
  2122.   if (status != flFileNotFound) {
  2123.     if (status == flOK)              /* there is a file with that path.*/
  2124.       return flFileAlreadyExists;
  2125.     else
  2126.       return status;
  2127.   }
  2128.   /* open the new file.*/
  2129.   ioreq2.irFlags = OPEN_FOR_WRITE;
  2130.   ioreq2.irPath = ioreq->irPath;
  2131.   checkStatus(openFile(&vol,&ioreq2));
  2132.   newFile = fileTable + ioreq2.irHandle;
  2133.   newFile->flags |= FILE_MODIFIED;
  2134.   file->flags |= FILE_MODIFIED;
  2135.   if (file->currentPosition % vol.bytesPerCluster) { /* not on a cluster boundary.*/
  2136.     SectorNo sectorNo, newSectorNo;
  2137.     int i;
  2138.     checkStatus(allocateCluster(newFile));
  2139.     sectorNo = firstSectorOfCluster(&vol,file->currentCluster);
  2140.     newSectorNo = firstSectorOfCluster(&vol,newFile->currentCluster);
  2141.     /* copy the current cluster of the original file to the first cluster
  2142.        of the new file, sector after sector.*/
  2143.     for(i = 0; i < vol.sectorsPerCluster; i++) {
  2144.       checkStatus(updateSector(&vol,sectorNo, TRUE));
  2145.       buffer.sectorNo = newSectorNo;
  2146.       checkStatus(flushBuffer(&vol));
  2147.       sectorNo++;
  2148.       newSectorNo++;
  2149.     }
  2150.     /* adjust the FAT chain of the new file.*/
  2151.     checkStatus(setFATentry(&vol,newFile->currentCluster,
  2152. getFATentry(&vol,file->currentCluster)));
  2153.     /* mark current cluster 0 (as current position).*/
  2154.     newFile->currentCluster = 0;
  2155.   }
  2156.   else {                                  /* on a cluster boundary.*/
  2157.     DirectoryEntry *newDirEntry;
  2158.     /* adjust the directory entry of the new file.*/
  2159.     checkStatus(getDirEntryForUpdate(newFile,&newDirEntry));
  2160.     if (file->currentPosition) { /* split at the middle of the file.*/
  2161.       toLE2(newDirEntry->startingCluster, getFATentry(&vol,file->currentCluster));
  2162.       setCurrentDateTime(newDirEntry);
  2163.     }
  2164.     else {                     /* split at the beginning of the file.*/
  2165.       DirectoryEntry *dirEntry;
  2166.       /* first cluster of file becomes the first cluster of the new file.*/
  2167.       newDirEntry->startingCluster = getDirEntry(file)->startingCluster;
  2168.       setCurrentDateTime(newDirEntry);
  2169.       /* starting cluster of file becomes 0.*/
  2170.       checkStatus(getDirEntryForUpdate(file, &dirEntry));
  2171.       toLE2(dirEntry->startingCluster, 0);
  2172.       setCurrentDateTime(dirEntry);
  2173.     }
  2174.   }
  2175.   /* adjust the size of the new file.*/
  2176.   newFile->fileSize = file->fileSize - file->currentPosition +
  2177.      (file->currentPosition % vol.bytesPerCluster);
  2178.   /* adjust the chain and size of the original file.*/
  2179.   if (file->currentPosition)    /* we didn't split at the beginning.*/
  2180.     checkStatus(setFATentry(&vol,file->currentCluster, FAT_LAST_CLUSTER));
  2181.   file->fileSize = file->currentPosition;
  2182.   /* return to the user the handle of the new file.*/
  2183.   ioreq->irHandle = ioreq2.irHandle;
  2184.   return flOK;
  2185. }
  2186. #endif /* SPLIT_JOIN_FILE */
  2187. #endif /* FILES > 0 */
  2188. /*----------------------------------------------------------------------*/
  2189. /*            f l C a l l    */
  2190. /* */
  2191. /* Common entry-point to all file-system functions. Macros are          */
  2192. /* to call individual function, which are separately described below. */
  2193. /*                                                                      */
  2194. /* Parameters:                                                          */
  2195. /* function : file-system function code (listed below) */
  2196. /* ioreq : IOreq structure */
  2197. /*                                                                      */
  2198. /* Returns:                                                             */
  2199. /* FLStatus : 0 on success, otherwise failed                */
  2200. /*----------------------------------------------------------------------*/
  2201. FLStatus flCall(FLFunctionNo functionNo, IOreq FAR2 *ioreq)
  2202. {
  2203.   Volume vol = NULL;
  2204.   FLStatus status;
  2205. #if FILES > 0
  2206.   File *file;
  2207. #endif
  2208.   if (!initDone)
  2209.     checkStatus(flInit());
  2210. #if FILES > 0
  2211.   /* Verify file handle if applicable */
  2212.   switch (functionNo) {
  2213.     case FL_FIND_FILE:
  2214.       if (!(ioreq->irFlags & FIND_BY_HANDLE))
  2215. break;
  2216. #ifdef SPLIT_JOIN_FILE
  2217.     case FL_JOIN_FILE:
  2218.     case FL_SPLIT_FILE:
  2219. #endif
  2220.     case FL_CLOSE_FILE:
  2221.     case FL_READ_FILE:
  2222.     case FL_WRITE_FILE:
  2223.     case FL_SEEK_FILE:
  2224.     case FL_FIND_NEXT_FILE:
  2225.       if (ioreq->irHandle < FILES &&
  2226.   (fileTable[ioreq->irHandle].flags & FILE_IS_OPEN)) {
  2227. file = fileTable + ioreq->irHandle;
  2228. pVol = file->fileVol;
  2229.       }
  2230.       else
  2231. return flBadFileHandle;
  2232.   }
  2233. #endif
  2234.   if (pVol == NULL) { /* irHandle is drive no. */
  2235. #if DRIVES > 1
  2236.     if (ioreq->irHandle >= noOfDrives)
  2237.       return flBadDriveHandle;
  2238.     pVol = &vols[ioreq->irHandle];
  2239. #else
  2240.     pVol = &vols[0];
  2241. #endif
  2242.   }
  2243.   checkStatus(setBusy(&vol,TFFS_ON));       /* Let everyone know we are here */
  2244.   /* Nag about mounting if necessary */
  2245.   switch (functionNo) {
  2246.     case FL_MOUNT_VOLUME:
  2247.     case FL_FORMAT_VOLUME:
  2248. #ifdef LOW_LEVEL
  2249.       if (vol.flags & VOLUME_LOW_LVL_MOUNTED)
  2250. dismountLowLevel(&vol);  /* mutual exclusive mounting */
  2251. #endif
  2252.       break;
  2253. #ifdef LOW_LEVEL
  2254.     case FL_PHYSICAL_WRITE:
  2255.     case FL_PHYSICAL_ERASE:
  2256.       if (vol.flags & VOLUME_ABS_MOUNTED) {
  2257. status = flGeneralFailure;  /* mutual exclusive mounting */
  2258. goto flCallExit;
  2259.       }
  2260.       if (!(vol.flags & VOLUME_LOW_LVL_MOUNTED)) {
  2261. status = mountLowLevel(&vol);  /* automatic low level mounting */
  2262.       }
  2263.       else {
  2264. status = flOK;
  2265. #ifndef FIXED_MEDIA
  2266. status = flMediaCheck(vol.socket);
  2267. if (status == flDiskChange)
  2268.   status = mountLowLevel(&vol); /* card was changed, remount */
  2269. #endif
  2270.       }
  2271.       if (status != flOK) {
  2272. dismountLowLevel(&vol);
  2273. goto flCallExit;
  2274.       }
  2275.       break;
  2276.     case FL_GET_PHYSICAL_INFO:
  2277.     case FL_PHYSICAL_READ:
  2278.       if (!(vol.flags & (VOLUME_LOW_LVL_MOUNTED | VOLUME_ABS_MOUNTED))) {
  2279. status = mountLowLevel(&vol);  /* automatic low level mounting */
  2280.       }
  2281.       else {
  2282. status = flOK;
  2283. #ifndef FIXED_MEDIA
  2284. status = flMediaCheck(vol.socket);
  2285. if (status == flDiskChange) {
  2286.           if (vol.flags & VOLUME_ABS_MOUNTED)
  2287.     dismountVolume(&vol);
  2288.   status = mountLowLevel(&vol); /* card was changed, remount */
  2289. }
  2290. #endif
  2291.       }
  2292.       if (status != flOK) {
  2293.         if (vol.flags & VOLUME_ABS_MOUNTED)
  2294.   dismountVolume(&vol);
  2295.         if (vol.flags & VOLUME_LOW_LVL_MOUNTED)
  2296.   dismountLowLevel(&vol);
  2297. goto flCallExit;
  2298.       }
  2299.       break;
  2300. #endif /* LOW_LEVEL */
  2301.     default:
  2302.       if (vol.flags & VOLUME_ABS_MOUNTED) {
  2303. FLStatus status = flOK;
  2304. #ifndef FIXED_MEDIA
  2305. status = flMediaCheck(vol.socket);
  2306. #endif
  2307. if (status != flOK)
  2308.   dismountVolume(&vol);
  2309.       }
  2310.       if (!(vol.flags & VOLUME_ABS_MOUNTED)) {
  2311. status = flNotMounted;
  2312. goto flCallExit;
  2313.       }
  2314.       switch (functionNo) {
  2315. case FL_DISMOUNT_VOLUME:
  2316. case FL_CHECK_VOLUME:
  2317. case FL_DEFRAGMENT_VOLUME:
  2318. case FL_ABS_READ:
  2319. case FL_ABS_WRITE:
  2320. case FL_ABS_DELETE:
  2321. case FL_GET_BPB:
  2322.   break;
  2323. default:
  2324.   if (!(vol.flags & VOLUME_MOUNTED)) {
  2325.     status = flNotMounted;
  2326.     goto flCallExit;
  2327.   }
  2328.       }
  2329.   }
  2330.   /* Execute specific function */
  2331.   switch (functionNo) {
  2332.     case FL_MOUNT_VOLUME:
  2333.       status = mountVolume(&vol);
  2334.       break;
  2335.     case FL_DISMOUNT_VOLUME:
  2336.       status = dismountVolume(&vol);
  2337.       break;
  2338.     case FL_CHECK_VOLUME:
  2339.       status = flOK; /* If we got this far */
  2340.       break;
  2341. #if defined(DEFRAGMENT_VOLUME) || defined(SINGLE_BUFFER)
  2342.     case FL_DEFRAGMENT_VOLUME:
  2343.       status = defragmentVolume(&vol,ioreq);
  2344.       break;
  2345. #endif
  2346. #ifdef FORMAT_VOLUME
  2347.     case FL_FORMAT_VOLUME:
  2348.       status = formatVolume(&vol,ioreq);
  2349.       break;
  2350. #endif
  2351.     case FL_DONT_MONITOR_FAT:
  2352.       status = disableFATmonitor(&vol);
  2353.       break;
  2354. #ifdef ABS_READ_WRITE
  2355.     case FL_ABS_READ:
  2356.       status = absRead(&vol,ioreq);
  2357.       break;
  2358.     case FL_ABS_WRITE:
  2359.       status = absWrite(&vol,ioreq);
  2360.       break;
  2361.     case FL_ABS_DELETE:
  2362.       status = absDelete(&vol,ioreq);
  2363.       break;
  2364.     case FL_GET_BPB:
  2365.       status = getBPB(&vol,ioreq);
  2366.       break;
  2367. #endif
  2368. #ifdef LOW_LEVEL
  2369.     case FL_GET_PHYSICAL_INFO:
  2370.       status = getPhysicalInfo(&vol, ioreq);
  2371.       break;
  2372.     case FL_PHYSICAL_READ:
  2373.       status = physicalRead(&vol, ioreq);
  2374.       break;
  2375.     case FL_PHYSICAL_WRITE:
  2376.       status = physicalWrite(&vol, ioreq);
  2377.       break;
  2378.     case FL_PHYSICAL_ERASE:
  2379.       status = physicalErase(&vol, ioreq);
  2380.       break;
  2381. #endif
  2382. #if FILES > 0
  2383.     case FL_OPEN_FILE:
  2384.       status = openFile(&vol,ioreq);
  2385.       break;
  2386.     case FL_CLOSE_FILE:
  2387.       status = closeFile(file);
  2388.       break;
  2389. #ifdef SPLIT_JOIN_FILE
  2390.     case FL_JOIN_FILE:
  2391.       status = joinFile(file, ioreq);
  2392.       break;
  2393.     case FL_SPLIT_FILE:
  2394.       status = splitFile(file, ioreq);
  2395.       break;
  2396. #endif
  2397.     case FL_READ_FILE:
  2398.       status = readFile(file,ioreq);
  2399.       break;
  2400.     case FL_WRITE_FILE:
  2401.       status = writeFile(file,ioreq);
  2402.       break;
  2403.     case FL_SEEK_FILE:
  2404.       status = seekFile(file,ioreq);
  2405.       break;
  2406.     case FL_FIND_FILE:
  2407.       status = findFile(&vol,file,ioreq);
  2408.       break;
  2409.     case FL_FIND_FIRST_FILE:
  2410.       status = findFirstFile(&vol,ioreq);
  2411.       break;
  2412.     case FL_FIND_NEXT_FILE:
  2413.       status = findNextFile(file,ioreq);
  2414.       break;
  2415.     case FL_GET_DISK_INFO:
  2416.       status = getDiskInfo(&vol,ioreq);
  2417.       break;
  2418.     case FL_DELETE_FILE:
  2419.       status = deleteFile(&vol,ioreq,FALSE);
  2420.       break;
  2421. #ifdef RENAME_FILE
  2422.     case FL_RENAME_FILE:
  2423.       status = renameFile(&vol,ioreq);
  2424.       break;
  2425. #endif
  2426. #ifdef SUB_DIRECTORY
  2427.     case FL_MAKE_DIR:
  2428.       status = makeDir(&vol,ioreq);
  2429.       break;
  2430.     case FL_REMOVE_DIR:
  2431.       status = deleteFile(&vol,ioreq,TRUE);
  2432.       break;
  2433. #endif
  2434. #endif /* FILES > 0 */
  2435.     default:
  2436.       status = flBadFunction;
  2437.   }
  2438. #if FILES > 0
  2439.   if (buffer.checkPoint) {
  2440.     FLStatus st = flushBuffer(&vol);
  2441.     if (status == flOK)
  2442.       status = st;
  2443.   }
  2444. #endif
  2445. flCallExit:
  2446.   setBusy(&vol,TFFS_OFF); /* We're leaving */
  2447.   return status;
  2448. }
  2449. #if POLLING_INTERVAL != 0
  2450. /*----------------------------------------------------------------------*/
  2451. /*          s o c k e t I n t e r v a l R o u t i n e */
  2452. /* */
  2453. /* Routine called by the interval timer to perform periodic socket      */
  2454. /* actions and handle the watch-dog timer. */
  2455. /* */
  2456. /* Parameters:                                                          */
  2457. /*      None */
  2458. /*                                                                      */
  2459. /*----------------------------------------------------------------------*/
  2460. /* Routine called at time intervals to poll sockets */
  2461. static void socketIntervalRoutine(void)
  2462. {
  2463.   unsigned volNo;
  2464.   Volume vol = vols;
  2465.   flMsecCounter += POLLING_INTERVAL;
  2466.   for (volNo = 0; volNo < noOfDrives; volNo++, pVol++)
  2467. #ifdef BACKGROUND
  2468.     if (flTakeMutex(&execInProgress,0)) {
  2469. #endif
  2470.       if (vol.flags & VOLUME_ABS_MOUNTED)
  2471. #ifdef BACKGROUND
  2472. /* Allow background operation to proceed */
  2473. vol.tl.tlSetBusy(vol.tl.rec,TFFS_OFF);
  2474. #endif
  2475.     if (flTakeMutex(&execInProgress,0)) {
  2476.       flIntervalRoutine(vol.socket);
  2477.       flFreeMutex(&execInProgress);
  2478.       }
  2479. #ifdef BACKGROUND
  2480.       flFreeMutex(&execInProgress);
  2481.     }
  2482. #endif
  2483. }
  2484. #endif
  2485. /*----------------------------------------------------------------------*/
  2486. /*             f l I n i t */
  2487. /* */
  2488. /* Initializes the FLite system, sockets and timers. */
  2489. /* */
  2490. /* Calling this function is optional. If it is not called, */
  2491. /* initialization will be done automatically on the first FLite call. */
  2492. /* This function is provided for those applications who want to */
  2493. /* explicitly initialize the system and get an initialization status. */
  2494. /* */
  2495. /* Calling flInit after initialization was done has no effect. */
  2496. /* */
  2497. /* Parameters:                                                          */
  2498. /* None */
  2499. /*                                                                      */
  2500. /* Returns:                                                             */
  2501. /* FLStatus : 0 on success, otherwise failed                */
  2502. /*----------------------------------------------------------------------*/
  2503. FLStatus flInit(void)
  2504. {
  2505.   if (!initDone) {
  2506.     unsigned volNo;
  2507.     Volume vol = vols;
  2508. #ifdef SINGLE_BUFFER
  2509.     /* create mutex protecting FLite shared buffer */
  2510.     if (flCreateMutex(&execInProgress) != flOK)
  2511.       return flGeneralFailure;
  2512. #endif
  2513.     for (volNo = 0; volNo < DRIVES; volNo++, pVol++) {
  2514.       vol.socket = flSocketOf(volNo);
  2515.       vol.socket->volNo = volNo;
  2516.       vol.flags = 0;
  2517. #ifndef SINGLE_BUFFER
  2518.       /* create mutex protecting FLite volume access */
  2519.       if (flCreateMutex(&execInProgress) != flOK)
  2520. return flGeneralFailure;
  2521. #endif
  2522.     }
  2523.     initDone = TRUE;
  2524.     flSysfunInit();
  2525.   #ifdef BACKGROUND
  2526.     flCreateBackground();
  2527.   #endif
  2528.     flRegisterComponents();
  2529.     checkStatus(flInitSockets());
  2530.   #if POLLING_INTERVAL > 0
  2531.     checkStatus(flInstallTimer(socketIntervalRoutine,POLLING_INTERVAL));
  2532.   #endif
  2533.   }
  2534.   return flOK;
  2535. }
  2536. #ifdef EXIT
  2537. /*----------------------------------------------------------------------*/
  2538. /*             f l E x i t */
  2539. /* */
  2540. /* If the application ever exits, flExit should be called before exit.  */
  2541. /* flExit flushes all buffers, closes all open files, powers down the   */
  2542. /* sockets and removes the interval timer. */
  2543. /*                                                                      */
  2544. /* Parameters:                                                          */
  2545. /* None */
  2546. /*                                                                      */
  2547. /* Returns:                                                             */
  2548. /* Nothing */
  2549. /*----------------------------------------------------------------------*/
  2550. void flExit(void)
  2551. {
  2552.   unsigned volNo;
  2553.   Volume vol = vols;
  2554.   for (volNo = 0; volNo < noOfDrives; volNo++, pVol++) {
  2555.     if (setBusy(&vol,TFFS_ON) == flOK) {
  2556.       dismountVolume(&vol);
  2557. #ifdef LOW_LEVEL
  2558.       dismountLowLevel(&vol);
  2559. #endif
  2560.       flExitSocket(vol.socket);
  2561.       flFreeMutex(&execInProgress);  /* free the mutex that was taken in setBusy(TFFS_ON) */
  2562. #ifndef SINGLE_BUFFER
  2563.       /* delete mutex protecting FLite volume access */
  2564.       flDeleteMutex(&execInProgress);
  2565. #endif
  2566.     }
  2567.   }
  2568. #if POLLING_INTERVAL != 0
  2569.   flRemoveTimer();
  2570. #endif
  2571. #ifdef SINGLE_BUFFER
  2572.   /* delete mutex protecting FLite shared buffer */
  2573.   flDeleteMutex(&execInProgress);
  2574. #endif
  2575.   initDone = FALSE;
  2576. }
  2577. #ifdef __BORLANDC__
  2578. #pragma exit flExit
  2579. #include <dos.h>
  2580. static int cdecl flBreakExit(void)
  2581. {
  2582.   flExit();
  2583.   return 0;
  2584. }
  2585. static void setCBreak(void)
  2586. {
  2587.   ctrlbrk(flBreakExit);
  2588. }
  2589. #pragma startup setCBreak
  2590. #endif
  2591. #endif