/* * Lookup gets a vnode for a pathname. */ static int sfs_lookup(struct vnode *v, char *path, struct vnode **ret) { struct sfs_vnode *sv = v->vn_data; struct sfs_vnode *parent, *child; char name[SFS_NAMELEN]; int i, err; if (sv->sv_i.sfi_type != SFS_TYPE_DIR) { return ENOTDIR; } parent = sv; VOP_INCREF(&parent->sv_v); child = NULL; //just to be safe while(1){ i = 0; while(path[i] != '/' && path[i] != '\0' && i < SFS_NAMELEN) i++; if(i >= SFS_NAMELEN){ VOP_DECREF(&parent->sv_v); return ENAMETOOLONG; } else if(path[i] == '/'){ path[i] = 0; strcpy(name, path); path = &path[i + 1]; err = sfs_lookonce(parent, name, &child, NULL); if(err){ VOP_DECREF(&parent->sv_v); return err; } VOP_DECREF(&parent->sv_v); parent = child; if(parent->sv_i.sfi_type != SFS_TYPE_DIR) return ENOTDIR; } else{ /* Hit NULL, so this is our last time through */ assert(path[i] == '\0'); err = sfs_lookonce(parent, path, &child, NULL); if(err){ VOP_DECREF(&parent->sv_v); return err; } VOP_DECREF(&parent->sv_v); break; } } *ret = &child->sv_v; return 0; }
/* * Delete a file. */ static int sfs_remove(struct vnode *dir, const char *name) { struct sfs_vnode *sv = dir->vn_data; struct sfs_vnode *victim; int slot; int result; /* Look for the file and fetch a vnode for it. */ result = sfs_lookonce(sv, name, &victim, &slot); if (result) { return result; } /* 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; }
static int sfs_lookparent_internal(struct vnode *v, char *path, struct vnode **ret, char *buf, size_t buflen) { struct sfs_vnode *sv = v->vn_data; struct sfs_vnode *next; char *s; int result; VOP_INCREF(&sv->sv_absvn); while (1) { /* Don't need lock to check vnode type; it's constant */ if (sv->sv_type != SFS_TYPE_DIR) { VOP_DECREF(&sv->sv_absvn); return ENOTDIR; } s = strchr(path, '/'); if (!s) { /* Last component. */ break; } *s = 0; s++; lock_acquire(sv->sv_lock); result = sfs_lookonce(sv, path, &next, NULL); lock_release(sv->sv_lock); if (result) { VOP_DECREF(&sv->sv_absvn); return result; } VOP_DECREF(&sv->sv_absvn); sv = next; path = s; } if (strlen(path)+1 > buflen) { VOP_DECREF(&sv->sv_absvn); return ENAMETOOLONG; } strcpy(buf, path); *ret = &sv->sv_absvn; return 0; }
/* * Helper function for rename. Make sure COMPARE is not a direct * ancestor of (or the same as) CHILD. * * Note: acquires locks as it goes up. */ static int check_parent(struct sfs_vnode *lookfor, struct sfs_vnode *failon, struct sfs_vnode *child, int *found) { struct sfs_vnode *up; int result; *found = 0; VOP_INCREF(&child->sv_absvn); while (1) { if (failon == child) { /* Bad */ VOP_DECREF(&child->sv_absvn); return EINVAL; } if (lookfor == child) { *found = 1; } lock_acquire(child->sv_lock); result = sfs_lookonce(child, "..", &up, NULL); lock_release(child->sv_lock); if (result) { VOP_DECREF(&child->sv_absvn); return result; } if (child == up) { /* Hit root, done */ VOP_DECREF(&up->sv_absvn); break; } VOP_DECREF(&child->sv_absvn); child = up; } VOP_DECREF(&child->sv_absvn); return 0; }
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; }
/* * Rename a file. */ static int sfs_rename(struct vnode *d1, const char *n1, struct vnode *d2, const char *n2) { struct sfs_vnode *sv1 = d1->vn_data; struct sfs_vnode *sv2 = d2->vn_data; struct sfs_vnode *g1; int slot1, slot2; int result, result2; /* Look up the old name of the file and get its inode and slot number*/ result = sfs_lookonce(sv1, n1, &g1, &slot1); if (result) { return result; } /* * Link it to the 'new' directory under the new name. * * We could theoretically just overwrite the original * directory entry, except that we need to check to make sure * the new name doesn't already exist; might as well use the * existing link routine. */ result = sfs_dir_link(sv2, n2, g1->sv_ino, &slot2); if (result) { goto puke; } /* Increment the link count, and mark inode dirty */ g1->sv_i.sfi_linkcount++; g1->sv_dirty = 1; /* Unlink the old slot */ result = sfs_dir_unlink(sv1, slot1); if (result) { goto puke_harder; } /* * Decrement the link count again, and mark the inode dirty again, * in case it's been synced behind our back. */ assert(g1->sv_i.sfi_linkcount>0); g1->sv_i.sfi_linkcount--; g1->sv_dirty = 1; /* Let go of the reference to g1 */ VOP_DECREF(&g1->sv_v); return 0; puke_harder: /* * Error recovery: try to undo what we already did */ result2 = sfs_dir_unlink(sv2, slot2); if (result2) { kprintf("sfs: rename: %s\n", strerror(result)); kprintf("sfs: rename: while cleaning up: %s\n", strerror(result2)); panic("sfs: rename: Cannot recover\n"); } g1->sv_i.sfi_linkcount--; puke: /* Let go of the reference to g1 */ VOP_DECREF(&g1->sv_v); return result; }
/* * Delete a file. * * Locking: locks the directory, then the file. Unlocks both. * This follows the hierarchical locking order imposed by the directory tree. * * Requires up to 4 buffers. */ static int sfs_remove(struct vnode *dir, const char *name) { struct sfs_vnode *sv = dir->vn_data; struct sfs_vnode *victim; struct sfs_dinode *victim_inodeptr; struct sfs_dinode *dir_inodeptr; int slot; int result; /* need to check this to avoid deadlock even in error condition */ if (!strcmp(name, ".") || !strcmp(name, "..")) { return EISDIR; } lock_acquire(sv->sv_lock); reserve_buffers(SFS_BLOCKSIZE); result = sfs_dinode_load(sv); if (result) { goto out_buffers; } dir_inodeptr = sfs_dinode_map(sv); if (dir_inodeptr->sfi_linkcount == 0) { result = ENOENT; goto out_loadsv; } /* Look for the file and fetch a vnode for it. */ result = sfs_lookonce(sv, name, &victim, &slot); if (result) { goto out_loadsv; } lock_acquire(victim->sv_lock); result = sfs_dinode_load(victim); if (result) { lock_release(victim->sv_lock); VOP_DECREF(&victim->sv_absvn); goto out_loadsv; } victim_inodeptr = sfs_dinode_map(victim); KASSERT(victim_inodeptr->sfi_linkcount > 0); /* Not allowed on directories */ if (victim_inodeptr->sfi_type == SFS_TYPE_DIR) { result = EISDIR; goto out_reference; } /* Erase its directory entry. */ result = sfs_dir_unlink(sv, slot); if (result) { goto out_reference; } /* Decrement the link count. */ KASSERT(victim_inodeptr->sfi_linkcount > 0); victim_inodeptr->sfi_linkcount--; sfs_dinode_mark_dirty(victim); out_reference: /* Discard the reference that sfs_lookonce got us */ sfs_dinode_unload(victim); lock_release(victim->sv_lock); VOP_DECREF(&victim->sv_absvn); out_loadsv: sfs_dinode_unload(sv); out_buffers: lock_release(sv->sv_lock); unreserve_buffers(SFS_BLOCKSIZE); return result; }
/* * Delete a directory. * * Locking: Acquires vnode lock for parent dir and then vnode lock for * victim dir. Releases both. * * Requires 4 buffers. */ static int sfs_rmdir(struct vnode *v, const char *name) { struct sfs_vnode *sv = v->vn_data; struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data; struct sfs_vnode *victim; struct sfs_dinode *dir_inodeptr; struct sfs_dinode *victim_inodeptr; int result, result2; int slot; /* Cannot remove the . or .. entries from a directory! */ if (!strcmp(name, ".") || !strcmp(name, "..")) { return EINVAL; } lock_acquire(sv->sv_lock); reserve_buffers(SFS_BLOCKSIZE); result = sfs_dinode_load(sv); if (result) { goto die_loadsv; } dir_inodeptr = sfs_dinode_map(sv); if (dir_inodeptr->sfi_linkcount == 0) { result = ENOENT; goto die_linkcount; } result = sfs_lookonce(sv, name, &victim, &slot); if (result) { goto die_linkcount; } lock_acquire(victim->sv_lock); result = sfs_dinode_load(victim); if (result) { goto die_loadvictim; } victim_inodeptr = sfs_dinode_map(victim); if (victim->sv_ino == SFS_ROOTDIR_INO) { result = EPERM; goto die_total; } /* Only allowed on directories */ if (victim_inodeptr->sfi_type != SFS_TYPE_DIR) { result = ENOTDIR; goto die_total; } result = sfs_dir_checkempty(victim); if (result) { goto die_total; } result = sfs_dir_unlink(sv, slot); if (result) { goto die_total; } KASSERT(dir_inodeptr->sfi_linkcount > 1); KASSERT(victim_inodeptr->sfi_linkcount==2); dir_inodeptr->sfi_linkcount--; sfs_dinode_mark_dirty(sv); victim_inodeptr->sfi_linkcount -= 2; sfs_dinode_mark_dirty(victim); result = sfs_itrunc(victim, 0); if (result) { victim_inodeptr->sfi_linkcount += 2; dir_inodeptr->sfi_linkcount++; result2 = sfs_dir_link(sv, name, victim->sv_ino, NULL); if (result2) { /* XXX: would be better if this case didn't exist */ panic("sfs: %s: rmdir: %s; while recovering: %s\n", sfs->sfs_sb.sb_volname, strerror(result), strerror(result2)); } goto die_total; } die_total: sfs_dinode_unload(victim); die_loadvictim: lock_release(victim->sv_lock); VOP_DECREF(&victim->sv_absvn); die_linkcount: sfs_dinode_unload(sv); die_loadsv: unreserve_buffers(SFS_BLOCKSIZE); lock_release(sv->sv_lock); return result; }
/* * Get the full pathname for a file. This only needs to work on directories. * * Locking: Gets/releases vnode locks, but only one at a time. * * Requires up to 3 buffers. */ static int sfs_namefile(struct vnode *vv, struct uio *uio) { struct sfs_vnode *sv = vv->vn_data; struct sfs_vnode *parent = NULL; int result; char *buf; size_t bufpos, bufmax, len; KASSERT(uio->uio_rw == UIO_READ); bufmax = uio->uio_resid+1; if (bufmax > PATH_MAX) { bufmax = PATH_MAX; } buf = kmalloc(bufmax); if (buf == NULL) { return ENOMEM; } reserve_buffers(SFS_BLOCKSIZE); bufpos = bufmax; VOP_INCREF(&sv->sv_absvn); while (1) { lock_acquire(sv->sv_lock); /* not allowed to lock child since we're going up the tree */ result = sfs_lookonce(sv, "..", &parent, NULL); lock_release(sv->sv_lock); if (result) { VOP_DECREF(&sv->sv_absvn); kfree(buf); unreserve_buffers(SFS_BLOCKSIZE); return result; } if (parent == sv) { /* .. was equal to . - must be root, so we're done */ VOP_DECREF(&parent->sv_absvn); VOP_DECREF(&sv->sv_absvn); break; } lock_acquire(parent->sv_lock); result = sfs_getonename(parent, sv->sv_ino, buf, &bufpos); lock_release(parent->sv_lock); if (result) { VOP_DECREF(&parent->sv_absvn); VOP_DECREF(&sv->sv_absvn); kfree(buf); unreserve_buffers(SFS_BLOCKSIZE); return result; } VOP_DECREF(&sv->sv_absvn); sv = parent; parent = NULL; } /* Done looking, now send back the string */ if (bufmax == bufpos) { /* root directory; do nothing (send back empty string) */ result = 0; } else { len = bufmax - bufpos; len--; /* skip the trailing slash */ KASSERT(len <= uio->uio_resid); result = uiomove(buf+bufpos, len, uio); } kfree(buf); unreserve_buffers(SFS_BLOCKSIZE); 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; }