
Added initial support Xilinx Embedded Software. Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
1044 lines
38 KiB
C
Executable file
1044 lines
38 KiB
C
Executable file
/******************************************************************************
|
|
*
|
|
* Copyright (C) 2002 - 2014 Xilinx, Inc. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* Use of the Software is limited solely to applications:
|
|
* (a) running on a Xilinx device, or
|
|
* (b) that interact with a Xilinx device through a bus or interconnect.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* Except as contained in this notice, the name of the Xilinx shall not be used
|
|
* in advertising or otherwise to promote the sale, use or other dealings in
|
|
* this Software without prior written authorization from Xilinx.
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include <string.h>
|
|
#include "xilmfs.h"
|
|
/** Global data for file system and open files
|
|
* There can be only one MFS file system because of these globals
|
|
* If you ever need multiple instances of the MFS file system
|
|
* encapsulate these globals in a struct,
|
|
* and replace all instances of these globals with members of the new struct
|
|
* and pass a pointer to an instance of this struct to every function
|
|
* that uses these globals
|
|
*/
|
|
/* file system data */
|
|
struct mfs_file_block* mfs_file_system; /* file system blocks allocated or reserved before calling mfs_init */
|
|
int mfs_max_file_blocks; /* max number of blocks available - parameter to mfs_init */
|
|
int mfs_free_block_list; /* first element in free list; set by mfs_init */
|
|
/* data for file open */
|
|
struct mfs_open_file_struct mfs_open_files[MFS_MAX_OPEN_FILES];
|
|
int mfs_num_open_files; /* the number of mfs_open_files */
|
|
int mfs_current_dir; /* index of current directory block */
|
|
|
|
/**
|
|
* initialize the file system;
|
|
* this function must be called before any file system operations
|
|
* use mfs_init_genimage instead of this function for initializing with
|
|
* file images generated by mfsgen
|
|
* @param numbytes is the number of bytes allocated or reserved for this file system
|
|
* @param address is the starting address of the memory block
|
|
* Note: address must be word aligned (4 byte boundary)
|
|
* @param init_type is one of
|
|
* MFSINIT_NEW for creating empty read/write filesystem
|
|
* MFSINIT_IMAGE for creating read/write filesystem with predefined data
|
|
* MFSINIT_ROM_IMAGE for creating read-only filesystem with predefined data
|
|
*/
|
|
void mfs_init_fs(int numbytes, char *address, int init_type) {
|
|
int i;
|
|
mfs_max_file_blocks = numbytes/sizeof(struct mfs_file_block);
|
|
mfs_file_system = (struct mfs_file_block *)address;
|
|
if (init_type == MFSINIT_NEW) {
|
|
/* set the zeroth block to be a dir_block with itself as its parent */
|
|
/* initialize the free block list */
|
|
mfs_file_system[0].block_type = MFS_BLOCK_TYPE_DIR;
|
|
mfs_file_system[0].index = 0;
|
|
mfs_file_system[0].next_block = 0;
|
|
mfs_file_system[0].prev_block = 0;
|
|
mfs_file_system[0].u.dir_data.num_entries = 2; /* .. and . */
|
|
mfs_file_system[0].u.dir_data.num_deleted = 0;
|
|
strcpy(mfs_file_system[0].u.dir_data.dir_ent[0].name, "..");
|
|
mfs_file_system[0].u.dir_data.dir_ent[0].deleted = 'n';
|
|
mfs_file_system[0].u.dir_data.dir_ent[0].index = 0; /* .. = index of this block */
|
|
strcpy(mfs_file_system[0].u.dir_data.dir_ent[1].name, ".");
|
|
mfs_file_system[0].u.dir_data.dir_ent[1].deleted = 'n';
|
|
mfs_file_system[0].u.dir_data.dir_ent[1].index = 0; /* . = index of this block */
|
|
for (i = 1; i < mfs_max_file_blocks; i++) {
|
|
mfs_file_system[i].block_type = MFS_BLOCK_TYPE_EMPTY;
|
|
mfs_file_system[i].next_block = i+1;
|
|
mfs_file_system[i].index = i;
|
|
mfs_file_system[i].prev_block = i-1;
|
|
}
|
|
mfs_file_system[mfs_max_file_blocks-1].next_block = 0;
|
|
/* the last block points to 0 */
|
|
/* initialize free block list to the first free index = 1 */
|
|
mfs_free_block_list = 1;
|
|
}
|
|
else if (init_type == MFSINIT_IMAGE) {
|
|
i = 0;
|
|
while (mfs_file_system[i].block_type != MFS_BLOCK_TYPE_EMPTY) {
|
|
i++;
|
|
}
|
|
/* initialize free block list to the first free index = 1 */
|
|
mfs_free_block_list = i;
|
|
}
|
|
else { // (init_type == MFSINIT_ROM_IMAGE)
|
|
mfs_free_block_list = 0;
|
|
}
|
|
|
|
/* initialize current dir to the top level */
|
|
mfs_current_dir = 0;
|
|
|
|
/* initialize mfs_open_files */
|
|
for (i = 0; i < MFS_MAX_OPEN_FILES; i++)
|
|
mfs_open_files[i].mode = MFS_MODE_FREE;
|
|
mfs_num_open_files = 0;
|
|
}
|
|
|
|
/**
|
|
* initialize the file system with a file image generated by mfsgen;
|
|
* this function must be called before any file system operations
|
|
* use mfs_init_fs instead of this function for other initialization
|
|
* @param numbytes is the number of bytes allocated or reserved for this file system
|
|
* @param address is the starting address of the memory block
|
|
* Note: address must be word aligned (4 byte boundary)
|
|
* @param init_type is one of
|
|
* MFSINIT_IMAGE for creating read/write filesystem with predefined data
|
|
* MFSINIT_ROM_IMAGE for creating read-only filesystem with predefined data
|
|
*/
|
|
void mfs_init_genimage(int numbytes, char *address, int init_type) {
|
|
/* mfsgen generates a file image that contains 4 bytes identifying
|
|
* the file type as mfs2 or MFS2, followed by the file system blocks.
|
|
* So the number of bytes is 4 more than the number of bytes in the
|
|
* file system itself, and the start address of the file system blocks
|
|
* is the given address plus 4.
|
|
* Use these new values to call the mfs_init_fs function to do
|
|
* the actual work
|
|
*/
|
|
mfs_init_fs(numbytes-4, address+4, init_type);
|
|
}
|
|
|
|
|
|
/**
|
|
* Given a filename, get the directory block and the directory index within
|
|
* that block that correspond to the entry for this filename
|
|
* @param filename
|
|
* @param dir_block is a pointer to the block that is found
|
|
* @param dir_index is a pointer to the index within the block that is found
|
|
* @return 0 for failure and 1 for success
|
|
* on success:
|
|
* return dir_block = index of dir block (may not always be mfs_current_dir)
|
|
* return dir_index = index within dir_block or undefined on failure
|
|
* on failure:
|
|
* return dir_block = index of dir block that has the first "free entry" or
|
|
* index of last searched block which should have no free entries or
|
|
* -1 to indicate error
|
|
* return dir_index = index within dir_block of first free entry or
|
|
* MFS_MAX_LOCAL_ENT if last searched block has no free entry or
|
|
* -1 to indicate error
|
|
*
|
|
*/
|
|
static int get_dir_ent_base(const char *filename, int *dir_block, int *dir_index, int *reuse_block, int *reuse_index) {
|
|
/* *dir_index = 0; *dir_block = valid dir corresponding to filename prefixes processed so far, on entry to this proc */
|
|
int numentriesleft = mfs_file_system[*dir_block].u.dir_data.num_entries;
|
|
char tmpfilename[MFS_MAX_FILENAME_LENGTH];
|
|
int index = 0;
|
|
int basename = 0;
|
|
int looking_for_reuse = 0;
|
|
|
|
while(*filename != '/' && *filename != '\0') {
|
|
tmpfilename[index] = *filename;
|
|
filename++;
|
|
index++;
|
|
}
|
|
tmpfilename[index] = '\0';
|
|
if (*filename == '\0' || (*filename == '/' && *(filename+1)=='\0')) { /* this is the basename */
|
|
basename = 1;
|
|
looking_for_reuse = 1;
|
|
}
|
|
while (numentriesleft > 0) {
|
|
if (*dir_index == MFS_MAX_LOCAL_ENT) { /* move to the next dir block */
|
|
*dir_index = 0;
|
|
*dir_block = mfs_file_system[*dir_block].next_block;
|
|
}
|
|
if (mfs_file_system[*dir_block].u.dir_data.dir_ent[*dir_index].deleted != 'y' &&
|
|
!strcmp(mfs_file_system[*dir_block].u.dir_data.dir_ent[*dir_index].name,
|
|
tmpfilename)) { /* found the entry */
|
|
/* *dir_index = index; */
|
|
/* *dir_block = dir; */
|
|
if (basename == 1) /* this is the base file name, ignore final '/' if present */
|
|
return 1;
|
|
else { /* tmpname is the current prefix, filename is the rest of the path */
|
|
*dir_block = mfs_file_system[*dir_block].u.dir_data.dir_ent[*dir_index].index;
|
|
*dir_index = 0;
|
|
filename++;
|
|
return(get_dir_ent_base(filename, dir_block, dir_index, reuse_block, reuse_index));
|
|
}
|
|
|
|
}
|
|
else if ((looking_for_reuse == 1) && (mfs_file_system[*dir_block].u.dir_data.dir_ent[*dir_index].deleted == 'y') && (basename == 1)) {
|
|
/* found a possible reuse block */
|
|
*reuse_block = *dir_block;
|
|
*reuse_index = *dir_index;
|
|
looking_for_reuse = 0;
|
|
}
|
|
*dir_index += 1;
|
|
numentriesleft--;
|
|
}
|
|
if (basename == 1) { /* could not find the base name but path prefix is correct */
|
|
return 0;
|
|
}
|
|
else { /* path prefix is wrong */
|
|
*dir_block = -1;
|
|
*dir_index = -1;
|
|
return 0;
|
|
}
|
|
}
|
|
/**
|
|
* filename is an arbitrarily long '/' separated path name
|
|
* each component of the path name is never longer than MFS_MAX_FILENAME_LENGTH
|
|
* if the first character is '/', search starting at the root directory
|
|
* else search starting at the current directory
|
|
* return 0 for failure and 1 for success
|
|
* on success:
|
|
* return dir_block = index of dir block (may not always be mfs_current_dir)
|
|
* return dir_index = index within dir_block or undefined on failure
|
|
* on failure:
|
|
* return dir_block = index of dir block that has the first "free entry" or
|
|
* index of last searched block which should have no free entries or
|
|
* -1 to indicate error
|
|
* return dir_index = index within dir_block of first free entry or
|
|
* MFS_MAX_LOCAL_ENT if last searched block has no free entry or
|
|
* -1 to indicate error
|
|
*
|
|
*/
|
|
static int get_dir_ent(const char *filename, int *dir_block, int *dir_index, int *reuse_block, int *reuse_index) {
|
|
if (filename != NULL && *filename != '\0') {
|
|
if (*filename == '/') {
|
|
filename++;
|
|
*dir_block = 0;
|
|
}
|
|
else {
|
|
*dir_block = mfs_current_dir;
|
|
}
|
|
*dir_index = 0;
|
|
if (*filename == '\0') /* done - looking for the root directory */
|
|
return 1;
|
|
else
|
|
return(get_dir_ent_base(filename, dir_block, dir_index, reuse_block, reuse_index));
|
|
}
|
|
/* error condition */
|
|
*dir_block = -1;
|
|
*dir_index = -1;
|
|
return 0;
|
|
|
|
}
|
|
|
|
/**
|
|
* get the directory block and the index within the directory block that contain the value file_index
|
|
* @param dir_block is the pointer to the directory block that is found
|
|
* @param dir_index is the pointer to the directory index that is found
|
|
* @return 0 for failure and 1 for success
|
|
* on success:
|
|
* return dir_block = index of dir block (may not always be mfs_current_dir)
|
|
* return dir_index = index within dir_block or undefined on failure
|
|
* on failure:
|
|
* return dir_block = index of dir block that has the first "free entry" or
|
|
* index of last searched block which should have no free entries
|
|
* return dir_index = index within dir_block of first free entry or MFS_MAX_LOCAL_ENT if last searched block has no free entry
|
|
*/
|
|
static int get_dir_ent_by_index(int file_index, int *dir_block, int *dir_index) {
|
|
|
|
int numentriesleft;
|
|
/* dir_block is the parent directory of file_index block -> entry 0 is always ".." */
|
|
*dir_block = mfs_file_system[file_index].u.dir_data.dir_ent[0].index;
|
|
numentriesleft = mfs_file_system[*dir_block].u.dir_data.num_entries;
|
|
|
|
*dir_index = 0;
|
|
|
|
while (numentriesleft > 0) {
|
|
|
|
if (*dir_index == MFS_MAX_LOCAL_ENT) { /* move to the next dir block */
|
|
*dir_index = 0;
|
|
*dir_block = mfs_file_system[*dir_block].next_block;
|
|
}
|
|
|
|
if (mfs_file_system[*dir_block].u.dir_data.dir_ent[*dir_index].deleted != 'y' &&
|
|
mfs_file_system[*dir_block].u.dir_data.dir_ent[*dir_index].index == file_index) { /* found the entry */
|
|
/* *dir_index = index; */
|
|
/* *dir_block = dir; */
|
|
return 1;
|
|
}
|
|
*dir_index += 1;
|
|
numentriesleft--;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* modify global mfs_current_dir to index of newdir if it exists
|
|
* mfs_current_dir is not modified otherwise
|
|
* @param newdir is the name of the new directory
|
|
* @return 1 for success and 0 for failure
|
|
*/
|
|
int mfs_change_dir(const char *newdir) {
|
|
/* search current dir for newdir and change current dir if found */
|
|
/* return 1 for success, 0 for failure */
|
|
int new_dir_block;
|
|
int new_dir_index;
|
|
int reuse_block = -1;
|
|
int reuse_index = -1;
|
|
if (get_dir_ent(newdir, &new_dir_block, &new_dir_index, &reuse_block, &reuse_index)) {
|
|
mfs_current_dir = mfs_file_system[new_dir_block].u.dir_data.dir_ent[new_dir_index].index;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* allocate a new block from the free list
|
|
* @param new_entry_index is modified to point to the newly allocated block
|
|
* @return 1 on success, 0 on failure
|
|
*/
|
|
static int get_next_free_block(int *new_entry_index) {
|
|
if (mfs_free_block_list != 0) {
|
|
*new_entry_index = mfs_free_block_list;
|
|
|
|
/* update free_list */
|
|
mfs_free_block_list = mfs_file_system[*new_entry_index].next_block;
|
|
if (mfs_free_block_list != 0) {
|
|
mfs_file_system[mfs_free_block_list].prev_block = 0;
|
|
}
|
|
else {
|
|
/* free list is empty so do not update prev pointer of first element */
|
|
}
|
|
/* remove block from free list */
|
|
mfs_file_system[*new_entry_index].prev_block = 0;
|
|
mfs_file_system[*new_entry_index].next_block = 0;
|
|
return 1;
|
|
}
|
|
return 0; /* failed to get free block */
|
|
}
|
|
|
|
/**
|
|
* create a new directory block, and initialize it with info about . and ..
|
|
* if this dir wants to know its name, it needs to ask its parent
|
|
* @param file_type is either MFS_BLOCK_TYPE_DIR or MFS_BLOCK_TYPE_FILE
|
|
* @param new_entry_index is a pointer to the index of the newly allocated block for the new file
|
|
* @param parent_dir_block is the index of the parent directory of the new file/dir
|
|
* @return 1 for success and 0 for failure
|
|
*/
|
|
static int create_new_file(int file_type, int *new_entry_index, int parent_dir_block) {
|
|
if (get_next_free_block(new_entry_index)) {
|
|
if (file_type == MFS_BLOCK_TYPE_DIR) {
|
|
/* fill in the new dir block with .. and . */
|
|
mfs_file_system[*new_entry_index].block_type = MFS_BLOCK_TYPE_DIR;
|
|
mfs_file_system[*new_entry_index].u.dir_data.num_entries = 2;
|
|
mfs_file_system[*new_entry_index].u.dir_data.num_deleted = 0;
|
|
mfs_file_system[*new_entry_index].u.dir_data.dir_ent[0].index = parent_dir_block; /* parent dir of this new dir */
|
|
strcpy(mfs_file_system[*new_entry_index].u.dir_data.dir_ent[0].name, "..");
|
|
mfs_file_system[*new_entry_index].u.dir_data.dir_ent[0].deleted = 'n';
|
|
mfs_file_system[*new_entry_index].u.dir_data.dir_ent[1].index = *new_entry_index;
|
|
strcpy(mfs_file_system[*new_entry_index].u.dir_data.dir_ent[1].name, ".");
|
|
mfs_file_system[*new_entry_index].u.dir_data.dir_ent[1].deleted = 'n';
|
|
return 1;
|
|
}
|
|
else if (file_type == MFS_BLOCK_TYPE_FILE) {
|
|
mfs_file_system[*new_entry_index].block_type = MFS_BLOCK_TYPE_FILE;
|
|
mfs_file_system[*new_entry_index].block_size = 0; /* empty file */
|
|
return 1;
|
|
}
|
|
else { /* unknown file_type - error */
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* given a filename such as a/b/c/d.txt return d.txt
|
|
* @param filename is the input file name; this is not modified by the function
|
|
* @return pointer to the base name portion of filename
|
|
* return a pointer to the first char that follows the last '/' in filename
|
|
* unless the last char in filename is a '/', in which case return a pointer
|
|
* to the first char that follows the previous '/'
|
|
* if there is no '/' in filename, return filename itself
|
|
*/
|
|
|
|
static char *get_basename(const char *filename) {
|
|
char *base_filename = (char*)filename;
|
|
|
|
while(*filename != '\0') {
|
|
if (*filename == '/') {
|
|
filename++;
|
|
if (*filename != '\0')
|
|
base_filename = (char*)filename; /* the first char after / */
|
|
}
|
|
else
|
|
filename++;
|
|
}
|
|
return base_filename;
|
|
}
|
|
|
|
/**
|
|
* special strcpy for filename that omits the final '/' in the stored file name
|
|
* @param to_name
|
|
* @param from_name
|
|
* @return 2 if final '/' is seen
|
|
* @return 1 otherwise
|
|
*/
|
|
static int set_filename(char *to_name, char *from_name) {
|
|
while (*from_name != '\0') {
|
|
if (*from_name == '/') {
|
|
*to_name = '\0';
|
|
return 2;
|
|
}
|
|
*to_name = *from_name;
|
|
to_name++;
|
|
from_name++;
|
|
}
|
|
*to_name = '\0';
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* get the first directory block corresponding to "this" directory block
|
|
* the first directory block contains info about the number of files in the dir
|
|
* @param dir_block is the index of "this" directory block
|
|
* @return the index of the first directory block
|
|
*/
|
|
static int get_first_dir_block(int dir_block) {
|
|
while (mfs_file_system[dir_block].prev_block != 0) {
|
|
dir_block = mfs_file_system[dir_block].prev_block;
|
|
}
|
|
/* Fix for CR #161963 */
|
|
/* If the previous block was '0' and not a free block return 0 */
|
|
if (mfs_file_system[0].next_block == dir_block)
|
|
return 0;
|
|
return dir_block;
|
|
}
|
|
|
|
/**
|
|
* Create a new file or dir named filename
|
|
* If a dir or file of the same name exists, return 0
|
|
* If there is no space on the file system to create file, return 0
|
|
* else create the new file or dir, add entry in current dir table
|
|
* and return index of first block of file or dir
|
|
* @param filename is name of file to create
|
|
* @param file_type is either MFS_BLOCK_TYPE_DIR (for directory) or MFS_BLOCK_TYPE_FILE (for a regular file)
|
|
* @return 1 for success and 0 for failure
|
|
*/
|
|
static int create_file(const char *filename, int file_type) {
|
|
int new_dir_block;
|
|
int new_dir_index;
|
|
int new_entry_index;
|
|
int new_block;
|
|
int first_dir_block;
|
|
int reuse_block = -1;
|
|
int reuse_index = -1;
|
|
int reusing = 0;
|
|
|
|
if (get_dir_ent(filename, &new_dir_block, &new_dir_index, &reuse_block, &reuse_index)) {
|
|
/* file already exists */
|
|
return 0 ; /* cannot create file if it already exists */
|
|
}
|
|
else if (new_dir_block == -1 || new_dir_index == -1) {
|
|
/* file does not exist but path prefix does not exist either */
|
|
return 0; /* cannot create file because its parent dir does not exist */
|
|
}
|
|
else { /* create the file */
|
|
/* first check if there is a reusable entry */
|
|
if ((reuse_block != -1) && (reuse_index != -1)) {
|
|
/* found an entry to reuse */
|
|
new_dir_index = reuse_index;
|
|
new_dir_block = reuse_block;
|
|
reusing = 1;
|
|
}
|
|
else {
|
|
/* check if the current dir block is full and
|
|
allocate a new dir block if needed */
|
|
|
|
if (new_dir_index == MFS_MAX_LOCAL_ENT) {
|
|
/* create a new dir block linked from this one */
|
|
if (get_next_free_block(&new_block)) { /* found a free block */
|
|
mfs_file_system[new_block].prev_block = new_dir_block;
|
|
mfs_file_system[new_block].next_block = 0;
|
|
mfs_file_system[new_block].block_type = MFS_BLOCK_TYPE_DIR;
|
|
mfs_file_system[new_block].u.dir_data.num_entries = 0;
|
|
mfs_file_system[new_block].u.dir_data.num_deleted = 0;
|
|
mfs_file_system[new_dir_block].next_block = new_block;
|
|
new_dir_block = new_block;
|
|
new_dir_index = 0;
|
|
}
|
|
else { /* no space for new block - return failure */
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
/* at this point new_dir_index and new_dir_block both point to
|
|
the first free entry */
|
|
first_dir_block = get_first_dir_block(new_dir_block);
|
|
if (!create_new_file(file_type, &new_entry_index, first_dir_block)) { /* cannot create new file */
|
|
return 0; /* failure */
|
|
};
|
|
|
|
if (reusing != 1) {
|
|
/* update number of entries in current block */
|
|
mfs_file_system[new_dir_block].u.dir_data.num_entries += 1;
|
|
/* update number of entries in directory if it is different than current block */
|
|
|
|
if (new_dir_block != first_dir_block)
|
|
mfs_file_system[first_dir_block].u.dir_data.num_entries += 1;
|
|
}
|
|
mfs_file_system[new_dir_block].u.dir_data.dir_ent[new_dir_index].index = new_entry_index;
|
|
set_filename(mfs_file_system[new_dir_block].u.dir_data.dir_ent[new_dir_index].name, get_basename(filename));
|
|
mfs_file_system[new_dir_block].u.dir_data.dir_ent[new_dir_index].deleted = 'n';
|
|
return new_entry_index;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* move a list of blocks starting and start_index and chained
|
|
* through next_block pointers and ending at end_index
|
|
* to the free list
|
|
* the blocks are all already marked as MFS_BLOCK_TYPE_EMPTY
|
|
* the prev_block of start_index is 0, and the next_block of end_index = 0
|
|
* @param start_index
|
|
* @param end_indexA
|
|
* @return 1 - always succeeds
|
|
*/
|
|
static int move_to_free_list(int start_index, int end_index) {
|
|
if (mfs_free_block_list != 0) { /* free list exists and is non empty */
|
|
/* prepend this list to the existing free list */
|
|
mfs_file_system[mfs_free_block_list].prev_block = end_index;
|
|
mfs_file_system[end_index].next_block = mfs_free_block_list;
|
|
}
|
|
else { /* free list is empty - no need to prepend */
|
|
}
|
|
mfs_free_block_list = start_index;
|
|
return 1; /* always succeeds */
|
|
}
|
|
|
|
/**
|
|
* mark all the data blocks associated with a file as free and add them
|
|
* to the free list
|
|
* @param file_index is the index of the first block in the list of blocks that make up the file
|
|
* @return 1 for success and 0 for failure
|
|
* delete dir blocks only if the directory is empty
|
|
*/
|
|
static int delete_data_in_file(int file_index) {
|
|
int next_block;
|
|
int current_block;
|
|
if (mfs_file_system[file_index].block_type == MFS_BLOCK_TYPE_FILE) {
|
|
/* OK to delete */
|
|
}
|
|
else if (mfs_file_system[file_index].block_type == MFS_BLOCK_TYPE_DIR) {
|
|
/* first verify whether dir is actually empty */
|
|
if (mfs_file_system[file_index].u.dir_data.num_entries - mfs_file_system[file_index].u.dir_data.num_deleted != 2) {
|
|
/* dir is not empty so cannot delete */
|
|
return 0;
|
|
}
|
|
}
|
|
else { /* don't know what this is; cannot delete */
|
|
return 0;
|
|
}
|
|
/* at this point we know that it is OK to delete */
|
|
|
|
current_block = file_index;
|
|
while((next_block = mfs_file_system[current_block].next_block) != 0) {
|
|
mfs_file_system[current_block].block_type = MFS_BLOCK_TYPE_EMPTY;
|
|
current_block = next_block;
|
|
}
|
|
mfs_file_system[current_block].block_type = MFS_BLOCK_TYPE_EMPTY;
|
|
move_to_free_list(file_index, current_block);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* delete a file
|
|
* @param filename is the name of the file to be deleted
|
|
* delete the data blocks corresponding to the file and then delete the
|
|
* file entry from its directory
|
|
* @return 1 on success, 0 on failure
|
|
* delete will not work on a directory unless the directory is empty
|
|
*/
|
|
int mfs_delete_file (char *filename) {
|
|
int dir_block;
|
|
int dir_index;
|
|
int entry_index;
|
|
int first_dir_block;
|
|
int reuse_block = -1;
|
|
int reuse_index = -1;
|
|
|
|
if (!get_dir_ent(filename, &dir_block, &dir_index, &reuse_block, &reuse_index)) {
|
|
/* file does not exist */
|
|
return 0 ; /* cannot delete file if it does not exist */
|
|
}
|
|
entry_index = mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].index;
|
|
if (delete_data_in_file(entry_index)) {
|
|
/* now delete the file entry from the directory */
|
|
mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].deleted = 'y';
|
|
mfs_file_system[dir_block].u.dir_data.num_deleted += 1;
|
|
first_dir_block = get_first_dir_block(dir_block);
|
|
if (dir_block != first_dir_block)
|
|
mfs_file_system[first_dir_block].u.dir_data.num_deleted += 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* create a new empty directory inside the current directory
|
|
* @param newdir is the name of the directory
|
|
* @return index of new directory in file system if success, 0 if failure
|
|
*/
|
|
int mfs_create_dir(char *newdir) {
|
|
return create_file(newdir, MFS_BLOCK_TYPE_DIR);
|
|
}
|
|
|
|
/**
|
|
* delete the directory named newdir if it exists, and is empty
|
|
* return 1 on success, 0 on failure
|
|
* cannot delete . or ..
|
|
*/
|
|
int mfs_delete_dir (char *newdir) {
|
|
if (!strcmp(newdir,"..") || !strcmp(newdir,"."))
|
|
return 0;
|
|
return mfs_delete_file(newdir);
|
|
}
|
|
|
|
/**
|
|
* rename from_file to to_file
|
|
* works for dirs as well as files
|
|
* cannot rename to something that already exists
|
|
* @param from_file
|
|
* @param to_file
|
|
* @return 1 on success, 0 on failure
|
|
*/
|
|
int mfs_rename_file(char *from_file, char *to_file) {
|
|
int from_dir_block;
|
|
int to_dir_block;
|
|
int from_dir_index;
|
|
int to_dir_index;
|
|
int reuse_block = -1;
|
|
int reuse_index = -1;
|
|
if (get_dir_ent(from_file, &from_dir_block, &from_dir_index, &reuse_block, &reuse_index) &&
|
|
!get_dir_ent(to_file, &to_dir_block, &to_dir_index, &reuse_block, &reuse_index)) {
|
|
set_filename(mfs_file_system[from_dir_block].u.dir_data.dir_ent[from_dir_index].name, get_basename(to_file));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* check if a file exists
|
|
* @param filename is the name of the file
|
|
* @return 0 if filename is not a file in the current directory
|
|
* @return 1 if filename is a file in the current directory
|
|
* @return 2 if filename is a directory in the current directory
|
|
*/
|
|
int mfs_exists_file(char *filename) {
|
|
int dir_block;
|
|
int dir_index;
|
|
int file_block;
|
|
int reuse_block = -1;
|
|
int reuse_index = -1;
|
|
if (get_dir_ent(filename, &dir_block, &dir_index, &reuse_block, &reuse_index)) {
|
|
file_block = mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].index;
|
|
if (mfs_file_system[file_block].block_type == MFS_BLOCK_TYPE_DIR)
|
|
return 2;
|
|
else if (mfs_file_system[file_block].block_type == MFS_BLOCK_TYPE_FILE)
|
|
return 1;
|
|
else return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* get the name of the current directory
|
|
* @param dirname = pre_allocated buffer of at least MFS_MAX_FILENAME_SIZE+1 chars
|
|
* The directory name is copied to this buffer
|
|
* @return 1 if success, 0 if failure
|
|
*/
|
|
int mfs_get_current_dir_name(char *dirname){
|
|
int dir_block;
|
|
int dir_index;
|
|
int file_block;
|
|
|
|
file_block = mfs_current_dir;
|
|
if (file_block == 0) {
|
|
strcpy(dirname, "/");
|
|
return 1;
|
|
}
|
|
if(!get_dir_ent_by_index(file_block, &dir_block, &dir_index)) {
|
|
return 0; /* cannot find current dir in its parent */
|
|
}
|
|
strcpy(dirname, mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].name);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* get the number of used blocks and the number of free blocks in the file system through pointers
|
|
* @param num_blocks_used
|
|
* @param num_blocks_free
|
|
* the return value is 1 (for success) and 0 for failure to obtain the numbers
|
|
*/
|
|
int mfs_get_usage(int *num_blocks_used, int *num_blocks_free) {
|
|
int i;
|
|
*num_blocks_used = 0;
|
|
*num_blocks_free = 0;
|
|
for ( i = 0; i < mfs_max_file_blocks; i++) {
|
|
if (mfs_file_system[i].block_type == MFS_BLOCK_TYPE_EMPTY)
|
|
*num_blocks_free += 1;
|
|
else
|
|
*num_blocks_used += 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**
|
|
* get the first available/free block
|
|
* @return the index of the first free entry in the mfs_open_files array
|
|
* if there is no free entry, returns -1
|
|
*/
|
|
static int get_first_free_ftab_index() {
|
|
int i;
|
|
for (i=0; i < MFS_MAX_OPEN_FILES; i++) {
|
|
if (mfs_open_files[i].mode == MFS_MODE_FREE)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* open a directory for reading
|
|
* each subsequent call to mfs_dir_read() returns one directory entry until
|
|
* end of directory
|
|
* @param dirname is the name of the directory to open
|
|
* @return index of dir in array mfs_open_files or -1
|
|
*/
|
|
int mfs_dir_open(const char *dirname) {
|
|
return mfs_file_open(dirname, MFS_MODE_READ);
|
|
}
|
|
|
|
/**
|
|
* close a directory - same as closing a file
|
|
* @param fd is the descriptor of the directory to close
|
|
* @return 1 on success, 0 otherwise
|
|
*/
|
|
int mfs_dir_close(int fd) {
|
|
return mfs_file_close(fd);
|
|
}
|
|
|
|
/**
|
|
* read values from the next valid directory entry
|
|
* The last 3 parameters are output values
|
|
* @param fd is the file descriptor for an open directory file
|
|
* @param filename is a pointer to the filename within the MFS itself
|
|
* @param filesize is the size in bytes for a regular file or
|
|
* the number of entries in a directory
|
|
* @param filetype is MFS_BLOCK_TYPE_FILE or MFS_BLOCK_TYPE_DIR
|
|
* @return 1 for success and 0 for failure or end of dir
|
|
*/
|
|
int mfs_dir_read(int fd, char **filename, int *filesize, int *filetype) {
|
|
int numentriesleft =
|
|
mfs_file_system[mfs_open_files[fd].first_block].u.dir_data.num_entries;
|
|
int dir_block = mfs_open_files[fd].current_block;
|
|
int dir_index = mfs_open_files[fd].offset;
|
|
int direntry_block;
|
|
|
|
numentriesleft -= dir_index;
|
|
/* now get dir_index modulo MFS_MAX_LOCAL_ENT -> current_block is the correct block but dir_index is the number of entries processed so far in all the blocks so it can be arbitrarily large */
|
|
|
|
while(dir_index > MFS_MAX_LOCAL_ENT)
|
|
dir_index -= MFS_MAX_LOCAL_ENT;
|
|
while (numentriesleft > 0) {
|
|
if (dir_index == MFS_MAX_LOCAL_ENT) { /* move to the next dir block */
|
|
dir_index = 0;
|
|
dir_block = mfs_file_system[dir_block].next_block;
|
|
mfs_open_files[fd].current_block = dir_block;
|
|
}
|
|
if (mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].deleted
|
|
!= 'y' ) {
|
|
/* found a valid entry */
|
|
|
|
*filename =
|
|
mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].name;
|
|
direntry_block =
|
|
mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].index;
|
|
*filetype = mfs_file_system[direntry_block].block_type;
|
|
if (*filetype == MFS_BLOCK_TYPE_DIR) {
|
|
*filesize =
|
|
mfs_file_system[direntry_block].u.dir_data.num_entries
|
|
- mfs_file_system[direntry_block].u.dir_data.num_deleted;
|
|
}
|
|
else {
|
|
*filesize = mfs_file_system[direntry_block].block_size;
|
|
}
|
|
mfs_open_files[fd].offset +=1;
|
|
return 1;
|
|
}
|
|
dir_index += 1;
|
|
mfs_open_files[fd].offset += 1;
|
|
numentriesleft--;
|
|
}
|
|
|
|
return 0; /* nothing left to read */
|
|
}
|
|
|
|
/**
|
|
* open a file
|
|
* @param filename is the name of the file to open
|
|
* @param mode is MFS_MODE_READ or MFS_MODE_WRITE or MFS_MODE_CREATE
|
|
* this function should be used for FILEs and not DIRs
|
|
* no error checking (is this FILE and not DIR?) is done for MFS_MODE_READ
|
|
* MFS_MODE_CREATE automatically creates a FILE and not a DIR
|
|
* MFS_MODE_WRITE fails if the specified file is a DIR
|
|
* @return index of file in array mfs_open_files or -1
|
|
*/
|
|
int mfs_file_open(const char *filename, int mode) {
|
|
int dir_block;
|
|
int dir_index;
|
|
int current_index;
|
|
int reuse_block = -1;
|
|
int reuse_index = -1;
|
|
|
|
if (mfs_num_open_files >= MFS_MAX_OPEN_FILES) {/* cannot open any more files */
|
|
return -1;
|
|
}
|
|
if (mode == MFS_MODE_READ || mode == MFS_MODE_WRITE) { /* look for existing file */
|
|
if (get_dir_ent(filename, &dir_block, &dir_index, &reuse_block, &reuse_index)) { /* found it */
|
|
if (mode == MFS_MODE_WRITE && mfs_file_system[mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].index].block_type != MFS_BLOCK_TYPE_FILE) {
|
|
/* cannot open anything other than FILE for write */
|
|
return -1;
|
|
}
|
|
mfs_num_open_files++;
|
|
current_index = get_first_free_ftab_index();
|
|
mfs_open_files[current_index].first_block = mfs_file_system[dir_block].u.dir_data.dir_ent[dir_index].index;
|
|
mfs_open_files[current_index].current_block = mfs_open_files[current_index].first_block;
|
|
mfs_open_files[current_index].mode = mode;
|
|
mfs_open_files[current_index].offset = 0;
|
|
return current_index;
|
|
}
|
|
else {
|
|
//file/dir not found, open it in create mode
|
|
if (mode == MFS_MODE_WRITE)
|
|
mode = MFS_MODE_CREATE;
|
|
}
|
|
}
|
|
|
|
if (mode == MFS_MODE_CREATE) { /* create a new file */
|
|
dir_block = create_file(filename, MFS_BLOCK_TYPE_FILE);
|
|
if (dir_block == 0) { /* failed to create the file */
|
|
return -1;
|
|
}
|
|
mfs_num_open_files++;
|
|
current_index = get_first_free_ftab_index();
|
|
mfs_open_files[current_index].first_block = dir_block;
|
|
mfs_open_files[current_index].current_block = dir_block;
|
|
mfs_open_files[current_index].mode = MFS_MODE_WRITE;
|
|
mfs_open_files[current_index].offset = 0;
|
|
return current_index;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* read characters to a file
|
|
* @param fd is a descriptor for the file from which the characters are read
|
|
* @param buf is a pre allocated buffer that will contain the read characters
|
|
* @param buflen is the number of characters from buf to be read
|
|
* fd should be a valid index in mfs_open_files array
|
|
* Works only if fd points to a file and not a dir
|
|
* buf should be a pointer to a pre-allocated buffer of size buflen or more
|
|
* buflen chars are read and placed in buf
|
|
* if fewer than buflen chars are available then only that many chars are read
|
|
* @return num bytes read or 0 for error=no bytes read
|
|
*/
|
|
int mfs_file_read(int fd, char *buf, int buflen) {
|
|
int num_read = 0;
|
|
char *from_ptr = (char *) &(mfs_file_system[mfs_open_files[fd].current_block].u.block_data[mfs_open_files[fd].offset]);
|
|
int num_left ;
|
|
num_left = mfs_file_system[mfs_open_files[fd].current_block].block_size ;
|
|
if (num_left > MFS_BLOCK_DATA_SIZE)
|
|
num_left = MFS_BLOCK_DATA_SIZE;
|
|
num_left -= mfs_open_files[fd].offset ;
|
|
while (buflen > 0) {
|
|
if (num_left == 0) { /* see if there is a next_block */
|
|
int next_block = mfs_file_system[mfs_open_files[fd].current_block].next_block;
|
|
if (next_block == 0) { /* nothing more to read */
|
|
break;
|
|
}
|
|
if (mfs_file_system[next_block].block_size == 0) { /* nothing more to read */
|
|
break;
|
|
}
|
|
from_ptr = (char *) &(mfs_file_system[next_block].u.block_data[0]);
|
|
num_left = mfs_file_system[next_block].block_size;
|
|
mfs_open_files[fd].current_block = next_block;
|
|
mfs_open_files[fd].offset = 0;
|
|
}
|
|
|
|
*buf = *from_ptr;
|
|
buf++;
|
|
from_ptr++;
|
|
mfs_open_files[fd].offset += 1;
|
|
num_read++;
|
|
num_left--;
|
|
buflen--;
|
|
}
|
|
return num_read;
|
|
}
|
|
|
|
/**
|
|
* write characters to a file
|
|
* @param fd is a descriptor for the file to which the characters are written
|
|
* @param buf is a buffer containing the characters to be written out
|
|
* @param buflen is the number of characters from buf to be written out
|
|
* fd should be a valid index in mfs_open_files array
|
|
* buf should be a pointer to a pre-allocated buffer of size buflen or more
|
|
* buflen chars are read from buf and written to 1 or more blocks of the file
|
|
* @return 1 for success or 0 for error=unable to write to file
|
|
*/
|
|
int mfs_file_write (int fd, const char *buf, int buflen) {
|
|
char *to_ptr = (char *) &(mfs_file_system[mfs_open_files[fd].current_block].u.block_data[mfs_open_files[fd].offset]);
|
|
int num_left = MFS_BLOCK_DATA_SIZE - mfs_open_files[fd].offset;
|
|
|
|
while (buflen > 0) {
|
|
if (num_left == 0) { /* create next_block */
|
|
int new_block;
|
|
/* create a new file block linked from this one */
|
|
if (get_next_free_block(&new_block)) { /* found a free block */
|
|
mfs_file_system[new_block].prev_block = mfs_open_files[fd].current_block;
|
|
mfs_file_system[new_block].next_block = 0;
|
|
mfs_file_system[new_block].block_type = MFS_BLOCK_TYPE_FILE;
|
|
mfs_file_system[new_block].block_size = 0;
|
|
mfs_file_system[mfs_open_files[fd].current_block].next_block = new_block;
|
|
mfs_open_files[fd].current_block = new_block;
|
|
mfs_open_files[fd].offset = 0;
|
|
}
|
|
else { /* no space for new block - return failure */
|
|
return 0;
|
|
}
|
|
|
|
to_ptr = (char *) &(mfs_file_system[new_block].u.block_data[0]);
|
|
num_left = MFS_BLOCK_DATA_SIZE;
|
|
}
|
|
|
|
*to_ptr = *buf;
|
|
buf++;
|
|
to_ptr++;
|
|
mfs_open_files[fd].offset += 1;
|
|
num_left--;
|
|
mfs_file_system[mfs_open_files[fd].current_block].block_size +=1;
|
|
if (mfs_open_files[fd].current_block != mfs_open_files[fd].first_block)
|
|
mfs_file_system[mfs_open_files[fd].first_block].block_size += 1;
|
|
buflen--;
|
|
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* close an open file and
|
|
* recover the file table entry in mfs_open_files corresponding to the fd
|
|
* if the fd is not valid, return 0
|
|
* fd is not valid if the index in mfs_open_files is out of range, or
|
|
* if the corresponding entry is not an open file
|
|
* @param fd is the file descriptor for the file to be closed
|
|
* @return 1 on success, 0 otherwise
|
|
*/
|
|
int mfs_file_close(int fd) {
|
|
if (fd <0 || fd >= MFS_MAX_OPEN_FILES)
|
|
return 0;
|
|
if (mfs_open_files[fd].mode != MFS_MODE_FREE) {
|
|
mfs_open_files[fd].mode = MFS_MODE_FREE;
|
|
mfs_num_open_files--;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* seek to a given offset within the file
|
|
* @param fd should be a valid file descriptor for an open file
|
|
* @param whence is one of MFS_SEEK_SET, MFS_SEEK_CUR or MFS_SEEK_END
|
|
* @param offset is the offset from the beginning, end or current position as specified by the whence parameter
|
|
* if MFS_SEEK_END is specified, the offset can be either 0 or negative
|
|
* otherwise offset should be positive or 0
|
|
* it is an error to seek before beginning of file or after the end of file
|
|
* @return -1 on failure, value of offset from beginning of file on success
|
|
*/
|
|
long mfs_file_lseek(int fd, long offset, int whence) {
|
|
long local_offset;
|
|
int local_block;
|
|
if (fd <0 || fd >= MFS_MAX_OPEN_FILES || mfs_open_files[fd].mode == MFS_MODE_FREE)
|
|
return -1;
|
|
/* calculate value of offset from the beginning of the file */
|
|
if (whence == MFS_SEEK_SET || whence == MFS_SEEK_CUR) {
|
|
if (whence == MFS_SEEK_CUR) {
|
|
/* add the size of all the previous blocks if any */
|
|
local_block = mfs_open_files[fd].first_block;
|
|
while (mfs_open_files[fd].current_block != local_block) {
|
|
local_block = mfs_file_system[local_block].next_block;
|
|
offset += MFS_BLOCK_DATA_SIZE;
|
|
}
|
|
/* add the offset within the current block */
|
|
offset += mfs_open_files[fd].offset;
|
|
} else {
|
|
/* nothing to do here - offset is already calculated from the beginning of the file */
|
|
}
|
|
if (offset >= mfs_file_system[mfs_open_files[fd].first_block].block_size) { /* attempting to seek beyond end of file */
|
|
return -1;
|
|
}
|
|
}
|
|
else { /* whence == MFS_SEEK_END */
|
|
if (offset > 0) { /* cannot seek beyond end of file */
|
|
return -1;
|
|
}
|
|
else if (offset == 0) { /* return file size */
|
|
return (mfs_file_system[mfs_open_files[fd].first_block].block_size);
|
|
}
|
|
else {
|
|
offset += mfs_file_system[mfs_open_files[fd].first_block].block_size;
|
|
}
|
|
}
|
|
/* at this point offset is a positive value, guaranteed to be within the file
|
|
*/
|
|
local_offset = offset;
|
|
local_block = mfs_open_files[fd].first_block;
|
|
while(local_offset >= MFS_BLOCK_DATA_SIZE) {
|
|
local_block = mfs_file_system[local_block].next_block;
|
|
local_offset -= MFS_BLOCK_DATA_SIZE;
|
|
}
|
|
mfs_open_files[fd].current_block = local_block;
|
|
mfs_open_files[fd].offset = local_offset;
|
|
return offset;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|