/*!!*/ /* * $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,§orOfDir,&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,§orOfDir,&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)<=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,§orToRead,&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,§orCount)); checkStatus(vol.tl.readSectors(vol.tl.rec,sectorToRead,userData,sectorCount)); readThisTime = (sectorCount<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)<=vol.bytesPerCluster) { if ((long)(file->currentPosition+(sectorCount<= 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,§orToWrite,&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,§orCount)); } 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<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,§orNo,&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, §orNo, &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