331 lines
13 KiB
C
331 lines
13 KiB
C
#include "ece391fs.h"
|
|
|
|
ece391fs_bootblk_t* fs_bootblk = NULL;
|
|
|
|
unified_fs_interface_t ece391fs_file_if = {
|
|
.open = file_open,
|
|
.read = file_read,
|
|
.write = file_write,
|
|
.ioctl = NULL,
|
|
.close = file_close
|
|
};
|
|
|
|
unified_fs_interface_t ece391fs_dir_if = {
|
|
.open = dir_open,
|
|
.read = dir_read,
|
|
.write = dir_write,
|
|
.ioctl = NULL,
|
|
.close = dir_close
|
|
};
|
|
|
|
/* int32_t ece391fs_init(uint32_t module_start, uint32_t module_end)
|
|
* @input: module_start - start position of initramfs
|
|
* module_end - end position of initramfs
|
|
* @output: variable fs_bootblk - set to module_start
|
|
* return value - SUCCESS / FAIL
|
|
* @description: Verifies that the filesystem between module_start and module_end
|
|
* is a valid ECE391FS file system. If it is, put the start address to fs_bootblk
|
|
* global variable and return SUCCESS. If it isn't, return FAIL.
|
|
*/
|
|
int32_t ece391fs_init(uint32_t module_start, uint32_t module_end) {
|
|
ece391fs_bootblk_t* fs_candidate = (ece391fs_bootblk_t*) module_start;
|
|
// Check if the number of files is within MAX_FILE_COUNT
|
|
if(fs_candidate->num_dir_entries > ECE391FS_MAX_FILE_COUNT) {
|
|
return FAIL;
|
|
}
|
|
// Check if the filesystem ends at correct address
|
|
// The FS consists of 1 bootblock, *num_inodes* inodes, *num_data_blocks* data blocks.
|
|
if((ece391fs_bootblk_t*) module_end
|
|
!= fs_candidate + (1 + fs_candidate->num_inodes + fs_candidate->num_data_blocks)) {
|
|
return FAIL;
|
|
}
|
|
// Register the filesystem globally
|
|
fs_bootblk = fs_candidate;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* int32_t ece391fs_is_initialized()
|
|
* @output: return value - SUCCESS / FAIL
|
|
* @description: Return whether ece391fs is initialied.
|
|
*/
|
|
int32_t ece391fs_is_initialized() {
|
|
return fs_bootblk ? SUCCESS : FAIL;
|
|
}
|
|
|
|
/* int32_t ece391fs_size(uint32_t inode_idx)
|
|
* @input: inode_idx - index of inode whose size to be queried
|
|
* @output: return value - file size in bytes of the inode
|
|
* FAIL - only if the inode doesn't exist
|
|
* @description: query file size of given inode index.
|
|
*/
|
|
int32_t ece391fs_size(uint32_t inode_idx) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
if(inode_idx >= fs_bootblk->num_inodes) return FAIL; // Index over inode count
|
|
// Calculate pointer to the inode
|
|
ece391fs_inode_t* inode = (ece391fs_inode_t*) fs_bootblk + (inode_idx + 1);
|
|
return inode->size;
|
|
}
|
|
|
|
/* int32_t read_dentry_by_name(const char* fname, ece391fs_file_info_t* file_info)
|
|
* @input: fname - filename
|
|
* file_info - file info struct to be written into
|
|
* @output: file_info - filled with the information of queried file
|
|
* return value - PASS / FAIL
|
|
* @description: query file info of given filename.
|
|
* "file info" is equivalent to "dentry" to make ece391 staff happy.
|
|
*/
|
|
int32_t read_dentry_by_name(const char* fname, ece391fs_file_info_t* file_info) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
if(!file_info) return FAIL; // File info ptr invalid
|
|
if(strlen(fname) > ECE391FS_MAX_FILENAME_LEN) return FAIL;// Filename too long
|
|
int i;
|
|
for(i = 0; i < ECE391FS_MAX_FILE_COUNT; i++) {
|
|
ece391fs_file_info_t* f = &(fs_bootblk->file[i]);
|
|
// Compare up to ECE391FS_MAX_FILENAME_LEN characters if filename is very long.
|
|
// Otherwise, compare up to strlen(fname) + 1, to include comparison of terminating 0x0.
|
|
int fname_len = strlen(fname) == ECE391FS_MAX_FILENAME_LEN ? ECE391FS_MAX_FILENAME_LEN : strlen(fname) + 1;
|
|
if(0 == strncmp(fname, f->name, fname_len)) {
|
|
// This is the file we're looking for
|
|
*file_info = *f;
|
|
return SUCCESS;
|
|
}
|
|
}
|
|
// File not found
|
|
return FAIL;
|
|
}
|
|
|
|
/* int32_t read_dentry_by_index(uint32_t index, ece391fs_file_info_t* file_info)
|
|
* @input: index - index of file inode to be queried.
|
|
* file_info - file info struct to be written into
|
|
* @output: file_info - filled with the information of queried file
|
|
* return value - PASS / FAIL
|
|
* @description: query file info of given index.
|
|
* "file info" is equivalent to "dentry" to make ece391 staff happy.
|
|
*/
|
|
int32_t read_dentry_by_index(uint32_t index, ece391fs_file_info_t* file_info) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
if(index >= fs_bootblk->num_dir_entries) return FAIL; // Index over inode count
|
|
if(!file_info) return FAIL; // File info ptr invalid
|
|
*file_info = fs_bootblk->file[index];
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* int32_t read_data(uint32_t inode_idx, uint32_t offset, char* buf, uint32_t length)
|
|
* @input: inode_idx - inode of file to be read from
|
|
* offset - starting byte from the beginning of file
|
|
* buf - where the data will be written into
|
|
* length - the number of bytes to be read
|
|
* @output: buf - *length* bytes are written here
|
|
* return value - bytes of data copied, or FAIL
|
|
* @description: read data from given inode.
|
|
*/
|
|
int32_t read_data(uint32_t inode_idx, uint32_t offset, char* buf, uint32_t length) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
if(inode_idx >= fs_bootblk->num_inodes) return FAIL; // Index over inode count
|
|
if(!buf) return FAIL; // Buffer ptr invalid
|
|
ece391fs_inode_t* inode = (ece391fs_inode_t*) fs_bootblk + (1 + inode_idx);
|
|
if(offset >= inode->size) return 0; // Offset over file length, nothing can be copied
|
|
if(offset + length > inode->size) length = inode->size - offset; // Length over end of file, reduce it
|
|
|
|
// Calculate the block range where data will be read
|
|
uint32_t first_data_block = offset / ECE391FS_BLOCK_SIZE;
|
|
uint32_t last_data_block = (offset + length) / ECE391FS_BLOCK_SIZE;
|
|
uint32_t bytes_done = 0; // Counter of bytes copied, to determine the position of buf to write into
|
|
uint32_t i;
|
|
for(i = first_data_block; i <= last_data_block; i++) {
|
|
// Find the actual data block
|
|
uint32_t data_id = inode->data[i];
|
|
ece391fs_data_block_t* data_ptr = (ece391fs_data_block_t*) fs_bootblk + (fs_bootblk->num_inodes + 1 + data_id);
|
|
// Calculate the beginning and ending position of copying
|
|
uint32_t block_byte_begin = 0;
|
|
uint32_t block_byte_end = ECE391FS_BLOCK_SIZE;
|
|
if(i * ECE391FS_BLOCK_SIZE < offset) {
|
|
// This is the first block to be processed, offset should be applied.
|
|
block_byte_begin = offset - i * ECE391FS_BLOCK_SIZE;
|
|
}
|
|
if((i + 1) * ECE391FS_BLOCK_SIZE > offset + length) {
|
|
// This is the last block to be processed, length should be enforced.
|
|
block_byte_end = offset + length - i * ECE391FS_BLOCK_SIZE;
|
|
}
|
|
//printf("Block #%d, done %d, range %d %d\n", data_id, bytes_done, block_byte_begin, block_byte_end);
|
|
memcpy((char*) buf + bytes_done, (char*) data_ptr + block_byte_begin, block_byte_end - block_byte_begin);
|
|
bytes_done += block_byte_end - block_byte_begin;
|
|
}
|
|
return bytes_done;
|
|
}
|
|
|
|
/* int32_t read_dir(uint32_t offset, char* buf, uint32_t length)
|
|
* @input: offset - the index of file to be read.
|
|
* buf - the location data should be written to.
|
|
* length - the size of buffer.
|
|
* @output: buf - written with file *offset*'s filename
|
|
* @description: read the directory on ECE391FS.
|
|
* As there's only the root directory, we don't need to know
|
|
* which directory is being queried (there's only one).
|
|
* Offset denotes the index of file to be read.
|
|
*/
|
|
int32_t read_dir(uint32_t offset, char* buf, uint32_t length) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
if(offset >= ECE391FS_MAX_FILE_COUNT) return FAIL; // Index over inode count
|
|
if(!buf) return FAIL; // Buffer ptr invalid
|
|
if(length > ECE391FS_MAX_FILENAME_LEN) length = ECE391FS_MAX_FILENAME_LEN;
|
|
|
|
ece391fs_file_info_t* finfo = &(fs_bootblk->file[offset]);
|
|
if(length > strlen(finfo->name)) length = strlen(finfo->name);
|
|
memcpy((char*) buf, (char*) finfo->name, length);
|
|
|
|
return length;
|
|
}
|
|
|
|
/* void ece391fs_print_file_info(ece391fs_file_info_t* file_info)
|
|
* @input: file_info: the dentry of the file to be displayed
|
|
* @output: file info on screen
|
|
* @description: print file info onto screen for debugging.
|
|
*/
|
|
void ece391fs_print_file_info(ece391fs_file_info_t* file_info) {
|
|
int i;
|
|
switch(file_info->type) {
|
|
case ECE391FS_FILE_TYPE_RTC:
|
|
printf("RTC"); break;
|
|
case ECE391FS_FILE_TYPE_FOLDER:
|
|
printf("Folder"); break;
|
|
case ECE391FS_FILE_TYPE_FILE:
|
|
printf("File"); break;
|
|
default: break;
|
|
}
|
|
printf(" name: ");
|
|
int name_length = strlen(file_info->name);
|
|
if(name_length > ECE391FS_MAX_FILENAME_LEN) {
|
|
name_length = ECE391FS_MAX_FILENAME_LEN;
|
|
}
|
|
for(i = 0; i < name_length; i++) {
|
|
if(file_info->name[i]) {
|
|
putc(file_info->name[i]);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
printf("\n");
|
|
if(file_info->type != ECE391FS_FILE_TYPE_FILE) return;
|
|
printf("Inode #: %d \n", file_info->inode);
|
|
ece391fs_inode_t* inode = (ece391fs_inode_t*) fs_bootblk + (file_info->inode + 1);
|
|
printf("Size: %d \n", inode->size);
|
|
printf("Blocks #: ");
|
|
for(i = 0; i * ECE391FS_BLOCK_SIZE < inode->size; i++) {
|
|
printf("%d ", inode->data[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
// Following code only works for CP2, not meant for CP3 and afterwards
|
|
/* int32_t file_open(int32_t* inode, char* filename)
|
|
* @input: inode - file descriptor
|
|
* filename - name of file to be opened
|
|
* @output: inode - set to inode id of file
|
|
* ret val - SUCCESS / FAIL
|
|
* @description: open a file.
|
|
*/
|
|
int32_t file_open(int32_t* inode, char* filename) {
|
|
ece391fs_file_info_t finfo;
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
if(-1 == read_dentry_by_name(filename, &finfo)) return FAIL;
|
|
if(finfo.type != ECE391FS_FILE_TYPE_FILE) return FAIL;
|
|
*inode = finfo.inode;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* int32_t file_read(int32_t* inode, uint32_t* offset, char* buf, uint32_t len)
|
|
* @input: inode - file descriptor
|
|
* offset - starting position to be read
|
|
* buf - location data to be written to
|
|
* len - length of data to be written to
|
|
* @output: buf - data written
|
|
* offset - added the number of bytes written to buf
|
|
* ret val - bytes written / FAIL
|
|
* @description: read data from a file.
|
|
*/
|
|
int32_t file_read(int32_t* inode, uint32_t* offset, char* buf, uint32_t len) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
int32_t result = read_data(*inode, *offset, buf, len);
|
|
if(result > 0) *offset += result;
|
|
return result;
|
|
}
|
|
|
|
/* int32_t file_write(int32_t* inode, uint32_t* offset, char* buf, uint32_t len)
|
|
* @input: inode - file descriptor
|
|
* offset - starting position to be read
|
|
* buf - location data to be read from
|
|
* len - length of data to be read
|
|
* @output: ret val - FAIL
|
|
* @description: write data to a file. Just return fail as filesystem is readonly.
|
|
*/
|
|
int32_t file_write(int32_t* inode, uint32_t* offset, const char* buf, uint32_t len) {
|
|
return FAIL;
|
|
}
|
|
|
|
/* int32_t file_close(int32_t* inode)
|
|
* @input: inode - file descriptor
|
|
* @output: inode - set to 0
|
|
* ret val - SUCCESS
|
|
* @description: closes a file
|
|
*/
|
|
int32_t file_close(int32_t* inode) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
*inode = 0;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* int32_t dir_open(int32_t* inode, char* filename)
|
|
* @input: inode - file descriptor
|
|
* filename - name of dir to be opened
|
|
* @output: ret val - SUCCESS / FAIL
|
|
* @description: open a directory.
|
|
*/
|
|
int32_t dir_open(int32_t* inode, char* filename) {
|
|
ece391fs_file_info_t finfo;
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
if(-1 == read_dentry_by_name(filename, &finfo)) return FAIL;
|
|
if(finfo.type != ECE391FS_FILE_TYPE_FOLDER) return FAIL;
|
|
*inode = finfo.inode;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* int32_t dir_read(int32_t* inode, uint32_t* offset, char* buf, uint32_t len)
|
|
* @input: inode - file descriptor
|
|
* offset - starting position to be read
|
|
* buf - location data to be written to
|
|
* len - length of data to be written to
|
|
* @output: buf - data written
|
|
* offset - added 1 to indicate next file.
|
|
* ret val - bytes written / FAIL
|
|
* @description: read file list from a folder.
|
|
*/
|
|
int32_t dir_read(int32_t* inode, uint32_t* offset, char* buf, uint32_t len) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
int32_t result = read_dir(*offset, buf, len);
|
|
if(result > 0) *offset += 1;
|
|
return result;
|
|
}
|
|
|
|
/* int32_t dir_write(int32_t* inode, uint32_t* offset, char* buf, uint32_t len)
|
|
* @input: inode - file descriptor
|
|
* offset - starting position to be read
|
|
* buf - location data to be read from
|
|
* len - length of data to be read
|
|
* @output: ret val - FAIL
|
|
* @description: write data to a folder. Just return fail as filesystem is readonly.
|
|
*/
|
|
int32_t dir_write(int32_t* inode, uint32_t* offset, const char* buf, uint32_t len) {
|
|
return FAIL;
|
|
}
|
|
|
|
/* int32_t dir_close(int32_t* inode)
|
|
* @input: inode - file descriptor
|
|
* @output: ret val - SUCCESS
|
|
* @description: closes a directory
|
|
*/
|
|
int32_t dir_close(int32_t* inode) {
|
|
if(!fs_bootblk) return FAIL; // FS not initialized
|
|
return SUCCESS;
|
|
}
|