4c1a844d1d73205a72b77ba9f1f69422ed01573e
[gnupg.git] / common / membuf.c
1 /* membuf.c - A simple implementation of a dynamic buffer.
2  * Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc.
3  * Copyright (C) 2013 Werner Koch
4  *
5  * This file is part of GnuPG.
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of either
9  *
10  *   - the GNU Lesser General Public License as published by the Free
11  *     Software Foundation; either version 3 of the License, or (at
12  *     your option) any later version.
13  *
14  * or
15  *
16  *   - the GNU General Public License as published by the Free
17  *     Software Foundation; either version 2 of the License, or (at
18  *     your option) any later version.
19  *
20  * or both in parallel, as here.
21  *
22  * This file is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, see <https://www.gnu.org/licenses/>.
29  */
30
31 #include <config.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <stdarg.h>
35
36 #include "util.h"
37 #include "membuf.h"
38
39
40 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
41    create a buffer, put_membuf to append bytes and get_membuf to
42    release and return the buffer.  Allocation errors are detected but
43    only returned at the final get_membuf(), this helps not to clutter
44    the code with out of core checks.  */
45
46 void
47 init_membuf (membuf_t *mb, int initiallen)
48 {
49   mb->len = 0;
50   mb->size = initiallen;
51   mb->out_of_core = 0;
52   mb->buf = xtrymalloc (initiallen);
53   if (!mb->buf)
54     mb->out_of_core = errno;
55 }
56
57 /* Same as init_membuf but allocates the buffer in secure memory.  */
58 void
59 init_membuf_secure (membuf_t *mb, int initiallen)
60 {
61   mb->len = 0;
62   mb->size = initiallen;
63   mb->out_of_core = 0;
64   mb->buf = xtrymalloc_secure (initiallen);
65   if (!mb->buf)
66     mb->out_of_core = errno;
67 }
68
69
70 /* Shift the the content of the membuf MB by AMOUNT bytes.  The next
71    operation will then behave as if AMOUNT bytes had not been put into
72    the buffer.  If AMOUNT is greater than the actual accumulated
73    bytes, the membuf is basically reset to its initial state.  */
74 void
75 clear_membuf (membuf_t *mb, size_t amount)
76 {
77   /* No need to clear if we are already out of core.  */
78   if (mb->out_of_core)
79     return;
80   if (amount >= mb->len)
81     mb->len = 0;
82   else
83     {
84       mb->len -= amount;
85       memmove (mb->buf, mb->buf+amount, mb->len);
86     }
87 }
88
89
90 void
91 put_membuf (membuf_t *mb, const void *buf, size_t len)
92 {
93   if (mb->out_of_core || !len)
94     return;
95
96   if (mb->len + len >= mb->size)
97     {
98       char *p;
99
100       mb->size += len + 1024;
101       p = xtryrealloc (mb->buf, mb->size);
102       if (!p)
103         {
104           mb->out_of_core = errno ? errno : ENOMEM;
105           /* Wipe out what we already accumulated.  This is required
106              in case we are storing sensitive data here.  The membuf
107              API does not provide another way to cleanup after an
108              error. */
109           wipememory (mb->buf, mb->len);
110           return;
111         }
112       mb->buf = p;
113     }
114   memcpy (mb->buf + mb->len, buf, len);
115   mb->len += len;
116 }
117
118
119 /* A variant of put_membuf accepting a void * and returning a
120    gpg_error_t (which will always return 0) to be used as a generic
121    callback handler.  This function also allows buffer to be NULL.  */
122 gpg_error_t
123 put_membuf_cb (void *opaque, const void *buf, size_t len)
124 {
125   membuf_t *data = opaque;
126
127   if (buf)
128     put_membuf (data, buf, len);
129   return 0;
130 }
131
132
133 void
134 put_membuf_str (membuf_t *mb, const char *string)
135 {
136   put_membuf (mb, string, strlen (string));
137 }
138
139
140 void
141 put_membuf_printf (membuf_t *mb, const char *format, ...)
142 {
143   int rc;
144   va_list arg_ptr;
145   char *buf;
146
147   va_start (arg_ptr, format);
148   rc = gpgrt_vasprintf (&buf, format, arg_ptr);
149   if (rc < 0)
150     mb->out_of_core = errno ? errno : ENOMEM;
151   va_end (arg_ptr);
152   if (rc >= 0)
153     {
154       put_membuf (mb, buf, strlen (buf));
155       xfree (buf);
156     }
157 }
158
159
160 void *
161 get_membuf (membuf_t *mb, size_t *len)
162 {
163   char *p;
164
165   if (mb->out_of_core)
166     {
167       if (mb->buf)
168         {
169           wipememory (mb->buf, mb->len);
170           xfree (mb->buf);
171           mb->buf = NULL;
172         }
173       gpg_err_set_errno (mb->out_of_core);
174       return NULL;
175     }
176
177   p = mb->buf;
178   if (len)
179     *len = mb->len;
180   mb->buf = NULL;
181   mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
182   return p;
183 }
184
185
186 /* Same as get_membuf but shrinks the reallocated space to the
187    required size.  */
188 void *
189 get_membuf_shrink (membuf_t *mb, size_t *len)
190 {
191   void *p, *pp;
192   size_t dummylen;
193
194   if (!len)
195     len = &dummylen;
196
197   p = get_membuf (mb, len);
198   if (!p)
199     return NULL;
200   if (*len)
201     {
202       pp = xtryrealloc (p, *len);
203       if (pp)
204         p = pp;
205     }
206
207   return p;
208 }
209
210
211 /* Peek at the membuf MB.  On success a pointer to the buffer is
212    returned which is valid until the next operation on MB.  If LEN is
213    not NULL the current LEN of the buffer is stored there.  On error
214    NULL is returned and ERRNO is set.  */
215 const void *
216 peek_membuf (membuf_t *mb, size_t *len)
217 {
218   const char *p;
219
220   if (mb->out_of_core)
221     {
222       gpg_err_set_errno (mb->out_of_core);
223       return NULL;
224     }
225
226   p = mb->buf;
227   if (len)
228     *len = mb->len;
229   return p;
230 }