fsinit.c 5.32 KB
#include <PR/bcp.h>
#include "os.h"
#include "os_bbcard.h"
#include "os_bbfs.h"

BbFat16* __osBbFat;
u16 __osBbFatBlock;
u16 __osBbFsBlocks;
u8  __osBbFsAutoSync = 1;
static u8 __osBbFsAccessQueueInit;

#define NUM_ACCESS_MESG		1
/* Access queue used for mutual exclusion on file system */
static OSMesg	accessBuf[NUM_ACCESS_MESG];
static OSMesgQueue	__osBbFsAccessQueue;

static void
createAccessQueue(void) {
    __osBbFsAccessQueueInit = 1;
    osCreateMesgQueue(&__osBbFsAccessQueue, accessBuf, NUM_ACCESS_MESG);
    (void)osSendMesg(&__osBbFsAccessQueue, (OSMesg)0, OS_MESG_NOBLOCK);
}


s32
__osBbFsGetAccess(void) {
    s32 rv;
    if ((rv = osBbCardUnhappy())) return rv;
    else if (!__osBbFat) return BBFS_ERR_UNINIT;
    (void)osRecvMesg(&__osBbFsAccessQueue, (OSMesg*)0, OS_MESG_BLOCK);
    return 0;
}


void
__osBbFsRelAccess(void) {
    (void)osSendMesg(&__osBbFsAccessQueue, (OSMesg)0, OS_MESG_NOBLOCK);
}

static u16
csum(const void *p) {
    const u16* x = p;
    u16 sum = 0, i;
    for(i = 0; i < BB_FL_BLOCK_SIZE/sizeof(u16); i++)
	sum += x[i];
    return sum;
}

void
__osBbFCheck(void) {
    BbFat16* fat = __osBbFat;
    BbInode* in;
    u16 i, j;
    static u32 map[8192/32];	/*XXXblythe move to superblock structure*/
    u16 b, used;
    u32 size;

restart:
    used = 0;
    bzero(map, sizeof map);
    for(i = 0; i < BB_INODE16_ENTRIES; i++) {
	in = fat->inode+i;
	if (!in->type) continue;
	size = 0;
	/* check blocks */
	for(b = in->block; b != BB_FAT_LAST; b = BB_FAT16_NEXT(fat,b)) {
	    if (b <  BB_FL_BYTE_TO_BLOCK(BB_SYSTEM_AREA_SIZE)) goto delete;
	    if (b >= __osBbFsBlocks-BB_FAT16_BLOCKS) goto delete;
	    if (map[b>>5] & (1<<(b&31))) goto delete;
	    map[b>>5] |= 1<<(b&31);
	    size += BB_FL_BLOCK_SIZE;
	}
	if (size != in->size) goto delete;
	if (!in->name[0]) goto delete;
	for(j = 1; j < BB_INODE16_NAMELEN; j++)
	    if (in->name[j] && (in->name[j] < ' ' || in->name[j] > '~')) goto delete;
	continue;
delete:
#ifdef _DEBUG
osSyncPrintf("deleting bad file inode %d %d %.11s %d %d %d\n", in-fat->inode, b, in->name, in->size, size, in->block);
#endif
	bzero(in->name, BB_INODE16_NAMELEN);
	in->size = 0;
	in->type = 0;
	in->block = 0;
	goto restart;
    }
    /* reclaim unaccounted for blocks */
    for(b = BB_FL_BYTE_TO_BLOCK(BB_SYSTEM_AREA_SIZE);
	    b < __osBbFsBlocks-BB_FAT16_BLOCKS; b++) {
	if (map[b>>5] & (1<<(b&31))) continue;
#ifdef _DEBUG
if(BB_FAT16_NEXT(fat,b) != BB_FAT_BAD && BB_FAT16_NEXT(fat,b) != BB_FAT_AVAIL)
osSyncPrintf("reclaim block %d\n", b);
#endif
	if (BB_FAT16_NEXT(fat,b) != BB_FAT_BAD)
	    BB_FAT16_NEXT(fat,b) = BB_FAT_AVAIL;
    }
    for(i = 0; i < BB_FAT16_BLOCKS; i++) {
	if (BB_FAT16_NEXT(fat, __osBbFsBlocks-1-i) != BB_FAT_BAD)
	    BB_FAT16_NEXT(fat, __osBbFsBlocks-1-i) = BB_FAT_RESERVED;
    }
    for(i = 0; i < BB_FL_BYTE_TO_BLOCK(BB_SYSTEM_AREA_SIZE); i++) {
	if (BB_FAT16_NEXT(fat, i) != BB_FAT_BAD)
	    BB_FAT16_NEXT(fat, i) = BB_FAT_RESERVED;
    }
}

static s32
__osBbFsReadFat(u16 block, BbFat16* fat, const u8* magic) {
    s32 rv;
    if ((rv = osBbCardReadBlock(0, block, fat, NULL)) == 0) {
	/* check veracity, compare checksum */
	if (csum(fat) != BB_FAT16_CKSUM) return 1;
	if (bcmp(fat->magic, magic, sizeof fat->magic) != 0) return 1;
    }
    return rv;
}

static s32
__osBbFsReadFatLinks(BbFat16* fat) {
    s32 k, rv;
    /* check whether linked FATs are valid */
    for(k = 1; k < __osBbFsBlocks/BB_FAT16_ENTRIES; k++) {
	if (!fat[k-1].link) return 1;
	if ((rv = __osBbFsReadFat(fat[k-1].link, fat+k, BB_FAT16_LINK_MAGIC)) != 0) return rv;
	if (fat[k-1].seq != fat[k].seq) return 1;
    }
    return 0;
}

s32
osBbFInit(OSBbFs* fs) {
    u32 i, start;
    s32 best = -1, seq = 0, rv;
    BbFat16* fat = (BbFat16*)fs->root;

    if (!__osBbFsAccessQueueInit)
	createAccessQueue();
    (void)osRecvMesg(&__osBbFsAccessQueue, (OSMesg*)0, OS_MESG_BLOCK);
    osBbCardInit();
    __osBbFat = 0;
    if ((rv = osBbCardUnhappy())) goto error;
    start = (__osBbFsBlocks = osBbCardBlocks(0))-1;
    /* try to find the superblock */
restart:
    for(i = 0; i < BB_FAT16_BLOCKS; i++) {
	if ((rv = __osBbFsReadFat(start-i, fat, BB_FAT16_MAGIC)) == 0) {
	    if ((rv = __osBbFsReadFatLinks(fat)) == 0 && fat->seq >= seq) {
		best = i;
		seq = fat->seq;
	    }
	}
	if (rv < 0 && rv != BBCARD_ERR_FAIL)
	    goto error;
    }
    if (best == -1) {
	rv = BBFS_ERR_FAIL;
	goto error;
    }
    /* re-read best */
    if (osBbCardReadBlock(0, start-best, fat, NULL) ||
	csum(fat) != BB_FAT16_CKSUM ||
	seq != fat->seq ||
	__osBbFsReadFatLinks(fat)) goto restart;
    __osBbFat = fat;
    __osBbFatBlock = best;
    rv = 0;
    /* check consistency */
    __osBbFsAutoSync = 1;
    __osBbFCheck();
error:
    __osBbFsRelAccess();
    return rv;
}

s32
osBbFStatFs(OSBbStatFs* statfs) {
    u16 b, i, j;
    s32 rv;
    BbFat16* fat;
    if ((rv = __osBbFsGetAccess()) < 0) return rv;
    fat = __osBbFat;
    j = 0;
    for(i = 0; i < BB_INODE16_ENTRIES; i++)
	if (fat->inode[i].type) j++;
    statfs->files = j;
    statfs->freeFiles = BB_INODE16_ENTRIES-j;
    i = j = 0;
    for(b = BB_FL_BYTE_TO_BLOCK(BB_SYSTEM_AREA_SIZE);
	    b < __osBbFsBlocks-BB_FAT16_BLOCKS; b++)
	if (BB_FAT16_NEXT(fat,b) == BB_FAT_AVAIL)
	    j++;
        else if (BB_FAT16_NEXT(fat,b) != BB_FAT_BAD && BB_FAT16_NEXT(fat,b) != BB_FAT_RESERVED)
	    i++;
        
    statfs->blocks = i;
    statfs->freeBlocks = j;
    rv = 0;
    __osBbFsRelAccess();
    return rv;
}