/* * Based on uio->uio_offset, get the name of the next directory entry, * if one exists. */ static int sfs_getdirentry(struct vnode *vv, struct uio *uio) { struct sfs_vnode *sv; struct sfs_dir tsd; int nentries, slot, err; assert(uio->uio_iovec.iov_ubase != NULL); assert(uio->uio_rw == UIO_READ); sv = vv->vn_data; if(sv->sv_i.sfi_type != SFS_TYPE_DIR) return ENOTDIR; nentries = sfs_dir_nentries(sv); if(nentries == 0) return ENOENT; /* Get slot of interest */ if((uio->uio_offset % sizeof(struct sfs_dir)) <= 4){ /* Use dir entry in which we are currently pointing to */ slot = uio->uio_offset/sizeof(struct sfs_dir); } else{ /* Use next dir entry */ slot = uio->uio_offset/sizeof(struct sfs_dir) + 1; } while(slot < nentries){ err = sfs_readdir(sv, &tsd, slot); if(err) return err; if(tsd.sfd_ino != SFS_NOINO) break; slot++; } if(slot >= nentries){ /* EOF - just return */ return 0; } /* Set things up and do the move */ tsd.sfd_name[sizeof(tsd.sfd_name)-1] = 0; uio->uio_offset = 0; err = uiomove(tsd.sfd_name, sizeof(tsd.sfd_name), uio); if(err) return err; /* Reset offset */ uio->uio_offset = ((slot + 1) * sizeof(struct sfs_dir)); return 0; }
/* * Read a single filename from a directory into a uio. * */ int sfs_getdirentry(struct vnode *v, struct uio *uio) { struct sfs_vnode *sv = v->vn_data; struct sfs_dir tsd; int nentries = sfs_dir_nentries(sv); int result; vfs_biglock_acquire(); while (uio->uio_offset <= nentries) { // Read directory entry result = sfs_readdir(sv, &tsd, uio->uio_offset); if (result) { vfs_biglock_release(); return result; } if (tsd.sfd_ino != SFS_NOINO) { break; } uio->uio_offset ++; } // If we still don't have a result, there must be nothing left if (tsd.sfd_ino == SFS_NOINO) { vfs_biglock_release(); return ENOMEM; } /* Ensure null termination, just in case */ tsd.sfd_name[sizeof(tsd.sfd_name)-1] = 0; // Copy results to uio result = uiomove(tsd.sfd_name, strlen(tsd.sfd_name), uio); if (result) { vfs_biglock_release(); return result; } uio->uio_offset ++; vfs_biglock_release(); return 0; }
static int sfs_dir_findname(struct sfs_vnode *sv, const char *name, u_int32_t *ino, int *slot, int *emptyslot) { struct sfs_dir tsd; int found = 0; int nentries = sfs_dir_nentries(sv); int i, result; /* For each slot... */ for (i=0; i<nentries; i++) { /* Read the entry from that slot */ result = sfs_readdir(sv, &tsd, i); if (result) { return result; } if (tsd.sfd_ino == SFS_NOINO) { /* Free slot - report it back if one was requested */ if (emptyslot != NULL) { *emptyslot = i; } } else { /* Ensure null termination, just in case */ tsd.sfd_name[sizeof(tsd.sfd_name)-1] = 0; if (!strcmp(tsd.sfd_name, name)) { /* Each name may legally appear only once... */ assert(found==0); found = 1; if (slot != NULL) { *slot = i; } if (ino != NULL) { *ino = tsd.sfd_ino; } } } } return found ? 0 : ENOENT; }
/* Setup . and .. in root directory if this is the first mount */ int sfs_setup_root(struct sfs_fs *sfs) { struct fs *fs = &sfs->sfs_absfs; struct vnode *root_vv; struct sfs_vnode *root_sv; int result; root_vv = sfs_getroot(fs); root_sv = root_vv->vn_data; if(sfs_dir_nentries(root_sv) != 0){ VOP_DECREF(root_vv); return 0; } result = sfs_dir_link(root_sv, ".", SFS_ROOT_LOCATION, NULL); if (result) { VOP_DECREF(root_vv); return result; } root_sv->sv_i.sfi_linkcount++; root_sv->sv_dirty = 1; result = sfs_dir_link(root_sv, "..", SFS_ROOT_LOCATION, NULL); if (result) { sfs_dir_unlink(root_sv, 0); /* Hope this doesn't fail?? */ root_sv->sv_i.sfi_linkcount--; root_sv->sv_dirty = 1; VOP_DECREF(root_vv); return result; } root_sv->sv_i.sfi_linkcount++; root_sv->sv_dirty = 1; /* Decrement reference that sfs_getroot incremented */ VOP_DECREF(root_vv); return 0; }
/* * Create a link in a directory to the specified inode by number, with * the specified name, and optionally hand back the slot. */ static int sfs_dir_link(struct sfs_vnode *sv, const char *name, uint32_t ino, int *slot) { int emptyslot = -1; int result; struct sfs_dir sd; /* Look up the name. We want to make sure it *doesn't* exist. */ result = sfs_dir_findname(sv, name, NULL, NULL, &emptyslot); if (result!=0 && result!=ENOENT) { return result; } if (result==0) { return EEXIST; } if (strlen(name)+1 > sizeof(sd.sfd_name)) { return ENAMETOOLONG; } /* If we didn't get an empty slot, add the entry at the end. */ if (emptyslot < 0) { emptyslot = sfs_dir_nentries(sv); } /* Set up the entry. */ bzero(&sd, sizeof(sd)); sd.sfd_ino = ino; strcpy(sd.sfd_name, name); /* Hand back the slot, if so requested. */ if (slot) { *slot = emptyslot; } /* Write the entry. */ return sfs_writedir(sv, &sd, emptyslot); }
int sfs_getdirentry(struct vnode *v_node, struct uio *user_io) { struct sfs_vnode *s_node = v_node->vn_data; struct sfs_dir sdir; int offset = user_io->uio_offset; vfs_biglock_acquire(); //acquire lock int nentries = sfs_dir_nentries(s_node); if (offset >= nentries) { vfs_biglock_release(); //release lock return ENOENT; //Error no entry } int result; while(1) { result = sfs_readdir(s_node, &sdir, user_io->uio_offset); if (result) { vfs_biglock_release(); //release lock return result; //return this result } if (sdir.sfd_ino == SFS_NOINO) { //if this sdir is the inode # for free dir entry offset++; if(offset >= nentries) { vfs_biglock_release(); //release lock return ENOENT; //error no entry } user_io->uio_offset++; continue; } break; } result = uiomove(sdir.sfd_name, strlen(sdir.sfd_name), user_io); //get the result user_io->uio_offset = offset + 1; //update uio offset vfs_biglock_release(); //release lock return 0; //and return }
static int sfs_rmdir(struct vnode *vv, const char *name){ struct sfs_vnode *sv = vv->vn_data; struct sfs_vnode *victim; struct sfs_dir tsd; int slot, slot2, nentries; int result; /* Look for the directory and fetch a vnode for it. */ result = sfs_lookonce(sv, name, &victim, &slot); if (result) { return result; } /* Make sure it's a directory and that only . and .. are left */ if(victim->sv_i.sfi_type != SFS_TYPE_DIR){ VOP_DECREF(&victim->sv_v); return ENOTDIR; } nentries = sfs_dir_nentries(victim); if(nentries != 2){ slot2 = 2; while(slot2 < nentries){ /* Ensure all other entries are blank */ result = sfs_readdir(victim, &tsd, slot2); if(result){ VOP_DECREF(&victim->sv_v); return result; } if(tsd.sfd_ino != SFS_NOINO){ VOP_DECREF(&victim->sv_v); return ENOTEMPTY; } slot2++; } } /* Get rid of . and .. (should always be in slot 0 and 1) */ result = sfs_dir_unlink(victim, 0); if(result){ VOP_DECREF(&victim->sv_v); return result; } assert(victim->sv_i.sfi_linkcount > 0); victim->sv_i.sfi_linkcount--; victim->sv_dirty = 1; result = sfs_dir_unlink(victim, 1); if(result){ VOP_DECREF(&victim->sv_v); return result; } assert(sv->sv_i.sfi_linkcount > 0); sv->sv_i.sfi_linkcount--; sv->sv_dirty = 1; /* Erase its directory entry. */ result = sfs_dir_unlink(sv, slot); if (result==0) { /* If we succeeded, decrement the link count. */ assert(victim->sv_i.sfi_linkcount > 0); victim->sv_i.sfi_linkcount--; victim->sv_dirty = 1; } /* Discard the reference that sfs_lookonce got us */ VOP_DECREF(&victim->sv_v); return result; }
/* * Get the full pathname for a file. This only needs to work on directories. * Since we don't support subdirectories, assume it's the root directory * and hand back the empty string. (The VFS layer takes care of the * device name, leading slash, etc.) */ static int sfs_namefile(struct vnode *vv, struct uio *uio) { /* * 1. All you really have is inode number of directory passed in. * 2. Get inode number of parent by reading directory slot 0 (..) * 3. loadvnode of parent * 4. Get our name by reading through parent directory for our inode * number * 5. Add this name to string * 6. Repeat 2 through 5 until we hit root * 7. uiomove * Deal with reference counters as you need to, as loadvnode * increments refcount on vnode that we load. */ struct sfs_vnode *sv = vv->vn_data; struct sfs_vnode *child_dir, *parent_dir; struct sfs_dir tsd; int err, nentries, slot; char to_add[SFS_NAMELEN + 1], pathname[(SFS_NAMELEN + 1) * SFS_DIR_DEPTH]; /* If we're root, do nothing */ if(sv->sv_ino == SFS_ROOT_LOCATION) return 0; child_dir = sv; VOP_INCREF(&child_dir->sv_v); while(1){ err = sfs_readdir(child_dir, &tsd, 1); if(err) return err; assert(!strcmp(tsd.sfd_name, "..")); err = sfs_loadvnode(child_dir->sv_v.vn_fs->fs_data, tsd.sfd_ino, SFS_TYPE_INVAL, &parent_dir); if(err) return err; nentries = sfs_dir_nentries(parent_dir); slot = 2; while (slot < nentries){ err = sfs_readdir(parent_dir, &tsd, slot); if(err) return err; if(tsd.sfd_ino == child_dir->sv_ino) break; slot++; } /* * Doesn't make sense if we don't find our directory listed in our * parent directory.. */ assert(slot < nentries); strcpy(to_add, tsd.sfd_name); strcat(to_add, "/"); strcat(to_add, pathname); strcpy(pathname, to_add); VOP_DECREF(&child_dir->sv_v); if(parent_dir->sv_ino == SFS_ROOT_LOCATION){ VOP_DECREF(&parent_dir->sv_v); break; } else child_dir = parent_dir; } err = uiomove(pathname, strlen(pathname) + 1, uio); if(err) return err; return 0; }
/* * Called for getdirentry() * * Locking: gets/releases vnode lock. * * Requires up to 4 buffers. */ static int sfs_getdirentry(struct vnode *v, struct uio *uio) { struct sfs_vnode *sv = v->vn_data; struct sfs_direntry tsd; off_t pos; int nentries; int result; KASSERT(uio->uio_offset >= 0); KASSERT(uio->uio_rw==UIO_READ); lock_acquire(sv->sv_lock); reserve_buffers(SFS_BLOCKSIZE); result = sfs_dinode_load(sv); if (result) { unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; } result = sfs_dir_nentries(sv, &nentries); if (result) { sfs_dinode_unload(sv); unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; } /* Use uio_offset as the slot index. */ pos = uio->uio_offset; while (1) { if (pos >= nentries) { /* EOF */ result = 0; break; } result = sfs_readdir(sv, pos, &tsd); if (result) { break; } pos++; if (tsd.sfd_ino == SFS_NOINO) { /* Blank entry */ continue; } /* Ensure null termination, just in case */ tsd.sfd_name[sizeof(tsd.sfd_name)-1] = 0; result = uiomove(tsd.sfd_name, strlen(tsd.sfd_name), uio); break; } sfs_dinode_unload(sv); unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); /* Update the offset the way we want it */ uio->uio_offset = pos; return result; }