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