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