Initial Spring 2016 commit.
This commit is contained in:
388
kern/vfs/device.c
Normal file
388
kern/vfs/device.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Vnode operations for VFS devices.
|
||||
*
|
||||
* These hand off to the functions in the VFS device structure (see dev.h)
|
||||
* but take care of a bunch of common tasks in a uniform fashion.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <kern/fcntl.h>
|
||||
#include <stat.h>
|
||||
#include <lib.h>
|
||||
#include <uio.h>
|
||||
#include <synch.h>
|
||||
#include <vnode.h>
|
||||
#include <device.h>
|
||||
|
||||
/*
|
||||
* Called for each open().
|
||||
*
|
||||
* We reject O_APPEND.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_eachopen(struct vnode *v, int flags)
|
||||
{
|
||||
struct device *d = v->vn_data;
|
||||
|
||||
if (flags & (O_CREAT | O_TRUNC | O_EXCL | O_APPEND)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return DEVOP_EACHOPEN(d, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the vnode refcount reaches zero.
|
||||
* Do nothing; devices are permanent.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_reclaim(struct vnode *v)
|
||||
{
|
||||
(void)v;
|
||||
/* nothing - device continues to exist even when not in use */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check a seek position.
|
||||
*
|
||||
* For block devices, require block alignment.
|
||||
* For character devices, we should prohibit seeking entirely, but
|
||||
* for the moment we need to accept any position. (XXX)
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_tryseek(struct device *d, off_t pos)
|
||||
{
|
||||
if (d->d_blocks > 0) {
|
||||
if ((pos % d->d_blocksize)!=0) {
|
||||
/* not block-aligned */
|
||||
return EINVAL;
|
||||
}
|
||||
if (pos / d->d_blocksize >= d->d_blocks) {
|
||||
/* off the end */
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//return ESPIPE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for read. Hand off to DEVOP_IO.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_read(struct vnode *v, struct uio *uio)
|
||||
{
|
||||
struct device *d = v->vn_data;
|
||||
int result;
|
||||
|
||||
result = dev_tryseek(d, uio->uio_offset);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
KASSERT(uio->uio_rw == UIO_READ);
|
||||
return DEVOP_IO(d, uio);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for write. Hand off to DEVOP_IO.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_write(struct vnode *v, struct uio *uio)
|
||||
{
|
||||
struct device *d = v->vn_data;
|
||||
int result;
|
||||
|
||||
result = dev_tryseek(d, uio->uio_offset);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
KASSERT(uio->uio_rw == UIO_WRITE);
|
||||
return DEVOP_IO(d, uio);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for ioctl(). Just pass through.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_ioctl(struct vnode *v, int op, userptr_t data)
|
||||
{
|
||||
struct device *d = v->vn_data;
|
||||
return DEVOP_IOCTL(d, op, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called for stat().
|
||||
* Set the type and the size (block devices only).
|
||||
* The link count for a device is always 1.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_stat(struct vnode *v, struct stat *statbuf)
|
||||
{
|
||||
struct device *d = v->vn_data;
|
||||
int result;
|
||||
|
||||
bzero(statbuf, sizeof(struct stat));
|
||||
|
||||
if (d->d_blocks > 0) {
|
||||
statbuf->st_size = d->d_blocks * d->d_blocksize;
|
||||
statbuf->st_blksize = d->d_blocksize;
|
||||
}
|
||||
else {
|
||||
statbuf->st_size = 0;
|
||||
}
|
||||
|
||||
result = VOP_GETTYPE(v, &statbuf->st_mode);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
/* Make up some plausible default permissions. */
|
||||
statbuf->st_mode |= 0600;
|
||||
|
||||
statbuf->st_nlink = 1;
|
||||
statbuf->st_blocks = d->d_blocks;
|
||||
|
||||
/* The device number this device sits on (in OS/161, it doesn't) */
|
||||
statbuf->st_dev = 0;
|
||||
|
||||
/* The device number this device *is* */
|
||||
statbuf->st_rdev = d->d_devnumber;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the type. A device is a "block device" if it has a known
|
||||
* length. A device that generates data in a stream is a "character
|
||||
* device".
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_gettype(struct vnode *v, mode_t *ret)
|
||||
{
|
||||
struct device *d = v->vn_data;
|
||||
if (d->d_blocks > 0) {
|
||||
*ret = S_IFBLK;
|
||||
}
|
||||
else {
|
||||
*ret = S_IFCHR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if seeking is allowed.
|
||||
*/
|
||||
static
|
||||
bool
|
||||
dev_isseekable(struct vnode *v)
|
||||
{
|
||||
struct device *d = v->vn_data;
|
||||
|
||||
if (d->d_blocks == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* For fsync() - meaningless, do nothing.
|
||||
*/
|
||||
static
|
||||
int
|
||||
null_fsync(struct vnode *v)
|
||||
{
|
||||
(void)v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For mmap. If you want this to do anything, you have to write it
|
||||
* yourself. Some devices may not make sense to map. Others do.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_mmap(struct vnode *v /* add stuff as needed */)
|
||||
{
|
||||
(void)v;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
/*
|
||||
* For ftruncate().
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_truncate(struct vnode *v, off_t len)
|
||||
{
|
||||
struct device *d = v->vn_data;
|
||||
|
||||
/*
|
||||
* Allow truncating to the object's own size, if it has one.
|
||||
*/
|
||||
if (d->d_blocks > 0 && (off_t)(d->d_blocks*d->d_blocksize) == len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* For namefile (which implements "pwd")
|
||||
*
|
||||
* This should never be reached, as it's not possible to chdir to a
|
||||
* device vnode.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_namefile(struct vnode *v, struct uio *uio)
|
||||
{
|
||||
/*
|
||||
* The name of a device is always just "device:". The VFS
|
||||
* layer puts in the device name for us, so we don't need to
|
||||
* do anything further.
|
||||
*/
|
||||
|
||||
(void)v;
|
||||
(void)uio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Name lookup.
|
||||
*
|
||||
* One interesting feature of device:name pathname syntax is that you
|
||||
* can implement pathnames on arbitrary devices. For instance, if you
|
||||
* had a graphics device that supported multiple resolutions (which we
|
||||
* don't), you might arrange things so that you could open it with
|
||||
* pathnames like "video:800x600/24bpp" in order to select the operating
|
||||
* mode.
|
||||
*
|
||||
* However, we have no support for this in the base system.
|
||||
*/
|
||||
static
|
||||
int
|
||||
dev_lookup(struct vnode *dir,
|
||||
char *pathname, struct vnode **result)
|
||||
{
|
||||
/*
|
||||
* If the path was "device:", we get "". For that, return self.
|
||||
* Anything else is an error.
|
||||
* Increment the ref count of the vnode before returning it.
|
||||
*/
|
||||
if (strlen(pathname)>0) {
|
||||
return ENOENT;
|
||||
}
|
||||
VOP_INCREF(dir);
|
||||
*result = dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function table for device vnodes.
|
||||
*/
|
||||
static const struct vnode_ops dev_vnode_ops = {
|
||||
.vop_magic = VOP_MAGIC,
|
||||
|
||||
.vop_eachopen = dev_eachopen,
|
||||
.vop_reclaim = dev_reclaim,
|
||||
.vop_read = dev_read,
|
||||
.vop_readlink = vopfail_uio_inval,
|
||||
.vop_getdirentry = vopfail_uio_notdir,
|
||||
.vop_write = dev_write,
|
||||
.vop_ioctl = dev_ioctl,
|
||||
.vop_stat = dev_stat,
|
||||
.vop_gettype = dev_gettype,
|
||||
.vop_isseekable = dev_isseekable,
|
||||
.vop_fsync = null_fsync,
|
||||
.vop_mmap = dev_mmap,
|
||||
.vop_truncate = dev_truncate,
|
||||
.vop_namefile = dev_namefile,
|
||||
.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 = dev_lookup,
|
||||
.vop_lookparent = vopfail_lookparent_notdir,
|
||||
};
|
||||
|
||||
/*
|
||||
* Function to create a vnode for a VFS device.
|
||||
*/
|
||||
struct vnode *
|
||||
dev_create_vnode(struct device *dev)
|
||||
{
|
||||
int result;
|
||||
struct vnode *v;
|
||||
|
||||
v = kmalloc(sizeof(struct vnode));
|
||||
if (v==NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = vnode_init(v, &dev_vnode_ops, NULL, dev);
|
||||
if (result != 0) {
|
||||
panic("While creating vnode for device: vnode_init: %s\n",
|
||||
strerror(result));
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* Undo dev_create_vnode.
|
||||
*
|
||||
* Note: this is only used in failure paths; we don't support
|
||||
* hotpluggable devices, so once a device is attached it's permanent.
|
||||
*/
|
||||
void
|
||||
dev_uncreate_vnode(struct vnode *vn)
|
||||
{
|
||||
KASSERT(vn->vn_ops == &dev_vnode_ops);
|
||||
vnode_cleanup(vn);
|
||||
kfree(vn);
|
||||
}
|
124
kern/vfs/devnull.c
Normal file
124
kern/vfs/devnull.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implementation of the null device, "null:", which generates an
|
||||
* immediate EOF on read and throws away anything written to it.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <uio.h>
|
||||
#include <vfs.h>
|
||||
#include <device.h>
|
||||
|
||||
/* For open() */
|
||||
static
|
||||
int
|
||||
nullopen(struct device *dev, int openflags)
|
||||
{
|
||||
(void)dev;
|
||||
(void)openflags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For d_io() */
|
||||
static
|
||||
int
|
||||
nullio(struct device *dev, struct uio *uio)
|
||||
{
|
||||
/*
|
||||
* On write, discard everything without looking at it.
|
||||
* (Notice that you can write to the null device from invalid
|
||||
* buffer pointers and it will still succeed. This behavior is
|
||||
* traditional.)
|
||||
*
|
||||
* On read, do nothing, generating an immediate EOF.
|
||||
*/
|
||||
|
||||
(void)dev; // unused
|
||||
|
||||
if (uio->uio_rw == UIO_WRITE) {
|
||||
uio->uio_resid = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For ioctl() */
|
||||
static
|
||||
int
|
||||
nullioctl(struct device *dev, int op, userptr_t data)
|
||||
{
|
||||
/*
|
||||
* No ioctls.
|
||||
*/
|
||||
|
||||
(void)dev;
|
||||
(void)op;
|
||||
(void)data;
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
static const struct device_ops null_devops = {
|
||||
.devop_eachopen = nullopen,
|
||||
.devop_io = nullio,
|
||||
.devop_ioctl = nullioctl,
|
||||
};
|
||||
|
||||
/*
|
||||
* Function to create and attach null:
|
||||
*/
|
||||
void
|
||||
devnull_create(void)
|
||||
{
|
||||
int result;
|
||||
struct device *dev;
|
||||
|
||||
dev = kmalloc(sizeof(*dev));
|
||||
if (dev==NULL) {
|
||||
panic("Could not add null device: out of memory\n");
|
||||
}
|
||||
|
||||
dev->d_ops = &null_devops;
|
||||
|
||||
dev->d_blocks = 0;
|
||||
dev->d_blocksize = 1;
|
||||
|
||||
dev->d_devnumber = 0; /* assigned by vfs_adddev */
|
||||
|
||||
dev->d_data = NULL;
|
||||
|
||||
result = vfs_adddev("null", dev, 0);
|
||||
if (result) {
|
||||
panic("Could not add null device: %s\n", strerror(result));
|
||||
}
|
||||
}
|
184
kern/vfs/vfscwd.c
Normal file
184
kern/vfs/vfscwd.c
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* VFS operations involving the current directory.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <stat.h>
|
||||
#include <lib.h>
|
||||
#include <uio.h>
|
||||
#include <proc.h>
|
||||
#include <current.h>
|
||||
#include <vfs.h>
|
||||
#include <fs.h>
|
||||
#include <vnode.h>
|
||||
|
||||
/*
|
||||
* Get current directory as a vnode.
|
||||
*/
|
||||
int
|
||||
vfs_getcurdir(struct vnode **ret)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
spinlock_acquire(&curproc->p_lock);
|
||||
if (curproc->p_cwd!=NULL) {
|
||||
VOP_INCREF(curproc->p_cwd);
|
||||
*ret = curproc->p_cwd;
|
||||
}
|
||||
else {
|
||||
rv = ENOENT;
|
||||
}
|
||||
spinlock_release(&curproc->p_lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set current directory as a vnode.
|
||||
* The passed vnode must in fact be a directory.
|
||||
*/
|
||||
int
|
||||
vfs_setcurdir(struct vnode *dir)
|
||||
{
|
||||
struct vnode *old;
|
||||
mode_t vtype;
|
||||
int result;
|
||||
|
||||
result = VOP_GETTYPE(dir, &vtype);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
if (vtype != S_IFDIR) {
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
VOP_INCREF(dir);
|
||||
|
||||
spinlock_acquire(&curproc->p_lock);
|
||||
old = curproc->p_cwd;
|
||||
curproc->p_cwd = dir;
|
||||
spinlock_release(&curproc->p_lock);
|
||||
|
||||
if (old!=NULL) {
|
||||
VOP_DECREF(old);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set current directory to "none".
|
||||
*/
|
||||
int
|
||||
vfs_clearcurdir(void)
|
||||
{
|
||||
struct vnode *old;
|
||||
|
||||
spinlock_acquire(&curproc->p_lock);
|
||||
old = curproc->p_cwd;
|
||||
curproc->p_cwd = NULL;
|
||||
spinlock_release(&curproc->p_lock);
|
||||
|
||||
if (old!=NULL) {
|
||||
VOP_DECREF(old);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set current directory, as a pathname. Use vfs_lookup to translate
|
||||
* it to a vnode.
|
||||
*/
|
||||
int
|
||||
vfs_chdir(char *path)
|
||||
{
|
||||
struct vnode *vn;
|
||||
int result;
|
||||
|
||||
result = vfs_lookup(path, &vn);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = vfs_setcurdir(vn);
|
||||
VOP_DECREF(vn);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get current directory, as a pathname.
|
||||
* Use VOP_NAMEFILE to get the pathname and FSOP_GETVOLNAME to get the
|
||||
* volume name.
|
||||
*/
|
||||
int
|
||||
vfs_getcwd(struct uio *uio)
|
||||
{
|
||||
struct vnode *cwd;
|
||||
int result;
|
||||
const char *name;
|
||||
char colon=':';
|
||||
|
||||
KASSERT(uio->uio_rw==UIO_READ);
|
||||
|
||||
result = vfs_getcurdir(&cwd);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* The current dir must be a directory, and thus it is not a device. */
|
||||
KASSERT(cwd->vn_fs != NULL);
|
||||
|
||||
name = FSOP_GETVOLNAME(cwd->vn_fs);
|
||||
if (name==NULL) {
|
||||
vfs_biglock_acquire();
|
||||
name = vfs_getdevname(cwd->vn_fs);
|
||||
vfs_biglock_release();
|
||||
}
|
||||
KASSERT(name != NULL);
|
||||
|
||||
result = uiomove((char *)name, strlen(name), uio);
|
||||
if (result) {
|
||||
goto out;
|
||||
}
|
||||
result = uiomove(&colon, 1, uio);
|
||||
if (result) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = VOP_NAMEFILE(cwd, uio);
|
||||
|
||||
out:
|
||||
|
||||
VOP_DECREF(cwd);
|
||||
return result;
|
||||
}
|
264
kern/vfs/vfsfail.c
Normal file
264
kern/vfs/vfsfail.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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 <vnode.h>
|
||||
|
||||
/*
|
||||
* Routines that fail.
|
||||
*
|
||||
* It is kind of silly to write these out each with their particular
|
||||
* arguments; however, portable C doesn't let you cast function
|
||||
* pointers with different argument signatures even if the arguments
|
||||
* are never used.
|
||||
*
|
||||
* The 4.4BSD approach (all vnode ops take a void pointer that's cast
|
||||
* to a op-specific args structure, so they're all the same type)
|
||||
* avoids this problem but is otherwise not very appealing.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// uio ops (read, readlink, getdirentry, write, namefile)
|
||||
|
||||
int
|
||||
vopfail_uio_notdir(struct vnode *vn, struct uio *uio)
|
||||
{
|
||||
(void)vn;
|
||||
(void)uio;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_uio_isdir(struct vnode *vn, struct uio *uio)
|
||||
{
|
||||
(void)vn;
|
||||
(void)uio;
|
||||
return EISDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_uio_inval(struct vnode *vn, struct uio *uio)
|
||||
{
|
||||
(void)vn;
|
||||
(void)uio;
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_uio_nosys(struct vnode *vn, struct uio *uio)
|
||||
{
|
||||
(void)vn;
|
||||
(void)uio;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// mmap
|
||||
|
||||
int
|
||||
vopfail_mmap_isdir(struct vnode *vn /*add stuff */)
|
||||
{
|
||||
(void)vn;
|
||||
return EISDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_mmap_perm(struct vnode *vn /*add stuff */)
|
||||
{
|
||||
(void)vn;
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_mmap_nosys(struct vnode *vn /*add stuff */)
|
||||
{
|
||||
(void)vn;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// truncate
|
||||
|
||||
int
|
||||
vopfail_truncate_isdir(struct vnode *vn, off_t pos)
|
||||
{
|
||||
(void)vn;
|
||||
(void)pos;
|
||||
return EISDIR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// creat
|
||||
|
||||
int
|
||||
vopfail_creat_notdir(struct vnode *vn, const char *name, bool excl,
|
||||
mode_t mode, struct vnode **result)
|
||||
{
|
||||
(void)vn;
|
||||
(void)name;
|
||||
(void)excl;
|
||||
(void)mode;
|
||||
(void)result;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// symlink
|
||||
|
||||
int
|
||||
vopfail_symlink_notdir(struct vnode *vn, const char *contents,
|
||||
const char *name)
|
||||
{
|
||||
(void)vn;
|
||||
(void)contents;
|
||||
(void)name;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_symlink_nosys(struct vnode *vn, const char *contents,
|
||||
const char *name)
|
||||
{
|
||||
(void)vn;
|
||||
(void)contents;
|
||||
(void)name;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// mkdir
|
||||
|
||||
int
|
||||
vopfail_mkdir_notdir(struct vnode *vn, const char *name, mode_t mode)
|
||||
{
|
||||
(void)vn;
|
||||
(void)name;
|
||||
(void)mode;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_mkdir_nosys(struct vnode *vn, const char *name, mode_t mode)
|
||||
{
|
||||
(void)vn;
|
||||
(void)name;
|
||||
(void)mode;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// link
|
||||
|
||||
int
|
||||
vopfail_link_notdir(struct vnode *dir, const char *name, struct vnode *file)
|
||||
{
|
||||
(void)dir;
|
||||
(void)name;
|
||||
(void)file;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_link_nosys(struct vnode *dir, const char *name, struct vnode *file)
|
||||
{
|
||||
(void)dir;
|
||||
(void)name;
|
||||
(void)file;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// string ops (remove and rmdir)
|
||||
|
||||
int
|
||||
vopfail_string_notdir(struct vnode *vn, const char *name)
|
||||
{
|
||||
(void)vn;
|
||||
(void)name;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_string_nosys(struct vnode *vn, const char *name)
|
||||
{
|
||||
(void)vn;
|
||||
(void)name;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// rename
|
||||
|
||||
int
|
||||
vopfail_rename_notdir(struct vnode *fromdir, const char *fromname,
|
||||
struct vnode *todir, const char *toname)
|
||||
{
|
||||
(void)fromdir;
|
||||
(void)fromname;
|
||||
(void)todir;
|
||||
(void)toname;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_rename_nosys(struct vnode *fromdir, const char *fromname,
|
||||
struct vnode *todir, const char *toname)
|
||||
{
|
||||
(void)fromdir;
|
||||
(void)fromname;
|
||||
(void)todir;
|
||||
(void)toname;
|
||||
return ENOSYS;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// lookup
|
||||
|
||||
int
|
||||
vopfail_lookup_notdir(struct vnode *vn, char *path, struct vnode **result)
|
||||
{
|
||||
(void)vn;
|
||||
(void)path;
|
||||
(void)result;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
||||
int
|
||||
vopfail_lookparent_notdir(struct vnode *vn, char *path, struct vnode **result,
|
||||
char *buf, size_t len)
|
||||
{
|
||||
(void)vn;
|
||||
(void)path;
|
||||
(void)result;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
return ENOTDIR;
|
||||
}
|
||||
|
667
kern/vfs/vfslist.c
Normal file
667
kern/vfs/vfslist.c
Normal file
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* VFS operations that involve the list of VFS (named) devices
|
||||
* (the "dev" in "dev:path" syntax).
|
||||
*/
|
||||
|
||||
#define VFSINLINE
|
||||
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <array.h>
|
||||
#include <synch.h>
|
||||
#include <vfs.h>
|
||||
#include <fs.h>
|
||||
#include <vnode.h>
|
||||
#include <device.h>
|
||||
|
||||
/*
|
||||
* Structure for a single named device.
|
||||
*
|
||||
* kd_name - Name of device (eg, "lhd0"). Should always be set to
|
||||
* a valid string.
|
||||
*
|
||||
* kd_rawname - Name of raw device (eg, "lhd0raw"). Is non-NULL if and
|
||||
* only if this device can have a filesystem mounted on
|
||||
* it.
|
||||
*
|
||||
* kd_device - Device object this name refers to. May be NULL if kd_fs
|
||||
* is hardwired.
|
||||
*
|
||||
* kd_fs - Filesystem object mounted on, or associated with, this
|
||||
* device. NULL if there is no filesystem.
|
||||
*
|
||||
* A filesystem can be associated with a device without having been
|
||||
* mounted if the device was created that way. In this case,
|
||||
* kd_rawname is NULL (prohibiting mount/unmount), and, as there is
|
||||
* then no way to access kd_device, it will be NULL as well. This is
|
||||
* intended for devices that are inherently filesystems, like emu0.
|
||||
*
|
||||
* Referencing kd_name, or the filesystem volume name, on a device
|
||||
* with a filesystem mounted returns the root of the filesystem.
|
||||
* Referencing kd_name on a mountable device with no filesystem
|
||||
* returns ENXIO. Referencing kd_name on a device that is not
|
||||
* mountable and has no filesystem, or kd_rawname on a mountable
|
||||
* device, returns the device itself.
|
||||
*/
|
||||
|
||||
struct knowndev {
|
||||
char *kd_name;
|
||||
char *kd_rawname;
|
||||
struct device *kd_device;
|
||||
struct vnode *kd_vnode;
|
||||
struct fs *kd_fs;
|
||||
};
|
||||
|
||||
DECLARRAY(knowndev, static __UNUSED inline);
|
||||
DEFARRAY(knowndev, static __UNUSED inline);
|
||||
|
||||
static struct knowndevarray *knowndevs;
|
||||
|
||||
/* The big lock for all FS ops. Remove for filesystem assignment. */
|
||||
static struct lock *vfs_biglock;
|
||||
static unsigned vfs_biglock_depth;
|
||||
|
||||
|
||||
/*
|
||||
* Setup function
|
||||
*/
|
||||
void
|
||||
vfs_bootstrap(void)
|
||||
{
|
||||
knowndevs = knowndevarray_create();
|
||||
if (knowndevs==NULL) {
|
||||
panic("vfs: Could not create knowndevs array\n");
|
||||
}
|
||||
|
||||
vfs_biglock = lock_create("vfs_biglock");
|
||||
if (vfs_biglock==NULL) {
|
||||
panic("vfs: Could not create vfs big lock\n");
|
||||
}
|
||||
vfs_biglock_depth = 0;
|
||||
|
||||
devnull_create();
|
||||
semfs_bootstrap();
|
||||
}
|
||||
|
||||
/*
|
||||
* Operations on vfs_biglock. We make it recursive to avoid having to
|
||||
* think about where we do and don't already hold it. This is an
|
||||
* undesirable hack that's frequently necessary when a lock covers too
|
||||
* much material. Your solution scheme for FS and VFS locking should
|
||||
* not require recursive locks.
|
||||
*/
|
||||
void
|
||||
vfs_biglock_acquire(void)
|
||||
{
|
||||
if (!lock_do_i_hold(vfs_biglock)) {
|
||||
lock_acquire(vfs_biglock);
|
||||
}
|
||||
vfs_biglock_depth++;
|
||||
}
|
||||
|
||||
void
|
||||
vfs_biglock_release(void)
|
||||
{
|
||||
KASSERT(lock_do_i_hold(vfs_biglock));
|
||||
KASSERT(vfs_biglock_depth > 0);
|
||||
vfs_biglock_depth--;
|
||||
if (vfs_biglock_depth == 0) {
|
||||
lock_release(vfs_biglock);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
vfs_biglock_do_i_hold(void)
|
||||
{
|
||||
return lock_do_i_hold(vfs_biglock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Global sync function - call FSOP_SYNC on all devices.
|
||||
*/
|
||||
int
|
||||
vfs_sync(void)
|
||||
{
|
||||
struct knowndev *dev;
|
||||
unsigned i, num;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
num = knowndevarray_num(knowndevs);
|
||||
for (i=0; i<num; i++) {
|
||||
dev = knowndevarray_get(knowndevs, i);
|
||||
if (dev->kd_fs != NULL) {
|
||||
/*result =*/ FSOP_SYNC(dev->kd_fs);
|
||||
}
|
||||
}
|
||||
|
||||
vfs_biglock_release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a device name (lhd0, emu0, somevolname, null, etc.), hand
|
||||
* back an appropriate vnode.
|
||||
*/
|
||||
int
|
||||
vfs_getroot(const char *devname, struct vnode **ret)
|
||||
{
|
||||
struct knowndev *kd;
|
||||
unsigned i, num;
|
||||
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
num = knowndevarray_num(knowndevs);
|
||||
for (i=0; i<num; i++) {
|
||||
kd = knowndevarray_get(knowndevs, i);
|
||||
|
||||
/*
|
||||
* If this device has a mounted filesystem, and
|
||||
* DEVNAME names either the filesystem or the device,
|
||||
* return the root of the filesystem.
|
||||
*
|
||||
* If it has no mounted filesystem, it's mountable,
|
||||
* and DEVNAME names the device, return ENXIO.
|
||||
*/
|
||||
|
||||
if (kd->kd_fs!=NULL) {
|
||||
const char *volname;
|
||||
volname = FSOP_GETVOLNAME(kd->kd_fs);
|
||||
|
||||
if (!strcmp(kd->kd_name, devname) ||
|
||||
(volname!=NULL && !strcmp(volname, devname))) {
|
||||
return FSOP_GETROOT(kd->kd_fs, ret);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (kd->kd_rawname!=NULL &&
|
||||
!strcmp(kd->kd_name, devname)) {
|
||||
return ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If DEVNAME names the device, and we get here, it
|
||||
* must have no fs and not be mountable. In this case,
|
||||
* we return the device itself.
|
||||
*/
|
||||
if (!strcmp(kd->kd_name, devname)) {
|
||||
KASSERT(kd->kd_fs==NULL);
|
||||
KASSERT(kd->kd_rawname==NULL);
|
||||
KASSERT(kd->kd_device != NULL);
|
||||
VOP_INCREF(kd->kd_vnode);
|
||||
*ret = kd->kd_vnode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device has a rawname and DEVNAME names that,
|
||||
* return the device itself.
|
||||
*/
|
||||
if (kd->kd_rawname!=NULL && !strcmp(kd->kd_rawname, devname)) {
|
||||
KASSERT(kd->kd_device != NULL);
|
||||
VOP_INCREF(kd->kd_vnode);
|
||||
*ret = kd->kd_vnode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If none of the above tests matched, we didn't name
|
||||
* any of the names of this device, so go on to the
|
||||
* next one.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* If we got here, the device specified by devname doesn't exist.
|
||||
*/
|
||||
|
||||
return ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a filesystem, hand back the name of the device it's mounted on.
|
||||
*/
|
||||
const char *
|
||||
vfs_getdevname(struct fs *fs)
|
||||
{
|
||||
struct knowndev *kd;
|
||||
unsigned i, num;
|
||||
|
||||
KASSERT(fs != NULL);
|
||||
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
num = knowndevarray_num(knowndevs);
|
||||
for (i=0; i<num; i++) {
|
||||
kd = knowndevarray_get(knowndevs, i);
|
||||
|
||||
if (kd->kd_fs == fs) {
|
||||
/*
|
||||
* This is not a race condition: as long as the
|
||||
* guy calling us holds a reference to the fs,
|
||||
* the fs cannot go away, and the device can't
|
||||
* go away until the fs goes away.
|
||||
*/
|
||||
return kd->kd_name;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assemble the name for a raw device from the name for the regular device.
|
||||
*/
|
||||
static
|
||||
char *
|
||||
mkrawname(const char *name)
|
||||
{
|
||||
char *s = kmalloc(strlen(name)+3+1);
|
||||
if (!s) {
|
||||
return NULL;
|
||||
}
|
||||
strcpy(s, name);
|
||||
strcat(s, "raw");
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check if the two strings passed in are the same, if they're both
|
||||
* not NULL (the latter part being significant).
|
||||
*/
|
||||
static
|
||||
inline
|
||||
int
|
||||
samestring(const char *a, const char *b)
|
||||
{
|
||||
if (a==NULL || b==NULL) {
|
||||
return 0;
|
||||
}
|
||||
return !strcmp(a, b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the first string passed is the same as any of the three others,
|
||||
* if they're not NULL.
|
||||
*/
|
||||
static
|
||||
inline
|
||||
int
|
||||
samestring3(const char *a, const char *b, const char *c, const char *d)
|
||||
{
|
||||
return samestring(a,b) || samestring(a,c) || samestring(a,d);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if any of the three names passed in already exists as a device
|
||||
* name.
|
||||
*/
|
||||
|
||||
static
|
||||
int
|
||||
badnames(const char *n1, const char *n2, const char *n3)
|
||||
{
|
||||
const char *volname;
|
||||
unsigned i, num;
|
||||
struct knowndev *kd;
|
||||
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
num = knowndevarray_num(knowndevs);
|
||||
for (i=0; i<num; i++) {
|
||||
kd = knowndevarray_get(knowndevs, i);
|
||||
|
||||
if (kd->kd_fs) {
|
||||
volname = FSOP_GETVOLNAME(kd->kd_fs);
|
||||
if (samestring3(volname, n1, n2, n3)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (samestring3(kd->kd_rawname, n1, n2, n3) ||
|
||||
samestring3(kd->kd_name, n1, n2, n3)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a new device to the VFS layer's device table.
|
||||
*
|
||||
* If "mountable" is set, the device will be treated as one that expects
|
||||
* to have a filesystem mounted on it, and a raw device will be created
|
||||
* for direct access.
|
||||
*/
|
||||
static
|
||||
int
|
||||
vfs_doadd(const char *dname, int mountable, struct device *dev, struct fs *fs)
|
||||
{
|
||||
char *name=NULL, *rawname=NULL;
|
||||
struct knowndev *kd=NULL;
|
||||
struct vnode *vnode=NULL;
|
||||
const char *volname=NULL;
|
||||
unsigned index;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
name = kstrdup(dname);
|
||||
if (name==NULL) {
|
||||
result = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
if (mountable) {
|
||||
rawname = mkrawname(name);
|
||||
if (rawname==NULL) {
|
||||
result = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
vnode = dev_create_vnode(dev);
|
||||
if (vnode==NULL) {
|
||||
result = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
kd = kmalloc(sizeof(struct knowndev));
|
||||
if (kd==NULL) {
|
||||
result = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
kd->kd_name = name;
|
||||
kd->kd_rawname = rawname;
|
||||
kd->kd_device = dev;
|
||||
kd->kd_vnode = vnode;
|
||||
kd->kd_fs = fs;
|
||||
|
||||
if (fs!=NULL) {
|
||||
volname = FSOP_GETVOLNAME(fs);
|
||||
}
|
||||
|
||||
if (badnames(name, rawname, volname)) {
|
||||
result = EEXIST;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
result = knowndevarray_add(knowndevs, kd, &index);
|
||||
if (result) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (dev != NULL) {
|
||||
/* use index+1 as the device number, so 0 is reserved */
|
||||
dev->d_devnumber = index+1;
|
||||
}
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (name) {
|
||||
kfree(name);
|
||||
}
|
||||
if (rawname) {
|
||||
kfree(rawname);
|
||||
}
|
||||
if (vnode) {
|
||||
dev_uncreate_vnode(vnode);
|
||||
}
|
||||
if (kd) {
|
||||
kfree(kd);
|
||||
}
|
||||
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a new device, by name. See above for the description of
|
||||
* mountable.
|
||||
*/
|
||||
int
|
||||
vfs_adddev(const char *devname, struct device *dev, int mountable)
|
||||
{
|
||||
return vfs_doadd(devname, mountable, dev, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a filesystem that does not have an underlying device.
|
||||
* This is used for emufs, but might also be used for network
|
||||
* filesystems and the like.
|
||||
*/
|
||||
int
|
||||
vfs_addfs(const char *devname, struct fs *fs)
|
||||
{
|
||||
return vfs_doadd(devname, 0, NULL, fs);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Look for a mountable device named DEVNAME.
|
||||
* Should already hold knowndevs_lock.
|
||||
*/
|
||||
static
|
||||
int
|
||||
findmount(const char *devname, struct knowndev **result)
|
||||
{
|
||||
struct knowndev *dev;
|
||||
unsigned i, num;
|
||||
bool found = false;
|
||||
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
num = knowndevarray_num(knowndevs);
|
||||
for (i=0; !found && i<num; i++) {
|
||||
dev = knowndevarray_get(knowndevs, i);
|
||||
if (dev->kd_rawname==NULL) {
|
||||
/* not mountable/unmountable */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(devname, dev->kd_name)) {
|
||||
*result = dev;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return found ? 0 : ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mount a filesystem. Once we've found the device, call MOUNTFUNC to
|
||||
* set up the filesystem and hand back a struct fs.
|
||||
*
|
||||
* The DATA argument is passed through unchanged to MOUNTFUNC.
|
||||
*/
|
||||
int
|
||||
vfs_mount(const char *devname, void *data,
|
||||
int (*mountfunc)(void *data, struct device *, struct fs **ret))
|
||||
{
|
||||
const char *volname;
|
||||
struct knowndev *kd;
|
||||
struct fs *fs;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
result = findmount(devname, &kd);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
if (kd->kd_fs != NULL) {
|
||||
vfs_biglock_release();
|
||||
return EBUSY;
|
||||
}
|
||||
KASSERT(kd->kd_rawname != NULL);
|
||||
KASSERT(kd->kd_device != NULL);
|
||||
|
||||
result = mountfunc(data, kd->kd_device, &fs);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
KASSERT(fs != NULL);
|
||||
|
||||
kd->kd_fs = fs;
|
||||
|
||||
volname = FSOP_GETVOLNAME(fs);
|
||||
kprintf("vfs: Mounted %s: on %s\n",
|
||||
volname ? volname : kd->kd_name, kd->kd_name);
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unmount a filesystem/device by name.
|
||||
* First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT.
|
||||
*/
|
||||
int
|
||||
vfs_unmount(const char *devname)
|
||||
{
|
||||
struct knowndev *kd;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
result = findmount(devname, &kd);
|
||||
if (result) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (kd->kd_fs == NULL) {
|
||||
result = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
KASSERT(kd->kd_rawname != NULL);
|
||||
KASSERT(kd->kd_device != NULL);
|
||||
|
||||
/* sync the fs */
|
||||
result = FSOP_SYNC(kd->kd_fs);
|
||||
if (result) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
result = FSOP_UNMOUNT(kd->kd_fs);
|
||||
if (result) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
kprintf("vfs: Unmounted %s:\n", kd->kd_name);
|
||||
|
||||
/* now drop the filesystem */
|
||||
kd->kd_fs = NULL;
|
||||
|
||||
KASSERT(result==0);
|
||||
|
||||
fail:
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Global unmount function.
|
||||
*/
|
||||
int
|
||||
vfs_unmountall(void)
|
||||
{
|
||||
struct knowndev *dev;
|
||||
unsigned i, num;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
num = knowndevarray_num(knowndevs);
|
||||
for (i=0; i<num; i++) {
|
||||
dev = knowndevarray_get(knowndevs, i);
|
||||
if (dev->kd_rawname == NULL) {
|
||||
/* not mountable/unmountable */
|
||||
continue;
|
||||
}
|
||||
if (dev->kd_fs == NULL) {
|
||||
/* not mounted */
|
||||
continue;
|
||||
}
|
||||
|
||||
kprintf("vfs: Unmounting %s:\n", dev->kd_name);
|
||||
|
||||
result = FSOP_SYNC(dev->kd_fs);
|
||||
if (result) {
|
||||
kprintf("vfs: Warning: sync failed for %s: %s, trying "
|
||||
"again\n", dev->kd_name, strerror(result));
|
||||
|
||||
result = FSOP_SYNC(dev->kd_fs);
|
||||
if (result) {
|
||||
kprintf("vfs: Warning: sync failed second time"
|
||||
" for %s: %s, giving up...\n",
|
||||
dev->kd_name, strerror(result));
|
||||
/*
|
||||
* Do not attempt to complete the
|
||||
* unmount as it will likely explode.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result = FSOP_UNMOUNT(dev->kd_fs);
|
||||
if (result == EBUSY) {
|
||||
kprintf("vfs: Cannot unmount %s: (busy)\n",
|
||||
dev->kd_name);
|
||||
continue;
|
||||
}
|
||||
if (result) {
|
||||
kprintf("vfs: Warning: unmount failed for %s:"
|
||||
" %s, already synced, dropping...\n",
|
||||
dev->kd_name, strerror(result));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* now drop the filesystem */
|
||||
dev->kd_fs = NULL;
|
||||
}
|
||||
|
||||
vfs_biglock_release();
|
||||
|
||||
return 0;
|
||||
}
|
293
kern/vfs/vfslookup.c
Normal file
293
kern/vfs/vfslookup.c
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* VFS operations relating to pathname translation
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <limits.h>
|
||||
#include <lib.h>
|
||||
#include <synch.h>
|
||||
#include <vfs.h>
|
||||
#include <fs.h>
|
||||
#include <vnode.h>
|
||||
|
||||
static struct vnode *bootfs_vnode = NULL;
|
||||
|
||||
/*
|
||||
* Helper function for actually changing bootfs_vnode.
|
||||
*/
|
||||
static
|
||||
void
|
||||
change_bootfs(struct vnode *newvn)
|
||||
{
|
||||
struct vnode *oldvn;
|
||||
|
||||
oldvn = bootfs_vnode;
|
||||
bootfs_vnode = newvn;
|
||||
|
||||
if (oldvn != NULL) {
|
||||
VOP_DECREF(oldvn);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set bootfs_vnode.
|
||||
*
|
||||
* Bootfs_vnode is the vnode used for beginning path translation of
|
||||
* pathnames starting with /.
|
||||
*
|
||||
* It is also incidentally the system's first current directory.
|
||||
*/
|
||||
int
|
||||
vfs_setbootfs(const char *fsname)
|
||||
{
|
||||
char tmp[NAME_MAX+1];
|
||||
char *s;
|
||||
int result;
|
||||
struct vnode *newguy;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
snprintf(tmp, sizeof(tmp)-1, "%s", fsname);
|
||||
s = strchr(tmp, ':');
|
||||
if (s) {
|
||||
/* If there's a colon, it must be at the end */
|
||||
if (strlen(s)>0) {
|
||||
vfs_biglock_release();
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
strcat(tmp, ":");
|
||||
}
|
||||
|
||||
result = vfs_chdir(tmp);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
result = vfs_getcurdir(&newguy);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
change_bootfs(newguy);
|
||||
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the bootfs vnode (preparatory to system shutdown).
|
||||
*/
|
||||
void
|
||||
vfs_clearbootfs(void)
|
||||
{
|
||||
vfs_biglock_acquire();
|
||||
change_bootfs(NULL);
|
||||
vfs_biglock_release();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Common code to pull the device name, if any, off the front of a
|
||||
* path and choose the vnode to begin the name lookup relative to.
|
||||
*/
|
||||
|
||||
static
|
||||
int
|
||||
getdevice(char *path, char **subpath, struct vnode **startvn)
|
||||
{
|
||||
int slash=-1, colon=-1, i;
|
||||
struct vnode *vn;
|
||||
int result;
|
||||
|
||||
KASSERT(vfs_biglock_do_i_hold());
|
||||
|
||||
/*
|
||||
* Locate the first colon or slash.
|
||||
*/
|
||||
|
||||
for (i=0; path[i]; i++) {
|
||||
if (path[i]==':') {
|
||||
colon = i;
|
||||
break;
|
||||
}
|
||||
if (path[i]=='/') {
|
||||
slash = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (colon < 0 && slash != 0) {
|
||||
/*
|
||||
* No colon before a slash, so no device name
|
||||
* specified, and the slash isn't leading or is also
|
||||
* absent, so this is a relative path or just a bare
|
||||
* filename. Start from the current directory, and
|
||||
* use the whole thing as the subpath.
|
||||
*/
|
||||
*subpath = path;
|
||||
return vfs_getcurdir(startvn);
|
||||
}
|
||||
|
||||
if (colon>0) {
|
||||
/* device:path - get root of device's filesystem */
|
||||
path[colon]=0;
|
||||
while (path[colon+1]=='/') {
|
||||
/* device:/path - skip slash, treat as device:path */
|
||||
colon++;
|
||||
}
|
||||
*subpath = &path[colon+1];
|
||||
|
||||
result = vfs_getroot(path, startvn);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have either /path or :path.
|
||||
*
|
||||
* /path is a path relative to the root of the "boot filesystem".
|
||||
* :path is a path relative to the root of the current filesystem.
|
||||
*/
|
||||
KASSERT(colon==0 || slash==0);
|
||||
|
||||
if (path[0]=='/') {
|
||||
if (bootfs_vnode==NULL) {
|
||||
return ENOENT;
|
||||
}
|
||||
VOP_INCREF(bootfs_vnode);
|
||||
*startvn = bootfs_vnode;
|
||||
}
|
||||
else {
|
||||
KASSERT(path[0]==':');
|
||||
|
||||
result = vfs_getcurdir(&vn);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current directory may not be a device, so it
|
||||
* must have a fs.
|
||||
*/
|
||||
KASSERT(vn->vn_fs!=NULL);
|
||||
|
||||
result = FSOP_GETROOT(vn->vn_fs, startvn);
|
||||
|
||||
VOP_DECREF(vn);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
while (path[1]=='/') {
|
||||
/* ///... or :/... */
|
||||
path++;
|
||||
}
|
||||
|
||||
*subpath = path+1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Name-to-vnode translation.
|
||||
* (In BSD, both of these are subsumed by namei().)
|
||||
*/
|
||||
|
||||
int
|
||||
vfs_lookparent(char *path, struct vnode **retval,
|
||||
char *buf, size_t buflen)
|
||||
{
|
||||
struct vnode *startvn;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
result = getdevice(path, &path, &startvn);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
if (strlen(path)==0) {
|
||||
/*
|
||||
* It does not make sense to use just a device name in
|
||||
* a context where "lookparent" is the desired
|
||||
* operation.
|
||||
*/
|
||||
result = EINVAL;
|
||||
}
|
||||
else {
|
||||
result = VOP_LOOKPARENT(startvn, path, retval, buf, buflen);
|
||||
}
|
||||
|
||||
VOP_DECREF(startvn);
|
||||
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
vfs_lookup(char *path, struct vnode **retval)
|
||||
{
|
||||
struct vnode *startvn;
|
||||
int result;
|
||||
|
||||
vfs_biglock_acquire();
|
||||
|
||||
result = getdevice(path, &path, &startvn);
|
||||
if (result) {
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
||||
|
||||
if (strlen(path)==0) {
|
||||
*retval = startvn;
|
||||
vfs_biglock_release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = VOP_LOOKUP(startvn, path, retval);
|
||||
|
||||
VOP_DECREF(startvn);
|
||||
vfs_biglock_release();
|
||||
return result;
|
||||
}
|
316
kern/vfs/vfspath.c
Normal file
316
kern/vfs/vfspath.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* High-level VFS operations on pathnames.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <kern/fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <lib.h>
|
||||
#include <vfs.h>
|
||||
#include <vnode.h>
|
||||
|
||||
|
||||
/* Does most of the work for open(). */
|
||||
int
|
||||
vfs_open(char *path, int openflags, mode_t mode, struct vnode **ret)
|
||||
{
|
||||
int how;
|
||||
int result;
|
||||
int canwrite;
|
||||
struct vnode *vn = NULL;
|
||||
|
||||
how = openflags & O_ACCMODE;
|
||||
|
||||
switch (how) {
|
||||
case O_RDONLY:
|
||||
canwrite=0;
|
||||
break;
|
||||
case O_WRONLY:
|
||||
case O_RDWR:
|
||||
canwrite=1;
|
||||
break;
|
||||
default:
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (openflags & O_CREAT) {
|
||||
char name[NAME_MAX+1];
|
||||
struct vnode *dir;
|
||||
int excl = (openflags & O_EXCL)!=0;
|
||||
|
||||
result = vfs_lookparent(path, &dir, name, sizeof(name));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = VOP_CREAT(dir, name, excl, mode, &vn);
|
||||
|
||||
VOP_DECREF(dir);
|
||||
}
|
||||
else {
|
||||
result = vfs_lookup(path, &vn);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
KASSERT(vn != NULL);
|
||||
|
||||
result = VOP_EACHOPEN(vn, openflags);
|
||||
if (result) {
|
||||
VOP_DECREF(vn);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (openflags & O_TRUNC) {
|
||||
if (canwrite==0) {
|
||||
result = EINVAL;
|
||||
}
|
||||
else {
|
||||
result = VOP_TRUNCATE(vn, 0);
|
||||
}
|
||||
if (result) {
|
||||
VOP_DECREF(vn);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = vn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Does most of the work for close(). */
|
||||
void
|
||||
vfs_close(struct vnode *vn)
|
||||
{
|
||||
/*
|
||||
* VOP_DECREF doesn't return an error.
|
||||
*
|
||||
* We assume that the file system makes every reasonable
|
||||
* effort to not fail. If it does fail - such as on a hard I/O
|
||||
* error or something - vnode.c prints a warning. The reason
|
||||
* we don't report errors up to or above this level is that
|
||||
* (1) most application software does not check for close
|
||||
* failing, and more importantly
|
||||
* (2) we're often called from places like process exit
|
||||
* where reporting the error is impossible and
|
||||
* meaningful recovery is entirely impractical.
|
||||
*/
|
||||
|
||||
VOP_DECREF(vn);
|
||||
}
|
||||
|
||||
/* Does most of the work for remove(). */
|
||||
int
|
||||
vfs_remove(char *path)
|
||||
{
|
||||
struct vnode *dir;
|
||||
char name[NAME_MAX+1];
|
||||
int result;
|
||||
|
||||
result = vfs_lookparent(path, &dir, name, sizeof(name));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = VOP_REMOVE(dir, name);
|
||||
VOP_DECREF(dir);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Does most of the work for rename(). */
|
||||
int
|
||||
vfs_rename(char *oldpath, char *newpath)
|
||||
{
|
||||
struct vnode *olddir;
|
||||
char oldname[NAME_MAX+1];
|
||||
struct vnode *newdir;
|
||||
char newname[NAME_MAX+1];
|
||||
int result;
|
||||
|
||||
result = vfs_lookparent(oldpath, &olddir, oldname, sizeof(oldname));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = vfs_lookparent(newpath, &newdir, newname, sizeof(newname));
|
||||
if (result) {
|
||||
VOP_DECREF(olddir);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (olddir->vn_fs==NULL || newdir->vn_fs==NULL ||
|
||||
olddir->vn_fs != newdir->vn_fs) {
|
||||
VOP_DECREF(newdir);
|
||||
VOP_DECREF(olddir);
|
||||
return EXDEV;
|
||||
}
|
||||
|
||||
result = VOP_RENAME(olddir, oldname, newdir, newname);
|
||||
|
||||
VOP_DECREF(newdir);
|
||||
VOP_DECREF(olddir);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Does most of the work for link(). */
|
||||
int
|
||||
vfs_link(char *oldpath, char *newpath)
|
||||
{
|
||||
struct vnode *oldfile;
|
||||
struct vnode *newdir;
|
||||
char newname[NAME_MAX+1];
|
||||
int result;
|
||||
|
||||
result = vfs_lookup(oldpath, &oldfile);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = vfs_lookparent(newpath, &newdir, newname, sizeof(newname));
|
||||
if (result) {
|
||||
VOP_DECREF(oldfile);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (oldfile->vn_fs==NULL || newdir->vn_fs==NULL ||
|
||||
oldfile->vn_fs != newdir->vn_fs) {
|
||||
VOP_DECREF(newdir);
|
||||
VOP_DECREF(oldfile);
|
||||
return EXDEV;
|
||||
}
|
||||
|
||||
result = VOP_LINK(newdir, newname, oldfile);
|
||||
|
||||
VOP_DECREF(newdir);
|
||||
VOP_DECREF(oldfile);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does most of the work for symlink().
|
||||
*
|
||||
* Note, however, if you're implementing symlinks, that various
|
||||
* other parts of the VFS layer are missing crucial elements of
|
||||
* support for symlinks.
|
||||
*/
|
||||
int
|
||||
vfs_symlink(const char *contents, char *path)
|
||||
{
|
||||
struct vnode *newdir;
|
||||
char newname[NAME_MAX+1];
|
||||
int result;
|
||||
|
||||
result = vfs_lookparent(path, &newdir, newname, sizeof(newname));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = VOP_SYMLINK(newdir, newname, contents);
|
||||
VOP_DECREF(newdir);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does most of the work for readlink().
|
||||
*
|
||||
* Note, however, if you're implementing symlinks, that various
|
||||
* other parts of the VFS layer are missing crucial elements of
|
||||
* support for symlinks.
|
||||
*/
|
||||
int
|
||||
vfs_readlink(char *path, struct uio *uio)
|
||||
{
|
||||
struct vnode *vn;
|
||||
int result;
|
||||
|
||||
result = vfs_lookup(path, &vn);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = VOP_READLINK(vn, uio);
|
||||
|
||||
VOP_DECREF(vn);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does most of the work for mkdir.
|
||||
*/
|
||||
int
|
||||
vfs_mkdir(char *path, mode_t mode)
|
||||
{
|
||||
struct vnode *parent;
|
||||
char name[NAME_MAX+1];
|
||||
int result;
|
||||
|
||||
result = vfs_lookparent(path, &parent, name, sizeof(name));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = VOP_MKDIR(parent, name, mode);
|
||||
|
||||
VOP_DECREF(parent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does most of the work for rmdir.
|
||||
*/
|
||||
int
|
||||
vfs_rmdir(char *path)
|
||||
{
|
||||
struct vnode *parent;
|
||||
char name[NAME_MAX+1];
|
||||
int result;
|
||||
|
||||
result = vfs_lookparent(path, &parent, name, sizeof(name));
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = VOP_RMDIR(parent, name);
|
||||
|
||||
VOP_DECREF(parent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
177
kern/vfs/vnode.c
Normal file
177
kern/vfs/vnode.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Basic vnode support functions.
|
||||
*/
|
||||
#include <types.h>
|
||||
#include <kern/errno.h>
|
||||
#include <lib.h>
|
||||
#include <synch.h>
|
||||
#include <vfs.h>
|
||||
#include <vnode.h>
|
||||
|
||||
/*
|
||||
* Initialize an abstract vnode.
|
||||
*/
|
||||
int
|
||||
vnode_init(struct vnode *vn, const struct vnode_ops *ops,
|
||||
struct fs *fs, void *fsdata)
|
||||
{
|
||||
KASSERT(vn != NULL);
|
||||
KASSERT(ops != NULL);
|
||||
|
||||
vn->vn_ops = ops;
|
||||
vn->vn_refcount = 1;
|
||||
spinlock_init(&vn->vn_countlock);
|
||||
vn->vn_fs = fs;
|
||||
vn->vn_data = fsdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy an abstract vnode.
|
||||
*/
|
||||
void
|
||||
vnode_cleanup(struct vnode *vn)
|
||||
{
|
||||
KASSERT(vn->vn_refcount == 1);
|
||||
|
||||
spinlock_cleanup(&vn->vn_countlock);
|
||||
|
||||
vn->vn_ops = NULL;
|
||||
vn->vn_refcount = 0;
|
||||
vn->vn_fs = NULL;
|
||||
vn->vn_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Increment refcount.
|
||||
* Called by VOP_INCREF.
|
||||
*/
|
||||
void
|
||||
vnode_incref(struct vnode *vn)
|
||||
{
|
||||
KASSERT(vn != NULL);
|
||||
|
||||
spinlock_acquire(&vn->vn_countlock);
|
||||
vn->vn_refcount++;
|
||||
spinlock_release(&vn->vn_countlock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrement refcount.
|
||||
* Called by VOP_DECREF.
|
||||
* Calls VOP_RECLAIM if the refcount hits zero.
|
||||
*/
|
||||
void
|
||||
vnode_decref(struct vnode *vn)
|
||||
{
|
||||
bool destroy;
|
||||
int result;
|
||||
|
||||
KASSERT(vn != NULL);
|
||||
|
||||
spinlock_acquire(&vn->vn_countlock);
|
||||
|
||||
KASSERT(vn->vn_refcount > 0);
|
||||
if (vn->vn_refcount > 1) {
|
||||
vn->vn_refcount--;
|
||||
destroy = false;
|
||||
}
|
||||
else {
|
||||
/* Don't decrement; pass the reference to VOP_RECLAIM. */
|
||||
destroy = true;
|
||||
}
|
||||
spinlock_release(&vn->vn_countlock);
|
||||
|
||||
if (destroy) {
|
||||
result = VOP_RECLAIM(vn);
|
||||
if (result != 0 && result != EBUSY) {
|
||||
// XXX: lame.
|
||||
kprintf("vfs: Warning: VOP_RECLAIM: %s\n",
|
||||
strerror(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for various things being valid.
|
||||
* Called before all VOP_* calls.
|
||||
*/
|
||||
void
|
||||
vnode_check(struct vnode *v, const char *opstr)
|
||||
{
|
||||
vfs_biglock_acquire();
|
||||
|
||||
if (v == NULL) {
|
||||
panic("vnode_check: vop_%s: null vnode\n", opstr);
|
||||
}
|
||||
if (v == (void *)0xdeadbeef) {
|
||||
panic("vnode_check: vop_%s: deadbeef vnode\n", opstr);
|
||||
}
|
||||
|
||||
if (v->vn_ops == NULL) {
|
||||
panic("vnode_check: vop_%s: null ops pointer\n", opstr);
|
||||
}
|
||||
if (v->vn_ops == (void *)0xdeadbeef) {
|
||||
panic("vnode_check: vop_%s: deadbeef ops pointer\n", opstr);
|
||||
}
|
||||
|
||||
if (v->vn_ops->vop_magic != VOP_MAGIC) {
|
||||
panic("vnode_check: vop_%s: ops with bad magic number %lx\n",
|
||||
opstr, v->vn_ops->vop_magic);
|
||||
}
|
||||
|
||||
// Device vnodes have null fs pointers.
|
||||
//if (v->vn_fs == NULL) {
|
||||
// panic("vnode_check: vop_%s: null fs pointer\n", opstr);
|
||||
//}
|
||||
if (v->vn_fs == (void *)0xdeadbeef) {
|
||||
panic("vnode_check: vop_%s: deadbeef fs pointer\n", opstr);
|
||||
}
|
||||
|
||||
spinlock_acquire(&v->vn_countlock);
|
||||
|
||||
if (v->vn_refcount < 0) {
|
||||
panic("vnode_check: vop_%s: negative refcount %d\n", opstr,
|
||||
v->vn_refcount);
|
||||
}
|
||||
else if (v->vn_refcount == 0) {
|
||||
panic("vnode_check: vop_%s: zero refcount\n", opstr);
|
||||
}
|
||||
else if (v->vn_refcount > 0x100000) {
|
||||
kprintf("vnode_check: vop_%s: warning: large refcount %d\n",
|
||||
opstr, v->vn_refcount);
|
||||
}
|
||||
|
||||
spinlock_release(&v->vn_countlock);
|
||||
vfs_biglock_release();
|
||||
}
|
Reference in New Issue
Block a user