xref: /original-bsd/sys/kern/vfs_vnops.c (revision 552d28cb)
1b5970980Smckusick /*
2760e73c7Smckusick  * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
3760e73c7Smckusick  * All rights reserved.
4b5970980Smckusick  *
573a8e095Sbostic  * %sccs.include.redist.c%
6760e73c7Smckusick  *
7*552d28cbSmckusick  *	@(#)vfs_vnops.c	7.31 (Berkeley) 05/30/91
8b5970980Smckusick  */
982e77ef7Ssam 
10ecd0bebeSbloom #include "param.h"
11ecd0bebeSbloom #include "systm.h"
12760e73c7Smckusick #include "kernel.h"
13ecd0bebeSbloom #include "file.h"
14760e73c7Smckusick #include "stat.h"
15760e73c7Smckusick #include "buf.h"
16760e73c7Smckusick #include "proc.h"
17760e73c7Smckusick #include "mount.h"
182d8de7e9Smckusick #include "namei.h"
19760e73c7Smckusick #include "vnode.h"
20760e73c7Smckusick #include "ioctl.h"
21760e73c7Smckusick #include "tty.h"
221d37f34bSbill 
23760e73c7Smckusick struct 	fileops vnops =
24760e73c7Smckusick 	{ vn_read, vn_write, vn_ioctl, vn_select, vn_close };
25ae1aeb7bSmckusick 
261d37f34bSbill /*
27760e73c7Smckusick  * Common code for vnode open operations.
28760e73c7Smckusick  * Check permissions, and call the VOP_OPEN or VOP_CREATE routine.
291d37f34bSbill  */
300b0833e3Skarels vn_open(ndp, p, fmode, cmode)
31760e73c7Smckusick 	register struct nameidata *ndp;
320b0833e3Skarels 	struct proc *p;
33760e73c7Smckusick 	int fmode, cmode;
341d37f34bSbill {
35760e73c7Smckusick 	register struct vnode *vp;
360b0833e3Skarels 	register struct ucred *cred = p->p_ucred;
37760e73c7Smckusick 	struct vattr vat;
38760e73c7Smckusick 	struct vattr *vap = &vat;
39760e73c7Smckusick 	int error;
401d37f34bSbill 
41*552d28cbSmckusick 	if (fmode & O_CREAT) {
42760e73c7Smckusick 		ndp->ni_nameiop = CREATE | LOCKPARENT | LOCKLEAF;
43*552d28cbSmckusick 		if ((fmode & O_EXCL) == 0)
44760e73c7Smckusick 			ndp->ni_nameiop |= FOLLOW;
450b0833e3Skarels 		if (error = namei(ndp, p))
46760e73c7Smckusick 			return (error);
47760e73c7Smckusick 		if (ndp->ni_vp == NULL) {
489a8d5e2aSmckusick 			VATTR_NULL(vap);
49760e73c7Smckusick 			vap->va_type = VREG;
50760e73c7Smckusick 			vap->va_mode = cmode;
512d8de7e9Smckusick 			if (error = VOP_CREATE(ndp, vap, p))
52760e73c7Smckusick 				return (error);
53*552d28cbSmckusick 			fmode &= ~O_TRUNC;
54760e73c7Smckusick 			vp = ndp->ni_vp;
55760e73c7Smckusick 		} else {
56ebdb0513Smckusick 			VOP_ABORTOP(ndp);
5783225eaeSmckusick 			if (ndp->ni_dvp == ndp->ni_vp)
5883225eaeSmckusick 				vrele(ndp->ni_dvp);
59e8a20c25Smckusick 			else
6083225eaeSmckusick 				vput(ndp->ni_dvp);
6183225eaeSmckusick 			ndp->ni_dvp = NULL;
62760e73c7Smckusick 			vp = ndp->ni_vp;
63*552d28cbSmckusick 			if (fmode & O_EXCL) {
64760e73c7Smckusick 				error = EEXIST;
65760e73c7Smckusick 				goto bad;
66760e73c7Smckusick 			}
67*552d28cbSmckusick 			fmode &= ~O_CREAT;
68760e73c7Smckusick 		}
69760e73c7Smckusick 	} else {
70760e73c7Smckusick 		ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF;
710b0833e3Skarels 		if (error = namei(ndp, p))
72760e73c7Smckusick 			return (error);
73760e73c7Smckusick 		vp = ndp->ni_vp;
74760e73c7Smckusick 	}
75760e73c7Smckusick 	if (vp->v_type == VSOCK) {
76760e73c7Smckusick 		error = EOPNOTSUPP;
77760e73c7Smckusick 		goto bad;
78760e73c7Smckusick 	}
79*552d28cbSmckusick 	if ((fmode & O_CREAT) == 0) {
80760e73c7Smckusick 		if (fmode & FREAD) {
812d8de7e9Smckusick 			if (error = VOP_ACCESS(vp, VREAD, cred, p))
82760e73c7Smckusick 				goto bad;
83760e73c7Smckusick 		}
84*552d28cbSmckusick 		if (fmode & (FWRITE | O_TRUNC)) {
85760e73c7Smckusick 			if (vp->v_type == VDIR) {
86760e73c7Smckusick 				error = EISDIR;
87760e73c7Smckusick 				goto bad;
88760e73c7Smckusick 			}
89a597f5e9Smckusick 			if ((error = vn_writechk(vp)) ||
902d8de7e9Smckusick 			    (error = VOP_ACCESS(vp, VWRITE, cred, p)))
91a597f5e9Smckusick 				goto bad;
92760e73c7Smckusick 		}
93760e73c7Smckusick 	}
94*552d28cbSmckusick 	if (fmode & O_TRUNC) {
959a8d5e2aSmckusick 		VATTR_NULL(vap);
96760e73c7Smckusick 		vap->va_size = 0;
972d8de7e9Smckusick 		if (error = VOP_SETATTR(vp, vap, cred, p))
98760e73c7Smckusick 			goto bad;
99760e73c7Smckusick 	}
100*552d28cbSmckusick 	if ((error = VOP_OPEN(vp, fmode, cred, p)) == 0)
101*552d28cbSmckusick 		return (0);
102760e73c7Smckusick bad:
103760e73c7Smckusick 	vput(vp);
104760e73c7Smckusick 	return (error);
105760e73c7Smckusick }
106760e73c7Smckusick 
10745db6fc1Ssam /*
108a597f5e9Smckusick  * Check for write permissions on the specified vnode.
109a597f5e9Smckusick  * The read-only status of the file system is checked.
110a597f5e9Smckusick  * Also, prototype text segments cannot be written.
11145db6fc1Ssam  */
112a597f5e9Smckusick vn_writechk(vp)
113760e73c7Smckusick 	register struct vnode *vp;
114760e73c7Smckusick {
115760e73c7Smckusick 
116760e73c7Smckusick 	/*
117760e73c7Smckusick 	 * Disallow write attempts on read-only file systems;
118760e73c7Smckusick 	 * unless the file is a socket or a block or character
119760e73c7Smckusick 	 * device resident on the file system.
120760e73c7Smckusick 	 */
1219fc842b5Smckusick 	if (vp->v_mount->mnt_flag & MNT_RDONLY) {
1229fc842b5Smckusick 		switch (vp->v_type) {
1239fc842b5Smckusick 		case VREG: case VDIR: case VLNK:
124760e73c7Smckusick 			return (EROFS);
1259fc842b5Smckusick 		}
1269fc842b5Smckusick 	}
12745db6fc1Ssam 	/*
12845db6fc1Ssam 	 * If there's shared text associated with
129a597f5e9Smckusick 	 * the vnode, try to free it up once.  If
13045db6fc1Ssam 	 * we fail, we can't allow writing.
13145db6fc1Ssam 	 */
1323065037bSmckusick 	if ((vp->v_flag & VTEXT) && !vnode_pager_uncache(vp))
133760e73c7Smckusick 		return (ETXTBSY);
134a597f5e9Smckusick 	return (0);
1351d37f34bSbill }
1361d37f34bSbill 
1371d37f34bSbill /*
138760e73c7Smckusick  * Vnode version of rdwri() for calls on file systems.
1391d37f34bSbill  */
1402d8de7e9Smckusick vn_rdwr(rw, vp, base, len, offset, segflg, ioflg, cred, aresid, p)
141760e73c7Smckusick 	enum uio_rw rw;
142760e73c7Smckusick 	struct vnode *vp;
143760e73c7Smckusick 	caddr_t base;
144760e73c7Smckusick 	int len;
145760e73c7Smckusick 	off_t offset;
146760e73c7Smckusick 	enum uio_seg segflg;
147760e73c7Smckusick 	int ioflg;
148760e73c7Smckusick 	struct ucred *cred;
149760e73c7Smckusick 	int *aresid;
1502d8de7e9Smckusick 	struct proc *p;
1511d37f34bSbill {
152760e73c7Smckusick 	struct uio auio;
153760e73c7Smckusick 	struct iovec aiov;
154760e73c7Smckusick 	int error;
1551d37f34bSbill 
1562a601dc3Smckusick 	if ((ioflg & IO_NODELOCKED) == 0)
1572a601dc3Smckusick 		VOP_LOCK(vp);
158760e73c7Smckusick 	auio.uio_iov = &aiov;
159760e73c7Smckusick 	auio.uio_iovcnt = 1;
160760e73c7Smckusick 	aiov.iov_base = base;
161760e73c7Smckusick 	aiov.iov_len = len;
162760e73c7Smckusick 	auio.uio_resid = len;
163760e73c7Smckusick 	auio.uio_offset = offset;
164760e73c7Smckusick 	auio.uio_segflg = segflg;
165760e73c7Smckusick 	auio.uio_rw = rw;
1662d8de7e9Smckusick 	auio.uio_procp = p;
167760e73c7Smckusick 	if (rw == UIO_READ)
1682a601dc3Smckusick 		error = VOP_READ(vp, &auio, ioflg, cred);
169760e73c7Smckusick 	else
1702a601dc3Smckusick 		error = VOP_WRITE(vp, &auio, ioflg, cred);
171760e73c7Smckusick 	if (aresid)
172760e73c7Smckusick 		*aresid = auio.uio_resid;
173760e73c7Smckusick 	else
174760e73c7Smckusick 		if (auio.uio_resid && error == 0)
175760e73c7Smckusick 			error = EIO;
1762a601dc3Smckusick 	if ((ioflg & IO_NODELOCKED) == 0)
1772a601dc3Smckusick 		VOP_UNLOCK(vp);
178760e73c7Smckusick 	return (error);
1792510f0cdSmckusick }
1801d37f34bSbill 
181760e73c7Smckusick vn_read(fp, uio, cred)
182760e73c7Smckusick 	struct file *fp;
183760e73c7Smckusick 	struct uio *uio;
184760e73c7Smckusick 	struct ucred *cred;
1851d37f34bSbill {
1862a601dc3Smckusick 	register struct vnode *vp = (struct vnode *)fp->f_data;
1872a601dc3Smckusick 	int count, error;
1881d37f34bSbill 
1892a601dc3Smckusick 	VOP_LOCK(vp);
1902a601dc3Smckusick 	uio->uio_offset = fp->f_offset;
1912a601dc3Smckusick 	count = uio->uio_resid;
192*552d28cbSmckusick 	error = VOP_READ(vp, uio, (fp->f_flag & FNONBLOCK) ? IO_NDELAY : 0,
193*552d28cbSmckusick 		cred);
1942a601dc3Smckusick 	fp->f_offset += count - uio->uio_resid;
1952a601dc3Smckusick 	VOP_UNLOCK(vp);
1962a601dc3Smckusick 	return (error);
197760e73c7Smckusick }
198760e73c7Smckusick 
199760e73c7Smckusick vn_write(fp, uio, cred)
200760e73c7Smckusick 	struct file *fp;
201760e73c7Smckusick 	struct uio *uio;
202760e73c7Smckusick 	struct ucred *cred;
203760e73c7Smckusick {
204760e73c7Smckusick 	register struct vnode *vp = (struct vnode *)fp->f_data;
2052a601dc3Smckusick 	int count, error, ioflag = 0;
206760e73c7Smckusick 
207*552d28cbSmckusick 	if (vp->v_type == VREG && (fp->f_flag & O_APPEND))
208760e73c7Smckusick 		ioflag |= IO_APPEND;
209*552d28cbSmckusick 	if (fp->f_flag & FNONBLOCK)
210760e73c7Smckusick 		ioflag |= IO_NDELAY;
2112a601dc3Smckusick 	VOP_LOCK(vp);
2122a601dc3Smckusick 	uio->uio_offset = fp->f_offset;
2132a601dc3Smckusick 	count = uio->uio_resid;
2142a601dc3Smckusick 	error = VOP_WRITE(vp, uio, ioflag, cred);
2152a601dc3Smckusick 	if (ioflag & IO_APPEND)
2162a601dc3Smckusick 		fp->f_offset = uio->uio_offset;
2172a601dc3Smckusick 	else
2182a601dc3Smckusick 		fp->f_offset += count - uio->uio_resid;
2192a601dc3Smckusick 	VOP_UNLOCK(vp);
2202a601dc3Smckusick 	return (error);
221760e73c7Smckusick }
222760e73c7Smckusick 
223760e73c7Smckusick /*
224760e73c7Smckusick  * Get stat info for a vnode.
225760e73c7Smckusick  */
2262d8de7e9Smckusick vn_stat(vp, sb, p)
227760e73c7Smckusick 	struct vnode *vp;
228760e73c7Smckusick 	register struct stat *sb;
2292d8de7e9Smckusick 	struct proc *p;
230760e73c7Smckusick {
231760e73c7Smckusick 	struct vattr vattr;
232760e73c7Smckusick 	register struct vattr *vap;
233760e73c7Smckusick 	int error;
234760e73c7Smckusick 	u_short mode;
235760e73c7Smckusick 
236760e73c7Smckusick 	vap = &vattr;
2372d8de7e9Smckusick 	error = VOP_GETATTR(vp, vap, p->p_ucred, p);
238760e73c7Smckusick 	if (error)
239760e73c7Smckusick 		return (error);
240760e73c7Smckusick 	/*
241760e73c7Smckusick 	 * Copy from vattr table
242760e73c7Smckusick 	 */
243760e73c7Smckusick 	sb->st_dev = vap->va_fsid;
244760e73c7Smckusick 	sb->st_ino = vap->va_fileid;
245760e73c7Smckusick 	mode = vap->va_mode;
246760e73c7Smckusick 	switch (vp->v_type) {
247760e73c7Smckusick 	case VREG:
2486357f7baSmckusick 		mode |= S_IFREG;
249760e73c7Smckusick 		break;
250760e73c7Smckusick 	case VDIR:
2516357f7baSmckusick 		mode |= S_IFDIR;
252760e73c7Smckusick 		break;
253760e73c7Smckusick 	case VBLK:
2546357f7baSmckusick 		mode |= S_IFBLK;
255760e73c7Smckusick 		break;
256760e73c7Smckusick 	case VCHR:
2576357f7baSmckusick 		mode |= S_IFCHR;
258760e73c7Smckusick 		break;
259760e73c7Smckusick 	case VLNK:
2606357f7baSmckusick 		mode |= S_IFLNK;
261760e73c7Smckusick 		break;
262760e73c7Smckusick 	case VSOCK:
2636357f7baSmckusick 		mode |= S_IFSOCK;
264760e73c7Smckusick 		break;
265920ab10fSmckusick 	case VFIFO:
266920ab10fSmckusick 		mode |= S_IFIFO;
267920ab10fSmckusick 		break;
268760e73c7Smckusick 	default:
269760e73c7Smckusick 		return (EBADF);
270760e73c7Smckusick 	};
271760e73c7Smckusick 	sb->st_mode = mode;
272760e73c7Smckusick 	sb->st_nlink = vap->va_nlink;
273760e73c7Smckusick 	sb->st_uid = vap->va_uid;
274760e73c7Smckusick 	sb->st_gid = vap->va_gid;
275760e73c7Smckusick 	sb->st_rdev = vap->va_rdev;
276760e73c7Smckusick 	sb->st_size = vap->va_size;
277760e73c7Smckusick 	sb->st_atime = vap->va_atime.tv_sec;
278760e73c7Smckusick 	sb->st_spare1 = 0;
279760e73c7Smckusick 	sb->st_mtime = vap->va_mtime.tv_sec;
280760e73c7Smckusick 	sb->st_spare2 = 0;
281760e73c7Smckusick 	sb->st_ctime = vap->va_ctime.tv_sec;
282760e73c7Smckusick 	sb->st_spare3 = 0;
283760e73c7Smckusick 	sb->st_blksize = vap->va_blocksize;
28418b27549Smckusick 	sb->st_flags = vap->va_flags;
28518b27549Smckusick 	sb->st_gen = vap->va_gen;
286aa6f7503Smckusick 	sb->st_blocks = vap->va_bytes / S_BLKSIZE;
2871d37f34bSbill 	return (0);
2881d37f34bSbill }
289760e73c7Smckusick 
290760e73c7Smckusick /*
291760e73c7Smckusick  * Vnode ioctl call
292760e73c7Smckusick  */
2932d8de7e9Smckusick vn_ioctl(fp, com, data, p)
294760e73c7Smckusick 	struct file *fp;
295760e73c7Smckusick 	int com;
296760e73c7Smckusick 	caddr_t data;
2972d8de7e9Smckusick 	struct proc *p;
298760e73c7Smckusick {
299760e73c7Smckusick 	register struct vnode *vp = ((struct vnode *)fp->f_data);
300760e73c7Smckusick 	struct vattr vattr;
301760e73c7Smckusick 	int error;
302760e73c7Smckusick 
303760e73c7Smckusick 	switch (vp->v_type) {
304760e73c7Smckusick 
305760e73c7Smckusick 	case VREG:
306760e73c7Smckusick 	case VDIR:
307760e73c7Smckusick 		if (com == FIONREAD) {
3082d8de7e9Smckusick 			if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p))
309760e73c7Smckusick 				return (error);
310760e73c7Smckusick 			*(off_t *)data = vattr.va_size - fp->f_offset;
311760e73c7Smckusick 			return (0);
312760e73c7Smckusick 		}
313760e73c7Smckusick 		if (com == FIONBIO || com == FIOASYNC)	/* XXX */
314760e73c7Smckusick 			return (0);			/* XXX */
315760e73c7Smckusick 		/* fall into ... */
316760e73c7Smckusick 
317760e73c7Smckusick 	default:
318760e73c7Smckusick 		return (ENOTTY);
319760e73c7Smckusick 
320920ab10fSmckusick 	case VFIFO:
321760e73c7Smckusick 	case VCHR:
322760e73c7Smckusick 	case VBLK:
3232d8de7e9Smckusick 		error = VOP_IOCTL(vp, com, data, fp->f_flag, p->p_ucred, p);
324e79e4887Smarc 		if (error == 0 && com == TIOCSCTTY) {
3250b0833e3Skarels 			p->p_session->s_ttyvp = vp;
326e79e4887Smarc 			VREF(vp);
327e79e4887Smarc 		}
328e79e4887Smarc 		return (error);
329760e73c7Smckusick 	}
330760e73c7Smckusick }
331760e73c7Smckusick 
332760e73c7Smckusick /*
333760e73c7Smckusick  * Vnode select call
334760e73c7Smckusick  */
3352d8de7e9Smckusick vn_select(fp, which, p)
336760e73c7Smckusick 	struct file *fp;
337760e73c7Smckusick 	int which;
3382d8de7e9Smckusick 	struct proc *p;
339760e73c7Smckusick {
3400b0833e3Skarels 
341920ab10fSmckusick 	return (VOP_SELECT(((struct vnode *)fp->f_data), which, fp->f_flag,
3422d8de7e9Smckusick 		p->p_ucred, p));
343760e73c7Smckusick }
344760e73c7Smckusick 
345760e73c7Smckusick /*
346760e73c7Smckusick  * Vnode close call
347760e73c7Smckusick  */
3482d8de7e9Smckusick vn_close(fp, p)
349760e73c7Smckusick 	register struct file *fp;
3502d8de7e9Smckusick 	struct proc *p;
351760e73c7Smckusick {
352760e73c7Smckusick 	struct vnode *vp = ((struct vnode *)fp->f_data);
353760e73c7Smckusick 	int error;
354760e73c7Smckusick 
355760e73c7Smckusick 	/*
356760e73c7Smckusick 	 * Must delete vnode reference from this file entry
357760e73c7Smckusick 	 * before VOP_CLOSE, so that only other references
358760e73c7Smckusick 	 * will prevent close.
359760e73c7Smckusick 	 */
360760e73c7Smckusick 	fp->f_data = (caddr_t) 0;
3612d8de7e9Smckusick 	error = VOP_CLOSE(vp, fp->f_flag, fp->f_cred, p);
362760e73c7Smckusick 	vrele(vp);
363760e73c7Smckusick 	return (error);
364760e73c7Smckusick }
365760e73c7Smckusick 
366760e73c7Smckusick /*
367760e73c7Smckusick  * vn_fhtovp() - convert a fh to a vnode ptr (optionally locked)
368760e73c7Smckusick  * 	- look up fsid in mount list (if not found ret error)
369760e73c7Smckusick  *	- get vp by calling VFS_FHTOVP() macro
370760e73c7Smckusick  *	- if lockflag lock it with VOP_LOCK()
371760e73c7Smckusick  */
372760e73c7Smckusick vn_fhtovp(fhp, lockflag, vpp)
373760e73c7Smckusick 	fhandle_t *fhp;
374760e73c7Smckusick 	int lockflag;
375760e73c7Smckusick 	struct vnode **vpp;
376760e73c7Smckusick {
377760e73c7Smckusick 	register struct mount *mp;
378760e73c7Smckusick 
379760e73c7Smckusick 	if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
380760e73c7Smckusick 		return (ESTALE);
381229daf47Smckusick 	if (VFS_FHTOVP(mp, &fhp->fh_fid, vpp))
382229daf47Smckusick 		return (ESTALE);
383229daf47Smckusick 	if (!lockflag)
384229daf47Smckusick 		VOP_UNLOCK(*vpp);
385760e73c7Smckusick 	return (0);
386760e73c7Smckusick }
387