vfs_syscalls.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,115 行 · 第 1/4 页
C
2,115 行
/* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ *//* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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 REGENTS 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 REGENTS 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_syscalls.c 8.28 (Berkeley) 12/10/94 */#include <sys/param.h>#include <sys/systm.h>#include <sys/namei.h>#include <sys/filedesc.h>#include <sys/kernel.h>#include <sys/file.h>#include <sys/stat.h>#include <sys/vnode.h>#include <sys/mount.h>#include <sys/proc.h>#include <sys/uio.h>#include <sys/malloc.h>#include <sys/dirent.h>#include <sys/syscallargs.h>#include <vm/vm.h>#include <sys/sysctl.h>static int change_dir __P((struct nameidata *, struct proc *));void checkdirs __P((struct vnode *));int dounmount __P((struct mount *, int, struct proc *));/* * Virtual File System System Calls *//* * Mount a file system. *//* ARGSUSED */intsys_mount(p, v, retval) struct proc *p; void *v; register_t *retval;{ register struct sys_mount_args /* { syscallarg(char *) type; syscallarg(char *) path; syscallarg(int) flags; syscallarg(caddr_t) data; } */ *uap = v; register struct vnode *vp; register struct mount *mp; int error, flag = 0; u_long fsindex = 0; char fstypename[MFSNAMELEN]; struct vattr va; struct nameidata nd; /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (SCARG(uap, flags) & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((SCARG(uap, flags) & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } mp->mnt_flag |= SCARG(uap, flags) & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); /* * Only root, or the user that did the original mount is * permitted to update it. */ if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && (error = suser(p->p_ucred, &p->p_acflag))) { vput(vp); return (error); } /* * Do not allow NFS export by non-root users. Silently * enforce MNT_NOSUID and MNT_NODEV for non-root users. */ if (p->p_ucred->cr_uid != 0) { if (SCARG(uap, flags) & MNT_EXPORTED) { vput(vp); return (EPERM); } SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; } VOP_UNLOCK(vp); goto update; } /* * If the user is not root, ensure that they own the directory * onto which we are attempting to mount. */ if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || (va.va_uid != p->p_ucred->cr_uid && (error = suser(p->p_ucred, &p->p_acflag)))) { vput(vp); return (error); } /* * Do not allow NFS export by non-root users. Silently * enforce MNT_NOSUID and MNT_NODEV for non-root users. */ if (p->p_ucred->cr_uid != 0) { if (SCARG(uap, flags) & MNT_EXPORTED) { vput(vp); return (EPERM); } SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; } if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL); if (error) {#if defined(COMPAT_09) || defined(COMPAT_43) /* * Historically filesystem types were identified by number. * If we get an integer for the filesystem type instead of a * string, we check to see if it matches one of the historic * filesystem types. */ fsindex = (u_long)SCARG(uap, type); if (fsindex >= nvfssw || vfssw[fsindex] == NULL) { vput(vp); return (ENODEV); } strncpy(fstypename, vfssw[fsindex]->vfs_name, MFSNAMELEN);#else vput(vp); return (error);#endif }#ifdef COMPAT_10 /* Accept `ufs' as an alias for `ffs'. */ if (!strncmp(fstypename, "ufs", MFSNAMELEN)) strncpy(fstypename, "ffs", MFSNAMELEN);#endif for (fsindex = 0; fsindex < nvfssw; fsindex++) if (vfssw[fsindex] != NULL && !strncmp(vfssw[fsindex]->vfs_name, fstypename, MFSNAMELEN)) break; if (fsindex >= nvfssw) { vput(vp); return (ENODEV); } if (vp->v_mountedhere != NULL) { vput(vp); return (EBUSY); } /* * Allocate and initialize the file system. */ mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); mp->mnt_op = vfssw[fsindex]; if ((error = vfs_lock(mp)) != 0) { free((caddr_t)mp, M_MOUNT); vput(vp); return (error); } /* Do this early in case we block later. */ vfssw[fsindex]->vfs_refcount++; vp->v_mountedhere = mp; mp->mnt_vnodecovered = vp; mp->mnt_stat.f_owner = p->p_ucred->cr_uid;update: /* * Set the mount level flags. */ if (SCARG(uap, flags) & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_flag |= MNT_WANTRDWR; mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); /* * Mount the filesystem. */ error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); if (mp->mnt_flag & MNT_UPDATE) { vrele(vp); if (mp->mnt_flag & MNT_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); if (error) mp->mnt_flag = flag; return (error); } /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); checkdirs(vp); VOP_UNLOCK(vp); vfs_unlock(mp); (void) VFS_STATFS(mp, &mp->mnt_stat, p); error = VFS_START(mp, 0, p); } else { mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; vfssw[fsindex]->vfs_refcount--; vfs_unlock(mp); free((caddr_t)mp, M_MOUNT); vput(vp); } return (error);}/* * Scan all active processes to see if any of them have a current * or root directory onto which the new filesystem has just been * mounted. If so, replace them with the new mount point. */voidcheckdirs(olddp) struct vnode *olddp;{ struct filedesc *fdp; struct vnode *newdp; struct proc *p; if (olddp->v_usecount == 1) return; if (VFS_ROOT(olddp->v_mountedhere, &newdp)) panic("mount: lost mount"); for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { fdp = p->p_fd; if (fdp->fd_cdir == olddp) { vrele(fdp->fd_cdir); VREF(newdp); fdp->fd_cdir = newdp; } if (fdp->fd_rdir == olddp) { vrele(fdp->fd_rdir); VREF(newdp); fdp->fd_rdir = newdp; } } if (rootvnode == olddp) { vrele(rootvnode); VREF(newdp); rootvnode = newdp; } vput(newdp);}/* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). *//* ARGSUSED */intsys_unmount(p, v, retval) struct proc *p; void *v; register_t *retval;{ register struct sys_unmount_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap = v; register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; mp = vp->v_mount; /* * Only root, or the user that did the original mount is * permitted to unmount this filesystem. */ if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) && (error = suser(p->p_ucred, &p->p_acflag))) { vput(vp); return (error); } /* * Don't allow unmounting the root file system. */ if (mp->mnt_flag & MNT_ROOTFS) { vput(vp); return (EINVAL); } /* * Must be the root of the filesystem */ if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } vput(vp); return (dounmount(mp, SCARG(uap, flags), p));}/* * Do the actual file system unmount. */intdounmount(mp, flags, p) register struct mount *mp; int flags; struct proc *p;{ struct vnode *coveredvp; int error; coveredvp = mp->mnt_vnodecovered; if (vfs_busy(mp)) return (EBUSY); mp->mnt_flag |= MNT_UNMOUNT; if ((error = vfs_lock(mp)) != 0) return (error); mp->mnt_flag &=~ MNT_ASYNC; vnode_pager_umount(mp); /* release cached vnodes */ cache_purgevfs(mp); /* remove cache entries for this file sys */ if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || (flags & MNT_FORCE)) error = VFS_UNMOUNT(mp, flags, p); mp->mnt_flag &= ~MNT_UNMOUNT; vfs_unbusy(mp); if (error) { vfs_unlock(mp); } else { CIRCLEQ_REMOVE(&mountlist, mp, mnt_list); if (coveredvp != NULLVP) { vrele(coveredvp); coveredvp->v_mountedhere = (struct mount *)0; } mp->mnt_op->vfs_refcount--; vfs_unlock(mp); if (mp->mnt_vnodelist.lh_first != NULL) panic("unmount: dangling vnode"); free((caddr_t)mp, M_MOUNT); } return (error);}/* * Sync each mounted filesystem. */#ifdef DEBUGint syncprt = 0;struct ctldebug debug0 = { "syncprt", &syncprt };#endif/* ARGSUSED */intsys_sync(p, v, retval) struct proc *p; void *v; register_t *retval;{ register struct mount *mp, *nmp; int asyncflag; for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { /* * Get the next pointer in case we hang on vfs_busy * while we are being unmounted. */ nmp = mp->mnt_list.cqe_next; /* * The lock check below is to avoid races with mount * and unmount. */ if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 && !vfs_busy(mp)) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p); if (asyncflag) mp->mnt_flag |= MNT_ASYNC; /* * Get the next pointer again, as the next filesystem * might have been unmounted while we were sync'ing. */ nmp = mp->mnt_list.cqe_next; vfs_unbusy(mp); } }#ifdef DEBUG if (syncprt) vfs_bufstats();#endif /* DEBUG */ return (0);}/* * Change filesystem quotas. *//* ARGSUSED */intsys_quotactl(p, v, retval) struct proc *p; void *v; register_t *retval;{ register struct sys_quotactl_args /* { syscallarg(char *) path; syscallarg(int) cmd; syscallarg(int) uid; syscallarg(caddr_t) arg; } */ *uap = v; register struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; vrele(nd.ni_vp); return (VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid), SCARG(uap, arg), p));}/* * Get filesystem statistics. *//* ARGSUSED */intsys_statfs(p, v, retval) struct proc *p; void *v; register_t *retval;{ register struct sys_statfs_args /* { syscallarg(char *) path; syscallarg(struct statfs *) buf; } */ *uap = v; register struct mount *mp; register struct statfs *sp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; vrele(nd.ni_vp); if ((error = VFS_STATFS(mp, sp, p)) != 0) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp)));}/* * Get filesystem statistics. *//* ARGSUSED */intsys_fstatfs(p, v, retval) struct proc *p;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?