xref: /freebsd/sys/compat/linuxkpi/common/src/linux_seq_file.c (revision bb75b0d581f74e22a68d7868ad1f5da1146a8de0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2016-2018, Matthew Macy <mmacy@freebsd.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/types.h>
30 #include <sys/systm.h>
31 #include <sys/param.h>
32 #include <sys/sbuf.h>
33 #include <sys/syslog.h>
34 #include <sys/vnode.h>
35 
36 #include <linux/seq_file.h>
37 #include <linux/file.h>
38 
39 #undef file
40 MALLOC_DEFINE(M_LSEQ, "seq_file", "seq_file");
41 
42 ssize_t
43 seq_read(struct linux_file *f, char __user *ubuf, size_t size, off_t *ppos)
44 {
45 	struct seq_file *m;
46 	struct sbuf *sbuf;
47 	void *p;
48 	ssize_t rc, oldlen;
49 	size_t todo;
50 
51 	m = f->private_data;
52 	sbuf = m->buf;
53 	sbuf_clear(sbuf);
54 
55 	p = m->op->start(m, ppos);
56 	if (p == NULL)
57 		return (0);
58 
59 	rc = 0;
60 	while (size > sbuf_len(sbuf)) {
61 		oldlen = sbuf_len(sbuf);
62 		rc = m->op->show(m, p);
63 		if (rc < 0)
64 			break;
65 
66 		if (rc == SEQ_SKIP) {
67 			/* Discard any data written in show callback. */
68 			sbuf_setpos(sbuf, oldlen);
69 			rc = 0;
70 		}
71 
72 		/*
73 		 * If the sbuf has overflowed, bail.  Discard any
74 		 * partial output from the show callback for this item
75 		 * preserving output from any earlier items.  Since we
76 		 * break before calling the next callback to update
77 		 * *ppos, a subsequent read() will start by displaying
78 		 * the current item.  However, if the current item
79 		 * could not be displayed by itself, still fail with
80 		 * ENOMEM rather than returning EOF.
81 		 */
82 		if (sbuf_error(sbuf)) {
83 			if (oldlen != 0)
84 				sbuf_setpos(sbuf, oldlen);
85 			break;
86 		}
87 
88 		/*
89 		 * XXX: The seq_file documentation claims that Linux
90 		 * warns if this callback doesn't update the value in
91 		 * *ppos.  We don't bother warning here.
92 		 */
93 		p = m->op->next(m, p, ppos);
94 		if (p == NULL)
95 			break;
96 	}
97 	m->op->stop(m, p);
98 
99 	if (rc < 0)
100 		return (rc);
101 
102 	rc = sbuf_finish(sbuf);
103 	if (rc != 0)
104 		return (-rc);
105 
106 	todo = sbuf_len(sbuf);
107 	if (todo > size)
108 		todo = size;
109 
110 	rc = copy_to_user(ubuf, sbuf_data(sbuf), todo);
111 	if (rc != 0)
112 		return (-EFAULT);
113 
114 	return (todo);
115 }
116 
117 int
118 seq_write(struct seq_file *seq, const void *data, size_t len)
119 {
120 	int ret;
121 
122 	ret = sbuf_bcpy(seq->buf, data, len);
123 	if (ret == 0)
124 		seq->size = sbuf_len(seq->buf);
125 
126 	return (ret);
127 }
128 
129 void
130 seq_putc(struct seq_file *seq, char c)
131 {
132 	int ret;
133 
134 	ret = sbuf_putc(seq->buf, c);
135 	if (ret == 0)
136 		seq->size = sbuf_len(seq->buf);
137 }
138 
139 void
140 seq_puts(struct seq_file *seq, const char *str)
141 {
142 	int ret;
143 
144 	ret = sbuf_printf(seq->buf, "%s", str);
145 	if (ret == 0)
146 		seq->size = sbuf_len(seq->buf);
147 }
148 
149 /*
150  * This only needs to be a valid address for lkpi
151  * drivers it should never actually be called
152  */
153 off_t
154 seq_lseek(struct linux_file *file, off_t offset, int whence)
155 {
156 
157 	panic("%s not supported\n", __FUNCTION__);
158 	return (0);
159 }
160 
161 static void *
162 single_start(struct seq_file *p, off_t *pos)
163 {
164 
165 	return ((void *)(uintptr_t)(*pos == 0));
166 }
167 
168 static void *
169 single_next(struct seq_file *p, void *v, off_t *pos)
170 {
171 
172 	++*pos;
173 	return (NULL);
174 }
175 
176 static void
177 single_stop(struct seq_file *p, void *v)
178 {
179 }
180 
181 static int
182 _seq_open_without_sbuf(struct linux_file *f, const struct seq_operations *op)
183 {
184 	struct seq_file *p;
185 
186 	if ((p = malloc(sizeof(*p), M_LSEQ, M_NOWAIT|M_ZERO)) == NULL)
187 		return (-ENOMEM);
188 
189 	p->file = f;
190 	p->op = op;
191 	f->private_data = (void *) p;
192 	return (0);
193 }
194 
195 int
196 seq_open(struct linux_file *f, const struct seq_operations *op)
197 {
198 	int ret;
199 
200 	ret = _seq_open_without_sbuf(f, op);
201 	if (ret == 0)
202 		((struct seq_file *)f->private_data)->buf = sbuf_new_auto();
203 
204 	return (ret);
205 }
206 
207 void *
208 __seq_open_private(struct linux_file *f, const struct seq_operations *op, int size)
209 {
210 	struct seq_file *seq_file;
211 	void *private;
212 	int error;
213 
214 	private = malloc(size, M_LSEQ, M_NOWAIT|M_ZERO);
215 	if (private == NULL)
216 		return (NULL);
217 
218 	error = seq_open(f, op);
219 	if (error < 0) {
220 		free(private, M_LSEQ);
221 		return (NULL);
222 	}
223 
224 	seq_file = (struct seq_file *)f->private_data;
225 	seq_file->private = private;
226 
227 	return (private);
228 }
229 
230 static int
231 _single_open_without_sbuf(struct linux_file *f, int (*show)(struct seq_file *, void *), void *d)
232 {
233 	struct seq_operations *op;
234 	int rc = -ENOMEM;
235 
236 	op = malloc(sizeof(*op), M_LSEQ, M_NOWAIT);
237 	if (op) {
238 		op->start = single_start;
239 		op->next = single_next;
240 		op->stop = single_stop;
241 		op->show = show;
242 		rc = _seq_open_without_sbuf(f, op);
243 		if (rc)
244 			free(op, M_LSEQ);
245 		else
246 			((struct seq_file *)f->private_data)->private = d;
247 	}
248 	return (rc);
249 }
250 
251 int
252 single_open(struct linux_file *f, int (*show)(struct seq_file *, void *), void *d)
253 {
254 	int ret;
255 
256 	ret = _single_open_without_sbuf(f, show, d);
257 	if (ret == 0)
258 		((struct seq_file *)f->private_data)->buf = sbuf_new_auto();
259 
260 	return (ret);
261 }
262 
263 int
264 single_open_size(struct linux_file *f, int (*show)(struct seq_file *, void *), void *d, size_t size)
265 {
266 	int ret;
267 
268 	ret = _single_open_without_sbuf(f, show, d);
269 	if (ret == 0)
270 		((struct seq_file *)f->private_data)->buf = sbuf_new(
271 		    NULL, NULL, size, SBUF_AUTOEXTEND);
272 
273 	return (ret);
274 }
275 
276 int
277 seq_release(struct inode *inode __unused, struct linux_file *file)
278 {
279 	struct seq_file *m;
280 	struct sbuf *s;
281 
282 	m = file->private_data;
283 	s = m->buf;
284 
285 	sbuf_delete(s);
286 	free(m, M_LSEQ);
287 
288 	return (0);
289 }
290 
291 int
292 seq_release_private(struct inode *inode __unused, struct linux_file *f)
293 {
294 	struct seq_file *seq;
295 
296 	seq = (struct seq_file *)f->private_data;
297 	free(seq->private, M_LSEQ);
298 	return (seq_release(inode, f));
299 }
300 
301 int
302 single_release(struct vnode *v, struct linux_file *f)
303 {
304 	const struct seq_operations *op;
305 	struct seq_file *m;
306 	int rc;
307 
308 	/* be NULL safe */
309 	if ((m = f->private_data) == NULL)
310 		return (0);
311 
312 	op = m->op;
313 	rc = seq_release(v, f);
314 	free(__DECONST(void *, op), M_LSEQ);
315 	return (rc);
316 }
317 
318 void
319 lkpi_seq_vprintf(struct seq_file *m, const char *fmt, va_list args)
320 {
321 	int ret;
322 
323 	ret = sbuf_vprintf(m->buf, fmt, args);
324 	if (ret == 0)
325 		m->size = sbuf_len(m->buf);
326 }
327 
328 void
329 lkpi_seq_printf(struct seq_file *m, const char *fmt, ...)
330 {
331 	va_list args;
332 
333 	va_start(args, fmt);
334 	lkpi_seq_vprintf(m, fmt, args);
335 	va_end(args);
336 }
337 
338 bool
339 seq_has_overflowed(struct seq_file *m)
340 {
341 	return (sbuf_len(m->buf) == -1);
342 }
343