Пример #1
0
/* 
 * 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;
}
Пример #2
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;

 }
Пример #3
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;
}
Пример #4
0
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
}
Пример #5
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;
}
Пример #6
0
/*
 * 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;
}
Пример #7
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;
}
Пример #8
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;
}
Пример #9
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;
}
Пример #10
0
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);
  }
}
Пример #11
0
/*
 * 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);
}