605 lines
14 KiB
C
605 lines
14 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "filesystem.h"
|
|
#include "descriptor.c"
|
|
|
|
extern Descriptor descriptors[];
|
|
int descriptor_size = 0;
|
|
|
|
/* block size vars */
|
|
const int SUPERBLOCK_BLOCK_SIZE
|
|
= (sizeof(Superblock) + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
|
const int FAT_BLOCK_SIZE
|
|
= (sizeof(FAT) + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
|
const int DIRECTORY_BLOCK_SIZE
|
|
= (sizeof(Directory) + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
|
|
|
/* disk structures ---------------------------------------------------------- */
|
|
static char * disk;
|
|
|
|
static Superblock * super;
|
|
static FAT * fat;
|
|
static Directory * dir;
|
|
static char * data;
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
static int virt_disk_active = 0;
|
|
|
|
int make_fs(char * disk_name)
|
|
{
|
|
if (make_disk(disk_name) < 0)
|
|
return -1;
|
|
if (open_disk(disk_name) < 0)
|
|
return -1;
|
|
|
|
if (virt_disk_active != 1)
|
|
init_virt_disk();
|
|
|
|
// reserve 0 to data_block_offset in FAT
|
|
for (int i = 0; i < super->data_block_offset; i++)
|
|
fat->table[i] = FAT_RESERVED;
|
|
|
|
if (write_blocks(disk, 0, DISK_BLOCKS) < 0)
|
|
return -1;
|
|
|
|
if (close_disk(disk_name) < 0)
|
|
exit(1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mount_fs(char * disk_name)
|
|
{
|
|
if (open_disk(disk_name) < 0)
|
|
return -1;
|
|
|
|
if (virt_disk_active != 1)
|
|
init_virt_disk();
|
|
|
|
if (read_blocks(disk, 0, DISK_BLOCKS) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int umount_fs(char * disk_name)
|
|
{
|
|
if (write_blocks(disk, 0, DISK_BLOCKS) < 0)
|
|
return -1;
|
|
|
|
if (close_disk(disk_name) < 0)
|
|
return -1;
|
|
|
|
free(disk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fs_open(char * name)
|
|
{
|
|
int idx = 0;
|
|
Descriptor * desc;
|
|
Attribute * attr;
|
|
|
|
if (descriptor_size == MAX_OPEN_FILES)
|
|
{
|
|
printf("fs_open: can't open more files\n");
|
|
return -1;
|
|
}
|
|
|
|
while (idx < dir->size && strcmp(name, dir->attributes[idx].name) != 0)
|
|
{
|
|
idx++;
|
|
}
|
|
|
|
if (idx == dir->size)
|
|
{
|
|
printf("fs_open: file not found: %s\n", name);
|
|
return -1;
|
|
}
|
|
attr = &dir->attributes[idx];
|
|
|
|
// find an empty descriptor spot
|
|
idx = 0;
|
|
while (idx < descriptor_size
|
|
&& descriptors[idx].descriptor != DESCRIPTOR_UNUSED)
|
|
{
|
|
idx++;
|
|
}
|
|
desc = &descriptors[idx];
|
|
|
|
// finally, create a descriptor
|
|
desc->descriptor = idx;
|
|
desc->ptr = disk + attr->offset * BLOCK_SIZE;
|
|
desc->attr = attr;
|
|
descriptor_size++;
|
|
|
|
return desc->descriptor;
|
|
}
|
|
|
|
int fs_close(int fildes)
|
|
{
|
|
int idx = get_fildes_index(fildes);
|
|
if (idx < 0)
|
|
{
|
|
printf("fs_close: file with descriptor %d doesn't exist\n", fildes);
|
|
return -1;
|
|
}
|
|
descriptor_size--;
|
|
|
|
if (descriptor_size == 0)
|
|
return 0;
|
|
|
|
descriptors[idx] = descriptors[descriptor_size];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fs_create(char * name)
|
|
{
|
|
int found = 0;
|
|
int dir_idx = 0;
|
|
int name_idx = 0;
|
|
int fat_idx = super->fat_offset;
|
|
Attribute * attrib;
|
|
|
|
if (dir->size == MAX_FILES)
|
|
{
|
|
printf("Max file count reached (64)\n");
|
|
return -1;
|
|
}
|
|
|
|
while (name[name_idx] != '\0')
|
|
name_idx++;
|
|
|
|
if (name_idx >= MAX_FILENAME)
|
|
{
|
|
printf("Filename can't be longer than 15 characters.\n");
|
|
return -1;
|
|
}
|
|
|
|
while (!found && dir_idx < dir->size)
|
|
{
|
|
if (strcmp(dir->attributes[dir_idx].name, name) == 0)
|
|
found = 1;
|
|
else
|
|
dir_idx++;
|
|
}
|
|
if (found)
|
|
{
|
|
printf("File already exists\n");
|
|
return -1;
|
|
}
|
|
|
|
// find an empty FAT entry
|
|
fat_idx = find_avail_alloc_entry();
|
|
if (fat_idx < 0)
|
|
{
|
|
printf("Not enough space\n");
|
|
return -1;
|
|
}
|
|
|
|
// finally, create a file attrib entry
|
|
attrib = &dir->attributes[dir->size];
|
|
memcpy(attrib->name, name, MAX_FILENAME);
|
|
attrib->size = 0;
|
|
attrib->offset = fat_idx;
|
|
|
|
fat->table[fat_idx] = FAT_EOF;
|
|
|
|
dir->size++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fs_delete(char * name)
|
|
{
|
|
int idx = 0; /* index of file with matching name */
|
|
|
|
/* find file within descriptors (is it open?) */
|
|
while (idx < descriptor_size
|
|
&& strcmp(name, descriptors[idx].attr->name) != 0)
|
|
{
|
|
idx++;
|
|
}
|
|
|
|
if (idx < descriptor_size)
|
|
{
|
|
printf("fs_delete: can't delete, file is open: %s\n", name);
|
|
return -1;
|
|
}
|
|
|
|
/* find file within dir structure */
|
|
idx = 0;
|
|
while (idx < dir->size && strcmp(name, dir->attributes[idx].name) != 0)
|
|
{
|
|
idx++;
|
|
}
|
|
|
|
if (idx == dir->size)
|
|
{
|
|
printf("fs_delete: file not found: %s\n", name);
|
|
return -1;
|
|
}
|
|
|
|
// finally "delete" the file
|
|
dir->size--;
|
|
free_alloc_chain(dir->attributes[idx].offset);
|
|
if (dir->size == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
dir->attributes[idx] = dir->attributes[dir->size];
|
|
return 0;
|
|
}
|
|
|
|
int fs_read(int fildes, void * buf, size_t nbyte)
|
|
{
|
|
size_t block_offset, /* current block's offset */
|
|
bytes_to_read /* bytes readable from the block, up to nbytes */,
|
|
bytes_r = 0; /* bytes read from recursive calls, if any */
|
|
int fat_idx,
|
|
idx = get_fildes_index(fildes);
|
|
char * destination = (char *) buf;
|
|
|
|
if (idx < 0)
|
|
return -1;
|
|
|
|
block_offset = (long unsigned)(descriptors[idx].ptr - disk) / BLOCK_SIZE;
|
|
bytes_to_read = (long unsigned)disk + (block_offset + 1) * BLOCK_SIZE
|
|
- (long unsigned)(descriptors[idx].ptr);
|
|
|
|
// only read up to filesize
|
|
if (nbyte > (size_t)descriptors[idx].attr->size)
|
|
nbyte = (size_t)descriptors[idx].attr->size;
|
|
|
|
// only read up to nbytes
|
|
if (bytes_to_read > nbyte)
|
|
bytes_to_read = nbyte;
|
|
|
|
nbyte -= bytes_to_read;
|
|
|
|
memcpy(destination, descriptors[idx].ptr, bytes_to_read);
|
|
descriptors[idx].ptr += bytes_to_read;
|
|
destination += bytes_to_read;
|
|
|
|
if (nbyte == 0)
|
|
return bytes_to_read;
|
|
|
|
// the entire block has been read: find next block
|
|
if (descriptors[idx].ptr == disk + (block_offset + 1) * BLOCK_SIZE)
|
|
{
|
|
fat_idx = fat->table[block_offset];
|
|
|
|
// no more blocks: return bytes of what's been read
|
|
if (fat_idx == FAT_EOF)
|
|
return bytes_to_read;
|
|
|
|
descriptors[idx].ptr = disk + fat_idx * BLOCK_SIZE;
|
|
}
|
|
|
|
bytes_r = fs_read(fildes, destination, nbyte);
|
|
return bytes_to_read + bytes_r;
|
|
}
|
|
|
|
int fs_write(int fildes, void * buf, size_t nbyte)
|
|
{
|
|
size_t block_offset, /* current block's offset */
|
|
bytes_to_fill, /* bytes that can fit in the block, up to nbytes */
|
|
bytes_r = 0; /* bytes written from recursive calls, if any */
|
|
int fat_idx,
|
|
bytes_delta,
|
|
idx = get_fildes_index(fildes);
|
|
char * source = (char *) buf;
|
|
|
|
if (idx < 0)
|
|
return -1;
|
|
|
|
block_offset = (long unsigned)(descriptors[idx].ptr - disk) / BLOCK_SIZE;
|
|
bytes_to_fill = (long unsigned)disk + (block_offset + 1) * BLOCK_SIZE
|
|
- (long unsigned)(descriptors[idx].ptr);
|
|
|
|
// write only up to nbytes
|
|
if (bytes_to_fill > nbyte)
|
|
bytes_to_fill = nbyte;
|
|
|
|
nbyte -= bytes_to_fill;
|
|
|
|
// write to virt disk and advance pointer forward
|
|
memcpy(descriptors[idx].ptr, source, bytes_to_fill);
|
|
descriptors[idx].ptr += bytes_to_fill;
|
|
|
|
/* if at EOF block AND writing past file limit, increase filesize */
|
|
if (block_offset == (size_t)get_eof_block_idx(fildes))
|
|
{
|
|
bytes_delta = bytes_to_fill - descriptors[idx].attr->size % BLOCK_SIZE;
|
|
if (bytes_delta > 0)
|
|
descriptors[idx].attr->size += bytes_delta;
|
|
}
|
|
|
|
// file ptr is at end of block: move it to next available block
|
|
if (descriptors[idx].ptr == disk + (block_offset + 1) * BLOCK_SIZE)
|
|
{
|
|
fat_idx = fat->table[block_offset];
|
|
if (fat_idx == FAT_EOF)
|
|
{
|
|
fat_idx = find_avail_alloc_entry();
|
|
|
|
// no more blocks avail, ret bytes written up to now
|
|
if (fat_idx < 0)
|
|
return bytes_to_fill;
|
|
|
|
// extend current FAT idx to the next chain
|
|
fat->table[block_offset] = fat_idx;
|
|
fat->table[fat_idx] = FAT_EOF;
|
|
descriptors[idx].ptr = disk + fat_idx * BLOCK_SIZE;
|
|
}
|
|
}
|
|
|
|
if (nbyte > 0)
|
|
bytes_r = fs_write(fildes, source + bytes_to_fill, nbyte);
|
|
return bytes_to_fill + bytes_r;
|
|
}
|
|
|
|
int fs_get_filesize(int fildes)
|
|
{
|
|
int idx = get_fildes_index(fildes);
|
|
if (idx < 0)
|
|
return -1;
|
|
return descriptors[idx].attr->size;
|
|
}
|
|
|
|
int fs_lseek(int fildes, off_t offset)
|
|
{
|
|
int blocks,
|
|
fat_idx,
|
|
idx = get_fildes_index(fildes);
|
|
|
|
if (idx < 0)
|
|
return -1;
|
|
if (descriptors[idx].attr->size < offset)
|
|
return -1;
|
|
if (offset < 0)
|
|
return -1;
|
|
|
|
// seek to beginning if offset = 0
|
|
if (offset == 0)
|
|
{
|
|
descriptors[idx].ptr
|
|
= disk + descriptors[idx].attr->offset * BLOCK_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
blocks = get_file_blocksize(fildes);
|
|
|
|
// file's FAT table index for its final block
|
|
fat_idx = descriptors[idx].attr->offset;
|
|
for (int i = 0; i < blocks - 1; i++)
|
|
{
|
|
fat_idx = fat->table[fat_idx];
|
|
}
|
|
|
|
// seek to that location
|
|
descriptors[idx].ptr = disk + fat_idx * BLOCK_SIZE + offset % BLOCK_SIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fs_truncate(int fildes, off_t length)
|
|
{
|
|
int blocks,
|
|
fat_idx,
|
|
eof_idx,
|
|
idx = get_fildes_index(fildes);
|
|
char * file_ptr;
|
|
|
|
if (idx < 0)
|
|
return -1;
|
|
if (descriptors[idx].attr->size < length)
|
|
return -1;
|
|
|
|
// truncated file's block footprint
|
|
blocks = (length + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
|
|
|
// find truncated file's FAT table index for its final block
|
|
fat_idx = descriptors[idx].attr->offset;
|
|
for (int i = 1; i < blocks - 1; i++)
|
|
{
|
|
fat_idx = fat->table[fat_idx];
|
|
}
|
|
|
|
// truncated length is EOF? ret 0
|
|
if (fat->table[fat_idx] == FAT_EOF)
|
|
return 0;
|
|
|
|
// else, set truncated block's end as EOF and free the rest
|
|
eof_idx = fat_idx;
|
|
fat_idx = fat->table[fat_idx];
|
|
free_alloc_chain(fat_idx);
|
|
fat->table[eof_idx] = FAT_EOF;
|
|
memset(disk + eof_idx * BLOCK_SIZE + length % BLOCK_SIZE, 0,
|
|
BLOCK_SIZE - (length % BLOCK_SIZE));
|
|
descriptors[idx].attr->size = length;
|
|
|
|
// ptr to final byte of truncated file
|
|
file_ptr = disk + eof_idx * BLOCK_SIZE
|
|
+ descriptors[idx].attr->size % BLOCK_SIZE;
|
|
|
|
// trim the rest of the EOF block
|
|
memset(file_ptr + 1, 0,
|
|
BLOCK_SIZE - descriptors[idx].attr->size & BLOCK_SIZE);
|
|
|
|
// reset file ptr if it's pointing past the truncated end
|
|
if (file_ptr < descriptors[idx].ptr)
|
|
return fs_lseek(fildes, descriptors[idx].attr->size);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* helpers ------------------------------------------------------------------ */
|
|
|
|
void print_disk_struct()
|
|
{
|
|
Attribute * attrib = &dir->attributes[0];
|
|
|
|
printf("----------\n");
|
|
printf("Superblock: fat=%d dir=%d data=%d \n",
|
|
super->fat_offset,
|
|
super->directory_offset,
|
|
super->data_block_offset);
|
|
|
|
printf("FAT (first 20): ");
|
|
for (int i = 0; i < 20; i++)
|
|
{
|
|
printf("%d ", fat->table[i]);
|
|
}
|
|
if (dir->size == 0)
|
|
printf("\nNo files present\n");
|
|
else
|
|
printf("\nFirst file: name=%s size=%d offset=%d\n",
|
|
attrib->name,
|
|
attrib->size,
|
|
attrib->offset);
|
|
printf("First 50 bytes of data block: |");
|
|
for (int i = 0; i < 50; i++)
|
|
{
|
|
printf("%c", data[i]);
|
|
}
|
|
printf("|\n");
|
|
printf("----------\n");
|
|
}
|
|
|
|
int write_blocks(char * buf, int block_offset, int block_count)
|
|
{
|
|
int block_end = block_count + block_offset;
|
|
while (block_offset < block_end)
|
|
{
|
|
if (block_write(block_offset, buf) < 0)
|
|
return -1;
|
|
buf += BLOCK_SIZE;
|
|
block_offset++;
|
|
}
|
|
return block_offset;
|
|
}
|
|
int read_blocks(char * buf, int block_offset, int block_count)
|
|
{
|
|
int block_end = block_count + block_offset;
|
|
while (block_offset < block_end)
|
|
{
|
|
if (block_read(block_offset, buf) < 0)
|
|
return -1;
|
|
buf += BLOCK_SIZE;
|
|
block_offset++;
|
|
}
|
|
return block_offset;
|
|
}
|
|
|
|
void init_virt_disk()
|
|
{
|
|
disk = malloc(DISK_BLOCKS * BLOCK_SIZE);
|
|
memset (disk, 0, DISK_BLOCKS * BLOCK_SIZE);
|
|
|
|
// init superblock
|
|
super = (Superblock*) disk;
|
|
super->fat_offset = SUPERBLOCK_BLOCK_SIZE;
|
|
super->directory_offset = SUPERBLOCK_BLOCK_SIZE + FAT_BLOCK_SIZE;
|
|
super->data_block_offset =
|
|
SUPERBLOCK_BLOCK_SIZE + FAT_BLOCK_SIZE + DIRECTORY_BLOCK_SIZE;
|
|
|
|
fat = (FAT *) (disk + super->fat_offset * BLOCK_SIZE);
|
|
dir = (Directory*) (disk + super->directory_offset * BLOCK_SIZE);
|
|
data = disk + super->data_block_offset * BLOCK_SIZE;
|
|
|
|
// mark filesystem blocks as reserved
|
|
for (int i = 0; i < super->data_block_offset; i++)
|
|
{
|
|
fat->table[i] = FAT_RESERVED;
|
|
}
|
|
|
|
virt_disk_active = 1;
|
|
}
|
|
|
|
int free_alloc_chain(int head)
|
|
{
|
|
int idx;
|
|
|
|
/* this shouldn't happen */
|
|
if (head > DISK_BLOCKS || head == FAT_RESERVED)
|
|
return -1;
|
|
|
|
idx = fat->table[head];
|
|
memset(disk + head * BLOCK_SIZE, 0, BLOCK_SIZE);
|
|
|
|
/* either unused or probably hit the end of the chain */
|
|
if (idx == FAT_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
fat->table[head] = FAT_UNUSED;
|
|
if (idx != FAT_EOF)
|
|
return free_alloc_chain(idx);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int find_avail_alloc_entry()
|
|
{
|
|
int fat_idx = super->data_block_offset;
|
|
while (fat_idx < DISK_BLOCKS
|
|
&& fat->table[fat_idx] != FAT_UNUSED)
|
|
{
|
|
fat_idx++;
|
|
}
|
|
|
|
if (fat_idx == DISK_BLOCKS)
|
|
return -1;
|
|
return fat_idx;
|
|
}
|
|
int get_fildes_index(int fildes)
|
|
{
|
|
int i = 0;
|
|
|
|
if (descriptor_size == 0)
|
|
return -1;
|
|
|
|
while (i < descriptor_size && descriptors[i].descriptor != fildes)
|
|
{
|
|
i++;
|
|
}
|
|
|
|
if (i == descriptor_size)
|
|
return -1;
|
|
return i;
|
|
}
|
|
int get_file_blocksize(int fildes)
|
|
{
|
|
int idx = get_fildes_index(fildes);
|
|
|
|
if (idx < 0)
|
|
return -1;
|
|
|
|
return (descriptors[idx].attr->size + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
|
}
|
|
int get_eof_block_idx(int fildes)
|
|
{
|
|
int block_idx,
|
|
idx = get_fildes_index(fildes);
|
|
|
|
if (idx < 0)
|
|
return -1;
|
|
|
|
block_idx = descriptors[idx].attr->offset;
|
|
while (fat->table[block_idx] != FAT_EOF)
|
|
{
|
|
block_idx = fat->table[block_idx];
|
|
}
|
|
return block_idx;
|
|
}
|