Ejemplo n.º 1
0
/*
 * Removes an entry from a directory.
 */
PUBLIC int dir_remove(struct inode *dinode, const char *filename)
{
	struct buffer *buf; /* Block buffer.    */
	struct d_dirent *d; /* Directory entry. */
	struct inode *file; /* File inode.      */
	
	d = dirent_search(dinode, filename, &buf, 0);
	
	/* Not found. */
	if (d == NULL)
		return (-ENOENT);

	/* Cannot remove '.' */
	if (d->d_ino == dinode->num)
	{
		brelse(buf);
		return (-EBUSY);
	}
	
	file = inode_get(dinode->dev, d->d_ino);
	
	/* Failed to get file's inode. */
	if (file == NULL)
	{
		brelse(buf);
		return (-ENOENT);
	}
	
	/* Unlinking directory. */
	if (S_ISDIR(file->mode))
	{
		/* Not allowed. */
		if (!IS_SUPERUSER(curr_proc))
		{
			inode_put(file);
			brelse(buf);
			return (-EPERM);
		}
		
		/* Directory not empty. */
		if (dinode->size)
		{
			inode_put(file);
			brelse(buf);
			return (-EBUSY);			
		}
	}
	
	/* Remove directory entry. */
	d->d_ino = INODE_NULL;
	buf->flags |= BUFFER_DIRTY;
	inode_touch(dinode);
	file->nlinks--;
	inode_touch(file);
	inode_put(file);
	brelse(buf);
	
	return (0);
}
Ejemplo n.º 2
0
/**
 * @brief Create an indirect block.
 *
 * @details Creates a indirect block and saves in @param dest.
 *
 * @param dest Destination buffer, It is the corresponding disk block to be changed.
 * @param ip File to use.
 * @param offset Offset to be calculated the block.
 * @param creat If 1, creates an block, otherwise, just return this value.
 *
 * @returns If successful, the block number created / obtained, otherwise BLOCK_NULL is 
 *          returned.
 *
 * @note @p ip must be locked.
 */
PUBLIC block_t create_indirect_block
(struct buffer *dest, struct inode *ip, off_t offset, int create)
{
	block_t phys; /* Physical block number. */

	if (((block_t *)buffer_data(dest))[offset] == BLOCK_NULL && create)
	{
		/* Allocate an block. */
		superblock_lock(ip->sb);
		phys = block_alloc(ip->sb);
		superblock_unlock(ip->sb);

		if (phys != BLOCK_NULL)
		{
			((block_t *)buffer_data(dest))[offset] = phys;
			buffer_dirty(dest, 1);
			inode_touch(ip);
			brelse(dest);
			return (phys);
		}
		else
		{
			brelse(dest);
			return (phys);
		}
	}
	else
	{
		brelse(dest);
		return ((block_t *)buffer_data(dest))[offset];
	}
}
Ejemplo n.º 3
0
Archivo: dir.c Proyecto: jgarzik/nfs4d
enum nfsstat4 dir_add(DB_TXN *txn, struct nfs_inode *dir_ino,
		      const struct nfs_buf *name_in,
		      struct nfs_inode *ent_ino)
{
	enum nfsstat4 status = NFS4_OK, lu_stat;
	int rc;

	lu_stat = dir_lookup(txn, dir_ino, name_in, 0, NULL);
	if (lu_stat != NFS4ERR_NOENT) {
		if (lu_stat == NFS4_OK)
			status = NFS4ERR_EXIST;
		else
			status = lu_stat;
		return status;
	}

	rc = fsdb_dirent_put(&srv.fsdb, txn, dir_ino->inum, name_in,
			     DB_NOOVERWRITE, ent_ino->inum);
	if (rc) {
		if (rc == DB_KEYEXIST)
			status = NFS4ERR_EXIST;
		else
			status = NFS4ERR_IO;
		goto out;
	}

	rc = inode_touch(txn, dir_ino);
	if (rc) {
		status = NFS4ERR_IO;
		goto out;
	}

out:
	return status;
}
Ejemplo n.º 4
0
/*
 * Reads from a regular file.
 */
PUBLIC ssize_t file_read(struct inode *i, void *buf, size_t n, off_t off)
{
	char *p;             /* Writing pointer.      */
	size_t blkoff;       /* Block offset.         */
	size_t chunk;        /* Data chunk size.      */
	block_t blk;         /* Working block number. */
	struct buffer *bbuf; /* Working block buffer. */
		
	p = buf;
	
	inode_lock(i);
	
	/* Read data. */
	do
	{
		blk = block_map(i, off, 0);
		
		/* End of file reached. */
		if (blk == BLOCK_NULL)
			goto out;
		
		bbuf = bread(i->dev, blk);
			
		blkoff = off % BLOCK_SIZE;
		
		/* Calculate read chunk size. */
		chunk = (n < BLOCK_SIZE - blkoff) ? n : BLOCK_SIZE - blkoff;
		if ((off_t)chunk > i->size - off)
		{
			chunk = i->size - off;
			if (chunk == 0)
			{
				brelse(bbuf);
				goto out;
			}
		}
		
		kmemcpy(p, (char *)bbuf->data + blkoff, chunk);
		brelse(bbuf);
		
		n -= chunk;
		off += chunk;
		p += chunk;
	} while (n > 0);

out:
	inode_touch(i);
	inode_unlock(i);
	return ((ssize_t)(p - (char *)buf));
}
Ejemplo n.º 5
0
/*
 * Writes to a regular file.
 */
PUBLIC ssize_t file_write(struct inode *i, const void *buf, size_t n, off_t off)
{
	const char *p;       /* Reading pointer.      */
	size_t blkoff;       /* Block offset.         */
	size_t chunk;        /* Data chunk size.      */
	block_t blk;         /* Working block number. */
	struct buffer *bbuf; /* Working block buffer. */
		
	p = buf;
	
	inode_lock(i);
	
	/* Write data. */
	do
	{
		blk = block_map(i, off, 1);
		
		/* End of file reached. */
		if (blk == BLOCK_NULL)
			goto out;
		
		bbuf = bread(i->dev, blk);
		
		blkoff = off % BLOCK_SIZE;
		
		chunk = (n < BLOCK_SIZE - blkoff) ? n : BLOCK_SIZE - blkoff;
		kmemcpy((char *)bbuf->data + blkoff, buf, chunk);
		bbuf->flags |= BUFFER_DIRTY;
		brelse(bbuf);
		
		n -= chunk;
		off += chunk;
		p += chunk;
		
		/* Update file size. */
		if (off > i->size)
		{
			i->size = off;
			i->flags |= INODE_DIRTY;
		}
		
	} while (n > 0);

out:

	inode_touch(i);
	inode_unlock(i);
	return ((ssize_t)(p - (char *)buf));
}
Ejemplo n.º 6
0
/**
 * @brief Create an direct block.
 *
 * @details Creates a direct block and saves in @param dest.
 *
 * @param ip File to use.
 * @param offset Offset to be calculated the block.
 * @param creat If 1, creates an block, otherwise, just return this value.
 *
 * @returns If successful, the block number created/obtained, otherwise BLOCK_NULL is 
 *          returned.
 *
 * @note @p ip must be locked.
 */
PUBLIC block_t create_direct_block(struct inode *ip, off_t offset, int create)
{
	block_t phys; /* Physical block number. */

	if (ip->blocks[offset] == BLOCK_NULL && create)
	{
		/* Allocate an block. */
		superblock_lock(ip->sb);
		phys = block_alloc(ip->sb);
		superblock_unlock(ip->sb);

		if (phys != BLOCK_NULL)
		{
			ip->blocks[offset] = phys;
			inode_touch(ip);
			return (phys);
		}
		else
			return (phys);
	}
	else
		return (ip->blocks[offset]);
}
Ejemplo n.º 7
0
/**
 * @brief Maps a file byte offset in a disk block number.
 * 
 * @details Maps the offset @p off in the file pointed to by @p ip in a disk
 *          block number. If @p create is not zero and such file by offset is
 *          invalid, the file is expanded accordingly to make it valid.
 * 
 * @param ip     File to use
 * @param off    File byte offset.
 * @param create Create offset?
 * 
 * @returns Upon successful completion, the disk block number that is associated
 *          with the file byte offset is returned. Upon failure, #BLOCK_NULL is
 *          returned instead.
 * 
 * @note @p ip must be locked.
 */
PUBLIC block_t block_map(struct inode *ip, off_t off, int create)
{
	block_t phys;       /* Physical block number.    */
	block_t logic;      /* Logical block number.     */
	struct buffer *buf; /* Underlying buffer.        */
	unsigned tmp;       /* Logical block number tmp. */
	
	logic = off/BLOCK_SIZE;
	
	/* File offset too big. */
	if (off >= ip->sb->max_size)
	{
		curr_proc->errno = -EFBIG;
		return (BLOCK_NULL);
	}
	
	/* 
	 * Create blocks that are
	 * in a valid offset.
	 */
	if (off < ip->size)
		create = 1;
	
	/* Direct block. */
	if (logic < NR_ZONES_DIRECT)
	{
		/* Create direct block. */
		if (ip->blocks[logic] == BLOCK_NULL && create)
		{
			superblock_lock(ip->sb);
			phys = block_alloc(ip->sb);
			superblock_unlock(ip->sb);
			
			if (phys != BLOCK_NULL)
			{
				ip->blocks[logic] = phys;
				inode_touch(ip);
			}
		}
		
		return (ip->blocks[logic]);
	}
	
	logic -= NR_ZONES_DIRECT;
	
	/* Single indirect block. */
	if (logic < NR_SINGLE)
	{
		/* Create single indirect block. */
		if (ip->blocks[ZONE_SINGLE] == BLOCK_NULL && create)
		{
			superblock_lock(ip->sb);
			phys = block_alloc(ip->sb);
			superblock_unlock(ip->sb);
			
			if (phys != BLOCK_NULL)
			{
				ip->blocks[ZONE_SINGLE] = phys;
				inode_touch(ip);
			}
		}
		
		/* We cannot go any further. */
		if ((phys = ip->blocks[ZONE_SINGLE]) == BLOCK_NULL)
			return (BLOCK_NULL);
	
		buf = bread(ip->dev, phys);
		
		/* Create direct block. */
		if (((block_t *)buffer_data(buf))[logic] == BLOCK_NULL && create)
		{
			superblock_lock(ip->sb);
			phys = block_alloc(ip->sb);
			superblock_unlock(ip->sb);
			
			if (phys != BLOCK_NULL)
			{
				((block_t *)buffer_data(buf))[logic] = phys;
				buffer_dirty(buf, 1);
				inode_touch(ip);
			}
		}
		
		brelse(buf);
		
		return (((block_t *)buffer_data(buf))[logic]);
	}
	
	logic = off - REMAINING_OFFSET;
	
	/* Double indirect block. */
	tmp = logic / BLOCK_SIZE;
	if (tmp < NR_DOUBLE)
	{
		size_t logicSingle = logic / (NR_SINGLE * BLOCK_SIZE);
		size_t logicDouble = logic / BLOCK_SIZE;
		
		/* Create single, double and/or direct block. */
		if ( (phys = create_direct_block(ip,ZONE_DOUBLE,create)) != BLOCK_NULL)
		{
			buf = bread(ip->dev, phys);
			if ( (phys = create_indirect_block(buf,ip,logicSingle,create)) 
				!= BLOCK_NULL)
			{
				buf = bread(ip->dev, phys);
				if ( (phys = create_indirect_block(buf,ip,logicDouble,create)) 
					!= BLOCK_NULL)
					return (phys);
				else
					return (BLOCK_NULL);
			}
			else
				return (BLOCK_NULL);
		}
		else
			return (BLOCK_NULL);
	}

	/* Triple indirect zone. */
	kpanic("triple indirect zones not supported");
	
	return (BLOCK_NULL);
}
Ejemplo n.º 8
0
/*
 * Reads from a file.
 */
PUBLIC ssize_t sys_read(int fd, void *buf, size_t n)
{
	dev_t dev;       /* Device number.       */
	struct file *f;  /* File.                */
	struct inode *i; /* Inode.               */
	ssize_t count;   /* Bytes actually read. */
	
	/* Invalid file descriptor. */
	if ((fd < 0) || (fd >= OPEN_MAX) || ((f = curr_proc->ofiles[fd]) == NULL))
		return (-EBADF);
	
	/* File not opened for reading. */
	if (ACCMODE(f->oflag) == O_WRONLY)
		return (-EBADF);

#if (EDUCATIONAL_KERNEL == 0)
	
	/* Invalid buffer. */	
	if (!chkmem(buf, n, MAY_WRITE))
		return (-EINVAL);

#endif

	/* Nothing to do. */
	if (n == 0)
		return (0);
	
	 i = f->inode;
	
	/* Character special file. */
	if (S_ISCHR(i->mode))
	{		
		dev = i->blocks[0];
		count = cdev_read(dev, buf, n);
		return (count);
	}
	
	/* Block special file. */
	else if (S_ISBLK(i->mode))
	{
		dev = i->blocks[0];
		count = bdev_read(dev, buf, n, f->pos);
	}
	
	/* Pipe file. */
	else if (S_ISFIFO(i->mode))
	{
		kprintf("read from pipe");
		count = pipe_read(i, buf, n);
	}
	
	/* Regular file/directory. */
	else if ((S_ISDIR(i->mode)) || (S_ISREG(i->mode)))
		count = file_read(i, buf, n, f->pos);
	
	/* Unknown file type. */
	else
		return (-EINVAL);
	
	/* Failed to read. */
	if (count < 0)
		return (curr_proc->errno);

	inode_touch(i);
	f->pos += count;

	return (count);
}
Ejemplo n.º 9
0
Archivo: dir.c Proyecto: jgarzik/nfs4d
nfsstat4 nfs_op_rename(struct nfs_cxn *cxn, const RENAME4args *args,
		       struct list_head *writes, struct rpc_write **wr)
{
	nfsstat4 status = NFS4_OK;
	struct nfs_inode *src_dir = NULL, *target_dir = NULL;
	struct nfs_inode *old_file = NULL, *new_file = NULL;
	struct nfs_buf oldname, newname;
	change_info4 src = { true, 0, 0 };
	change_info4 target = { true, 0, 0 };
	DB_TXN *txn = NULL;
	DB_ENV *dbenv = srv.fsdb.env;
	int rc;
	nfsino_t old_dirent, new_dirent;

	oldname.len = args->oldname.utf8string_len;
	oldname.val = args->oldname.utf8string_val;
	newname.len = args->newname.utf8string_len;
	newname.val = args->newname.utf8string_val;

	if (debugging)
		applog(LOG_INFO, "op RENAME (OLD:%.*s, NEW:%.*s)",
		       oldname.len,
		       oldname.val,
		       newname.len,
		       newname.val);

	/* validate text input */
	if ((!valid_utf8string(&oldname)) ||
	    (!valid_utf8string(&newname))) {
		status = NFS4ERR_INVAL;
		goto out;
	}
	if (has_dots(&oldname) || has_dots(&newname)) {
		status = NFS4ERR_BADNAME;
		goto out;
	}

	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
	if (rc) {
		status = NFS4ERR_IO;
		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
		goto out;
	}

	/* reference source, target directories.
	 * NOTE: src_dir and target_dir may point to the same object
	 */
	src_dir = inode_fhdec(txn, cxn->save_fh, DB_RMW);
	if (fh_equal(cxn->save_fh, cxn->current_fh))
		target_dir = src_dir;
	else
		target_dir = inode_fhdec(txn, cxn->current_fh, DB_RMW);
	if (!src_dir || !target_dir) {
		status = NFS4ERR_NOFILEHANDLE;
		goto out_abort;
	}
	if ((src_dir->type != NF4DIR) || (target_dir->type != NF4DIR)) {
		status = NFS4ERR_NOTDIR;
		goto out_abort;
	}

	/* lookup source, target names */
	status = dir_lookup(txn, src_dir, &oldname, 0, &old_dirent);
	if (status != NFS4_OK)
		goto out_abort;

	old_file = inode_getdec(txn, old_dirent, 0);
	if (!old_file) {
		status = NFS4ERR_NOENT;
		goto out_abort;
	}

	status = dir_lookup(txn, target_dir, &newname, 0, &new_dirent);
	if (status != NFS4_OK && status != NFS4ERR_NOENT)
		goto out_abort;

	/* if target (newname) is present, attempt to remove */
	if (status == NFS4_OK) {
		bool ok_to_remove = false;

		/* read to-be-deleted inode */
		new_file = inode_getdec(txn, new_dirent, DB_RMW);
		if (!new_file) {
			status = NFS4ERR_NOENT;
			goto out_abort;
		}

		/* do oldname and newname refer to same file? */
		if (old_file->inum == new_file->inum) {
			src.after =
			src.before = src_dir->version;
			target.after =
			target.before = target_dir->version;
			goto out_abort;
		}

		if (old_file->type != NF4DIR && new_file->type != NF4DIR)
			ok_to_remove = true;
		else if (old_file->type == NF4DIR &&
			 new_file->type == NF4DIR &&
			 dir_is_empty(txn, new_file))
			ok_to_remove = true;

		if (!ok_to_remove) {
			status = NFS4ERR_EXIST;
			goto out_abort;
		}

		/* remove target inode from directory */
		rc = fsdb_dirent_del(&srv.fsdb, txn, target_dir->inum,
				     &newname, 0);
		if (rc == 0)
			rc = inode_unlink(txn, new_file);

		if (rc) {
			status = NFS4ERR_IO;
			goto out_abort;
		}
	} else
		status = NFS4_OK;

	new_dirent = old_dirent;

	/* delete entry from source directory; add to target directory */
	rc = fsdb_dirent_del(&srv.fsdb, txn, src_dir->inum, &oldname, 0);
	if (rc == 0)
		rc = fsdb_dirent_put(&srv.fsdb, txn, target_dir->inum,
				     &newname, 0, new_dirent);
	if (rc) {
		status = NFS4ERR_IO;
		goto out_abort;
	}

	/* if renamed file is a directory, ensure its 'parent' is updated */
	if (old_file->type == NF4DIR) {
		old_file->parent = target_dir->inum;
		if (inode_touch(txn, old_file)) {
			status = NFS4ERR_IO;
			goto out_abort;
		}
	}

	/* record directory change info */
	src.before = src_dir->version;
	target.before = target_dir->version;

	/* update last-modified stamps of directory inodes */
	rc = inode_touch(txn, src_dir);
	if (rc == 0 && src_dir != target_dir)
		rc = inode_touch(txn, target_dir);
	if (rc) {
		status = NFS4ERR_IO;
		goto out_abort;
	}

	/* close the transaction */
	rc = txn->commit(txn, 0);
	if (rc) {
		dbenv->err(dbenv, rc, "DB_ENV->txn_commit");
		status = NFS4ERR_IO;
		goto out;
	}

	src.after = src_dir->version;
	target.after = target_dir->version;

out:
	WR32(status);
	if (status == NFS4_OK) {
		WR32(src.atomic ? 1 : 0); /* src cinfo.atomic */
		WR64(src.before);	/* src cinfo.before */
		WR64(src.after);	/* src cinfo.after */
		WR32(target.atomic ? 1 : 0); /* target cinfo.atomic */
		WR64(target.before);	/* target cinfo.before */
		WR64(target.after);	/* target cinfo.after */
	}
	inode_free(src_dir);
	if (src_dir != target_dir)
		inode_free(target_dir);
	inode_free(old_file);
	inode_free(new_file);
	return status;

out_abort:
	if (txn->abort(txn))
		dbenv->err(dbenv, rc, "DB_ENV->txn_abort");
	goto out;
}
Ejemplo n.º 10
0
Archivo: dir.c Proyecto: jgarzik/nfs4d
nfsstat4 nfs_op_remove(struct nfs_cxn *cxn, const REMOVE4args *args,
		       struct list_head *writes, struct rpc_write **wr)
{
	nfsstat4 status = NFS4_OK;
	struct nfs_inode *dir_ino = NULL, *target_ino = NULL;
	struct nfs_buf target;
	change_info4 cinfo = { true, 0, 0 };
	DB_TXN *txn = NULL;
	DB_ENV *dbenv = srv.fsdb.env;
	int rc;
	nfsino_t de_inum;

	target.len = args->target.utf8string_len;
	target.val = args->target.utf8string_val;

	if (debugging)
		applog(LOG_INFO, "op REMOVE ('%.*s')",
		       target.len,
		       target.val);

	if (target.len > SRV_MAX_NAME) {
		status = NFS4ERR_NAMETOOLONG;
		goto out;
	}

	if (!valid_utf8string(&target)) {
		status = NFS4ERR_INVAL;
		goto out;
	}
	if (has_dots(&target)) {
		status = NFS4ERR_BADNAME;
		goto out;
	}

	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
	if (rc) {
		status = NFS4ERR_IO;
		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
		goto out;
	}

	/* reference container directory */
	status = dir_curfh(txn, cxn, &dir_ino, DB_RMW);
	if (status != NFS4_OK)
		goto out_abort;

	/* lookup target name in directory */
	status = dir_lookup(txn, dir_ino, &target, 0, &de_inum);
	if (status != NFS4_OK)
		goto out_abort;

	/* reference target inode */
	target_ino = inode_getdec(txn, de_inum, DB_RMW);
	if (!target_ino) {
		status = NFS4ERR_NOENT;
		goto out_abort;
	}

	/* prevent root dir deletion */
	if (target_ino->inum == INO_ROOT) {
		status = NFS4ERR_INVAL;
		goto out_abort;
	}

	/* prevent removal of non-empty dirs */
	if ((target_ino->type == NF4DIR) && !dir_is_empty(txn, target_ino)) {
		status = NFS4ERR_NOTEMPTY;
		goto out_abort;
	}

	/* remove target inode from directory */
	rc = fsdb_dirent_del(&srv.fsdb, txn, dir_ino->inum, &target, 0);
	if (rc) {
		status = NFS4ERR_IO;
		goto out_abort;
	}

	/* record directory change info */
	cinfo.before = dir_ino->version;

	rc = inode_touch(txn, dir_ino);
	if (rc) {
		status = NFS4ERR_IO;
		goto out_abort;
	}

	cinfo.after = dir_ino->version;

	/* remove link, possibly deleting inode */
	rc = inode_unlink(txn, target_ino);
	if (rc) {
		status = NFS4ERR_IO;
		goto out_abort;
	}

	rc = txn->commit(txn, 0);
	if (rc) {
		dbenv->err(dbenv, rc, "DB_ENV->txn_commit");
		status = NFS4ERR_IO;
		goto out;
	}

out:
	WR32(status);
	if (status == NFS4_OK) {
		WR32(cinfo.atomic ? 1 : 0);	/* cinfo.atomic */
		WR64(cinfo.before);		/* cinfo.before */
		WR64(cinfo.after);		/* cinfo.after */
	}
	inode_free(dir_ino);
	inode_free(target_ino);
	return status;

out_abort:
	if (txn->abort(txn))
		dbenv->err(dbenv, rc, "DB_ENV->txn_abort");
	goto out;
}
Ejemplo n.º 11
0
Archivo: dir.c Proyecto: jgarzik/nfs4d
nfsstat4 nfs_op_link(struct nfs_cxn *cxn, const LINK4args *args,
		     struct list_head *writes, struct rpc_write **wr)
{
	nfsstat4 status;
	struct nfs_inode *dir_ino = NULL, *src_ino = NULL;
	struct nfs_buf newname;
	uint64_t before = 0, after = 0;
	DB_TXN *txn;
	DB_ENV *dbenv = srv.fsdb.env;
	int rc;

	newname.len = args->newname.utf8string_len;
	newname.val = args->newname.utf8string_val;

	if (debugging)
		applog(LOG_INFO, "op LINK (%.*s)",
		       newname.len,
		       newname.val);

	/* verify input parameters */
	if (!valid_fh(cxn->current_fh) || !valid_fh(cxn->save_fh)) {
		status = NFS4ERR_NOFILEHANDLE;
		goto out;
	}
	if (newname.len > SRV_MAX_NAME) {
		status = NFS4ERR_NAMETOOLONG;
		goto out;
	}

	/* open transaction */
	rc = dbenv->txn_begin(dbenv, NULL, &txn, 0);
	if (rc) {
		status = NFS4ERR_IO;
		dbenv->err(dbenv, rc, "DB_ENV->txn_begin");
		goto out;
	}

	/* read source inode's directory inode */
	dir_ino = inode_fhdec(txn, cxn->current_fh, 0);
	if (!dir_ino) {
		status = NFS4ERR_NOFILEHANDLE;
		goto out_abort;
	}

	/* make sure target is a directory */
	if (dir_ino->type != NF4DIR) {
		status = NFS4ERR_NOTDIR;
		goto out_abort;
	}

	/* read source inode */
	src_ino = inode_fhdec(txn, cxn->save_fh, 0);
	if (!src_ino) {
		status = NFS4ERR_NOFILEHANDLE;
		goto out_abort;
	}

	/* make sure source is a not a directory */
	if (src_ino->type == NF4DIR) {
		status = NFS4ERR_ISDIR;
		goto out_abort;
	}

	before = dir_ino->version;

	/* add directory entry */
	status = dir_add(txn, dir_ino, &newname, src_ino);
	if (status != NFS4_OK)
		goto out_abort;

	after = dir_ino->version;

	/* update source inode */
	src_ino->n_link++;
	if (inode_touch(txn, src_ino)) {
		status = NFS4ERR_IO;
		goto out_abort;
	}

	/* close transaction */
	rc = txn->commit(txn, 0);
	if (rc) {
		dbenv->err(dbenv, rc, "DB_ENV->txn_commit");
		status = NFS4ERR_IO;
		goto out;
	}

out:
	WR32(status);
	if (status == NFS4_OK) {
		WR32(1);		/* cinfo.atomic */
		WR64(before);		/* cinfo.before */
		WR64(after);		/* cinfo.after */
	}
	inode_free(src_ino);
	inode_free(dir_ino);
	return status;

out_abort:
	if (txn->abort(txn))
		dbenv->err(dbenv, rc, "DB_ENV->txn_abort");
	goto out;
}
Ejemplo n.º 12
0
/**
 * @brief Searches for a directory entry.
 * 
 * @details Searches for a directory entry named @p filename in the directory
 *          pointed to be @p dip. If @p create is not zero and such file entry
 *          does not exist, the entry is created.
 * 
 * @param dip      Directory where the directory entry shall be searched.
 * @param filename Name of the directory entry that shall be searched.
 * @param buf      Buffer where the directory entry is loaded.
 * @param create   Create directory entry?
 * 
 * @returns Upon successful completion, the directory entry is returned. In this
 *          case, @p buf is set to point to the (locked) buffer associated to
 *          the requested directory entry. However, upon failure, a #NULL
 *          pointer is returned instead.
 * 
 * @note @p dip must be locked.
 * @note @p filename must point to a valid location.
 * @note @p buf must point to a valid location
 */
PRIVATE struct d_dirent *dirent_search
(struct inode *dip, const char *filename, struct buffer **buf, int create)
{
	int i;              /* Working directory entry index.       */
	int entry;          /* Index of first free directory entry. */
	block_t blk;        /* Working block number.                */
	int nentries;       /* Number of directory entries.         */
	struct d_dirent *d; /* Directory entry.                     */
	
	nentries = dip->size/sizeof(struct d_dirent);
	
	/* Search from very first block. */
	i = 0;
	entry = -1;
	blk = dip->blocks[0];		
	(*buf) = NULL;
	d = NULL;
	/* Search directory entry. */
	while (i < nentries)
	{
		/*
		 * Skip invalid blocks. As directory entries
		 * are removed from a directory, a whole block
		 * may become free.
		 */
		if (blk == BLOCK_NULL)
		{
			i += BLOCK_SIZE/sizeof(struct d_dirent);
			blk = block_map(dip, i*sizeof(struct d_dirent), 0);
			continue;
		}
		
		/* Get buffer. */
		if ((*buf) == NULL)
		{
			(*buf) = bread(dip->dev, blk);
			d = (*buf)->data;
		}
		
		/* Get next block */
		else if ((char *)d >= BLOCK_SIZE + (char *) (*buf)->data)
		{
			brelse((*buf));
			(*buf) = NULL;
			blk = block_map(dip, i*sizeof(struct d_dirent), 0);
			continue;
		}
		
		/* Valid entry. */
		if (d->d_ino != INODE_NULL)
		{
			/* Found */
			if (!kstrncmp(d->d_name, filename, NAME_MAX))
			{
				/* Duplicated entry. */
				if (create)
				{
					brelse((*buf));
					d = NULL;
					curr_proc->errno = EEXIST;
				}
				
				return (d);
			}
		}

		/* Remember entry index. */
		else
			entry = i;
		
		d++; i++;	
	}
	
	/* House keeping. */
	if ((*buf) != NULL)
	{
		brelse((*buf));
		(*buf) = NULL;
	}
	
	/* Create entry. */
	if (create)
	{
		/* Expand directory. */
		if (entry < 0)
		{
			entry = nentries;
			
			blk = block_map(dip, entry*sizeof(struct d_dirent), 1);
			
			/* Failed to create entry. */
			if (blk == BLOCK_NULL)
			{
				curr_proc->errno = -ENOSPC;
				return (NULL);
			}
			
			dip->size += sizeof(struct d_dirent);
			inode_touch(dip);
		}
		
		else
			blk = block_map(dip, entry*sizeof(struct d_dirent), 0);
		
		(*buf) = bread(dip->dev, blk);
		entry %= (BLOCK_SIZE/sizeof(struct d_dirent));
		d = &((struct d_dirent *)((*buf)->data))[entry];
		
		return (d);
	}
	
	return (NULL);
}