| /* Copyright 1996-2006,2008,2009 Alain Knaff. |
| * This file is part of mtools. |
| * |
| * Mtools is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * Mtools is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with Mtools. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "sysincludes.h" |
| #include "msdos.h" |
| #include "stream.h" |
| #include "mtools.h" |
| #include "fsP.h" |
| #include "file_name.h" |
| |
| #ifdef HAVE_LONG_LONG |
| typedef long long fatBitMask; |
| #else |
| typedef long fatBitMask; |
| #endif |
| |
| typedef struct FatMap_t { |
| unsigned char *data; |
| fatBitMask dirty; |
| fatBitMask valid; |
| } FatMap_t; |
| |
| #define SECT_PER_ENTRY (sizeof(fatBitMask)*8) |
| #define ONE ((fatBitMask) 1) |
| |
| static __inline__ int readSector(Fs_t *This, char *buf, unsigned int off, |
| size_t size) |
| { |
| return READS(This->Next, buf, sectorsToBytes((Stream_t *)This, off), |
| size << This->sectorShift); |
| } |
| |
| |
| static __inline__ int forceReadSector(Fs_t *This, char *buf, unsigned int off, |
| size_t size) |
| { |
| return force_read(This->Next, buf, sectorsToBytes((Stream_t *)This, off), |
| size << This->sectorShift); |
| } |
| |
| |
| static __inline__ int forceWriteSector(Fs_t *This, char *buf, unsigned int off, |
| size_t size) |
| { |
| return force_write(This->Next, buf, sectorsToBytes((Stream_t*)This, off), |
| size << This->sectorShift); |
| } |
| |
| |
| static FatMap_t *GetFatMap(Fs_t *Stream) |
| { |
| size_t nr_entries; |
| size_t i; |
| FatMap_t *map; |
| |
| Stream->fat_error = 0; |
| nr_entries = (Stream->fat_len + SECT_PER_ENTRY - 1) / SECT_PER_ENTRY; |
| map = NewArray(nr_entries, FatMap_t); |
| if(!map) |
| return 0; |
| |
| for(i=0; i< nr_entries; i++) { |
| map[i].data = 0; |
| map[i].valid = 0; |
| map[i].dirty = 0; |
| } |
| |
| return map; |
| } |
| |
| static __inline__ int locate(Fs_t *Stream, size_t offset, int *slot, int *bit) |
| { |
| if(offset >= Stream->fat_len) |
| return -1; |
| *slot = offset / SECT_PER_ENTRY; |
| *bit = offset % SECT_PER_ENTRY; |
| return 0; |
| } |
| |
| static __inline__ int fatReadSector(Fs_t *This, int sector, int slot, |
| int bit, int dupe, fatBitMask bitmap) |
| { |
| int fat_start, ret; |
| int nr_sectors; |
| |
| dupe = (dupe + This->primaryFat) % This->num_fat; |
| fat_start = This->fat_start + This->fat_len * dupe; |
| |
| if(bitmap == 0) { |
| nr_sectors = SECT_PER_ENTRY - bit%SECT_PER_ENTRY; |
| } else { |
| nr_sectors = 1; |
| } |
| |
| /* first, read as much as the buffer can give us */ |
| ret = readSector(This, |
| (char *)(This->FatMap[slot].data+(bit<<This->sectorShift)), |
| fat_start+sector, |
| nr_sectors); |
| if(ret < 0) |
| return 0; |
| |
| if((unsigned int) ret < This->sector_size) { |
| /* if we got less than one sector's worth, insist to get at |
| * least one sector */ |
| ret = forceReadSector(This, |
| (char *) (This->FatMap[slot].data + |
| (bit << This->sectorShift)), |
| fat_start+sector, 1); |
| if(ret < (int) This->sector_size) |
| return 0; |
| return 1; |
| } |
| |
| return ret >> This->sectorShift; |
| } |
| |
| |
| static int fatWriteSector(Fs_t *This, int sector, int slot, int bit, int dupe) |
| { |
| int fat_start; |
| |
| dupe = (dupe + This->primaryFat) % This->num_fat; |
| if(dupe && !This->writeAllFats) |
| return This->sector_size; |
| |
| fat_start = This->fat_start + This->fat_len * dupe; |
| |
| return forceWriteSector(This, |
| (char *) |
| (This->FatMap[slot].data + bit * This->sector_size), |
| fat_start+sector, 1); |
| } |
| |
| static unsigned char *loadSector(Fs_t *This, |
| unsigned int sector, fatAccessMode_t mode, |
| int recurs) |
| { |
| int slot, bit, ret; |
| |
| if(locate(This,sector, &slot, &bit) < 0) |
| return 0; |
| #if 0 |
| if (((This->fat_len + SECT_PER_ENTRY - 1) / SECT_PER_ENTRY) <= slot) { |
| fprintf(stderr,"This should not happen\n"); |
| fprintf(stderr, "fat_len = %d\n", This->fat_len); |
| fprintf(stderr, "SECT_PER_ENTRY=%d\n", (int)SECT_PER_ENTRY); |
| fprintf(stderr, "sector = %d slot = %d bit=%d\n", |
| sector, slot, bit); |
| fprintf(stderr, "left = %d",(int) |
| ((This->fat_len+SECT_PER_ENTRY-1) / SECT_PER_ENTRY)); |
| return 0; |
| } |
| #endif |
| if(!This->FatMap[slot].data) { |
| /* allocate the storage space */ |
| This->FatMap[slot].data = |
| malloc(This->sector_size * SECT_PER_ENTRY); |
| if(!This->FatMap[slot].data) |
| return 0; |
| memset(This->FatMap[slot].data, 0xee, |
| This->sector_size * SECT_PER_ENTRY); |
| } |
| |
| if(! (This->FatMap[slot].valid & (ONE << bit))) { |
| unsigned int i; |
| ret = -1; |
| for(i=0; i< This->num_fat; i++) { |
| /* read the sector */ |
| ret = fatReadSector(This, sector, slot, bit, i, |
| This->FatMap[slot].valid); |
| |
| if(ret == 0) { |
| fprintf(stderr, |
| "Error reading fat number %d\n", i); |
| continue; |
| } |
| if(This->FatMap[slot].valid) |
| /* Set recurs if there have already been |
| * sectors loaded in this bitmap long |
| */ |
| recurs = 1; |
| break; |
| } |
| |
| /* all copies bad. Return error */ |
| if(ret == 0) |
| return 0; |
| |
| for(i=0; (int) i < ret; i++) |
| This->FatMap[slot].valid |= ONE << (bit + i); |
| |
| if(!recurs && ret == 1) |
| /* do some prefetching, if we happened to only |
| * get one sector */ |
| loadSector(This, sector+1, mode, 1); |
| if(!recurs && batchmode) |
| for(i=0; i < 1024; i++) |
| loadSector(This, sector+i, mode, 1); |
| } |
| |
| if(mode == FAT_ACCESS_WRITE) { |
| This->FatMap[slot].dirty |= ONE << bit; |
| This->fat_dirty = 1; |
| } |
| return This->FatMap[slot].data + (bit << This->sectorShift); |
| } |
| |
| |
| static unsigned char *getAddress(Fs_t *Stream, |
| unsigned int num, fatAccessMode_t mode) |
| { |
| unsigned char *ret; |
| int sector; |
| int offset; |
| |
| sector = num >> Stream->sectorShift; |
| ret = 0; |
| if(sector == Stream->lastFatSectorNr && |
| Stream->lastFatAccessMode >= mode) |
| ret = Stream->lastFatSectorData; |
| if(!ret) { |
| ret = loadSector(Stream, sector, mode, 0); |
| if(!ret) |
| return 0; |
| Stream->lastFatSectorNr = sector; |
| Stream->lastFatSectorData = ret; |
| Stream->lastFatAccessMode = mode; |
| } |
| offset = num & Stream->sectorMask; |
| return ret+offset; |
| } |
| |
| |
| static int readByte(Fs_t *Stream, int start) |
| { |
| unsigned char *address; |
| |
| address = getAddress(Stream, start, FAT_ACCESS_READ); |
| if(!address) |
| return -1; |
| return *address; |
| } |
| |
| |
| /* |
| * Fat 12 encoding: |
| * | byte n | byte n+1 | byte n+2 | |
| * |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0| |
| * | | | | | | | | | | | | | | | | | | | | | | | | | |
| * | n+0.0 | n+0.5 | n+1.0 | n+1.5 | n+2.0 | n+2.5 | |
| * \_____ \____ \______/________/_____ / |
| * ____\______\________/ _____/ ____\_/ |
| * / \ \ / / \ |
| * | n+1.5 | n+0.0 | n+0.5 | n+2.0 | n+2.5 | n+1.0 | |
| * | FAT entry k | FAT entry k+1 | |
| */ |
| |
| /* |
| * Get and decode a FAT (file allocation table) entry. Returns the cluster |
| * number on success or 1 on failure. |
| */ |
| |
| static unsigned int fat12_decode(Fs_t *Stream, unsigned int num) |
| { |
| unsigned int start = num * 3 / 2; |
| int byte0 = readByte(Stream, start); |
| int byte1 = readByte(Stream, start+1); |
| |
| if (num < 2 || byte0 < 0 || byte1 < 0 || num > Stream->num_clus+1) { |
| fprintf(stderr,"[1] Bad address %d\n", num); |
| return 1; |
| } |
| |
| if (num & 1) |
| return (byte1 << 4) | ((byte0 & 0xf0)>>4); |
| else |
| return ((byte1 & 0xf) << 8) | byte0; |
| } |
| |
| |
| /* |
| * Puts a code into the FAT table. Is the opposite of fat_decode(). No |
| * sanity checking is done on the code. Returns a 1 on error. |
| */ |
| static void fat12_encode(Fs_t *Stream, unsigned int num, unsigned int code) |
| { |
| int start = num * 3 / 2; |
| unsigned char *address0 = getAddress(Stream, start, FAT_ACCESS_WRITE); |
| unsigned char *address1 = getAddress(Stream, start+1, FAT_ACCESS_WRITE); |
| |
| if (num & 1) { |
| /* (odd) not on byte boundary */ |
| *address0 = (*address0 & 0x0f) | ((code << 4) & 0xf0); |
| *address1 = (code >> 4) & 0xff; |
| } else { |
| /* (even) on byte boundary */ |
| *address0 = code & 0xff; |
| *address1 = (*address1 & 0xf0) | ((code >> 8) & 0x0f); |
| } |
| } |
| |
| |
| /* |
| * Fat 16 encoding: |
| * | byte n | byte n+1 | |
| * |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0| |
| * | | | | | | | | | | | | | | | | | |
| * | FAT entry k | |
| */ |
| |
| static unsigned int fat16_decode(Fs_t *Stream, unsigned int num) |
| { |
| unsigned char *address = getAddress(Stream, num << 1, FAT_ACCESS_READ); |
| if(!address) |
| return 1; |
| return _WORD(address); |
| } |
| |
| static void fat16_encode(Fs_t *Stream, unsigned int num, unsigned int code) |
| { |
| unsigned char *address = getAddress(Stream, num << 1, FAT_ACCESS_WRITE); |
| set_word(address, code); |
| } |
| |
| |
| static unsigned int fast_fat16_decode(Fs_t *Stream, unsigned int num) |
| { |
| unsigned short *address = |
| (unsigned short *) getAddress(Stream, num << 1, |
| FAT_ACCESS_READ); |
| if(!address) |
| return 1; |
| return *address; |
| } |
| |
| static void fast_fat16_encode(Fs_t *Stream, unsigned int num, unsigned int code) |
| { |
| unsigned short *address = |
| (unsigned short *) getAddress(Stream, num << 1, |
| FAT_ACCESS_WRITE); |
| *address = code; |
| } |
| |
| |
| |
| |
| /* |
| * Fat 32 encoding |
| */ |
| #define FAT32_HIGH 0xf0000000 |
| #define FAT32_ADDR 0x0fffffff |
| |
| static unsigned int fat32_decode(Fs_t *Stream, unsigned int num) |
| { |
| unsigned char *address = getAddress(Stream, num << 2, FAT_ACCESS_READ); |
| if(!address) |
| return 1; |
| return _DWORD(address) & FAT32_ADDR; |
| } |
| |
| static void fat32_encode(Fs_t *Stream, unsigned int num, unsigned int code) |
| { |
| unsigned char *address = getAddress(Stream, num << 2, FAT_ACCESS_WRITE); |
| set_dword(address,(code&FAT32_ADDR) | (_DWORD(address)&FAT32_HIGH)); |
| } |
| |
| |
| static unsigned int fast_fat32_decode(Fs_t *Stream, unsigned int num) |
| { |
| unsigned int *address = |
| (unsigned int *) getAddress(Stream, num << 2, |
| FAT_ACCESS_READ); |
| if(!address) |
| return 1; |
| return (*address) & FAT32_ADDR; |
| } |
| |
| static void fast_fat32_encode(Fs_t *Stream, unsigned int num, unsigned int code) |
| { |
| unsigned int *address = |
| (unsigned int *) getAddress(Stream, num << 2, |
| FAT_ACCESS_WRITE); |
| *address = (*address & FAT32_HIGH) | (code & FAT32_ADDR); |
| } |
| |
| |
| /* |
| * Write the FAT table to the disk. Up to now the FAT manipulation has |
| * been done in memory. All errors are fatal. (Might not be too smart |
| * to wait till the end of the program to write the table. Oh well...) |
| */ |
| |
| void fat_write(Fs_t *This) |
| { |
| unsigned int i, j, dups, bit, slot; |
| int ret; |
| |
| /*fprintf(stderr, "Fat write\n");*/ |
| |
| if (!This->fat_dirty) |
| return; |
| |
| dups = This->num_fat; |
| if (This->fat_error) |
| dups = 1; |
| |
| |
| for(i=0; i<dups; i++){ |
| j = 0; |
| for(slot=0;j<This->fat_len;slot++) { |
| if(!This->FatMap[slot].dirty) { |
| j += SECT_PER_ENTRY; |
| continue; |
| } |
| for(bit=0; |
| bit < SECT_PER_ENTRY && j<This->fat_len; |
| bit++,j++) { |
| if(!(This->FatMap[slot].dirty & (ONE << bit))) |
| continue; |
| ret = fatWriteSector(This,j,slot, bit, i); |
| if (ret < (int) This->sector_size){ |
| if (ret < 0 ){ |
| perror("error in fat_write"); |
| exit(1); |
| } else { |
| fprintf(stderr, |
| "end of file in fat_write\n"); |
| exit(1); |
| } |
| } |
| /* if last dupe, zero it out */ |
| if(i==dups-1) |
| This->FatMap[slot].dirty &= ~(ONE<<bit); |
| } |
| } |
| } |
| /* write the info sector, if any */ |
| if(This->infoSectorLoc && This->infoSectorLoc != MAX32) { |
| /* initialize info sector */ |
| InfoSector_t *infoSector; |
| infoSector = (InfoSector_t *) safe_malloc(This->sector_size); |
| if(forceReadSector(This, (char *)infoSector, |
| This->infoSectorLoc, 1) != |
| (signed int) This->sector_size) { |
| fprintf(stderr,"Trouble reading the info sector\n"); |
| memset(infoSector->filler1, 0, sizeof(infoSector->filler1)); |
| memset(infoSector->filler2, 0, sizeof(infoSector->filler2)); |
| } |
| set_dword(infoSector->signature1, INFOSECT_SIGNATURE1); |
| set_dword(infoSector->signature2, INFOSECT_SIGNATURE2); |
| set_dword(infoSector->pos, This->last); |
| set_dword(infoSector->count, This->freeSpace); |
| set_word(infoSector->signature3, 0xaa55); |
| if(forceWriteSector(This, (char *)infoSector, This->infoSectorLoc, 1) != |
| (signed int) This->sector_size) |
| fprintf(stderr,"Trouble writing the info sector\n"); |
| free(infoSector); |
| } |
| This->fat_dirty = 0; |
| This->lastFatAccessMode = FAT_ACCESS_READ; |
| } |
| |
| |
| |
| /* |
| * Zero-Fat |
| * Used by mformat. |
| */ |
| int zero_fat(Fs_t *Stream, int media_descriptor) |
| { |
| unsigned int i, j; |
| unsigned int fat_start; |
| unsigned char *buf; |
| |
| buf = malloc(Stream->sector_size); |
| if(!buf) { |
| perror("alloc fat sector buffer"); |
| return -1; |
| } |
| for(i=0; i< Stream->num_fat; i++) { |
| fat_start = Stream->fat_start + i*Stream->fat_len; |
| for(j = 0; j < Stream->fat_len; j++) { |
| if(j <= 1) |
| memset(buf, 0, Stream->sector_size); |
| if(!j) { |
| buf[0] = media_descriptor; |
| buf[2] = buf[1] = 0xff; |
| if(Stream->fat_bits > 12) |
| buf[3] = 0xff; |
| if(Stream->fat_bits > 16) { |
| buf[4] = 0xff; |
| buf[5] = 0xff; |
| buf[6] = 0xff; |
| buf[7] = 0x0f; |
| } |
| } |
| |
| if(forceWriteSector(Stream, (char *)buf, |
| fat_start + j, 1) != |
| (signed int) Stream->sector_size) { |
| fprintf(stderr, |
| "Trouble initializing a FAT sector\n"); |
| free(buf); |
| return -1; |
| } |
| } |
| } |
| |
| free(buf); |
| Stream->FatMap = GetFatMap(Stream); |
| if (Stream->FatMap == NULL) { |
| perror("alloc fat map"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| |
| void set_fat12(Fs_t *This) |
| { |
| This->fat_bits = 12; |
| This->end_fat = 0xfff; |
| This->last_fat = 0xff6; |
| This->fat_decode = fat12_decode; |
| This->fat_encode = fat12_encode; |
| } |
| |
| static char word_endian_test[] = { 0x34, 0x12 }; |
| |
| void set_fat16(Fs_t *This) |
| { |
| This->fat_bits = 16; |
| This->end_fat = 0xffff; |
| This->last_fat = 0xfff6; |
| |
| if(sizeof(unsigned short) == 2 && |
| * (unsigned short *) word_endian_test == 0x1234) { |
| This->fat_decode = fast_fat16_decode; |
| This->fat_encode = fast_fat16_encode; |
| } else { |
| This->fat_decode = fat16_decode; |
| This->fat_encode = fat16_encode; |
| } |
| } |
| |
| static char dword_endian_test[] = { 0x78, 0x56, 0x34, 0x12 }; |
| |
| void set_fat32(Fs_t *This) |
| { |
| This->fat_bits = 32; |
| This->end_fat = 0xfffffff; |
| This->last_fat = 0xffffff6; |
| |
| if(sizeof(unsigned int) == 4 && |
| * (unsigned int *) dword_endian_test == 0x12345678) { |
| This->fat_decode = fast_fat32_decode; |
| This->fat_encode = fast_fat32_encode; |
| } else { |
| This->fat_decode = fat32_decode; |
| This->fat_encode = fat32_encode; |
| } |
| } |
| |
| |
| static int check_fat(Fs_t *This) |
| { |
| /* |
| * This is only a sanity check. For disks with really big FATs, |
| * there is no point in checking the whole FAT. |
| */ |
| |
| unsigned int i, f; |
| unsigned int tocheck; |
| if(mtools_skip_check) |
| return 0; |
| |
| /* too few sectors in the FAT */ |
| if(This->fat_len < NEEDED_FAT_SIZE(This)) |
| return -1; |
| /* we do not warn about too much sectors in FAT, which may |
| * happen when a partition has been shrunk using FIPS, or on |
| * other occurrences */ |
| |
| tocheck = This->num_clus; |
| if (tocheck + 1 >= This->last_fat) { |
| fprintf(stderr, "Too many clusters in FAT\n"); |
| return -1; |
| } |
| |
| if(tocheck > 4096) |
| tocheck = 4096; |
| |
| for ( i= 3 ; i < tocheck; i++){ |
| f = This->fat_decode(This,i); |
| if (f == 1 || (f < This->last_fat && f > This->num_clus)){ |
| fprintf(stderr, |
| "Cluster # at %d too big(%#x)\n", i,f); |
| fprintf(stderr,"Probably non MS-DOS disk\n"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| |
| /* |
| * Read the first sector of FAT table into memory. Crude error detection on |
| * wrong FAT encoding scheme. |
| */ |
| static int check_media_type(Fs_t *This, union bootsector *boot, |
| unsigned int tot_sectors) |
| { |
| unsigned char *address; |
| |
| This->num_clus = (tot_sectors - This->clus_start) / This->cluster_size; |
| |
| This->FatMap = GetFatMap(This); |
| if (This->FatMap == NULL) { |
| perror("alloc fat map"); |
| return -1; |
| } |
| |
| address = getAddress(This, 0, FAT_ACCESS_READ); |
| if(!address) { |
| fprintf(stderr, |
| "Could not read first FAT sector\n"); |
| return -1; |
| } |
| |
| if(mtools_skip_check) |
| return 0; |
| |
| if(!address[0] && !address[1] && !address[2]) |
| /* Some Atari disks have zeroes where Dos has media descriptor |
| * and 0xff. Do not consider this as an error */ |
| return 0; |
| |
| if((address[0] != boot->boot.descr && boot->boot.descr >= 0xf0 && |
| ((address[0] != 0xf9 && address[0] != 0xf7) |
| || boot->boot.descr != 0xf0)) || address[0] < 0xf0) { |
| fprintf(stderr, |
| "Bad media types %02x/%02x, probably non-MSDOS disk\n", |
| address[0], |
| boot->boot.descr); |
| return -1; |
| } |
| |
| if(address[1] != 0xff || address[2] != 0xff){ |
| fprintf(stderr,"Initial byte of fat is not 0xff\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int fat_32_read(Fs_t *This, union bootsector *boot, |
| unsigned int tot_sectors) |
| { |
| size_t size; |
| |
| This->fat_len = DWORD(ext.fat32.bigFat); |
| This->writeAllFats = !(boot->boot.ext.fat32.extFlags[0] & 0x80); |
| This->primaryFat = boot->boot.ext.fat32.extFlags[0] & 0xf; |
| This->rootCluster = DWORD(ext.fat32.rootCluster); |
| This->clus_start = This->fat_start + This->num_fat * This->fat_len; |
| |
| /* read the info sector */ |
| size = This->sector_size; |
| This->infoSectorLoc = WORD(ext.fat32.infoSector); |
| if(This->sector_size >= 512 && |
| This->infoSectorLoc && This->infoSectorLoc != MAX32) { |
| InfoSector_t *infoSector; |
| infoSector = (InfoSector_t *) safe_malloc(size); |
| if(forceReadSector(This, (char *)infoSector, |
| This->infoSectorLoc, 1) == |
| (signed int) This->sector_size && |
| _DWORD(infoSector->signature1) == INFOSECT_SIGNATURE1 && |
| _DWORD(infoSector->signature2) == INFOSECT_SIGNATURE2) { |
| This->freeSpace = _DWORD(infoSector->count); |
| This->last = _DWORD(infoSector->pos); |
| } |
| free(infoSector); |
| } |
| |
| set_fat32(This); |
| return(check_media_type(This, boot, tot_sectors) || |
| check_fat(This)); |
| } |
| |
| |
| static int old_fat_read(Fs_t *This, union bootsector *boot, |
| size_t tot_sectors, int nodups) |
| { |
| This->writeAllFats = 1; |
| This->primaryFat = 0; |
| This->dir_start = This->fat_start + This->num_fat * This->fat_len; |
| This->clus_start = This->dir_start + This->dir_len; |
| This->infoSectorLoc = MAX32; |
| |
| if(nodups) |
| This->num_fat = 1; |
| |
| if(check_media_type(This,boot, tot_sectors)) |
| return -1; |
| |
| if(This->num_clus >= FAT12) { |
| set_fat16(This); |
| /* third FAT byte must be 0xff */ |
| if(!mtools_skip_check && readByte(This, 3) != 0xff) |
| return -1; |
| } else |
| set_fat12(This); |
| |
| return check_fat(This); |
| } |
| |
| /* |
| * Read the first sector of the FAT table into memory and initialize |
| * structures. |
| */ |
| int fat_read(Fs_t *This, union bootsector *boot, |
| size_t tot_sectors, int nodups) |
| { |
| This->fat_error = 0; |
| This->fat_dirty = 0; |
| This->last = MAX32; |
| This->freeSpace = MAX32; |
| This->lastFatSectorNr = 0; |
| This->lastFatSectorData = 0; |
| |
| if(This->fat_len) |
| return old_fat_read(This, boot, tot_sectors, nodups); |
| else |
| return fat_32_read(This, boot, tot_sectors); |
| } |
| |
| |
| unsigned int fatDecode(Fs_t *This, unsigned int pos) |
| { |
| unsigned int ret; |
| |
| ret = This->fat_decode(This, pos); |
| if(ret && (ret < 2 || ret > This->num_clus+1) && ret < This->last_fat) { |
| fprintf(stderr, "Bad FAT entry %d at %d\n", ret, pos); |
| This->fat_error++; |
| } |
| return ret; |
| } |
| |
| /* append a new cluster */ |
| void fatAppend(Fs_t *This, unsigned int pos, unsigned int newpos) |
| { |
| This->fat_encode(This, pos, newpos); |
| This->fat_encode(This, newpos, This->end_fat); |
| if(This->freeSpace != MAX32) |
| This->freeSpace--; |
| } |
| |
| /* de-allocates the given cluster */ |
| void fatDeallocate(Fs_t *This, unsigned int pos) |
| { |
| This->fat_encode(This, pos, 0); |
| if(This->freeSpace != MAX32) |
| This->freeSpace++; |
| } |
| |
| /* allocate a new cluster */ |
| void fatAllocate(Fs_t *This, unsigned int pos, unsigned int value) |
| { |
| This->fat_encode(This, pos, value); |
| if(This->freeSpace != MAX32) |
| This->freeSpace--; |
| } |
| |
| void fatEncode(Fs_t *This, unsigned int pos, unsigned int value) |
| { |
| unsigned int oldvalue = This->fat_decode(This, pos); |
| This->fat_encode(This, pos, value); |
| if(This->freeSpace != MAX32) { |
| if(oldvalue) |
| This->freeSpace++; |
| if(value) |
| This->freeSpace--; |
| } |
| } |
| |
| unsigned int get_next_free_cluster(Fs_t *This, unsigned int last) |
| { |
| unsigned int i; |
| |
| if(This->last != MAX32) |
| last = This->last; |
| |
| if (last < 2 || |
| last >= This->num_clus+1) |
| last = 1; |
| |
| for (i=last+1; i< This->num_clus+2; i++) { |
| unsigned int r = fatDecode(This, i); |
| if(r == 1) |
| goto exit_0; |
| if (!r) { |
| This->last = i; |
| return i; |
| } |
| } |
| |
| for(i=2; i < last+1; i++) { |
| unsigned int r = fatDecode(This, i); |
| if(r == 1) |
| goto exit_0; |
| if (!r) { |
| This->last = i; |
| return i; |
| } |
| } |
| |
| |
| fprintf(stderr,"No free cluster %d %d\n", This->preallocatedClusters, |
| This->last); |
| return 1; |
| exit_0: |
| fprintf(stderr, "FAT error\n"); |
| return 1; |
| } |
| |
| int fat_error(Stream_t *Dir) |
| { |
| Stream_t *Stream = GetFs(Dir); |
| DeclareThis(Fs_t); |
| |
| if(This->fat_error) |
| fprintf(stderr,"Fat error detected\n"); |
| |
| return This->fat_error; |
| } |
| |
| int fat32RootCluster(Stream_t *Dir) |
| { |
| Stream_t *Stream = GetFs(Dir); |
| DeclareThis(Fs_t); |
| |
| if(This->fat_bits == 32) |
| return This->rootCluster; |
| else |
| return 0; |
| } |
| |
| |
| /* |
| * Get the amount of free space on the diskette |
| */ |
| |
| mt_size_t getfree(Stream_t *Dir) |
| { |
| Stream_t *Stream = GetFs(Dir); |
| DeclareThis(Fs_t); |
| |
| if(This->freeSpace == MAX32 || This->freeSpace == 0) { |
| register unsigned int i; |
| size_t total; |
| |
| total = 0L; |
| for (i = 2; i < This->num_clus + 2; i++) { |
| unsigned int r = fatDecode(This,i); |
| if(r == 1) { |
| return -1; |
| } |
| if (!r) |
| total++; |
| } |
| This->freeSpace = total; |
| } |
| return sectorsToBytes((Stream_t*)This, |
| This->freeSpace * This->cluster_size); |
| } |
| |
| |
| /* |
| * Ensure that there is a minimum of total sectors free |
| */ |
| int getfreeMinClusters(Stream_t *Dir, size_t size) |
| { |
| Stream_t *Stream = GetFs(Dir); |
| DeclareThis(Fs_t); |
| register unsigned int i, last; |
| size_t total; |
| |
| if(batchmode && This->freeSpace == MAX32) |
| getfree(Stream); |
| |
| if(This->freeSpace != MAX32) { |
| if(This->freeSpace >= size) |
| return 1; |
| else { |
| fprintf(stderr, "Disk full\n"); |
| got_signal = 1; |
| return 0; |
| } |
| } |
| |
| total = 0L; |
| |
| /* we start at the same place where we'll start later to actually |
| * allocate the sectors. That way, the same sectors of the FAT, which |
| * are already loaded during getfreeMin will be able to be reused |
| * during get_next_free_cluster */ |
| last = This->last; |
| |
| if ( last < 2 || last >= This->num_clus + 2) |
| last = 1; |
| for (i=last+1; i< This->num_clus+2; i++){ |
| unsigned int r = fatDecode(This, i); |
| if(r == 1) { |
| goto exit_0; |
| } |
| if (!r) |
| total++; |
| if(total >= size) |
| return 1; |
| } |
| for(i=2; i < last+1; i++){ |
| unsigned int r = fatDecode(This, i); |
| if(r == 1) { |
| goto exit_0; |
| } |
| if (!r) |
| total++; |
| if(total >= size) |
| return 1; |
| } |
| fprintf(stderr, "Disk full\n"); |
| got_signal = 1; |
| return 0; |
| exit_0: |
| fprintf(stderr, "FAT error\n"); |
| return 0; |
| } |
| |
| |
| int getfreeMinBytes(Stream_t *Dir, mt_size_t size) |
| { |
| Stream_t *Stream = GetFs(Dir); |
| DeclareThis(Fs_t); |
| size_t size2; |
| |
| size2 = size / (This->sector_size * This->cluster_size); |
| if(size % (This->sector_size * This->cluster_size)) |
| size2++; |
| return getfreeMinClusters(Dir, size2); |
| } |
| |
| |
| unsigned int getStart(Stream_t *Dir, struct directory *dir) |
| { |
| Stream_t *Stream = GetFs(Dir); |
| unsigned int first; |
| |
| first = START(dir); |
| if(fat32RootCluster(Stream)) |
| first |= STARTHI(dir) << 16; |
| return first; |
| } |
| |
| int fs_free(Stream_t *Stream) |
| { |
| DeclareThis(Fs_t); |
| |
| if(This->FatMap) { |
| int i, nr_entries; |
| nr_entries = (This->fat_len + SECT_PER_ENTRY - 1) / |
| SECT_PER_ENTRY; |
| for(i=0; i< nr_entries; i++) |
| if(This->FatMap[i].data) |
| free(This->FatMap[i].data); |
| free(This->FatMap); |
| } |
| if(This->cp) |
| cp_close(This->cp); |
| return 0; |
| } |