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