/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2002 by Linus Nielsen Feltzing
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "fat.h"
#define SECTOR_SIZE 512
#define MAX_BLOCK_SIZE 2048
#define BYTES2INT16(array,pos) \
(array[pos] | (array[pos+1] << 8 ))
#define BYTES2INT32(array,pos) \
((long)array[pos] | ((long)array[pos+1] << 8 ) | \
((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
/* BPB offsets; generic */
#define BS_JMPBOOT 0
#define BS_OEMNAME 3
#define BPB_BYTSPERSEC 11
#define BPB_SECPERCLUS 13
#define BPB_RSVDSECCNT 14
#define BPB_NUMFATS 16
#define BPB_ROOTENTCNT 17
#define BPB_TOTSEC16 19
#define BPB_MEDIA 21
#define BPB_FATSZ16 22
#define BPB_SECPERTRK 24
#define BPB_NUMHEADS 26
#define BPB_HIDDSEC 28
#define BPB_TOTSEC32 32
/* fat32 */
#define BPB_FATSZ32 36
#define BPB_EXTFLAGS 40
#define BPB_FSVER 42
#define BPB_ROOTCLUS 44
#define BPB_FSINFO 48
#define BPB_BKBOOTSEC 50
#define BS_32_DRVNUM 64
#define BS_32_BOOTSIG 66
#define BS_32_VOLID 67
#define BS_32_VOLLAB 71
#define BS_32_FILSYSTYPE 82
#define BPB_LAST_WORD 510
/* attributes */
#define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID)
#define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \
FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \
FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE )
/* NTRES flags */
#define FAT_NTRES_LC_NAME 0x08
#define FAT_NTRES_LC_EXT 0x10
#define FATDIR_NAME 0
#define FATDIR_ATTR 11
#define FATDIR_NTRES 12
#define FATDIR_CRTTIMETENTH 13
#define FATDIR_CRTTIME 14
#define FATDIR_CRTDATE 16
#define FATDIR_LSTACCDATE 18
#define FATDIR_FSTCLUSHI 20
#define FATDIR_WRTTIME 22
#define FATDIR_WRTDATE 24
#define FATDIR_FSTCLUSLO 26
#define FATDIR_FILESIZE 28
#define FATLONG_ORDER 0
#define FATLONG_TYPE 12
#define FATLONG_CHKSUM 13
#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4)
#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2)
#define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE)
#define DIR_ENTRY_SIZE 32
#define NAME_BYTES_PER_ENTRY 13
#define FAT_BAD_MARK 0x0ffffff7
#define FAT_EOF_MARK 0x0ffffff8
struct fsinfo {
unsigned long freecount; /* last known free cluster count */
unsigned long nextfree; /* first cluster to start looking for free
clusters, or 0xffffffff for no hint */
};
/* fsinfo offsets */
#define FSINFO_FREECOUNT 488
#define FSINFO_NEXTFREE 492
#define FAT_CACHE_SIZE 0x20
#define FAT_CACHE_MASK (FAT_CACHE_SIZE-1)
struct fat_cache_entry
{
long secnum;
bool inuse;
bool dirty;
};
static char fat_cache_sectors[FAT_CACHE_SIZE][SECTOR_SIZE];
static struct fat_cache_entry fat_cache[FAT_CACHE_SIZE];
struct bpb
{
int bpb_bytspersec; /* Bytes per sector, typically 512 */
unsigned int bpb_secperclus; /* Sectors per cluster */
int bpb_rsvdseccnt; /* Number of reserved sectors */
int bpb_numfats; /* Number of FAT structures, typically 2 */
int bpb_totsec16; /* Number of sectors on the volume (old 16-bit) */
int bpb_media; /* Media type (typically 0xf0 or 0xf8) */
int bpb_fatsz16; /* Number of used sectors per FAT structure */
unsigned long bpb_totsec32; /* Number of sectors on the volume
(new 32-bit) */
unsigned int last_word; /* 0xAA55 */
/**** FAT32 specific *****/
long bpb_fatsz32;
long bpb_rootclus;
long bpb_fsinfo;
/* variables for internal use */
unsigned long fatsize;
unsigned long totalsectors;
unsigned long rootdirsector;
unsigned long firstdatasector;
unsigned long startsector;
unsigned long dataclusters;
struct fsinfo fsinfo;
#ifdef HAVE_FAT16SUPPORT
int bpb_rootentcnt; /* Number of dir entries in the root */
/* internals for FAT16 support */
bool is_fat16; /* true if we mounted a FAT16 partition, false if FAT32 */
unsigned int rootdiroffset; /* sector offset of root dir relative to start
* of first pseudo cluster */
#endif /* #ifdef HAVE_FAT16SUPPORT */
#ifdef HAVE_MULTIVOLUME
int drive; /* on which physical device is this located */
bool mounted; /* flag if this volume is mounted */
#endif
};
static const unsigned char utf8comp[6] =
{
0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
};
static struct bpb fat_bpbs[NUM_VOLUMES]; /* mounted partition info */
struct mutex
{
bool locked;
};
static struct mutex cache_mutex;
/****************************************************************************
* Simple mutex functions
****************************************************************************/
void mutex_init(struct mutex *m)
{
m->locked = false;
}
void mutex_lock(struct mutex *m)
{
/* Wait until the lock is open... */
while(m->locked)
{
}
// sleep_thread();
// wake_up_thread();
/* ...and lock it */
m->locked = true;
}
void mutex_unlock(struct mutex *m)
{
m->locked = false;
}
static long cluster2sec( long cluster )
{
struct bpb* fat_bpb = &fat_bpbs[0];
long zerocluster;
// NATE: This is to deal with when fat_mount sets the root cluster to negative
if (cluster < 0)
zerocluster = 0;
else
zerocluster = 2;
if (cluster > (long)(fat_bpb->dataclusters + 1))
{
DEBUGF( "cluster2sec() - Bad cluster number (%ld)\n", cluster);
return -1;
}
return (cluster - zerocluster) * fat_bpb->bpb_secperclus
+ fat_bpb->firstdatasector;
}
void fat_size(IF_MV2(int volume,) unsigned long* size, unsigned long* free)
{
#ifndef HAVE_MULTIVOLUME
const int volume = 0;
#endif
struct bpb* fat_bpb = &fat_bpbs[volume];
if (size)
*size = fat_bpb->dataclusters * fat_bpb->bpb_secperclus / 2;
if (free)
*free = fat_bpb->fsinfo.freecount * fat_bpb->bpb_secperclus / 2;
}
void fat_init(void)
{
unsigned int i;
mutex_init(&cache_mutex);
/* mark the FAT cache as unused */
for(i = 0;i < FAT_CACHE_SIZE;i++)
{
fat_cache[i].secnum = 8; /* We use a "safe" sector just in case */
fat_cache[i].inuse = false;
fat_cache[i].dirty = false;
#ifdef HAVE_MULTIVOLUME
fat_cache[i].fat_vol = NULL;
#endif
}
#ifdef HAVE_MULTIVOLUME
/* mark the possible volumes as not mounted */
for (i=0; i<NUM_VOLUMES;i++)
{
fat_bpbs[i].mounted = false;
}
#endif
}
static void flush_fat_sector(struct