xref: /freebsd-src/sys/compat/linuxkpi/common/src/linux_seq_file.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
13f6cab07SMatt Macy /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
33f6cab07SMatt Macy  *
43f6cab07SMatt Macy  * Copyright (c) 2016-2018, Matthew Macy <mmacy@freebsd.org>
53f6cab07SMatt Macy  *
63f6cab07SMatt Macy  * Redistribution and use in source and binary forms, with or without
73f6cab07SMatt Macy  * modification, are permitted provided that the following conditions
83f6cab07SMatt Macy  * are met:
93f6cab07SMatt Macy  * 1. Redistributions of source code must retain the above copyright
103f6cab07SMatt Macy  *    notice, this list of conditions and the following disclaimer.
113f6cab07SMatt Macy  * 2. Redistributions in binary form must reproduce the above copyright
123f6cab07SMatt Macy  *    notice, this list of conditions and the following disclaimer in the
133f6cab07SMatt Macy  *    documentation and/or other materials provided with the distribution.
143f6cab07SMatt Macy  *
153f6cab07SMatt Macy  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
163f6cab07SMatt Macy  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
173f6cab07SMatt Macy  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
183f6cab07SMatt Macy  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
193f6cab07SMatt Macy  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
203f6cab07SMatt Macy  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
213f6cab07SMatt Macy  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
223f6cab07SMatt Macy  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
233f6cab07SMatt Macy  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
243f6cab07SMatt Macy  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
253f6cab07SMatt Macy  * SUCH DAMAGE.
263f6cab07SMatt Macy  *
273f6cab07SMatt Macy  */
283f6cab07SMatt Macy 
293f6cab07SMatt Macy #include <sys/types.h>
303f6cab07SMatt Macy #include <sys/systm.h>
313f6cab07SMatt Macy #include <sys/param.h>
323f6cab07SMatt Macy #include <sys/sbuf.h>
333f6cab07SMatt Macy #include <sys/syslog.h>
343f6cab07SMatt Macy #include <sys/vnode.h>
353f6cab07SMatt Macy 
363f6cab07SMatt Macy #include <linux/seq_file.h>
373f6cab07SMatt Macy #include <linux/file.h>
383f6cab07SMatt Macy 
393f6cab07SMatt Macy #undef file
403f6cab07SMatt Macy MALLOC_DEFINE(M_LSEQ, "seq_file", "seq_file");
413f6cab07SMatt Macy 
423f6cab07SMatt Macy ssize_t
seq_read(struct linux_file * f,char * ubuf,size_t size,off_t * ppos)433f6cab07SMatt Macy seq_read(struct linux_file *f, char *ubuf, size_t size, off_t *ppos)
443f6cab07SMatt Macy {
45f697b943SJake Freeland 	struct seq_file *m;
46f697b943SJake Freeland 	struct sbuf *sbuf;
473f6cab07SMatt Macy 	void *p;
48f697b943SJake Freeland 	ssize_t rc;
493f6cab07SMatt Macy 
50f697b943SJake Freeland 	m = f->private_data;
51f697b943SJake Freeland 	sbuf = m->buf;
52f697b943SJake Freeland 
53f697b943SJake Freeland 	p = m->op->start(m, ppos);
543f6cab07SMatt Macy 	rc = m->op->show(m, p);
553f6cab07SMatt Macy 	if (rc)
563f6cab07SMatt Macy 		return (rc);
57f697b943SJake Freeland 
58f697b943SJake Freeland 	rc = sbuf_finish(sbuf);
59f697b943SJake Freeland 	if (rc)
60f697b943SJake Freeland 		return (rc);
61f697b943SJake Freeland 
62f697b943SJake Freeland 	rc = sbuf_len(sbuf);
63f697b943SJake Freeland 	if (*ppos >= rc || size < 1)
64f697b943SJake Freeland 		return (-EINVAL);
65f697b943SJake Freeland 
66f697b943SJake Freeland 	size = min(rc - *ppos, size);
67ccd8c448SEmmanuel Vadot 	rc = strscpy(ubuf, sbuf_data(sbuf) + *ppos, size + 1);
68f697b943SJake Freeland 
69f697b943SJake Freeland 	/* add 1 for null terminator */
70f697b943SJake Freeland 	if (rc > 0)
71f697b943SJake Freeland 		rc += 1;
72f697b943SJake Freeland 
73f697b943SJake Freeland 	return (rc);
743f6cab07SMatt Macy }
753f6cab07SMatt Macy 
763f6cab07SMatt Macy int
seq_write(struct seq_file * seq,const void * data,size_t len)773f6cab07SMatt Macy seq_write(struct seq_file *seq, const void *data, size_t len)
783f6cab07SMatt Macy {
795fbfe951SJean-Sébastien Pédron 	int ret;
803f6cab07SMatt Macy 
815fbfe951SJean-Sébastien Pédron 	ret = sbuf_bcpy(seq->buf, data, len);
825fbfe951SJean-Sébastien Pédron 	if (ret == 0)
835fbfe951SJean-Sébastien Pédron 		seq->size = sbuf_len(seq->buf);
845fbfe951SJean-Sébastien Pédron 
855fbfe951SJean-Sébastien Pédron 	return (ret);
865fbfe951SJean-Sébastien Pédron }
875fbfe951SJean-Sébastien Pédron 
885fbfe951SJean-Sébastien Pédron void
seq_putc(struct seq_file * seq,char c)895fbfe951SJean-Sébastien Pédron seq_putc(struct seq_file *seq, char c)
905fbfe951SJean-Sébastien Pédron {
915fbfe951SJean-Sébastien Pédron 	int ret;
925fbfe951SJean-Sébastien Pédron 
935fbfe951SJean-Sébastien Pédron 	ret = sbuf_putc(seq->buf, c);
945fbfe951SJean-Sébastien Pédron 	if (ret == 0)
955fbfe951SJean-Sébastien Pédron 		seq->size = sbuf_len(seq->buf);
965fbfe951SJean-Sébastien Pédron }
975fbfe951SJean-Sébastien Pédron 
985fbfe951SJean-Sébastien Pédron void
seq_puts(struct seq_file * seq,const char * str)995fbfe951SJean-Sébastien Pédron seq_puts(struct seq_file *seq, const char *str)
1005fbfe951SJean-Sébastien Pédron {
1015fbfe951SJean-Sébastien Pédron 	int ret;
1025fbfe951SJean-Sébastien Pédron 
1035fbfe951SJean-Sébastien Pédron 	ret = sbuf_printf(seq->buf, "%s", str);
1045fbfe951SJean-Sébastien Pédron 	if (ret == 0)
1055fbfe951SJean-Sébastien Pédron 		seq->size = sbuf_len(seq->buf);
1063f6cab07SMatt Macy }
1073f6cab07SMatt Macy 
1083f6cab07SMatt Macy /*
1093f6cab07SMatt Macy  * This only needs to be a valid address for lkpi
1103f6cab07SMatt Macy  * drivers it should never actually be called
1113f6cab07SMatt Macy  */
1123f6cab07SMatt Macy off_t
seq_lseek(struct linux_file * file,off_t offset,int whence)1133f6cab07SMatt Macy seq_lseek(struct linux_file *file, off_t offset, int whence)
1143f6cab07SMatt Macy {
1153f6cab07SMatt Macy 
1163f6cab07SMatt Macy 	panic("%s not supported\n", __FUNCTION__);
1173f6cab07SMatt Macy 	return (0);
1183f6cab07SMatt Macy }
1193f6cab07SMatt Macy 
1203f6cab07SMatt Macy static void *
single_start(struct seq_file * p,off_t * pos)1213f6cab07SMatt Macy single_start(struct seq_file *p, off_t *pos)
1223f6cab07SMatt Macy {
1233f6cab07SMatt Macy 
1243f6cab07SMatt Macy 	return ((void *)(uintptr_t)(*pos == 0));
1253f6cab07SMatt Macy }
1263f6cab07SMatt Macy 
1273f6cab07SMatt Macy static void *
single_next(struct seq_file * p,void * v,off_t * pos)1283f6cab07SMatt Macy single_next(struct seq_file *p, void *v, off_t *pos)
1293f6cab07SMatt Macy {
1303f6cab07SMatt Macy 
1313f6cab07SMatt Macy 	++*pos;
1323f6cab07SMatt Macy 	return (NULL);
1333f6cab07SMatt Macy }
1343f6cab07SMatt Macy 
1353f6cab07SMatt Macy static void
single_stop(struct seq_file * p,void * v)1363f6cab07SMatt Macy single_stop(struct seq_file *p, void *v)
1373f6cab07SMatt Macy {
1383f6cab07SMatt Macy }
1393f6cab07SMatt Macy 
1405fbfe951SJean-Sébastien Pédron static int
_seq_open_without_sbuf(struct linux_file * f,const struct seq_operations * op)1415fbfe951SJean-Sébastien Pédron _seq_open_without_sbuf(struct linux_file *f, const struct seq_operations *op)
1423f6cab07SMatt Macy {
1433f6cab07SMatt Macy 	struct seq_file *p;
1443f6cab07SMatt Macy 
1453f6cab07SMatt Macy 	if ((p = malloc(sizeof(*p), M_LSEQ, M_NOWAIT|M_ZERO)) == NULL)
1463f6cab07SMatt Macy 		return (-ENOMEM);
1473f6cab07SMatt Macy 
1483f6cab07SMatt Macy 	p->file = f;
149f697b943SJake Freeland 	p->op = op;
150f697b943SJake Freeland 	f->private_data = (void *) p;
1513f6cab07SMatt Macy 	return (0);
1523f6cab07SMatt Macy }
1533f6cab07SMatt Macy 
1545fbfe951SJean-Sébastien Pédron int
seq_open(struct linux_file * f,const struct seq_operations * op)1555fbfe951SJean-Sébastien Pédron seq_open(struct linux_file *f, const struct seq_operations *op)
1565fbfe951SJean-Sébastien Pédron {
1575fbfe951SJean-Sébastien Pédron 	int ret;
1585fbfe951SJean-Sébastien Pédron 
1595fbfe951SJean-Sébastien Pédron 	ret = _seq_open_without_sbuf(f, op);
1605fbfe951SJean-Sébastien Pédron 	if (ret == 0)
1615fbfe951SJean-Sébastien Pédron 		((struct seq_file *)f->private_data)->buf = sbuf_new_auto();
1625fbfe951SJean-Sébastien Pédron 
1635fbfe951SJean-Sébastien Pédron 	return (ret);
1645fbfe951SJean-Sébastien Pédron }
1655fbfe951SJean-Sébastien Pédron 
166b5a81075SBjoern A. Zeeb void *
__seq_open_private(struct linux_file * f,const struct seq_operations * op,int size)167b5a81075SBjoern A. Zeeb __seq_open_private(struct linux_file *f, const struct seq_operations *op, int size)
168b5a81075SBjoern A. Zeeb {
169b5a81075SBjoern A. Zeeb 	struct seq_file *seq_file;
170b5a81075SBjoern A. Zeeb 	void *private;
171b5a81075SBjoern A. Zeeb 	int error;
172b5a81075SBjoern A. Zeeb 
173b5a81075SBjoern A. Zeeb 	private = malloc(size, M_LSEQ, M_NOWAIT|M_ZERO);
174b5a81075SBjoern A. Zeeb 	if (private == NULL)
175b5a81075SBjoern A. Zeeb 		return (NULL);
176b5a81075SBjoern A. Zeeb 
177b5a81075SBjoern A. Zeeb 	error = seq_open(f, op);
178b5a81075SBjoern A. Zeeb 	if (error < 0) {
179b5a81075SBjoern A. Zeeb 		free(private, M_LSEQ);
180b5a81075SBjoern A. Zeeb 		return (NULL);
181b5a81075SBjoern A. Zeeb 	}
182b5a81075SBjoern A. Zeeb 
183b5a81075SBjoern A. Zeeb 	seq_file = (struct seq_file *)f->private_data;
184b5a81075SBjoern A. Zeeb 	seq_file->private = private;
185b5a81075SBjoern A. Zeeb 
186b5a81075SBjoern A. Zeeb 	return (private);
187b5a81075SBjoern A. Zeeb }
188b5a81075SBjoern A. Zeeb 
1895fbfe951SJean-Sébastien Pédron static int
_single_open_without_sbuf(struct linux_file * f,int (* show)(struct seq_file *,void *),void * d)1905fbfe951SJean-Sébastien Pédron _single_open_without_sbuf(struct linux_file *f, int (*show)(struct seq_file *, void *), void *d)
1913f6cab07SMatt Macy {
1923f6cab07SMatt Macy 	struct seq_operations *op;
1933f6cab07SMatt Macy 	int rc = -ENOMEM;
1943f6cab07SMatt Macy 
1953f6cab07SMatt Macy 	op = malloc(sizeof(*op), M_LSEQ, M_NOWAIT);
1963f6cab07SMatt Macy 	if (op) {
1973f6cab07SMatt Macy 		op->start = single_start;
1983f6cab07SMatt Macy 		op->next = single_next;
1993f6cab07SMatt Macy 		op->stop = single_stop;
2003f6cab07SMatt Macy 		op->show = show;
2015fbfe951SJean-Sébastien Pédron 		rc = _seq_open_without_sbuf(f, op);
2023f6cab07SMatt Macy 		if (rc)
2033f6cab07SMatt Macy 			free(op, M_LSEQ);
2043f6cab07SMatt Macy 		else
2053f6cab07SMatt Macy 			((struct seq_file *)f->private_data)->private = d;
2063f6cab07SMatt Macy 	}
2073f6cab07SMatt Macy 	return (rc);
2083f6cab07SMatt Macy }
2093f6cab07SMatt Macy 
2103f6cab07SMatt Macy int
single_open(struct linux_file * f,int (* show)(struct seq_file *,void *),void * d)2115fbfe951SJean-Sébastien Pédron single_open(struct linux_file *f, int (*show)(struct seq_file *, void *), void *d)
2125fbfe951SJean-Sébastien Pédron {
2135fbfe951SJean-Sébastien Pédron 	int ret;
2145fbfe951SJean-Sébastien Pédron 
2155fbfe951SJean-Sébastien Pédron 	ret = _single_open_without_sbuf(f, show, d);
2165fbfe951SJean-Sébastien Pédron 	if (ret == 0)
2175fbfe951SJean-Sébastien Pédron 		((struct seq_file *)f->private_data)->buf = sbuf_new_auto();
2185fbfe951SJean-Sébastien Pédron 
2195fbfe951SJean-Sébastien Pédron 	return (ret);
2205fbfe951SJean-Sébastien Pédron }
2215fbfe951SJean-Sébastien Pédron 
2225fbfe951SJean-Sébastien Pédron int
single_open_size(struct linux_file * f,int (* show)(struct seq_file *,void *),void * d,size_t size)2235fbfe951SJean-Sébastien Pédron single_open_size(struct linux_file *f, int (*show)(struct seq_file *, void *), void *d, size_t size)
2245fbfe951SJean-Sébastien Pédron {
2255fbfe951SJean-Sébastien Pédron 	int ret;
2265fbfe951SJean-Sébastien Pédron 
2275fbfe951SJean-Sébastien Pédron 	ret = _single_open_without_sbuf(f, show, d);
2285fbfe951SJean-Sébastien Pédron 	if (ret == 0)
2295fbfe951SJean-Sébastien Pédron 		((struct seq_file *)f->private_data)->buf = sbuf_new(
2305fbfe951SJean-Sébastien Pédron 		    NULL, NULL, size, SBUF_AUTOEXTEND);
2315fbfe951SJean-Sébastien Pédron 
2325fbfe951SJean-Sébastien Pédron 	return (ret);
2335fbfe951SJean-Sébastien Pédron }
2345fbfe951SJean-Sébastien Pédron 
2355fbfe951SJean-Sébastien Pédron int
seq_release(struct inode * inode __unused,struct linux_file * file)2363f6cab07SMatt Macy seq_release(struct inode *inode __unused, struct linux_file *file)
2373f6cab07SMatt Macy {
2383f6cab07SMatt Macy 	struct seq_file *m;
239f697b943SJake Freeland 	struct sbuf *s;
2403f6cab07SMatt Macy 
2413f6cab07SMatt Macy 	m = file->private_data;
242f697b943SJake Freeland 	s = m->buf;
243f697b943SJake Freeland 
244f697b943SJake Freeland 	sbuf_delete(s);
2453f6cab07SMatt Macy 	free(m, M_LSEQ);
246f697b943SJake Freeland 
2473f6cab07SMatt Macy 	return (0);
2483f6cab07SMatt Macy }
2493f6cab07SMatt Macy 
2503f6cab07SMatt Macy int
seq_release_private(struct inode * inode __unused,struct linux_file * f)251b5a81075SBjoern A. Zeeb seq_release_private(struct inode *inode __unused, struct linux_file *f)
252b5a81075SBjoern A. Zeeb {
253b5a81075SBjoern A. Zeeb 	struct seq_file *seq;
254b5a81075SBjoern A. Zeeb 
255b5a81075SBjoern A. Zeeb 	seq = (struct seq_file *)f->private_data;
256b5a81075SBjoern A. Zeeb 	free(seq->private, M_LSEQ);
257b5a81075SBjoern A. Zeeb 	return (seq_release(inode, f));
258b5a81075SBjoern A. Zeeb }
259b5a81075SBjoern A. Zeeb 
260b5a81075SBjoern A. Zeeb int
single_release(struct vnode * v,struct linux_file * f)2613f6cab07SMatt Macy single_release(struct vnode *v, struct linux_file *f)
2623f6cab07SMatt Macy {
263a23e475cSHans Petter Selasky 	const struct seq_operations *op;
264a23e475cSHans Petter Selasky 	struct seq_file *m;
2653f6cab07SMatt Macy 	int rc;
2663f6cab07SMatt Macy 
267a23e475cSHans Petter Selasky 	/* be NULL safe */
268a23e475cSHans Petter Selasky 	if ((m = f->private_data) == NULL)
269a23e475cSHans Petter Selasky 		return (0);
270a23e475cSHans Petter Selasky 
271a23e475cSHans Petter Selasky 	op = m->op;
2723f6cab07SMatt Macy 	rc = seq_release(v, f);
2733f6cab07SMatt Macy 	free(__DECONST(void *, op), M_LSEQ);
2743f6cab07SMatt Macy 	return (rc);
2753f6cab07SMatt Macy }
276f697b943SJake Freeland 
277f697b943SJake Freeland void
lkpi_seq_vprintf(struct seq_file * m,const char * fmt,va_list args)278cbda8bedSHans Petter Selasky lkpi_seq_vprintf(struct seq_file *m, const char *fmt, va_list args)
279f697b943SJake Freeland {
2805fbfe951SJean-Sébastien Pédron 	int ret;
2815fbfe951SJean-Sébastien Pédron 
2825fbfe951SJean-Sébastien Pédron 	ret = sbuf_vprintf(m->buf, fmt, args);
2835fbfe951SJean-Sébastien Pédron 	if (ret == 0)
2845fbfe951SJean-Sébastien Pédron 		m->size = sbuf_len(m->buf);
285f697b943SJake Freeland }
286f697b943SJake Freeland 
287f697b943SJake Freeland void
lkpi_seq_printf(struct seq_file * m,const char * fmt,...)288cbda8bedSHans Petter Selasky lkpi_seq_printf(struct seq_file *m, const char *fmt, ...)
289f697b943SJake Freeland {
290f697b943SJake Freeland 	va_list args;
291f697b943SJake Freeland 
292f697b943SJake Freeland 	va_start(args, fmt);
2935fbfe951SJean-Sébastien Pédron 	lkpi_seq_vprintf(m, fmt, args);
294f697b943SJake Freeland 	va_end(args);
295f697b943SJake Freeland }
2965fbfe951SJean-Sébastien Pédron 
2975fbfe951SJean-Sébastien Pédron bool
seq_has_overflowed(struct seq_file * m)2985fbfe951SJean-Sébastien Pédron seq_has_overflowed(struct seq_file *m)
2995fbfe951SJean-Sébastien Pédron {
3005fbfe951SJean-Sébastien Pédron 	return (sbuf_len(m->buf) == -1);
3015fbfe951SJean-Sébastien Pédron }
302