b29d75eb9e0988b5a51491a617c6d5e03bddf16a
[oweals/gnunet.git] / src / util / bio.c
1 /*
2      This file is part of GNUnet.
3      (C) 2006, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file util/bio.c
22  * @brief functions for buffering IO
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_bio_lib.h"
27 #include "gnunet_disk_lib.h"
28
29 #define BIO_BUFFER_SIZE 65536
30
31 #define MAX_META_DATA (1024 * 1024)
32
33 /**
34  * Handle for buffered reading.
35  */
36 struct GNUNET_BIO_ReadHandle
37 {
38   struct GNUNET_DISK_FileHandle *fd;
39   char *emsg;
40   char *buffer;
41   size_t have;
42   size_t size;
43   off_t pos;
44 };
45
46
47 /**
48  * Open a file for reading.
49  *
50  * @param fn file name to be opened
51  * @return IO handle on success, NULL on error
52  */
53 struct GNUNET_BIO_ReadHandle *
54 GNUNET_BIO_read_open (const char *fn)
55 {
56   struct GNUNET_DISK_FileHandle *fd;
57   struct GNUNET_BIO_ReadHandle *h;
58
59   fd = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
60   if (NULL == fd)
61     return NULL;
62   h = GNUNET_malloc (sizeof (struct GNUNET_BIO_ReadHandle) + BIO_BUFFER_SIZE);
63   h->buffer = (char *) &h[1];
64   h->size = BIO_BUFFER_SIZE;
65   h->fd = fd;
66   return h;
67 }
68
69
70 /**
71  * Close an open file.  Reports if any errors reading
72  * from the file were encountered.
73  *
74  * @param h file handle
75  * @param emsg set to the error message
76  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
77  */
78 int
79 GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg)
80 {
81   int err;
82
83   err = (NULL == h->emsg) ? GNUNET_OK : GNUNET_SYSERR;
84   if (emsg != NULL)
85     *emsg = h->emsg;
86   else
87     GNUNET_free_non_null (h->emsg);
88   GNUNET_DISK_file_close (h->fd);
89   GNUNET_free (h);
90   return err;
91 }
92
93
94 /**
95  * Read the contents of a binary file into a buffer.
96  *
97  * @param h handle to an open file
98  * @param what describes what is being read (for error message creation)
99  * @param result the buffer to write the result to
100  * @param len the number of bytes to read
101  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
102  */
103 int
104 GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h,
105                  const char *what, void *result, size_t len)
106 {
107   char *dst = result;
108   size_t min;
109   size_t pos;
110   ssize_t ret;
111
112   if (h->emsg != NULL)
113     return GNUNET_SYSERR;
114   pos = 0;
115   do
116   {
117     /* first, use buffer */
118     min = h->have - h->pos;
119     if (min > 0)
120     {
121       if (min > len - pos)
122         min = len - pos;
123       memcpy (&dst[pos], &h->buffer[h->pos], min);
124       h->pos += min;
125       pos += min;
126     }
127     if (pos == len)
128       return GNUNET_OK;         /* done! */
129     GNUNET_assert (h->have == h->pos);
130     /* fill buffer */
131     ret = GNUNET_DISK_file_read (h->fd, h->buffer, h->size);
132     if (ret == -1)
133     {
134       GNUNET_asprintf (&h->emsg,
135                        _("Error reading `%s': %s"), what, STRERROR (errno));
136       return GNUNET_SYSERR;
137     }
138     if (ret == 0)
139     {
140       GNUNET_asprintf (&h->emsg,
141                        _("Error reading `%s': %s"), what, _("End of file"));
142       return GNUNET_SYSERR;
143     }
144     h->pos = 0;
145     h->have = ret;
146   }
147   while (pos < len);            /* should always be true */
148   return GNUNET_OK;
149 }
150
151
152 /**
153  * Read the contents of a binary file into a buffer.
154  *
155  * @param h handle to an open file
156  * @param file name of the source file
157  * @param line line number in the source file
158  * @param result the buffer to write the result to
159  * @param len the number of bytes to read
160  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
161  */
162 int
163 GNUNET_BIO_read_fn (struct GNUNET_BIO_ReadHandle *h,
164                     const char *file, int line, void *result, size_t len)
165 {
166   char what[1024];
167
168   GNUNET_snprintf (what, sizeof (what), "%s:%d", file, line);
169   return GNUNET_BIO_read (h, what, result, len);
170 }
171
172
173 /**
174  * Read 0-terminated string from a file.
175  *
176  * @param h handle to an open file
177  * @param what describes what is being read (for error message creation)
178  * @param result the buffer to store a pointer to the (allocated) string to
179  *        (note that *result could be set to NULL as well)
180  * @param maxLen maximum allowed length for the string
181  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
182  */
183 int
184 GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h,
185                         const char *what, char **result, size_t maxLen)
186 {
187   char *buf;
188   uint32_t big;
189
190   if (GNUNET_OK != GNUNET_BIO_read_int32 (h, &big))
191   {
192     GNUNET_free_non_null (h->emsg);
193     GNUNET_asprintf (&h->emsg, _("Error reading length of string `%s'"), what);
194     return GNUNET_SYSERR;
195   }
196   if (big == 0)
197   {
198     *result = NULL;
199     return GNUNET_OK;
200   }
201   if (big > maxLen)
202   {
203     GNUNET_asprintf (&h->emsg,
204                      _("String `%s' longer than allowed (%u > %u)"),
205                      what, big, maxLen);
206     return GNUNET_SYSERR;
207   }
208   buf = GNUNET_malloc (big);
209   *result = buf;
210   buf[--big] = '\0';
211   if (big == 0)
212     return GNUNET_OK;
213   if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, big))
214   {
215     GNUNET_free (buf);
216     *result = NULL;
217     return GNUNET_SYSERR;
218   }
219   return GNUNET_OK;
220 }
221
222
223 /**
224  * Read metadata container from a file.
225  *
226  * @param h handle to an open file
227  * @param what describes what is being read (for error message creation)
228  * @param result the buffer to store a pointer to the (allocated) metadata
229  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
230  */
231 int
232 GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h,
233                            const char *what,
234                            struct GNUNET_CONTAINER_MetaData **result)
235 {
236   uint32_t size;
237   char *buf;
238   struct GNUNET_CONTAINER_MetaData *meta;
239
240   if (GNUNET_BIO_read_int32 (h, (int32_t *) & size) != GNUNET_OK)
241     return GNUNET_SYSERR;
242   if (size == 0)
243   {
244     *result = NULL;
245     return GNUNET_OK;
246   }
247   if (size > MAX_META_DATA)
248   {
249     GNUNET_asprintf (&h->emsg,
250                      _("Serialized metadata `%s' larger than allowed (%u>%u)"),
251                      what, size, MAX_META_DATA);
252     return GNUNET_SYSERR;
253   }
254   buf = GNUNET_malloc (size);
255   if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, size))
256   {
257     GNUNET_free (buf);
258     return GNUNET_SYSERR;
259   }
260   meta = GNUNET_CONTAINER_meta_data_deserialize (buf, size);
261   if (meta == NULL)
262   {
263     GNUNET_free (buf);
264     GNUNET_asprintf (&h->emsg, _("Metadata `%s' failed to deserialize"), what);
265     return GNUNET_SYSERR;
266   }
267   GNUNET_free (buf);
268   *result = meta;
269   return GNUNET_OK;
270 }
271
272
273 /**
274  * Read an (u)int32_t.
275  *
276  * @param h hande to open file
277  * @param file name of the source file
278  * @param line line number in the source file
279  * @param i address of 32-bit integer to read
280  * @return GNUNET_OK on success, GNUNET_SYSERR on error
281  */
282 int
283 GNUNET_BIO_read_int32__ (struct GNUNET_BIO_ReadHandle *h,
284                          const char *file, int line, int32_t * i)
285 {
286   int32_t big;
287
288   if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof (int32_t)))
289     return GNUNET_SYSERR;
290   *i = ntohl (big);
291   return GNUNET_OK;
292 }
293
294
295 /**
296  * Read an (u)int64_t.
297  *
298  * @param h hande to open file
299  * @param file name of the source file
300  * @param line line number in the source file
301  * @param i address of 64-bit integer to read
302  * @return GNUNET_OK on success, GNUNET_SYSERR on error
303  */
304 int
305 GNUNET_BIO_read_int64__ (struct GNUNET_BIO_ReadHandle *h,
306                          const char *file, int line, int64_t * i)
307 {
308   int64_t big;
309
310   if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof (int64_t)))
311     return GNUNET_SYSERR;
312   *i = GNUNET_ntohll (big);
313   return GNUNET_OK;
314 }
315
316
317 /**
318  * Handle for buffered writing.
319  */
320 struct GNUNET_BIO_WriteHandle
321 {
322   struct GNUNET_DISK_FileHandle *fd;
323   char *buffer;
324   size_t have;
325   size_t size;
326 };
327
328
329 /**
330  * Open a file for writing.
331  *
332  * @param fn file name to be opened
333  * @return IO handle on success, NULL on error
334  */
335 struct GNUNET_BIO_WriteHandle *
336 GNUNET_BIO_write_open (const char *fn)
337 {
338   struct GNUNET_DISK_FileHandle *fd;
339   struct GNUNET_BIO_WriteHandle *h;
340
341   fd = GNUNET_DISK_file_open (fn,
342                               GNUNET_DISK_OPEN_WRITE |
343                               GNUNET_DISK_OPEN_TRUNCATE |
344                               GNUNET_DISK_OPEN_CREATE,
345                               GNUNET_DISK_PERM_USER_READ |
346                               GNUNET_DISK_PERM_USER_WRITE);
347   if (NULL == fd)
348     return NULL;
349   h = GNUNET_malloc (sizeof (struct GNUNET_BIO_WriteHandle) + BIO_BUFFER_SIZE);
350   h->buffer = (char *) &h[1];
351   h->size = BIO_BUFFER_SIZE;
352   h->fd = fd;
353
354   return h;
355 }
356
357
358 /**
359  * Close an open file for writing.
360  *
361  * @param h file handle
362  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
363  */
364 int
365 GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h)
366 {
367   ssize_t wrt;
368   int ret;
369
370   if (NULL == h->fd)
371   {
372     ret = GNUNET_SYSERR;
373   }
374   else
375   {
376     wrt = GNUNET_DISK_file_write (h->fd, h->buffer, h->have);
377     if (wrt == h->have)
378       ret = GNUNET_OK;
379     else
380       ret = GNUNET_SYSERR;
381     GNUNET_DISK_file_close (h->fd);
382   }
383   GNUNET_free (h);
384   return ret;
385 }
386
387
388 /**
389  * Write a buffer to a file.
390  *
391  * @param h handle to open file
392  * @param buffer the data to write
393  * @param n number of bytes to write
394  * @return GNUNET_OK on success, GNUNET_SYSERR on error
395  */
396 int
397 GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h,
398                   const void *buffer, size_t n)
399 {
400   const char *src = buffer;
401   size_t min;
402   size_t pos;
403   ssize_t ret;
404
405   if (NULL == h->fd)
406     return GNUNET_SYSERR;
407   pos = 0;
408   do
409   {
410     /* first, just use buffer */
411     min = h->size - h->have;
412     if (min > n - pos)
413       min = n - pos;
414     memcpy (&h->buffer[h->have], &src[pos], min);
415     pos += min;
416     h->have += min;
417     if (pos == n)
418       return GNUNET_OK;         /* done */
419     GNUNET_assert (h->have == h->size);
420     ret = GNUNET_DISK_file_write (h->fd, h->buffer, h->size);
421     if (ret != h->size)
422     {
423       GNUNET_DISK_file_close (h->fd);
424       h->fd = NULL;
425       return GNUNET_SYSERR;     /* error */
426     }
427     h->have = 0;
428   }
429   while (pos < n);              /* should always be true */
430   GNUNET_break (0);
431   return GNUNET_OK;
432 }
433
434
435 /**
436  * Write a string to a file.
437  *
438  * @param h handle to open file
439  * @param s string to write (can be NULL)
440  * @return GNUNET_OK on success, GNUNET_SYSERR on error
441  */
442 int
443 GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h, const char *s)
444 {
445   uint32_t slen;
446
447   slen = (uint32_t) ((s == NULL) ? 0 : strlen (s) + 1);
448   if (GNUNET_OK != GNUNET_BIO_write_int32 (h, slen))
449     return GNUNET_SYSERR;
450   if (0 != slen)
451     return GNUNET_BIO_write (h, s, slen - 1);
452   return GNUNET_OK;
453 }
454
455
456 /**
457  * Write metadata container to a file.
458  *
459  * @param h handle to open file
460  * @param m metadata to write
461  * @return GNUNET_OK on success, GNUNET_SYSERR on error
462  */
463 int
464 GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h,
465                             const struct GNUNET_CONTAINER_MetaData *m)
466 {
467   ssize_t size;
468   char *buf;
469
470   if (m == NULL)
471     return GNUNET_BIO_write_int32 (h, 0);
472   buf = NULL;
473   size = GNUNET_CONTAINER_meta_data_serialize (m,
474                                                &buf,
475                                                MAX_META_DATA,
476                                                GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
477   if (size == -1)
478   {
479     GNUNET_free (buf);
480     return GNUNET_SYSERR;
481   }
482   if ((GNUNET_OK != GNUNET_BIO_write_int32 (h, (uint32_t) size)) ||
483       (GNUNET_OK != GNUNET_BIO_write (h, buf, size)))
484   {
485     GNUNET_free (buf);
486     return GNUNET_SYSERR;
487   }
488   GNUNET_free (buf);
489   return GNUNET_OK;
490 }
491
492
493 /**
494  * Write an (u)int32_t.
495  *
496  * @param h hande to open file
497  * @param i address of 32-bit integer to write
498  * @return GNUNET_OK on success, GNUNET_SYSERR on error
499  */
500 int
501 GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, int32_t i)
502 {
503   int32_t big;
504
505   big = htonl (i);
506   return GNUNET_BIO_write (h, &big, sizeof (int32_t));
507 }
508
509
510 /**
511  * Write an (u)int64_t.
512  *
513  * @param h hande to open file
514  * @param i address of 64-bit integer to write
515  * @return GNUNET_OK on success, GNUNET_SYSERR on error
516  */
517 int
518 GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, int64_t i)
519 {
520   int64_t big;
521
522   big = GNUNET_htonll (i);
523   return GNUNET_BIO_write (h, &big, sizeof (int64_t));
524 }
525
526
527 /* end of bio.c */