Copyright Tristan Aubrey-Jones and Dale Caffull December 2006.
/* COMP2009 File System Assignment
* Tristan Aubrey-Jones (taj105) and Dale Caffull (dc1005)
* ----------------------------------------------------------
* This code implements the methods myfs_read_file, myfs_write_file and
* myfs_delete_file and requires myfs_write_sector and myfs_read_sector.
* It provides a single directoried file system for a 1.44MB floppy disk
* with 2880 sectors of 512B each. Max file size: 1458176B (1.39MB)
*
* Features:
*
* - The file system uses a block size of 4 sectors
* which limits disk fragmentation but means that the disk has a maximum capacity of 712 files.
*
* - The file system contains two FAT-16 tables with a adler-32 checksums so that if one becomes corrupted
* it is restored on the next file read or write.
*
* - The FAT is cached between subsequent file read, write and delete calls. On a call the first
* sector of the FAT is read and it's checksum is compared to the cached FAT's version.
* If the cache's checksum matches the disk's the cache is used, otherwise the rest of the FAT
* is read, and its checksum is computed and checked to ensure that the FAT is valid.
*
* - The directory information is stored in a 26 sector hashtable so that on average only 1 sector must be
* read to find a file's information.
*
* - Files are allocated optimally by finding all free space in the FAT, sorting by descending "gap" size and
* selecting the smallest gap that will fit the file. If the file is too big for one gap, the
* largest gap is used and the smallest gap is found that will fit the rest and so on.
* Before writing the file these block ranges are sorted by ascending LBA (using quick sort),
* so that reads and writes are sequential.
*/
#include "myfs.h"
#define MYFS_BLANK_DISK (-6)
#define MYFS_FAT_CORRUPT (-7)
#define SECTOR_SIZE 512
#define BLOCK_SIZE 4
#define HOUSEKEEPING_SIZE 8
#define DISK_SIZE 720
#define FAT_SIZE 1440
#define FILENAME_LENGTH 11
#define FILES_PER_SECTOR 30
#define MAX_FILES (DISK_SIZE - HOUSEKEEPING_SIZE)
#define FREE_SPACE 00
#define EOF 01
#define FALSE 0
#define TRUE !0
typedef char BYTE;
typedef unsigned short int LBA;
typedef unsigned char BOOL;
/* stores the information for a single transfer
* of a block either writing to disk from data, or
* reading from disk into data.
*/
struct BLOCK_TRANSFER
{
LBA lba; /* starting lba of transfer */
BYTE *data; /* pointer to array of at least length B */
int length; /* length of data to read and write in B */
};
/* global variables */
static struct BLOCK_TRANSFER block_list[DISK_SIZE]; /* stores a list of blocks in memory */
/* caches last read FAT */
static BOOL formatted = FALSE;
static BOOL fat_needsrestore = FALSE; /* fat needs to be restored from FAT-2 */
static BYTE fat_buffer[3 * SECTOR_SIZE]; /* stores a fat in memory */
/* caches last read sector from filetable */
static BYTE filetable_buffer[SECTOR_SIZE];
static LBA filetable_buffer_sector;
/* copies count bytes from src to dest */
static void *memcpy(void* dest, const void* src, unsigned int length)
{
/* local variables */
unsigned int c;
char *dst8, *src8;
double *dst64, *src64;
/* copy 1B at a time */
dst8 = (char *)dest;
src8 = (char *)src;
c = length % sizeof(double);
while (c--)
{
*dst8++ = *src8++;
}
/* copy 8B at a time */
dst64 = (double *)dst8;
src64 = (double *)src8;
c = length - (length % sizeof(double));
while (c)
{
*dst64++ = *src64++;
c -= sizeof(double);
}
return dest;
}
/* checks if two arrays are equal */
static int equals(const void *a, const void *b, int length)
{
/* local variables */
int c;
char *a8, *b8;
int *a32, *b32;
/* check byte by byte */
c = length % sizeof(int);
a8 = (char *)a;
b8 = (char *)b;
while (c--)
{
if (*a8++ != *b8++) return 0;
}
/* check 4B at a time */
c = length - (length % sizeof(int));
a32 = (int *)a8;
b32 = (int *)b8;
while (c)
{
if (*a32++ != *b32++) return 0;
c -= sizeof(int);
}
/* arrays are equal */
return 1;
}
/* computes an adler-32 checksum of a byte array */
static int checksum(BYTE *arr, unsigned int length)
{
int r;
unsigned int a = 1, b = 0;
int c = length;
while (c--)
{
a = (a + *arr) % 65521;
b = (b + a) % 65521;
arr++;
}
r = b;
r = r << 16;
return r|a;
}
/* generates a hashcode for any array of bytes */
static int hash(const BYTE *arr, int length)
{
int h = 0;
while (length--)
{
h = h + (*arr * (31 ^ length));
arr++;
}
return h;
}
/* write length bytes from data to disk starting at the sector sector 'sector'. */
static int write_data(LBA sector, const BYTE *data, int length)
{
/* variables */
int i;
const BYTE *data_ptr = data;
BYTE buffer[SECTOR_SIZE];
/* validate range */
if ((sector * SECTOR_SIZE) + length >
DISK_SIZE * BLOCK_SIZE * SECTOR_SIZE) return MYFS_FAT_CORRUPT;
/* write data */
while (length > 0)
{
/* if less than a sector of data left in buffer */
if (length < SECTOR_SIZE)
{
/* copy remaining data into buffer and zero rest */
memcpy(buffer, data_ptr, length);
for (i = length; i < SECTOR_SIZE; i++) { buffer[i] = 0; }
/* write last sector from this buffer */
if (myfs_write_sector(buffer, sector) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
length = 0;
}
else
{
/* write straight from main buffer */
if (myfs_write_sector(data_ptr, sector) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
data_ptr += SECTOR_SIZE;
length -= SECTOR_SIZE;
sector++;
}
}
return MYFS_SUCCESS;
}
/* reads length bytes starting at the sector into data */
static int read_data(LBA sector, BYTE *data, int length)
{
/* variables */
BYTE buffer[SECTOR_SIZE];
/* validate range */
if ((sector * SECTOR_SIZE) + length >
DISK_SIZE * BLOCK_SIZE * SECTOR_SIZE) return MYFS_FAT_CORRUPT;
/* write data */
while (length > 0)
{
/* if less than a sector of data left to read */
if (length < SECTOR_SIZE)
{
/* read remaining sector into buffer */
if (myfs_read_sector(buffer, sector) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
/* and copy length to buffer */
memcpy(data, buffer, length);
length = 0;
}
else
{
/* read straight to main buffer */
if (myfs_read_sector(data, sector) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
data += SECTOR_SIZE;
length -= SECTOR_SIZE;
sector++;
}
}
return MYFS_SUCCESS;
}
/* swaps the value at arr[a] with that at arr[b] */
static void sort_swap(struct BLOCK_TRANSFER arr[], int a, int b)
{
/* swap values */
struct BLOCK_TRANSFER v = arr[a];
arr[a] = arr[b];
arr[b] = v;
}
/* partitions array between arr[left] and arr[right], moving
* values lt the pivot to the left, and gt the pivot to the right.
*/
static int sort_partition(struct BLOCK_TRANSFER arr[], int left, int right, int pivotIndex,
int (*comp)(const struct BLOCK_TRANSFER, const struct BLOCK_TRANSFER))
{
/* locals */
int i; int storeIndex;
/* get pivot value */
struct BLOCK_TRANSFER pivotValue = arr[pivotIndex];
/* move pivot to end */
sort_swap(arr, pivotIndex, right);
/* swap values on either side of pivot */
storeIndex = left;
for(i = left; i < right; i++)
{
if (comp(arr[i], pivotValue))
{
/* swap */
sort_swap(arr, storeIndex, i);
storeIndex++;
}
}
/* move pivot to final place */
sort_swap(arr, right, storeIndex);
return storeIndex;
}
/* sorts the array between arr[left] and arr[right] into ascending order by lba */
static void sort_recursive(struct BLOCK_TRANSFER arr[], int left, int right,
int (*comp)(const struct BLOCK_TRANSFER, const struct BLOCK_TRANSFER))
{
/* perform quicksort on the two halves of the array */
if (right > left)
{
int pivotIndex = left + ((right - left) / 2);
int pivotNewIndex = sort_partition(arr, left, right, pivotIndex, comp);
sort_recursive(arr, left, pivotNewIndex - 1, comp);
sort_recursive(arr, pivotNewIndex+1, right, comp);
}
}
/* compares the LBA's of two BLOCK_TRANSFERs for sorting them in ascending order. */
static int compare_bylba_asc(const struct BLOCK_TRANSFER arrVal, const struct BLOCK_TRANSFER pivotVal)
{
return arrVal.lba <= pivotVal.lba;
}
/* compares the lengths of two BLOCK_TRANSFERs for sorting them in descending order */
static int compare_bylength_desc(const struct BLOCK_TRANSFER arrVal, const struct BLOCK_TRANSFER pivotVal)
{
return arrVal.length > pivotVal.length;
}
/* sorts an array of pending block transfers into ascending order by lba
* to ensure optimal reading and writing. */
static void sort_bylba(struct BLOCK_TRANSFER arr[], int length)
{
sort_recursive(arr, 0, length-1, compare_bylba_asc);
}
/* sorts an array of block transfers into descending order by length
* for use when finding optimal free blocks. */
static void sort_bylength(struct BLOCK_TRANSFER arr[], int length)
{
sort_recursive(arr, 0, length-1, compare_bylength_desc);
}
/* inits all flags to defaults */
static void init()
{
fat_needsrestore = FALSE;
formatted = FALSE;
filetable_buffer_sector = 0;
}
/* reads the fat at sector if not already cached. */
static int fat_read(LBA sector)
{
int *hashpointer;
BYTE buffer[SECTOR_SIZE * 3];
/* read the first sector of the house-keeping */
if (read_data(sector, buffer, SECTOR_SIZE) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
/* check if is blank */
if (buffer[0] == 0) return MYFS_BLANK_DISK;
/* check if checksum maches */
if (equals(buffer + 2, fat_buffer + 2, 4)) return MYFS_SUCCESS;
/* read in the rest of the fat */
if (read_data(sector + 1, buffer + SECTOR_SIZE, SECTOR_SIZE * 2) != MYFS_SUCCESS)
return MYFS_DISK_FAILURE;
/* check the checksum */
hashpointer = (int *)(buffer + 2);
if (*hashpointer == checksum(buffer + 6, FAT_SIZE))
{
/* if checksums match, return true */
memcpy(fat_buffer,buffer,SECTOR_SIZE * 3);
return MYFS_SUCCESS;
}
else return MYFS_FAT_CORRUPT;
}
/* loads the fat table from disk (or uses cache if can) */
static int fat_load()
{
int r = fat_read(0);
if (r == MYFS_FAT_CORRUPT)
{
fat_needsrestore = TRUE;
r = fat_read(3);
if (r == MYFS_FAT_CORRUPT)
{
/* both fat tables are corrupt */
return MYFS_FAT_CORRUPT;
}
}
return r;
}
/* saves the current fat table to disk (in sectors 0, and 3) */
static int fat_save()
{
if (write_data(0, fat_buffer, 3 * SECTOR_SIZE) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
if (write_data(3, fat_buffer, 3 * SECTOR_SIZE) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
return MYFS_SUCCESS;
}
/* computes the checksum for the hashtable and writes that back to the
* fat buffer */
static void fat_computechecksum()
{
int cs = checksum(fat_buffer + sizeof(int) + 2, FAT_SIZE);
memcpy(fat_buffer + sizeof(LBA), &cs, sizeof(int));
}
/* traverses linked list of blocks starting at sector and
* puts them into global array (block_list), and returns length
* or MYFS_FAT_CORRUPT if a sector points into the housekeeping section. */
static int fat_read_blocks(LBA sector, int file_size)
{
int i = 0;
while (sector != EOF)
{
/* check points into data area, and not housekeeping */
if (sector < HOUSEKEEPING_SIZE) return MYFS_FAT_CORRUPT;
block_list[i].lba = sector;
sector = *((LBA *)(fat_buffer + 6 + (sector * 2)));
if (sector == EOF) block_list[i].length = file_size;
else
{
block_list[i].length = SECTOR_SIZE * BLOCK_SIZE;
file_size -= SECTOR_SIZE * BLOCK_SIZE;
}
i++;
}
return i;
}
/* marks the linked list of blocks starting at sector as free */
static int fat_delete_blocks(LBA sector)
{
int i = 0;
LBA sector2;
while (sector != EOF)
{
/* check points into data area, and not housekeeping */
if (sector < HOUSEKEEPING_SIZE) return MYFS_FAT_CORRUPT;
sector2 = *((LBA *)(fat_buffer + 6 + (sector * 2)));
*(fat_buffer + 6 + (sector * 2)) = 0;
sector = sector2;
i++;
}
/* recompute checksum */
fat_computechecksum();
return MYFS_SUCCESS;
}
/* finds all the free blocks on the disk and adds them to the block_list array
* returns the length of block_list */
static int fat_findallfreeblocks()
{
int i, j = 0, start = 0;
LBA lba;
for (i = HOUSEKEEPING_SIZE; i < DISK_SIZE; i++)
{
lba = *(fat_buffer + 6 + (i * 2));
if (start == 0)
{
if (lba == 0)
{
start = i;
}
}
else
{
if (lba != 0)
{
/* add to list: start --> i - 1*/
block_list[j].lba = start;
block_list[j].length = (i - start) * BLOCK_SIZE * SECTOR_SIZE;
j++;
start = 0;
}
}
}
if (start != 0)
{
block_list[j].lba = start;
block_list[j].length = (i - start) * BLOCK_SIZE * SECTOR_SIZE;
j++;
}
return j;
}
/* look in block_list for the smallest block range
* that will fit a file of length bytes (searching from
* start into block_list). returns the index in block_list for the
* block that fits, or -1 if no free block is big enough */
static int fat_minthatfits(int length, int start, int list_length)
{
int current = start;
while(start < list_length && block_list[start].length > length)
{
current = start;
start++;
}
if (block_list[current].length >= length) return current;
else return -1;
}
/* finds a list of free block ranges that will fit length bytes
* puts it in block_list and returns the length of block_list
* or MYFS_DISK_FULL if there wernt enough blocks available. */
static int fat_findblocks(int length)
{
/* variables */
int list_length, i = 0, j = 0;
struct BLOCK_TRANSFER result[DISK_SIZE];
/* find all free block ranges on disk, and sort by length */
list_length = fat_findallfreeblocks();
if (list_length < 1) return MYFS_DISK_FULL;
sort_bylength(block_list, list_length);
/* loop while still need more blocks */
while (i < list_length)
{
/* trys to find 1 block range to fit whole file */
int c = fat_minthatfits(length, i, list_length);
if (c != -1)
{
/* add block range as final block */
result[j] = block_list[c];
/* assign correct length to range */
result[j].length = length;
j++;
/* sort by ascending lba */
sort_bylba(result, j);
/* copy into block_list */
for (i = 0; i < j; i++)
{
block_list[i] = result[i];
}
return j;
}
else
{
/* if current not enough, add to list and look for more */
result[j] = block_list[i];
length = length - block_list[i].length;
i++; j++;
}
}
return MYFS_DISK_FULL;
}
/* writes the file block chain for a file to the FAT */
static void fat_writeblocks(int length)
{
LBA lba;
int i, count;
/* for each block range */
for (i = 0; i < length; i++)
{
/* for each block in block range */
lba = block_list[i].lba;
count = block_list[i].length;
while (count > BLOCK_SIZE * SECTOR_SIZE)
{
/* link to next block */
*((LBA *)(fat_buffer + 6 + (lba * 2))) = lba + 1;
count -= BLOCK_SIZE * SECTOR_SIZE;
lba++;
}
/* link to next block range in list */
if (i == length - 1) *((LBA *)(fat_buffer + 6 + (lba * 2))) = EOF;
else *((LBA *)(fat_buffer + 6 + (lba * 2))) = block_list[i+1].lba;
}
fat_computechecksum();
}
/* get the file details of the file at i in the directory table */
static int filetable_getentry(int i, LBA *start, int *length, char name[FILENAME_LENGTH])
{
/* variables */
int j;
LBA sector = (i / FILES_PER_SECTOR) + 6;
/* check LBAs */
if (i < 0 || i > MAX_FILES) return MYFS_FAT_CORRUPT;
/* read if sector is not in buffer already */
if (filetable_buffer_sector != sector)
{
/* read from disk */
if (read_data(sector, filetable_buffer, SECTOR_SIZE) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
filetable_buffer_sector = sector;
}
/* extract file information from sector and return it */
j = (i % FILES_PER_SECTOR) * 17;
memcpy(start, filetable_buffer + j, sizeof(LBA));
memcpy(length, filetable_buffer + j + sizeof(LBA), sizeof(int));
memcpy(name, filetable_buffer + j + sizeof(LBA) + sizeof(int), FILENAME_LENGTH);
return MYFS_SUCCESS;
}
/* set the file details of the file at i in the directory table */
static int filetable_setentry(int i, LBA start, int length, const char name[FILENAME_LENGTH])
{
/* variables */
int j;
LBA sector = (i / FILES_PER_SECTOR) + 6;
/* check LBAs */
if (i < 0 || i > MAX_FILES) return MYFS_FAT_CORRUPT;
/* read if sector is not in buffer already */
if (filetable_buffer_sector != sector)
{
/* read from disk */
if (read_data(sector, filetable_buffer, SECTOR_SIZE) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
filetable_buffer_sector = sector;
}
/* change entry */
j = (i % FILES_PER_SECTOR) * 17;
memcpy(filetable_buffer + j, &start, sizeof(LBA));
memcpy(filetable_buffer + j + sizeof(LBA), &length, sizeof(int));
memcpy(filetable_buffer + j + sizeof(LBA) + sizeof(int), name, FILENAME_LENGTH);
/* write back to disk */
if (write_data(sector, filetable_buffer, SECTOR_SIZE) != MYFS_SUCCESS)
return MYFS_DISK_FAILURE;
return MYFS_SUCCESS;
}
/* find the filetable entry with the name searchname, and return its
* filetable index, putting its start lba and length into the params */
static int filetable_findentry(const char searchname[FILENAME_LENGTH], LBA *start, int *length)
{
/* our initial index */
int r, i;
char name[FILENAME_LENGTH];
int begin = hash(searchname, FILENAME_LENGTH) % MAX_FILES;
r = filetable_getentry(begin, start, length, name);
if (r != MYFS_SUCCESS) return r;
/* loop while wrong file */
i = begin;
while(*start != 0 && !equals(searchname, name, FILENAME_LENGTH))
{
i++;
if (i >= MAX_FILES) i = 0;
if (i == begin) return MYFS_DISK_FULL;
r = filetable_getentry(i, start, length, name);
if (r != MYFS_SUCCESS) return r;
}
/* why loop terminate? */
if (*start == 0) return MYFS_FILE_NOT_FOUND;
else return i;
}
/* gets the file details for the file with the given name */
static int filetable_get_file(const char name[FILENAME_LENGTH], LBA *start, int *length)
{
int r;
r = filetable_findentry(name, start, length);
if (r >= 0) return MYFS_SUCCESS;
else if (r == MYFS_DISK_FULL) return MYFS_FILE_NOT_FOUND;
else return r;
}
/* deletes the filetable entry for the file with the given name */
static int filetable_delete_file(const char name[FILENAME_LENGTH])
{
/* search for entry */
LBA start;
int length;
char zeros[] = {0,0,0,0,0,0,0,0,0,0,0};
int r = filetable_findentry(name, &start, &length);
if (r >= MYFS_SUCCESS)
{
/* overwrite file entry */
return filetable_setentry(r, 1, 0, zeros);
}
else return r;
}
/* adds the file entry for the given details to the filetable */
static int filetable_add_file(const char name[FILENAME_LENGTH], LBA start, int length)
{
/* find a spot for the entry */
LBA start_buffer;
char name_buffer[FILENAME_LENGTH];
int length_buffer, i, r, begin = hash(name, FILENAME_LENGTH) % MAX_FILES;
r = filetable_getentry(begin, &start_buffer, &length_buffer, name_buffer);
if (r != MYFS_SUCCESS) return r;
/* loop until found blank entry */
i = begin;
while (start_buffer > 1)
{
/* check if file already exists */
if (equals(name_buffer, name, FILENAME_LENGTH))
return MYFS_FILE_EXISTS;
/* progress linearly to next file entry */
i++;
if (i >= MAX_FILES) i = 0;
if (i == begin) return MYFS_DISK_FULL;
r = filetable_getentry(i, &start_buffer, &length_buffer, name_buffer);
if (r != MYFS_SUCCESS) return r;
}
/* add the entry */
return filetable_setentry(i, start, length, name);
}
/* formats a blank disk */
static int format_disk()
{
/* vars */
int i, r;
BYTE buffer[SECTOR_SIZE];
/* make and save blank fat */
for (i = 0; i < 3 * SECTOR_SIZE; i++) { fat_buffer[i] = 0; }
fat_buffer[0] = 1;
fat_computechecksum();
/* (fat saved at end of a read or write) */
/*r = fat_save();
if (r != MYFS_SUCCESS) return r;*/
/* blank directory tables */
for (i = 0; i < SECTOR_SIZE; i++) { buffer[i] = 0; }
for (i = 6; i < HOUSEKEEPING_SIZE * BLOCK_SIZE; i++)
{
r = write_data(i, buffer, SECTOR_SIZE);
if (r != MYFS_SUCCESS) return r;
}
formatted = TRUE;
return MYFS_SUCCESS;
}
/* writes the array of blocks as defined in block_list */
static int write_blocklist(int length, const BYTE *data)
{
int i, r, written = 0;
for (i = 0; i < length; i++)
{
r = write_data(block_list[i].lba * BLOCK_SIZE, data + written, block_list[i].length);
if (r != MYFS_SUCCESS) return r;
written += block_list[i].length;
}
return MYFS_SUCCESS;
}
/* reads the array of blocks as defined in block_list */
static int read_blocklist(int length)
{
int i, r;
for (i = 0; i < length; i++)
{
r = read_data(block_list[i].lba * BLOCK_SIZE, block_list[i].data, block_list[i].length);
if (r != MYFS_SUCCESS) return r;
}
return MYFS_SUCCESS;
}
int myfs_read_file(const char filename[11], char buffer[], int buffer_size, int *file_size)
{
/* vars */
LBA lba;
int i, r, l, length, size;
/* initialization */
init();
r = fat_load();
if (r == MYFS_BLANK_DISK) return MYFS_FILE_NOT_FOUND;
else if (r == MYFS_FAT_CORRUPT) return MYFS_DISK_FAILURE;
/* get file from dir table */
r = filetable_get_file(filename, &lba, &size);
if (r == MYFS_FAT_CORRUPT) return MYFS_DISK_FAILURE;
else if (r != MYFS_SUCCESS) return r;
/* if file exists, return its actual size */
*file_size = size;
/* check for 0 length */
if (size > 0)
{
/* check buffer length */
if (buffer_size < size) return MYFS_BUFFER_TOO_SMALL;
/* read block list */
l = fat_read_blocks(lba, size);
if (l < 0) return MYFS_DISK_FAILURE;
/* prepare transaction list */
length = 0;
for (i = 0; i < l; i++)
{
block_list[i].data = buffer + length;
length += block_list[i].length;
}
/* read block data */
if (read_blocklist(l) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
}
/* file read */
return MYFS_SUCCESS;
}
int myfs_write_file(const char filename[11], const char buffer[], int file_size)
{
/* local variables */
LBA lba_buffer;
int r, l, length_buffer;
/* initialization */
init();
/* check file size bounds */
if (file_size > MAX_FILES * BLOCK_SIZE * SECTOR_SIZE)
return MYFS_DISK_FULL;
if (file_size < 0)
return MYFS_DISK_FAILURE;
/* load fat table */
r = fat_load();
if (r == MYFS_BLANK_DISK)
{
if (format_disk() != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
formatted = TRUE;
}
else if (r == MYFS_FAT_CORRUPT)
{
format_disk();
fat_save();
return MYFS_DISK_FAILURE;
}
else if (r != MYFS_SUCCESS)
{
return MYFS_DISK_FAILURE;
}
/* check if file already exists */
r = filetable_get_file(filename, &lba_buffer, &length_buffer);
if (r == MYFS_SUCCESS) return MYFS_FILE_EXISTS;
else if (r == MYFS_FAT_CORRUPT)
{
format_disk();
fat_save();
return MYFS_DISK_FAILURE;
}
else if (r != MYFS_FILE_NOT_FOUND) return r;
/* check for 0 length file */
if (file_size > 0)
{
/* find free space */
l = fat_findblocks(file_size);
if (l == MYFS_DISK_FULL) return l;
/* write blocks */
if (write_blocklist(l, buffer) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
lba_buffer = block_list[0].lba;
/* update fat table cache */
fat_writeblocks(l);
}
else lba_buffer = 8;
/* save fat table to disk if need to */
if (file_size > 0 || formatted)
{
if (fat_save() != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
}
/* add entry to file table */
r = filetable_add_file(filename, lba_buffer, file_size);
if (r == MYFS_DISK_FULL) return r;
else if (r != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
/* file written! */
return MYFS_SUCCESS;
}
int myfs_delete_file(const char filename[11])
{
/* local variables */
LBA lba_buffer;
int r, length_buffer;
/* initialization */
init();
r = fat_load();
if (r == MYFS_BLANK_DISK)
{
if (format_disk() != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
if (fat_save() != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
return MYFS_FILE_NOT_FOUND;
}
else if (r == MYFS_FAT_CORRUPT)
{
format_disk();
fat_save();
return MYFS_DISK_FAILURE;
}
else if (r != MYFS_SUCCESS)
{
return MYFS_DISK_FAILURE;
}
/* check if file already exists */
r = filetable_get_file(filename, &lba_buffer, &length_buffer);
if (r == MYFS_FAT_CORRUPT)
{
if (format_disk() != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
}
else if (r != MYFS_SUCCESS) return r;
/* check if is 0 length */
if (length_buffer > 0)
{
/* delete from fat */
if (fat_delete_blocks(lba_buffer) != MYFS_SUCCESS)
{
format_disk();
return MYFS_DISK_FAILURE;
}
/* save fat */
if (fat_save() != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
}
/* remove filetable entry */
if (filetable_delete_file(filename) != MYFS_SUCCESS) return MYFS_DISK_FAILURE;
/* return if succeeded */
return MYFS_SUCCESS;
}