Initial Spring 2016 commit.

This commit is contained in:
Geoffrey Challen
2015-12-23 00:50:04 +00:00
commit cafa9f5690
732 changed files with 92195 additions and 0 deletions

123
kern/fs/semfs/semfs.h Normal file
View File

@@ -0,0 +1,123 @@
/*
* Copyright (c) 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 SEMFS_H
#define SEMFS_H
#include <array.h>
#include <fs.h>
#include <vnode.h>
#ifndef SEMFS_INLINE
#define SEMFS_INLINE INLINE
#endif
/*
* Constants
*/
#define SEMFS_ROOTDIR 0xffffffffU /* semnum for root dir */
/*
* A user-facing semaphore.
*
* We don't use the kernel-level semaphore to implement it (although
* that would be tidy) because we'd have to violate its abstraction.
* XXX: or would we? review once all this is done.
*/
struct semfs_sem {
struct lock *sems_lock; /* Lock to protect count */
struct cv *sems_cv; /* CV to wait */
unsigned sems_count; /* Semaphore count */
bool sems_hasvnode; /* The vnode exists */
bool sems_linked; /* In the directory */
};
DECLARRAY(semfs_sem, SEMFS_INLINE);
/*
* Directory entry; name and reference to a semaphore.
*/
struct semfs_direntry {
char *semd_name; /* Name */
unsigned semd_semnum; /* Which semaphore */
};
DECLARRAY(semfs_direntry, SEMFS_INLINE);
/*
* Vnode. These are separate from the semaphore structures so they can
* come and go at the whim of VOP_RECLAIM. (It might seem tidier to
* ignore VOP_RECLAIM and destroy vnodes only when the underlying
* objects are removed; but it ends up being more complicated in
* practice. XXX: review after finishing)
*/
struct semfs_vnode {
struct vnode semv_absvn; /* Abstract vnode */
struct semfs *semv_semfs; /* Back-pointer to fs */
unsigned semv_semnum; /* Which semaphore */
};
/*
* The structure for the semaphore file system. Ordinarily there
* is only one of these.
*/
struct semfs {
struct fs semfs_absfs; /* Abstract fs object */
struct lock *semfs_tablelock; /* Lock for following */
struct vnodearray *semfs_vnodes; /* Currently extant vnodes */
struct semfs_semarray *semfs_sems; /* Semaphores */
struct lock *semfs_dirlock; /* Lock for following */
struct semfs_direntryarray *semfs_dents; /* The root directory */
};
/*
* Arrays
*/
DEFARRAY(semfs_sem, SEMFS_INLINE);
DEFARRAY(semfs_direntry, SEMFS_INLINE);
/*
* Functions.
*/
/* in semfs_obj.c */
struct semfs_sem *semfs_sem_create(const char *name);
int semfs_sem_insert(struct semfs *, struct semfs_sem *, unsigned *);
void semfs_sem_destroy(struct semfs_sem *);
struct semfs_direntry *semfs_direntry_create(const char *name, unsigned semno);
void semfs_direntry_destroy(struct semfs_direntry *);
/* in semfs_vnops.c */
int semfs_getvnode(struct semfs *, unsigned, struct vnode **ret);
#endif /* SEMFS_H */

226
kern/fs/semfs/semfs_fsops.c Normal file
View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 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.
*/
#include <types.h>
#include <kern/errno.h>
#include <synch.h>
#include <vfs.h>
#include <fs.h>
#include <vnode.h>
#include "semfs.h"
////////////////////////////////////////////////////////////
// fs-level operations
/*
* Sync doesn't need to do anything.
*/
static
int
semfs_sync(struct fs *fs)
{
(void)fs;
return 0;
}
/*
* We have only one volume name and it's hardwired.
*/
static
const char *
semfs_getvolname(struct fs *fs)
{
(void)fs;
return "sem";
}
/*
* Get the root directory vnode.
*/
static
int
semfs_getroot(struct fs *fs, struct vnode **ret)
{
struct semfs *semfs = fs->fs_data;
struct vnode *vn;
int result;
result = semfs_getvnode(semfs, SEMFS_ROOTDIR, &vn);
if (result) {
kprintf("semfs: couldn't load root vnode: %s\n",
strerror(result));
return result;
}
*ret = vn;
return 0;
}
////////////////////////////////////////////////////////////
// mount and unmount logic
/*
* Destructor for struct semfs.
*/
static
void
semfs_destroy(struct semfs *semfs)
{
struct semfs_sem *sem;
struct semfs_direntry *dent;
unsigned i, num;
num = semfs_semarray_num(semfs->semfs_sems);
for (i=0; i<num; i++) {
sem = semfs_semarray_get(semfs->semfs_sems, i);
semfs_sem_destroy(sem);
}
semfs_semarray_setsize(semfs->semfs_sems, 0);
num = semfs_direntryarray_num(semfs->semfs_dents);
for (i=0; i<num; i++) {
dent = semfs_direntryarray_get(semfs->semfs_dents, i);
semfs_direntry_destroy(dent);
}
semfs_direntryarray_setsize(semfs->semfs_dents, 0);
semfs_direntryarray_destroy(semfs->semfs_dents);
lock_destroy(semfs->semfs_dirlock);
semfs_semarray_destroy(semfs->semfs_sems);
vnodearray_destroy(semfs->semfs_vnodes);
lock_destroy(semfs->semfs_tablelock);
kfree(semfs);
}
/*
* Unmount routine. XXX: Since semfs is attached at boot and can't be
* remounted, maybe unmounting it shouldn't be allowed.
*/
static
int
semfs_unmount(struct fs *fs)
{
struct semfs *semfs = fs->fs_data;
lock_acquire(semfs->semfs_tablelock);
if (vnodearray_num(semfs->semfs_vnodes) > 0) {
lock_release(semfs->semfs_tablelock);
return EBUSY;
}
lock_release(semfs->semfs_tablelock);
semfs_destroy(semfs);
return 0;
}
/*
* Operations table.
*/
static const struct fs_ops semfs_fsops = {
.fsop_sync = semfs_sync,
.fsop_getvolname = semfs_getvolname,
.fsop_getroot = semfs_getroot,
.fsop_unmount = semfs_unmount,
};
/*
* Constructor for struct semfs.
*/
static
struct semfs *
semfs_create(void)
{
struct semfs *semfs;
semfs = kmalloc(sizeof(*semfs));
if (semfs == NULL) {
goto fail_total;
}
semfs->semfs_tablelock = lock_create("semfs_table");
if (semfs->semfs_tablelock == NULL) {
goto fail_semfs;
}
semfs->semfs_vnodes = vnodearray_create();
if (semfs->semfs_vnodes == NULL) {
goto fail_tablelock;
}
semfs->semfs_sems = semfs_semarray_create();
if (semfs->semfs_sems == NULL) {
goto fail_vnodes;
}
semfs->semfs_dirlock = lock_create("semfs_dir");
if (semfs->semfs_dirlock == NULL) {
goto fail_sems;
}
semfs->semfs_dents = semfs_direntryarray_create();
if (semfs->semfs_dents == NULL) {
goto fail_dirlock;
}
semfs->semfs_absfs.fs_data = semfs;
semfs->semfs_absfs.fs_ops = &semfs_fsops;
return semfs;
fail_dirlock:
lock_destroy(semfs->semfs_dirlock);
fail_sems:
semfs_semarray_destroy(semfs->semfs_sems);
fail_vnodes:
vnodearray_destroy(semfs->semfs_vnodes);
fail_tablelock:
lock_destroy(semfs->semfs_tablelock);
fail_semfs:
kfree(semfs);
fail_total:
return NULL;
}
/*
* Create the semfs. There is only one semfs and it's attached as
* "sem:" during bootup.
*/
void
semfs_bootstrap(void)
{
struct semfs *semfs;
int result;
semfs = semfs_create();
if (semfs == NULL) {
panic("Out of memory creating semfs\n");
}
result = vfs_addfs("sem", &semfs->semfs_absfs);
if (result) {
panic("Attaching semfs: %s\n", strerror(result));
}
}

145
kern/fs/semfs/semfs_obj.c Normal file
View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 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.
*/
#include <types.h>
#include <kern/errno.h>
#include <synch.h>
#define SEMFS_INLINE
#include "semfs.h"
////////////////////////////////////////////////////////////
// semfs_sem
/*
* Constructor for semfs_sem.
*/
struct semfs_sem *
semfs_sem_create(const char *name)
{
struct semfs_sem *sem;
char lockname[32];
char cvname[32];
snprintf(lockname, sizeof(lockname), "sem:l.%s", name);
snprintf(cvname, sizeof(cvname), "sem:%s", name);
sem = kmalloc(sizeof(*sem));
if (sem == NULL) {
goto fail_return;
}
sem->sems_lock = lock_create(lockname);
if (sem->sems_lock == NULL) {
goto fail_sem;
}
sem->sems_cv = cv_create(cvname);
if (sem->sems_cv == NULL) {
goto fail_lock;
}
sem->sems_count = 0;
sem->sems_hasvnode = false;
sem->sems_linked = false;
return sem;
fail_lock:
lock_destroy(sem->sems_lock);
fail_sem:
kfree(sem);
fail_return:
return NULL;
}
/*
* Destructor for semfs_sem.
*/
void
semfs_sem_destroy(struct semfs_sem *sem)
{
cv_destroy(sem->sems_cv);
lock_destroy(sem->sems_lock);
kfree(sem);
}
/*
* Helper to insert a semfs_sem into the semaphore table.
*/
int
semfs_sem_insert(struct semfs *semfs, struct semfs_sem *sem, unsigned *ret)
{
unsigned i, num;
KASSERT(lock_do_i_hold(semfs->semfs_tablelock));
num = semfs_semarray_num(semfs->semfs_sems);
if (num == SEMFS_ROOTDIR) {
/* Too many */
return ENOSPC;
}
for (i=0; i<num; i++) {
if (semfs_semarray_get(semfs->semfs_sems, i) == NULL) {
semfs_semarray_set(semfs->semfs_sems, i, sem);
*ret = i;
return 0;
}
}
return semfs_semarray_add(semfs->semfs_sems, sem, ret);
}
////////////////////////////////////////////////////////////
// semfs_direntry
/*
* Constructor for semfs_direntry.
*/
struct semfs_direntry *
semfs_direntry_create(const char *name, unsigned semnum)
{
struct semfs_direntry *dent;
dent = kmalloc(sizeof(*dent));
if (dent == NULL) {
return NULL;
}
dent->semd_name = kstrdup(name);
if (dent->semd_name == NULL) {
kfree(dent);
return NULL;
}
dent->semd_semnum = semnum;
return dent;
}
/*
* Destructor for semfs_direntry.
*/
void
semfs_direntry_destroy(struct semfs_direntry *dent)
{
kfree(dent->semd_name);
kfree(dent);
}

799
kern/fs/semfs/semfs_vnops.c Normal file
View File

@@ -0,0 +1,799 @@
/*
* Copyright (c) 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.
*/
#include <types.h>
#include <kern/errno.h>
#include <kern/fcntl.h>
#include <stat.h>
#include <uio.h>
#include <synch.h>
#include <thread.h>
#include <proc.h>
#include <current.h>
#include <vfs.h>
#include <vnode.h>
#include "semfs.h"
////////////////////////////////////////////////////////////
// basic ops
static
int
semfs_eachopen(struct vnode *vn, int openflags)
{
struct semfs_vnode *semv = vn->vn_data;
if (semv->semv_semnum == SEMFS_ROOTDIR) {
if ((openflags & O_ACCMODE) != O_RDONLY) {
return EISDIR;
}
if (openflags & O_APPEND) {
return EISDIR;
}
}
return 0;
}
static
int
semfs_ioctl(struct vnode *vn, int op, userptr_t data)
{
(void)vn;
(void)op;
(void)data;
return EINVAL;
}
static
int
semfs_gettype(struct vnode *vn, mode_t *ret)
{
struct semfs_vnode *semv = vn->vn_data;
*ret = semv->semv_semnum == SEMFS_ROOTDIR ? S_IFDIR : S_IFREG;
return 0;
}
static
bool
semfs_isseekable(struct vnode *vn)
{
struct semfs_vnode *semv = vn->vn_data;
if (semv->semv_semnum != SEMFS_ROOTDIR) {
/* seeking a semaphore doesn't mean anything */
return false;
}
return true;
}
static
int
semfs_fsync(struct vnode *vn)
{
(void)vn;
return 0;
}
////////////////////////////////////////////////////////////
// semaphore ops
/*
* XXX fold these two together
*/
static
struct semfs_sem *
semfs_getsembynum(struct semfs *semfs, unsigned semnum)
{
struct semfs_sem *sem;
lock_acquire(semfs->semfs_tablelock);
sem = semfs_semarray_get(semfs->semfs_sems, semnum);
lock_release(semfs->semfs_tablelock);
return sem;
}
static
struct semfs_sem *
semfs_getsem(struct semfs_vnode *semv)
{
struct semfs *semfs = semv->semv_semfs;
return semfs_getsembynum(semfs, semv->semv_semnum);
}
/*
* Wakeup helper. We only need to wake up if there are sleepers, which
* should only be the case if the old count is 0; and we only
* potentially need to wake more than one sleeper if the new count
* will be more than 1.
*/
static
void
semfs_wakeup(struct semfs_sem *sem, unsigned newcount)
{
if (sem->sems_count > 0 || newcount == 0) {
return;
}
if (newcount == 1) {
cv_signal(sem->sems_cv, sem->sems_lock);
}
else {
cv_broadcast(sem->sems_cv, sem->sems_lock);
}
}
/*
* stat() for semaphore vnodes
*/
static
int
semfs_semstat(struct vnode *vn, struct stat *buf)
{
struct semfs_vnode *semv = vn->vn_data;
struct semfs_sem *sem;
sem = semfs_getsem(semv);
bzero(buf, sizeof(*buf));
lock_acquire(sem->sems_lock);
buf->st_size = sem->sems_count;
buf->st_nlink = sem->sems_linked ? 1 : 0;
lock_release(sem->sems_lock);
buf->st_mode = S_IFREG | 0666;
buf->st_blocks = 0;
buf->st_dev = 0;
buf->st_ino = semv->semv_semnum;
return 0;
}
/*
* Read. This is P(); decrease the count by the amount read.
* Don't actually bother to transfer any data.
*/
static
int
semfs_read(struct vnode *vn, struct uio *uio)
{
struct semfs_vnode *semv = vn->vn_data;
struct semfs_sem *sem;
size_t consume;
sem = semfs_getsem(semv);
lock_acquire(sem->sems_lock);
while (uio->uio_resid > 0) {
if (sem->sems_count > 0) {
consume = uio->uio_resid;
if (consume > sem->sems_count) {
consume = sem->sems_count;
}
DEBUG(DB_SEMFS, "semfs: sem%u: P, count %u -> %u\n",
semv->semv_semnum, sem->sems_count,
sem->sems_count - consume);
sem->sems_count -= consume;
/* don't bother advancing the uio data pointers */
uio->uio_offset += consume;
uio->uio_resid -= consume;
}
if (uio->uio_resid == 0) {
break;
}
if (sem->sems_count == 0) {
DEBUG(DB_SEMFS, "semfs: sem%u: blocking\n",
semv->semv_semnum);
cv_wait(sem->sems_cv, sem->sems_lock);
}
}
lock_release(sem->sems_lock);
return 0;
}
/*
* Write. This is V(); increase the count by the amount written.
* Don't actually bother to transfer any data.
*/
static
int
semfs_write(struct vnode *vn, struct uio *uio)
{
struct semfs_vnode *semv = vn->vn_data;
struct semfs_sem *sem;
unsigned newcount;
sem = semfs_getsem(semv);
lock_acquire(sem->sems_lock);
while (uio->uio_resid > 0) {
newcount = sem->sems_count + uio->uio_resid;
if (newcount < sem->sems_count) {
/* overflow */
lock_release(sem->sems_lock);
return EFBIG;
}
DEBUG(DB_SEMFS, "semfs: sem%u: V, count %u -> %u\n",
semv->semv_semnum, sem->sems_count, newcount);
semfs_wakeup(sem, newcount);
sem->sems_count = newcount;
uio->uio_offset += uio->uio_resid;
uio->uio_resid = 0;
}
lock_release(sem->sems_lock);
return 0;
}
/*
* Truncate. Set the count to the specified value.
*
* This is slightly cheesy but it allows open(..., O_TRUNC) to reset a
* semaphore as one would expect. Also it allows creating semaphores
* and then initializing their counts to values other than zero.
*/
static
int
semfs_truncate(struct vnode *vn, off_t len)
{
/* We should just use UINT_MAX but we don't have it in the kernel */
const unsigned max = (unsigned)-1;
struct semfs_vnode *semv = vn->vn_data;
struct semfs_sem *sem;
unsigned newcount;
if (len < 0) {
return EINVAL;
}
if (len > (off_t)max) {
return EFBIG;
}
newcount = len;
sem = semfs_getsem(semv);
lock_acquire(sem->sems_lock);
semfs_wakeup(sem, newcount);
sem->sems_count = newcount;
lock_release(sem->sems_lock);
return 0;
}
////////////////////////////////////////////////////////////
// directory ops
/*
* Directory read. Note that there's only one directory (the semfs
* root) that has all the semaphores in it.
*/
static
int
semfs_getdirentry(struct vnode *dirvn, struct uio *uio)
{
struct semfs_vnode *dirsemv = dirvn->vn_data;
struct semfs *semfs = dirsemv->semv_semfs;
struct semfs_direntry *dent;
unsigned num, pos;
int result;
KASSERT(uio->uio_offset >= 0);
pos = uio->uio_offset;
lock_acquire(semfs->semfs_dirlock);
num = semfs_direntryarray_num(semfs->semfs_dents);
if (pos >= num) {
/* EOF */
result = 0;
}
else {
dent = semfs_direntryarray_get(semfs->semfs_dents, pos);
result = uiomove(dent->semd_name, strlen(dent->semd_name),
uio);
}
lock_release(semfs->semfs_dirlock);
return result;
}
/*
* stat() for dirs
*/
static
int
semfs_dirstat(struct vnode *vn, struct stat *buf)
{
struct semfs_vnode *semv = vn->vn_data;
struct semfs *semfs = semv->semv_semfs;
bzero(buf, sizeof(*buf));
lock_acquire(semfs->semfs_dirlock);
buf->st_size = semfs_direntryarray_num(semfs->semfs_dents);
lock_release(semfs->semfs_dirlock);
buf->st_mode = S_IFDIR | 1777;
buf->st_nlink = 2;
buf->st_blocks = 0;
buf->st_dev = 0;
buf->st_ino = SEMFS_ROOTDIR;
return 0;
}
/*
* Backend for getcwd. Since we don't support subdirs, it's easy; send
* back the empty string.
*/
static
int
semfs_namefile(struct vnode *vn, struct uio *uio)
{
(void)vn;
(void)uio;
return 0;
}
/*
* Create a semaphore.
*/
static
int
semfs_creat(struct vnode *dirvn, const char *name, bool excl, mode_t mode,
struct vnode **resultvn)
{
struct semfs_vnode *dirsemv = dirvn->vn_data;
struct semfs *semfs = dirsemv->semv_semfs;
struct semfs_direntry *dent;
struct semfs_sem *sem;
unsigned i, num, empty, semnum;
int result;
(void)mode;
if (!strcmp(name, ".") || !strcmp(name, "..")) {
return EEXIST;
}
lock_acquire(semfs->semfs_dirlock);
num = semfs_direntryarray_num(semfs->semfs_dents);
empty = num;
for (i=0; i<num; i++) {
dent = semfs_direntryarray_get(semfs->semfs_dents, i);
if (dent == NULL) {
if (empty == num) {
empty = i;
}
continue;
}
if (!strcmp(dent->semd_name, name)) {
/* found */
if (excl) {
lock_release(semfs->semfs_dirlock);
return EEXIST;
}
result = semfs_getvnode(semfs, dent->semd_semnum,
resultvn);
lock_release(semfs->semfs_dirlock);
return result;
}
}
/* create it */
sem = semfs_sem_create(name);
if (sem == NULL) {
result = ENOMEM;
goto fail_unlock;
}
lock_acquire(semfs->semfs_tablelock);
result = semfs_sem_insert(semfs, sem, &semnum);
lock_release(semfs->semfs_tablelock);
if (result) {
goto fail_uncreate;
}
dent = semfs_direntry_create(name, semnum);
if (dent == NULL) {
goto fail_uninsert;
}
if (empty < num) {
semfs_direntryarray_set(semfs->semfs_dents, empty, dent);
}
else {
result = semfs_direntryarray_add(semfs->semfs_dents, dent,
&empty);
if (result) {
goto fail_undent;
}
}
result = semfs_getvnode(semfs, semnum, resultvn);
if (result) {
goto fail_undir;
}
sem->sems_linked = true;
lock_release(semfs->semfs_dirlock);
return 0;
fail_undir:
semfs_direntryarray_set(semfs->semfs_dents, empty, NULL);
fail_undent:
semfs_direntry_destroy(dent);
fail_uninsert:
lock_acquire(semfs->semfs_tablelock);
semfs_semarray_set(semfs->semfs_sems, semnum, NULL);
lock_release(semfs->semfs_tablelock);
fail_uncreate:
semfs_sem_destroy(sem);
fail_unlock:
lock_release(semfs->semfs_dirlock);
return result;
}
/*
* Unlink a semaphore. As with other files, it may not actually
* go away if it's currently open.
*/
static
int
semfs_remove(struct vnode *dirvn, const char *name)
{
struct semfs_vnode *dirsemv = dirvn->vn_data;
struct semfs *semfs = dirsemv->semv_semfs;
struct semfs_direntry *dent;
struct semfs_sem *sem;
unsigned i, num;
int result;
if (!strcmp(name, ".") || !strcmp(name, "..")) {
return EINVAL;
}
lock_acquire(semfs->semfs_dirlock);
num = semfs_direntryarray_num(semfs->semfs_dents);
for (i=0; i<num; i++) {
dent = semfs_direntryarray_get(semfs->semfs_dents, i);
if (dent == NULL) {
continue;
}
if (!strcmp(name, dent->semd_name)) {
/* found */
sem = semfs_getsembynum(semfs, dent->semd_semnum);
lock_acquire(sem->sems_lock);
KASSERT(sem->sems_linked);
sem->sems_linked = false;
if (sem->sems_hasvnode == false) {
lock_acquire(semfs->semfs_tablelock);
semfs_semarray_set(semfs->semfs_sems,
dent->semd_semnum, NULL);
lock_release(semfs->semfs_tablelock);
lock_release(sem->sems_lock);
semfs_sem_destroy(sem);
}
else {
lock_release(sem->sems_lock);
}
semfs_direntryarray_set(semfs->semfs_dents, i, NULL);
semfs_direntry_destroy(dent);
result = 0;
goto out;
}
}
result = ENOENT;
out:
lock_release(semfs->semfs_dirlock);
return result;
}
/*
* Lookup: get a semaphore by name.
*/
static
int
semfs_lookup(struct vnode *dirvn, char *path, struct vnode **resultvn)
{
struct semfs_vnode *dirsemv = dirvn->vn_data;
struct semfs *semfs = dirsemv->semv_semfs;
struct semfs_direntry *dent;
unsigned i, num;
int result;
if (!strcmp(path, ".") || !strcmp(path, "..")) {
VOP_INCREF(dirvn);
*resultvn = dirvn;
return 0;
}
lock_acquire(semfs->semfs_dirlock);
num = semfs_direntryarray_num(semfs->semfs_dents);
for (i=0; i<num; i++) {
dent = semfs_direntryarray_get(semfs->semfs_dents, i);
if (dent == NULL) {
continue;
}
if (!strcmp(path, dent->semd_name)) {
result = semfs_getvnode(semfs, dent->semd_semnum,
resultvn);
lock_release(semfs->semfs_dirlock);
return result;
}
}
lock_release(semfs->semfs_dirlock);
return ENOENT;
}
/*
* Lookparent: because we don't have subdirs, just return the root
* dir and copy the name.
*/
static
int
semfs_lookparent(struct vnode *dirvn, char *path,
struct vnode **resultdirvn, char *namebuf, size_t bufmax)
{
if (strlen(path)+1 > bufmax) {
return ENAMETOOLONG;
}
strcpy(namebuf, path);
VOP_INCREF(dirvn);
*resultdirvn = dirvn;
return 0;
}
////////////////////////////////////////////////////////////
// vnode lifecycle operations
/*
* Destructor for semfs_vnode.
*/
static
void
semfs_vnode_destroy(struct semfs_vnode *semv)
{
vnode_cleanup(&semv->semv_absvn);
kfree(semv);
}
/*
* Reclaim - drop a vnode that's no longer in use.
*/
static
int
semfs_reclaim(struct vnode *vn)
{
struct semfs_vnode *semv = vn->vn_data;
struct semfs *semfs = semv->semv_semfs;
struct vnode *vn2;
struct semfs_sem *sem;
unsigned i, num;
lock_acquire(semfs->semfs_tablelock);
/* vnode refcount is protected by the vnode's ->vn_countlock */
spinlock_acquire(&vn->vn_countlock);
if (vn->vn_refcount > 1) {
/* consume the reference VOP_DECREF passed us */
vn->vn_refcount--;
spinlock_release(&vn->vn_countlock);
lock_release(semfs->semfs_tablelock);
return EBUSY;
}
spinlock_release(&vn->vn_countlock);
/* remove from the table */
num = vnodearray_num(semfs->semfs_vnodes);
for (i=0; i<num; i++) {
vn2 = vnodearray_get(semfs->semfs_vnodes, i);
if (vn2 == vn) {
vnodearray_remove(semfs->semfs_vnodes, i);
break;
}
}
if (semv->semv_semnum != SEMFS_ROOTDIR) {
sem = semfs_semarray_get(semfs->semfs_sems, semv->semv_semnum);
KASSERT(sem->sems_hasvnode);
sem->sems_hasvnode = false;
if (sem->sems_linked == false) {
semfs_semarray_set(semfs->semfs_sems,
semv->semv_semnum, NULL);
semfs_sem_destroy(sem);
}
}
/* done with the table */
lock_release(semfs->semfs_tablelock);
/* destroy it */
semfs_vnode_destroy(semv);
return 0;
}
/*
* Vnode ops table for dirs.
*/
static const struct vnode_ops semfs_dirops = {
.vop_magic = VOP_MAGIC,
.vop_eachopen = semfs_eachopen,
.vop_reclaim = semfs_reclaim,
.vop_read = vopfail_uio_isdir,
.vop_readlink = vopfail_uio_isdir,
.vop_getdirentry = semfs_getdirentry,
.vop_write = vopfail_uio_isdir,
.vop_ioctl = semfs_ioctl,
.vop_stat = semfs_dirstat,
.vop_gettype = semfs_gettype,
.vop_isseekable = semfs_isseekable,
.vop_fsync = semfs_fsync,
.vop_mmap = vopfail_mmap_isdir,
.vop_truncate = vopfail_truncate_isdir,
.vop_namefile = semfs_namefile,
.vop_creat = semfs_creat,
.vop_symlink = vopfail_symlink_nosys,
.vop_mkdir = vopfail_mkdir_nosys,
.vop_link = vopfail_link_nosys,
.vop_remove = semfs_remove,
.vop_rmdir = vopfail_string_nosys,
.vop_rename = vopfail_rename_nosys,
.vop_lookup = semfs_lookup,
.vop_lookparent = semfs_lookparent,
};
/*
* Vnode ops table for semaphores (files).
*/
static const struct vnode_ops semfs_semops = {
.vop_magic = VOP_MAGIC,
.vop_eachopen = semfs_eachopen,
.vop_reclaim = semfs_reclaim,
.vop_read = semfs_read,
.vop_readlink = vopfail_uio_inval,
.vop_getdirentry = vopfail_uio_notdir,
.vop_write = semfs_write,
.vop_ioctl = semfs_ioctl,
.vop_stat = semfs_semstat,
.vop_gettype = semfs_gettype,
.vop_isseekable = semfs_isseekable,
.vop_fsync = semfs_fsync,
.vop_mmap = vopfail_mmap_perm,
.vop_truncate = semfs_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,
};
/*
* Constructor for semfs vnodes.
*/
static
struct semfs_vnode *
semfs_vnode_create(struct semfs *semfs, unsigned semnum)
{
const struct vnode_ops *optable;
struct semfs_vnode *semv;
int result;
if (semnum == SEMFS_ROOTDIR) {
optable = &semfs_dirops;
}
else {
optable = &semfs_semops;
}
semv = kmalloc(sizeof(*semv));
if (semv == NULL) {
return NULL;
}
semv->semv_semfs = semfs;
semv->semv_semnum = semnum;
result = vnode_init(&semv->semv_absvn, optable,
&semfs->semfs_absfs, semv);
/* vnode_init doesn't actually fail */
KASSERT(result == 0);
return semv;
}
/*
* Look up the vnode for a semaphore by number; if it doesn't exist,
* create it.
*/
int
semfs_getvnode(struct semfs *semfs, unsigned semnum, struct vnode **ret)
{
struct vnode *vn;
struct semfs_vnode *semv;
struct semfs_sem *sem;
unsigned i, num;
int result;
/* Lock the vnode table */
lock_acquire(semfs->semfs_tablelock);
/* Look for it */
num = vnodearray_num(semfs->semfs_vnodes);
for (i=0; i<num; i++) {
vn = vnodearray_get(semfs->semfs_vnodes, i);
semv = vn->vn_data;
if (semv->semv_semnum == semnum) {
VOP_INCREF(vn);
lock_release(semfs->semfs_tablelock);
*ret = vn;
return 0;
}
}
/* Make it */
semv = semfs_vnode_create(semfs, semnum);
if (semv == NULL) {
lock_release(semfs->semfs_tablelock);
return ENOMEM;
}
result = vnodearray_add(semfs->semfs_vnodes, &semv->semv_absvn, NULL);
if (result) {
semfs_vnode_destroy(semv);
lock_release(semfs->semfs_tablelock);
return ENOMEM;
}
if (semnum != SEMFS_ROOTDIR) {
sem = semfs_semarray_get(semfs->semfs_sems, semnum);
KASSERT(sem != NULL);
KASSERT(sem->sems_hasvnode == false);
sem->sems_hasvnode = true;
}
lock_release(semfs->semfs_tablelock);
*ret = &semv->semv_absvn;
return 0;
}

103
kern/fs/sfs/sfs_balloc.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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_ */