+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)vfs_syscalls.c 7.13 (Berkeley) %G%
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "syscontext.h"
+#include "kernel.h"
+#include "file.h"
+#include "stat.h"
+#include "vnode.h"
+#include "../ufs/inode.h"
+#include "mount.h"
+#include "proc.h"
+#include "uio.h"
+#include "malloc.h"
+
+/*
+ * Virtual File System System Calls
+ */
+
+/*
+ * mount system call
+ */
+mount()
+{
+ register struct a {
+ int type;
+ char *dir;
+ int flags;
+ caddr_t data;
+ } *uap = (struct a *)u.u_ap;
+ register struct nameidata *ndp = &u.u_nd;
+ struct vnode *vp;
+ struct mount *mp;
+ int error;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(u.u_cred, &u.u_acflag))
+ RETURN (error);
+ /*
+ * Get vnode to be covered
+ */
+ ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->dir;
+ if (error = namei(ndp))
+ RETURN (error);
+ vp = ndp->ni_vp;
+ if (vp->v_count != 1) {
+ vput(vp);
+ RETURN (EBUSY);
+ }
+ if (vp->v_type != VDIR) {
+ vput(vp);
+ RETURN (ENOTDIR);
+ }
+ if (uap->type > MOUNT_MAXTYPE ||
+ vfssw[uap->type] == (struct vfsops *)0) {
+ vput(vp);
+ RETURN (ENODEV);
+ }
+
+ /*
+ * Mount the filesystem.
+ */
+ mp = (struct mount *)malloc((u_long)sizeof(struct mount),
+ M_MOUNT, M_WAITOK);
+ mp->m_op = vfssw[uap->type];
+ mp->m_flag = 0;
+ mp->m_exroot = 0;
+ error = vfs_add(vp, mp, uap->flags);
+ if (!error)
+ error = VFS_MOUNT(mp, uap->dir, uap->data, ndp);
+ cache_purge(vp);
+ VOP_UNLOCK(vp);
+ if (!error) {
+ vfs_unlock(mp);
+ } else {
+ vfs_remove(mp);
+ free((caddr_t)mp, M_MOUNT);
+ vrele(vp);
+ }
+ RETURN (error);
+}
+
+/*
+ * 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;
+ int flags;
+ } *uap = (struct a *)u.u_ap;
+ register struct vnode *vp;
+ register struct mount *mp;
+ register struct nameidata *ndp = &u.u_nd;
+ struct vnode *coveredvp;
+ int error;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(u.u_cred, &u.u_acflag))
+ RETURN (error);
+
+ ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->pathp;
+ if (error = namei(ndp))
+ RETURN (error);
+ vp = ndp->ni_vp;
+ /*
+ * Must be the root of the filesystem
+ */
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ RETURN (EINVAL);
+ }
+ mp = vp->v_mount;
+ vput(vp);
+ /*
+ * Do the unmount.
+ */
+ coveredvp = mp->m_vnodecovered;
+ if (error = vfs_lock(mp))
+ RETURN (error);
+
+ xumount(mp); /* remove unused sticky files from text table */
+ cache_purgevfs(mp); /* remove cache entries for this file sys */
+ VFS_SYNC(mp, MNT_WAIT);
+
+ error = VFS_UNMOUNT(mp, uap->flags);
+ if (error) {
+ vfs_unlock(mp);
+ } else {
+ vrele(coveredvp);
+ vfs_remove(mp);
+ free((caddr_t)mp, M_MOUNT);
+ }
+ RETURN (error);
+}
+
+/*
+ * Sync system call.
+ * Sync each mounted filesystem.
+ */
+sync()
+{
+ register struct mount *mp;
+
+ mp = rootfs;
+ do {
+ if ((mp->m_flag & M_RDONLY) == 0)
+ VFS_SYNC(mp, MNT_NOWAIT);
+ mp = mp->m_next;
+ } while (mp != rootfs);
+}
+
+/*
+ * get filesystem statistics
+ */
+statfs()
+{
+ struct a {
+ char *path;
+ struct statfs *buf;
+ } *uap = (struct a *)u.u_ap;
+ register struct vnode *vp;
+ register struct nameidata *ndp = &u.u_nd;
+ struct statfs sb;
+ int error;
+
+ ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->path;
+ if (error = namei(ndp))
+ RETURN (error);
+ vp = ndp->ni_vp;
+ if (error = VFS_STATFS(vp->v_mount, &sb))
+ goto out;
+ error = copyout((caddr_t)&sb, (caddr_t)uap->buf, sizeof(sb));
+out:
+ vput(vp);
+ RETURN (error);
+}
+
+fstatfs()
+{
+ struct a {
+ int fd;
+ struct statfs *buf;
+ } *uap = (struct a *)u.u_ap;
+ struct file *fp;
+ struct statfs sb;
+ int error;
+
+ if (error = getvnode(uap->fd, &fp))
+ RETURN (error);
+ if (error = VFS_STATFS(((struct vnode *)fp->f_data)->v_mount, &sb))
+ RETURN (error);
+ RETURN (copyout((caddr_t)&sb, (caddr_t)uap->buf, sizeof(sb)));
+}
+
+/*
+ * get statistics on all filesystems
+ */
+getfsstat()
+{
+ struct a {
+ struct statfs *buf;
+ long bufsize;
+ } *uap = (struct a *)u.u_ap;
+ register struct mount *mp;
+ register struct statfs *sfsp;
+ long count, maxcount, error;
+
+ maxcount = uap->bufsize / sizeof(struct statfs);
+ sfsp = uap->buf;
+ mp = rootfs;
+ count = 0;
+ do {
+ count++;
+ if (sfsp && count <= maxcount) {
+ if (error = VFS_STATFS(mp, sfsp))
+ RETURN (error);
+ sfsp++;
+ }
+ mp = mp->m_prev;
+ } while (mp != rootfs);
+ if (sfsp && count > maxcount)
+ u.u_r.r_val1 = maxcount;
+ else
+ u.u_r.r_val1 = count;
+ RETURN (0);
+}
+
+/*
+ * Change current working directory to a given file descriptor.
+ */
+fchdir()
+{
+ struct a {
+ int fd;
+ } *uap = (struct a *)u.u_ap;
+ register struct vnode *vp;
+ struct file *fp;
+ int error;
+
+ if (error = getvnode(uap->fd, &fp))
+ RETURN (error);
+ vp = (struct vnode *)fp->f_data;
+ VOP_LOCK(vp);
+ if (vp->v_type != VDIR)
+ error = ENOTDIR;
+ else
+ error = VOP_ACCESS(vp, VEXEC, u.u_cred);
+ VOP_UNLOCK(vp);
+ vrele(u.u_cdir);
+ u.u_cdir = vp;
+ RETURN (error);
+}