/****************************************************************************** * * 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 #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; }