fsshuffle.c 2.72 KB
#include <PR/bcp.h>
#include "os.h"
#include "os_bbfs.h"
#include "os_bbcard.h"
#include "bbint.h"

#define SHUFFLE_SIZE	32

s32
osBbFShuffle(s32 sfd, s32 dfd, s32 release, void* buf, u32 len) {
    BbFat16* fat;
    s32 rv = BBFS_ERR_INVALID;
    BbInode* si, * di;
    int b, prev, n, i;
    u16 blocks[SHUFFLE_SIZE];

#ifdef _DEBUG
    if ((u32)buf&15) return rv;
#endif
    if (sfd < 0 || sfd >= BB_INODE16_ENTRIES || dfd < 0 || dfd >= BB_INODE16_ENTRIES) return rv;
    if ((rv = __osBbFsGetAccess()) < 0) return rv;
    fat = __osBbFat;
    si = fat->inode+sfd;
    di = fat->inode+dfd;
    rv = BBFS_ERR_INVALID;
    if (!si->type || !di->type) goto error;
    if (len == 0) goto error;
    if (len & (BB_FL_BLOCK_SIZE-1)) goto error;
    if (len > si->size) goto error;
    n = len / BB_FL_BLOCK_SIZE;
    if (len == 0) {
	rv = 0;
	goto error;
    }

    for(i = 0; i < n; i += SHUFFLE_SIZE) {
	int l = (n-i < SHUFFLE_SIZE) ? n-i : SHUFFLE_SIZE, j = 0;
	int stop;
	/* start allocation at end of destination file */
	for(prev = b = di->block; b != BB_FAT_LAST; b = BB_FAT16_NEXT(fat,b))
	    prev = b;
	if (prev != BB_FAT_LAST)
	    b = prev+1;
	else
	    b = BB_FL_BYTE_TO_BLOCK(BB_SYSTEM_AREA_SIZE);
        stop = b;
	while(j < l) {
	    while(BB_FAT16_NEXT(fat,b) != BB_FAT_AVAIL) {
		b++;
		if (b >= __osBbFsBlocks)
		    b = BB_FL_BYTE_TO_BLOCK(BB_SYSTEM_AREA_SIZE);
		/*XXXblythe handle no more blocks case*/
		if (b == stop) {
		    rv = BBFS_ERR_SPACE;
		    goto error;
		}
	    }
	    blocks[j++] = b++;
	}

	/* write blocks to end of dfd */
	if ((rv = osBbCardEraseBlocks(0, blocks, l)) < 0 ||
	    (rv = osBbCardWriteBlocks(0, blocks, l, buf, 0)) < 0) {
	    if (rv != BBFS_ERR_FAIL) goto error;
	    /* determine which blocks are bad, by repeating one at a time  */
	    for(j = 0; j < l; j++) {
		u16 b = blocks[j];
retry:
		if ((rv = osBbCardEraseBlock(0, b)) < 0 ||
		    (rv = osBbCardWriteBlock(0, b, buf+i*BB_FL_BLOCK_SIZE, 0)) < 0) {
		    if (rv != BBFS_ERR_FAIL ||
			(b = __osBbFReallocBlock(di, b, BB_FAT_BAD)) == BB_FAT_BAD) goto error;
		    goto retry;
		}
		blocks[j] = b;
	    }
	}

	/* append block list to end of dfd */
	j = 0;
	if (prev == BB_FAT_LAST) {
	    di->block = blocks[j];
	    prev = blocks[j++];
	}
	for(; j < l; j++) {
	    BB_FAT16_NEXT(fat, prev) = blocks[j];
	    prev = blocks[j];
	}
	BB_FAT16_NEXT(fat, prev) = BB_FAT_LAST;

	if (release) {
	    /* release blocks from beginning of sfd */
	    b = si->block;
	    for(j = 0; j < l; j++) {
		u16 o = BB_FAT16_NEXT(fat,b);
		BB_FAT16_NEXT(fat,b) = BB_FAT_AVAIL;
		b = o;
	    }
	    si->block = b;
	    si->size -= l*BB_FL_BLOCK_SIZE;
	}
	buf += BB_FL_BLOCK_SIZE*l;
	di->size += BB_FL_BLOCK_SIZE*l;
    }
    rv = __osBbFsSync(0);
error:
    __osBbFsRelAccess();
    return rv;
}