/* * See the comment in vfs.h for what is expected of this function. * * When this function returns, the inode refcount should be decremented. * * You probably want to use s5_free_inode() if there are no more links to * the inode, and dont forget to unpin the page */ static void s5fs_delete_vnode(vnode_t *vnode) { pframe_t *p; mmobj_t *fs_mmobj = S5FS_TO_VMOBJ(VNODE_TO_S5FS(vnode)); if (pframe_get(fs_mmobj, S5_INODE_BLOCK(vnode->vn_vno), &p)){ panic("pframe_get failed. What the hell do we do?\n"); } s5_inode_t *inode = ((s5_inode_t *) p->pf_addr) + S5_INODE_OFFSET(vnode->vn_vno); dbg(DBG_S5FS, "decrementing link count on inode %d from %d to %d\n", inode->s5_number, inode->s5_linkcount,inode->s5_linkcount - 1); inode->s5_linkcount--; if (inode->s5_linkcount == 0){ s5_free_inode(vnode); } else { s5_dirty_inode(VNODE_TO_S5FS(vnode), inode); } pframe_unpin(p); }
/* * s5fs_fillpage: * Read the page of 'vnode' containing 'offset' into the * page-aligned and page-sized buffer pointed to by * 'pagebuf'. * param *vnode: the pointer to the vnode object * param offset: the arbitrary offset of that file (bytes offset) * param pagebuf: the buffer for storing the data you have read * return: number of bytes that have been read, or negative number for errors */ static int s5fs_fillpage(vnode_t *vnode, off_t offset, void *pagebuf) { dbg(DBG_S5FS, "{\n"); KASSERT(vnode != NULL); KASSERT(pagebuf != NULL); int disk_number = 0; if ((disk_number = s5_seek_to_block(vnode, offset, 0)) < 0) { return disk_number; } if (disk_number == 0) { memset(pagebuf, 0, PAGE_SIZE); return 0; } /* If the disk_number is zero, ignore pagebuf */ int ret = VNODE_TO_S5FS(vnode)->s5f_bdev->bd_ops->read_block( VNODE_TO_S5FS(vnode)->s5f_bdev, pagebuf, disk_number, 1); dbg(DBG_S5FS, "}\n"); return ret; }
/* * See the comment in vnode.h for what is expected of this function. * * When this function returns, the inode refcount on the parent should be * decremented (since ".." in the removed directory no longer references * it). Remember that the directory must be empty (except for "." and * ".."). * * You probably want to use s5_find_dirent() and s5_remove_dirent(). */ static int s5fs_rmdir(vnode_t *parent, const char *name, size_t namelen) { KASSERT(!(namelen == 1 && name[0] == '.')); KASSERT(!(namelen == 2 && name[0] == '.' && name[1] == '.')); KASSERT(parent->vn_ops->rmdir != NULL); kmutex_lock(&parent->vn_mutex); int ino = s5_find_dirent(parent, name, namelen); /* we check in do_rmdir to make sure the directory exists */ KASSERT(ino != -ENOENT); if (ino < 0){ dbg(DBG_S5FS, "error finding child dir to delete\n"); kmutex_unlock(&parent->vn_mutex); return ino; } vnode_t *child = vget(VNODE_TO_S5FS(parent)->s5f_fs, ino); kmutex_lock(&child->vn_mutex); int dot_lookup_res = s5_find_dirent(child, ".", 1); int dotdot_lookup_res = s5_find_dirent(child, "..", 2); KASSERT(dot_lookup_res != -ENOENT && dotdot_lookup_res != -ENOENT); if (dot_lookup_res < 0 || dotdot_lookup_res < 0){ dbg(DBG_S5FS, "error reading dirents of directory to delete\n"); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&parent->vn_mutex); return (dot_lookup_res < 0) ? dot_lookup_res : dotdot_lookup_res; } KASSERT((unsigned) child->vn_len >= 2 * sizeof(s5_dirent_t)); if ((unsigned) child->vn_len > 2 * sizeof(s5_dirent_t)){ vput(child); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&parent->vn_mutex); return -ENOTEMPTY; } vput(child); VNODE_TO_S5INODE(parent)->s5_linkcount--; s5_dirty_inode(VNODE_TO_S5FS(parent), VNODE_TO_S5INODE(parent)); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&parent->vn_mutex); return s5_remove_dirent(parent, name, namelen); }
/* * s5fs_dirtypage: * A hook; an attempt is being made to dirty the page * belonging to 'vnode' that contains 'offset'. (If the * underlying fs supports sparse blocks/pages, and the page * containing this offset is currently sparse, this is * where an attempt should be made to allocate a block in * the underlying fs for that block/page). Return zero on * success and nonzero otherwise. * param vnode: the pointer to the vnode object * param offset: the arbitrary offset of that file (bytes offset) * return: the block number or 0; negative number for failure */ static int s5fs_dirtypage(vnode_t *vnode, off_t offset) { dbg(DBG_S5FS, "{\n"); KASSERT(vnode != NULL); int disk_number = 0; int sparse = 0; if ((sparse = s5_seek_to_block(vnode, offset, 0)) < 0) { return sparse; } else if (sparse > 0) { /* find the page */ return 0; } if ((disk_number = s5_seek_to_block(vnode, offset, 1)) < 0) { return disk_number; } /* Dirty the page */ s5_dirty_inode(VNODE_TO_S5FS(vnode), VNODE_TO_S5INODE(vnode)); dbg(DBG_S5FS, "}\n"); return 0; }
/* * Locate the directory entry in the given inode with the given name, * and delete it. If there is no entry with the given name, return * -ENOENT. * * In order to ensure that the directory entries are contiguous in the * directory file, you will need to move the last directory entry into * the remove dirent's place. * * When this function returns, the inode refcount on the removed file * should be decremented. * * It would be a nice extension to free blocks from the end of the * directory file which are no longer needed. * * Don't forget to dirty appropriate blocks! * * You probably want to use vget(), vput(), s5_read_file(), * s5_write_file(), and s5_dirty_inode(). */ int s5_remove_dirent(vnode_t *vnode, const char *name, size_t namelen) { dbg_print("s5_remove_dirent: Removing Dirent"); int inode = s5_find_dirent(vnode,name,namelen); if(inode == -ENOENT) { dbg_print("s5_remove_dirent: ERROR, Directory not found"); return -ENOENT; } else { vnode_t* vnode_to_remove = vget(vnode->vn_fs,inode); s5_inode_t* inode_to_remove = VNODE_TO_S5INODE(vnode_to_remove); inode_to_remove->s5_linkcount--; vput(vnode_to_remove); /* Need to get the offset of where the dirent is */ int total_count = 0; int dir_index = 0; /* Loop through the inode blocks until the dirent is found. */ s5_dirent_t traverse_dir; while (s5_read_file(vnode, dir_index * sizeof(s5_dirent_t), (char *) &traverse_dir, sizeof(s5_dirent_t)) > 0) { /* Once found, return. */ if (name_match(traverse_dir.s5d_name, name, namelen)) { dir_index = total_count; } total_count++; } total_count--; if(dir_index != total_count) { /* swap dir with last */ char* last_buffer = kmalloc(sizeof(s5_dirent_t)); s5_dirent_t* dirent = (s5_dirent_t*)last_buffer; int read = s5_read_file(vnode,vnode->vn_len - sizeof(s5_dirent_t),last_buffer,sizeof(s5_dirent_t)); KASSERT(read == sizeof(s5_dirent_t)); int write = s5_write_file(vnode, dir_index * sizeof(s5_dirent_t), last_buffer, sizeof(s5_dirent_t)); KASSERT(write == sizeof(s5_dirent_t)); } s5_inode_t* inode = VNODE_TO_S5INODE(vnode); vnode->vn_len -= sizeof(s5_dirent_t); inode->s5_size -= sizeof(s5_dirent_t); s5_dirty_inode(VNODE_TO_S5FS(vnode),inode); return 0; } /* NOT_YET_IMPLEMENTED("S5FS: s5_remove_dirent"); * return -1; */ }
/* * See the comment in vfs.h for what is expected of this function. * * When this function returns, the inode link count should be incremented. * Note that most UNIX filesystems don't do this, they have a separate * flag to indicate that the VFS is using a file. However, this is * simpler to implement. * * To get the inode you need to use pframe_get then use the pf_addr * and the S5_INODE_OFFSET(vnode) to get the inode * * Don't forget to update linkcounts and pin the page. * * Note that the devid is stored in the indirect_block in the case of * a char or block device * * Finally, the main idea is to do special initialization based on the * type of inode (i.e. regular, directory, char/block device, etc'). * */ static void s5fs_read_vnode(vnode_t *vnode) { pframe_t *p; mmobj_t *fs_mmobj = S5FS_TO_VMOBJ(VNODE_TO_S5FS(vnode)); if (pframe_get(fs_mmobj, S5_INODE_BLOCK(vnode->vn_vno), &p)){ panic("pframe_get failed. What the hell do we do?\n"); } pframe_pin(p); s5_inode_t *inode = ((s5_inode_t *) p->pf_addr) + S5_INODE_OFFSET(vnode->vn_vno); /* generic initializations */ vnode->vn_len = inode->s5_size; vnode->vn_i = (void *) inode; inode->s5_linkcount++; /* type-specific initializations */ KASSERT(inode->s5_type == S5_TYPE_DATA || inode->s5_type == S5_TYPE_DIR || inode->s5_type == S5_TYPE_CHR || inode->s5_type == S5_TYPE_BLK); switch (inode->s5_type){ case S5_TYPE_DATA: vnode->vn_ops = &s5fs_file_vops; vnode->vn_mode = S_IFREG; break; case S5_TYPE_DIR: vnode->vn_ops = &s5fs_dir_vops; vnode->vn_mode = S_IFDIR; break; case S5_TYPE_CHR: vnode->vn_mode = S_IFCHR; vnode->vn_devid = inode->s5_indirect_block; break; default: /* S5_TYPE_BLK */ vnode->vn_mode = S_IFBLK; vnode->vn_devid = inode->s5_indirect_block; } s5_dirty_inode(VNODE_TO_S5FS(vnode), inode); }
/* allocated an indirect block for a vnode who's indirect block is currently sparse */ static int alloc_indirect_block(vnode_t *v){ /* an array of 0's that we'll use to quickly create blocks of zeros */ static int zero_array[BLOCK_SIZE] = {}; s5_inode_t *inode = VNODE_TO_S5INODE(v); KASSERT(inode->s5_indirect_block == 0); /* first, get an indirect block */ int indirect_block = s5_alloc_block(VNODE_TO_S5FS(v)); if (indirect_block == -ENOSPC){ dbg(DBG_S5FS, "couldn't alloc a new block\n"); return -ENOSPC; } KASSERT(indirect_block > 0 && "forgot to handle an error case"); /* then, zero it */ pframe_t *ind_page; mmobj_t *mmo = S5FS_TO_VMOBJ(VNODE_TO_S5FS(v)); int get_res = pframe_get(mmo, indirect_block, &ind_page); if (get_res < 0){ return get_res; } memcpy(ind_page->pf_addr, zero_array, BLOCK_SIZE); int dirty_res = pframe_dirty(ind_page); if (dirty_res < 0){ return dirty_res; } /* finally, set this block to be the indirect block of the inode */ inode->s5_indirect_block = indirect_block; s5_dirty_inode(VNODE_TO_S5FS(v), inode); return 0; }
/* * Create a new directory entry in directory 'parent' with the given name, which * refers to the same file as 'child'. * * When this function returns, the inode refcount on the file that was linked to * should be incremented. * * Remember to increment the ref counts appropriately * * You probably want to use s5_find_dirent(), s5_write_file(), and s5_dirty_inode(). */ int s5_link(vnode_t *parent, vnode_t *child, const char *name, size_t namelen) { KASSERT(parent->vn_ops->mkdir != NULL); KASSERT(s5_find_dirent(parent, name, namelen) < 0 && "file exists\n"); int init_refcount = VNODE_TO_S5INODE(child)->s5_linkcount; s5_dirent_t d; d.s5d_inode = VNODE_TO_S5INODE(child)->s5_number; memcpy(d.s5d_name, name, namelen); d.s5d_name[namelen] = '\0'; off_t write_offset = parent->vn_len; /*find_empty_dirent(parent);*/ if (write_offset < 0){ dbg(DBG_S5FS, "error finding dirent to write to\n"); } int res = s5_write_file(parent, write_offset, (char *) &d, sizeof(s5_dirent_t)); if (res < 0){ dbg(DBG_S5FS, "error writing child entry in parent\n"); return res; } s5_dirty_inode(VNODE_TO_S5FS(parent), VNODE_TO_S5INODE(parent)); if (parent != child){ dbg(DBG_S5FS, "incrementing link count on inode %d from %d to %d\n", VNODE_TO_S5INODE(child)->s5_number, VNODE_TO_S5INODE(child)->s5_linkcount, VNODE_TO_S5INODE(child)->s5_linkcount + 1); VNODE_TO_S5INODE(child)->s5_linkcount++; s5_dirty_inode(VNODE_TO_S5FS(child), VNODE_TO_S5INODE(child)); KASSERT(VNODE_TO_S5INODE(child)->s5_linkcount == init_refcount + 1 && "link count not incremented properly"); } return 0; }
/* * See the comment in vnode.h for what is expected of this function. * * This function is similar to s5fs_create, but it creates a special * file specified by 'devid'. * * You probably want to use s5_alloc_inode, s5_link(), vget(), and vput(). */ static int s5fs_mknod(vnode_t *dir, const char *name, size_t namelen, int mode, devid_t devid) { KASSERT(namelen < NAME_LEN); kmutex_lock(&dir->vn_mutex); fs_t *fs = VNODE_TO_S5FS(dir)->s5f_fs; int ino; if (S_ISCHR(mode)){ ino = s5_alloc_inode(fs, S5_TYPE_CHR, devid); } else if (S_ISBLK(mode)){ ino = s5_alloc_inode(fs, S5_TYPE_BLK, devid); } else { panic("invalid mode"); } if (ino < 0){ dbg(DBG_S5FS, "unable to alloc a new inode\n"); kmutex_unlock(&dir->vn_mutex); return ino; } vnode_t *child = vget(fs, ino); kmutex_lock(&child->vn_mutex); /* make sure the state of the new vnode is correct */ assert_new_vnode_state(child, ino, S_ISCHR(mode) ? S5_TYPE_CHR : S5_TYPE_BLK, devid); int link_res = s5_link(dir, child, name, namelen); if (link_res < 0){ dbg(DBG_S5FS, "error creating entry for new directory in parent dir\n"); vput(child); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&dir->vn_mutex); /*s5_free_inode(child);*/ return link_res; } vput(child); KASSERT(child->vn_refcount == 0); KASSERT(VNODE_TO_S5INODE(child)->s5_linkcount == 1); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&dir->vn_mutex); return 0; }
/* * s5fs_cleanpage: * Write the contents of the page-aligned and page-sized * buffer pointed to by 'pagebuf' to the page of 'vnode' * containing 'offset'. * param *vnode: the pointer to the vnode object * param offset: the arbitrary offset of that file (bytes offset) * param pagebuf: the source buffer * return: number of bytes that have been written, or negative number for errors */ static int s5fs_cleanpage(vnode_t *vnode, off_t offset, void *pagebuf) { dbg(DBG_S5FS, "{\n"); KASSERT(vnode != NULL); KASSERT(pagebuf != NULL); /* Alloc */ int disk_number = 0; if ((disk_number = s5_seek_to_block(vnode, offset, 0)) < 0) { return disk_number;/* error */ } /* If the disk_number is zero, ignore pagebuf */ int ret = VNODE_TO_S5FS(vnode)->s5f_bdev->bd_ops->write_block( VNODE_TO_S5FS(vnode)->s5f_bdev, pagebuf, disk_number, 1); dbg(DBG_S5FS, "}\n"); return ret; }
/* * Return the number of blocks that this inode has allocated on disk. * This should include the indirect block, but not include sparse * blocks. * * This is only used by s5fs_stat(). * * You'll probably want to use pframe_get(). */ int s5_inode_blocks(vnode_t *vnode) { /* NOT_YET_IMPLEMENTED("S5FS: s5_inode_blocks"); * return -1; */ s5_inode_t* inode = VNODE_TO_S5INODE(vnode); int max_blocks = S5_DATA_BLOCK(vnode->vn_len); int count = 0; int block_index = 0; while(block_index < max_blocks) { if(block_index < S5_NDIRECT_BLOCKS) { if(inode->s5_direct_blocks[block_index] != 0) { /* Not a sparse block */ count++; } } else { /* Indirect block */ if(inode->s5_indirect_block == 0) { /* No indirect blocks */ /* Do Nothing */ } else { pframe_t* pf; pframe_get(S5FS_TO_VMOBJ(VNODE_TO_S5FS(vnode)),inode->s5_indirect_block,&pf); pframe_pin(pf); uint32_t indirect_map = block_index - S5_NDIRECT_BLOCKS; uint32_t* block_array = (uint32_t*)pf->pf_addr; if(block_array[indirect_map] != 0) { count++; } pframe_unpin(pf); } } block_index++; } return count; }
/* * s5_link: * Create a new directory entry in directory 'parent' with the given name, which * refers to the same file as 'child'. * param *parent: the pointer to the parent vnode object of the vnode child * param *child: the pointer to the child vnode object * param *name: name string * param namelen: the length of the name * return: bytes that has been added to the parent's block, or negative number * on failure */ int s5_link(vnode_t *parent, vnode_t *child, const char *name, size_t namelen) { dbg(DBG_S5FS, "{\n"); KASSERT(parent != NULL); KASSERT(child != NULL); KASSERT(name != NULL); KASSERT(S_ISDIR(parent->vn_mode)); KASSERT((uint32_t)parent->vn_len == VNODE_TO_S5INODE(parent)->s5_size); KASSERT((uint32_t)child->vn_len == VNODE_TO_S5INODE(child)->s5_size); int exist = s5_find_dirent(parent, name, namelen); if (exist >= 0) { return -1; } else { s5_inode_t* child_inode = VNODE_TO_S5INODE(child); ino_t child_inode_num = child_inode->s5_number; KASSERT(child_inode_num == child->vn_vno); s5_dirent_t dirent; memcpy(dirent.s5d_name, name, S5_NAME_LEN); KASSERT(dirent.s5d_name[namelen] == '\0'); dirent.s5d_inode = child_inode_num; s5_inode_t* inode_parent = VNODE_TO_S5INODE(parent); KASSERT((uint32_t)parent->vn_len == inode_parent->s5_size); int bytes = s5_write_file(parent, inode_parent->s5_size, (char*)&dirent, sizeof(s5_dirent_t)); if (bytes == sizeof(s5_dirent_t)) { /* If not '.' or '..', we need to add the link */ if (!name_match(name, ".", 1)) { child_inode->s5_linkcount++; s5_dirty_inode(VNODE_TO_S5FS(child), child_inode); } dbg(DBG_S5FS, "}\n"); return 0; } return bytes; } }
/* * See the comment in vnode.h for what is expected of this function. * * When this function returns, the inode refcount of the file should be 2 * and the vnode refcount should be 1. * * You probably want to use s5_alloc_inode(), s5_link(), and vget(). */ static int s5fs_create(vnode_t *dir, const char *name, size_t namelen, vnode_t **result) { KASSERT(namelen < NAME_LEN); kmutex_lock(&dir->vn_mutex); fs_t *fs = VNODE_TO_S5FS(dir)->s5f_fs; int ino = s5_alloc_inode(fs, S5_TYPE_DATA, NULL); if (ino < 0){ dbg(DBG_S5FS, "unable to alloc a new inode\n"); kmutex_unlock(&dir->vn_mutex); return ino; } vnode_t *child = vget(fs, ino); kmutex_lock(&child->vn_mutex); /* make sure the state of the new vnode is correct */ assert_new_vnode_state(child, ino, S5_TYPE_DATA, 0); int link_res = s5_link(dir, child, name, namelen); if (link_res < 0){ dbg(DBG_S5FS, "error creating entry for new directory in parent dir\n"); vput(child); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&dir->vn_mutex); /*s5_free_inode(child);*/ return link_res; } KASSERT(child->vn_refcount == 1); KASSERT(VNODE_TO_S5INODE(child)->s5_linkcount == 2); *result = child; kmutex_unlock(&child->vn_mutex); kmutex_unlock(&dir->vn_mutex); return 0; }
/* * s5fs_delete_vnode: * s5fs_delete_vnode is called by vput when the * specified vnode_t no longer needs to exist * param *vnode: the pointer to the vnode object */ static void s5fs_delete_vnode(vnode_t *vnode) { dbg(DBG_S5FS, "{\n"); KASSERT(vnode != NULL); KASSERT(vnode->vn_fs != NULL); /* Lock */ kmutex_lock(&vnode->vn_mutex); pframe_t* page = NULL; int ret = pframe_get(S5FS_TO_VMOBJ(FS_TO_S5FS(vnode->vn_fs)), S5_INODE_BLOCK(vnode->vn_vno), &page); KASSERT(ret == 0); KASSERT(page != NULL); s5_inode_t* inode = ((s5_inode_t*)page->pf_addr) + S5_INODE_OFFSET(vnode->vn_vno); inode->s5_linkcount--; s5_dirty_inode(VNODE_TO_S5FS(vnode), inode); KASSERT(VNODE_TO_S5INODE(vnode) == inode); KASSERT(inode->s5_linkcount >= 0); if (inode->s5_linkcount== 0) { s5_free_inode(vnode); } pframe_unpin(page); /* Unlock */ kmutex_unlock(&vnode->vn_mutex); dbg(DBG_S5FS, "}\n"); }
/* * See the comment in vnode.h for what is expected of this function. * * You probably want to use s5_find_dirent() and vget(). */ int s5fs_lookup(vnode_t *base, const char *name, size_t namelen, vnode_t **result) { kmutex_lock(&base->vn_mutex); int ino = s5_find_dirent(base, name, namelen); if (ino == -ENOENT){ kmutex_unlock(&base->vn_mutex); return -ENOENT; } KASSERT(ino >= 0 && "forgot an error case\n"); vnode_t *child = vget(VNODE_TO_S5FS(base)->s5f_fs, ino); KASSERT(child != NULL); *result = child; kmutex_unlock(&base->vn_mutex); return 0; }
/* * Return the number of blocks that this inode has allocated on disk. * This should include the indirect block, but not include sparse * blocks. * * This is only used by s5fs_stat(). * * You'll probably want to use pframe_get(). */ int s5_inode_blocks(vnode_t *vnode) { s5_inode_t *inode = VNODE_TO_S5INODE(vnode); int allocated_blocks = 0; int i; for (i = 0; i < S5_NDIRECT_BLOCKS; i++){ if (inode->s5_direct_blocks[i] != 0){ allocated_blocks++; } } if (inode->s5_indirect_block == 0){ return allocated_blocks; } /* count the indirect block as an allocated block */ allocated_blocks++; pframe_t *p; mmobj_t *mmobj = S5FS_TO_VMOBJ(VNODE_TO_S5FS(vnode)); int get_res = pframe_get(mmobj, inode->s5_indirect_block, &p); if (get_res < 0){ return get_res; } int j; for (j = 0; j < S5_NDIRECT_BLOCKS; j++){ if (((int *)p->pf_addr)[j] != 0){ allocated_blocks++; } } return allocated_blocks; }
/* * Create a new directory entry in directory 'parent' with the given name, which * refers to the same file as 'child'. * * When this function returns, the inode refcount on the file that was linked to * should be incremented. * * Remember to incrament the ref counts appropriately * * You probably want to use s5_find_dirent(), s5_write_file(), and s5_dirty_inode(). */ int s5_link(vnode_t *parent, vnode_t *child, const char *name, size_t namelen) { /* NOT_YET_IMPLEMENTED("S5FS: s5_link"); * return -1; */ dbg_print("s5_link: Linking!\n"); s5_inode_t* child_inode = VNODE_TO_S5INODE(child); if(s5_find_dirent(parent,name,namelen) >= 0) { /* Directory already exists */ dbg_print("s5_link: Directory Already Exists, Name: %s\n",name); return -1; } else { s5_dirent_t* new_dir = kmalloc(sizeof(s5_dirent_t)); /* memset(new_dir, 0, sizeof(s5_dirent_t)); */ new_dir->s5d_inode = child_inode->s5_number; /* Copy Over name for Directory */ memcpy(new_dir->s5d_name,name,namelen); new_dir->s5d_name[namelen] = '\0'; int write_dir = s5_write_file(parent,parent->vn_len,(char*)new_dir,sizeof(s5_dirent_t)); KASSERT(write_dir == sizeof(s5_dirent_t)); /* Increment indoe refcount */ dbg_print("s5_link: Incrementing Linkcount for Inode: %s\n",name); child_inode->s5_linkcount++; dbg_print("s5_link: Linkcount for Inode: %s is: %i\n",name,child_inode->s5_linkcount); s5_dirty_inode(VNODE_TO_S5FS(parent),child_inode); kfree(new_dir); return 0; } }
/* * s5_free_inode: * Free an inode by freeing its disk blocks and putting it back on the * inode free list. * param *vnode: the pointer to the vnode object */ void s5_free_inode(vnode_t *vnode) { uint32_t i = 0; s5_inode_t *inode = VNODE_TO_S5INODE(vnode); s5fs_t *fs = VNODE_TO_S5FS(vnode); KASSERT((S5_TYPE_DATA == inode->s5_type) || (S5_TYPE_DIR == inode->s5_type) || (S5_TYPE_CHR == inode->s5_type) || (S5_TYPE_BLK == inode->s5_type)); /* free any direct blocks */ for (i = 0; i < S5_NDIRECT_BLOCKS; ++i) { if (inode->s5_direct_blocks[i]) { dprintf("freeing block %d\n", inode->s5_direct_blocks[i]); s5_free_block(fs, inode->s5_direct_blocks[i]); s5_dirty_inode(fs, inode); inode->s5_direct_blocks[i] = 0; } } if (((S5_TYPE_DATA == inode->s5_type) || (S5_TYPE_DIR == inode->s5_type)) && inode->s5_indirect_block) { pframe_t *ibp; uint32_t *b; pframe_get(S5FS_TO_VMOBJ(fs), (unsigned)inode->s5_indirect_block, &ibp); KASSERT(ibp && "because never fails for block_device " "vm_objects"); pframe_pin(ibp); b = (uint32_t *)(ibp->pf_addr); for (i = 0; i < S5_NIDIRECT_BLOCKS; ++i) { KASSERT(b[i] != inode->s5_indirect_block); if (b[i]) s5_free_block(fs, b[i]); } pframe_unpin(ibp); s5_free_block(fs, inode->s5_indirect_block); } inode->s5_indirect_block = 0; inode->s5_type = S5_TYPE_FREE; s5_dirty_inode(fs, inode); lock_s5(fs); inode->s5_next_free = fs->s5f_super->s5s_free_inode; fs->s5f_super->s5s_free_inode = inode->s5_number; unlock_s5(fs); s5_dirty_inode(fs, inode); s5_dirty_super(fs); }
/* * s5_seek_to_block: * Return the disk-block number for the given seek pointer (aka file * position). * param vnode: pointer to vnode * param seekptr: seek offset * alloc: allocate new page or not * return: the block number, or negative number on failure */ int s5_seek_to_block(vnode_t *vnode, off_t seekptr, int alloc) { KASSERT(vnode != 0); KASSERT(alloc == 1 || alloc == 0); KASSERT(seekptr >= 0); s5_inode_t* inode = VNODE_TO_S5INODE(vnode); uint32_t block_number = S5_DATA_BLOCK(seekptr); /* check if the block_number is valid */ if (block_number >= S5_NIDIRECT_BLOCKS + S5_NDIRECT_BLOCKS) { return -1; } if (block_number < S5_NDIRECT_BLOCKS) { /* sparse block */ if (inode->s5_direct_blocks[block_number] == 0) { /* alloc is zero, simply return */ if (alloc == 0) { return 0; } else { /* alloc a new disk block */ int block_num = s5_alloc_block(VNODE_TO_S5FS(vnode)); if (block_num < 0) { /* error */ return block_num; } else { /* add the new block to inode */ inode->s5_direct_blocks[block_number] = block_num; /* dirty the inode */ s5_dirty_inode((VNODE_TO_S5FS(vnode)), inode); return block_num; } } } else /* already there*/ { return inode->s5_direct_blocks[block_number]; } } else { /* indirect blocks */ /* if the indirect block is zero, alloc it firstly */ pframe_t* page = NULL; if (inode->s5_indirect_block == 0) { int block_num = 0; if ((block_num = s5_alloc_block(FS_TO_S5FS(vnode->vn_fs))) < 0) { return block_num; } else { int ret = pframe_get(S5FS_TO_VMOBJ(FS_TO_S5FS(vnode->vn_fs)), block_num, &page); if (ret < 0 || page == NULL) { return ret; } else { pframe_pin(page); memset(page->pf_addr, 0, S5_BLOCK_SIZE); pframe_dirty(page); pframe_unpin(page); inode->s5_indirect_block = block_num; s5_dirty_inode(FS_TO_S5FS(vnode->vn_fs), inode); } } } else { int ret = pframe_get(S5FS_TO_VMOBJ(FS_TO_S5FS(vnode->vn_fs)), inode->s5_indirect_block, &page); if (ret < 0 || page == NULL) return ret; } KASSERT(page != NULL); pframe_pin(page); int off = block_number - S5_NDIRECT_BLOCKS; int32_t* addr = ((int32_t*)page->pf_addr) + off; pframe_unpin(page); if (*addr == 0) { if (alloc == 0) { return 0; } else { int block_num = s5_alloc_block(VNODE_TO_S5FS(vnode)); if (block_num < 0) { return block_num; } else { pframe_pin(page); *addr = block_num; pframe_dirty(page); pframe_unpin(page); return block_num; } } } else { /* already there, return */ return *addr; } } }
/* * s5_write_file: * write len bytes to the given inode, starting at seek bytes from the * beginning of the inode. On success, * param *vnode: the pointer to the vnode object * param seek: the seek position * param *bytes: the source buffer * param len: the length of the source buffer in bytes * return: the number of bytes actually written; on failure, return -errno. */ int s5_write_file(vnode_t *vnode, off_t seek, const char *bytes, size_t len) { dbg(DBG_S5FS, "{\n"); KASSERT(vnode != NULL); KASSERT(bytes != NULL); KASSERT(PAGE_SIZE == S5_BLOCK_SIZE); KASSERT((uint32_t)vnode->vn_len == VNODE_TO_S5INODE(vnode)->s5_size); off_t start_pos = seek; off_t start_block_offset = S5_DATA_OFFSET(start_pos); off_t end_pos = MIN(seek + len, S5_MAX_FILE_BLOCKS*PAGE_SIZE); off_t end_block_offset = S5_DATA_OFFSET(end_pos); int start_block = S5_DATA_BLOCK(start_pos); int end_block = S5_DATA_BLOCK(end_pos); int ret = 0; pframe_t* start = NULL; ret = pframe_get(&vnode->vn_mmobj, start_block, &start); if (ret < 0) { dbg(DBG_S5FS, "}(error code returend)\n"); return ret; } pframe_t* end = NULL; ret = pframe_get(&vnode->vn_mmobj, end_block, &end); if (ret < 0) { dbg(DBG_S5FS, "}(error code returend)\n"); return ret; } uint32_t num_bytes = 0; if (start == end) { pframe_pin(start); memcpy((char*)start->pf_addr + start_block_offset, bytes, len); KASSERT((char*)start->pf_addr + start_block_offset + len == (char*)start->pf_addr + end_block_offset); /* dirty the page */ pframe_dirty(start); pframe_unpin(start); num_bytes = len; s5_inode_t* inode = VNODE_TO_S5INODE(vnode ); inode->s5_size = MAX(end_pos, vnode->vn_len); vnode->vn_len = inode->s5_size; s5_dirty_inode(VNODE_TO_S5FS(vnode),inode); } else { /* copy the start block */ pframe_pin(start); memcpy((char*)start->pf_addr + start_block_offset, bytes, S5_BLOCK_SIZE - start_block_offset); bytes += S5_BLOCK_SIZE - start_block_offset; num_bytes += S5_BLOCK_SIZE - start_block_offset; pframe_dirty(start); pframe_unpin(start); s5_inode_t* inode = VNODE_TO_S5INODE(vnode ); inode->s5_size = MAX(start_pos + num_bytes, (uint32_t)vnode->vn_len); vnode->vn_len = inode->s5_size; while (1) { pframe_t* tmp; int block_number = S5_DATA_BLOCK(start_pos + num_bytes ); ret = pframe_get(&vnode->vn_mmobj, block_number, &tmp); if (tmp == NULL) { VNODE_TO_S5INODE(vnode)->s5_size = MAX(start_pos + num_bytes, (uint32_t)vnode->vn_len); vnode->vn_len = VNODE_TO_S5INODE(vnode)->s5_size; s5_dirty_inode(VNODE_TO_S5FS(vnode),inode); return ret; } if (tmp == end) { break; } pframe_pin(tmp); memcpy(tmp->pf_addr, bytes, S5_BLOCK_SIZE); pframe_dirty(tmp); pframe_unpin(tmp); bytes += S5_BLOCK_SIZE; num_bytes += S5_BLOCK_SIZE; } /* copy the last one */ pframe_pin(end); memcpy(end->pf_addr, bytes, len - num_bytes); num_bytes += len - num_bytes; /* len */ pframe_dirty(end); pframe_unpin(end); /* add the size */ inode->s5_size = MAX(end_pos, vnode->vn_len); s5_dirty_inode(VNODE_TO_S5FS(vnode),inode); vnode->vn_len = inode->s5_size; } KASSERT((uint32_t)vnode->vn_len == VNODE_TO_S5INODE(vnode)->s5_size); dbg(DBG_S5FS, "}\n"); return num_bytes; }
/* * Locate the directory entry in the given inode with the given name, * and delete it. If there is no entry with the given name, return * -ENOENT. * * In order to ensure that the directory entries are contiguous in the * directory file, you will need to move the last directory entry into * the remove dirent's place. * * When this function returns, the inode refcount on the removed file * should be decremented. * * It would be a nice extension to free blocks from the end of the * directory file which are no longer needed. * * Don't forget to dirty appropriate blocks! * * You probably want to use vget(), vput(), s5_read_file(), * s5_write_file(), and s5_dirty_inode(). */ int s5_remove_dirent(vnode_t *vnode, const char *name, size_t namelen) { static unsigned int dirent_size = sizeof(s5_dirent_t); KASSERT(vnode->vn_ops->mkdir != NULL); int deleted_ino; off_t dir_offset; int find_res = s5_find_dirent_helper(vnode, name, namelen, &dir_offset, &deleted_ino); if (find_res == -ENOENT){ dbg(DBG_S5FS, "couldn't find specified dirent\n"); return -ENOENT; } KASSERT(find_res == 0 && "you missed an error case"); KASSERT((unsigned) vnode->vn_len >= dir_offset + dirent_size); /* if the dirent to remove isn't the last dirent, we need to * copy the last dirent into its place */ if ((unsigned) vnode->vn_len > dir_offset + dirent_size){ KASSERT((unsigned) vnode->vn_len >= dir_offset + (2 * dirent_size)); s5_dirent_t to_move; int read_res = s5_read_file(vnode, vnode->vn_len - dirent_size, (char *) &to_move, dirent_size); if (read_res < 0){ dbg(DBG_S5FS, "error reading final dirent in directory\n"); return read_res; } int write_res = s5_write_file(vnode, dir_offset, (char *) &to_move, dirent_size); if (write_res < 0){ dbg(DBG_S5FS, "error overwriting dirent to remove with last dirent\n"); return write_res; } } s5fs_t *fs = VNODE_TO_S5FS(vnode); /* decrease the length of the directory file */ s5_inode_t *dir_inode = VNODE_TO_S5INODE(vnode); vnode->vn_len -= dirent_size; dir_inode->s5_size -= dirent_size; s5_dirty_inode(fs, dir_inode); /* decrement the linkcount on the unlinked file */ vnode_t *deleted_vnode = vget(fs->s5f_fs, deleted_ino); s5_inode_t *deleted_inode = VNODE_TO_S5INODE(deleted_vnode); deleted_inode->s5_linkcount--; KASSERT(deleted_inode->s5_linkcount >= 0 && "linkcount went below 0!"); s5_dirty_inode(fs, deleted_inode); vput(deleted_vnode); return 0; }
/* * Write len bytes to the given inode, starting at seek bytes from the * beginning of the inode. On success, return the number of bytes * actually written (which should be 'len', unless there's only enough * room for a partial write); on failure, return -errno. * * This function should allow writing to files or directories, treating * them identically. * * Writing to a sparse block of the file should cause that block to be * allocated. Writing past the end of the file should increase the size * of the file. Blocks between the end and where you start writing will * be sparse. * * Do not call s5_seek_to_block() directly from this function. You will * use the vnode's pframe functions, which will eventually result in a * call to s5_seek_to_block(). * * You will need pframe_dirty(), pframe_get(), memcpy(). */ int s5_write_file(vnode_t *vnode, off_t seek, const char *bytes, size_t len) { dbg_print("s5_write_file: Writing to File, Length: %i\n",len); uint32_t to_write = len; /* Block Number */ uint32_t block_index = S5_DATA_BLOCK(seek); if(block_index >= S5_MAX_FILE_BLOCKS) { dbg_print("s5_write_file: Exiting with Value: 0\n"); return 0; } /* Offset within block */ uint32_t block_offset = S5_DATA_OFFSET(seek); uint32_t remaining = S5_BLOCK_SIZE - block_offset; int total_written = 0; s5_inode_t* inode = VNODE_TO_S5INODE(vnode); s5fs_t* dir_fs = VNODE_TO_S5FS(vnode); if(seek >= vnode->vn_len) { /* End to Start of Writing should be written as sparse */ } while(to_write > 0) { pframe_t* pf; pframe_get(&(vnode->vn_mmobj),block_index,&pf); pframe_pin(pf); if(to_write <= remaining) { memcpy((char*)pf->pf_addr + block_offset,bytes + total_written,to_write); total_written += to_write; block_offset = 0; to_write = 0; } else { /* to_write > remaining */ memcpy((char*)pf->pf_addr + block_offset,bytes + total_written,remaining); total_written += remaining; block_offset = 0; to_write -= remaining; block_index++; remaining = S5_BLOCK_SIZE; if(block_index == S5_MAX_FILE_BLOCKS) { break; } } pframe_dirty(pf); pframe_unpin(pf); } if(seek + total_written > vnode->vn_len) { vnode->vn_len = seek + total_written; inode->s5_size = seek + total_written; } s5_dirty_inode(dir_fs,inode); return total_written; }
/* * Write len bytes to the given inode, starting at seek bytes from the * beginning of the inode. On success, return the number of bytes * actually written (which should be 'len', unless there's only enough * room for a partial write); on failure, return -errno. * * This function should allow writing to files or directories, treating * them identically. * * Writing to a sparse block of the file should cause that block to be * allocated. Writing past the end of the file should increase the size * of the file. Blocks between the end and where you start writing will * be sparse. * * Do not call s5_seek_to_block() directly from this function. You will * use the vnode's pframe functions, which will eventually result in a * call to s5_seek_to_block(). * * You will need pframe_dirty(), pframe_get(), memcpy(). */ int s5_write_file(vnode_t *vnode, off_t seek, const char *bytes, size_t len) { if (seek < 0){ dbg(DBG_S5FS, "invalid seek value\n"); return -EINVAL; } if (seek + len >= S5_MAX_FILE_SIZE){ len = S5_MAX_FILE_SIZE - seek - 1; } /* extend file size, if necessary */ uint32_t newlength = max(seek + len, vnode->vn_len); /* if (seek + len > (unsigned) vnode->vn_len){*/ /*vnode->vn_len = seek + len;*/ /*VNODE_TO_S5INODE(vnode)->s5_size = vnode->vn_len;*/ /*s5_dirty_inode(VNODE_TO_S5FS(vnode), VNODE_TO_S5INODE(vnode));*/ /*}*/ off_t start_pos = seek; /*off_t end_pos = min(seek + len, vnode->vn_len);*/ off_t end_pos = min(seek + len, newlength); unsigned int srcpos = 0; int get_res; int write_size; pframe_t *p; uint32_t err = 0; while (srcpos < len){ int data_offset = S5_DATA_OFFSET(seek); get_res = pframe_get(&vnode->vn_mmobj, S5_DATA_BLOCK(seek), &p); if (get_res < 0){ dbg(DBG_S5FS, "error getting page\n"); err = get_res; break; } write_size = min(PAGE_SIZE - data_offset, end_pos - seek); KASSERT(write_size >= 0 && "write size is negative"); memcpy((char *) p->pf_addr + data_offset, (void *) bytes, write_size); int dirty_res = pframe_dirty(p); if (dirty_res < 0){ err = dirty_res; break; } srcpos += write_size; seek += write_size; } if (seek > vnode->vn_len){ vnode->vn_len = seek; VNODE_TO_S5INODE(vnode)->s5_size = vnode->vn_len; s5_dirty_inode(VNODE_TO_S5FS(vnode), VNODE_TO_S5INODE(vnode)); } return err ? err : srcpos; }
/* * Return the disk-block number for the given seek pointer (aka file * position). * * If the seek pointer refers to a sparse block, and alloc is false, * then return 0. If the seek pointer refers to a sparse block, and * alloc is true, then allocate a new disk block (and make the inode * point to it) and return it. * * Be sure to handle indirect blocks! * * If there is an error, return -errno. * * You probably want to use pframe_get, pframe_pin, pframe_unpin, pframe_dirty. */ int s5_seek_to_block(vnode_t *vnode, off_t seekptr, int alloc) { int block_index = S5_DATA_BLOCK(seekptr); if ((unsigned) block_index >= S5_MAX_FILE_BLOCKS){ dbg(DBG_S5FS, "file too large"); return -EFBIG; } if (seekptr > vnode->vn_len && !alloc){ return 0; } s5_inode_t *inode = VNODE_TO_S5INODE(vnode); uint32_t block_num; if (block_index >= S5_NDIRECT_BLOCKS){ pframe_t *ind_page; mmobj_t *mmo = S5FS_TO_VMOBJ(VNODE_TO_S5FS(vnode)); if (inode->s5_indirect_block == 0){ if (!alloc){ return 0; } int alloc_res = alloc_indirect_block(vnode); if (alloc_res < 0){ dbg(DBG_S5FS, "error allocating indirect block\n"); return alloc_res; } } if (pframe_get(mmo, inode->s5_indirect_block, &ind_page) < 0){ panic("an indirect block is messed up\n"); } block_num = ((uint32_t *) ind_page->pf_addr)[block_index - S5_NDIRECT_BLOCKS]; /* case where we've found a sparse block and need to allocate*/ if (block_num == 0 && alloc){ pframe_pin(ind_page); int block_num = s5_alloc_block(VNODE_TO_S5FS(vnode)); pframe_unpin(ind_page); if (block_num == -ENOSPC){ dbg(DBG_S5FS, "couldn't alloc a new block\n"); return -ENOSPC; } KASSERT(block_num > 0 && "forgot to handle an error case"); ((uint32_t *) ind_page->pf_addr)[block_index - S5_NDIRECT_BLOCKS] = block_num; int dirty_res = pframe_dirty(ind_page); if (dirty_res < 0){ return dirty_res; } } } else { block_num = inode->s5_direct_blocks[block_index]; /* case where we've found a sparse block and need to allocate*/ if (block_num == 0 && alloc){ int block_num = s5_alloc_block(VNODE_TO_S5FS(vnode)); if (block_num == -ENOSPC){ dbg(DBG_S5FS, "couldn't alloc a new block\n"); return -ENOSPC; } KASSERT(block_num > 0 && "forgot to handle an error case"); inode->s5_direct_blocks[block_index] = block_num; s5_dirty_inode(VNODE_TO_S5FS(vnode), inode); } } return block_num; }
/* * See the comment in vnode.h for what is expected of this function. * * You need to create the "." and ".." directory entries in the new * directory. These are simply links to the new directory and its * parent. * * When this function returns, the inode refcount on the parent should * be incremented, and the inode refcount on the new directory should be * 1. It might make more sense for the inode refcount on the new * directory to be 2 (since "." refers to it as well as its entry in the * parent dir), but convention is that empty directories have only 1 * link. * * You probably want to use s5_alloc_inode, and s5_link(). * * Assert, a lot. */ static int s5fs_mkdir(vnode_t *dir, const char *name, size_t namelen) { static const char *dotstring = "."; static const char *dotdotstring = ".."; KASSERT(namelen < NAME_LEN); KASSERT(dir->vn_ops->mkdir != NULL); kmutex_lock(&dir->vn_mutex); fs_t *fs = VNODE_TO_S5FS(dir)->s5f_fs; int ino = s5_alloc_inode(fs, S5_TYPE_DIR, NULL); if (ino < 0){ dbg(DBG_S5FS, "unable to alloc a new inode\n"); kmutex_unlock(&dir->vn_mutex); return ino; } vnode_t *child = vget(fs, ino); kmutex_lock(&child->vn_mutex); /* make sure the state of the new vnode is correct */ assert_new_vnode_state(child, ino, S5_TYPE_DIR, 0); int link_res = s5_link(child, child, dotstring, 1); if (link_res < 0){ dbg(DBG_S5FS, "error creating entry for \'.\' in new directory\n"); /* TODO make sure we should be vputting */ /*s5_free_inode(child);*/ vput(child); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&dir->vn_mutex); return link_res; } KASSERT(VNODE_TO_S5INODE(child)->s5_linkcount == 1); link_res = s5_link(child, dir, dotdotstring, 2); if (link_res < 0){ dbg(DBG_S5FS, "error creating entry for \'..\' in new directory\n"); /*s5_free_inode(child);*/ vput(child); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&dir->vn_mutex); return link_res; } link_res = s5_link(dir, child, name, namelen); if (link_res < 0){ dbg(DBG_S5FS, "error creating entry for new directory in parent dir\n"); /*s5_free_inode(child);*/ vput(child); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&dir->vn_mutex); return link_res; } KASSERT(VNODE_TO_S5INODE(child)->s5_linkcount == 2); vput(child); KASSERT(child->vn_refcount - child->vn_nrespages == 0); kmutex_unlock(&child->vn_mutex); kmutex_unlock(&dir->vn_mutex); return 0; }
/* * s5fs_read_vnode: * s5fs_read_vnode will be passed a vnode_t*, which will have its vn_fs * and vn_vno fields initialized. * param *vnode: the pointer to the vnode object */ static void s5fs_read_vnode(vnode_t *vnode) { dbg(DBG_S5FS, "{\n"); KASSERT(vnode != NULL); KASSERT(vnode->vn_fs != NULL); kmutex_lock(&vnode->vn_mutex); pframe_t* page = NULL; int ret = pframe_get(S5FS_TO_VMOBJ(FS_TO_S5FS(vnode->vn_fs)), S5_INODE_BLOCK(vnode->vn_vno), &page); KASSERT(ret == 0); KASSERT(page != NULL); pframe_pin(page); s5_inode_t* inode = ((s5_inode_t*)page->pf_addr) + S5_INODE_OFFSET(vnode->vn_vno); inode->s5_linkcount++; s5_dirty_inode(VNODE_TO_S5FS(vnode), inode); vnode->vn_i = inode; vnode->vn_len = inode->s5_size; switch(inode->s5_type) { case S5_TYPE_DIR: { vnode->vn_mode = S_IFDIR; vnode->vn_ops = &s5fs_dir_vops; break; } case S5_TYPE_DATA: { vnode->vn_mode = S_IFREG; vnode->vn_ops = &s5fs_file_vops; break; } case S5_TYPE_CHR: { vnode->vn_mode = S_IFCHR; vnode->vn_ops = NULL; vnode->vn_devid = (devid_t)(inode->s5_indirect_block); vnode->vn_cdev = bytedev_lookup(vnode->vn_devid); break; } case S5_TYPE_BLK: { vnode->vn_mode = S_IFBLK; vnode->vn_ops = NULL; vnode->vn_devid = (devid_t)(inode->s5_indirect_block); vnode->vn_bdev = blockdev_lookup(vnode->vn_devid); break; } default: { panic("inode %d has unknown/invalid type %d!!\n", (int)vnode->vn_vno, (int)inode->s5_type); } } kmutex_unlock(&vnode->vn_mutex); dbg(DBG_S5FS, "}\n"); }
/* * s5_remove_dirent: * Locate the directory entry in the given inode with the given name, * and delete it. If there is no entry with the given name, return * -ENOENT. * param *vnode: the pointer to the vnode object * param *name: the name string * param namelen: the length of the name string * return: 0 on success and -ENOENT if there is no entry */ int s5_remove_dirent(vnode_t *vnode, const char *name, size_t namelen) { dbg(DBG_S5FS, "{\n"); KASSERT(vnode != NULL); KASSERT(name != NULL); KASSERT(namelen <= S5_NAME_LEN - 1); KASSERT((uint32_t)vnode->vn_len == VNODE_TO_S5INODE(vnode)->s5_size); KASSERT(vnode->vn_len%sizeof(s5_dirent_t) == 0); s5_dirent_t tmp; off_t offset = 0; while (1) { int bytes = s5_read_file(vnode, offset, (char*)&tmp, sizeof(s5_dirent_t)); if (bytes < 0) { return bytes; } else if (bytes == 0) { return -ENOENT; } else if (bytes != sizeof(s5_dirent_t)) { return -1; } else { if (name_match(tmp.s5d_name, name, namelen)) { if (offset + (off_t)sizeof(s5_dirent_t) < vnode->vn_len) { s5_dirent_t last_dirent; int bytes2 = s5_read_file(vnode, vnode->vn_len - sizeof(s5_dirent_t), (char*)&last_dirent, sizeof(s5_dirent_t)); if (bytes2 < 0) { return bytes2; } else if (bytes2 != sizeof(s5_dirent_t)) { return -1; } else { bytes2 = s5_write_file(vnode, offset, (char*)&last_dirent, sizeof(s5_dirent_t)); if (bytes2 < 0) { return bytes2; } } } vnode->vn_len -= sizeof(s5_dirent_t); s5_inode_t* inode = VNODE_TO_S5INODE(vnode ); inode->s5_size = vnode->vn_len; s5_dirty_inode(VNODE_TO_S5FS(vnode),inode); vnode_t* vn = vget(vnode->vn_fs,tmp.s5d_inode); KASSERT(vn); s5_inode_t* remove_inode = VNODE_TO_S5INODE(vn); remove_inode->s5_linkcount--; s5_dirty_inode(FS_TO_S5FS(vn->vn_fs), remove_inode); vput(vn); return 0; } else { offset+= sizeof(s5_dirent_t); } } } KASSERT(0); }
/* * Return the disk-block number for the given seek pointer (aka file * position). * * If the seek pointer refers to a sparse block, and alloc is false, * then return 0. If the seek pointer refers to a sparse block, and * alloc is true, then allocate a new disk block (and make the inode * point to it) and return it. * * Be sure to handle indirect blocks! * * If there is an error, return -errno. * * You probably want to use pframe_get, pframe_pin, pframe_unpin, pframe_dirty. */ int s5_seek_to_block(vnode_t *vnode, off_t seekptr, int alloc) { /* CASE BLOCK TYPE ALLOC Direct Sparse Indirect Sparse WAT DO? * 1 (BLOCK > Total Blocks) Return Error * 2 DIRECT FALSE TRUE N/A return block from s5_direct_blocks * 3 DIRECT TRUE TRUE N/A allocate new block and point inode (also memcpy) * 4 DIRECT TRUE FALSE N/A return block from s5_direct_blocks * 5 INDIRECT FALSE TRUE TRUE return 0 * 7 INDIRECT FALSE FALSE FALSE Find block we want * 8 INDIRECT TRUE FALSE FALSE Find block we want * 9 INDIRECT TRUE TRUE FALSE allocate new block, memcpy to 0, set data address in indirect * * INDIRECT TRUE N/A TRUE allocate new block, pframe_get on inode->indirect_block */ dbg_print("s5_seek_to_block: Entering Function, seekptr: %i, alloc: %i\n",seekptr,alloc); s5fs_t* vnode_s5fs = VNODE_TO_S5FS(vnode); s5_inode_t* vnode_inode = VNODE_TO_S5INODE(vnode); struct mmobj* vnode_vmobj = S5FS_TO_VMOBJ(vnode_s5fs); uint32_t data_block = S5_DATA_BLOCK(seekptr); pframe_t* pf; dbg_print("s5_seek_to_block: a\n"); if(data_block > S5_MAX_FILE_BLOCKS) { /* Case 1 */ dbg_print("s5_seek_to_block: Case 1\n"); return 0; } if(data_block < S5_NDIRECT_BLOCKS) { dbg_print("s5_seek_to_block: b\n"); /* Direct Block */ if(!alloc) { /* ALLOC FALSE */ /* CASE 2 */ dbg_print("s5_seek_to_block: c\n"); dbg_print("s5_seek_to_block: Case 2\n"); return vnode_inode->s5_direct_blocks[data_block]; } else { /* ALLOC TRUE */ dbg_print("s5_seek_to_block: d\n"); if(vnode_inode->s5_direct_blocks[data_block] == 0) { /* Sparse Block */ /* CASE 3 */ dbg_print("s5_seek_to_block: e\n"); pframe_get(vnode_vmobj,data_block,&pf); pframe_pin(pf); int block_alloc = s5_alloc_block(vnode_s5fs); dbg_print("s5_seek_to_block: f\n"); if(block_alloc == -ENOSPC) { /* Allocation Failure */ dbg_print("s5_seek_to_block: g\n"); pframe_unpin(pf); dbg_print("s5_seek_to_block: Allocation Failure #1\n"); return -ENOSPC; } else { /* Success in Allocation, Connect Inode and Dirty */ dbg_print("s5_seek_to_block: h\n"); vnode_inode->s5_direct_blocks[data_block] = block_alloc; /* memset(pf->pf_addr, 0, PAGE_SIZE); */ pframe_dirty(pf); s5_dirty_inode(vnode_s5fs,vnode_inode); pframe_unpin(pf); dbg_print("s5_seek_to_block: Case 3\n"); return block_alloc; } } else { /* Not Sparse Block */ /* CASE 4 */ dbg_print("s5_seek_to_block: Case 4\n"); return vnode_inode->s5_direct_blocks[data_block]; } } } else { /* Indirect Block */ dbg_print("s5_seek_to_block: i\n"); if(!alloc) { /* ALLOC FALSE */ dbg_print("s5_seek_to_block: j\n"); if(vnode_inode->s5_indirect_block == 0) { /* Sparse Block */ /* CASE 5 */ dbg_print("s5_seek_to_block: Case 5\n"); return 0; } else { /* Not Sparse Block */ /* CASE 7 */ dbg_print("s5_seek_to_block: Case 7\n"); return vnode_inode->s5_direct_blocks[data_block - S5_NDIRECT_BLOCKS]; } } else { /* ALLOC TRUE */ dbg_print("s5_seek_to_block: k\n"); if(vnode_inode->s5_indirect_block == 0) { /* Sparse Block */ /* CASE 5 */ dbg_print("s5_seek_to_block: l\n"); int indirect_alloc = s5_alloc_block(vnode_s5fs); if(indirect_alloc == -ENOSPC) { /* Allocation Failure */ dbg_print("s5_seek_to_block: Allocation Failure #2\n"); return -ENOSPC; } /* Success in Allocation, Connect Inode and Dirty */ dbg_print("s5_seek_to_block: m\n"); pframe_get(vnode_vmobj,vnode_inode->s5_indirect_block,&pf); pframe_pin(pf); /* memset(pf->pf_addr, 0, PAGE_SIZE); */ vnode_inode->s5_indirect_block = indirect_alloc; pframe_dirty(pf); s5_dirty_inode(vnode_s5fs,vnode_inode); dbg_print("s5_seek_to_block: n\n"); } else { /* Not Sparse Block */ dbg_print("s5_seek_to_block: o\n"); pframe_get(vnode_vmobj,vnode_inode->s5_indirect_block,&pf); pframe_pin(pf); } dbg_print("s5_seek_to_block: p\n"); uint32_t indirect_map = data_block - S5_NDIRECT_BLOCKS; uint32_t* block_array = (uint32_t*)pf->pf_addr; int direct_index = block_array[indirect_map]; if(direct_index == 0) { dbg_print("s5_seek_to_block: q\n"); direct_index = s5_alloc_block(vnode_s5fs); if(direct_index == -ENOSPC) { /* Allocation Failure */ dbg_print("s5_seek_to_block: Allocation Failure #3\n"); return -ENOSPC; } } dbg_print("s5_seek_to_block: rn"); block_array[indirect_map] = direct_index; pframe_dirty(pf); pframe_unpin(pf); dbg_print("s5_seek_to_block: Case 6\n"); return direct_index; } } /* NOT_YET_IMPLEMENTED("S5FS: s5_seek_to_block"); * return -1; */ }
/* * s5fs_rmdir: * s5fs_rmdir removes the directory called name from dir. the directory * to be removed must be empty (except for . and .. of course). * param *parent: the pointer to the parent dir of the name specified * param *name: name string * param namelen: the length of the name string * return: 0 on success; negative numbers on a variety of errors */ static int s5fs_rmdir(vnode_t *parent, const char *name, size_t namelen) { dbg(DBG_S5FS, "{\n"); KASSERT(parent != NULL); KASSERT(name != NULL); KASSERT(namelen <= NAME_LEN - 1); KASSERT((uint32_t)parent->vn_len == VNODE_TO_S5INODE(parent)->s5_size); kmutex_lock(&parent->vn_mutex); int inode_number = 0; if ((inode_number = s5_find_dirent(parent, name, namelen)) < 0) { kmutex_unlock(&parent->vn_mutex); /* Need vput? */ return inode_number; } /* May block here */ vnode_t* vn = vget(parent->vn_fs, inode_number); KASSERT(vn != NULL); if (!S_ISDIR(vn->vn_mode)) { /* May block here */ vput(vn); kmutex_unlock(&parent->vn_mutex); return -ENOTDIR; } /* Check empty */ if (VNODE_TO_S5INODE(vn)->s5_size > sizeof(dirent_t)*2) { vput(vn); kmutex_unlock(&parent->vn_mutex); return -ENOTEMPTY; } int ret; if ((ret = s5_remove_dirent(parent, name, namelen)) < 0) { /* May block here */ vput(vn); kmutex_unlock(&parent->vn_mutex); return ret; } /* Decrease the linkcount because .. is removed */ s5_inode_t* parent_inode = VNODE_TO_S5INODE(parent); parent_inode->s5_linkcount--; s5_dirty_inode(VNODE_TO_S5FS(parent), parent_inode); /* May block here */ vput(vn); kmutex_unlock(&parent->vn_mutex); dbg(DBG_S5FS, "}\n"); return ret; }