/* 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 . */ #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<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; ifat_len;slot++) { if(!This->FatMap[slot].dirty) { j += SECT_PER_ENTRY; continue; } for(bit=0; bit < SECT_PER_ENTRY && jfat_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<infoSectorLoc && This->infoSectorLoc != MAX32) { /* initialize info sector */ InfoSector_t *infoSector; infoSector = (InfoSector_t *) safe_malloc(This->sector_size); set_dword(infoSector->signature1, INFOSECT_SIGNATURE1); memset(infoSector->filler1, 0, sizeof(infoSector->filler1)); memset(infoSector->filler2, 0, sizeof(infoSector->filler2)); 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; }