initial commit
commit
b4594eeaa5
|
@ -0,0 +1,16 @@
|
|||
# HW04
|
||||
|
||||
## Building
|
||||
```text
|
||||
$ gcc *.h *.c -o fs
|
||||
```
|
||||
|
||||
## Running
|
||||
```text
|
||||
$ ./fs
|
||||
```
|
||||
|
||||
## Todo
|
||||
|
||||
1. `fs_write` needs to handle filesizes better
|
||||
1. write better test cases
|
|
@ -0,0 +1,17 @@
|
|||
#include "filesystem.h"
|
||||
|
||||
#define MAX_OPEN_FILES 32
|
||||
#define DESCRIPTOR_UNUSED -1
|
||||
|
||||
/* Descriptor -- file descriptor for an open file
|
||||
* Don't store this struct on disk! Generate it on the fly
|
||||
* descriptor: file descriptor (0-31)
|
||||
* ptr: where the next read/write should occur in this file
|
||||
*/
|
||||
typedef struct {
|
||||
int descriptor;
|
||||
Attribute * attr;
|
||||
char * ptr;
|
||||
} Descriptor;
|
||||
|
||||
Descriptor descriptors[MAX_OPEN_FILES];
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef _DISK_H_
|
||||
#define _DISK_H_
|
||||
|
||||
/******************************************************************************/
|
||||
#define DISK_BLOCKS 8192 /* number of blocks on the disk */
|
||||
#define BLOCK_SIZE 4096 /* block size on "disk" */
|
||||
|
||||
/******************************************************************************/
|
||||
int make_disk(char *name); /* create an empty, virtual disk file */
|
||||
int open_disk(char *name); /* open a virtual disk (file) */
|
||||
int close_disk(); /* close a previously opened disk (file) */
|
||||
|
||||
int block_write(int block, char *buf);
|
||||
/* write a block of size BLOCK_SIZE to disk */
|
||||
int block_read(int block, char *buf);
|
||||
/* read a block of size BLOCK_SIZE from disk */
|
||||
/******************************************************************************/
|
||||
|
||||
#endif
|
|
@ -0,0 +1,571 @@
|
|||
#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;
|
||||
|
||||
// create a file real quick
|
||||
fs_create("example");
|
||||
|
||||
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];
|
||||
strncpy(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;
|
||||
Attribute * del, * end;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
if (dir->size == 1)
|
||||
{
|
||||
dir->size--;
|
||||
return 0;
|
||||
}
|
||||
free_alloc_chain(dir->attributes[idx].offset);
|
||||
del = &dir->attributes[idx];
|
||||
end = &dir->attributes[dir->size - 1];
|
||||
strcpy(del->name, end->name);
|
||||
del->size = end->size;
|
||||
del->offset = end->offset;
|
||||
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,
|
||||
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;
|
||||
|
||||
memcpy(descriptors[idx].ptr, source, bytes_to_fill);
|
||||
descriptors[idx].attr->size += bytes_to_fill;
|
||||
descriptors[idx].ptr += bytes_to_fill;
|
||||
|
||||
// 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,
|
||||
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 = 0; i < blocks - 1; i++)
|
||||
{
|
||||
fat_idx = fat->table[fat_idx];
|
||||
}
|
||||
|
||||
free_alloc_chain(fat_idx);
|
||||
descriptors[idx].attr->size = length;
|
||||
|
||||
// ptr to final byte of truncated file
|
||||
file_ptr = disk + fat_idx * 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]);
|
||||
}
|
||||
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;
|
||||
|
||||
if (head > DISK_BLOCKS)
|
||||
return -1;
|
||||
if (head < 0)
|
||||
return -1;
|
||||
|
||||
idx = fat->table[head];
|
||||
if (idx == FAT_UNUSED || idx == FAT_RESERVED)
|
||||
return -1;
|
||||
if (idx == FAT_EOF)
|
||||
return 0;
|
||||
|
||||
fat->table[head] = FAT_EOF;
|
||||
while (idx != FAT_EOF)
|
||||
{
|
||||
head = idx;
|
||||
idx = fat->table[head];
|
||||
fat->table[head] = FAT_UNUSED;
|
||||
}
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
#ifndef _FILESYSTEM_H_
|
||||
#define _FILESYSTEM_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "disk.h"
|
||||
|
||||
#define MAX_FILES 64
|
||||
#define MAX_FILENAME 16
|
||||
|
||||
#define FAT_UNUSED 0
|
||||
#define FAT_EOF -1
|
||||
#define FAT_RESERVED -2
|
||||
|
||||
/*
|
||||
* Superblock -- superblock, first in line
|
||||
* All offsets are in blocks
|
||||
* alloc_table_offset: offset where allocation table starts
|
||||
* directory_offset: offset where root directory struct is stored
|
||||
* data_block_offset: offset where data block begins
|
||||
*/
|
||||
typedef struct {
|
||||
int fat_offset;
|
||||
int directory_offset;
|
||||
int data_block_offset;
|
||||
} Superblock;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int table[DISK_BLOCKS];
|
||||
} FAT;
|
||||
|
||||
|
||||
/* Attribute -- an entry for 'Directory' struct mimicking the FAT filesystem
|
||||
* name: name of file
|
||||
* size: size of file in BYTES
|
||||
* offset: block offset where file's head block starts
|
||||
*/
|
||||
typedef struct {
|
||||
char name[MAX_FILENAME];
|
||||
int size;
|
||||
int offset;
|
||||
} Attribute;
|
||||
|
||||
|
||||
/* Directory -- represents the root dir
|
||||
* size: how many files are present
|
||||
* capacity: capacity of directory
|
||||
* attributes: each file entry, represented as an Attribute struct
|
||||
*/
|
||||
typedef struct {
|
||||
int size;
|
||||
Attribute attributes[MAX_FILES];
|
||||
} Directory;
|
||||
|
||||
typedef struct {
|
||||
Superblock superblock;
|
||||
FAT fat;
|
||||
Directory directory;
|
||||
} Disk;
|
||||
|
||||
|
||||
/* filesystem api unctions */
|
||||
|
||||
int make_fs(char * disk_name);
|
||||
int mount_fs(char * disk_name);
|
||||
int umount_fs(char * disk_name);
|
||||
|
||||
int fs_open(char * name);
|
||||
int fs_close(int fildes);
|
||||
int fs_create(char * name);
|
||||
int fs_delete(char * name);
|
||||
int fs_read(int fildes, void * buf, size_t nbyte);
|
||||
int fs_write(int fildes, void * buf, size_t nbyte);
|
||||
int fs_get_filesize(int fildes);
|
||||
int fs_lseek(int fildes, off_t offset);
|
||||
int fs_truncate(int fildes, off_t length);
|
||||
|
||||
/* helpers */
|
||||
void print_disk_struct();
|
||||
int write_blocks(char * buf, int block_offset, int block_count);
|
||||
int read_blocks(char * buf, int block_offset, int block_count);
|
||||
void init_virt_disk();
|
||||
int free_alloc_chain(int head);
|
||||
int find_avail_alloc_entry();
|
||||
int get_fildes_index(int fildes);
|
||||
int get_file_blocksize(int fildes);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,152 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "filesystem.h"
|
||||
|
||||
static const int DISKNAME_LEN = 100;
|
||||
static const char * DISK_DIR = "./disks/";
|
||||
|
||||
int get_diskname(char * name);
|
||||
int list_disks();
|
||||
|
||||
int main()
|
||||
{
|
||||
char buf[DISKNAME_LEN - strlen(DISK_DIR)];
|
||||
char diskname[DISKNAME_LEN];
|
||||
int create = 0;
|
||||
|
||||
create = get_diskname(buf);
|
||||
strcpy(diskname, DISK_DIR);
|
||||
strncat(diskname, buf, DISKNAME_LEN);
|
||||
|
||||
if (create)
|
||||
{
|
||||
if (make_fs(diskname) < 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mount_fs(diskname) < 0)
|
||||
return 1;
|
||||
|
||||
// test it out real quick
|
||||
int d = fs_open("example");
|
||||
char buffer[13] = "what is this";
|
||||
char target[13];
|
||||
printf("\nwriting 'what is this'\n");
|
||||
int bytes = fs_write(d, buffer, 13);
|
||||
fs_lseek(d, 0);
|
||||
printf("%d bytes written\n", bytes);
|
||||
print_disk_struct();
|
||||
|
||||
printf("\nreading 'example' file\n");
|
||||
bytes = fs_read(d, target, 13);
|
||||
fs_lseek(d, 0);
|
||||
printf("result should be '%s': |%s|\n", buffer, target);
|
||||
printf("%d bytes read\n", bytes);
|
||||
print_disk_struct();
|
||||
|
||||
printf("\ntruncating 'example' to 10 bytes\n");
|
||||
fs_truncate(0, 10);
|
||||
print_disk_struct();
|
||||
|
||||
printf("\nreading truncated 'example' file\n");
|
||||
memset(target, 0, 13);
|
||||
bytes = fs_read(d, target, 13);
|
||||
fs_lseek(d, 0);
|
||||
printf("result should be '%.10s': |%s|\n", buffer, target);
|
||||
printf("%d bytes read\n", bytes);
|
||||
print_disk_struct();
|
||||
|
||||
char chonk[9000];
|
||||
for (int i = 0; i < 8999; i++)
|
||||
{
|
||||
chonk[i] = 'a' + i % 25;
|
||||
}
|
||||
chonk[8999] = '\0';
|
||||
printf("\nwriting a really long file\n");
|
||||
bytes = fs_write(d, chonk, 9000);
|
||||
fs_lseek(d, 0);
|
||||
printf("%d bytes written\n", bytes);
|
||||
print_disk_struct();
|
||||
|
||||
printf("\nreading really long file\n");
|
||||
memset(target, 0, 13);
|
||||
bytes = fs_read(d, target, 12);
|
||||
fs_lseek(d, 0);
|
||||
printf("%d bytes read\n", bytes);
|
||||
printf("result should be '%.12s': |%s|\n", chonk, target);
|
||||
print_disk_struct();
|
||||
|
||||
printf("\nreading the whole chonk\n");
|
||||
char chonkt[9000];
|
||||
memset(chonkt, 0, 9000);
|
||||
bytes = fs_read(d, chonkt, 8999);
|
||||
fs_lseek(d, 0);
|
||||
printf("%d bytes read\n", bytes);
|
||||
int a =printf("result should be '%s'", chonk);
|
||||
int b = printf(", and it was this|%s|\n", chonkt);
|
||||
printf("printed %d %d\n", a, b);
|
||||
print_disk_struct();
|
||||
|
||||
|
||||
fs_close(0);
|
||||
|
||||
if (umount_fs(diskname) < 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_diskname(char * name)
|
||||
{
|
||||
int create;
|
||||
|
||||
printf("Create a new disk? (y/n): ");
|
||||
create = fgetc(stdin) != 'n';
|
||||
fgetc(stdin);
|
||||
|
||||
if (create)
|
||||
{
|
||||
printf("Name the new disk: ");
|
||||
}
|
||||
else
|
||||
{
|
||||
list_disks();
|
||||
printf("Enter the name of disk to load: ");
|
||||
}
|
||||
|
||||
fgets(name, DISKNAME_LEN - strlen(DISK_DIR), stdin);
|
||||
|
||||
strtok(name, "\n");
|
||||
if (name[0] == '\n')
|
||||
{
|
||||
printf("Invalid disk name!\n");
|
||||
exit(1);
|
||||
}
|
||||
return create;
|
||||
}
|
||||
|
||||
int list_disks()
|
||||
{
|
||||
struct dirent * file;
|
||||
|
||||
DIR * dir = opendir(DISK_DIR);
|
||||
if (dir == NULL)
|
||||
{
|
||||
printf("Couldn't find %s directory\n", DISK_DIR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("List of available disks:\n");
|
||||
while ((file = readdir(dir)) != NULL)
|
||||
{
|
||||
if (strncmp(file->d_name, ".", 1) != 0
|
||||
&& strncmp(file->d_name, "..", 2) != 0)
|
||||
printf(" %s\n", file->d_name);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "disk.h"
|
||||
|
||||
/******************************************************************************/
|
||||
static int active = 0; /* is the virtual disk open (active) */
|
||||
static int handle; /* file handle to virtual disk */
|
||||
|
||||
/******************************************************************************/
|
||||
int make_disk(char *name)
|
||||
{
|
||||
int f, cnt;
|
||||
char buf[BLOCK_SIZE];
|
||||
|
||||
if (!name) {
|
||||
fprintf(stderr, "make_disk: invalid file name\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((f = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
|
||||
perror("make_disk: cannot open file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(buf, 0, BLOCK_SIZE);
|
||||
for (cnt = 0; cnt < DISK_BLOCKS; ++cnt)
|
||||
write(f, buf, BLOCK_SIZE);
|
||||
|
||||
close(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_disk(char *name)
|
||||
{
|
||||
int f;
|
||||
|
||||
if (!name) {
|
||||
fprintf(stderr, "open_disk: invalid file name\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
fprintf(stderr, "open_disk: disk is already open\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((f = open(name, O_RDWR, 0644)) < 0) {
|
||||
perror("open_disk: cannot open file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
handle = f;
|
||||
active = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int close_disk()
|
||||
{
|
||||
if (!active) {
|
||||
fprintf(stderr, "close_disk: no open disk\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(handle);
|
||||
|
||||
active = handle = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int block_write(int block, char *buf)
|
||||
{
|
||||
if (!active) {
|
||||
fprintf(stderr, "block_write: disk not active\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((block < 0) || (block >= DISK_BLOCKS)) {
|
||||
fprintf(stderr, "block_write: block index out of bounds\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lseek(handle, block * BLOCK_SIZE, SEEK_SET) < 0) {
|
||||
perror("block_write: failed to lseek");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write(handle, buf, BLOCK_SIZE) < 0) {
|
||||
perror("block_write: failed to write");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int block_read(int block, char *buf)
|
||||
{
|
||||
if (!active) {
|
||||
fprintf(stderr, "block_read: disk not active\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((block < 0) || (block >= DISK_BLOCKS)) {
|
||||
fprintf(stderr, "block_read: block index out of bounds\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lseek(handle, block * BLOCK_SIZE, SEEK_SET) < 0) {
|
||||
perror("block_read: failed to lseek");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read(handle, buf, BLOCK_SIZE) < 0) {
|
||||
perror("block_read: failed to read");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue