/* * 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; }
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; }
/* * Process a directory. INO is the inode number; PARENTINO is the * parent's inode number; PATHSOFAR is the path to this directory. * * Recursively checks its subdirs. * * In the FUTURE we might want to improve the handling of crosslinked * directories so it picks the parent that the .. entry points to, * instead of the first entry we recursively find. Beware of course * that the .. entry might not point to anywhere valid at all... */ static int pass2_dir(uint32_t ino, uint32_t parentino, const char *pathsofar) { struct sfs_dinode sfi; struct sfs_dir *direntries; int *sortvector; uint32_t dirsize, ndirentries, maxdirentries, subdircount, i; int ichanged=0, dchanged=0, dotseen=0, dotdotseen=0; if (inode_visitdir(ino)) { /* crosslinked dir; tell parent to remove the entry */ return 1; } /* Load the inode. */ sfs_readinode(ino, &sfi); /* * Load the directory. If there is any leftover room in the * last block, allocate space for it in case we want to insert * entries. */ ndirentries = sfi.sfi_size/sizeof(struct sfs_dir); maxdirentries = SFS_ROUNDUP(ndirentries, SFS_BLOCKSIZE/sizeof(struct sfs_dir)); dirsize = maxdirentries * sizeof(struct sfs_dir); direntries = domalloc(dirsize); sortvector = domalloc(ndirentries * sizeof(int)); sfs_readdir(&sfi, direntries, ndirentries); for (i=ndirentries; i<maxdirentries; i++) { direntries[i].sfd_ino = SFS_NOINO; bzero(direntries[i].sfd_name, sizeof(direntries[i].sfd_name)); } /* * Sort by name and check for duplicate names. */ sfsdir_sort(direntries, ndirentries, sortvector); /* don't use ndirentries-1 here, in case ndirentries == 0 */ for (i=0; i+1<ndirentries; i++) { struct sfs_dir *d1 = &direntries[sortvector[i]]; struct sfs_dir *d2 = &direntries[sortvector[i+1]]; assert(d1 != d2); if (d1->sfd_ino == SFS_NOINO || d2->sfd_ino == SFS_NOINO) { /* sfsdir_sort puts these last */ continue; } if (!strcmp(d1->sfd_name, d2->sfd_name)) { if (d1->sfd_ino == d2->sfd_ino) { setbadness(EXIT_RECOV); warnx("Directory %s: Duplicate entries for " "%s (merged)", pathsofar, d1->sfd_name); d1->sfd_ino = SFS_NOINO; d1->sfd_name[0] = 0; } else { /* XXX: what if FSCK.n.m already exists? */ snprintf(d1->sfd_name, sizeof(d1->sfd_name), "FSCK.%lu.%lu", (unsigned long) d1->sfd_ino, (unsigned long) uniqueid()); setbadness(EXIT_RECOV); warnx("Directory %s: Duplicate names %s " "(one renamed: %s)", pathsofar, d2->sfd_name, d1->sfd_name); } dchanged = 1; } } /* * Look for the . and .. entries. */ for (i=0; i<ndirentries; i++) { if (!strcmp(direntries[i].sfd_name, ".")) { if (direntries[i].sfd_ino != ino) { setbadness(EXIT_RECOV); warnx("Directory %s: Incorrect `.' entry " "(fixed)", pathsofar); direntries[i].sfd_ino = ino; dchanged = 1; } /* duplicates are checked above -> only one . here */ assert(dotseen==0); dotseen = 1; } else if (!strcmp(direntries[i].sfd_name, "..")) { if (direntries[i].sfd_ino != parentino) { setbadness(EXIT_RECOV); warnx("Directory %s: Incorrect `..' entry " "(fixed)", pathsofar); direntries[i].sfd_ino = parentino; dchanged = 1; } /* duplicates are checked above -> only one .. here */ assert(dotdotseen==0); dotdotseen = 1; } } /* * If no . entry, try to insert one. */ if (!dotseen) { if (sfsdir_tryadd(direntries, ndirentries, ".", ino)==0) { setbadness(EXIT_RECOV); warnx("Directory %s: No `.' entry (added)", pathsofar); dchanged = 1; } else if (sfsdir_tryadd(direntries, maxdirentries, ".", ino)==0) { setbadness(EXIT_RECOV); warnx("Directory %s: No `.' entry (added)", pathsofar); ndirentries++; dchanged = 1; sfi.sfi_size += sizeof(struct sfs_dir); ichanged = 1; } else { setbadness(EXIT_UNRECOV); warnx("Directory %s: No `.' entry (NOT FIXED)", pathsofar); } } /* * If no .. entry, try to insert one. */ if (!dotdotseen) { if (sfsdir_tryadd(direntries, ndirentries, "..", parentino)==0) { setbadness(EXIT_RECOV); warnx("Directory %s: No `..' entry (added)", pathsofar); dchanged = 1; } else if (sfsdir_tryadd(direntries, maxdirentries, "..", parentino)==0) { setbadness(EXIT_RECOV); warnx("Directory %s: No `..' entry (added)", pathsofar); ndirentries++; dchanged = 1; sfi.sfi_size += sizeof(struct sfs_dir); ichanged = 1; } else { setbadness(EXIT_UNRECOV); warnx("Directory %s: No `..' entry (NOT FIXED)", pathsofar); } } /* * Now load each inode in the directory. * * For regular files, count the number of links we see; for * directories, recurse. Count the number of subdirs seen * so we can correct our own link count if necessary. */ subdircount=0; for (i=0; i<ndirentries; i++) { if (direntries[i].sfd_ino == SFS_NOINO) { /* nothing */ } else if (!strcmp(direntries[i].sfd_name, ".")) { /* nothing */ } else if (!strcmp(direntries[i].sfd_name, "..")) { /* nothing */ } else { char path[strlen(pathsofar)+SFS_NAMELEN+1]; struct sfs_dinode subsfi; sfs_readinode(direntries[i].sfd_ino, &subsfi); snprintf(path, sizeof(path), "%s/%s", pathsofar, direntries[i].sfd_name); switch (subsfi.sfi_type) { case SFS_TYPE_FILE: inode_addlink(direntries[i].sfd_ino); break; case SFS_TYPE_DIR: if (pass2_dir(direntries[i].sfd_ino, ino, path)) { setbadness(EXIT_RECOV); warnx("Directory %s: Crosslink to " "other directory (removed)", path); direntries[i].sfd_ino = SFS_NOINO; direntries[i].sfd_name[0] = 0; dchanged = 1; } else { subdircount++; } break; default: setbadness(EXIT_RECOV); warnx("Object %s: Invalid inode type " "(removed)", path); direntries[i].sfd_ino = SFS_NOINO; direntries[i].sfd_name[0] = 0; dchanged = 1; break; } } } /* * Fix up the link count if needed. */ if (sfi.sfi_linkcount != subdircount+2) { setbadness(EXIT_RECOV); warnx("Directory %s: Link count %lu should be %lu (fixed)", pathsofar, (unsigned long) sfi.sfi_linkcount, (unsigned long) subdircount+2); sfi.sfi_linkcount = subdircount+2; ichanged = 1; } /* * Write back anything that changed, clean up, and return. */ if (dchanged) { sfs_writedir(&sfi, direntries, ndirentries); } if (ichanged) { sfs_writeinode(ino, &sfi); } free(direntries); free(sortvector); 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; }
/* * Rename a file. * * Locking: * Locks sfs_renamelock. * Calls check_parent, which locks various directories one at a * time. * Locks the target vnodes and their parents in a complex fashion * (described in detail below) which is carefully arranged so * it won't deadlock with rmdir. Or at least I hope so. * Then unlocks everything. * * The rationale for all this is complex. See the comments below. * * Requires up to 7 buffers. */ static int sfs_rename(struct vnode *absdir1, const char *name1, struct vnode *absdir2, const char *name2) { struct sfs_fs *sfs = absdir1->vn_fs->fs_data; struct sfs_vnode *dir1 = absdir1->vn_data; struct sfs_vnode *dir2 = absdir2->vn_data; struct sfs_vnode *obj1=NULL, *obj2=NULL; struct sfs_dinode *dir1_inodeptr, *dir2_inodeptr; struct sfs_dinode *obj1_inodeptr, *obj2_inodeptr; int slot1=-1, slot2=-1; int result, result2; struct sfs_direntry sd; int found_dir1; /* make gcc happy */ obj2_inodeptr = NULL; /* The VFS layer is supposed to enforce this */ KASSERT(absdir1->vn_fs == absdir2->vn_fs); if (!strcmp(name1, ".") || !strcmp(name2, ".") || !strcmp(name1, "..") || !strcmp(name2, "..")) { return EINVAL; } if (strlen(name2)+1 > sizeof(sd.sfd_name)) { return ENAMETOOLONG; } /* * We only allow one rename to occur at a time. This appears * to be necessary to preserve the consistency of the * filesystem: once you do the parent check (that n1 is not an * ancestor of d2/n2) nothing may be allowed to happen that * might invalidate that result until all of the * rearrangements are complete. If other renames are allowed * to proceed, we'd need to lock every descendent of n1 to * make sure that some ancestor of d2/n2 doesn't get inserted * at some point deep down. This is impractical, so we use one * global lock. * * To prevent certain deadlocks while locking the vnodes we * need, the rename lock goes outside all the vnode locks. */ reserve_buffers(SFS_BLOCKSIZE); lock_acquire(sfs->sfs_renamelock); /* * Get the objects we're moving. * * Lock each directory temporarily. We'll check again later to * make sure they haven't disappeared and to find slots. */ lock_acquire(dir1->sv_lock); result = sfs_lookonce(dir1, name1, &obj1, NULL); lock_release(dir1->sv_lock); if (result) { goto out0; } lock_acquire(dir2->sv_lock); result = sfs_lookonce(dir2, name2, &obj2, NULL); lock_release(dir2->sv_lock); if (result && result != ENOENT) { goto out0; } if (result==ENOENT) { /* * sfs_lookonce returns a null vnode with ENOENT in * order to make our life easier. */ KASSERT(obj2==NULL); } /* * Prohibit the case where obj1 is a directory and it's a direct * ancestor in the tree of dir2 (or is the same as dir2). If * that were to be permitted, it'd create a detached chunk of * the directory tree, and we don't like that. * * If we see dir1 while checking up the tree, found_dir1 is * set to true. We use this info to choose the correct ordering * for locking dir1 and dir2. * * To prevent deadlocks, the parent check must be done without * holding locks on any other directories. */ result = check_parent(dir1, obj1, dir2, &found_dir1); if (result) { goto out0; } /* * Now check for cases where some of the four vnodes we have * are the same. * * These cases are, in the order they are handled below: * * dir1 == obj1 Already checked. * dir2 == obj2 Already checked. * dir2 == obj1 Already checked. * dir1 == obj2 Checked below. * dir1 == dir2 Legal. * obj1 == obj2 Legal, special handling. */ /* * A directory should have no entries for itself other than '.'. * Thus, since we explicitly reject '.' above, the names * within the directories should not refer to the directories * themselves. */ KASSERT(dir1 != obj1); KASSERT(dir2 != obj2); /* * The parent check should have caught this case. */ KASSERT(dir2 != obj1); /* * Check for dir1 == obj2. * * This is not necessarily wrong if obj1 is the last entry in * dir1 (this is essentially "mv ./foo/bar ./foo") but our * implementation doesn't tolerate it. Because we need to * unlink g2 before linking g1 in the new place, it will * always fail complaining that g2 (sv1) isn't empty. We could * just charge ahead and let this happen, but we'll get into * trouble with our locks if we do, so detect this as a * special case and return ENOTEMPTY. */ if (obj2==dir1) { result = ENOTEMPTY; goto out0; } /* * Now we can begin acquiring locks for real. * * If we saw dir1 while doing the parent check, it means * dir1 is higher in the tree than dir2. Thus, we should * lock dir1 before dir2. * * If on the other hand we didn't see dir1, either dir2 is * higher in the tree than dir1, in which case we should lock * dir2 first, or dir1 and dir2 are on disjoint branches of * the tree, in which case (because there's a single rename * lock for the whole fs) it doesn't matter what order we lock * in. * * If we lock dir1 first, we don't need to lock obj1 before * dir2, since (due to the parent check) obj1 cannot be an * ancestor of dir2. * * However, if we lock dir2 first, obj2 must be locked before * dir1, in case obj2 is an ancestor of dir1. (In this case we * will find that obj2 is not empty and cannot be removed, but * we must lock it before we can check that.) * * Thus we lock in this order: * * dir1 (if found_dir1) * dir2 * obj2 (if non-NULL) * dir1 (if !found_dir1) * obj1 * * Also, look out for the case where both dirs are the same. * (If this is true, found_dir1 will be set.) */ if (dir1==dir2) { /* This locks "both" dirs */ lock_acquire(dir1->sv_lock); KASSERT(found_dir1); } else { if (found_dir1) { lock_acquire(dir1->sv_lock); } lock_acquire(dir2->sv_lock); } /* * Now lock obj2. * * Note that we must redo the lookup and get a new obj2, as it * may have changed under us. Since we hold the rename lock * for the whole fs, the fs structure cannot have changed, so * we don't need to redo the parent check or any of the checks * for vnode aliasing with dir1 or dir2 above. Note however * that obj1 and obj2 may now be the same even if they weren't * before. */ KASSERT(lock_do_i_hold(dir2->sv_lock)); if (obj2) { VOP_DECREF(&obj2->sv_absvn); obj2 = NULL; } result = sfs_lookonce(dir2, name2, &obj2, &slot2); if (result==0) { KASSERT(obj2 != NULL); lock_acquire(obj2->sv_lock); result = sfs_dinode_load(obj2); if (result) { /* ENOENT would confuse us below; but it can't be */ KASSERT(result != ENOENT); lock_release(obj2->sv_lock); VOP_DECREF(&obj2->sv_absvn); /* continue to check below */ } else { obj2_inodeptr = sfs_dinode_map(obj2); } } else if (result==ENOENT) { /* * sfs_lookonce returns a null vnode and an empty slot * with ENOENT in order to make our life easier. */ KASSERT(obj2==NULL); KASSERT(slot2>=0); } if (!found_dir1) { lock_acquire(dir1->sv_lock); } /* Postpone this check to simplify the error cleanup. */ if (result != 0 && result != ENOENT) { goto out1; } /* * Now reload obj1. */ KASSERT(lock_do_i_hold(dir1->sv_lock)); VOP_DECREF(&obj1->sv_absvn); obj1 = NULL; result = sfs_lookonce(dir1, name1, &obj1, &slot1); if (result) { goto out1; } /* * POSIX mandates that if obj1==obj2, we succeed and nothing * happens. This is somewhat stupid if obj1==obj2 and dir1 != dir2, * but we'll go with POSIX anyway. */ if (obj1==obj2) { result = 0; VOP_DECREF(&obj1->sv_absvn); obj1 = NULL; goto out1; } lock_acquire(obj1->sv_lock); result = sfs_dinode_load(obj1); if (result) { lock_release(obj1->sv_lock); VOP_DECREF(&obj1->sv_absvn); obj1 = NULL; goto out1; } obj1_inodeptr = sfs_dinode_map(obj1); result = sfs_dinode_load(dir2); if (result) { goto out2; } dir2_inodeptr = sfs_dinode_map(dir2); result = sfs_dinode_load(dir1); if (result) { goto out3; } dir1_inodeptr = sfs_dinode_map(dir1); /* * One final piece of paranoia: make sure dir2 hasn't been rmdir'd. * (If dir1 was, the obj1 lookup above would have failed.) */ if (dir2_inodeptr->sfi_linkcount==0) { result = ENOENT; goto out4; } /* * Now we have all the locks we need and we can proceed with * the operation. */ /* At this point we should have valid slots in both dirs. */ KASSERT(slot1>=0); KASSERT(slot2>=0); if (obj2 != NULL) { /* * Target already exists. * Must be the same type (file or directory) as the source, * and if a directory, must be empty. Then unlink it. */ if (obj1_inodeptr->sfi_type == SFS_TYPE_DIR) { if (obj2_inodeptr->sfi_type != SFS_TYPE_DIR) { result = ENOTDIR; goto out4; } result = sfs_dir_checkempty(obj2); if (result) { goto out4; } /* Remove the name */ result = sfs_dir_unlink(dir2, slot2); if (result) { goto out4; } /* Dispose of the directory */ KASSERT(dir2_inodeptr->sfi_linkcount > 1); KASSERT(obj2_inodeptr->sfi_linkcount == 2); dir2_inodeptr->sfi_linkcount--; obj2_inodeptr->sfi_linkcount -= 2; sfs_dinode_mark_dirty(dir2); sfs_dinode_mark_dirty(obj2); /* ignore errors on this */ sfs_itrunc(obj2, 0); } else { KASSERT(obj1->sv_type == SFS_TYPE_FILE); if (obj2->sv_type != SFS_TYPE_FILE) { result = EISDIR; goto out4; } /* Remove the name */ result = sfs_dir_unlink(dir2, slot2); if (result) { goto out4; } /* Dispose of the file */ KASSERT(obj2_inodeptr->sfi_linkcount > 0); obj2_inodeptr->sfi_linkcount--; sfs_dinode_mark_dirty(obj2); } sfs_dinode_unload(obj2); lock_release(obj2->sv_lock); VOP_DECREF(&obj2->sv_absvn); obj2 = NULL; } /* * At this point the target should be nonexistent and we have * a slot in the target directory we can use. Create a link * there. Do it by hand instead of using sfs_dir_link to avoid * duplication of effort. */ KASSERT(obj2==NULL); bzero(&sd, sizeof(sd)); sd.sfd_ino = obj1->sv_ino; strcpy(sd.sfd_name, name2); result = sfs_writedir(dir2, slot2, &sd); if (result) { goto out4; } obj1_inodeptr->sfi_linkcount++; sfs_dinode_mark_dirty(obj1); if (obj1->sv_type == SFS_TYPE_DIR && dir1 != dir2) { /* Directory: reparent it */ result = sfs_readdir(obj1, DOTDOTSLOT, &sd); if (result) { goto recover1; } if (strcmp(sd.sfd_name, "..")) { panic("sfs: %s: rename: moving dir: .. is not " "in slot %d\n", sfs->sfs_sb.sb_volname, DOTDOTSLOT); } if (sd.sfd_ino != dir1->sv_ino) { panic("sfs: %s: rename: moving dir: .. is i%u " "and not i%u\n", sfs->sfs_sb.sb_volname, sd.sfd_ino, dir1->sv_ino); } sd.sfd_ino = dir2->sv_ino; result = sfs_writedir(obj1, DOTDOTSLOT, &sd); if (result) { goto recover1; } dir1_inodeptr->sfi_linkcount--; sfs_dinode_mark_dirty(dir1); dir2_inodeptr->sfi_linkcount++; sfs_dinode_mark_dirty(dir2); } result = sfs_dir_unlink(dir1, slot1); if (result) { goto recover2; } obj1_inodeptr->sfi_linkcount--; sfs_dinode_mark_dirty(obj1); KASSERT(result==0); if (0) { /* Only reached on error */ recover2: if (obj1->sv_type == SFS_TYPE_DIR) { sd.sfd_ino = dir1->sv_ino; result2 = sfs_writedir(obj1, DOTDOTSLOT, &sd); if (result2) { recovermsg(sfs->sfs_sb.sb_volname, result, result2); } dir1_inodeptr->sfi_linkcount++; sfs_dinode_mark_dirty(dir1); dir2_inodeptr->sfi_linkcount--; sfs_dinode_mark_dirty(dir2); } recover1: result2 = sfs_dir_unlink(dir2, slot2); if (result2) { recovermsg(sfs->sfs_sb.sb_volname, result, result2); } obj1_inodeptr->sfi_linkcount--; sfs_dinode_mark_dirty(obj1); } out4: sfs_dinode_unload(dir1); out3: sfs_dinode_unload(dir2); out2: sfs_dinode_unload(obj1); lock_release(obj1->sv_lock); out1: if (obj2) { sfs_dinode_unload(obj2); lock_release(obj2->sv_lock); } lock_release(dir1->sv_lock); if (dir1 != dir2) { lock_release(dir2->sv_lock); } out0: if (obj2 != NULL) { VOP_DECREF(&obj2->sv_absvn); } if (obj1 != NULL) { VOP_DECREF(&obj1->sv_absvn); } unreserve_buffers(SFS_BLOCKSIZE); lock_release(sfs->sfs_renamelock); return result; }
main() { int i; int retval; /* used to hold return values of file system calls */ /* do forever: 1) print a list of available commands 2) read a command 3) read arguments for the command 4) perform the requested operation 5) display the results of the operation */ while(1) { /* print a list of available commands */ printf("\n"); printf("o: open a file\n"); printf("r: read from a file\n"); printf("w: write to a file\n"); printf("R: read from a directory\n"); printf("c: close a file\n"); printf("m: create (make) a new file\n"); printf("d: delete a file\n"); printf("s: get the size of a file\n"); printf("t: get the type of a file\n"); printf("i: initialize the file system\n"); printf("q: quit - exit this program\n"); /* read in the next command */ printf("\nCommand? "); if (gets(command_buffer) == NULL) break; /* determine which command was requested */ switch(command_buffer[0]) { case 'o': /* Open a file */ printf("Enter full path name of file to open: "); scanf(INPUT_BUF_FORMAT,data_buffer_1); retval = sfs_open(data_buffer_1); if (retval >= 0) { printf("Open succeeded. File Descriptor number is %d\n",retval); } else { printf("Error. Return value was %d\n",retval); } break; case 'r': /* Read from a file */ printf("Enter file descriptor number: "); scanf("%d",&p1); printf("Enter read start location: "); scanf("%d",&p2); printf("Enter number of bytes to read: "); scanf("%d",&p3); retval = sfs_read(p1,p2,p3,io_buffer); if (retval > 0) { printf("Read succeeded.\n"); printf("The following data was read (only printable ASCII will display)\n"); for(i=0;i<p3;i++) { putchar(io_buffer[i]); } printf("\n"); } else { printf("Error. Return value was %d\n",retval); } break; case 'w': /* Write to a file */ printf("Enter file descriptor number: "); scanf("%d",&p1); printf("Enter write start location: "); scanf("%d",&p2); printf("Enter number of bytes to write: "); scanf("%d",&p3); printf("This program allows only non-white-space, printable ASCII characters to be written to a file.\n"); printf("Enter %d characters to be written: ",p3); scanf(IO_BUF_FORMAT,io_buffer); retval = sfs_write(p1,p2,p3,io_buffer); if (retval > 0) { printf("Write succeeded.\n"); printf("Wrote %s to the disk\n",io_buffer); } else { printf("Error. Return value was %d\n",retval); } break; case 'R': /* Read from a directory */ printf("Enter file descriptor number: "); scanf("%d",&p1); retval = sfs_readdir(p1,io_buffer); if (retval > 0) { printf("sfs_readdir succeeded.\n"); printf("Directory entry is: %s\n",io_buffer); } else if (retval == 0) { printf("sfs_readdir succeeded.\n"); printf("No more entries in this directory\n"); } else { printf("Error. Return value was %d\n",retval); } break; case 'c': /* Close a file */ printf("Enter file descriptor number: "); scanf("%d",&p1); retval = sfs_close(p1); if (retval > 0) { printf("sfs_close succeeded.\n"); } else { printf("Error. Return value was %d\n",retval); } break; case 'm': /* Create a new file */ printf("Enter full path name of new file: "); scanf(INPUT_BUF_FORMAT,data_buffer_1); printf("Enter 0 for regular file, 1 for directory: "); scanf("%d",&p1); retval = sfs_create(data_buffer_1,p1); if (retval > 0) { printf("sfs_create succeeded.\n"); } else { printf("Error. Return value was %d\n",retval); } break; case 'd': /* Delete a file */ printf("Enter full path name of file to delete: "); scanf(INPUT_BUF_FORMAT,data_buffer_1); retval = sfs_delete(data_buffer_1); if (retval > 0) { printf("sfs_delete succeeded.\n"); } else { printf("Error. Return value was %d\n",retval); } break; case 's': /* Get the size of a file */ printf("Enter full path name of file: "); scanf(INPUT_BUF_FORMAT,data_buffer_1); retval = sfs_getsize(data_buffer_1); if (retval >= 0) { printf("sfs_getsize succeeded.\n"); printf("size = %d\n",retval); } else { printf("Error. Return value was %d\n",retval); } break; case 't': /* Get the type of a file */ printf("Enter full path name of file: "); scanf(INPUT_BUF_FORMAT,data_buffer_1); retval = sfs_gettype(data_buffer_1); if (retval >= 0) { printf("sfs_gettype succeeded.\n"); if (retval == 0) { printf("file type is REGULAR\n"); } else if (retval == 1) { printf("file type is DIRECTORY\n"); } else { printf("file has unknown type %d\n",retval); } } else { printf("Error. Return value was %d\n",retval); } break; case 'i': /* Initialize the file system */ printf("Enter 1 to erase disk while initializing, 0 otherwise: "); scanf("%d",&p1); retval = sfs_initialize(p1); if (retval > 0) { printf("sfs_initialize succeeded.\n"); } else { printf("Error. Return value was %d\n",retval); } break; case 'q': /* Quit this program */ break; default: printf("Unknown command: %s\n",command_buffer); break; } if (command_buffer[0] == 'q') break; /* cleanup the newline that remains after reading command parameter(s) */ gets(command_buffer); } }
/* * Check a directory. INO is the inode number; PATHSOFAR is the path * to this directory. This traverses the volume directory tree * recursively. */ static void pass1_dir(uint32_t ino, const char *pathsofar) { struct sfs_dinode sfi; struct sfs_dir *direntries; uint32_t ndirentries, i; int ichanged=0, dchanged=0; sfs_readinode(ino, &sfi); if (sfi.sfi_size % sizeof(struct sfs_dir) != 0) { setbadness(EXIT_RECOV); warnx("Directory %s has illegal size %lu (fixed)", pathsofar, (unsigned long) sfi.sfi_size); sfi.sfi_size = SFS_ROUNDUP(sfi.sfi_size, sizeof(struct sfs_dir)); ichanged = 1; } count_dirs++; if (pass1_inode(ino, &sfi, ichanged)) { /* been here before; crosslinked dir, sort it out in pass 2 */ return; } ndirentries = sfi.sfi_size/sizeof(struct sfs_dir); direntries = domalloc(sfi.sfi_size); sfs_readdir(&sfi, direntries, ndirentries); for (i=0; i<ndirentries; i++) { if (pass1_direntry(pathsofar, i, &direntries[i])) { dchanged = 1; } } for (i=0; i<ndirentries; i++) { if (direntries[i].sfd_ino == SFS_NOINO) { /* nothing */ } else if (!strcmp(direntries[i].sfd_name, ".")) { /* nothing */ } else if (!strcmp(direntries[i].sfd_name, "..")) { /* nothing */ } else { char path[strlen(pathsofar)+SFS_NAMELEN+1]; struct sfs_dinode subsfi; uint32_t subino; subino = direntries[i].sfd_ino; sfs_readinode(subino, &subsfi); snprintf(path, sizeof(path), "%s/%s", pathsofar, direntries[i].sfd_name); switch (subsfi.sfi_type) { case SFS_TYPE_FILE: if (pass1_inode(subino, &subsfi, 0)) { /* been here before */ break; } count_files++; break; case SFS_TYPE_DIR: pass1_dir(subino, path); break; default: setbadness(EXIT_RECOV); warnx("Object %s: Invalid inode type " "(removed)", path); direntries[i].sfd_ino = SFS_NOINO; direntries[i].sfd_name[0] = 0; dchanged = 1; break; } } } if (dchanged) { sfs_writedir(&sfi, direntries, ndirentries); } free(direntries); }