static int sfs_mapio(struct sfs_fs *sfs, enum uio_rw rw) { uint32_t j, mapsize; char *bitdata; int result; /* Number of blocks in the bitmap. */ mapsize = SFS_FS_BITBLOCKS(sfs); /* Pointer to our bitmap data in memory. */ bitdata = bitmap_getdata(sfs->sfs_freemap); /* For each sector in the bitmap... */ for (j=0; j<mapsize; j++) { /* Get a pointer to its data */ void *ptr = bitdata + j*SFS_BLOCKSIZE; /* and read or write it. The bitmap starts at sector 2. */ if (rw == UIO_READ) { result = sfs_rblock(sfs, ptr, SFS_MAP_LOCATION+j); } else { result = sfs_wblock(sfs, ptr, SFS_MAP_LOCATION+j); } /* If we failed, stop. */ if (result) { return result; } } return 0; }
/* Zero out a disk block. */ static int sfs_clearblock(struct sfs_fs *sfs, u_int32_t block) { /* static -> automatically initialized to zero */ static char zeros[SFS_BLOCKSIZE]; return sfs_wblock(sfs, zeros, block); }
/* Write an on-disk inode structure back out to disk. */ static int sfs_sync_inode(struct sfs_vnode *sv) { if (sv->sv_dirty) { struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data; int result = sfs_wblock(sfs, &sv->sv_i, sv->sv_ino); if (result) { return result; } sv->sv_dirty = 0; } return 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; }
/* * Look up the disk block number (from 0 up to the number of blocks on * the disk) given a file and the logical block number within that * file. If DOALLOC is set, and no such block exists, one will be * allocated. */ static int sfs_bmap(struct sfs_vnode *sv, u_int32_t fileblock, int doalloc, u_int32_t *diskblock) { /* * I/O buffer for handling indirect blocks. * * 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 u_int32_t idbuf[SFS_DBPERIDB]; struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data; u_int32_t block; u_int32_t idblock; u_int32_t idnum, idoff; int result; assert(sizeof(idbuf)==SFS_BLOCKSIZE); /* * If the block we want is one of the direct blocks... */ if (fileblock < SFS_NDIRECT) { /* * Get the block number */ block = sv->sv_i.sfi_direct[fileblock]; /* * Do we need to allocate? */ if (block==0 && doalloc) { result = sfs_balloc(sfs, &block); if (result) { return result; } /* Remember what we allocated; mark inode dirty */ sv->sv_i.sfi_direct[fileblock] = block; sv->sv_dirty = 1; } /* * Hand back the block */ if (block != 0 && !sfs_bused(sfs, block)) { panic("sfs: Data block %u (block %u of file %u) " "marked free\n", block, fileblock, sv->sv_ino); } *diskblock = block; return 0; } /* * It's not a direct block; it must be in the indirect block. * Subtract off the number of direct blocks, so FILEBLOCK is * now the offset into the indirect block space. */ fileblock -= SFS_NDIRECT; /* Get the indirect block number and offset w/i that indirect block */ idnum = fileblock / SFS_DBPERIDB; idoff = fileblock % SFS_DBPERIDB; /* * We only have one indirect block. If the offset we were asked for * is too large, we can't handle it, so fail. */ if (idnum > 0) { return EINVAL; } /* * Probably need to synchronize the stuff below. Maybe not though * since no two threads can be writing to the same file at the same * time (this is protected with file locks in the VFS layer). */ /* Get the disk block number of the indirect block. */ idblock = sv->sv_i.sfi_indirect; if (idblock==0 && !doalloc) { /* * There's no indirect block allocated. We weren't * asked to allocate anything, so pretend the indirect * block was filled with all zeros. */ *diskblock = 0; return 0; } else if (idblock==0) { /* * There's no indirect block allocated, but we need to * allocate a block whose number needs to be stored in * the indirect block. Thus, we need to allocate an * indirect block. */ result = sfs_balloc(sfs, &idblock); if (result) { return result; } /* Remember the block we just allocated */ sv->sv_i.sfi_indirect = idblock; /* Mark the inode dirty */ sv->sv_dirty = 1; /* Clear the indirect block buffer */ bzero(idbuf, sizeof(idbuf)); } else { /* * We already have an indirect block allocated; load it. */ result = sfs_rblock(sfs, idbuf, idblock); if (result) { return result; } } /* Get the block out of the indirect block buffer */ block = idbuf[idoff]; /* If there's no block there, allocate one */ if (block==0 && doalloc) { result = sfs_balloc(sfs, &block); if (result) { return result; } /* Remember the block we allocated */ idbuf[idoff] = block; /* The indirect block is now dirty; write it back */ result = sfs_wblock(sfs, idbuf, idblock); if (result) { return result; } } /* Hand back the result and return. */ if (block != 0 && !sfs_bused(sfs, block)) { panic("sfs: Data block %u (block %u of file %u) marked free\n", block, fileblock, sv->sv_ino); } *diskblock = block; return 0; }
/* * Called for ftruncate() and from sfs_reclaim. */ static int sfs_truncate(struct vnode *v, off_t len) { /* * I/O buffer for handling the indirect block. * * 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 u_int32_t idbuf[SFS_DBPERIDB]; struct sfs_vnode *sv = v->vn_data; struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data; /* Length in blocks (divide rounding up) */ u_int32_t blocklen = DIVROUNDUP(len, SFS_BLOCKSIZE); u_int32_t i, j, block; u_int32_t idblock, baseblock, highblock; int result; int hasnonzero, iddirty; assert(sizeof(idbuf)==SFS_BLOCKSIZE); /* * Go through the direct blocks. Discard any that are * past the limit we're truncating to. */ for (i=0; i<SFS_NDIRECT; i++) { block = sv->sv_i.sfi_direct[i]; if (i >= blocklen && block != 0) { sfs_bfree(sfs, block); sv->sv_i.sfi_direct[i] = 0; sv->sv_dirty = 1; } } /* Indirect block number */ idblock = sv->sv_i.sfi_indirect; /* The lowest block in the indirect block */ baseblock = SFS_NDIRECT; /* The highest block in the indirect block */ highblock = baseblock + SFS_DBPERIDB - 1; if (blocklen < highblock && idblock != 0) { /* We're past the proposed EOF; may need to free stuff */ /* Read the indirect block */ result = sfs_rblock(sfs, idbuf, idblock); if (result) { return result; } hasnonzero = 0; iddirty = 0; for (j=0; j<SFS_DBPERIDB; j++) { /* Discard any blocks that are past the new EOF */ if (blocklen < baseblock+j && idbuf[j] != 0) { sfs_bfree(sfs, idbuf[j]); idbuf[j] = 0; iddirty = 1; } /* Remember if we see any nonzero blocks in here */ if (idbuf[j]!=0) { hasnonzero=1; } } if (!hasnonzero) { /* The whole indirect block is empty now; free it */ sfs_bfree(sfs, idblock); sv->sv_i.sfi_indirect = 0; sv->sv_dirty = 1; } else if (iddirty) { /* The indirect block is dirty; write it back */ result = sfs_wblock(sfs, idbuf, idblock); if (result) { return result; } } } /* Set the file size */ sv->sv_i.sfi_size = len; /* Mark the inode dirty */ sv->sv_dirty = 1; return 0; }
static int sfs_sync(struct fs *fs) { struct sfs_fs *sfs; unsigned i, num; int result; vfs_biglock_acquire(); /* * Get the sfs_fs from the generic abstract fs. * * Note that the abstract struct fs, which is all the VFS * layer knows about, is actually a member of struct sfs_fs. * The pointer in the struct fs points back to the top of the * struct sfs_fs - essentially the same object. This can be a * little confusing at first. * * The following diagram may help: * * struct sfs_fs <-------------\ * : | * : sfs_absfs (struct fs) | <------\ * : : | | * : : various members | | * : : | | * : : fs_data ----------/ | * : : ...|... * : . VFS . * : . layer . * : other members ....... * : * : * * This construct is repeated with vnodes and devices and other * similar things all over the place in OS/161, so taking the * time to straighten it out in your mind is worthwhile. */ sfs = fs->fs_data; /* Go over the array of loaded vnodes, syncing as we go. */ num = vnodearray_num(sfs->sfs_vnodes); for (i=0; i<num; i++) { struct vnode *v = vnodearray_get(sfs->sfs_vnodes, i); VOP_FSYNC(v); } /* If the free block map needs to be written, write it. */ if (sfs->sfs_freemapdirty) { result = sfs_mapio(sfs, UIO_WRITE); if (result) { vfs_biglock_release(); return result; } sfs->sfs_freemapdirty = false; } /* If the superblock needs to be written, write it. */ if (sfs->sfs_superdirty) { result = sfs_wblock(sfs, &sfs->sfs_super, SFS_SB_LOCATION); if (result) { vfs_biglock_release(); return result; } sfs->sfs_superdirty = false; } vfs_biglock_release(); return 0; }
static int sfs_sync(struct fs *fs) { struct sfs_fs *sfs; int i, num, result; struct array *tmp; /* * Get the sfs_fs from the generic abstract fs. * * Note that the abstract struct fs, which is all the VFS * layer knows about, is actually a member of struct sfs_fs. * The pointer in the struct fs points back to the top of the * struct sfs_fs - essentially the same object. This can be a * little confusing at first. * * The following diagram may help: * * struct sfs_fs <-------------\ * : | * : sfs_absfs (struct fs) | <------\ * : : | | * : : various members | | * : : | | * : : fs_data ----------/ | * : : ...|... * : . VFS . * : . layer . * : other members ....... * : * : * * This construct is repeated with vnodes and devices and other * similar things all over the place in OS/161, so taking the * time to straighten it out in your mind is worthwhile. */ sfs = fs->fs_data; /* * This is kind of a hack. We can't acquire vnode locks while * holding sfs_vnlock, because that violates the ordering * constraints (see sfs_vnode.c) - so we *copy* the array of * loaded vnodes into a temporary array and sync those. */ tmp = array_create(); if (tmp == NULL) { return ENOMEM; } lock_acquire(sfs->sfs_vnlock); /* Go over the array of loaded vnodes. */ num = array_getnum(sfs->sfs_vnodes); for (i=0; i<num; i++) { struct sfs_vnode *sv = array_getguy(sfs->sfs_vnodes, i); VOP_INCREF(&sv->sv_v); if (array_add(tmp, sv) != 0) { // XXX panic("sfs_sync: array_add failed\n"); } } lock_release(sfs->sfs_vnlock); /* Now sync. */ num = array_getnum(tmp); for (i=0; i<num; i++) { struct sfs_vnode *sv = array_getguy(tmp, i); result = VOP_FSYNC(&sv->sv_v); if (result) { kprintf("SFS: Warning: syncing inode %d: %s\n", sv->sv_ino, strerror(result)); } VOP_DECREF(&sv->sv_v); } array_destroy(tmp); lock_acquire(sfs->sfs_bitlock); /* If the free block map needs to be written, write it. */ if (sfs->sfs_freemapdirty) { result = sfs_mapio(sfs, UIO_WRITE); if (result) { kprintf("SFS: Warning: syncing bitmap: %s\n", strerror(result)); } else { /* Only clear the dirty bit if we succeeded */ sfs->sfs_freemapdirty = 0; } } /* If the superblock needs to be written, write it. */ if (sfs->sfs_superdirty) { result = sfs_wblock(sfs, &sfs->sfs_super, SFS_SB_LOCATION); if (result) { kprintf("SFS: Warning: syncing superblock: %s\n", strerror(result)); } else { /* Only clear the dirty bit if we succeeded */ sfs->sfs_superdirty = 0; } } lock_release(sfs->sfs_bitlock); return 0; }
/* * Look up the disk block number (from 0 up to the number of blocks on * the disk) given a file and the logical block number within that * file. If DOALLOC is set, and no such block exists, one will be * allocated. */ static int sfs_bmap(struct sfs_vnode *sv, u_int32_t fileblock, int doalloc, u_int32_t *diskblock) { /* * I/O buffer for handling indirect blocks. * * 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 u_int32_t blockbuf[SFS_DBPERIDB]; // I/O blockbuffer struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data; u_int32_t block; // used to store datablock u_int32_t idblock; // stores indirect/double id/triple id blocks (not datablocks) u_int32_t idnum; int result; u_int32_t idblevel = 0; // indirect block level (1=indirect,2=double,3=triple) u_int32_t blockno; // the blocknumber to load (offset into indirect space) /* * ********************************************************************** * Two helpfunctions for assignment 3 goes here. I keep this internal * to keep things a bit simpler for this assignment. Otherwise I would * have made this as external functions equal to bfree, balloc etc... * ********************************************************************** */ /* * loadbuffer: load "block" into the block buffer "blockbuf" * if level == 0 we also update inode indirect/doub.ind./trip.ind pointers * This function should only be called if "doalloc" is set */ int loadbuffer(u_int32_t block, u_int32_t level){ if (block==0) { /* * There's no indirect block allocated, but we need to * allocate a block whose number needs to be stored in * the indirect block. Thus, we need to allocate an * indirect block. */ result = sfs_balloc(sfs, &block); if (result) return result; /* keep track of the number of blocks */ sv->sv_i.sfi_blocks++; /* Remember the block we just allocated */ if (level == 0){ if (idblevel==1) sv->sv_i.sfi_indirect = block; else if (idblevel==2) sv->sv_i.sfi_doubleindirect = block; else if (idblevel==3) sv->sv_i.sfi_tripleindirect = block; } /* update global idblock */ idblock = block; /* Mark the inode dirty */ sv->sv_dirty = 1; /* Clear the block buffer */ bzero(blockbuf, sizeof(blockbuf)); } else { /* * We already have an indirect block allocated; load it. */ result = sfs_rblock(sfs, blockbuf, block); if (result) return result; } return 0; } /* * get_block: the function fetches a block out of * the buffer and into "block", it might allocate a new * if block is empty */ int get_block(u_int32_t blockno){ /* get block from buffer */ block = blockbuf[blockno]; /* If there's no block there, allocate one */ if (block==0 && doalloc) { result = sfs_balloc(sfs, &block); if (result) { return result; } /* increase blockcounter */ sv->sv_i.sfi_blocks++; /* Remember the block we allocated */ blockbuf[blockno] = block; /* The indirect block is now dirty; write it back */ result = sfs_wblock(sfs, blockbuf, idblock); if (result) return result; } return 0; }