/* * 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; */ }
/* Simply call s5_write_file. */ static int s5fs_write(vnode_t *vnode, off_t offset, const void *buf, size_t len) { kmutex_lock(&vnode->vn_mutex); int ret = s5_write_file(vnode, offset, buf, len); kmutex_unlock(&vnode->vn_mutex); return ret; }
/* * s5fs_write: * Simply call s5_write_file; should be in critical section * param *vnode: the pointer to the vnode object * param offset: the offset in the file where you want to write * param *buf: the source buffer * param len: the length of the buffer * return: just return the result of s5_write_file */ static int s5fs_write(vnode_t *vnode, off_t offset, const void *buf, size_t len) { dbg(DBG_S5FS, "{\n"); KASSERT(vnode != NULL); KASSERT(buf != NULL); int ret = 0; kmutex_lock(&vnode->vn_mutex); ret = s5_write_file(vnode, offset, buf, len); kmutex_unlock(&vnode->vn_mutex); dbg(DBG_S5FS, "}\n"); return ret; }
/* * 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; } }
/* * 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; }
/* * 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_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); }
/* * 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; }