Exemple #1
0
/*
 * Do I/O (either read or write) of a single whole block.
 */
static
int
sfs_blockio(struct sfs_vnode *sv, struct uio *uio)
{
	struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data;
	u_int32_t diskblock;
	u_int32_t fileblock;
	int result;
	int doalloc = (uio->uio_rw==UIO_WRITE);
	off_t saveoff;
	off_t diskoff;
	off_t saveres;
	off_t diskres;

	/* Get the block number within the file */
	fileblock = uio->uio_offset / SFS_BLOCKSIZE;

	/* Look up the disk block number */
	result = sfs_bmap(sv, fileblock, doalloc, &diskblock);
	if (result) {
		return result;
	}

	if (diskblock == 0) {
		/*
		 * No block - fill with zeros.
		 *
		 * We must be reading, or sfs_bmap would have
		 * allocated a block for us.
		 */
		assert(uio->uio_rw == UIO_READ);
		return uiomovezeros(SFS_BLOCKSIZE, uio);
	}

	/*
	 * Do the I/O directly to the uio region. Save the uio_offset,
	 * and substitute one that makes sense to the device.
	 */
	saveoff = uio->uio_offset;
	diskoff = diskblock * SFS_BLOCKSIZE;
	uio->uio_offset = diskoff;

	/*
	 * Temporarily set the residue to be one block size.
	 */
	assert(uio->uio_resid >= SFS_BLOCKSIZE);
	saveres = uio->uio_resid;
	diskres = SFS_BLOCKSIZE;
	uio->uio_resid = diskres;
	
	result = sfs_rwblock(sfs, uio);

	/*
	 * Now, restore the original uio_offset and uio_resid and update 
	 * them by the amount of I/O done.
	 */
	uio->uio_offset = (uio->uio_offset - diskoff) + saveoff;
	uio->uio_resid = (uio->uio_resid - diskres) + saveres;

	return result;
}
Exemple #2
0
/*
 * Do I/O to a block of a file that doesn't cover the whole block.  We
 * need to read in the original block first, even if we're writing, so
 * we don't clobber the portion of the block we're not intending to
 * write over.
 *
 * skipstart is the number of bytes to skip past at the beginning of
 * the sector; len is the number of bytes to actually read or write.
 * uio is the area to do the I/O into.
 */
static
int
sfs_partialio(struct sfs_vnode *sv, struct uio *uio,
	      u_int32_t skipstart, u_int32_t len)
{
	/*
	 * I/O buffer for handling partial sectors.
	 *
	 * Note: in real life (and when you've done the fs assignment)
	 * you would get space from the disk buffer cache for this,
	 * not use a static area.
	 */
	static char iobuf[SFS_BLOCKSIZE];

	struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data;
	u_int32_t diskblock;
	u_int32_t fileblock;
	int result;
	
	/* Allocate missing blocks if and only if we're writing */
	int doalloc = (uio->uio_rw==UIO_WRITE);

	assert(skipstart + len <= SFS_BLOCKSIZE);

	/* Compute the block offset of this block in the file */
	fileblock = uio->uio_offset / SFS_BLOCKSIZE;

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

	/* f_test 3 debug */
	u_int32_t i;
	char ch;

	if (diskblock == 0) {
		/*
		 * There was no block mapped at this point in the file.
		 * Zero the buffer.
		 */
		assert(uio->uio_rw == UIO_READ);
		bzero(iobuf, sizeof(iobuf));
	}
	else {
		/*
		 * Read the block.
		 */
		result = sfs_rblock(sfs, iobuf, diskblock);
		if (result) {
			return result;
		}

		/* f_test 3 debug
		if(uio->uio_rw == UIO_READ && diskblock > 6){
                	for(i=0; i<len; i++){
                        	ch = (iobuf+skipstart)[i];
                        	if(ch != 'w' && ch != 'r') kprintf("Read Error on diskblock %d - iobuf[%d]: %c\n", diskblock, i, ch);
                	}       
        	}
		*/
	}

	/*
	 * Now perform the requested operation into/out of the buffer.
	 */
	result = uiomove(iobuf+skipstart, len, uio);
	if (result) {
		return result;
	}
	
	/*
	 * If it was a write, write back the modified block.
	 */
	if (uio->uio_rw == UIO_WRITE) {
		result = sfs_wblock(sfs, iobuf, diskblock);
		if (result) {
			return result;
		}
	}

	return 0;
}
Exemple #3
0
/*
 * Do I/O (either read or write) of a single whole block.
 *
 * Locking: must hold vnode lock. May get/release sfs_freemaplock.
 *
 * Requires up to 2 buffers.
 */
static
int
sfs_blockio(struct sfs_vnode *sv, struct uio *uio)
{
	struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
	struct buf *iobuf;
	void *ioptr;
	daddr_t diskblock;
	uint32_t fileblock;
	int result;
	bool doalloc = (uio->uio_rw==UIO_WRITE);
	unsigned new_checksum = 0;

	KASSERT(lock_do_i_hold(sv->sv_lock));

	/* Get the block number within the file */
	fileblock = uio->uio_offset / SFS_BLOCKSIZE;

	/* Look up the disk block number */
	result = sfs_bmap(sv, fileblock, doalloc, &diskblock);
	if (result) {
		return result;
	}

	if (diskblock == 0) {
		/*
		 * No block - fill with zeros.
		 *
		 * We must be reading, or sfs_bmap would have
		 * allocated a block for us.
		 */
		KASSERT(uio->uio_rw == UIO_READ);
		return uiomovezeros(SFS_BLOCKSIZE, uio);
	}

	if (uio->uio_rw == UIO_READ) {
		result = buffer_read(&sfs->sfs_absfs, diskblock, SFS_BLOCKSIZE,
				     &iobuf);
	}
	else {
		result = buffer_get(&sfs->sfs_absfs, diskblock, SFS_BLOCKSIZE,
				    &iobuf);
	}
	if (result) {
		return result;
	}

	/*
	 * Do the I/O into the buffer.
	 */
	ioptr = buffer_map(iobuf);
	result = uiomove(ioptr, SFS_BLOCKSIZE, uio);
	if (result) {
		buffer_release(iobuf);
		return result;
	}

	if (uio->uio_rw == UIO_WRITE) {
		new_checksum = checksum(ioptr);
		sfs_jphys_write_wrapper(sfs, NULL, 
			jentry_block_write(diskblock, new_checksum, false));
		buffer_mark_valid(iobuf);
		buffer_mark_dirty(iobuf);	// Journalled
	}

	buffer_release(iobuf);
	return 0;
}
Exemple #4
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;
}
Exemple #5
0
/*
 * Do I/O to a block of a file that doesn't cover the whole block.  We
 * need to read in the original block first, even if we're writing, so
 * we don't clobber the portion of the block we're not intending to
 * write over.
 *
 * SKIPSTART is the number of bytes to skip past at the beginning of
 * the sector; LEN is the number of bytes to actually read or write.
 * UIO is the area to do the I/O into.
 *
 * Requires up to 2 buffers.
 */
static
int
sfs_partialio(struct sfs_vnode *sv, struct uio *uio,
	      uint32_t skipstart, uint32_t len)
{
	struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
	struct buf *iobuffer;
	unsigned char *ioptr;
	daddr_t diskblock;
	uint32_t fileblock;
	int result;
	unsigned new_checksum = 0;

	/* Allocate missing blocks if and only if we're writing */
	bool doalloc = (uio->uio_rw==UIO_WRITE);

	KASSERT(lock_do_i_hold(sv->sv_lock));
	KASSERT(skipstart + len <= SFS_BLOCKSIZE);

	/* Compute the block offset of this block in the file */
	fileblock = uio->uio_offset / SFS_BLOCKSIZE;

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

	if (diskblock == 0) {
		/*
		 * There was no block mapped at this point in the file.
		 *
		 * We must be reading, or sfs_bmap would have
		 * allocated a block for us.
		 */
		KASSERT(uio->uio_rw == UIO_READ);
		return uiomovezeros(len, uio);
	}
	else {
		/*
		 * Read the block.
		 */
		result = buffer_read(&sfs->sfs_absfs, diskblock, SFS_BLOCKSIZE,
				     &iobuffer);
		if (result) {
			return result;
		}
	}

	/*
	 * Now perform the requested operation into/out of the buffer.
	 */
	ioptr = buffer_map(iobuffer);
	result = uiomove(ioptr+skipstart, len, uio);
	if (result) {
		buffer_release(iobuffer);
		return result;
	}

	/*
	 * If it was a write, mark the modified block dirty and journal
	 */
	if (uio->uio_rw == UIO_WRITE) {
		// Compute checksum and journal
		new_checksum = checksum(ioptr);
		sfs_jphys_write_wrapper(sfs, NULL, 
			jentry_block_write(diskblock, new_checksum, false));

		buffer_mark_dirty(iobuffer);	// Journalled
	}

	buffer_release(iobuffer);
	return 0;
}