Example #1
0
/*
 * Make a hard link to a file.
 * The VFS layer should prevent this being called unless both
 * vnodes are ours.
 *
 * Locking: locks both vnodes, parent first. Since we aren't allowed
 * to hardlink directories, the target can't be an ancestor of the
 * directory we're working in.
 *
 * Requires up to 4 buffers.
 */
static
int
sfs_link(struct vnode *dir, const char *name, struct vnode *file)
{
	struct sfs_vnode *sv = dir->vn_data;
	struct sfs_vnode *f = file->vn_data;
	struct sfs_dinode *inodeptr;
	int result;

	KASSERT(file->vn_fs == dir->vn_fs);

	/* Hard links to directories aren't allowed. */
	if (f->sv_type == SFS_TYPE_DIR) {
		return EINVAL;
	}
	KASSERT(file != dir);

	reserve_buffers(SFS_BLOCKSIZE);

	/* directory must be locked first */
	lock_acquire(sv->sv_lock);
	lock_acquire(f->sv_lock);

	result = sfs_dinode_load(f);
	if (result) {
		lock_release(f->sv_lock);
		lock_release(sv->sv_lock);
		unreserve_buffers(SFS_BLOCKSIZE);
		return result;
	}

	/* Create the link */
	result = sfs_dir_link(sv, name, f->sv_ino, NULL);
	if (result) {
		sfs_dinode_unload(f);
		lock_release(f->sv_lock);
		lock_release(sv->sv_lock);
		unreserve_buffers(SFS_BLOCKSIZE);
		return result;
	}

	/* and update the link count, marking the inode dirty */
	inodeptr = sfs_dinode_map(f);
	inodeptr->sfi_linkcount++;
	sfs_dinode_mark_dirty(f);

	sfs_dinode_unload(f);
	lock_release(f->sv_lock);
	lock_release(sv->sv_lock);
	unreserve_buffers(SFS_BLOCKSIZE);
	return 0;
}
Example #2
0
/*
 * Called for stat/fstat/lstat.
 *
 * Locking: gets/releases vnode lock.
 *
 * Requires 1 buffer.
 */
static
int
sfs_stat(struct vnode *v, struct stat *statbuf)
{
	struct sfs_vnode *sv = v->vn_data;
	struct sfs_dinode *inodeptr;
	int result;

	/* Fill in the stat structure */
	bzero(statbuf, sizeof(struct stat));

	result = VOP_GETTYPE(v, &statbuf->st_mode);
	if (result) {
		return result;
	}

	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;
	}

	inodeptr = sfs_dinode_map(sv);

	statbuf->st_size = inodeptr->sfi_size;
	statbuf->st_nlink = inodeptr->sfi_linkcount;

	/* We don't support this yet */
	statbuf->st_blocks = 0;

	/* Fill in other fields as desired/possible... */

	sfs_dinode_unload(sv);
	unreserve_buffers(SFS_BLOCKSIZE);
	lock_release(sv->sv_lock);
	return 0;
}
Example #3
0
/*
 * 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;
}
Example #4
0
/*
 * 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;
}
Example #5
0
static
int
sfs_mkdir(struct vnode *v, const char *name, mode_t mode)
{
	struct sfs_fs *sfs = v->vn_fs->fs_data;
	struct sfs_vnode *sv = v->vn_data;
	int result;
	uint32_t ino;
	struct sfs_dinode *dir_inodeptr;
	struct sfs_dinode *new_inodeptr;
	struct sfs_vnode *newguy;

	(void)mode;

	lock_acquire(sv->sv_lock);
	reserve_buffers(SFS_BLOCKSIZE);

	result = sfs_dinode_load(sv);
	if (result) {
		goto die_early;
	}
	dir_inodeptr = sfs_dinode_map(sv);

	if (dir_inodeptr->sfi_linkcount == 0) {
		result = ENOENT;
		goto die_simple;
	}

	/* Look up the name */
	result = sfs_dir_findname(sv, name, &ino, NULL, NULL);
	if (result!=0 && result!=ENOENT) {
		goto die_simple;
	}

	/* If it exists, fail */
	if (result==0) {
		result = EEXIST;
		goto die_simple;
	}

	/*
	 * If we're trying to create . or .. and we get this far
	 * (meaning the entry in question is missing), the fs is
	 * corrupted. Let's not make it worse. Best at this point to
	 * bail out and run fsck.
	 */
	if (!strcmp(name, ".") || !strcmp(name, "..")) {
		panic("sfs: %s: No %s entry in dir %u; please fsck\n",
		      sfs->sfs_sb.sb_volname, name, sv->sv_ino);
	}

	result = sfs_makeobj(sfs, SFS_TYPE_DIR, &newguy);
	if (result) {
		goto die_simple;
	}
	new_inodeptr = sfs_dinode_map(newguy);

	result = sfs_dir_link(newguy, ".", newguy->sv_ino, NULL);
	if (result) {
		goto die_uncreate;
	}

	result = sfs_dir_link(newguy, "..", sv->sv_ino, NULL);
	if (result) {
		goto die_uncreate;
	}

	result = sfs_dir_link(sv, name, newguy->sv_ino, NULL);
	if (result) {
		goto die_uncreate;
	}

        /*
         * Increment link counts (Note: not until after the names are
         * added - that way if one fails, the link count will be zero,
         * and reclaim will dispose of the new directory.
         *
         * Note also that the name in the parent directory gets added
         * last, so there's no case in which we have to go back and
         * remove it.
         */

	new_inodeptr->sfi_linkcount += 2;
	dir_inodeptr->sfi_linkcount++;
	sfs_dinode_mark_dirty(newguy);
	sfs_dinode_mark_dirty(sv);

	sfs_dinode_unload(newguy);
	sfs_dinode_unload(sv);
	lock_release(newguy->sv_lock);
	lock_release(sv->sv_lock);
	VOP_DECREF(&newguy->sv_absvn);

	unreserve_buffers(SFS_BLOCKSIZE);

	KASSERT(result==0);
	return result;

die_uncreate:
	sfs_dinode_unload(newguy);
	lock_release(newguy->sv_lock);
	VOP_DECREF(&newguy->sv_absvn);

die_simple:
	sfs_dinode_unload(sv);

die_early:
	unreserve_buffers(SFS_BLOCKSIZE);
	lock_release(sv->sv_lock);
	return result;
}
Example #6
0
/*
 * Create a file. If EXCL is set, insist that the filename not already
 * exist; otherwise, if it already exists, just open it.
 *
 * Locking: Gets/releases the vnode lock for v. Does not lock the new vnode,
 * as nobody else can get to it except by searching the directory it's in,
 * which is locked.
 *
 * Requires up to 4 buffers as VOP_DECREF invocations may take 3.
 */
static
int
sfs_creat(struct vnode *v, const char *name, bool excl, mode_t mode,
	  struct vnode **ret)
{
	struct sfs_fs *sfs = v->vn_fs->fs_data;
	struct sfs_vnode *sv = v->vn_data;
	struct sfs_vnode *newguy;
	struct sfs_dinode *sv_dino;
	struct sfs_dinode *new_dino;
	uint32_t ino;
	int result;

	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;
	}
	sv_dino = sfs_dinode_map(sv);

	if (sv_dino->sfi_linkcount == 0) {
		sfs_dinode_unload(sv);
		unreserve_buffers(SFS_BLOCKSIZE);
		lock_release(sv->sv_lock);
		return ENOENT;
	}

	sfs_dinode_unload(sv);

	/* Look up the name */
	result = sfs_dir_findname(sv, name, &ino, NULL, NULL);
	if (result!=0 && result!=ENOENT) {
		unreserve_buffers(SFS_BLOCKSIZE);
		lock_release(sv->sv_lock);
		return result;
	}

	/* If it exists and we didn't want it to, fail */
	if (result==0 && excl) {
		unreserve_buffers(SFS_BLOCKSIZE);
		lock_release(sv->sv_lock);
		return EEXIST;
	}

	if (result==0) {
		/* We got something; load its vnode and return */
		result = sfs_loadvnode(sfs, ino, SFS_TYPE_INVAL, &newguy);
		if (result) {
			unreserve_buffers(SFS_BLOCKSIZE);
			lock_release(sv->sv_lock);
			return result;
		}

		*ret = &newguy->sv_absvn;
		unreserve_buffers(SFS_BLOCKSIZE);
		lock_release(sv->sv_lock);
		return 0;
	}

	/* Didn't exist - create it */
	result = sfs_makeobj(sfs, SFS_TYPE_FILE, &newguy);
	if (result) {
		unreserve_buffers(SFS_BLOCKSIZE);
		lock_release(sv->sv_lock);
		return result;
	}

	/* sfs_makeobj loads the inode for us */
	new_dino = sfs_dinode_map(newguy);

	/* We don't currently support file permissions; ignore MODE */
	(void)mode;

	/* Link it into the directory */
	result = sfs_dir_link(sv, name, newguy->sv_ino, NULL);
	if (result) {
		sfs_dinode_unload(newguy);
		lock_release(newguy->sv_lock);
		VOP_DECREF(&newguy->sv_absvn);
		lock_release(sv->sv_lock);
		unreserve_buffers(SFS_BLOCKSIZE);
		return result;
	}

	/* Update the linkcount of the new file */
	new_dino->sfi_linkcount++;

	/* and consequently mark it dirty. */
	sfs_dinode_mark_dirty(newguy);

	*ret = &newguy->sv_absvn;

	sfs_dinode_unload(newguy);
	unreserve_buffers(SFS_BLOCKSIZE);
	lock_release(newguy->sv_lock);
	lock_release(sv->sv_lock);
	return 0;
}
Example #7
0
/*
 * 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;
}
Example #8
0
/*
 * This is much the same as sfs_partialio, but intended for use with
 * metadata (e.g. directory entries). It assumes the objects being
 * handled are smaller than whole blocks, do not cross block
 * boundaries, and originate in the kernel.
 *
 * It is separate from sfs_partialio because, although there is no
 * such code in this version of SFS, it is often desirable when doing
 * more advanced things to handle metadata and user data I/O
 * differently.
 */
int
sfs_metaio(struct sfs_vnode *sv, off_t actualpos, void *data, size_t len,
	   enum uio_rw rw)
{
	struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
	struct sfs_dinode *dino;
	off_t endpos;
	uint32_t vnblock;
	uint32_t blockoffset;
	daddr_t diskblock;
	struct buf *iobuf;
	char *ioptr;
	bool doalloc;
	int result;

	KASSERT(lock_do_i_hold(sv->sv_lock));

	/* Figure out which block of the vnode (directory, whatever) this is */
	vnblock = actualpos / SFS_BLOCKSIZE;
	blockoffset = actualpos % SFS_BLOCKSIZE;

	result = sfs_dinode_load(sv);
	if (result) {
		return result;
	}
	dino = sfs_dinode_map(sv);

	/* Get the disk block number */
	doalloc = (rw == UIO_WRITE);
	result = sfs_bmap(sv, vnblock, doalloc, &diskblock);
	if (result) {
		sfs_dinode_unload(sv);
		return result;
	}

	if (diskblock == 0) {
		/* Should only get block 0 back if doalloc is false */
		KASSERT(rw == UIO_READ);

		/* Sparse file, read as zeros. */
		bzero(data, len);
		sfs_dinode_unload(sv);
		return 0;
	}

	/* Read the block */
	result = buffer_read(&sfs->sfs_absfs, diskblock, SFS_BLOCKSIZE,
			     &iobuf);
	if (result) {
		/*
		 * XXX: if we allocated, do we need to discard
		 * the block we allocated? urgh...
		 */
		sfs_dinode_unload(sv);
		return result;
	}


	ioptr = buffer_map(iobuf);
	if (rw == UIO_READ) {
		/* Copy out the selected region */
		memcpy(data, ioptr + blockoffset, len);
	}
	else {
		// Journal this!!!
		ioptr = buffer_map(iobuf);
		void *old_data = ioptr + blockoffset;
		sfs_jphys_write_wrapper(sfs, /*context*/ NULL, 
			jentry_meta_update(	diskblock, 
								blockoffset, 
								len,
								old_data, 
								data));

		/* Update the selected region */
		memcpy(ioptr + blockoffset, data, len);
		buffer_mark_dirty(iobuf);	// Journalled (meta_update)

		/* Update the vnode size if needed */
		endpos = actualpos + len;
		if (endpos > (off_t)dino->sfi_size) {
			sfs_jphys_write_wrapper(sfs, NULL,
				jentry_resize(	sv->sv_ino,	// disk_addr
								dino->sfi_size,	// old_size
								endpos));	// new_size
			dino->sfi_size = endpos;
			sfs_dinode_mark_dirty(sv);
		}
	}

	buffer_release(iobuf);
	sfs_dinode_unload(sv);

	/* Done */
	return 0;
}
Example #9
0
/*
 * Do I/O of a whole region of data, whether or not it's block-aligned.
 *
 * Locking: must hold vnode lock. May get/release sfs_freemaplock.
 *
 * Requires up to 3 buffers.
 */
int
sfs_io(struct sfs_vnode *sv, struct uio *uio)
{
	uint32_t blkoff;
	uint32_t nblocks, i;
	int result = 0;
	uint32_t origresid, extraresid = 0;
	struct sfs_dinode *inodeptr;

	KASSERT(lock_do_i_hold(sv->sv_lock));

	origresid = uio->uio_resid;

	result = sfs_dinode_load(sv);
	if (result) {
		return result;
	}
	inodeptr = sfs_dinode_map(sv);

	/*
	 * If reading, check for EOF. If we can read a partial area,
	 * remember how much extra there was in EXTRARESID so we can
	 * add it back to uio_resid at the end.
	 */
	if (uio->uio_rw == UIO_READ) {
		off_t size, endpos;

		size = inodeptr->sfi_size;
		endpos = uio->uio_offset + uio->uio_resid;

		if (uio->uio_offset >= size) {
			/* At or past EOF - just return */
			sfs_dinode_unload(sv);
			return 0;
		}

		if (endpos > size) {
			extraresid = endpos - size;
			KASSERT(uio->uio_resid > extraresid);
			uio->uio_resid -= extraresid;
		}
	}

	/*
	 * First, do any leading partial block.
	 */
	blkoff = uio->uio_offset % SFS_BLOCKSIZE;
	if (blkoff != 0) {
		/* Number of bytes at beginning of block to skip */
		uint32_t skip = blkoff;

		/* Number of bytes to read/write after that point */
		uint32_t len = SFS_BLOCKSIZE - blkoff;

		/* ...which might be less than the rest of the block */
		if (len > uio->uio_resid) {
			len = uio->uio_resid;
		}

		/* Call sfs_partialio() to do it. */
		result = sfs_partialio(sv, uio, skip, len);
		if (result) {
			goto out;
		}
	}

	/* If we're done, quit. */
	if (uio->uio_resid==0) {
		goto out;
	}

	/*
	 * Now we should be block-aligned. Do the remaining whole blocks.
	 */
	KASSERT(uio->uio_offset % SFS_BLOCKSIZE == 0);
	nblocks = uio->uio_resid / SFS_BLOCKSIZE;
	for (i=0; i<nblocks; i++) {
		result = sfs_blockio(sv, uio);
		if (result) {
			goto out;
		}
	}

	/*
	 * Now do any remaining partial block at the end.
	 */
	KASSERT(uio->uio_resid < SFS_BLOCKSIZE);

	if (uio->uio_resid > 0) {
		result = sfs_partialio(sv, uio, 0, uio->uio_resid);
		if (result) {
			goto out;
		}
	}

 out:

	/* If writing and we did anything, adjust file length */
	if (uio->uio_resid != origresid &&
	    uio->uio_rw == UIO_WRITE &&
	    uio->uio_offset > (off_t)inodeptr->sfi_size) {
		sfs_jphys_write_wrapper(sv->sv_absvn.vn_fs->fs_data, NULL,
			jentry_resize(	sv->sv_ino,	// disk_addr
							inodeptr->sfi_size,	// old_size
							uio->uio_offset));	// new_size
		inodeptr->sfi_size = uio->uio_offset;
		sfs_dinode_mark_dirty(sv);
	}
	sfs_dinode_unload(sv);

	/* Add in any extra amount we couldn't read because of EOF */
	uio->uio_resid += extraresid;

	/* Done */
	return result;
}