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