xref: /freebsd/sys/kern/subr_sbuf.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*-
2  * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Co�dan Sm�rgrav
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
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  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  *      $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/sbuf.h>
33 
34 #ifdef _KERNEL
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/systm.h>
38 #include <machine/stdarg.h>
39 #else /* _KERNEL */
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #endif /* _KERNEL */
43 
44 #ifdef _KERNEL
45 MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
46 #define SBMALLOC(size)		malloc(size, M_SBUF, M_WAITOK)
47 #define SBFREE(buf)		free(buf, M_SBUF)
48 #else /* _KERNEL */
49 #define KASSERT(e, m)
50 #define SBMALLOC(size)		malloc(size)
51 #define SBFREE(buf)		free(buf)
52 #define min(x,y)		MIN(x,y)
53 #endif /* _KERNEL */
54 
55 /*
56  * Predicates
57  */
58 #define SBUF_ISDYNAMIC(s)	((s)->s_flags & SBUF_DYNAMIC)
59 #define SBUF_ISDYNSTRUCT(s)	((s)->s_flags & SBUF_DYNSTRUCT)
60 #define SBUF_ISFINISHED(s)	((s)->s_flags & SBUF_FINISHED)
61 #define SBUF_HASOVERFLOWED(s)	((s)->s_flags & SBUF_OVERFLOWED)
62 #define SBUF_HASROOM(s)		((s)->s_len < (s)->s_size - 1)
63 
64 /*
65  * Set / clear flags
66  */
67 #define SBUF_SETFLAG(s, f)	do { (s)->s_flags |= (f); } while (0)
68 #define SBUF_CLEARFLAG(s, f)	do { (s)->s_flags &= ~(f); } while (0)
69 
70 /*
71  * Debugging support
72  */
73 #if defined(_KERNEL) && defined(INVARIANTS)
74 static void
75 _assert_sbuf_integrity(char *fun, struct sbuf *s)
76 {
77 	KASSERT(s != NULL,
78 	    ("%s called with a NULL sbuf pointer", fun));
79 	KASSERT(s->s_buf != NULL,
80 	    ("%s called with unitialized or corrupt sbuf", fun));
81 	KASSERT(s->s_len < s->s_size,
82 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
83 }
84 
85 static void
86 _assert_sbuf_state(char *fun, struct sbuf *s, int state)
87 {
88 	KASSERT((s->s_flags & SBUF_FINISHED) == state,
89 	    ("%s called with %sfinished or corrupt sbuf", fun,
90 	    (state ? "un" : "")));
91 }
92 #define assert_sbuf_integrity(s) _assert_sbuf_integrity(__FUNCTION__, (s))
93 #define assert_sbuf_state(s, i)	 _assert_sbuf_state(__FUNCTION__, (s), (i))
94 #else /* _KERNEL && INVARIANTS */
95 #define assert_sbuf_integrity(s) do { } while (0)
96 #define assert_sbuf_state(s, i)	 do { } while (0)
97 #endif /* _KERNEL && INVARIANTS */
98 
99 /*
100  * Initialize an sbuf.
101  * If buf is non-NULL, it points to a static or already-allocated string
102  * big enough to hold at least length characters.
103  */
104 struct sbuf *
105 sbuf_new(struct sbuf *s, char *buf, int length, int flags)
106 {
107 	KASSERT(length >= 0,
108 	    ("attempt to create an sbuf of negative length (%d)", length));
109 	KASSERT(flags == 0,
110 	    (__FUNCTION__ " called with non-zero flags"));
111 
112 	if (s == NULL) {
113 		s = (struct sbuf *)SBMALLOC(sizeof *s);
114 		if (s == NULL)
115 			return (NULL);
116 		bzero(s, sizeof *s);
117 		SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
118 	} else {
119 		bzero(s, sizeof *s);
120 	}
121 	s->s_size = length;
122 	if (buf) {
123 		s->s_buf = buf;
124 		return (s);
125 	}
126 	s->s_buf = (char *)SBMALLOC(s->s_size);
127 	if (s->s_buf == NULL) {
128 		if (SBUF_ISDYNSTRUCT(s))
129 			SBFREE(s);
130 		return (NULL);
131 	}
132 	SBUF_SETFLAG(s, SBUF_DYNAMIC);
133 	return (s);
134 }
135 
136 /*
137  * Clear an sbuf and reset its position
138  */
139 void
140 sbuf_clear(struct sbuf *s)
141 {
142 	assert_sbuf_integrity(s);
143 	/* don't care if it's finished or not */
144 
145 	SBUF_CLEARFLAG(s, SBUF_FINISHED);
146 	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
147 	s->s_len = 0;
148 }
149 
150 /*
151  * Set the sbuf's position to an arbitrary value
152  */
153 int
154 sbuf_setpos(struct sbuf *s, int pos)
155 {
156 	assert_sbuf_integrity(s);
157 	assert_sbuf_state(s, 0);
158 
159 	KASSERT(pos >= 0,
160 	    ("attempt to seek to a negative position (%d)", pos));
161 	KASSERT(pos < s->s_size,
162 	    ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
163 
164 	if (pos < 0 || pos > s->s_len)
165 		return (-1);
166 	s->s_len = pos;
167 	return (0);
168 }
169 
170 /*
171  * Append a byte string to an sbuf.
172  */
173 int
174 sbuf_bcat(struct sbuf *s, const char *str, size_t len)
175 {
176 	assert_sbuf_integrity(s);
177 	assert_sbuf_state(s, 0);
178 
179 	if (SBUF_HASOVERFLOWED(s))
180 		return (-1);
181 
182 	while (len-- && SBUF_HASROOM(s))
183 		s->s_buf[s->s_len++] = *str++;
184 	if (len) {
185 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
186 		return (-1);
187 	}
188 	return (0);
189 }
190 
191 #ifdef _KERNEL
192 /*
193  * Copy a byte string from userland into an sbuf.
194  */
195 int
196 sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
197 {
198 	assert_sbuf_integrity(s);
199 	assert_sbuf_state(s, 0);
200 
201 	if (SBUF_HASOVERFLOWED(s))
202 		return (-1);
203 
204 	if (len == 0)
205 		return (0);
206 	if (len > (s->s_size - s->s_len - 1))
207 		len = s->s_size - s->s_len - 1;
208 	if (copyin(uaddr, s->s_buf + s->s_len, len) != 0)
209 		return (-1);
210 	s->s_len += len;
211 
212 	return (0);
213 }
214 #endif
215 
216 /*
217  * Copy a byte string into an sbuf.
218  */
219 int
220 sbuf_bcpy(struct sbuf *s, const char *str, size_t len)
221 {
222 	assert_sbuf_integrity(s);
223 	assert_sbuf_state(s, 0);
224 
225 	sbuf_clear(s);
226 	return (sbuf_bcat(s, str, len));
227 }
228 
229 /*
230  * Append a string to an sbuf.
231  */
232 int
233 sbuf_cat(struct sbuf *s, const char *str)
234 {
235 	assert_sbuf_integrity(s);
236 	assert_sbuf_state(s, 0);
237 
238 	if (SBUF_HASOVERFLOWED(s))
239 		return (-1);
240 
241 	while (*str && SBUF_HASROOM(s))
242 		s->s_buf[s->s_len++] = *str++;
243 	if (*str) {
244 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
245 		return (-1);
246 	}
247 	return (0);
248 }
249 
250 #ifdef _KERNEL
251 /*
252  * Copy a string from userland into an sbuf.
253  */
254 int
255 sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
256 {
257 	size_t done;
258 
259 	assert_sbuf_integrity(s);
260 	assert_sbuf_state(s, 0);
261 
262 	if (SBUF_HASOVERFLOWED(s))
263 		return (-1);
264 
265 	if (len == 0 || len > (s->s_size - s->s_len - 1))
266 		len = s->s_size - s->s_len - 1;
267 	switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
268 	case ENAMETOOLONG:
269 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
270 		/* fall through */
271 	case 0:
272 		s->s_len += done - 1;
273 		break;
274 	default:
275 		return (-1);	/* XXX */
276 	}
277 
278 	return (0);
279 }
280 #endif
281 
282 /*
283  * Copy a string into an sbuf.
284  */
285 int
286 sbuf_cpy(struct sbuf *s, const char *str)
287 {
288 	assert_sbuf_integrity(s);
289 	assert_sbuf_state(s, 0);
290 
291 	sbuf_clear(s);
292 	return (sbuf_cat(s, str));
293 }
294 
295 /*
296  * Format the given arguments and append the resulting string to an sbuf.
297  */
298 int
299 sbuf_printf(struct sbuf *s, const char *fmt, ...)
300 {
301 	va_list ap;
302 	int len;
303 
304 	assert_sbuf_integrity(s);
305 	assert_sbuf_state(s, 0);
306 
307 	KASSERT(fmt != NULL,
308 	    (__FUNCTION__ " called with a NULL format string"));
309 
310 	if (SBUF_HASOVERFLOWED(s))
311 		return (-1);
312 
313 	va_start(ap, fmt);
314 	len = vsnprintf(&s->s_buf[s->s_len], s->s_size - s->s_len, fmt, ap);
315 	va_end(ap);
316 
317 	/*
318 	 * s->s_len is the length of the string, without the terminating nul.
319 	 * When updating s->s_len, we must subtract 1 from the length that
320 	 * we passed into vsnprintf() because that length includes the
321 	 * terminating nul.
322 	 *
323 	 * vsnprintf() returns the amount that would have been copied,
324 	 * given sufficient space, hence the min() calculation below.
325 	 */
326 	s->s_len += min(len, s->s_size - s->s_len - 1);
327 	if (!SBUF_HASROOM(s))
328 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
329 
330 	KASSERT(s->s_len < s->s_size,
331 	    ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
332 
333 	if (SBUF_HASOVERFLOWED(s))
334 		return (-1);
335 	return (0);
336 }
337 
338 /*
339  * Append a character to an sbuf.
340  */
341 int
342 sbuf_putc(struct sbuf *s, int c)
343 {
344 	assert_sbuf_integrity(s);
345 	assert_sbuf_state(s, 0);
346 
347 	if (SBUF_HASOVERFLOWED(s))
348 		return (-1);
349 
350 	if (!SBUF_HASROOM(s)) {
351 		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
352 		return (-1);
353 	}
354 	if (c != '\0')
355 	    s->s_buf[s->s_len++] = c;
356 	return (0);
357 }
358 
359 /*
360  * Check if an sbuf overflowed
361  */
362 int
363 sbuf_overflowed(struct sbuf *s)
364 {
365     return SBUF_HASOVERFLOWED(s);
366 }
367 
368 /*
369  * Finish off an sbuf.
370  */
371 void
372 sbuf_finish(struct sbuf *s)
373 {
374 	assert_sbuf_integrity(s);
375 	assert_sbuf_state(s, 0);
376 
377 	s->s_buf[s->s_len] = '\0';
378 	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
379 	SBUF_SETFLAG(s, SBUF_FINISHED);
380 }
381 
382 /*
383  * Return a pointer to the sbuf data.
384  */
385 char *
386 sbuf_data(struct sbuf *s)
387 {
388 	assert_sbuf_integrity(s);
389 	assert_sbuf_state(s, SBUF_FINISHED);
390 
391 	return s->s_buf;
392 }
393 
394 /*
395  * Return the length of the sbuf data.
396  */
397 int
398 sbuf_len(struct sbuf *s)
399 {
400 	assert_sbuf_integrity(s);
401 	/* don't care if it's finished or not */
402 
403 	if (SBUF_HASOVERFLOWED(s))
404 		return (-1);
405 	return s->s_len;
406 }
407 
408 /*
409  * Clear an sbuf, free its buffer if necessary.
410  */
411 void
412 sbuf_delete(struct sbuf *s)
413 {
414 	assert_sbuf_integrity(s);
415 	/* don't care if it's finished or not */
416 
417 	if (SBUF_ISDYNAMIC(s))
418 		SBFREE(s->s_buf);
419 	bzero(s, sizeof *s);
420 	if (SBUF_ISDYNSTRUCT(s))
421 		SBFREE(s);
422 }
423