chiark / gitweb /
5fda05fe41fffe1b6dae613d7213d354b9767a75
[dpkg] / lib / dpkg / buffer.c
1 /*
2  * libdpkg - Debian packaging suite library routines
3  * buffer.c - buffer I/O handling routines
4  *
5  * Copyright © 1999, 2000 Wichert Akkerman <wakkerma@debian.org>
6  * Copyright © 2000-2003 Adam Heath <doogie@debian.org>
7  * Copyright © 2008-2012 Guillem Jover <guillem@debian.org>
8  *
9  * This is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
21  */
22
23 #include <config.h>
24 #include <compat.h>
25
26 #include <sys/types.h>
27
28 #include <errno.h>
29 #include <md5.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33
34 #include <dpkg/i18n.h>
35 #include <dpkg/dpkg.h>
36 #include <dpkg/varbuf.h>
37 #include <dpkg/fdio.h>
38 #include <dpkg/buffer.h>
39
40 struct buffer_md5_ctx {
41         struct MD5Context ctx;
42         char *hash;
43 };
44
45 static void
46 buffer_md5_init(struct buffer_data *data)
47 {
48         struct buffer_md5_ctx *ctx;
49
50         ctx = m_malloc(sizeof(*ctx));
51         ctx->hash = data->arg.ptr;
52         data->arg.ptr = ctx;
53         MD5Init(&ctx->ctx);
54 }
55
56 static off_t
57 buffer_digest_init(struct buffer_data *data)
58 {
59         switch (data->type) {
60         case BUFFER_DIGEST_NULL:
61                 break;
62         case BUFFER_DIGEST_MD5:
63                 buffer_md5_init(data);
64                 break;
65         }
66         return 0;
67 }
68
69 static off_t
70 buffer_digest_update(struct buffer_data *digest, const void *buf, off_t length)
71 {
72         off_t ret = length;
73
74         switch (digest->type) {
75         case BUFFER_DIGEST_NULL:
76                 break;
77         case BUFFER_DIGEST_MD5:
78                 MD5Update(&(((struct buffer_md5_ctx *)digest->arg.ptr)->ctx),
79                           buf, length);
80                 break;
81         default:
82                 internerr("unknown data type %i", digest->type);
83         }
84
85         return ret;
86 }
87
88 static void
89 buffer_md5_done(struct buffer_data *data)
90 {
91         struct buffer_md5_ctx *ctx;
92         unsigned char digest[16], *p = digest;
93         char *hash;
94         int i;
95
96         ctx = (struct buffer_md5_ctx *)data->arg.ptr;
97         hash = ctx->hash;
98         MD5Final(digest, &ctx->ctx);
99         for (i = 0; i < 16; ++i) {
100                 sprintf(hash, "%02x", *p++);
101                 hash += 2;
102         }
103         *hash = '\0';
104         free(ctx);
105 }
106
107 static off_t
108 buffer_digest_done(struct buffer_data *data)
109 {
110         switch (data->type) {
111         case BUFFER_DIGEST_NULL:
112                 break;
113         case BUFFER_DIGEST_MD5:
114                 buffer_md5_done(data);
115                 break;
116         }
117         return 0;
118 }
119
120 static off_t
121 buffer_write(struct buffer_data *data, const void *buf, off_t length,
122              struct dpkg_error *err)
123 {
124         off_t ret = length;
125
126         switch (data->type) {
127         case BUFFER_WRITE_VBUF:
128                 varbuf_add_buf((struct varbuf *)data->arg.ptr, buf, length);
129                 break;
130         case BUFFER_WRITE_FD:
131                 ret = fd_write(data->arg.i, buf, length);
132                 if (ret < 0)
133                         dpkg_put_errno(err, _("failed to write"));
134                 break;
135         case BUFFER_WRITE_NULL:
136                 break;
137         default:
138                 internerr("unknown data type %i", data->type);
139         }
140
141         return ret;
142 }
143
144 static off_t
145 buffer_read(struct buffer_data *data, void *buf, off_t length,
146             struct dpkg_error *err)
147 {
148         off_t ret;
149
150         switch (data->type) {
151         case BUFFER_READ_FD:
152                 ret = fd_read(data->arg.i, buf, length);
153                 if (ret < 0)
154                         dpkg_put_errno(err, _("failed to read"));
155                 break;
156         default:
157                 internerr("unknown data type %i", data->type);
158         }
159
160         return ret;
161 }
162
163 off_t
164 buffer_digest(const void *input, void *output, int type, off_t limit)
165 {
166         struct buffer_data data = { .arg.ptr = output, .type = type };
167         off_t ret;
168
169         buffer_digest_init(&data);
170         ret = buffer_digest_update(&data, input, limit);
171         buffer_digest_done(&data);
172
173         return ret;
174 }
175
176 static off_t
177 buffer_copy(struct buffer_data *read_data,
178             struct buffer_data *digest,
179             struct buffer_data *write_data,
180             off_t limit, struct dpkg_error *err)
181 {
182         char *buf;
183         int bufsize = 32768;
184         off_t bytesread = 0, byteswritten = 0;
185         off_t totalread = 0, totalwritten = 0;
186
187         if ((limit != -1) && (limit < bufsize))
188                 bufsize = limit;
189         if (bufsize == 0)
190                 buf = NULL;
191         else
192                 buf = m_malloc(bufsize);
193
194         buffer_digest_init(digest);
195
196         while (bufsize > 0) {
197                 bytesread = buffer_read(read_data, buf, bufsize, err);
198                 if (bytesread < 0)
199                         break;
200                 if (bytesread == 0)
201                         break;
202
203                 totalread += bytesread;
204
205                 if (limit != -1) {
206                         limit -= bytesread;
207                         if (limit < bufsize)
208                                 bufsize = limit;
209                 }
210
211                 buffer_digest_update(digest, buf, bytesread);
212
213                 byteswritten = buffer_write(write_data, buf, bytesread, err);
214                 if (byteswritten < 0)
215                         break;
216                 if (byteswritten == 0)
217                         break;
218
219                 totalwritten += byteswritten;
220         }
221
222         buffer_digest_done(digest);
223
224         free(buf);
225
226         if (bytesread < 0 || byteswritten < 0)
227                 return -1;
228         if (limit > 0)
229                 return dpkg_put_error(err, _("unexpected end of file or stream"));
230
231         return totalread;
232 }
233
234 off_t
235 buffer_copy_IntInt(int Iin, int Tin,
236                    void *Pdigest, int Tdigest,
237                    int Iout, int Tout,
238                    off_t limit, struct dpkg_error *err)
239 {
240         struct buffer_data read_data = { .type = Tin, .arg.i = Iin };
241         struct buffer_data digest = { .type = Tdigest, .arg.ptr = Pdigest };
242         struct buffer_data write_data = { .type = Tout, .arg.i = Iout };
243
244         return buffer_copy(&read_data, &digest, &write_data, limit, err);
245 }
246
247 off_t
248 buffer_copy_IntPtr(int Iin, int Tin,
249                    void *Pdigest, int Tdigest,
250                    void *Pout, int Tout,
251                    off_t limit, struct dpkg_error *err)
252 {
253         struct buffer_data read_data = { .type = Tin, .arg.i = Iin };
254         struct buffer_data digest = { .type = Tdigest, .arg.ptr = Pdigest };
255         struct buffer_data write_data = { .type = Tout, .arg.ptr = Pout };
256
257         return buffer_copy(&read_data, &digest, &write_data, limit, err);
258 }
259
260 static off_t
261 buffer_skip(struct buffer_data *input, off_t limit, struct dpkg_error *err)
262 {
263         struct buffer_data output;
264         struct buffer_data digest;
265
266         switch (input->type) {
267         case BUFFER_READ_FD:
268                 if (lseek(input->arg.i, limit, SEEK_CUR) != -1)
269                         return limit;
270                 if (errno != ESPIPE)
271                         return dpkg_put_errno(err, _("failed to seek"));
272                 break;
273         default:
274                 internerr("unknown data type %i", input->type);
275         }
276
277         output.type = BUFFER_WRITE_NULL;
278         output.arg.ptr = NULL;
279         digest.type = BUFFER_DIGEST_NULL;
280         digest.arg.ptr = NULL;
281
282         return buffer_copy(input, &digest, &output, limit, err);
283 }
284
285 off_t
286 buffer_skip_Int(int I, int T, off_t limit, struct dpkg_error *err)
287 {
288         struct buffer_data input = { .type = T, .arg.i = I };
289
290         return buffer_skip(&input, limit, err);
291 }