/* * 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_lookup: * s5fs_lookup sets *result to the vnode in dir with the specified name. * param *base: the vnode object of the base directory * param *name: name string * param namelen: the length of the name * param **result: *result points to the vnode in dir with the specified name * return: 0 on success; negative number on a variety of errors */ int s5fs_lookup(vnode_t *base, const char *name, size_t namelen, vnode_t **result) { dbg(DBG_S5FS, "{\n"); KASSERT(base != NULL); KASSERT(name != NULL); KASSERT(namelen <= S5_NAME_LEN-1); kmutex_lock(&base->vn_mutex); int inode_number = 0; if ((inode_number = s5_find_dirent(base, name, namelen)) < 0) { kmutex_unlock(&base->vn_mutex); return inode_number; } /* May block here */ /* No modification, no need to lock */ *result = vget(base->vn_fs, inode_number); kmutex_unlock(&base->vn_mutex); 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; */ }
/* * 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; }
/* * 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; }
/* * 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; } }
/* * 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; }