/*!!*/
/*
 * $Log:   V:/Flite/archives/TrueFFS5/Src/FATLITE.C_V  $
 * 
 *    Rev 1.10   Jan 17 2002 23:00:28   oris
 * Removed SINGLE_BUFFER ifdef.
 * Changed debug print added \r.
 * Removed warnings.
 * 
 *    Rev 1.9   Nov 16 2001 00:26:46   oris
 * Removed warnings.
 * 
 *    Rev 1.8   Nov 08 2001 10:45:28   oris
 * Removed warnings.
 * 
 *    Rev 1.7   May 16 2001 21:17:30   oris
 * Added the FL_ prefix to the following defines: ON, OFF
 * Change "data" named variables to flData to avoid name clashes.
 * 
 *    Rev 1.6   Apr 18 2001 09:31:02   oris
 * added new line at the end of the file.
 * 
 *    Rev 1.5   Apr 16 2001 10:42:16   vadimk
 * Emty file bug was fixed ( we should not allocate cluster for an empty file )
 *
 *    Rev 1.4   Apr 09 2001 15:07:10   oris
 * End with an empty line.
 *
 *    Rev 1.3   Apr 03 2001 14:42:02   oris
 * Bug fix - 64 sectors in directory return flInvalidFatChain.
 *
 *    Rev 1.2   Apr 01 2001 08:02:46   oris
 * copywrite notice.
 *
 *    Rev 1.1   Feb 12 2001 12:16:42   oris
 * Changed mutexs for TrueFFS 5.0
 *
 *    Rev 1.0   Feb 04 2001 11:02:28   oris
 * Initial revision.
 *
 */

/***********************************************************************************/
/*                        M-Systems Confidential                                   */
/*           Copyright (C) M-Systems Flash Disk Pioneers Ltd. 1995-2001            */
/*                         All Rights Reserved                                     */
/***********************************************************************************/
/*                            NOTICE OF M-SYSTEMS OEM                              */
/*                           SOFTWARE LICENSE AGREEMENT                            */
/*                                                                                 */
/*      THE USE OF THIS SOFTWARE IS GOVERNED BY A SEPARATE LICENSE                 */
/*      AGREEMENT BETWEEN THE OEM AND M-SYSTEMS. REFER TO THAT AGREEMENT           */
/*      FOR THE SPECIFIC TERMS AND CONDITIONS OF USE,                              */
/*      OR CONTACT M-SYSTEMS FOR LICENSE ASSISTANCE:                               */
/*      E-MAIL = info@m-sys.com                                                    */
/***********************************************************************************/


#include "bddefs.h"
#include "blockdev.h"
#include "dosformt.h"

#if defined(FILES) && FILES>0

File        fileTable[FILES];       /* the file table */

#define directory ((DirectoryEntry *) vol.volBuffer.flData)

FLStatus closeFile(File *file);       /* forward */
FLStatus flushBuffer(Volume vol);       /* forward */

/*----------------------------------------------------------------------*/
/*                    d i s m o u n t V o l u m e                     */
/*                                                               */
/* Closing all files.                            */
/*                                                               */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus dismountFS(Volume vol,FLStatus status)
{
  int i;
#ifndef FL_READ_ONLY
  if (status == flOK)
    checkStatus(flushBuffer(&vol));
#endif
       /* Close or discard all files and make them available */
  for (i = 0; i < FILES; i++)
    if (fileTable[i].fileVol == &vol)
      if (vol.flags & VOLUME_MOUNTED)
       closeFile(&fileTable[i]);
      else
       fileTable[i].flags = 0;

  vol.volBuffer.sectorNo = UNASSIGNED_SECTOR;       /* Current sector no. (none) */
  vol.volBuffer.dirty = vol.volBuffer.checkPoint = FALSE;
  return flOK;
}

#ifndef FL_READ_ONLY

/*----------------------------------------------------------------------*/
/*                       f l u s h B u f f e r                            */
/*                                                               */
/* Writes the buffer contents if it is dirty.                           */
/*                                                               */
/* If this is a FAT sector, all FAT copies are written.                     */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus flushBuffer(Volume vol)
{
  if (vol.volBuffer.dirty) {
    FLStatus status;
    unsigned i;
    Volume *bufferOwner = &vol;

    status = (*bufferOwner).tl.writeSector((*bufferOwner).tl.rec, vol.volBuffer.sectorNo,
                                      vol.volBuffer.flData);
    if (status == flOK) {
      if (vol.volBuffer.sectorNo >= (*bufferOwner).firstFATSectorNo &&
         vol.volBuffer.sectorNo < (*bufferOwner).secondFATSectorNo)
       for (i = 1; i < (*bufferOwner).numberOfFATS; i++)
         checkStatus((*bufferOwner).tl.writeSector((*bufferOwner).tl.rec,
                                              vol.volBuffer.sectorNo + i * (*bufferOwner).sectorsPerFAT,
                                              vol.volBuffer.flData));
    }
    else
      vol.volBuffer.sectorNo = UNASSIGNED_SECTOR;

    vol.volBuffer.dirty = vol.volBuffer.checkPoint = FALSE;

    return status;
  }
  else
    return flOK;
}


/*----------------------------------------------------------------------*/
/*                      u p d a t e S e c t o r                            */
/*                                                               */
/* Prepares a sector for update in the buffer                            */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       sectorNo       : Sector no. to read                            */
/*       read              : Whether to initialize buffer by reading, or       */
/*                        clearing                                   */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus updateSector(Volume vol, SectorNo sectorNo, FLBoolean read)
{
  if (sectorNo != vol.volBuffer.sectorNo || &vol != vol.volBuffer.owner) {
    const void FAR0 *mappedSector;

    checkStatus(flushBuffer(&vol));
    vol.volBuffer.sectorNo = sectorNo;
    vol.volBuffer.owner = &vol;
    if (read) {
      mappedSector = vol.tl.mapSector(vol.tl.rec,sectorNo,NULL);
      if (mappedSector) {
        if(mappedSector==dataErrorToken)
          return flDataError;
       tffscpy(vol.volBuffer.flData,mappedSector,SECTOR_SIZE);
      }
      else
       return flSectorNotFound;
    }
    else
      tffsset(vol.volBuffer.flData,0,SECTOR_SIZE);
  }

  vol.volBuffer.dirty = TRUE;

  return flOK;
}

#endif /* FL_READ_ONLY   */
/*----------------------------------------------------------------------*/
/*                 f i r s t S e c t o r O f C l u s t e r              */
/*                                                               */
/* Get sector no. corresponding to cluster no.                            */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       cluster              : Cluster no.                                   */
/*                                                                      */
/* Returns:                                                             */
/*       first sector no. of cluster                                   */
/*----------------------------------------------------------------------*/

static SectorNo firstSectorOfCluster(Volume vol, unsigned cluster)
{
  return (SectorNo) (cluster - 2) * vol.sectorsPerCluster +
        vol.firstDataSectorNo;
}


/*----------------------------------------------------------------------*/
/*                     g e t D i r E n t r y                         */
/*                                                               */
/* Get a read-only copy of a directory entry.                            */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       file              : File belonging to directory entry              */
/*                                                                      */
/* Returns:                                                             */
/*       dirEntry       : Pointer to directory entry                     */
/*----------------------------------------------------------------------*/

static const DirectoryEntry FAR0 *getDirEntry(File *file)
{
  return (DirectoryEntry FAR0 *) findSector(file->fileVol,file->directorySector) +
        file->directoryIndex;
}

#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                g e t D i r E n t r y F o r U p d a t e              */
/*                                                               */
/* Read a directory sector into the sector buffer and point to an       */
/* entry, with the intention of modifying it.                            */
/* The buffer will be flushed on operation exit.                     */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       file              : File belonging to directory entry              */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*       dirEntry       : Pointer to directory entry in buffer              */
/*----------------------------------------------------------------------*/

static FLStatus getDirEntryForUpdate(File *file, DirectoryEntry * *dirEntry)
{
  Volume vol = file->fileVol;

  checkStatus(updateSector(file->fileVol,file->directorySector,TRUE));
  *dirEntry = directory + file->directoryIndex;
  vol.volBuffer.checkPoint = TRUE;

  return flOK;
}

#endif  /* FL_READ_ONLY */
/*----------------------------------------------------------------------*/
/*                s e t C u r r e n t D a t e T i m e                   */
/*                                                               */
/* Set current time/date in directory entry                             */
/*                                                                      */
/* Parameters:                                                          */
/*       dirEntry       : Pointer to directory entry                    */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void setCurrentDateTime(DirectoryEntry *dirEntry)
{
  toLE2(dirEntry->updateTime,flCurrentTime());
  toLE2(dirEntry->updateDate,flCurrentDate());
}


/*----------------------------------------------------------------------*/
/*                      g e t F A T e n t r y                            */
/*                                                               */
/* Get an entry from the FAT. The 1st FAT copy is used.                     */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       cluster              : Cluster no. of enrty.                            */
/*                                                                      */
/* Returns:                                                             */
/*       Value of FAT entry.                                          */
/*----------------------------------------------------------------------*/

static FLStatus getFATentry(Volume vol, unsigned* entry)
{
  unsigned cluster = *entry;
  LEushort FAR0 *fat16Sector;

  unsigned fatSectorNo = vol.firstFATSectorNo;
#ifdef FAT_12BIT
  if (vol.flags & VOLUME_12BIT_FAT)
    fatSectorNo += (cluster * 3) >> (SECTOR_SIZE_BITS + 1);
  else
#endif
    fatSectorNo += cluster >> (SECTOR_SIZE_BITS - 1);
#ifndef FL_READ_ONLY
  if (!vol.volBuffer.dirty) {
    /* If the buffer is free, use it to store this FAT sector */
    checkStatus(updateSector(&vol,fatSectorNo,TRUE));
    vol.volBuffer.dirty = FALSE;
  }

#endif /* FL_READ_ONLY */
  fat16Sector = (LEushort FAR0 *) findSector(&vol,fatSectorNo);

  if(fat16Sector==NULL)
    return flSectorNotFound;

  if(fat16Sector==dataErrorToken)
    return flDataError;

#ifdef FAT_12BIT
  if (vol.flags & VOLUME_12BIT_FAT) {
    unsigned char FAR0 *fat12Sector = (unsigned char FAR0 *) fat16Sector;
    unsigned halfByteOffset = (cluster * 3) & (SECTOR_SIZE * 2 - 1);
    unsigned char firstByte = fat12Sector[halfByteOffset / 2];
    halfByteOffset += 2;
    if (halfByteOffset >= SECTOR_SIZE * 2) {
      /* Entry continues on the next sector. What a mess */
      halfByteOffset -= SECTOR_SIZE * 2;
      fat12Sector = (unsigned char FAR0 *) findSector(&vol,fatSectorNo + 1);
      if(fat12Sector==NULL)
        return flSectorNotFound;

      if(fat12Sector==dataErrorToken)
        return flDataError;
    }
    if (halfByteOffset & 1)
      *entry = ((firstByte & 0xf0) >> 4) + (fat12Sector[halfByteOffset / 2] << 4);
    else
      *entry = firstByte + ((fat12Sector[halfByteOffset / 2] & 0xf) << 8);

    if (*entry == 0xfff)    /* in 12-bit fat, 0xfff marks the last cluster */
      *entry = FAT_LAST_CLUSTER; /* return 0xffff instead */
    return flOK;
  }
  else {
#endif
    *entry = LE2(fat16Sector[cluster & (SECTOR_SIZE / 2 - 1)]);
    return flOK;
#ifdef FAT_12BIT
  }
#endif
}

#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                       s e t F A T e n t r y                            */
/*                                                               */
/* Writes a new value to a given FAT cluster entry.                     */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       cluster              : Cluster no. of enrty.                            */
/*       entry              : New value of FAT entry.                     */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus setFATentry(Volume vol, unsigned cluster, unsigned entry)
{
  LEushort *fat16Sector;

  unsigned fatSectorNo = vol.firstFATSectorNo;
#ifdef FAT_12BIT
  if (vol.flags & VOLUME_12BIT_FAT)
    fatSectorNo += (cluster * 3) >> (SECTOR_SIZE_BITS + 1);
  else
#endif
    fatSectorNo += cluster >> (SECTOR_SIZE_BITS - 1);

  checkStatus(updateSector(&vol,fatSectorNo,TRUE));
  fat16Sector = (LEushort *) vol.volBuffer.flData;

#ifdef FAT_12BIT
  if (vol.flags & VOLUME_12BIT_FAT) {
    unsigned char *fat12Sector = (unsigned char *) vol.volBuffer.flData;
    unsigned halfByteOffset = (cluster * 3) & (SECTOR_SIZE * 2 - 1);
    if (halfByteOffset & 1) {
      fat12Sector[halfByteOffset / 2] &= 0xf;
      fat12Sector[halfByteOffset / 2] |= (entry & 0xf) << 4;
    }
    else
      fat12Sector[halfByteOffset / 2] = entry;
    halfByteOffset += 2;
    if (halfByteOffset >= SECTOR_SIZE * 2) {
      /* Entry continues on the next sector. What a mess */
      halfByteOffset -= SECTOR_SIZE * 2;
      fatSectorNo++;
      checkStatus(updateSector(&vol,fatSectorNo,TRUE));
    }
    if (halfByteOffset & 1)
      fat12Sector[halfByteOffset / 2] = entry >> 4;
    else {
      fat12Sector[halfByteOffset / 2] &= 0xf0;
      fat12Sector[halfByteOffset / 2] |= (entry & 0x0f00) >> 8;
    }
  }
  else
#endif
    toLE2(fat16Sector[cluster & (SECTOR_SIZE / 2 - 1)],entry);

  return flOK;
}


/*----------------------------------------------------------------------*/
/*                      a l l o c a t e C l u s t e r                     */
/*                                                               */
/* Allocates a new cluster for a file and adds it to a FAT chain or     */
/* marks it in a directory entry.                                   */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       file              : File to extend. It should be positioned at    */
/*                       end-of-file.                                   */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus allocateCluster(File *file)
{
  Volume vol = file->fileVol;
  unsigned originalRover;
  unsigned fatEntry;

  if (file->flags & FILE_READ_ONLY)
    return flNoWriteAccess;

  /* Look for a free cluster. Start at the allocation rover */
  originalRover = vol.allocationRover;

  do {
    vol.allocationRover++;
    if (vol.allocationRover > vol.maxCluster)
      vol.allocationRover = 2;       /* wraparound to start of volume */
    if (vol.allocationRover == originalRover)
      return flNoSpaceInVolume;
    fatEntry = vol.allocationRover;
    checkStatus(getFATentry(&vol,&fatEntry));
  } while ( fatEntry!= FAT_FREE);

  /* Found a free cluster. Mark it as an end of chain */
  checkStatus(setFATentry(&vol,vol.allocationRover,FAT_LAST_CLUSTER));

  /* Mark previous cluster or directory to point to it */
  if (file->currentCluster == 0) {
    DirectoryEntry *dirEntry;
    checkStatus(getDirEntryForUpdate(file,&dirEntry));

    toLE2(dirEntry->startingCluster,vol.allocationRover);
    setCurrentDateTime(dirEntry);
  }
  else
    checkStatus(setFATentry(&vol,file->currentCluster,vol.allocationRover));

  /* Set our new current cluster */
  file->currentCluster = vol.allocationRover;

  return flOK;
}

#endif /* FL_READ_ONLY  */
/*----------------------------------------------------------------------*/
/*                   g e t S e c t o r A n d O f f s e t              */
/*                                                               */
/* Based on the current position of a file, gets a sector number and    */
/* offset in the sector that marks the file's current position.              */
/* If the position is at the end-of-file, and the file is opened for    */
/* write, this routine will extend the file by allocating a new cluster.*/
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus getSectorAndOffset(File *file,
                             SectorNo *sectorNo,
                             unsigned *offsetInSector)
{
  Volume vol = file->fileVol;
  unsigned offsetInCluster =
             (unsigned) file->currentPosition & (vol.bytesPerCluster - 1);

  if (file->flags & FILE_IS_ROOT_DIR) {
    if (file->currentPosition >= file->fileSize)
      return flRootDirectoryFull;
  }

  if (offsetInCluster == 0) {       /* This cluster is finished. Get next */
    if (!(file->flags & FILE_IS_ROOT_DIR)) {
      if (((file->currentPosition >= file->fileSize) && (file->currentPosition>0))||((file->fileSize==0)&&!(file->flags & FILE_IS_DIRECTORY))) {
#ifndef FL_READ_ONLY
        checkStatus(allocateCluster(file));
#else
        return flSectorNotFound;
#endif
      }
      else {
        unsigned nextCluster;
        if (file->currentCluster == 0) {
          const DirectoryEntry FAR0 *dirEntry;
          dirEntry = getDirEntry(file);
          if(dirEntry==NULL)
            return flSectorNotFound;
          if(dirEntry==dataErrorToken)
            return flDataError;
          nextCluster = LE2(dirEntry->startingCluster);
        }
       else {
          nextCluster = file->currentCluster;
          checkStatus(getFATentry(&vol,&nextCluster));
        }
        if (nextCluster < 2 || nextCluster > vol.maxCluster)
          /* We have a bad file size, or the FAT is bad */
          return flInvalidFATchain;
        file->currentCluster = nextCluster;
      }
    }
  }

  *offsetInSector = offsetInCluster & (SECTOR_SIZE - 1);
  if (file->flags & FILE_IS_ROOT_DIR)
    *sectorNo = vol.rootDirectorySectorNo +
                  (SectorNo) (file->currentPosition >> SECTOR_SIZE_BITS);
  else
    *sectorNo = firstSectorOfCluster(&vol,file->currentCluster) +
                  (SectorNo) (offsetInCluster >> SECTOR_SIZE_BITS);

  return flOK;
}




/*----------------------------------------------------------------------*/
/*                      c l o s e F i l e                            */
/*                                                               */
/* Closes an open file, records file size and dates in directory and    */
/* releases file handle.                                          */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       file              : File to close.                              */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus closeFile(File *file)
{
#ifndef FL_READ_ONLY
  if ((file->flags & FILE_MODIFIED) && !(file->flags & FILE_IS_ROOT_DIR)) {
    DirectoryEntry *dirEntry;
    checkStatus(getDirEntryForUpdate(file,&dirEntry));

    dirEntry->attributes |= ATTR_ARCHIVE;
    if (!(file->flags & FILE_IS_DIRECTORY))
      toLE4(dirEntry->fileSize,file->fileSize);
    setCurrentDateTime(dirEntry);
  }
#endif
  file->flags = 0;              /* no longer open */

  return flOK;
}

#ifndef FL_READ_ONLY
#ifdef SUB_DIRECTORY

/*----------------------------------------------------------------------*/
/*                      e x t e n d D i r e c t o r y                     */
/*                                                               */
/* Extends a directory, writing empty entries and the mandatory '.' and */
/* '..' entries.                                                 */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       file              : Directory file to extend. On entry,               */
/*                       currentPosition == fileSize. On exit, fileSize*/
/*                       is updated.                                   */
/*       ownerDir       : Cluster no. of owner directory              */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

static FLStatus extendDirectory(File *file, unsigned ownerDir)
{
  Volume vol = file->fileVol;
  unsigned i;
  SectorNo sectorOfDir;
  unsigned offsetInSector;

  /* Assuming the current position is at the end-of-file, this will     */
  /* extend the directory.                                          */
  checkStatus(getSectorAndOffset(file,&sectorOfDir,&offsetInSector));

  for (i = 0; i < vol.sectorsPerCluster; i++) {
    /* Write binary zeroes to indicate never-used entries */
    checkStatus(updateSector(&vol,sectorOfDir + i,FALSE));
    vol.volBuffer.checkPoint = TRUE;
    if (file->currentPosition == 0 && i == 0) {
      /* Add the mandatory . and .. entries */
      tffscpy(directory[0].name,".          ",sizeof directory[0].name);
      directory[0].attributes = ATTR_ARCHIVE | ATTR_DIRECTORY;
      toLE2(directory[0].startingCluster,file->currentCluster);
      toLE4(directory[0].fileSize,0);
      setCurrentDateTime(&directory[0]);
      tffscpy(&directory[1],&directory[0],sizeof directory[0]);
      directory[1].name[1] = '.';       /* change . to .. */
      toLE2(directory[1].startingCluster,ownerDir);
    }
    file->fileSize += SECTOR_SIZE;
  }
  /* Update the parent directory by closing the file */
  file->flags |= FILE_MODIFIED;
  return closeFile(file);
}

#endif       /* SUB_DIRECTORY */
#endif /* FL_READ_ONLY */

/*----------------------------------------------------------------------*/
/*                      f i n d D i r E n t r y                            */
/*                                                               */
/* Finds a directory entry by path-name, or finds an available directory*/
/* entry if the file does not exist.                                   */
/* Most fields necessary for opening a file are set by this routine.       */
/*                                                                      */
/* Parameters:                                                          */
/*       vol              : Pointer identifying drive                     */
/*       path              : path to find                                   */
/*      file            : File in which to record directory information.*/
/*                        Specific fields on entry:                     */
/*                         flags: if FILE_MUST_OPEN = 1, directory        */
/*                               will be extended if necessary.       */
/*                       on exit:                                   */
/*                         flags: FILE_IS_DIRECTORY and                   */
/*                               FILE_IS_ROOT_DIR set if true.       */
/*                         fileSize: Set for non-directory files.         */
/*                          currentCluster: Set to 0 (unknown)              */
/*                         ownerDirCluster: Set to 1st cluster of      */
/*                                  owning directory.                     */
/*                         directorySector: Sector of directory. If 0  */
/*                                  entry not found and directory full*/
/*                         directoryEntry: Entry # in directory sector */
/*                         currentPosition: not set by this routine.   */
/*                                                               */
/* Returns:                                                             */
/*       FLStatus       : 0 on success and file found                     */
/*                       flFileNotFound on success and file not found       */
/*                       otherwise failed.                            */
/*----------------------------------------------------------------------*/

static FLStatus findDirEntry(Volume vol, FLSimplePath FAR1 *path, File *file)
{
  File scanFile;              /* Internal file of search */
  unsigned dirBackPointer = 0;       /* 1st cluster of previous directory */

  FLStatus status = flOK;              /* root directory exists */

  file->flags |= (FILE_IS_ROOT_DIR | FILE_IS_DIRECTORY);
  file->fileSize = (long) (vol.sectorsInRootDirectory) << SECTOR_SIZE_BITS;
  file->fileVol = &vol;

#ifdef SUB_DIRECTORY
  for (; path->name[0]; path++) /* while we have another path segment */
#else
  if (path->name[0])    /* search the root directory */
#endif
  {
    status = flFileNotFound;              /* not yet */
    if (!(file->flags & FILE_IS_DIRECTORY))
      return flPathNotFound;  /* if we don't have a directory,
                            we have no business here */

    scanFile = *file;           /* the previous file found becomes the scan file */
    scanFile.currentPosition = 0;

    file->directorySector = 0;       /* indicate no entry found yet */
    file->flags &= ~(FILE_IS_ROOT_DIR | FILE_IS_DIRECTORY | FILE_READ_ONLY);
    file->ownerDirCluster = dirBackPointer;
    file->fileSize = 0;
    file->currentCluster = 0;

    /* Scan directory */
    while (scanFile.currentPosition < scanFile.fileSize) {
      int i;
      DirectoryEntry FAR0 *dirEntry;
      SectorNo sectorOfDir;
      unsigned offsetInSector;
      FLStatus readStatus = getSectorAndOffset(&scanFile,&sectorOfDir,&offsetInSector);
      if (readStatus == flInvalidFATchain) {
       scanFile.fileSize = scanFile.currentPosition;       /* now we know */
       break;              /* we ran into the end of the directory file */
      }
      else if (readStatus != flOK)
       return readStatus;

      dirEntry = (DirectoryEntry FAR0 *) findSector(&vol,sectorOfDir);
      if (dirEntry == NULL)
       return flSectorNotFound;
      if(dirEntry==dataErrorToken)
        return flDataError;

      scanFile.currentPosition += SECTOR_SIZE;

      for (i = 0; i < DIRECTORY_ENTRIES_PER_SECTOR; i++, dirEntry++) {
       if (tffscmp(path,dirEntry->name,sizeof dirEntry->name) == 0 &&
           !(dirEntry->attributes & ATTR_VOL_LABEL)) {
         /* Found a match */
         file->directorySector = sectorOfDir;
         file->directoryIndex = i;
         file->fileSize = LE4(dirEntry->fileSize);
         if (dirEntry->attributes & ATTR_DIRECTORY) {
           file->flags |= FILE_IS_DIRECTORY;
           file->fileSize = 0x7fffffffl;
           /* Infinite. Directories don't have a recorded size */
         }
         if (dirEntry->attributes & ATTR_READ_ONLY)
           file->flags |= FILE_READ_ONLY;
         dirBackPointer = LE2(dirEntry->startingCluster);
         status = flOK;
         goto endOfPathSegment;
       }
       else if (dirEntry->name[0] == NEVER_USED_DIR_ENTRY ||
               dirEntry->name[0] == DELETED_DIR_ENTRY) {
         /* Found a free entry. Remember it in case we don't find a match */
         if (file->directorySector == 0) {
           file->directorySector = sectorOfDir;
           file->directoryIndex = i;
         }
         if (dirEntry->name[0] == NEVER_USED_DIR_ENTRY)       /* end of dir */
           goto endOfPathSegment;
       }
      }
    }

endOfPathSegment:
    ;
  }
#ifndef FL_READ_ONLY
  if (status == flFileNotFound && (file->flags & FILE_MUST_OPEN) &&
      file->directorySector == 0) {
    /* We did not find a place in the directory for this new entry. The */
    /* directory should be extended. 'scanFile' refers to the directory */
    /* to extend, and the current pointer is at its end                     */
#ifdef SUB_DIRECTORY
    checkStatus(extendDirectory(&scanFile,(unsigned) file->ownerDirCluster));
    file->directorySector = firstSectorOfCluster(&vol,scanFile.currentCluster);
    file->directoryIndex = 0;             /* Has to be. This is a new cluster */
#else
    status = flRootDirectoryFull;
#endif
  }
#endif /* FL_READ_ONLY */
  return status;
}


/*----------------------------------------------------------------------*/
/*                                                               */
/*                       r e a d M u l t i S e c t o r                       */
/*                                                               */
/* Checks if file was written on consequent sectors.                     */
/* Parameters:                                                          */
/*      file              : File to check                                   */
/*       stillToRead           : Number of bytes to read. If the read extends  */
/*                       beyond the end-of-file, the read is truncated */
/*                       at the end-of-file.                            */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*       sectors               : Number of consequent sectors                  */
/*                                                               */
/*----------------------------------------------------------------------*/

static FLStatus readMultiSector(Volume vol,File *file,
                                  unsigned long stillToRead,
                                  SectorNo* sectors)
{
  SectorNo sectorCount = 1;
  unsigned offsetInCluster = (unsigned)((file->currentPosition & (vol.bytesPerCluster - 1))+512);

  while(stillToRead>=((sectorCount+1)<<SECTOR_SIZE_BITS)){
    if(offsetInCluster>=vol.bytesPerCluster) {
      unsigned nextCluster;
      nextCluster = file->currentCluster;
      checkStatus(getFATentry(&vol,&nextCluster));
      if (nextCluster < 2 || nextCluster > vol.maxCluster)
        /* We have a bad file size, or the FAT is bad */
       return flInvalidFATchain;
      if(nextCluster!=file->currentCluster+1)
       break;
      file->currentCluster = nextCluster;
      offsetInCluster = 0;
    }
    offsetInCluster+=SECTOR_SIZE;
    sectorCount++;
  }
  *sectors = sectorCount;
  return flOK;
}


/*----------------------------------------------------------------------*/
/*                            r e a d F i l e                            */
/*                                                               */
/* Reads from the current position in the file to the user-buffer.       */
/* Parameters:                                                          */
/*      file              : File to read.                                   */
/*      ioreq->irData       : Address of user buffer                     */
/*       ioreq->irLength       : Number of bytes to read. If the read extends  */
/*                       beyond the end-of-file, the read is truncated */
/*                       at the end-of-file.                            */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*       ioreq->irLength       : Actual number of bytes read                     */
/*----------------------------------------------------------------------*/

FLStatus readFile(File *file,IOreq FAR2 *ioreq)
{
  Volume vol = file->fileVol;
  unsigned char FAR1 *userData = (unsigned char FAR1 *) ioreq->irData;   /* user buffer address */
  unsigned long stillToRead = ioreq->irLength;
  unsigned long remainingInFile = file->fileSize - file->currentPosition;
  ioreq->irLength = 0;              /* read so far */

  /* Should we return an end of file status ? */
  if (stillToRead > remainingInFile)
    stillToRead = (unsigned) remainingInFile;

  while (stillToRead > 0) {
    SectorNo sectorToRead;
    unsigned offsetInSector;
    unsigned long readThisTime;
    const char FAR0 *sector;

    checkStatus(getSectorAndOffset(file,&sectorToRead,&offsetInSector));

    if (stillToRead < SECTOR_SIZE || offsetInSector > 0 || vol.tl.readSectors==NULL) {
      sector = (const char FAR0 *) findSector(&vol,sectorToRead);
      if(sector==NULL)
       {
    DEBUG_PRINT(("readFile : sector was not found\r\n"));
       return flSectorNotFound;
       }
      if(sector==dataErrorToken)
        return flDataError;

      readThisTime = SECTOR_SIZE - offsetInSector;
      if (readThisTime > stillToRead)
        readThisTime = (unsigned) stillToRead;
      if (sector)
        tffscpy(userData,sector + offsetInSector,(unsigned short)readThisTime);
      else
        return flSectorNotFound;              /* Sector does not exist */
    }
    else {
      SectorNo sectorCount;
      checkStatus(readMultiSector(&vol,file,stillToRead,&sectorCount));
      checkStatus(vol.tl.readSectors(vol.tl.rec,sectorToRead,userData,sectorCount));
      readThisTime = (sectorCount<<SECTOR_SIZE_BITS);
    }
    stillToRead -= readThisTime;
    ioreq->irLength += readThisTime;
    userData = (unsigned char FAR1 *)flAddLongToFarPointer(userData,readThisTime);
    file->currentPosition += readThisTime;
  }

  return flOK;
}


/*----------------------------------------------------------------------*/
/*               f l F i n d N e x t F i l e                            */
/*                                                                      */
/* See the description of 'flFindFirstFile'.                            */
/*                                                                      */
/* Parameters:                                                          */
/*       irHandle       : File handle returned by flFindFirstFile.       */
/*       irData              : Address of user buffer to receive a              */
/*                       DirectoryEntry structure                     */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus findNextFile(File *file, IOreq FAR2 *ioreq)
{
  DirectoryEntry FAR1 *irDirEntry = (DirectoryEntry FAR1 *) ioreq->irData;
  FLStatus status;
  /* Do we have a directory ? */
  if (!(file->flags & FILE_IS_DIRECTORY))
    return flNotADirectory;

  ioreq->irLength = DIRECTORY_ENTRY_SIZE;
  do {
    /*checkStatus(readFile(file,ioreq));*/
     /*Vadim: add treatment for a full cluster sub-directory */
    status=readFile(file,ioreq);
    if ((ioreq->irLength != DIRECTORY_ENTRY_SIZE) ||
        (irDirEntry->name[0] == NEVER_USED_DIR_ENTRY)||
        (!(file->flags&FILE_IS_ROOT_DIR)&&(status==flInvalidFATchain)))
         {
      checkStatus(closeFile(file));
      return flNoMoreFiles;
    }
    else
      {
       if(status!=flOK)
           return status;
      }
  } while (irDirEntry->name[0] == DELETED_DIR_ENTRY ||
          (irDirEntry->attributes & ATTR_VOL_LABEL));

  return flOK;
}
#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                       d e l e t e F i l e                            */
/*                                                               */
/* Deletes a file or directory.                                         */
/*                                                                      */
/* Parameters:                                                          */
/*       ioreq->irPath       : path of file to delete                     */
/*       isDirectory       : 0 = delete file, other = delete directory       */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus deleteFile(Volume vol, IOreq FAR2 *ioreq, FLBoolean isDirectory)
{
  File file;              /* Our private file */
  DirectoryEntry *dirEntry;

  file.flags = 0;
  checkStatus(findDirEntry(&vol,ioreq->irPath,&file));

  if (file.flags & FILE_READ_ONLY)
    return flNoWriteAccess;

  if (isDirectory) {
    DirectoryEntry fileFindInfo;
    ioreq->irData = &fileFindInfo;

    if (!(file.flags & FILE_IS_DIRECTORY))
      return flNotADirectory;
    /* Verify that directory is empty */
    file.currentPosition = 0;
    for (;;) {
      FLStatus status = findNextFile(&file,ioreq);
      if (status == flNoMoreFiles)
       break;
      if (status != flOK)
       return status;
      if (!((fileFindInfo.attributes & ATTR_DIRECTORY) &&
           (tffscmp(fileFindInfo.name,".          ",sizeof fileFindInfo.name) == 0 ||
            tffscmp(fileFindInfo.name,"..         ",sizeof fileFindInfo.name) == 0)))
       return flDirectoryNotEmpty;
    }
  }
  else {
    /* Did we find a directory ? */
    if (file.flags & FILE_IS_DIRECTORY)
      return flFileIsADirectory;
  }

  /* Mark directory entry deleted */
  checkStatus(getDirEntryForUpdate(&file,&dirEntry));
  dirEntry->name[0] = DELETED_DIR_ENTRY;

  /* Delete FAT entries */
  file.currentPosition = 0;
  file.currentCluster = LE2(dirEntry->startingCluster);
  while (file.currentPosition < file.fileSize) {
    unsigned nextCluster;

    if (file.currentCluster < 2 || file.currentCluster > vol.maxCluster)
      /* We have a bad file size, or the FAT is bad */
      return isDirectory ? flOK : flInvalidFATchain;
    nextCluster = file.currentCluster;
    checkStatus(getFATentry(&vol,&nextCluster));

    /* mark FAT free */
    checkStatus(setFATentry(&vol,file.currentCluster,FAT_FREE));
    vol.volBuffer.checkPoint = TRUE;

    /* mark sectors free */
    checkStatus(vol.tl.deleteSector(vol.tl.rec,
                                firstSectorOfCluster(&vol,file.currentCluster),
                                vol.sectorsPerCluster));

    file.currentPosition += vol.bytesPerCluster;
    file.currentCluster = nextCluster;
  }
  if (file.currentCluster > 1 && file.currentCluster <= vol.maxCluster) {
    checkStatus(setFATentry(&vol,file.currentCluster,FAT_FREE));
    vol.volBuffer.checkPoint = TRUE;

    /* mark sectors free */
    checkStatus(vol.tl.deleteSector(vol.tl.rec,
                                firstSectorOfCluster(&vol,file.currentCluster),
                                vol.sectorsPerCluster));
  }
  return flOK;
}


/*----------------------------------------------------------------------*/
/*                   s e t N a m e I n D i r E n t r y                     */
/*                                                               */
/* Sets the file name in a directory entry from a path name             */
/*                                                                      */
/* Parameters:                                                          */
/*       dirEntry       : directory entry                            */
/*       path              : path the last segment of which is the name       */
/*                                                                      */
/*----------------------------------------------------------------------*/

static void setNameInDirEntry(DirectoryEntry *dirEntry, FLSimplePath FAR1 *path)
{
  FLSimplePath FAR1 *lastSegment;

  for (lastSegment = path;              /* Find null terminator */
       lastSegment->name[0];
       lastSegment++);

  tffscpy(dirEntry->name,--lastSegment,sizeof dirEntry->name);
}

#endif /* FL_READ_ONLY */
/*----------------------------------------------------------------------*/
/*                       o p e n F i l e                            */
/*                                                               */
/* Opens an existing file or creates a new file. Creates a file handle  */
/* for further file processing.                                          */
/*                                                                      */
/* Parameters:                                                          */
/*       ioreq->irFlags       : Access and action options, defined below       */
/*       ioreq->irPath       : path of file to open                           */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*       ioreq->irHandle       : New file handle for open file                 */
/*                                                                      */
/*----------------------------------------------------------------------*/


FLStatus openFile(Volume vol, IOreq FAR2 *ioreq)
{
  int i;
  FLStatus status;

  /* Look for an available file */
  File *file = fileTable;
  for (i = 0; i < FILES && (file->flags & FILE_IS_OPEN); i++, file++);
  if (i >= FILES)
    return flTooManyOpenFiles;
  file->fileVol = &vol;
  ioreq->irHandle = i;              /* return file handle */
#ifndef FL_READ_ONLY
  /* Delete file if exists and required */
  if (ioreq->irFlags & ACCESS_CREATE) {
    FLStatus status = deleteFile(&vol,ioreq,FALSE);
    if (status != flOK && status != flFileNotFound)
      return status;
  }

  /* Find the path */
  if (ioreq->irFlags & ACCESS_CREATE)
    file->flags |= FILE_MUST_OPEN;
#endif /* FL_READ_ONLY */
  status =  findDirEntry(file->fileVol,ioreq->irPath,file);
  if (status != flOK &&
      (status != flFileNotFound || !(ioreq->irFlags & ACCESS_CREATE)))
    return status;

  /* Did we find a directory ? */
  if (file->flags & FILE_IS_DIRECTORY)
    return flFileIsADirectory;

#ifndef FL_READ_ONLY
  /* Create file if required */
  if (ioreq->irFlags & ACCESS_CREATE) {
    DirectoryEntry *dirEntry;
    /* Look for a free cluster. Start at the allocation rover */
    checkStatus(getDirEntryForUpdate(file,&dirEntry));
    setNameInDirEntry(dirEntry,ioreq->irPath);
    dirEntry->attributes = ATTR_ARCHIVE;
    toLE2(dirEntry->startingCluster,0);
    toLE4(dirEntry->fileSize,0);
    setCurrentDateTime(dirEntry);
  }
#endif /* FL_READ_ONLY  */
  if (!(ioreq->irFlags & ACCESS_READ_WRITE))
    file->flags |= FILE_READ_ONLY;

  file->currentPosition = 0;       /* located at file beginning     */
  file->flags |= FILE_IS_OPEN;     /* this file now officially open */

  return flOK;
}

#ifndef FL_READ_ONLY
/*----------------------------------------------------------------------*/
/*                                                                      */
/*                     w r i t e M u l t i S e c t o r                  */
/*                                                                      */
/* Checks the possibility of writing file on consequent sectors.        */
/* Parameters:                                                          */
/*      file           : File to check                                  */
/*      stillToWrite   : Number of bytes to read. If the read extends   */
/*                       beyond the end-of-file, the read is truncated  */
/*                       at the end-of-file.                            */
/* Returns:                                                             */
/*       FLStatu       : 0 on success, otherwise failed                 */
/*       sectors       : Number of consequent sectors                   */
/*                                                                      */
/*----------------------------------------------------------------------*/

static FLStatus writeMultiSector(Volume vol,File *file,
                                  unsigned long stillToWrite,
                                  SectorNo* sectors)
{
  SectorNo sectorCount = 1;
  unsigned offsetInCluster = (unsigned)((file->currentPosition & (vol.bytesPerCluster - 1))+512);

  while(stillToWrite>=((sectorCount+1)<<SECTOR_SIZE_BITS)){
    if(offsetInCluster>=vol.bytesPerCluster) {
      if ((long)(file->currentPosition+(sectorCount<<SECTOR_SIZE_BITS))>= file->fileSize) {
        if(file->currentCluster <= vol.maxCluster) {
          unsigned fatEntry;
          if(file->currentCluster+1>vol.maxCluster)
            break;/*There is not free consequent cluster*/
          fatEntry = file->currentCluster+1;
         checkStatus(getFATentry(&vol,&fatEntry));
          if(fatEntry==FAT_FREE) {
            /* Found a free cluster. Mark it as an end of chain */
            checkStatus(setFATentry(&vol,file->currentCluster+1,FAT_LAST_CLUSTER));

            /* Mark previous cluster or directory to point to it */
            checkStatus(setFATentry(&vol,file->currentCluster,file->currentCluster+1));

            /* Set our new current cluster */
            file->currentCluster = file->currentCluster+1;
            offsetInCluster = 0;
          }
          else /*There is not free consequent cluster*/
            break;
        }
        else
          return flInvalidFATchain;
      }
      else { /* We did not passed end of file*/
        unsigned nextCluster = file->currentCluster;
        checkStatus(getFATentry(&vol,&nextCluster));
        if (nextCluster < 2 || nextCluster > vol.maxCluster)
         /* We have a bad file size, or the FAT is bad */
         return flInvalidFATchain;
       if(nextCluster!=file->currentCluster+1)
          break;
       file->currentCluster = nextCluster;
        offsetInCluster = 0;
      }
    }
    offsetInCluster+=SECTOR_SIZE;
    sectorCount++;
  }
  *sectors = sectorCount;
  return flOK;
}

/*----------------------------------------------------------------------*/
/*                       w r i t e F i l e                              */
/*                                                                      */
/* Writes from the current position in the file from the user-buffer.   */
/*                                                                      */
/* Parameters:                                                          */
/*       file              : File to write.                             */
/*       ioreq->irData     : Address of user buffer                     */
/*       ioreq->irLength   : Number of bytes to write.                  */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus          : 0 on success, otherwise failed             */
/*       ioreq->irLength   : Actual number of bytes written             */
/*----------------------------------------------------------------------*/

FLStatus writeFile(File *file, IOreq FAR2 *ioreq)
{
  Volume vol = file->fileVol;
  char FAR1 *userData = (char FAR1 *) ioreq->irData;   /* user buffer address */
  unsigned long stillToWrite = ioreq->irLength;

  if (file->flags & FILE_READ_ONLY)
    return flNoWriteAccess;

  file->flags |= FILE_MODIFIED;

  ioreq->irLength = 0;              /* written so far */

  while (stillToWrite > 0) {
    SectorNo sectorToWrite;
    unsigned offsetInSector;
    unsigned long writeThisTime;

    checkStatus(getSectorAndOffset(file,&sectorToWrite,&offsetInSector));

    if (stillToWrite < SECTOR_SIZE || offsetInSector > 0) {
      unsigned short shortWrite;
      /* Not on full sector boundary */
      checkStatus(updateSector(&vol,sectorToWrite,
                  ((file->currentPosition < file->fileSize) || (offsetInSector > 0))));

#ifdef HIGH_SECURITY
      if ((file->flags & FILE_IS_DIRECTORY)||(file->currentPosition < file->fileSize))
#else
      if(file->flags & FILE_IS_DIRECTORY)
#endif
        vol.volBuffer.checkPoint = TRUE;
      writeThisTime = SECTOR_SIZE - offsetInSector;
      if (writeThisTime > stillToWrite)
                            writeThisTime = stillToWrite;

      shortWrite = (unsigned short)writeThisTime;
      tffscpy(vol.volBuffer.flData + offsetInSector,userData,shortWrite);
    }
    else {
      SectorNo sectorCount;
      if(vol.tl.writeMultiSector!=NULL) {
        checkStatus(writeMultiSector(&vol,file,stillToWrite,&sectorCount));
      }
      else
        sectorCount = 1;

      if (((sectorToWrite+sectorCount > vol.volBuffer.sectorNo) && (sectorToWrite <= vol.volBuffer.sectorNo)) &&
          (&vol == vol.volBuffer.owner)) {
       vol.volBuffer.sectorNo = UNASSIGNED_SECTOR;              /* no longer valid */
       vol.volBuffer.dirty = vol.volBuffer.checkPoint = FALSE;
      }

      if(vol.tl.writeMultiSector==NULL) {
        checkStatus(vol.tl.writeSector(vol.tl.rec,sectorToWrite,userData));
      }
      else {
        checkStatus(vol.tl.writeMultiSector(vol.tl.rec,sectorToWrite,userData,sectorCount));
      }
      writeThisTime = (sectorCount<<SECTOR_SIZE_BITS);
    }
    stillToWrite -= writeThisTime;
    ioreq->irLength += writeThisTime;
    userData = (char FAR1 *)flAddLongToFarPointer(userData,writeThisTime);
    file->currentPosition += writeThisTime;
    if (file->currentPosition > file->fileSize)
      file->fileSize = file->currentPosition;
  }

  return flOK;
}

#endif /*  FL_READ_ONLY  */
/*----------------------------------------------------------------------*/
/*                        s e e k F i l e                               */
/*                                                                      */
/* Sets the current position in the file, relative to file start, end   */
/* or current position.                                                 */
/* Note: This function will not move the file pointer beyond the        */
/* beginning or end of file, so the actual file position may be         */
/* different from the required. The actual position is indicated on     */
/* return.                                                              */
/*                                                                      */
/* Parameters:                                                          */
/*       file              : File to set position.                      */
/*       ioreq->irLength   : Offset to set position.                    */
/*       ioreq->irFlags    : Method code                                */
/*                     SEEK_START: absolute offset from start of file   */
/*                     SEEK_CURR:  signed offset from current position  */
/*                     SEEK_END:   signed offset from end of file       */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus        : 0 on success, otherwise failed               */
/*       ioreq->irLength : Actual absolute offset from start of file    */
/*----------------------------------------------------------------------*/

FLStatus seekFile(File *file, IOreq FAR2 *ioreq)
{
  Volume vol = file->fileVol;
  long int seekPosition = ioreq->irLength;

  switch (ioreq->irFlags) {

    case SEEK_START:
      break;

    case SEEK_CURR:
      seekPosition += file->currentPosition;
      break;

    case SEEK_END:
      seekPosition += file->fileSize;
      break;

    default:
      return flBadParameter;
  }

  if (seekPosition < 0)
    seekPosition = 0;
  if (seekPosition > file->fileSize)
    seekPosition = file->fileSize;

  /* now set the position ... */
  if (seekPosition < file->currentPosition) {
    file->currentCluster = 0;
    file->currentPosition = 0;
  }
  while (file->currentPosition < seekPosition) {
    SectorNo sectorNo;
    unsigned offsetInSector;
    checkStatus(getSectorAndOffset(file,&sectorNo,&offsetInSector));

    file->currentPosition += vol.bytesPerCluster;
    file->currentPosition &= - (long) (vol.bytesPerCluster);
  }
  ioreq->irLength = file->currentPosition = seekPosition;

  return flOK;
}


/*----------------------------------------------------------------------*/
/*                        f l F i n d F i l e                            */
/*                                                                      */
/* Finds a file entry in a directory, optionally modifying the file     */
/* time/date and/or attributes.                                         */
/* Files may be found by handle no. provided they are open, or by name. */
/* Only the Hidden, System or Read-only attributes may be modified.       */
/* Entries may be found for any existing file or directory other than   */
/* the root. A DirectoryEntry structure describing the file is copied   */
/* to a user buffer.                                                 */
/*                                                                      */
/* The DirectoryEntry structure is defined in dosformt.h              */
/*                                                                      */
/* Parameters:                                                          */
/*       irHandle       : If by name: Drive number (0, 1, ...)              */
/*                       else      : Handle of open file              */
/*       irPath              : If by name: Specifies a file or directory path*/
/*       irFlags              : Options flags                                   */
/*                       FIND_BY_HANDLE: Find open file by handle.        */
/*                                     Default is access by path.    */
/*                        SET_DATETIME:       Update time/date from buffer       */
/*                       SET_ATTRIBUTES: Update attributes from buffer       */
/*       irDirEntry       : Address of user buffer to receive a              */
/*                       DirectoryEntry structure                     */
/*                                                                      */
/* Returns:                                                             */
/*       irLength       : Modified                                   */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus findFile(Volume vol, File *file, IOreq FAR2 *ioreq)
{
  File tFile;                     /* temporary file for searches */

  if (ioreq->irFlags & FIND_BY_HANDLE)
    tFile = *file;
  else {
    tFile.flags = 0;
    checkStatus(findDirEntry(&vol,ioreq->irPath,&tFile));
  }

  if (tFile.flags & FILE_IS_ROOT_DIR)
    if (ioreq->irFlags & (SET_DATETIME | SET_ATTRIBUTES))
      return flPathIsRootDirectory;
    else {
      DirectoryEntry FAR1 *irDirEntry = (DirectoryEntry FAR1 *) ioreq->irData;

      tffsset(irDirEntry,0,sizeof(DirectoryEntry));
      irDirEntry->attributes = ATTR_DIRECTORY;
      return flOK;
    }

#ifndef FL_READ_ONLY
  if (ioreq->irFlags & (SET_DATETIME | SET_ATTRIBUTES)) {
    DirectoryEntry FAR1 *irDirEntry = (DirectoryEntry FAR1 *) ioreq->irData;
    DirectoryEntry *dirEntry;

    checkStatus(getDirEntryForUpdate(&tFile,&dirEntry));
    if (ioreq->irFlags & SET_DATETIME) {
      COPY2(dirEntry->updateDate,irDirEntry->updateDate);
      COPY2(dirEntry->updateTime,irDirEntry->updateTime);
    }
    if (ioreq->irFlags & SET_ATTRIBUTES) {
      unsigned char attr;
      attr = dirEntry->attributes & ATTR_DIRECTORY;
      attr |= irDirEntry->attributes &
            (ATTR_ARCHIVE | ATTR_HIDDEN | ATTR_READ_ONLY | ATTR_SYSTEM);
      dirEntry->attributes = attr;
    }
    tffscpy(irDirEntry, dirEntry, sizeof(DirectoryEntry));

  }
  else
#endif /* FL_READ_ONLY */
{
    const DirectoryEntry FAR0 *dirEntry;
    dirEntry = getDirEntry(&tFile);

    if(dirEntry==NULL)
      return flSectorNotFound;
    if(dirEntry==dataErrorToken)
      return flDataError;

    tffscpy(ioreq->irData,dirEntry,sizeof(DirectoryEntry));
    if (ioreq->irFlags & FIND_BY_HANDLE)
      toLE4(((DirectoryEntry FAR1 *) (ioreq->irData))->fileSize, tFile.fileSize);
  }

  return flOK;
}


/*----------------------------------------------------------------------*/
/*               f l F i n d F i r s t F i l e                            */
/*                                                                      */
/* Finds the first file entry in a directory.                            */
/* This function is used in combination with the flFindNextFile call,   */
/* which returns the remaining file entries in a directory sequentially.*/
/* Entries are returned according to the unsorted directory order.       */
/* flFindFirstFile creates a file handle, which is returned by it. Calls*/
/* to flFindNextFile will provide this file handle. When flFindNextFile */
/* returns 'noMoreEntries', the file handle is automatically closed.    */
/* Alternatively the file handle can be closed by a 'closeFile' call    */
/* before actually reaching the end of directory.                     */
/* A DirectoryEntry structure is copied to the user buffer describing   */
/* each file found. This structure is defined in dosformt.h.              */
/*                                                                      */
/* Parameters:                                                          */
/*       irHandle       : Drive number (0, 1, ...)                     */
/*       irPath              : Specifies a directory path                     */
/*       irData              : Address of user buffer to receive a              */
/*                       DirectoryEntry structure                     */
/*                                                                      */
/* Returns:                                                             */
/*       irHandle       : File handle to use for subsequent operations. */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus findFirstFile(Volume vol, IOreq FAR2 *ioreq)
{
  int i;

  /* Look for an available file */
  File *file = fileTable;
  for (i = 0; i < FILES && (file->flags & FILE_IS_OPEN); i++, file++);
  if (i >= FILES)
    return flTooManyOpenFiles;
  file->fileVol = &vol;
  ioreq->irHandle = i;              /* return file handle */

  /* Find the path */
  checkStatus(findDirEntry(file->fileVol,ioreq->irPath,file));

  file->currentPosition = 0;              /* located at file beginning */
  file->flags |= FILE_IS_OPEN | FILE_READ_ONLY; /* this file now officially open */

  return findNextFile(file,ioreq);
}


/*----------------------------------------------------------------------*/
/*                       g e t D i s k I n f o                            */
/*                                                               */
/* Returns general allocation information.                            */
/*                                                               */
/* The bytes/sector, sector/cluster, total cluster and free cluster       */
/* information are returned into a DiskInfo structure.                     */
/*                                                                      */
/* Parameters:                                                          */
/*       ioreq->irData       : Address of DiskInfo structure                 */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus getDiskInfo(Volume vol, IOreq FAR2 *ioreq)
{
  unsigned i;
  unsigned fatEntry;

  DiskInfo FAR1 *diskInfo = (DiskInfo FAR1 *) ioreq->irData;

  diskInfo->bytesPerSector = SECTOR_SIZE;
  diskInfo->sectorsPerCluster = vol.sectorsPerCluster;
  diskInfo->totalClusters = vol.maxCluster - 1;
  diskInfo->freeClusters = 0;              /* let's count them */

  for (i = 2; i <= vol.maxCluster; i++) {
    fatEntry = i;
    checkStatus(getFATentry(&vol,&fatEntry));
    if ( fatEntry== 0)
      diskInfo->freeClusters++;
  }

  return flOK;
}

#ifndef FL_READ_ONLY
#ifdef RENAME_FILE

/*----------------------------------------------------------------------*/
/*                      r e n a m e F i l e                            */
/*                                                               */
/* Renames a file to another name.                                   */
/*                                                               */
/* Parameters:                                                          */
/*       ioreq->irPath       : path of existing file                            */
/*      ioreq->irData       : path of new name.                            */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus renameFile(Volume vol, IOreq FAR2 *ioreq)
{
  File file, file2;              /* temporary files for searches */
  DirectoryEntry *dirEntry, *dirEntry2;
  FLStatus status;
  FLSimplePath FAR1 *irPath2 = (FLSimplePath FAR1 *) ioreq->irData;

  file.flags = 0;
  checkStatus(findDirEntry(&vol,ioreq->irPath,&file));

  file2.flags = FILE_MUST_OPEN;
  status = findDirEntry(file.fileVol,irPath2,&file2);
  if (status != flFileNotFound)
    return status == flOK ? flFileAlreadyExists : status;

#ifndef VFAT_COMPATIBILITY
  if (file.ownerDirCluster == file2.ownerDirCluster) {       /* Same directory */
    /* Change name in directory entry */
    checkStatus(getDirEntryForUpdate(&file,&dirEntry));
    setNameInDirEntry(dirEntry,irPath2);
  }
  else
#endif
  {       /* not same directory */
    /* Write new directory entry */
    const DirectoryEntry FAR0 *dir;
    checkStatus(getDirEntryForUpdate(&file2,&dirEntry2));

    dir = getDirEntry(&file);
    if(dir==NULL)
      return flSectorNotFound;
    if(dir==dataErrorToken)
      return flDataError;
    *dirEntry2 = *dir;

     setNameInDirEntry(dirEntry2,irPath2);

    /* Delete original entry */
    checkStatus(getDirEntryForUpdate(&file,&dirEntry));
    dirEntry->name[0] = DELETED_DIR_ENTRY;
  }

  return flOK;
}

#endif /* RENAME_FILE */
#endif /* FL_READ_ONLY  */


#ifndef FL_READ_ONLY
#ifdef SUB_DIRECTORY

/*----------------------------------------------------------------------*/
/*                           m a k e D i r                            */
/*                                                               */
/* Creates a new directory.                                          */
/*                                                                      */
/* Parameters:                                                          */
/*       ioreq->irPath       : path of new directory.                     */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

FLStatus makeDir(Volume vol, IOreq FAR2 *ioreq)
{
  File file;                     /* temporary file for searches */
  unsigned dirBackPointer;
  DirectoryEntry *dirEntry;
  FLStatus status;
  unsigned originalRover;
  unsigned fatEntry;

  file.flags = FILE_MUST_OPEN;
  status = findDirEntry(&vol,ioreq->irPath,&file);
  if (status != flFileNotFound)
    return status == flOK ? flFileAlreadyExists : status;


  /* Look for a free cluster. Start at the allocation rover */
  originalRover = vol.allocationRover;
  do {
    vol.allocationRover++;
    if (vol.allocationRover > vol.maxCluster)
      vol.allocationRover = 2;       /* wraparound to start of volume */
    if (vol.allocationRover == originalRover)
      return flNoSpaceInVolume;

    fatEntry = vol.allocationRover;
    checkStatus(getFATentry(&vol,&fatEntry));
  } while ( fatEntry!= FAT_FREE);
    /* Found a free cluster. Mark it as an end of chain */
  checkStatus(setFATentry(&vol,vol.allocationRover,FAT_LAST_CLUSTER));

  /* Create the directory entry for the new dir */
  checkStatus(getDirEntryForUpdate(&file,&dirEntry));

  setNameInDirEntry(dirEntry,ioreq->irPath);
  dirEntry->attributes = ATTR_ARCHIVE | ATTR_DIRECTORY;
  toLE2(dirEntry->startingCluster,vol.allocationRover);
  toLE4(dirEntry->fileSize,0);
  setCurrentDateTime(dirEntry);

  /* Remember the back pointer to owning directory for the ".." entry */
  dirBackPointer = (unsigned) file.ownerDirCluster;

  file.flags |= FILE_IS_DIRECTORY;
  file.currentPosition = 0;
  file.fileSize = 0;
  return extendDirectory(&file,dirBackPointer);
}


#endif /* SUB_DIRECTORY */

#ifdef SPLIT_JOIN_FILE

/*------------------------------------------------------------------------*/
/*                        j o i n F i l e                                 */
/*                                                                        */
/* joins two files. If the end of the first file is on a cluster          */
/* boundary, the files will be joined there. Otherwise, the data in       */
/* the second file from the beginning until the offset that is equal to   */
/* the offset in cluster of the end of the first file will be lost. The   */
/* rest of the second file will be joined to the first file at the end of */
/* the first file. On exit, the first file is the expanded file and the   */
/* second file is deleted.                                                */
/* Note: The second file will be open by this function, it is advised to  */
/*        close it before calling this function in order to avoid          */
/*        inconsistencies.                                                 */
/*                                                                        */
/* Parameters:                                                            */
/*       file            : file to join to.                                */
/*       irPath          : Path name of the file to be joined.             */
/*                                                                        */
/* Returns:                                                               */
/*       FLStatus        : 0 on success, otherwise failed.                 */
/*                                                                        */
/*------------------------------------------------------------------------*/

FLStatus joinFile (File *file, IOreq FAR2 *ioreq)
{
  Volume vol = file->fileVol;
  File joinedFile;
  DirectoryEntry *joinedDirEntry;
  unsigned offsetInCluster = (unsigned)(file->fileSize % vol.bytesPerCluster);

  if (file->flags & FILE_READ_ONLY)
    return flNoWriteAccess;

  if (file->flags & FILE_IS_DIRECTORY)
    return flFileIsADirectory;

  /* open the joined file. */
  joinedFile.flags = 0;
  checkStatus(findDirEntry(file->fileVol,ioreq->irPath,&joinedFile));
  joinedFile.currentPosition = 0;

  /* Check if the two files are the same file. */
  if (file->directorySector == joinedFile.directorySector &&
      file->directoryIndex == joinedFile.directoryIndex)
    return flBadFileHandle;

  file->flags |= FILE_MODIFIED;

  if (joinedFile.fileSize > (long)offsetInCluster) { /* the joined file extends
                                             beyond file's end of file.*/
    unsigned lastCluster, nextCluster, firstCluster;
    const DirectoryEntry FAR0 *dir;
    dir = getDirEntry(&joinedFile);

    if(dir==NULL)
      return flSectorNotFound;
    if(dir==dataErrorToken)
      return flDataError;

    /* get the first cluster of the joined file. */
    firstCluster = LE2(dir->startingCluster);

    if (file->fileSize) {  /* the file is not empty.*/
      /* find the last cluster of file by following the FAT chain.*/
      if (file->currentCluster == 0) {    /* start from the first cluster.*/
        const DirectoryEntry FAR0 *dir;
        dir = getDirEntry(file);

        if(dir==NULL)
          return flSectorNotFound;
        if(dir==dataErrorToken)
          return flDataError;
        nextCluster = LE2(dir->startingCluster);
      }
      else                               /* start from the current cluster.*/
       nextCluster = file->currentCluster;
      /* follow the FAT chain.*/
      while (nextCluster != FAT_LAST_CLUSTER) {
       if (nextCluster < 2 || nextCluster > vol.maxCluster)
         return flInvalidFATchain;
       lastCluster = nextCluster;
        checkStatus(getFATentry(&vol,&nextCluster));
      }
    }
    else                   /* the file is empty. */
      lastCluster = 0;

    if (offsetInCluster) {      /* join in the middle of a cluster.*/
      SectorNo sectorNo, joinedSectorNo, tempSectorNo;
      unsigned offset, joinedOffset, numOfSectors = 1, i;
      const char FAR0 *startCopy;
      unsigned fatEntry;

      /* get the sector and offset of the end of the file.*/
      file->currentPosition = file->fileSize;
      file->currentCluster = lastCluster;
      checkStatus(getSectorAndOffset(file, &sectorNo, &offset));

      /* load the sector of the end of the file to the buffer.*/
      checkStatus(updateSector(&vol, sectorNo, TRUE));

      /*  copy the second part of the first cluster of the joined file
         to the end of the last cluster of the original file.*/
      /* first set the current position of the joined file.*/
      joinedFile.currentPosition = offsetInCluster;
      joinedFile.currentCluster = firstCluster;
      /* get the relevant sector in the joined file.*/
      checkStatus(getSectorAndOffset(&joinedFile, &joinedSectorNo, &joinedOffset));
      /* map sector and offset.*/
      startCopy = (const char FAR0 *) findSector(&vol,joinedSectorNo) + joinedOffset;
      if (startCopy == NULL)
       return flSectorNotFound;
      if(startCopy==dataErrorToken)
        return flDataError;

      /* copy.*/
      tffscpy(vol.volBuffer.flData + offset, startCopy, SECTOR_SIZE - offset);
      checkStatus(flushBuffer(&vol));

      /* find how many sectors should still be copied (the number of sectors
        until the end of the current cluster).*/
      tempSectorNo = firstSectorOfCluster(&vol,lastCluster);
      while(tempSectorNo != sectorNo) {
       tempSectorNo++;
       numOfSectors++;
      }

      /* copy the rest of the sectors in the current cluster.
        this is done by loading a sector from the joined file to the buffer,
        changing the sectoNo of the buffer to the relevant sector in file
        and then flushing the buffer.*/
      sectorNo++;
      joinedSectorNo++;
      for(i = 0; i < vol.sectorsPerCluster - numOfSectors; i++) {
       checkStatus(updateSector(&vol,joinedSectorNo, TRUE));
       vol.volBuffer.sectorNo = sectorNo;
       checkStatus(flushBuffer(&vol));
       sectorNo++;
       joinedSectorNo++;
      }
      fatEntry = firstCluster;
      checkStatus(getFATentry(&vol,&fatEntry));
      /* adjust the FAT chain.*/
      checkStatus(setFATentry(&vol,
                           lastCluster,
                           fatEntry));

      /* mark the first cluster of the joined file as free */
      checkStatus(setFATentry(&vol,firstCluster,FAT_FREE));
      vol.volBuffer.checkPoint = TRUE;

      /* mark sectors free */
      checkStatus(vol.tl.deleteSector(vol.tl.rec,firstSectorOfCluster(&vol,firstCluster),
                                  vol.sectorsPerCluster));
    }
    else {    /* join on a cluster boundary.*/
      if (lastCluster) {      /* file is not empty. */
       checkStatus(setFATentry(&vol,lastCluster, firstCluster));
      }
      else {                  /* file is empty.*/
       DirectoryEntry *dirEntry;

       checkStatus(getDirEntryForUpdate(file, &dirEntry));
       toLE2(dirEntry->startingCluster, firstCluster);
       setCurrentDateTime(dirEntry);
      }
    }
    /*adjust the size of the expanded file.*/
    file->fileSize += joinedFile.fileSize - offsetInCluster;

    /* mark the directory entry of the joined file as deleted.*/
    checkStatus(getDirEntryForUpdate(&joinedFile, &joinedDirEntry));
    joinedDirEntry->name[0] = DELETED_DIR_ENTRY;
  }
  else        /* the joined file is too small all is left to do is delete it */
    checkStatus(deleteFile (&vol, ioreq, FALSE));

  return flOK;
}


/*------------------------------------------------------------------------*/
/*                    s p l i t F i l e                                   */
/*                                                                        */
/* Splits the file into two files. The original file contains the first   */
/* part, and a new file (which is created for that purpose) contains      */
/* the second part. If the current position is on a cluster               */
/* boundary, the file will be split at the current position. Otherwise,   */
/* the cluster of the current position is duplicated, one copy is the     */
/* first cluster of the new file, and the other is the last cluster of the*/
/* original file, which now ends at the current position.                 */
/*                                                                        */
/* Parameters:                                                            */
/*       file            : file to split.                                  */
/*       irPath          : Path name of the new file.                      */
/*                                                                        */
/* Returns:                                                               */
/*       irHandle        : handle of the new file.                         */
/*       FLStatus        : 0 on success, otherwise failed.                 */
/*                                                                        */
/*------------------------------------------------------------------------*/

FLStatus splitFile (File *file, IOreq FAR2 *ioreq)
{
  Volume vol = file->fileVol;
  File *newFile, dummyFile;
  IOreq ioreq2;
  FLStatus status;
  unsigned fatEntry;

  if (file->flags & FILE_READ_ONLY)
    return flNoWriteAccess;

  if (file->flags & FILE_IS_DIRECTORY)
    return flFileIsADirectory;

  /* check if the path of the new file already exists.*/
  dummyFile.flags = 0;
  status = findDirEntry(&vol,ioreq->irPath,&dummyFile);
  if (status != flFileNotFound) {
    if (status == flOK)              /* there is a file with that path.*/
      return flFileAlreadyExists;
    else
      return status;
  }

  /* open the new file.*/
  ioreq2.irFlags = OPEN_FOR_WRITE;
  ioreq2.irPath = ioreq->irPath;
  checkStatus(openFile(&vol,&ioreq2));

  newFile = fileTable + ioreq2.irHandle;
  newFile->flags |= FILE_MODIFIED;
  file->flags |= FILE_MODIFIED;

  if (file->currentPosition % vol.bytesPerCluster) { /* not on a cluster boundary.*/
    SectorNo sectorNo, newSectorNo, lastSector;
    int i;
    if((status = allocateCluster(newFile)) != flOK) {
      newFile->flags = 0;                             /* close the new file */
      return status;
    }
    sectorNo = firstSectorOfCluster(&vol,file->currentCluster);
    newSectorNo = firstSectorOfCluster(&vol,newFile->currentCluster);
    /* deal with split in a last not full cluster */
    fatEntry = file->currentCluster;
    checkStatus(getFATentry(&vol,&fatEntry));
    if (fatEntry == FAT_LAST_CLUSTER)
      lastSector = ((file->fileSize - 1) % vol.bytesPerCluster)/SECTOR_SIZE +
                   sectorNo;
    else
      lastSector = sectorNo + vol.sectorsPerCluster; /* out of the cluster */

    /* copy the current cluster of the original file to the first cluster
       of the new file, sector after sector.*/
    for(i = 0; i < (int)vol.sectorsPerCluster; i++) {
      if((status = updateSector(&vol,sectorNo, TRUE)) != flOK) {
        newFile->flags = 0;                             /* close the new file */
       return status;
      }

      vol.volBuffer.sectorNo = newSectorNo;
      if((status = flushBuffer(&vol)) != flOK) {
        newFile->flags = 0;                             /* close the new file */
        return status;
      }

      sectorNo++;
      newSectorNo++;

      if(sectorNo > lastSector)
        break;
    }
    fatEntry = file->currentCluster;
    checkStatus(getFATentry(&vol,&fatEntry));

    /* adjust the FAT chain of the new file.*/
    if((status = setFATentry(&vol,newFile->currentCluster,
                             fatEntry)) != flOK) {
      newFile->flags = 0;                             /* close the new file */
      return status;
    }

    /* mark current cluster 0 (as current position).*/
    newFile->currentCluster = 0;
  }
  else {                                  /* on a cluster boundary.*/
    DirectoryEntry *newDirEntry;

    /* adjust the directory entry of the new file.*/
    if((status = getDirEntryForUpdate(newFile,&newDirEntry)) != flOK) {
      newFile->flags = 0;                             /* close the new file */
      return status;
    }

    if (file->currentPosition) { /* split at the middle of the file.*/
      fatEntry = file->currentCluster;
      checkStatus(getFATentry(&vol,&fatEntry));

      toLE2(newDirEntry->startingCluster, fatEntry);
      setCurrentDateTime(newDirEntry);
    }
    else {                     /* split at the beginning of the file.*/
      DirectoryEntry *dirEntry;

      const DirectoryEntry FAR0 *dir;
      dir = getDirEntry(file);
      if(dir==NULL)
       return flSectorNotFound;
      if(dir==dataErrorToken)
       return flDataError;

      /* first cluster of file becomes the first cluster of the new file.*/
      toLE2(newDirEntry->startingCluster,LE2(dir->startingCluster));
      setCurrentDateTime(newDirEntry);

      /* starting cluster of file becomes 0.*/
      if((status = getDirEntryForUpdate(file, &dirEntry)) != flOK) {
       newFile->flags = 0;                             /* close the new file */
       return status;
      }

      toLE2(dirEntry->startingCluster, 0);
      setCurrentDateTime(dirEntry);
    }
  }

  /* adjust the size of the new file.*/
  newFile->fileSize = file->fileSize - file->currentPosition +
                   (file->currentPosition % vol.bytesPerCluster);

  /* adjust the chain and size of the original file.*/
  if (file->currentPosition)    /* we didn't split at the beginning.*/
    if((status = setFATentry(&vol,file->currentCluster, FAT_LAST_CLUSTER)) != flOK) {
      newFile->flags = 0;                             /* close the new file */
      return status;
    }

  file->fileSize = file->currentPosition;

  /* return to the user the handle of the new file.*/
  ioreq->irHandle = ioreq2.irHandle;

  return flOK;
}

#endif /* SPLIT_JOIN_FILE */
#endif /*  FL_READ_ONLY  */


/*----------------------------------------------------------------------*/
/*                       I n i t F S                                */
/*                                                               */
/* Initializes the FLite file system.                                     */
/*                                                               */
/* Calling this function is optional. If it is not called,              */
/* initialization will be done automatically on the first FLite call.       */
/* This function is provided for those applications who want to              */
/* explicitly initialize the system and get an initialization status.       */
/*                                                               */
/* Calling flInit after initialization was done has no effect.              */
/*                                                               */
/* Parameters:                                                          */
/*       None                                                        */
/*                                                                      */
/* Returns:                                                             */
/*       FLStatus       : 0 on success, otherwise failed                */
/*----------------------------------------------------------------------*/

void initFS()
{
  unsigned i;
  unsigned volNo;
  Volume vol = vols;

  for (volNo = 0; volNo < VOLUMES; volNo++, pVol++) {
    vol.volBuffer.dirty = FALSE;
    vol.volBuffer.owner = NULL;
    vol.volBuffer.sectorNo = UNASSIGNED_SECTOR;       /* Current sector no. (none) */
    vol.volBuffer.checkPoint = FALSE;
  }

  for (i = 0; i < FILES; i++)
    fileTable[i].flags = 0;

}

#endif