// Copyright (c) 2006-2018 Frans Kaashoek, Robert Morris, Russ Cox, Massachusetts Institute of Technology // 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. // 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 AUTHORS OR COPYRIGHT HOLDERS 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. /** * @file fs.c * @brief support read and write the fs.img * @version 1.0 * @author AIIT XUOS Lab * @date 2024-01-25 */ /************************************************* File name: fs.c Description: support read and write the fs.img Others: take ARM_XV6 kernel/fs.c for references https://github.com/KingofHamyang/ARM_xv6 History: 1. Date: 2024-01-25 Author: AIIT XUOS Lab Modification: 1. remove inode lock and unlock 2. remove inode cache 3. rewrite skipelem function to PathElementExtract to fit XIZI_AIoT use sceneries *************************************************/ #include #include "block_io.h" #include "fs.h" #include "libserial.h" #define MIN_LENGTH(len1, len2) ((len1) < (len2) ? (len1) : (len2)) static int DirInodeAddEntry(struct Inode* dp, char* name, uint32_t inum); static int DirInodeDelEntry(struct Inode* parent_inode, char* name); static struct Inode* DirInodeLookup(struct Inode* dp, char* name); static struct Inode* InodeAlloc(int type); static int InodeFreeRecursive(struct Inode* dp); static char* PathElementExtract(char* path, char* name); static uint32_t InodeBlockMapping(struct Inode* inode, uint32_t block_num); #define MAX_SUPPORT_FD 4096 static struct FileDescriptor fd_table[MAX_SUPPORT_FD]; struct MemFsRange MemFsRange; /// @brief Using syscall to get fs.img real location in the memory void MemFsInit(uintptr_t _binary_fs_img_start, uint32_t fs_img_len) { MemFsRange.memfs_start = _binary_fs_img_start; MemFsRange.memfs_nr_blocks = fs_img_len / BLOCK_SIZE; } /// @brief Read the super block. void ReadSuperBlock(struct SuperBlock* sb) { uint8_t* block = BlockRead(ROOT_INUM); memmove(sb, block, sizeof(*sb)); } /// @brief Get a existed Inode by inum struct Inode* InodeGet(uint32_t inum) { struct Inode* inode; uint8_t* block = BlockRead(BLOCK_INDEX(inum)); inode = (struct Inode*)block + INODE_INDEX(inum); return inode; } /// @brief Create a new Inode under the parent Inode struct Inode* InodeCreate(struct Inode* parent_inode, char* name, int type) { struct Inode* inode; if ((inode = DirInodeLookup(parent_inode, name)) != 0) { if (type == FS_FILE && inode->type == FS_FILE) { return inode; } return 0; } if ((inode = InodeAlloc(type)) == 0) { printf("InodeCreate: alloc Inode failed, no free inode\n"); return 0; } if (type == FS_DIRECTORY) { if (DirInodeAddEntry(inode, ".", inode->inum) < 0 || DirInodeAddEntry(inode, "..", parent_inode->inum) < 0) { printf("InodeCreate: create dots"); return 0; } } if (DirInodeAddEntry(parent_inode, name, inode->inum) < 0) { printf("InodeCreate: DirInodeAddEntry failed"); return 0; } return inode; } /// @brief Delete a file Inode or a dir Inode int InodeDelete(struct Inode* parent_inode, char* name) { struct Inode* inode; if ((inode = DirInodeLookup(parent_inode, name)) == 0) { printf("Inode delete failed, file not exsit"); return -1; } if (inode->type == FS_FILE) { inode->type = 0; } else if (inode->type == FS_DIRECTORY) { // recursive free alloced Inode if (InodeFreeRecursive(inode) < 0) { return -1; } } DirInodeDelEntry(parent_inode, name); return 0; } /// @brief Read data from the Inode to the dst buffer. int InodeRead(struct Inode* inode, char* dst, int offset, int len) { uint32_t location, writen_len; uint8_t* block; if (len < 0 || offset > inode->size) { return -1; } if (offset + len > inode->size) { len = inode->size - offset; } location = 0; while (location < len) { if ((block = BlockRead(InodeBlockMapping(inode, offset / BLOCK_SIZE))) == 0) { return 0; } writen_len = MIN_LENGTH(len - location, BLOCK_SIZE - offset % BLOCK_SIZE); memmove(dst, block + offset % BLOCK_SIZE, writen_len); location += writen_len; offset += writen_len; dst += writen_len; } return len; } /// @brief Write data from src buffer to the Inode, then increase the Inode size if neccessary. int InodeWrite(struct Inode* inode, char* src, int offset, int len) { uint32_t location, writen_len; uint8_t* block; if (len < 0 || offset > inode->size) { return -1; } if (offset + len > MAX_FILE_SIZE * BLOCK_SIZE) { return -1; } location = 0; while (location < len) { if ((block = BlockRead(InodeBlockMapping(inode, offset / BLOCK_SIZE))) == 0) { return 0; } writen_len = MIN_LENGTH(len - location, BLOCK_SIZE - offset % BLOCK_SIZE); memmove(block + offset % BLOCK_SIZE, src, writen_len); location += writen_len; offset += writen_len; src += writen_len; } if (len > 0 && offset > inode->size) { inode->size = offset; } return len; } /// @brief Find target Inode from source Inode struct Inode* InodeSeek(struct Inode* source, char* path) { if (source->size == 0) { printf("Inode is empty\n"); return 0; } char name[DIR_NAME_SIZE] = { 0 }; struct Inode *cur_inode, *next_inode; cur_inode = source; while ((path = PathElementExtract(path, name)) != 0) { if (cur_inode->type != FS_DIRECTORY) { return NULL; } if ((next_inode = DirInodeLookup(cur_inode, name)) == 0) { return NULL; } cur_inode = next_inode; } return cur_inode; } /// @brief Find target parent Inode from source Inode struct Inode* InodeParentSeek(struct Inode* source, char* path, char* name) { if (source->size == 0) { printf("Inode is empty\n"); return 0; } struct Inode *cur_inode, *next_inode; cur_inode = source; while ((path = PathElementExtract(path, name)) != 0) { if (cur_inode->type != FS_DIRECTORY) { return NULL; } if (*path == '\0') { return cur_inode; } if ((next_inode = DirInodeLookup(cur_inode, name)) == 0) { return NULL; } cur_inode = next_inode; } return NULL; } /// @brief Alloc a new Inode using type static struct Inode* InodeAlloc(int type) { int inum; struct Inode* inode; struct SuperBlock sb; ReadSuperBlock(&sb); for (inum = 1; inum < sb.ninodes; inum++) { uint8_t* block = BlockRead(BLOCK_INDEX(inum)); inode = (struct Inode*)block + INODE_INDEX(inum); if (inode->type == 0) { memset(inode, 0, sizeof(*inode)); inode->inum = inum; inode->type = type; inode->size = 0; return inode; } } return NULL; } /// @brief Delete the dir and all files or dirs under the dir. static int InodeFreeRecursive(struct Inode* parent_inode) { uint32_t offset; struct Inode* inode; struct DirectEntry de; for (offset = 0; offset < parent_inode->size; offset += sizeof(de)) { if (InodeRead(parent_inode, (char*)&de, offset, sizeof(de)) != sizeof(de)) { printf("inode_delete_dir failed: read directory entry failed"); return -1; } if (de.inum == 0 || strcmp(de.name, "..") == 0 || strcmp(de.name, ".") == 0) { continue; } inode = InodeGet(de.inum); if (inode->type == FS_DIRECTORY) { if (InodeFreeRecursive(inode) < 0) { return -1; } } else if (inode->type == FS_FILE) { inode->type = 0; } // delete the dir entry de.inum = 0; if (InodeWrite(parent_inode, (char*)&de, offset, sizeof(de)) != sizeof(de)) { printf("InodeDelete failed: clear directory entry failed"); return -1; } } return 0; } /// @brief Mapping the direct block addrs or indirect block addrs of the Inode using the block_num static uint32_t InodeBlockMapping(struct Inode* inode, uint32_t block_num) { uint32_t addr; // block is in range of direct mapping if (block_num < NR_DIRECT_BLOCKS) { if ((addr = inode->addrs[block_num]) == 0) { inode->addrs[block_num] = addr = BlockAlloc(); } return addr; } // alloc a new indirect indexing block block_num -= NR_DIRECT_BLOCKS; int indirect_block_id = block_num / MAX_INDIRECT_BLOCKS; if (indirect_block_id < NR_INDIRECT_BLOCKS) { if ((addr = inode->addrs[NR_DIRECT_BLOCKS + indirect_block_id]) == 0) { inode->addrs[NR_DIRECT_BLOCKS + indirect_block_id] = addr = BlockAlloc(); } block_num -= indirect_block_id * MAX_INDIRECT_BLOCKS; } else { printf("InodeBlockMapping: out of range"); return 0; } // alloc a new indirect block uint32_t* indirect_block = (uint32_t*)BlockRead(addr); if ((addr = indirect_block[block_num]) == 0) { indirect_block[block_num] = addr = BlockAlloc(); } return addr; } /// @brief Look up the directory Inode for searching the target Inode static struct Inode* DirInodeLookup(struct Inode* parent_inode, char* name) { uint32_t offset, inum; struct DirectEntry de; if (parent_inode->type != FS_DIRECTORY) { printf("DirInodeLookup not DIR"); return 0; } for (offset = 0; offset < parent_inode->size; offset += sizeof(de)) { if (InodeRead(parent_inode, (char*)&de, offset, sizeof(de)) != sizeof(de)) { printf("DirInodeAddEntry read"); return 0; } if (de.inum == 0) { continue; } if (strncmp((const char*)name, (const char*)de.name, DIR_NAME_SIZE) == 0) { inum = de.inum; return InodeGet(inum); } } return 0; } /// @brief Add a new directory entry for dir Inode static int DirInodeAddEntry(struct Inode* parent_inode, char* name, uint32_t inum) { int offset; struct DirectEntry de; struct Inode* inode; // Check the direct entry is not existed. if ((inode = DirInodeLookup(parent_inode, name)) != 0) { return -1; } // Look for an empty dir entry. for (offset = 0; offset < parent_inode->size; offset += sizeof(de)) { if (InodeRead(parent_inode, (char*)&de, offset, sizeof(de)) != sizeof(de)) { printf("DirInodeAddEntry: read failed"); return -1; } if (de.inum == 0) { break; } } // build a new direct entry. strncpy(de.name, name, DIR_NAME_SIZE); de.inum = inum; if (InodeWrite(parent_inode, (char*)&de, offset, sizeof(de)) != sizeof(de)) { printf("DirInodeAddEntry: write failed"); return -1; } return 0; } /// @brief Delete the directory entry for dir Inode static int DirInodeDelEntry(struct Inode* parent_inode, char* name) { int offset; struct DirectEntry de; struct Inode* inode; // Check the direct entry is existed. if ((inode = DirInodeLookup(parent_inode, name)) == 0) { return -1; } // Look for an empty dir entry. for (offset = 0; offset < parent_inode->size; offset += sizeof(de)) { if (InodeRead(parent_inode, (char*)&de, offset, sizeof(de)) != sizeof(de)) { printf("DirInodeAddEntry: read failed"); return -1; } if (strncmp(de.name, name, DIR_NAME_SIZE) == 0) { break; } } de.inum = 0; if (InodeWrite(parent_inode, (char*)&de, offset, sizeof(de)) != sizeof(de)) { printf("DirInodeAddEntry: write failed"); return -1; } return 0; } // Paths process static char* PathElementExtract(char* path, char* name) { // Skip leading slashes while (*path == '/') path++; // Check for end of path if (*path == 0) return NULL; // Extract element char* start = path; while (*path != '/' && *path != 0) path++; // Calculate length and copy to 'name' int len = path - start; if (len >= DIR_NAME_SIZE) len = DIR_NAME_SIZE - 1; strncpy(name, start, len); name[len] = 0; // Skip trailing slashes while (*path == '/') path++; return path; } struct FileDescriptor* GetFileDescriptor(int fd) { if (fd < 0 || fd > MAX_SUPPORT_FD) { printf("fd invlid.\n"); return NULL; } return &fd_table[fd]; } void FreeFileDescriptor(int fd) { if (fd < 0 || fd > MAX_SUPPORT_FD) { printf("fd invlid.\n"); return; } fd_table[fd].data = NULL; return; } int AllocFileDescriptor(void) { int free_idx = -1; for (int i = 0; i < MAX_SUPPORT_FD; i++) { // found free fd if (free_idx == -1 && fd_table[i].data == NULL) { free_idx = i; break; } } if (free_idx == -1) { return -1; } return free_idx; }