X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/0a34b6fd2e5f8cabcaff493bdd094820e32c21a4..a937f8567ba9375553a85507f18042a6faaffaab:/usr/src/sys/kern/vfs_vnops.c diff --git a/usr/src/sys/kern/vfs_vnops.c b/usr/src/sys/kern/vfs_vnops.c index f527bcc629..53f12b312a 100644 --- a/usr/src/sys/kern/vfs_vnops.c +++ b/usr/src/sys/kern/vfs_vnops.c @@ -1,290 +1,583 @@ -/* vfs_vnops.c 4.6 %G% */ - -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/dir.h" -#include "../h/user.h" -#include "../h/filsys.h" -#include "../h/file.h" -#include "../h/conf.h" -#include "../h/inode.h" -#include "../h/reg.h" -#include "../h/acct.h" -#include "../h/mount.h" +/* + * Copyright (c) 1982, 1986, 1989 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_vnops.c 7.9 (Berkeley) %G% + */ + +#include "param.h" +#include "systm.h" +#include "user.h" +#include "kernel.h" +#include "file.h" +#include "stat.h" +#include "buf.h" +#include "proc.h" +#include "uio.h" +#include "socket.h" +#include "socketvar.h" +#include "mount.h" +#include "vnode.h" +#include "../ufs/inode.h" +#include "../ufs/fs.h" +#include "../ufs/quota.h" +#include "ioctl.h" +#include "tty.h" + +int vn_read(), vn_write(), vn_ioctl(), vn_select(), vn_close(); +struct fileops vnops = + { vn_read, vn_write, vn_ioctl, vn_select, vn_close }; /* - * Convert a user supplied - * file descriptor into a pointer - * to a file structure. - * Only task is to check range - * of the descriptor. + * Common code for vnode open operations. + * Check permissions, and call the VOP_OPEN or VOP_CREATE routine. */ -struct file * -getf(f) -register int f; +vn_open(ndp, fmode, cmode) + register struct nameidata *ndp; + int fmode, cmode; { - register struct file *fp; + register struct vnode *vp; + struct vattr vat; + struct vattr *vap = &vat; + int error; - if ((unsigned)f >= NOFILE || (fp = u.u_ofile[f]) == NULL) { - u.u_error = EBADF; - return (NULL); + if (fmode & FCREAT) { + ndp->ni_nameiop = CREATE | LOCKPARENT | LOCKLEAF; + if ((fmode & FEXCL) == 0) + ndp->ni_nameiop |= FOLLOW; + if (error = namei(ndp)) + return (error); + if (ndp->ni_vp == NULL) { + vattr_null(vap); + vap->va_type = VREG; + vap->va_mode = cmode; + if (error = VOP_CREATE(ndp, vap)) + return (error); + fmode &= ~FTRUNC; + vp = ndp->ni_vp; + } else { + vp = ndp->ni_vp; + ndp->ni_vp = 0; + VOP_ABORTOP(ndp); + ndp->ni_vp = vp; + if (fmode & FEXCL) { + error = EEXIST; + goto bad; + } + fmode &= ~FCREAT; + } + } else { + ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF; + if (error = namei(ndp)) + return (error); + vp = ndp->ni_vp; } -#ifdef UCBIPC - if (u.u_pofile[f] & ISPORT) { - u.u_error = EISPORT; - return (NULL); + if (vp->v_type == VSOCK) { + error = EOPNOTSUPP; + goto bad; } -#endif - return (fp); + if ((fmode & FCREAT) == 0) { + if (fmode & FREAD) { + if (error = VOP_ACCESS(vp, VREAD, ndp->ni_cred)) + goto bad; + } + if (fmode & (FWRITE|FTRUNC)) { + if (vp->v_type == VDIR) { + error = EISDIR; + goto bad; + } + if ((error = vn_writechk(vp)) || + (error = VOP_ACCESS(vp, VWRITE, ndp->ni_cred))) + goto bad; + } + } + if (fmode & FTRUNC) { + vattr_null(vap); + vap->va_size = 0; + if (error = VOP_SETATTR(vp, vap, ndp->ni_cred)) + goto bad; + } + VOP_UNLOCK(vp); + if (setjmp(&u.u_qsave)) { + if (error == 0) + error = EINTR; + return (error); + } + error = VOP_OPEN(vp, fmode, ndp->ni_cred); + if (error) + vrele(vp); + return (error); + +bad: + vput(vp); + return(error); } /* - * Internal form of close. - * Decrement reference count on - * file structure. - * Also make sure the pipe protocol - * does not constipate. - * - * Decrement reference count on the inode following - * removal to the referencing file structure. - * Call device handler on last close. + * Check for write permissions on the specified vnode. + * The read-only status of the file system is checked. + * Also, prototype text segments cannot be written. */ -closef(fp) -register struct file *fp; +vn_writechk(vp) + register struct vnode *vp; { - register struct inode *ip; - register struct mount *mp; - int flag, mode; - dev_t dev; - register int (*cfunc)(); - if(fp == NULL) - return; - if (fp->f_count > 1) { - fp->f_count--; - return; - } - ip = fp->f_inode; - flag = fp->f_flag; - dev = (dev_t)ip->i_un.i_rdev; - mode = ip->i_mode & IFMT; - - plock(ip); - fp->f_count = 0; - if(flag & FPIPE) { - ip->i_mode &= ~(IREAD|IWRITE); - wakeup((caddr_t)ip+1); - wakeup((caddr_t)ip+2); - } - iput(ip); + /* + * Disallow write attempts on read-only file systems; + * unless the file is a socket or a block or character + * device resident on the file system. + */ + if ((vp->v_mount->m_flag & M_RDONLY) && vp->v_type != VCHR && + vp->v_type != VBLK && vp->v_type != VSOCK) + return (EROFS); + /* + * If there's shared text associated with + * the vnode, try to free it up once. If + * we fail, we can't allow writing. + */ + if (vp->v_flag & VTEXT) + xrele(vp); + if (vp->v_flag & VTEXT) + return (ETXTBSY); + return (0); +} - switch(mode) { +/* + * Vnode version of rdwri() for calls on file systems. + */ +vn_rdwr(rw, vp, base, len, offset, segflg, ioflg, cred, aresid) + enum uio_rw rw; + struct vnode *vp; + caddr_t base; + int len; + off_t offset; + enum uio_seg segflg; + int ioflg; + struct ucred *cred; + int *aresid; +{ + struct uio auio; + struct iovec aiov; + int error; - case IFCHR: - case IFMPC: - cfunc = cdevsw[major(dev)].d_close; - break; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + aiov.iov_base = base; + aiov.iov_len = len; + auio.uio_resid = len; + auio.uio_offset = offset; + auio.uio_segflg = segflg; + auio.uio_rw = rw; + if (rw == UIO_READ) + error = VOP_READ(vp, &auio, &offset, ioflg, cred); + else + error = VOP_WRITE(vp, &auio, &offset, ioflg, cred); + if (aresid) + *aresid = auio.uio_resid; + else + if (auio.uio_resid && error == 0) + error = EIO; + return (error); +} - case IFBLK: - /* - * We don't want to really close the device if it is mounted - */ - for (mp = mount; mp < &mount[NMOUNT]; mp++) - if (mp->m_bufp != NULL && mp->m_dev == dev) - return; - case IFMPB: - cfunc = bdevsw[major(dev)].d_close; +vn_read(fp, uio, cred) + struct file *fp; + struct uio *uio; + struct ucred *cred; +{ + + return (VOP_READ((struct vnode *)fp->f_data, uio, &(fp->f_offset), + (fp->f_flag & FNDELAY) ? IO_NDELAY : 0, cred)); +} + +vn_write(fp, uio, cred) + struct file *fp; + struct uio *uio; + struct ucred *cred; +{ + register struct vnode *vp = (struct vnode *)fp->f_data; + int ioflag = 0; + + if (vp->v_type == VREG && (fp->f_flag & FAPPEND)) + ioflag |= IO_APPEND; + if (fp->f_flag & FNDELAY) + ioflag |= IO_NDELAY; + return (VOP_WRITE(vp, uio, &(fp->f_offset), ioflag, cred)); +} + +/* + * Get stat info for a vnode. + */ +vn_stat(vp, sb) + struct vnode *vp; + register struct stat *sb; +{ + struct vattr vattr; + register struct vattr *vap; + int error; + u_short mode; + + vap = &vattr; + error = VOP_GETATTR(vp, vap, u.u_cred); + if (error) + return (error); + /* + * Copy from vattr table + */ + sb->st_dev = vap->va_fsid; + sb->st_ino = vap->va_fileid; + mode = vap->va_mode; + switch (vp->v_type) { + case VREG: + mode |= IFREG; + break; + case VDIR: + mode |= IFDIR; + break; + case VBLK: + mode |= IFBLK; + break; + case VCHR: + mode |= IFCHR; + break; + case VLNK: + mode |= IFLNK; + break; + case VSOCK: + mode |= IFSOCK; break; default: - return; - } - - if ((flag & FMP) == 0) { - for(fp=file; fp < fileNFILE; fp++) - if (fp->f_count && (ip=fp->f_inode)->i_un.i_rdev==dev && - (ip->i_mode&IFMT) == mode) - return; - if (mode == IFBLK) { - /* - * on last close of a block device (that isn't mounted) - * we must invalidate any in core blocks - */ - bflush(dev); - binval(dev); - } - } - (*cfunc)(dev, flag, fp); + return (EBADF); + }; + sb->st_mode = mode; + sb->st_nlink = vap->va_nlink; + sb->st_uid = vap->va_uid; + sb->st_gid = vap->va_gid; + sb->st_rdev = vap->va_rdev; + sb->st_size = vap->va_size; + sb->st_atime = vap->va_atime.tv_sec; + sb->st_spare1 = 0; + sb->st_mtime = vap->va_mtime.tv_sec; + sb->st_spare2 = 0; + sb->st_ctime = vap->va_ctime.tv_sec; + sb->st_spare3 = 0; + sb->st_blksize = vap->va_blocksize; + sb->st_flags = vap->va_flags; + sb->st_gen = vap->va_gen; + sb->st_blocks = vap->va_bytes / S_BLKSIZE; + return (0); } /* - * openi called to allow handler - * of special files to initialize and - * validate before actual IO. + * Vnode ioctl call */ -openi(ip, rw) -register struct inode *ip; +vn_ioctl(fp, com, data) + struct file *fp; + int com; + caddr_t data; { - dev_t dev; - register unsigned int maj; + register struct vnode *vp = ((struct vnode *)fp->f_data); + struct vattr vattr; + int error; - dev = (dev_t)ip->i_un.i_rdev; - maj = major(dev); - switch(ip->i_mode&IFMT) { + switch (vp->v_type) { - case IFCHR: - case IFMPC: - if(maj >= nchrdev) - goto bad; - (*cdevsw[maj].d_open)(dev, rw); - break; + case VREG: + case VDIR: + if (com == FIONREAD) { + if (error = VOP_GETATTR(vp, &vattr, u.u_cred)) + return (error); + *(off_t *)data = vattr.va_size - fp->f_offset; + return (0); + } + if (com == FIONBIO || com == FIOASYNC) /* XXX */ + return (0); /* XXX */ + /* fall into ... */ - case IFBLK: - case IFMPB: - if(maj >= nblkdev) - goto bad; - (*bdevsw[maj].d_open)(dev, rw); + default: + return (ENOTTY); + + case VCHR: + case VBLK: + u.u_r.r_val1 = 0; + if (setjmp(&u.u_qsave)) { + if ((u.u_sigintr & sigmask(u.u_procp->p_cursig)) != 0) + return(EINTR); + u.u_eosys = RESTARTSYS; + return (0); + } + return (VOP_IOCTL(vp, com, data, fp->f_flag, u.u_cred)); } - return; +} -bad: - u.u_error = ENXIO; +/* + * Vnode select call + */ +vn_select(fp, which) + struct file *fp; + int which; +{ + return(VOP_SELECT(((struct vnode *)fp->f_data), which, u.u_cred)); +} + +/* + * Vnode close call + */ +vn_close(fp) + register struct file *fp; +{ + struct vnode *vp = ((struct vnode *)fp->f_data); + int error; + + if (fp->f_flag & (FSHLOCK|FEXLOCK)) + vn_unlock(fp, FSHLOCK|FEXLOCK); + /* + * Must delete vnode reference from this file entry + * before VOP_CLOSE, so that only other references + * will prevent close. + */ + fp->f_data = (caddr_t) 0; + error = VOP_CLOSE(vp, fp->f_flag, u.u_cred); + vrele(vp); + return (error); } /* - * Check mode permission on inode pointer. - * Mode is READ, WRITE or EXEC. - * In the case of WRITE, the - * read-only status of the file - * system is checked. - * Also in WRITE, prototype text - * segments cannot be written. - * The mode is shifted to select - * the owner/group/other fields. - * The super user is granted all - * permissions. + * Place an advisory lock on a vnode. + * !! THIS IMPLIES THAT ALL STATEFUL FILE SERVERS WILL USE file table entries */ -access(ip, mode) -register struct inode *ip; +vn_lock(fp, cmd) + register struct file *fp; + int cmd; { - register m; + register int priority = PLOCK; + register struct vnode *vp = (struct vnode *)fp->f_data; - m = mode; - if(m == IWRITE) { - if(getfs(ip->i_dev)->s_ronly != 0) { - u.u_error = EROFS; - return(1); + if ((cmd & LOCK_EX) == 0) + priority += 4; + if (setjmp(&u.u_qsave)) { + if ((u.u_sigintr & sigmask(u.u_procp->p_cursig)) != 0) + return(EINTR); + u.u_eosys = RESTARTSYS; + return (0); + } + /* + * If there's a exclusive lock currently applied + * to the file, then we've gotta wait for the + * lock with everyone else. + */ +again: + while (vp->v_flag & VEXLOCK) { + /* + * If we're holding an exclusive + * lock, then release it. + */ + if (fp->f_flag & FEXLOCK) { + vn_unlock(fp, FEXLOCK); + continue; } - if (ip->i_flag&ITEXT) /* try to free text */ - xrele(ip); - if(ip->i_flag & ITEXT) { - u.u_error = ETXTBSY; - return(1); + if (cmd & LOCK_NB) + return (EWOULDBLOCK); + vp->v_flag |= VLWAIT; + sleep((caddr_t)&vp->v_exlockc, priority); + } + if ((cmd & LOCK_EX) && (vp->v_flag & VSHLOCK)) { + /* + * Must wait for any shared locks to finish + * before we try to apply a exclusive lock. + * + * If we're holding a shared + * lock, then release it. + */ + if (fp->f_flag & FSHLOCK) { + vn_unlock(fp, FSHLOCK); + goto again; } + if (cmd & LOCK_NB) + return (EWOULDBLOCK); + vp->v_flag |= VLWAIT; + sleep((caddr_t)&vp->v_shlockc, PLOCK); + goto again; } - if(u.u_uid == 0) - return(0); - if(u.u_uid != ip->i_uid) { - m >>= 3; - if(u.u_gid != ip->i_gid) - m >>= 3; + if (fp->f_flag & FEXLOCK) + panic("vn_lock"); + if (cmd & LOCK_EX) { + cmd &= ~LOCK_SH; + vp->v_exlockc++; + vp->v_flag |= VEXLOCK; + fp->f_flag |= FEXLOCK; } - if((ip->i_mode&m) != 0) - return(0); + if ((cmd & LOCK_SH) && (fp->f_flag & FSHLOCK) == 0) { + vp->v_shlockc++; + vp->v_flag |= VSHLOCK; + fp->f_flag |= FSHLOCK; + } + return (0); +} + +/* + * Unlock a file. + */ +vn_unlock(fp, kind) + register struct file *fp; + int kind; +{ + register struct vnode *vp = (struct vnode *)fp->f_data; + int flags; - u.u_error = EACCES; - return(1); + kind &= fp->f_flag; + if (vp == NULL || kind == 0) + return; + flags = vp->v_flag; + if (kind & FSHLOCK) { + if ((flags & VSHLOCK) == 0) + panic("vn_unlock: SHLOCK"); + if (--vp->v_shlockc == 0) { + vp->v_flag &= ~VSHLOCK; + if (flags & VLWAIT) + wakeup((caddr_t)&vp->v_shlockc); + } + fp->f_flag &= ~FSHLOCK; + } + if (kind & FEXLOCK) { + if ((flags & VEXLOCK) == 0) + panic("vn_unlock: EXLOCK"); + if (--vp->v_exlockc == 0) { + vp->v_flag &= ~(VEXLOCK|VLWAIT); + if (flags & VLWAIT) + wakeup((caddr_t)&vp->v_exlockc); + } + fp->f_flag &= ~FEXLOCK; + } } /* - * Look up a pathname and test if - * the resultant inode is owned by the - * current user. - * If not, try for super-user. - * If permission is granted, - * return inode pointer. + * vn_fhtovp() - convert a fh to a vnode ptr (optionally locked) + * - look up fsid in mount list (if not found ret error) + * - get vp by calling VFS_FHTOVP() macro + * - if lockflag lock it with VOP_LOCK() */ -struct inode * -owner() +vn_fhtovp(fhp, lockflag, vpp) + fhandle_t *fhp; + int lockflag; + struct vnode **vpp; { - register struct inode *ip; - - ip = namei(uchar, 0); - if(ip == NULL) - return(NULL); - if(u.u_uid == ip->i_uid) - return(ip); - if(suser()) - return(ip); - iput(ip); - return(NULL); + register struct mount *mp; + int error; + + if ((mp = getvfs(&fhp->fh_fsid)) == NULL) + return (ESTALE); + if (VFS_FHTOVP(mp, &fhp->fh_fid, vpp)) + return (ESTALE); + if (!lockflag) + VOP_UNLOCK(*vpp); + return (0); } /* - * Test if the current user is the - * super user. + * Revoke access the current tty by all processes. + * Used only by the super-user in init + * to give ``clean'' terminals at login. */ -suser() +vhangup() { - if(u.u_uid == 0) { - u.u_acflag |= ASU; - return(1); + if (u.u_error = suser(u.u_cred, &u.u_acflag)) + return; + if (u.u_ttyp == NULL) + return; + forceclose(u.u_ttyd); + if ((u.u_ttyp->t_state) & TS_ISOPEN) + gsignal(u.u_ttyp->t_pgid, SIGHUP); + u.u_ttyp->t_session = 0; + u.u_ttyp->t_pgid = 0; +} + +forceclose(dev) + dev_t dev; +{ + register struct file *fp; + register struct vnode *vp; + + for (fp = file; fp < fileNFILE; fp++) { + if (fp->f_count == 0) + continue; + if (fp->f_type != DTYPE_VNODE) + continue; + vp = (struct vnode *)fp->f_data; + if (vp == 0) + continue; + if (vp->v_type != VCHR) + continue; + if (vp->v_rdev != dev) + continue; + fp->f_flag &= ~(FREAD|FWRITE); } - u.u_error = EPERM; - return(0); } /* - * Allocate a user file descriptor. + * Vnode reference, just increment the count */ -ufalloc() +void vref(vp) + struct vnode *vp; { - register i; - for(i=0; iv_count++; } -struct file *lastf; /* - * Allocate a user file descriptor - * and a file structure. - * Initialize the descriptor - * to point at the file structure. - * - * no file -- if there are no available - * file structures. + * Vnode release, just decrement the count and call VOP_INACTIVE() */ -struct file * -falloc() +void vrele(vp) + register struct vnode *vp; { - register struct file *fp; - register i; - - i = ufalloc(); - if (i < 0) - return(NULL); - if (lastf == 0) - lastf = file; - for (fp = lastf; fp < fileNFILE; fp++) - if (fp->f_count == 0) - goto slot; - for (fp = file; fp < lastf; fp++) - if (fp->f_count == 0) - goto slot; - printf("no file\n"); - u.u_error = ENFILE; - return (NULL); -slot: - u.u_ofile[i] = fp; - fp->f_count++; - fp->f_un.f_offset = 0; - lastf = fp + 1; - return (fp); + + if (vp == NULL) + return; + vp->v_count--; + if (vp->v_count < 0) + printf("inode %d, bad ref count %d\n", + VTOI(vp)->i_number, vp->v_count); + if (vp->v_count == 0) + VOP_INACTIVE(vp); +} + +/* + * vput(), just unlock and vrele() + */ +vput(vp) + register struct vnode *vp; +{ + VOP_UNLOCK(vp); + vrele(vp); +} + +/* + * Noop + */ +vfs_noop() +{ + + return (ENXIO); +} + +/* + * Null op + */ +vfs_nullop() +{ + + return (0); }