1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1990, 1993, 1995
5 *	The Regents of the University of California.
6 * Copyright (c) 2005 Robert N. M. Watson
7 * Copyright (c) 2012 Giovanni Trematerra
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	@(#)fifo_vnops.c	8.10 (Berkeley) 5/27/95
35 * $FreeBSD$
36 */
37
38#include <sys/param.h>
39#include <sys/event.h>
40#include <sys/file.h>
41#include <sys/filedesc.h>
42#include <sys/filio.h>
43#include <sys/fcntl.h>
44#include <sys/kernel.h>
45#include <sys/lock.h>
46#include <sys/mutex.h>
47#include <sys/malloc.h>
48#include <sys/selinfo.h>
49#include <sys/pipe.h>
50#include <sys/proc.h>
51#include <sys/signalvar.h>
52#include <sys/sx.h>
53#include <sys/systm.h>
54#include <sys/un.h>
55#include <sys/unistd.h>
56#include <sys/vnode.h>
57
58/*
59 * This structure is associated with the FIFO vnode and stores
60 * the state associated with the FIFO.
61 * Notes about locking:
62 *   - fi_pipe is invariant since init time.
63 *   - fi_readers and fi_writers are protected by the vnode lock.
64 */
65struct fifoinfo {
66	struct pipe *fi_pipe;
67	long	fi_readers;
68	long	fi_writers;
69	u_int	fi_rgen;
70	u_int	fi_wgen;
71};
72
73static vop_print_t	fifo_print;
74static vop_open_t	fifo_open;
75static vop_close_t	fifo_close;
76static vop_advlock_t	fifo_advlock;
77
78struct vop_vector fifo_specops = {
79	.vop_default =		&default_vnodeops,
80
81	.vop_advlock =		fifo_advlock,
82	.vop_close =		fifo_close,
83	.vop_create =		VOP_PANIC,
84	.vop_getattr =		VOP_EBADF,
85	.vop_ioctl =		VOP_PANIC,
86	.vop_kqfilter =		VOP_PANIC,
87	.vop_link =		VOP_PANIC,
88	.vop_mkdir =		VOP_PANIC,
89	.vop_mknod =		VOP_PANIC,
90	.vop_open =		fifo_open,
91	.vop_pathconf =		VOP_PANIC,
92	.vop_print =		fifo_print,
93	.vop_read =		VOP_PANIC,
94	.vop_readdir =		VOP_PANIC,
95	.vop_readlink =		VOP_PANIC,
96	.vop_reallocblks =	VOP_PANIC,
97	.vop_reclaim =		VOP_NULL,
98	.vop_remove =		VOP_PANIC,
99	.vop_rename =		VOP_PANIC,
100	.vop_rmdir =		VOP_PANIC,
101	.vop_setattr =		VOP_EBADF,
102	.vop_symlink =		VOP_PANIC,
103	.vop_write =		VOP_PANIC,
104};
105
106/*
107 * Dispose of fifo resources.
108 */
109static void
110fifo_cleanup(struct vnode *vp)
111{
112	struct fifoinfo *fip;
113
114	ASSERT_VOP_ELOCKED(vp, "fifo_cleanup");
115	fip = vp->v_fifoinfo;
116	if (fip->fi_readers == 0 && fip->fi_writers == 0) {
117		vp->v_fifoinfo = NULL;
118		pipe_dtor(fip->fi_pipe);
119		free(fip, M_VNODE);
120	}
121}
122
123/*
124 * Open called to set up a new instance of a fifo or
125 * to find an active instance of a fifo.
126 */
127/* ARGSUSED */
128static int
129fifo_open(ap)
130	struct vop_open_args /* {
131		struct vnode *a_vp;
132		int  a_mode;
133		struct ucred *a_cred;
134		struct thread *a_td;
135		struct file *a_fp;
136	} */ *ap;
137{
138	struct vnode *vp;
139	struct file *fp;
140	struct thread *td;
141	struct fifoinfo *fip;
142	struct pipe *fpipe;
143	u_int gen;
144	int error, stops_deferred;
145
146	vp = ap->a_vp;
147	fp = ap->a_fp;
148	td = ap->a_td;
149	ASSERT_VOP_ELOCKED(vp, "fifo_open");
150	if (fp == NULL || (ap->a_mode & FEXEC) != 0)
151		return (EINVAL);
152	if ((fip = vp->v_fifoinfo) == NULL) {
153		error = pipe_named_ctor(&fpipe, td);
154		if (error != 0)
155			return (error);
156		fip = malloc(sizeof(*fip), M_VNODE, M_WAITOK);
157		fip->fi_pipe = fpipe;
158		fpipe->pipe_wgen = fip->fi_readers = fip->fi_writers = 0;
159 		KASSERT(vp->v_fifoinfo == NULL, ("fifo_open: v_fifoinfo race"));
160		vp->v_fifoinfo = fip;
161	}
162	fpipe = fip->fi_pipe;
163 	KASSERT(fpipe != NULL, ("fifo_open: pipe is NULL"));
164
165	/*
166	 * Use the pipe mutex here, in addition to the vnode lock,
167	 * in order to allow vnode lock dropping before msleep() calls
168	 * and still avoiding missed wakeups.
169	 */
170	PIPE_LOCK(fpipe);
171	if (ap->a_mode & FREAD) {
172		fip->fi_readers++;
173		fip->fi_rgen++;
174		if (fip->fi_readers == 1) {
175			fpipe->pipe_state &= ~PIPE_EOF;
176			if (fip->fi_writers > 0) {
177				wakeup(&fip->fi_writers);
178				pipeselwakeup(fpipe);
179			}
180		}
181		fp->f_pipegen = fpipe->pipe_wgen - fip->fi_writers;
182	}
183	if (ap->a_mode & FWRITE) {
184		if ((ap->a_mode & O_NONBLOCK) && fip->fi_readers == 0) {
185			PIPE_UNLOCK(fpipe);
186			if (fip->fi_writers == 0)
187				fifo_cleanup(vp);
188			return (ENXIO);
189		}
190		fip->fi_writers++;
191		fip->fi_wgen++;
192		if (fip->fi_writers == 1) {
193			fpipe->pipe_state &= ~PIPE_EOF;
194			if (fip->fi_readers > 0) {
195				wakeup(&fip->fi_readers);
196				pipeselwakeup(fpipe);
197			}
198		}
199	}
200	if ((ap->a_mode & O_NONBLOCK) == 0) {
201		if ((ap->a_mode & FREAD) && fip->fi_writers == 0) {
202			gen = fip->fi_wgen;
203			VOP_UNLOCK(vp, 0);
204			stops_deferred = sigdeferstop(SIGDEFERSTOP_OFF);
205			error = msleep(&fip->fi_readers, PIPE_MTX(fpipe),
206			    PDROP | PCATCH | PSOCK, "fifoor", 0);
207			sigallowstop(stops_deferred);
208			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
209			if (error != 0 && gen == fip->fi_wgen) {
210				fip->fi_readers--;
211				if (fip->fi_readers == 0) {
212					PIPE_LOCK(fpipe);
213					fpipe->pipe_state |= PIPE_EOF;
214					if (fpipe->pipe_state & PIPE_WANTW)
215						wakeup(fpipe);
216					pipeselwakeup(fpipe);
217					PIPE_UNLOCK(fpipe);
218					fifo_cleanup(vp);
219				}
220				return (error);
221			}
222			PIPE_LOCK(fpipe);
223			/*
224			 * We must have got woken up because we had a writer.
225			 * That (and not still having one) is the condition
226			 * that we must wait for.
227			 */
228		}
229		if ((ap->a_mode & FWRITE) && fip->fi_readers == 0) {
230			gen = fip->fi_rgen;
231			VOP_UNLOCK(vp, 0);
232			stops_deferred = sigdeferstop(SIGDEFERSTOP_OFF);
233			error = msleep(&fip->fi_writers, PIPE_MTX(fpipe),
234			    PDROP | PCATCH | PSOCK, "fifoow", 0);
235			sigallowstop(stops_deferred);
236			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
237			if (error != 0 && gen == fip->fi_rgen) {
238				fip->fi_writers--;
239				if (fip->fi_writers == 0) {
240					PIPE_LOCK(fpipe);
241					fpipe->pipe_state |= PIPE_EOF;
242					if (fpipe->pipe_state & PIPE_WANTR)
243						wakeup(fpipe);
244					fpipe->pipe_wgen++;
245					pipeselwakeup(fpipe);
246					PIPE_UNLOCK(fpipe);
247					fifo_cleanup(vp);
248				}
249				return (error);
250			}
251			/*
252			 * We must have got woken up because we had
253			 * a reader.  That (and not still having one)
254			 * is the condition that we must wait for.
255			 */
256			PIPE_LOCK(fpipe);
257		}
258	}
259	PIPE_UNLOCK(fpipe);
260	KASSERT(fp != NULL, ("can't fifo/vnode bypass"));
261	KASSERT(fp->f_ops == &badfileops, ("not badfileops in fifo_open"));
262	finit(fp, fp->f_flag, DTYPE_FIFO, fpipe, &pipeops);
263	return (0);
264}
265
266/*
267 * Device close routine
268 */
269/* ARGSUSED */
270static int
271fifo_close(ap)
272	struct vop_close_args /* {
273		struct vnode *a_vp;
274		int  a_fflag;
275		struct ucred *a_cred;
276		struct thread *a_td;
277	} */ *ap;
278{
279	struct vnode *vp;
280	struct fifoinfo *fip;
281	struct pipe *cpipe;
282
283	vp = ap->a_vp;
284	fip = vp->v_fifoinfo;
285	cpipe = fip->fi_pipe;
286	ASSERT_VOP_ELOCKED(vp, "fifo_close");
287	if (ap->a_fflag & FREAD) {
288		fip->fi_readers--;
289		if (fip->fi_readers == 0) {
290			PIPE_LOCK(cpipe);
291			cpipe->pipe_state |= PIPE_EOF;
292			if ((cpipe->pipe_state & PIPE_WANTW)) {
293				cpipe->pipe_state &= ~PIPE_WANTW;
294				wakeup(cpipe);
295			}
296			pipeselwakeup(cpipe);
297			PIPE_UNLOCK(cpipe);
298		}
299	}
300	if (ap->a_fflag & FWRITE) {
301		fip->fi_writers--;
302		if (fip->fi_writers == 0) {
303			PIPE_LOCK(cpipe);
304			cpipe->pipe_state |= PIPE_EOF;
305			if ((cpipe->pipe_state & PIPE_WANTR)) {
306				cpipe->pipe_state &= ~PIPE_WANTR;
307				wakeup(cpipe);
308			}
309			cpipe->pipe_wgen++;
310			pipeselwakeup(cpipe);
311			PIPE_UNLOCK(cpipe);
312		}
313	}
314	fifo_cleanup(vp);
315	return (0);
316}
317
318/*
319 * Print out internal contents of a fifo vnode.
320 */
321int
322fifo_printinfo(vp)
323	struct vnode *vp;
324{
325	struct fifoinfo *fip = vp->v_fifoinfo;
326
327	if (fip == NULL){
328		printf(", NULL v_fifoinfo");
329		return (0);
330	}
331	printf(", fifo with %ld readers and %ld writers",
332		fip->fi_readers, fip->fi_writers);
333	return (0);
334}
335
336/*
337 * Print out the contents of a fifo vnode.
338 */
339static int
340fifo_print(ap)
341	struct vop_print_args /* {
342		struct vnode *a_vp;
343	} */ *ap;
344{
345	printf("    ");
346	fifo_printinfo(ap->a_vp);
347	printf("\n");
348	return (0);
349}
350
351/*
352 * Fifo advisory byte-level locks.
353 */
354/* ARGSUSED */
355static int
356fifo_advlock(ap)
357	struct vop_advlock_args /* {
358		struct vnode *a_vp;
359		caddr_t  a_id;
360		int  a_op;
361		struct flock *a_fl;
362		int  a_flags;
363	} */ *ap;
364{
365
366	return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL);
367}
368
369