Initial Spring 2016 commit.
This commit is contained in:
103
kern/fs/sfs/sfs_balloc.c
Normal file
103
kern/fs/sfs/sfs_balloc.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
|
||||
* The President and Fellows of Harvard College.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SFS filesystem
|
||||
*
|
||||
* Block allocation.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <lib.h>
|
||||
#include <bitmap.h>
|
||||
#include <sfs.h>
|
||||
#include "sfsprivate.h"
|
||||
|
||||
/*
|
||||
* Zero out a disk block.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_clearblock(struct sfs_fs *sfs, daddr_t block)
|
||||
{
|
||||
/* static -> automatically initialized to zero */
|
||||
static char zeros[SFS_BLOCKSIZE];
|
||||
|
||||
return sfs_writeblock(sfs, block, zeros, SFS_BLOCKSIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a block.
|
||||
*/
|
||||
int
|
||||
sfs_balloc(struct sfs_fs *sfs, daddr_t *diskblock)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = bitmap_alloc(sfs->sfs_freemap, diskblock);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
sfs->sfs_freemapdirty = true;
|
||||
|
||||
if (*diskblock >= sfs->sfs_sb.sb_nblocks) {
|
||||
panic("sfs: %s: balloc: invalid block %u\n",
|
||||
sfs->sfs_sb.sb_volname, *diskblock);
|
||||
}
|
||||
|
||||
/* Clear block before returning it */
|
||||
result = sfs_clearblock(sfs, *diskblock);
|
||||
if (result) {
|
||||
bitmap_unmark(sfs->sfs_freemap, *diskblock);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free a block.
|
||||
*/
|
||||
void
|
||||
sfs_bfree(struct sfs_fs *sfs, daddr_t diskblock)
|
||||
{
|
||||
bitmap_unmark(sfs->sfs_freemap, diskblock);
|
||||
sfs->sfs_freemapdirty = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a block is in use.
|
||||
*/
|
||||
int
|
||||
sfs_bused(struct sfs_fs *sfs, daddr_t diskblock)
|
||||
{
|
||||
if (diskblock >= sfs->sfs_sb.sb_nblocks) {
|
||||
panic("sfs: %s: sfs_bused called on out of range block %u\n",
|
||||
sfs->sfs_sb.sb_volname, diskblock);
|
||||
}
|
||||
return bitmap_isset(sfs->sfs_freemap, diskblock);
|
||||
}
|
||||
|
303
kern/fs/sfs/sfs_bmap.c
Normal file
303
kern/fs/sfs/sfs_bmap.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
|
||||
* The President and Fellows of Harvard College.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SFS filesystem
|
||||
*
|
||||
* Block mapping logic.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <vfs.h>
|
||||
#include <sfs.h>
|
||||
#include "sfsprivate.h"
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
int
|
||||
sfs_bmap(struct sfs_vnode *sv, uint32_t fileblock, bool doalloc,
|
||||
daddr_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 uint32_t idbuf[SFS_DBPERIDB];
|
||||
|
||||
struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
|
||||
daddr_t block;
|
||||
daddr_t idblock;
|
||||
uint32_t idnum, idoff;
|
||||
int result;
|
||||
|
||||
KASSERT(sizeof(idbuf)==SFS_BLOCKSIZE);
|
||||
|
||||
/* Since we're using a static buffer, we'd better be locked. */
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
/*
|
||||
* 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 = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hand back the block
|
||||
*/
|
||||
if (block != 0 && !sfs_bused(sfs, block)) {
|
||||
panic("sfs: %s: Data block %u (block %u of file %u) "
|
||||
"marked free\n", sfs->sfs_sb.sb_volname,
|
||||
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 >= SFS_NINDIRECT) {
|
||||
return EFBIG;
|
||||
}
|
||||
|
||||
/* 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 = true;
|
||||
|
||||
/* Clear the indirect block buffer */
|
||||
bzero(idbuf, sizeof(idbuf));
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* We already have an indirect block allocated; load it.
|
||||
*/
|
||||
result = sfs_readblock(sfs, idblock, idbuf, sizeof(idbuf));
|
||||
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_writeblock(sfs, idblock, idbuf, sizeof(idbuf));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hand back the result and return. */
|
||||
if (block != 0 && !sfs_bused(sfs, block)) {
|
||||
panic("sfs: %s: Data block %u (block %u of file %u) "
|
||||
"marked free\n", sfs->sfs_sb.sb_volname,
|
||||
block, fileblock, sv->sv_ino);
|
||||
}
|
||||
*diskblock = block;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for ftruncate() and from sfs_reclaim.
|
||||
*/
|
||||
int
|
||||
sfs_itrunc(struct sfs_vnode *sv, 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 uint32_t idbuf[SFS_DBPERIDB];
|
||||
|
||||
struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
|
||||
|
||||
/* Length in blocks (divide rounding up) */
|
||||
uint32_t blocklen = DIVROUNDUP(len, SFS_BLOCKSIZE);
|
||||
|
||||
uint32_t i, j;
|
||||
daddr_t block, idblock;
|
||||
uint32_t baseblock, highblock;
|
||||
int result;
|
||||
int hasnonzero, iddirty;
|
||||
|
||||
KASSERT(sizeof(idbuf)==SFS_BLOCKSIZE);
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
/*
|
||||
* 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 = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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_readblock(sfs, idblock, idbuf, sizeof(idbuf));
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
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 = true;
|
||||
}
|
||||
else if (iddirty) {
|
||||
/* The indirect block is dirty; write it back */
|
||||
result = sfs_writeblock(sfs, idblock, idbuf,
|
||||
sizeof(idbuf));
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the file size */
|
||||
sv->sv_i.sfi_size = len;
|
||||
|
||||
/* Mark the inode dirty */
|
||||
sv->sv_dirty = true;
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
239
kern/fs/sfs/sfs_dir.c
Normal file
239
kern/fs/sfs/sfs_dir.c
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
|
||||
* The President and Fellows of Harvard College.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SFS filesystem
|
||||
*
|
||||
* Directory I/O
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <vfs.h>
|
||||
#include <sfs.h>
|
||||
#include "sfsprivate.h"
|
||||
|
||||
/*
|
||||
* Read the directory entry out of slot SLOT of a directory vnode.
|
||||
* The "slot" is the index of the directory entry, starting at 0.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_readdir(struct sfs_vnode *sv, int slot, struct sfs_direntry *sd)
|
||||
{
|
||||
off_t actualpos;
|
||||
|
||||
/* Compute the actual position in the directory to read. */
|
||||
actualpos = slot * sizeof(struct sfs_direntry);
|
||||
|
||||
return sfs_metaio(sv, actualpos, sd, sizeof(*sd), UIO_READ);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write (overwrite) the directory entry in slot SLOT of a directory
|
||||
* vnode.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_writedir(struct sfs_vnode *sv, int slot, struct sfs_direntry *sd)
|
||||
{
|
||||
off_t actualpos;
|
||||
|
||||
/* Compute the actual position in the directory. */
|
||||
KASSERT(slot>=0);
|
||||
actualpos = slot * sizeof(struct sfs_direntry);
|
||||
|
||||
return sfs_metaio(sv, actualpos, sd, sizeof(*sd), UIO_WRITE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the number of entries in a directory.
|
||||
* This actually computes the number of existing slots, and does not
|
||||
* account for empty slots.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_dir_nentries(struct sfs_vnode *sv)
|
||||
{
|
||||
struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
|
||||
off_t size;
|
||||
|
||||
KASSERT(sv->sv_i.sfi_type == SFS_TYPE_DIR);
|
||||
|
||||
size = sv->sv_i.sfi_size;
|
||||
if (size % sizeof(struct sfs_direntry) != 0) {
|
||||
panic("sfs: %s: directory %u: Invalid size %llu\n",
|
||||
sfs->sfs_sb.sb_volname, sv->sv_ino, size);
|
||||
}
|
||||
|
||||
return size / sizeof(struct sfs_direntry);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search a directory for a particular filename in a directory, and
|
||||
* return its inode number, its slot, and/or the slot number of an
|
||||
* empty directory slot if one is found.
|
||||
*/
|
||||
int
|
||||
sfs_dir_findname(struct sfs_vnode *sv, const char *name,
|
||||
uint32_t *ino, int *slot, int *emptyslot)
|
||||
{
|
||||
struct sfs_direntry tsd;
|
||||
int found, nentries, i, result;
|
||||
|
||||
nentries = sfs_dir_nentries(sv);
|
||||
|
||||
/* For each slot... */
|
||||
found = 0;
|
||||
for (i=0; i<nentries; i++) {
|
||||
|
||||
/* Read the entry from that slot */
|
||||
result = sfs_readdir(sv, i, &tsd);
|
||||
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... */
|
||||
KASSERT(found==0);
|
||||
|
||||
found = 1;
|
||||
if (slot != NULL) {
|
||||
*slot = i;
|
||||
}
|
||||
if (ino != NULL) {
|
||||
*ino = tsd.sfd_ino;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found ? 0 : ENOENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a link in a directory to the specified inode by number, with
|
||||
* the specified name, and optionally hand back the slot.
|
||||
*/
|
||||
int
|
||||
sfs_dir_link(struct sfs_vnode *sv, const char *name, uint32_t ino, int *slot)
|
||||
{
|
||||
int emptyslot = -1;
|
||||
int result;
|
||||
struct sfs_direntry sd;
|
||||
|
||||
/* Look up the name. We want to make sure it *doesn't* exist. */
|
||||
result = sfs_dir_findname(sv, name, NULL, NULL, &emptyslot);
|
||||
if (result!=0 && result!=ENOENT) {
|
||||
return result;
|
||||
}
|
||||
if (result==0) {
|
||||
return EEXIST;
|
||||
}
|
||||
|
||||
if (strlen(name)+1 > sizeof(sd.sfd_name)) {
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
|
||||
/* If we didn't get an empty slot, add the entry at the end. */
|
||||
if (emptyslot < 0) {
|
||||
emptyslot = sfs_dir_nentries(sv);
|
||||
}
|
||||
|
||||
/* Set up the entry. */
|
||||
bzero(&sd, sizeof(sd));
|
||||
sd.sfd_ino = ino;
|
||||
strcpy(sd.sfd_name, name);
|
||||
|
||||
/* Hand back the slot, if so requested. */
|
||||
if (slot) {
|
||||
*slot = emptyslot;
|
||||
}
|
||||
|
||||
/* Write the entry. */
|
||||
return sfs_writedir(sv, emptyslot, &sd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlink a name in a directory, by slot number.
|
||||
*/
|
||||
int
|
||||
sfs_dir_unlink(struct sfs_vnode *sv, int slot)
|
||||
{
|
||||
struct sfs_direntry sd;
|
||||
|
||||
/* Initialize a suitable directory entry... */
|
||||
bzero(&sd, sizeof(sd));
|
||||
sd.sfd_ino = SFS_NOINO;
|
||||
|
||||
/* ... and write it */
|
||||
return sfs_writedir(sv, slot, &sd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for a name in a directory and hand back a vnode for the
|
||||
* file, if there is one.
|
||||
*/
|
||||
int
|
||||
sfs_lookonce(struct sfs_vnode *sv, const char *name,
|
||||
struct sfs_vnode **ret,
|
||||
int *slot)
|
||||
{
|
||||
struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
|
||||
uint32_t ino;
|
||||
int result;
|
||||
|
||||
result = sfs_dir_findname(sv, name, &ino, slot, NULL);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = sfs_loadvnode(sfs, ino, SFS_TYPE_INVAL, ret);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((*ret)->sv_i.sfi_linkcount == 0) {
|
||||
panic("sfs: %s: name %s (inode %u) in dir %u has "
|
||||
"linkcount 0\n", sfs->sfs_sb.sb_volname,
|
||||
name, (*ret)->sv_ino, sv->sv_ino);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
481
kern/fs/sfs/sfs_fsops.c
Normal file
481
kern/fs/sfs/sfs_fsops.c
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
|
||||
* The President and Fellows of Harvard College.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SFS filesystem
|
||||
*
|
||||
* Filesystem-level interface routines.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <array.h>
|
||||
#include <bitmap.h>
|
||||
#include <uio.h>
|
||||
#include <vfs.h>
|
||||
#include <device.h>
|
||||
#include <sfs.h>
|
||||
#include "sfsprivate.h"
|
||||
|
||||
|
||||
/* Shortcuts for the size macros in kern/sfs.h */
|
||||
#define SFS_FS_NBLOCKS(sfs) ((sfs)->sfs_sb.sb_nblocks)
|
||||
#define SFS_FS_FREEMAPBITS(sfs) SFS_FREEMAPBITS(SFS_FS_NBLOCKS(sfs))
|
||||
#define SFS_FS_FREEMAPBLOCKS(sfs) SFS_FREEMAPBLOCKS(SFS_FS_NBLOCKS(sfs))
|
||||
|
||||
/*
|
||||
* Routine for doing I/O (reads or writes) on the free block bitmap.
|
||||
* We always do the whole bitmap at once; writing individual sectors
|
||||
* might or might not be a worthwhile optimization.
|
||||
*
|
||||
* The free block bitmap consists of SFS_FREEMAPBLOCKS 512-byte
|
||||
* sectors of bits, one bit for each sector on the filesystem. The
|
||||
* number of blocks in the bitmap is thus rounded up to the nearest
|
||||
* multiple of 512*8 = 4096. (This rounded number is SFS_FREEMAPBITS.)
|
||||
* This means that the bitmap will (in general) contain space for some
|
||||
* number of invalid sectors that are actually beyond the end of the
|
||||
* disk device. This is ok. These sectors are supposed to be marked
|
||||
* "in use" by mksfs and never get marked "free".
|
||||
*
|
||||
* The sectors used by the superblock and the bitmap itself are
|
||||
* likewise marked in use by mksfs.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_freemapio(struct sfs_fs *sfs, enum uio_rw rw)
|
||||
{
|
||||
uint32_t j, freemapblocks;
|
||||
char *freemapdata;
|
||||
int result;
|
||||
|
||||
/* Number of blocks in the free block bitmap. */
|
||||
freemapblocks = SFS_FS_FREEMAPBLOCKS(sfs);
|
||||
|
||||
/* Pointer to our freemap data in memory. */
|
||||
freemapdata = bitmap_getdata(sfs->sfs_freemap);
|
||||
|
||||
/* For each block in the free block bitmap... */
|
||||
for (j=0; j<freemapblocks; j++) {
|
||||
|
||||
/* Get a pointer to its data */
|
||||
void *ptr = freemapdata + j*SFS_BLOCKSIZE;
|
||||
|
||||
/* and read or write it. The freemap starts at sector 2. */
|
||||
if (rw == UIO_READ) {
|
||||
result = sfs_readblock(sfs, SFS_FREEMAP_START+j, ptr,
|
||||
SFS_BLOCKSIZE);
|
||||
}
|
||||
else {
|
||||
result = sfs_writeblock(sfs, SFS_FREEMAP_START+j, ptr,
|
||||
SFS_BLOCKSIZE);
|
||||
}
|
||||
|
||||
/* If we failed, stop. */
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync routine for the vnode table.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_sync_vnodes(struct sfs_fs *sfs)
|
||||
{
|
||||
unsigned i, num;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync routine for the freemap.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_sync_freemap(struct sfs_fs *sfs)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (sfs->sfs_freemapdirty) {
|
||||
result = sfs_freemapio(sfs, UIO_WRITE);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
sfs->sfs_freemapdirty = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync routine for the superblock.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_sync_superblock(struct sfs_fs *sfs)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (sfs->sfs_superdirty) {
|
||||
result = sfs_writeblock(sfs, SFS_SUPER_BLOCK, &sfs->sfs_sb,
|
||||
sizeof(sfs->sfs_sb));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
sfs->sfs_superdirty = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync routine. This is what gets invoked if you do FS_SYNC on the
|
||||
* sfs filesystem structure.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_sync(struct fs *fs)
|
||||
{
|
||||
struct sfs_fs *sfs;
|
||||
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;
|
||||
|
||||
/* If any vnodes need to be written, write them. */
|
||||
result = sfs_sync_vnodes(sfs);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* If the free block map needs to be written, write it. */
|
||||
result = sfs_sync_freemap(sfs);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* If the superblock needs to be written, write it. */
|
||||
result = sfs_sync_superblock(sfs);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine to retrieve the volume name. Filesystems can be referred
|
||||
* to by their volume name followed by a colon as well as the name
|
||||
* of the device they're mounted on.
|
||||
*/
|
||||
static
|
||||
const char *
|
||||
sfs_getvolname(struct fs *fs)
|
||||
{
|
||||
struct sfs_fs *sfs = fs->fs_data;
|
||||
const char *ret;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
ret = sfs->sfs_sb.sb_volname;
|
||||
vfs_biglock_release();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destructor for struct sfs_fs.
|
||||
*/
|
||||
static
|
||||
void
|
||||
sfs_fs_destroy(struct sfs_fs *sfs)
|
||||
{
|
||||
if (sfs->sfs_freemap != NULL) {
|
||||
bitmap_destroy(sfs->sfs_freemap);
|
||||
}
|
||||
vnodearray_destroy(sfs->sfs_vnodes);
|
||||
KASSERT(sfs->sfs_device == NULL);
|
||||
kfree(sfs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmount code.
|
||||
*
|
||||
* VFS calls FS_SYNC on the filesystem prior to unmounting it.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_unmount(struct fs *fs)
|
||||
{
|
||||
struct sfs_fs *sfs = fs->fs_data;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
/* Do we have any files open? If so, can't unmount. */
|
||||
if (vnodearray_num(sfs->sfs_vnodes) > 0) {
|
||||
vfs_biglock_release();
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
/* We should have just had sfs_sync called. */
|
||||
KASSERT(sfs->sfs_superdirty == false);
|
||||
KASSERT(sfs->sfs_freemapdirty == false);
|
||||
|
||||
/* The vfs layer takes care of the device for us */
|
||||
sfs->sfs_device = NULL;
|
||||
|
||||
/* Destroy the fs object; once we start nuking stuff we can't fail. */
|
||||
sfs_fs_destroy(sfs);
|
||||
|
||||
/* nothing else to do */
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* File system operations table.
|
||||
*/
|
||||
static const struct fs_ops sfs_fsops = {
|
||||
.fsop_sync = sfs_sync,
|
||||
.fsop_getvolname = sfs_getvolname,
|
||||
.fsop_getroot = sfs_getroot,
|
||||
.fsop_unmount = sfs_unmount,
|
||||
};
|
||||
|
||||
/*
|
||||
* Basic constructor for struct sfs_fs. This initializes all fields
|
||||
* but skips stuff that requires reading the volume, like allocating
|
||||
* the freemap.
|
||||
*/
|
||||
static
|
||||
struct sfs_fs *
|
||||
sfs_fs_create(void)
|
||||
{
|
||||
struct sfs_fs *sfs;
|
||||
|
||||
/*
|
||||
* Make sure our on-disk structures aren't messed up
|
||||
*/
|
||||
COMPILE_ASSERT(sizeof(struct sfs_superblock)==SFS_BLOCKSIZE);
|
||||
COMPILE_ASSERT(sizeof(struct sfs_dinode)==SFS_BLOCKSIZE);
|
||||
COMPILE_ASSERT(SFS_BLOCKSIZE % sizeof(struct sfs_direntry) == 0);
|
||||
|
||||
/* Allocate object */
|
||||
sfs = kmalloc(sizeof(struct sfs_fs));
|
||||
if (sfs==NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill in fields
|
||||
*/
|
||||
|
||||
/* abstract vfs-level fs */
|
||||
sfs->sfs_absfs.fs_data = sfs;
|
||||
sfs->sfs_absfs.fs_ops = &sfs_fsops;
|
||||
|
||||
/* superblock */
|
||||
/* (ignore sfs_super, we'll read in over it shortly) */
|
||||
sfs->sfs_superdirty = false;
|
||||
|
||||
/* device we mount on */
|
||||
sfs->sfs_device = NULL;
|
||||
|
||||
/* vnode table */
|
||||
sfs->sfs_vnodes = vnodearray_create();
|
||||
if (sfs->sfs_vnodes == NULL) {
|
||||
goto cleanup_object;
|
||||
}
|
||||
|
||||
/* freemap */
|
||||
sfs->sfs_freemap = NULL;
|
||||
sfs->sfs_freemapdirty = false;
|
||||
|
||||
return sfs;
|
||||
|
||||
cleanup_object:
|
||||
kfree(sfs);
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mount routine.
|
||||
*
|
||||
* The way mount works is that you call vfs_mount and pass it a
|
||||
* filesystem-specific mount routine. Said routine takes a device and
|
||||
* hands back a pointer to an abstract filesystem. You can also pass
|
||||
* a void pointer through.
|
||||
*
|
||||
* This organization makes cleanup on error easier. Hint: it may also
|
||||
* be easier to synchronize correctly; it is important not to get two
|
||||
* filesystems with the same name mounted at once, or two filesystems
|
||||
* mounted on the same device at once.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_domount(void *options, struct device *dev, struct fs **ret)
|
||||
{
|
||||
int result;
|
||||
struct sfs_fs *sfs;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
/* We don't pass any options through mount */
|
||||
(void)options;
|
||||
|
||||
/*
|
||||
* We can't mount on devices with the wrong sector size.
|
||||
*
|
||||
* (Note: for all intents and purposes here, "sector" and
|
||||
* "block" are interchangeable terms. Technically a filesystem
|
||||
* block may be composed of several hardware sectors, but we
|
||||
* don't do that in sfs.)
|
||||
*/
|
||||
if (dev->d_blocksize != SFS_BLOCKSIZE) {
|
||||
vfs_biglock_release();
|
||||
kprintf("sfs: Cannot mount on device with blocksize %zu\n",
|
||||
dev->d_blocksize);
|
||||
return ENXIO;
|
||||
}
|
||||
|
||||
sfs = sfs_fs_create();
|
||||
if (sfs == NULL) {
|
||||
vfs_biglock_release();
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* Set the device so we can use sfs_readblock() */
|
||||
sfs->sfs_device = dev;
|
||||
|
||||
/* Load superblock */
|
||||
result = sfs_readblock(sfs, SFS_SUPER_BLOCK, &sfs->sfs_sb,
|
||||
sizeof(sfs->sfs_sb));
|
||||
if (result) {
|
||||
sfs->sfs_device = NULL;
|
||||
sfs_fs_destroy(sfs);
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Make some simple sanity checks */
|
||||
|
||||
if (sfs->sfs_sb.sb_magic != SFS_MAGIC) {
|
||||
kprintf("sfs: Wrong magic number in superblock "
|
||||
"(0x%x, should be 0x%x)\n",
|
||||
sfs->sfs_sb.sb_magic,
|
||||
SFS_MAGIC);
|
||||
sfs->sfs_device = NULL;
|
||||
sfs_fs_destroy(sfs);
|
||||
vfs_biglock_release();
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (sfs->sfs_sb.sb_nblocks > dev->d_blocks) {
|
||||
kprintf("sfs: warning - fs has %u blocks, device has %u\n",
|
||||
sfs->sfs_sb.sb_nblocks, dev->d_blocks);
|
||||
}
|
||||
|
||||
/* Ensure null termination of the volume name */
|
||||
sfs->sfs_sb.sb_volname[sizeof(sfs->sfs_sb.sb_volname)-1] = 0;
|
||||
|
||||
/* Load free block bitmap */
|
||||
sfs->sfs_freemap = bitmap_create(SFS_FS_FREEMAPBITS(sfs));
|
||||
if (sfs->sfs_freemap == NULL) {
|
||||
sfs->sfs_device = NULL;
|
||||
sfs_fs_destroy(sfs);
|
||||
vfs_biglock_release();
|
||||
return ENOMEM;
|
||||
}
|
||||
result = sfs_freemapio(sfs, UIO_READ);
|
||||
if (result) {
|
||||
sfs->sfs_device = NULL;
|
||||
sfs_fs_destroy(sfs);
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Hand back the abstract fs */
|
||||
*ret = &sfs->sfs_absfs;
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Actual function called from high-level code to mount an sfs.
|
||||
*/
|
||||
int
|
||||
sfs_mount(const char *device)
|
||||
{
|
||||
return vfs_mount(device, NULL, sfs_domount);
|
||||
}
|
320
kern/fs/sfs/sfs_inode.c
Normal file
320
kern/fs/sfs/sfs_inode.c
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
|
||||
* The President and Fellows of Harvard College.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SFS filesystem
|
||||
*
|
||||
* Inode-level operations and vnode/inode lifecycle logic.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <vfs.h>
|
||||
#include <sfs.h>
|
||||
#include "sfsprivate.h"
|
||||
|
||||
|
||||
/*
|
||||
* Write an on-disk inode structure back out to disk.
|
||||
*/
|
||||
int
|
||||
sfs_sync_inode(struct sfs_vnode *sv)
|
||||
{
|
||||
struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
|
||||
int result;
|
||||
|
||||
if (sv->sv_dirty) {
|
||||
result = sfs_writeblock(sfs, sv->sv_ino, &sv->sv_i,
|
||||
sizeof(sv->sv_i));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
sv->sv_dirty = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the vnode refcount (in-memory usage count) hits zero.
|
||||
*
|
||||
* This function should try to avoid returning errors other than EBUSY.
|
||||
*/
|
||||
int
|
||||
sfs_reclaim(struct vnode *v)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
struct sfs_fs *sfs = v->vn_fs->fs_data;
|
||||
unsigned ix, i, num;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
/*
|
||||
* Make sure someone else hasn't picked up the vnode since the
|
||||
* decision was made to reclaim it. (You must also synchronize
|
||||
* this with sfs_loadvnode.)
|
||||
*/
|
||||
spinlock_acquire(&v->vn_countlock);
|
||||
if (v->vn_refcount != 1) {
|
||||
|
||||
/* consume the reference VOP_DECREF gave us */
|
||||
KASSERT(v->vn_refcount>1);
|
||||
v->vn_refcount--;
|
||||
|
||||
spinlock_release(&v->vn_countlock);
|
||||
vfs_biglock_release();
|
||||
return EBUSY;
|
||||
}
|
||||
spinlock_release(&v->vn_countlock);
|
||||
|
||||
/* If there are no on-disk references to the file either, erase it. */
|
||||
if (sv->sv_i.sfi_linkcount == 0) {
|
||||
result = sfs_itrunc(sv, 0);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sync the inode to disk */
|
||||
result = sfs_sync_inode(sv);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* If there are no on-disk references, discard the inode */
|
||||
if (sv->sv_i.sfi_linkcount==0) {
|
||||
sfs_bfree(sfs, sv->sv_ino);
|
||||
}
|
||||
|
||||
/* Remove the vnode structure from the table in the struct sfs_fs. */
|
||||
num = vnodearray_num(sfs->sfs_vnodes);
|
||||
ix = num;
|
||||
for (i=0; i<num; i++) {
|
||||
struct vnode *v2 = vnodearray_get(sfs->sfs_vnodes, i);
|
||||
struct sfs_vnode *sv2 = v2->vn_data;
|
||||
if (sv2 == sv) {
|
||||
ix = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ix == num) {
|
||||
panic("sfs: %s: reclaim vnode %u not in vnode pool\n",
|
||||
sfs->sfs_sb.sb_volname, sv->sv_ino);
|
||||
}
|
||||
vnodearray_remove(sfs->sfs_vnodes, ix);
|
||||
|
||||
vnode_cleanup(&sv->sv_absvn);
|
||||
|
||||
vfs_biglock_release();
|
||||
|
||||
/* Release the storage for the vnode structure itself. */
|
||||
kfree(sv);
|
||||
|
||||
/* Done */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to load a inode into memory as a vnode, or dig up one
|
||||
* that's already resident.
|
||||
*/
|
||||
int
|
||||
sfs_loadvnode(struct sfs_fs *sfs, uint32_t ino, int forcetype,
|
||||
struct sfs_vnode **ret)
|
||||
{
|
||||
struct vnode *v;
|
||||
struct sfs_vnode *sv;
|
||||
const struct vnode_ops *ops;
|
||||
unsigned i, num;
|
||||
int result;
|
||||
|
||||
/* Look in the vnodes table */
|
||||
num = vnodearray_num(sfs->sfs_vnodes);
|
||||
|
||||
/* Linear search. Is this too slow? You decide. */
|
||||
for (i=0; i<num; i++) {
|
||||
v = vnodearray_get(sfs->sfs_vnodes, i);
|
||||
sv = v->vn_data;
|
||||
|
||||
/* Every inode in memory must be in an allocated block */
|
||||
if (!sfs_bused(sfs, sv->sv_ino)) {
|
||||
panic("sfs: %s: Found inode %u in unallocated block\n",
|
||||
sfs->sfs_sb.sb_volname, sv->sv_ino);
|
||||
}
|
||||
|
||||
if (sv->sv_ino==ino) {
|
||||
/* Found */
|
||||
|
||||
/* forcetype is only allowed when creating objects */
|
||||
KASSERT(forcetype==SFS_TYPE_INVAL);
|
||||
|
||||
VOP_INCREF(&sv->sv_absvn);
|
||||
*ret = sv;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Didn't have it loaded; load it */
|
||||
|
||||
sv = kmalloc(sizeof(struct sfs_vnode));
|
||||
if (sv==NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/* Must be in an allocated block */
|
||||
if (!sfs_bused(sfs, ino)) {
|
||||
panic("sfs: %s: Tried to load inode %u from "
|
||||
"unallocated block\n", sfs->sfs_sb.sb_volname, ino);
|
||||
}
|
||||
|
||||
/* Read the block the inode is in */
|
||||
result = sfs_readblock(sfs, ino, &sv->sv_i, sizeof(sv->sv_i));
|
||||
if (result) {
|
||||
kfree(sv);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Not dirty yet */
|
||||
sv->sv_dirty = false;
|
||||
|
||||
/*
|
||||
* FORCETYPE is set if we're creating a new file, because the
|
||||
* block on disk will have been zeroed out by sfs_balloc and
|
||||
* thus the type recorded there will be SFS_TYPE_INVAL.
|
||||
*/
|
||||
if (forcetype != SFS_TYPE_INVAL) {
|
||||
KASSERT(sv->sv_i.sfi_type == SFS_TYPE_INVAL);
|
||||
sv->sv_i.sfi_type = forcetype;
|
||||
sv->sv_dirty = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Choose the function table based on the object type.
|
||||
*/
|
||||
switch (sv->sv_i.sfi_type) {
|
||||
case SFS_TYPE_FILE:
|
||||
ops = &sfs_fileops;
|
||||
break;
|
||||
case SFS_TYPE_DIR:
|
||||
ops = &sfs_dirops;
|
||||
break;
|
||||
default:
|
||||
panic("sfs: %s: loadvnode: Invalid inode type "
|
||||
"(inode %u, type %u)\n", sfs->sfs_sb.sb_volname,
|
||||
ino, sv->sv_i.sfi_type);
|
||||
}
|
||||
|
||||
/* Call the common vnode initializer */
|
||||
result = vnode_init(&sv->sv_absvn, ops, &sfs->sfs_absfs, sv);
|
||||
if (result) {
|
||||
kfree(sv);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Set the other fields in our vnode structure */
|
||||
sv->sv_ino = ino;
|
||||
|
||||
/* Add it to our table */
|
||||
result = vnodearray_add(sfs->sfs_vnodes, &sv->sv_absvn, NULL);
|
||||
if (result) {
|
||||
vnode_cleanup(&sv->sv_absvn);
|
||||
kfree(sv);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Hand it back */
|
||||
*ret = sv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new filesystem object and hand back its vnode.
|
||||
*/
|
||||
int
|
||||
sfs_makeobj(struct sfs_fs *sfs, int type, struct sfs_vnode **ret)
|
||||
{
|
||||
uint32_t ino;
|
||||
int result;
|
||||
|
||||
/*
|
||||
* First, get an inode. (Each inode is a block, and the inode
|
||||
* number is the block number, so just get a block.)
|
||||
*/
|
||||
|
||||
result = sfs_balloc(sfs, &ino);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now load a vnode for it.
|
||||
*/
|
||||
|
||||
result = sfs_loadvnode(sfs, ino, type, ret);
|
||||
if (result) {
|
||||
sfs_bfree(sfs, ino);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get vnode for the root of the filesystem.
|
||||
* The root vnode is always found in block 1 (SFS_ROOTDIR_INO).
|
||||
*/
|
||||
int
|
||||
sfs_getroot(struct fs *fs, struct vnode **ret)
|
||||
{
|
||||
struct sfs_fs *sfs = fs->fs_data;
|
||||
struct sfs_vnode *sv;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
result = sfs_loadvnode(sfs, SFS_ROOTDIR_INO, SFS_TYPE_INVAL, &sv);
|
||||
if (result) {
|
||||
kprintf("sfs: %s: getroot: Cannot load root vnode\n",
|
||||
sfs->sfs_sb.sb_volname);
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
if (sv->sv_i.sfi_type != SFS_TYPE_DIR) {
|
||||
kprintf("sfs: %s: getroot: not directory (type %u)\n",
|
||||
sfs->sfs_sb.sb_volname, sv->sv_i.sfi_type);
|
||||
vfs_biglock_release();
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
vfs_biglock_release();
|
||||
|
||||
*ret = &sv->sv_absvn;
|
||||
return 0;
|
||||
}
|
480
kern/fs/sfs/sfs_io.c
Normal file
480
kern/fs/sfs/sfs_io.c
Normal file
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
|
||||
* The President and Fellows of Harvard College.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SFS filesystem
|
||||
*
|
||||
* I/O plumbing.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <uio.h>
|
||||
#include <vfs.h>
|
||||
#include <device.h>
|
||||
#include <sfs.h>
|
||||
#include "sfsprivate.h"
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Basic block-level I/O routines
|
||||
|
||||
/*
|
||||
* Note: sfs_readblock is used to read the superblock
|
||||
* early in mount, before sfs is fully (or even mostly)
|
||||
* initialized, and so may not use anything from sfs
|
||||
* except sfs_device.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Read or write a block, retrying I/O errors.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_rwblock(struct sfs_fs *sfs, struct uio *uio)
|
||||
{
|
||||
int result;
|
||||
int tries=0;
|
||||
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
DEBUG(DB_SFS, "sfs: %s %llu\n",
|
||||
uio->uio_rw == UIO_READ ? "read" : "write",
|
||||
uio->uio_offset / SFS_BLOCKSIZE);
|
||||
|
||||
retry:
|
||||
result = DEVOP_IO(sfs->sfs_device, uio);
|
||||
if (result == EINVAL) {
|
||||
/*
|
||||
* This means the sector we requested was out of range,
|
||||
* or the seek address we gave wasn't sector-aligned,
|
||||
* or a couple of other things that are our fault.
|
||||
*/
|
||||
panic("sfs: %s: DEVOP_IO returned EINVAL\n",
|
||||
sfs->sfs_sb.sb_volname);
|
||||
}
|
||||
if (result == EIO) {
|
||||
if (tries == 0) {
|
||||
tries++;
|
||||
kprintf("sfs: %s: block %llu I/O error, retrying\n",
|
||||
sfs->sfs_sb.sb_volname,
|
||||
uio->uio_offset / SFS_BLOCKSIZE);
|
||||
goto retry;
|
||||
}
|
||||
else if (tries < 10) {
|
||||
tries++;
|
||||
goto retry;
|
||||
}
|
||||
else {
|
||||
kprintf("sfs: %s: block %llu I/O error, giving up "
|
||||
"after %d retries\n",
|
||||
sfs->sfs_sb.sb_volname,
|
||||
uio->uio_offset / SFS_BLOCKSIZE, tries);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a block.
|
||||
*/
|
||||
int
|
||||
sfs_readblock(struct sfs_fs *sfs, daddr_t block, void *data, size_t len)
|
||||
{
|
||||
struct iovec iov;
|
||||
struct uio ku;
|
||||
|
||||
KASSERT(len == SFS_BLOCKSIZE);
|
||||
|
||||
SFSUIO(&iov, &ku, data, block, UIO_READ);
|
||||
return sfs_rwblock(sfs, &ku);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a block.
|
||||
*/
|
||||
int
|
||||
sfs_writeblock(struct sfs_fs *sfs, daddr_t block, void *data, size_t len)
|
||||
{
|
||||
struct iovec iov;
|
||||
struct uio ku;
|
||||
|
||||
KASSERT(len == SFS_BLOCKSIZE);
|
||||
|
||||
SFSUIO(&iov, &ku, data, block, UIO_WRITE);
|
||||
return sfs_rwblock(sfs, &ku);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// File-level I/O
|
||||
|
||||
/*
|
||||
* 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,
|
||||
uint32_t skipstart, uint32_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_absvn.vn_fs->fs_data;
|
||||
daddr_t diskblock;
|
||||
uint32_t fileblock;
|
||||
int result;
|
||||
|
||||
/* Allocate missing blocks if and only if we're writing */
|
||||
bool doalloc = (uio->uio_rw==UIO_WRITE);
|
||||
|
||||
KASSERT(skipstart + len <= SFS_BLOCKSIZE);
|
||||
|
||||
/* We're using a global static buffer; it had better be locked */
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
/* 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.
|
||||
* Zero the buffer.
|
||||
*/
|
||||
KASSERT(uio->uio_rw == UIO_READ);
|
||||
bzero(iobuf, sizeof(iobuf));
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Read the block.
|
||||
*/
|
||||
result = sfs_readblock(sfs, diskblock, iobuf, sizeof(iobuf));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_writeblock(sfs, diskblock, iobuf, sizeof(iobuf));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 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_absvn.vn_fs->fs_data;
|
||||
daddr_t diskblock;
|
||||
uint32_t fileblock;
|
||||
int result;
|
||||
bool 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.
|
||||
*/
|
||||
KASSERT(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.
|
||||
*/
|
||||
KASSERT(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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do I/O of a whole region of data, whether or not it's block-aligned.
|
||||
*/
|
||||
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;
|
||||
|
||||
origresid = uio->uio_resid;
|
||||
|
||||
/*
|
||||
* 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 = sv->sv_i.sfi_size;
|
||||
off_t endpos = uio->uio_offset + uio->uio_resid;
|
||||
|
||||
if (uio->uio_offset >= size) {
|
||||
/* At or past EOF - just return */
|
||||
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)sv->sv_i.sfi_size) {
|
||||
sv->sv_i.sfi_size = uio->uio_offset;
|
||||
sv->sv_dirty = true;
|
||||
}
|
||||
|
||||
/* Add in any extra amount we couldn't read because of EOF */
|
||||
uio->uio_resid += extraresid;
|
||||
|
||||
/* Done */
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Metadata I/O
|
||||
|
||||
/*
|
||||
* 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;
|
||||
off_t endpos;
|
||||
uint32_t vnblock;
|
||||
uint32_t blockoffset;
|
||||
daddr_t diskblock;
|
||||
bool doalloc;
|
||||
int result;
|
||||
|
||||
/*
|
||||
* I/O buffer for metadata ops.
|
||||
*
|
||||
* 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 metaiobuf[SFS_BLOCKSIZE];
|
||||
|
||||
/* We're using a global static buffer; it had better be locked */
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
/* Figure out which block of the vnode (directory, whatever) this is */
|
||||
vnblock = actualpos / SFS_BLOCKSIZE;
|
||||
blockoffset = actualpos % SFS_BLOCKSIZE;
|
||||
|
||||
/* Get the disk block number */
|
||||
doalloc = (rw == UIO_WRITE);
|
||||
result = sfs_bmap(sv, vnblock, doalloc, &diskblock);
|
||||
if (result) {
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read the block */
|
||||
result = sfs_readblock(sfs, diskblock, metaiobuf, sizeof(metaiobuf));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (rw == UIO_READ) {
|
||||
/* Copy out the selected region */
|
||||
memcpy(data, metaiobuf + blockoffset, len);
|
||||
}
|
||||
else {
|
||||
/* Update the selected region */
|
||||
memcpy(metaiobuf + blockoffset, data, len);
|
||||
|
||||
/* Write the block back */
|
||||
result = sfs_writeblock(sfs, diskblock,
|
||||
metaiobuf, sizeof(metaiobuf));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Update the vnode size if needed */
|
||||
endpos = actualpos + len;
|
||||
if (endpos > (off_t)sv->sv_i.sfi_size) {
|
||||
sv->sv_i.sfi_size = endpos;
|
||||
sv->sv_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Done */
|
||||
return 0;
|
||||
}
|
650
kern/fs/sfs/sfs_vnops.c
Normal file
650
kern/fs/sfs/sfs_vnops.c
Normal file
@@ -0,0 +1,650 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
|
||||
* The President and Fellows of Harvard College.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SFS filesystem
|
||||
*
|
||||
* File-level (vnode) interface routines.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <kern/fcntl.h>
|
||||
#include <stat.h>
|
||||
#include <lib.h>
|
||||
#include <uio.h>
|
||||
#include <vfs.h>
|
||||
#include <sfs.h>
|
||||
#include "sfsprivate.h"
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Vnode operations.
|
||||
|
||||
/*
|
||||
* This is called on *each* open().
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_eachopen(struct vnode *v, int openflags)
|
||||
{
|
||||
/*
|
||||
* At this level we do not need to handle O_CREAT, O_EXCL,
|
||||
* O_TRUNC, or O_APPEND.
|
||||
*
|
||||
* Any of O_RDONLY, O_WRONLY, and O_RDWR are valid, so we don't need
|
||||
* to check that either.
|
||||
*/
|
||||
|
||||
(void)v;
|
||||
(void)openflags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called on *each* open() of a directory.
|
||||
* Directories may only be open for read.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_eachopendir(struct vnode *v, int openflags)
|
||||
{
|
||||
switch (openflags & O_ACCMODE) {
|
||||
case O_RDONLY:
|
||||
break;
|
||||
case O_WRONLY:
|
||||
case O_RDWR:
|
||||
default:
|
||||
return EISDIR;
|
||||
}
|
||||
if (openflags & O_APPEND) {
|
||||
return EISDIR;
|
||||
}
|
||||
|
||||
(void)v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for read(). sfs_io() does the work.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_read(struct vnode *v, struct uio *uio)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
int result;
|
||||
|
||||
KASSERT(uio->uio_rw==UIO_READ);
|
||||
|
||||
vfs_biglock_acquire();
|
||||
result = sfs_io(sv, uio);
|
||||
vfs_biglock_release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for write(). sfs_io() does the work.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_write(struct vnode *v, struct uio *uio)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
int result;
|
||||
|
||||
KASSERT(uio->uio_rw==UIO_WRITE);
|
||||
|
||||
vfs_biglock_acquire();
|
||||
result = sfs_io(sv, uio);
|
||||
vfs_biglock_release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for ioctl()
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_ioctl(struct vnode *v, int op, userptr_t data)
|
||||
{
|
||||
/*
|
||||
* No ioctls.
|
||||
*/
|
||||
|
||||
(void)v;
|
||||
(void)op;
|
||||
(void)data;
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for stat/fstat/lstat.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_stat(struct vnode *v, struct stat *statbuf)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
int result;
|
||||
|
||||
/* Fill in the stat structure */
|
||||
bzero(statbuf, sizeof(struct stat));
|
||||
|
||||
result = VOP_GETTYPE(v, &statbuf->st_mode);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
statbuf->st_size = sv->sv_i.sfi_size;
|
||||
statbuf->st_nlink = sv->sv_i.sfi_linkcount;
|
||||
|
||||
/* We don't support this yet */
|
||||
statbuf->st_blocks = 0;
|
||||
|
||||
/* Fill in other fields as desired/possible... */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the type of the file (types as per kern/stat.h)
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_gettype(struct vnode *v, uint32_t *ret)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
struct sfs_fs *sfs = v->vn_fs->fs_data;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
switch (sv->sv_i.sfi_type) {
|
||||
case SFS_TYPE_FILE:
|
||||
*ret = S_IFREG;
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
case SFS_TYPE_DIR:
|
||||
*ret = S_IFDIR;
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
panic("sfs: %s: gettype: Invalid inode type (inode %u, type %u)\n",
|
||||
sfs->sfs_sb.sb_volname, sv->sv_ino, sv->sv_i.sfi_type);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if seeking is allowed. The answer is "yes".
|
||||
*/
|
||||
static
|
||||
bool
|
||||
sfs_isseekable(struct vnode *v)
|
||||
{
|
||||
(void)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for fsync(), and also on filesystem unmount, global sync(),
|
||||
* and some other cases.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_fsync(struct vnode *v)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
result = sfs_sync_inode(sv);
|
||||
vfs_biglock_release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for mmap().
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_mmap(struct vnode *v /* add stuff as needed */)
|
||||
{
|
||||
(void)v;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Truncate a file.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_truncate(struct vnode *v, off_t len)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
|
||||
return sfs_itrunc(sv, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct sfs_vnode *sv = vv->vn_data;
|
||||
KASSERT(sv->sv_ino == SFS_ROOTDIR_INO);
|
||||
|
||||
/* send back the empty string - just return */
|
||||
|
||||
(void)uio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a file. If EXCL is set, insist that the filename not already
|
||||
* exist; otherwise, if it already exists, just open it.
|
||||
*/
|
||||
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;
|
||||
uint32_t ino;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
/* Look up the name */
|
||||
result = sfs_dir_findname(sv, name, &ino, NULL, NULL);
|
||||
if (result!=0 && result!=ENOENT) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* If it exists and we didn't want it to, fail */
|
||||
if (result==0 && excl) {
|
||||
vfs_biglock_release();
|
||||
return EEXIST;
|
||||
}
|
||||
|
||||
if (result==0) {
|
||||
/* We got something; load its vnode and return */
|
||||
result = sfs_loadvnode(sfs, ino, SFS_TYPE_INVAL, &newguy);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
*ret = &newguy->sv_absvn;
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Didn't exist - create it */
|
||||
result = sfs_makeobj(sfs, SFS_TYPE_FILE, &newguy);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
VOP_DECREF(&newguy->sv_absvn);
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Update the linkcount of the new file */
|
||||
newguy->sv_i.sfi_linkcount++;
|
||||
|
||||
/* and consequently mark it dirty. */
|
||||
newguy->sv_dirty = true;
|
||||
|
||||
*ret = &newguy->sv_absvn;
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a hard link to a file.
|
||||
* The VFS layer should prevent this being called unless both
|
||||
* vnodes are ours.
|
||||
*/
|
||||
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;
|
||||
int result;
|
||||
|
||||
KASSERT(file->vn_fs == dir->vn_fs);
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
/* Hard links to directories aren't allowed. */
|
||||
if (f->sv_i.sfi_type == SFS_TYPE_DIR) {
|
||||
vfs_biglock_release();
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Create the link */
|
||||
result = sfs_dir_link(sv, name, f->sv_ino, NULL);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* and update the link count, marking the inode dirty */
|
||||
f->sv_i.sfi_linkcount++;
|
||||
f->sv_dirty = true;
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a file.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_remove(struct vnode *dir, const char *name)
|
||||
{
|
||||
struct sfs_vnode *sv = dir->vn_data;
|
||||
struct sfs_vnode *victim;
|
||||
int slot;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
/* Look for the file and fetch a vnode for it. */
|
||||
result = sfs_lookonce(sv, name, &victim, &slot);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Erase its directory entry. */
|
||||
result = sfs_dir_unlink(sv, slot);
|
||||
if (result==0) {
|
||||
/* If we succeeded, decrement the link count. */
|
||||
KASSERT(victim->sv_i.sfi_linkcount > 0);
|
||||
victim->sv_i.sfi_linkcount--;
|
||||
victim->sv_dirty = true;
|
||||
}
|
||||
|
||||
/* Discard the reference that sfs_lookonce got us */
|
||||
VOP_DECREF(&victim->sv_absvn);
|
||||
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rename a file.
|
||||
*
|
||||
* Since we don't support subdirectories, assumes that the two
|
||||
* directories passed are the same.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_rename(struct vnode *d1, const char *n1,
|
||||
struct vnode *d2, const char *n2)
|
||||
{
|
||||
struct sfs_vnode *sv = d1->vn_data;
|
||||
struct sfs_fs *sfs = sv->sv_absvn.vn_fs->fs_data;
|
||||
struct sfs_vnode *g1;
|
||||
int slot1, slot2;
|
||||
int result, result2;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
KASSERT(d1==d2);
|
||||
KASSERT(sv->sv_ino == SFS_ROOTDIR_INO);
|
||||
|
||||
/* Look up the old name of the file and get its inode and slot number*/
|
||||
result = sfs_lookonce(sv, n1, &g1, &slot1);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/* We don't support subdirectories */
|
||||
KASSERT(g1->sv_i.sfi_type == SFS_TYPE_FILE);
|
||||
|
||||
/*
|
||||
* Link it under the new name.
|
||||
*
|
||||
* We could theoretically just overwrite the original
|
||||
* directory entry, except that we need to check to make sure
|
||||
* the new name doesn't already exist; might as well use the
|
||||
* existing link routine.
|
||||
*/
|
||||
result = sfs_dir_link(sv, n2, g1->sv_ino, &slot2);
|
||||
if (result) {
|
||||
goto puke;
|
||||
}
|
||||
|
||||
/* Increment the link count, and mark inode dirty */
|
||||
g1->sv_i.sfi_linkcount++;
|
||||
g1->sv_dirty = true;
|
||||
|
||||
/* Unlink the old slot */
|
||||
result = sfs_dir_unlink(sv, slot1);
|
||||
if (result) {
|
||||
goto puke_harder;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrement the link count again, and mark the inode dirty again,
|
||||
* in case it's been synced behind our back.
|
||||
*/
|
||||
KASSERT(g1->sv_i.sfi_linkcount>0);
|
||||
g1->sv_i.sfi_linkcount--;
|
||||
g1->sv_dirty = true;
|
||||
|
||||
/* Let go of the reference to g1 */
|
||||
VOP_DECREF(&g1->sv_absvn);
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
|
||||
puke_harder:
|
||||
/*
|
||||
* Error recovery: try to undo what we already did
|
||||
*/
|
||||
result2 = sfs_dir_unlink(sv, slot2);
|
||||
if (result2) {
|
||||
kprintf("sfs: %s: rename: %s\n",
|
||||
sfs->sfs_sb.sb_volname, strerror(result));
|
||||
kprintf("sfs: %s: rename: while cleaning up: %s\n",
|
||||
sfs->sfs_sb.sb_volname, strerror(result2));
|
||||
panic("sfs: %s: rename: Cannot recover\n",
|
||||
sfs->sfs_sb.sb_volname);
|
||||
}
|
||||
g1->sv_i.sfi_linkcount--;
|
||||
puke:
|
||||
/* Let go of the reference to g1 */
|
||||
VOP_DECREF(&g1->sv_absvn);
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* lookparent returns the last path component as a string and the
|
||||
* directory it's in as a vnode.
|
||||
*
|
||||
* Since we don't support subdirectories, this is very easy -
|
||||
* return the root dir and copy the path.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_lookparent(struct vnode *v, char *path, struct vnode **ret,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
if (sv->sv_i.sfi_type != SFS_TYPE_DIR) {
|
||||
vfs_biglock_release();
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
if (strlen(path)+1 > buflen) {
|
||||
vfs_biglock_release();
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
strcpy(buf, path);
|
||||
|
||||
VOP_INCREF(&sv->sv_absvn);
|
||||
*ret = &sv->sv_absvn;
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup gets a vnode for a pathname.
|
||||
*
|
||||
* Since we don't support subdirectories, it's easy - just look up the
|
||||
* name.
|
||||
*/
|
||||
static
|
||||
int
|
||||
sfs_lookup(struct vnode *v, char *path, struct vnode **ret)
|
||||
{
|
||||
struct sfs_vnode *sv = v->vn_data;
|
||||
struct sfs_vnode *final;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
if (sv->sv_i.sfi_type != SFS_TYPE_DIR) {
|
||||
vfs_biglock_release();
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
result = sfs_lookonce(sv, path, &final, NULL);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
*ret = &final->sv_absvn;
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Ops tables
|
||||
|
||||
/*
|
||||
* Function table for sfs files.
|
||||
*/
|
||||
const struct vnode_ops sfs_fileops = {
|
||||
.vop_magic = VOP_MAGIC, /* mark this a valid vnode ops table */
|
||||
|
||||
.vop_eachopen = sfs_eachopen,
|
||||
.vop_reclaim = sfs_reclaim,
|
||||
|
||||
.vop_read = sfs_read,
|
||||
.vop_readlink = vopfail_uio_notdir,
|
||||
.vop_getdirentry = vopfail_uio_notdir,
|
||||
.vop_write = sfs_write,
|
||||
.vop_ioctl = sfs_ioctl,
|
||||
.vop_stat = sfs_stat,
|
||||
.vop_gettype = sfs_gettype,
|
||||
.vop_isseekable = sfs_isseekable,
|
||||
.vop_fsync = sfs_fsync,
|
||||
.vop_mmap = sfs_mmap,
|
||||
.vop_truncate = sfs_truncate,
|
||||
.vop_namefile = vopfail_uio_notdir,
|
||||
|
||||
.vop_creat = vopfail_creat_notdir,
|
||||
.vop_symlink = vopfail_symlink_notdir,
|
||||
.vop_mkdir = vopfail_mkdir_notdir,
|
||||
.vop_link = vopfail_link_notdir,
|
||||
.vop_remove = vopfail_string_notdir,
|
||||
.vop_rmdir = vopfail_string_notdir,
|
||||
.vop_rename = vopfail_rename_notdir,
|
||||
|
||||
.vop_lookup = vopfail_lookup_notdir,
|
||||
.vop_lookparent = vopfail_lookparent_notdir,
|
||||
};
|
||||
|
||||
/*
|
||||
* Function table for the sfs directory.
|
||||
*/
|
||||
const struct vnode_ops sfs_dirops = {
|
||||
.vop_magic = VOP_MAGIC, /* mark this a valid vnode ops table */
|
||||
|
||||
.vop_eachopen = sfs_eachopendir,
|
||||
.vop_reclaim = sfs_reclaim,
|
||||
|
||||
.vop_read = vopfail_uio_isdir,
|
||||
.vop_readlink = vopfail_uio_inval,
|
||||
.vop_getdirentry = vopfail_uio_nosys,
|
||||
.vop_write = vopfail_uio_isdir,
|
||||
.vop_ioctl = sfs_ioctl,
|
||||
.vop_stat = sfs_stat,
|
||||
.vop_gettype = sfs_gettype,
|
||||
.vop_isseekable = sfs_isseekable,
|
||||
.vop_fsync = sfs_fsync,
|
||||
.vop_mmap = vopfail_mmap_isdir,
|
||||
.vop_truncate = vopfail_truncate_isdir,
|
||||
.vop_namefile = sfs_namefile,
|
||||
|
||||
.vop_creat = sfs_creat,
|
||||
.vop_symlink = vopfail_symlink_nosys,
|
||||
.vop_mkdir = vopfail_mkdir_nosys,
|
||||
.vop_link = sfs_link,
|
||||
.vop_remove = sfs_remove,
|
||||
.vop_rmdir = vopfail_string_nosys,
|
||||
.vop_rename = sfs_rename,
|
||||
|
||||
.vop_lookup = sfs_lookup,
|
||||
.vop_lookparent = sfs_lookparent,
|
||||
};
|
81
kern/fs/sfs/sfsprivate.h
Normal file
81
kern/fs/sfs/sfsprivate.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014
|
||||
* The President and Fellows of Harvard College.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SFSPRIVATE_H_
|
||||
#define _SFSPRIVATE_H_
|
||||
|
||||
#include <uio.h> /* for uio_rw */
|
||||
|
||||
|
||||
/* ops tables (in sfs_vnops.c) */
|
||||
extern const struct vnode_ops sfs_fileops;
|
||||
extern const struct vnode_ops sfs_dirops;
|
||||
|
||||
/* Macro for initializing a uio structure */
|
||||
#define SFSUIO(iov, uio, ptr, block, rw) \
|
||||
uio_kinit(iov, uio, ptr, SFS_BLOCKSIZE, ((off_t)(block))*SFS_BLOCKSIZE, rw)
|
||||
|
||||
|
||||
/* Functions in sfs_balloc.c */
|
||||
int sfs_balloc(struct sfs_fs *sfs, daddr_t *diskblock);
|
||||
void sfs_bfree(struct sfs_fs *sfs, daddr_t diskblock);
|
||||
int sfs_bused(struct sfs_fs *sfs, daddr_t diskblock);
|
||||
|
||||
/* Functions in sfs_bmap.c */
|
||||
int sfs_bmap(struct sfs_vnode *sv, uint32_t fileblock, bool doalloc,
|
||||
daddr_t *diskblock);
|
||||
int sfs_itrunc(struct sfs_vnode *sv, off_t len);
|
||||
|
||||
/* Functions in sfs_dir.c */
|
||||
int sfs_dir_findname(struct sfs_vnode *sv, const char *name,
|
||||
uint32_t *ino, int *slot, int *emptyslot);
|
||||
int sfs_dir_link(struct sfs_vnode *sv, const char *name, uint32_t ino,
|
||||
int *slot);
|
||||
int sfs_dir_unlink(struct sfs_vnode *sv, int slot);
|
||||
int sfs_lookonce(struct sfs_vnode *sv, const char *name,
|
||||
struct sfs_vnode **ret,
|
||||
int *slot);
|
||||
|
||||
/* Functions in sfs_inode.c */
|
||||
int sfs_sync_inode(struct sfs_vnode *sv);
|
||||
int sfs_reclaim(struct vnode *v);
|
||||
int sfs_loadvnode(struct sfs_fs *sfs, uint32_t ino, int forcetype,
|
||||
struct sfs_vnode **ret);
|
||||
int sfs_makeobj(struct sfs_fs *sfs, int type, struct sfs_vnode **ret);
|
||||
int sfs_getroot(struct fs *fs, struct vnode **ret);
|
||||
|
||||
/* Functions in sfs_io.c */
|
||||
int sfs_readblock(struct sfs_fs *sfs, daddr_t block, void *data, size_t len);
|
||||
int sfs_writeblock(struct sfs_fs *sfs, daddr_t block, void *data, size_t len);
|
||||
int sfs_io(struct sfs_vnode *sv, struct uio *uio);
|
||||
int sfs_metaio(struct sfs_vnode *sv, off_t pos, void *data, size_t len,
|
||||
enum uio_rw rw);
|
||||
|
||||
|
||||
#endif /* _SFSPRIVATE_H_ */
|
Reference in New Issue
Block a user