4.3BSD-UWisc/src/sys/sys/vfs.c
/* @(#)vfs.c 1.1 86/02/03 SMI */
/* NFSSRC @(#)vfs.c 2.1 86/04/15 */
#include "param.h"
#include "user.h"
#include "uio.h"
#include "file.h"
#include "vfs.h"
#include "socket.h"
#undef NFS
#include "mount.h"
#include "pathname.h"
#include "vnode.h"
#include "../ufs/inode.h"
/*
* vfs global data
*/
struct vnode *rootdir; /* pointer to root vnode */
struct vfs *rootvfs; /* pointer to root vfs. This is */
/* also the head of the vfs list */
/*
* System calls
*/
/*
* mount system call
*/
mount()
{
register struct a {
int type;
char *dir;
int flags;
caddr_t data;
} *uap = (struct a *) u.u_ap;
struct pathname pn;
struct vnode *vp;
struct vfs *vfsp;
extern struct vnodeops ufs_vnodeops;
/*
* Must be super user
*/
if (!suser())
return;
/*
* Get vnode to be covered
*/
u.u_error =
lookupname(uap->dir, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &vp);
if (u.u_error)
return;
dnlc_purge();
if (vp->v_count != 1 || vp->v_type != VDIR) {
VN_RELE(vp);
u.u_error = EBUSY;
return;
}
u.u_error = pn_get(uap->dir, UIO_USERSPACE, &pn);
if (u.u_error) {
VN_RELE(vp);
return;
}
if (uap->type > MOUNT_MAXTYPE ||
vfssw[uap->type] == (struct vfsops *)0) {
u.u_error = ENODEV;
pn_free(&pn); /* release pathname */
VN_RELE(vp);
return;
}
vfsp = (struct vfs *)kmem_alloc(sizeof (struct vfs));
VFS_INIT(vfsp, vfssw[uap->type], (caddr_t)0);
/*
* Mount the filesystem.
* Lock covered vnode (XXX this currently only works if it is type ufs)
*/
if (vp->v_op == &ufs_vnodeops)
ilock(VTOI(vp));
u.u_error = vfs_add(vp, vfsp, uap->flags);
if (!u.u_error) {
u.u_error = VFS_MOUNT(vfsp, pn.pn_path, uap->data);
}
pn_free(&pn); /* release pathname */
if (vp->v_op == &ufs_vnodeops)
iunlock(VTOI(vp));
if (!u.u_error) {
vfs_unlock(vfsp);
} else {
vfs_remove(vfsp);
kmem_free((caddr_t)vfsp, sizeof (struct vfs));
VN_RELE(vp);
}
return;
}
/*
* Sync system call. sync each vfs
*/
sync()
{
register struct vfs *vfsp;
for (vfsp = rootvfs; vfsp != (struct vfs *)0; vfsp = vfsp->vfs_next) {
VFS_SYNC(vfsp);
}
}
/*
* get filesystem statistics
*/
statfs()
{
struct a {
char *path;
struct statfs *buf;
} *uap = (struct a *) u.u_ap;
struct vnode *vp;
u.u_error =
lookupname(uap->path, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &vp);
if (u.u_error)
return;
cstatfs(vp->v_vfsp, uap->buf);
VN_RELE(vp);
}
fstatfs()
{
struct a {
int fd;
struct statfs *buf;
} *uap = (struct a *) u.u_ap;
struct file *fp;
u.u_error = getvnodefp(uap->fd, &fp);
if (u.u_error == 0)
cstatfs(((struct vnode *)fp->f_data)->v_vfsp, uap->buf);
}
cstatfs(vfsp, ubuf)
struct vfs *vfsp;
struct statfs *ubuf;
{
struct statfs sb;
u.u_error = VFS_STATFS(vfsp, &sb);
if (u.u_error)
return;
u.u_error = copyout((caddr_t)&sb, (caddr_t)ubuf, sizeof(sb));
}
/*
* Unmount system call.
*
* Note: unmount takes a path to the vnode mounted on as argument,
* not special file (as before).
*/
unmount()
{
struct a {
char *pathp;
} *uap = (struct a *) u.u_ap;
struct vnode *fsrootvp;
register struct vnode *coveredvp;
register struct vfs *vfsp;
if (!suser()) {
u.u_error = EPERM;
return;
}
/*
* lookup root of fs
*/
u.u_error = lookupname(uap->pathp, UIO_USERSPACE, FOLLOW_LINK,
(struct vnode **)0, &fsrootvp);
if (u.u_error)
return;
/*
* make sure this is a root
*/
if ((fsrootvp->v_flag & VROOT) == 0) {
u.u_error = EINVAL;
VN_RELE(fsrootvp);
return;
}
/*
* get vfs and covered vnode
*/
vfsp = fsrootvp->v_vfsp;
VN_RELE(fsrootvp);
coveredvp = vfsp->vfs_vnodecovered;
/*
* lock vnode to maintain fs status quo during unmount
*/
u.u_error = vfs_lock(vfsp);
if(u.u_error)
return;
xumount(vfsp); /* remove unused sticky files from text table */
dnlc_purge(); /* remove dnlc entries for this file sys */
update();
u.u_error = VFS_UNMOUNT(vfsp);
if (u.u_error) {
vfs_unlock(vfsp);
} else {
VN_RELE(coveredvp);
vfs_remove(vfsp);
kmem_free((caddr_t)vfsp, (u_int)sizeof(*vfsp));
}
}
/*
* External routines
*/
/*
* vfs_mountroot is called by main (init_main.c) to
* mount the root filesystem.
*/
void
vfs_mountroot()
{
register int error;
extern int (*rootfsmount)(); /* pointer to root mounting routine */
/* set by (auto)configuration */
/*
* Rootfsmount is a pointer to the routine which will mount a specific
* filesystem as the root. It is setup by autoconfiguration.
* If error panic.
*/
error = (*rootfsmount)();
if (error) {
panic("rootmount cannot mount root");
}
/*
* Get vnode for '/'.
* Setup rootdir, u.u_rdir and u.u_cdir to point to it.
* These are used by lookuppn so that it knows where
* to start from '/' or '.'.
*/
error = VFS_ROOT(rootvfs, &rootdir);
if (error)
panic("rootmount: cannot find root vnode");
u.u_cdir = rootdir;
VN_HOLD(u.u_cdir);
u.u_rdir = NULL;
}
/*
* vfs_add is called by a specific filesystem's mount routine to add
* the new vfs into the vfs list and to cover the mounted on vnode.
* The vfs is also locked so that lookuppn will not venture into the
* covered vnodes subtree.
* coveredvp is zero if this is the root.
*/
int
vfs_add(coveredvp, vfsp, mflag)
register struct vnode *coveredvp;
register struct vfs *vfsp;
int mflag;
{
register int error;
error = vfs_lock(vfsp);
if(error)
return(error);
if (coveredvp != (struct vnode *)0) {
/*
* Return EBUSY if the covered vp is already mounted on.
*/
if (coveredvp->v_vfsmountedhere != (struct vfs *)0) {
vfs_unlock(vfsp);
return(EBUSY);
}
/*
* Put the new vfs on the vfs list after root.
* Point the covered vnode at the new vfs so lookuppn
* (vfs_lookup.c) can work its way into the new file system.
*/
vfsp->vfs_next = rootvfs->vfs_next;
rootvfs->vfs_next = vfsp;
coveredvp->v_vfsmountedhere = vfsp;
} else {
/*
* This is the root of the whole world.
*/
rootvfs = vfsp;
vfsp->vfs_next = (struct vfs *)0;
}
vfsp->vfs_vnodecovered = coveredvp;
if (mflag & M_RDONLY) {
vfsp->vfs_flag |= VFS_RDONLY;
} else {
vfsp->vfs_flag &= ~VFS_RDONLY;
}
if (mflag & M_NOSUID) {
vfsp->vfs_flag |= VFS_NOSUID;
} else {
vfsp->vfs_flag &= ~VFS_NOSUID;
}
return(0);
}
/*
* Remove a vfs from the vfs list, and destory pointers to it.
* Should be called by filesystem implementation after it determines
* that an unmount is legal but before it destroys the vfs.
*/
void
vfs_remove(vfsp)
register struct vfs *vfsp;
{
register struct vfs *tvfsp;
register struct vnode *vp;
/*
* can't unmount root. Should never happen, because fs will be busy.
*/
if (vfsp == rootvfs)
panic("vfs_remove: unmounting root");
for (tvfsp = rootvfs;
tvfsp != (struct vfs *)0; tvfsp = tvfsp->vfs_next) {
if (tvfsp->vfs_next == vfsp) {
/*
* remove vfs from list, unmount covered vp.
*/
tvfsp->vfs_next = vfsp->vfs_next;
vp = vfsp->vfs_vnodecovered;
vp->v_vfsmountedhere = (struct vfs *)0;
/*
* release lock and wakeup anybody waiting
*/
vfs_unlock(vfsp);
return;
}
}
/*
* can't find vfs to remove
*/
panic("vfs_remove: vfs not found");
}
/*
* Lock a filesystem to prevent access to it while mounting and unmounting.
* Returns error if already locked.
* XXX This totally inadequate for unmount right now - srk
*/
int
vfs_lock(vfsp)
register struct vfs *vfsp;
{
if (vfsp->vfs_flag & VFS_MLOCK)
return(EBUSY);
vfsp->vfs_flag |= VFS_MLOCK;
return(0);
}
/*
* Unlock a locked filesystem.
* Panics if not locked
*/
void
vfs_unlock(vfsp)
register struct vfs *vfsp;
{
if ((vfsp->vfs_flag & VFS_MLOCK) == 0)
panic("vfs_unlock");
vfsp->vfs_flag &= ~VFS_MLOCK;
/*
* Wake anybody waiting for the lock to clear
*/
if (vfsp->vfs_flag & VFS_MWAIT) {
vfsp->vfs_flag &= ~VFS_MWAIT;
wakeup((caddr_t)vfsp);
}
}