speedup mechanism to manipulate gnunet time
[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 = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
62   if (NULL == fd)
63     return NULL;
64   h = GNUNET_malloc (sizeof (struct GNUNET_BIO_ReadHandle) + BIO_BUFFER_SIZE);
65   h->buffer = (char *) &h[1];
66   h->size = BIO_BUFFER_SIZE;
67   h->fd = fd;
68   return h;
69 }
70
71
72 /**
73  * Close an open file.  Reports if any errors reading
74  * from the file were encountered.
75  *
76  * @param h file handle
77  * @param emsg set to the error message
78  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
79  */
80 int
81 GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg)
82 {
83   int err;
84
85   err = (NULL == h->emsg) ? GNUNET_OK : GNUNET_SYSERR;
86   if (emsg != NULL)
87     *emsg = h->emsg;
88   else
89     GNUNET_free_non_null (h->emsg);
90   GNUNET_DISK_file_close (h->fd);
91   GNUNET_free (h);
92   return err;
93 }
94
95
96 /**
97  * Read the contents of a binary file into a buffer.
98  *
99  * @param h handle to an open file
100  * @param what describes what is being read (for error message creation)
101  * @param result the buffer to write the result to
102  * @param len the number of bytes to read
103  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
104  */
105 int
106 GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, const char *what,
107                  void *result, size_t len)
108 {
109   char *dst = result;
110   size_t min;
111   size_t pos;
112   ssize_t ret;
113
114   if (h->emsg != NULL)
115     return GNUNET_SYSERR;
116   pos = 0;
117   do
118   {
119     /* first, use buffer */
120     min = h->have - h->pos;
121     if (min > 0)
122     {
123       if (min > len - pos)
124         min = len - pos;
125       memcpy (&dst[pos], &h->buffer[h->pos], min);
126       h->pos += min;
127       pos += min;
128     }
129     if (pos == len)
130       return GNUNET_OK;         /* done! */
131     GNUNET_assert (h->have == h->pos);
132     /* fill buffer */
133     ret = GNUNET_DISK_file_read (h->fd, h->buffer, h->size);
134     if (ret == -1)
135     {
136       GNUNET_asprintf (&h->emsg, _("Error reading `%s': %s"), what,
137                        STRERROR (errno));
138       return GNUNET_SYSERR;
139     }
140     if (ret == 0)
141     {
142       GNUNET_asprintf (&h->emsg, _("Error reading `%s': %s"), what,
143                        _("End of file"));
144       return GNUNET_SYSERR;
145     }
146     h->pos = 0;
147     h->have = ret;
148   }
149   while (pos < len);            /* should always be true */
150   return GNUNET_OK;
151 }
152
153
154 /**
155  * Read the contents of a binary file into a buffer.
156  *
157  * @param h handle to an open file
158  * @param file name of the source file
159  * @param line line number in the source file
160  * @param result the buffer to write the result to
161  * @param len the number of bytes to read
162  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
163  */
164 int
165 GNUNET_BIO_read_fn (struct GNUNET_BIO_ReadHandle *h, const char *file, int line,
166                     void *result, size_t len)
167 {
168   char what[1024];
169
170   GNUNET_snprintf (what, sizeof (what), "%s:%d", 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, const char *what,
187                         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, _("Error reading length of string `%s'"), what);
196     return GNUNET_SYSERR;
197   }
198   if (big == 0)
199   {
200     *result = NULL;
201     return GNUNET_OK;
202   }
203   if (big > maxLen)
204   {
205     GNUNET_asprintf (&h->emsg, _("String `%s' longer than allowed (%u > %u)"),
206                      what, big, maxLen);
207     return GNUNET_SYSERR;
208   }
209   buf = GNUNET_malloc (big);
210   *result = buf;
211   buf[--big] = '\0';
212   if (big == 0)
213     return GNUNET_OK;
214   if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, big))
215   {
216     GNUNET_free (buf);
217     *result = NULL;
218     return GNUNET_SYSERR;
219   }
220   return GNUNET_OK;
221 }
222
223
224 /**
225  * Read metadata container from a file.
226  *
227  * @param h handle to an open file
228  * @param what describes what is being read (for error message creation)
229  * @param result the buffer to store a pointer to the (allocated) metadata
230  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
231  */
232 int
233 GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h, 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, const char *file,
284                          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, const char *file,
306                          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 | GNUNET_DISK_OPEN_TRUNCATE
343                               | GNUNET_DISK_OPEN_CREATE,
344                               GNUNET_DISK_PERM_USER_READ |
345                               GNUNET_DISK_PERM_USER_WRITE);
346   if (NULL == fd)
347     return NULL;
348   h = GNUNET_malloc (sizeof (struct GNUNET_BIO_WriteHandle) + BIO_BUFFER_SIZE);
349   h->buffer = (char *) &h[1];
350   h->size = BIO_BUFFER_SIZE;
351   h->fd = fd;
352
353   return h;
354 }
355
356
357 /**
358  * Close an open file for writing.
359  *
360  * @param h file handle
361  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
362  */
363 int
364 GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h)
365 {
366   ssize_t wrt;
367   int ret;
368
369   if (NULL == h->fd)
370   {
371     ret = GNUNET_SYSERR;
372   }
373   else
374   {
375     wrt = GNUNET_DISK_file_write (h->fd, h->buffer, h->have);
376     if (wrt == h->have)
377       ret = GNUNET_OK;
378     else
379       ret = GNUNET_SYSERR;
380     GNUNET_DISK_file_close (h->fd);
381   }
382   GNUNET_free (h);
383   return ret;
384 }
385
386
387 /**
388  * Write a buffer to a file.
389  *
390  * @param h handle to open file
391  * @param buffer the data to write
392  * @param n number of bytes to write
393  * @return GNUNET_OK on success, GNUNET_SYSERR on error
394  */
395 int
396 GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, const void *buffer,
397                   size_t n)
398 {
399   const char *src = buffer;
400   size_t min;
401   size_t pos;
402   ssize_t ret;
403
404   if (NULL == h->fd)
405     return GNUNET_SYSERR;
406   pos = 0;
407   do
408   {
409     /* first, just use buffer */
410     min = h->size - h->have;
411     if (min > n - pos)
412       min = n - pos;
413     memcpy (&h->buffer[h->have], &src[pos], min);
414     pos += min;
415     h->have += min;
416     if (pos == n)
417       return GNUNET_OK;         /* done */
418     GNUNET_assert (h->have == h->size);
419     ret = GNUNET_DISK_file_write (h->fd, h->buffer, h->size);
420     if (ret != h->size)
421     {
422       GNUNET_DISK_file_close (h->fd);
423       h->fd = NULL;
424       return GNUNET_SYSERR;     /* error */
425     }
426     h->have = 0;
427   }
428   while (pos < n);              /* should always be true */
429   GNUNET_break (0);
430   return GNUNET_OK;
431 }
432
433
434 /**
435  * Write a string to a file.
436  *
437  * @param h handle to open file
438  * @param s string to write (can be NULL)
439  * @return GNUNET_OK on success, GNUNET_SYSERR on error
440  */
441 int
442 GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h, const char *s)
443 {
444   uint32_t slen;
445
446   slen = (uint32_t) ((s == NULL) ? 0 : strlen (s) + 1);
447   if (GNUNET_OK != GNUNET_BIO_write_int32 (h, slen))
448     return GNUNET_SYSERR;
449   if (0 != slen)
450     return GNUNET_BIO_write (h, s, slen - 1);
451   return GNUNET_OK;
452 }
453
454
455 /**
456  * Write metadata container to a file.
457  *
458  * @param h handle to open file
459  * @param m metadata to write
460  * @return GNUNET_OK on success, GNUNET_SYSERR on error
461  */
462 int
463 GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h,
464                             const struct GNUNET_CONTAINER_MetaData *m)
465 {
466   ssize_t size;
467   char *buf;
468
469   if (m == NULL)
470     return GNUNET_BIO_write_int32 (h, 0);
471   buf = NULL;
472   size =
473       GNUNET_CONTAINER_meta_data_serialize (m, &buf, MAX_META_DATA,
474                                             GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
475   if (size == -1)
476   {
477     GNUNET_free (buf);
478     return GNUNET_SYSERR;
479   }
480   if ((GNUNET_OK != GNUNET_BIO_write_int32 (h, (uint32_t) size)) ||
481       (GNUNET_OK != GNUNET_BIO_write (h, buf, size)))
482   {
483     GNUNET_free (buf);
484     return GNUNET_SYSERR;
485   }
486   GNUNET_free (buf);
487   return GNUNET_OK;
488 }
489
490
491 /**
492  * Write an (u)int32_t.
493  *
494  * @param h hande to open file
495  * @param i 32-bit integer to write
496  * @return GNUNET_OK on success, GNUNET_SYSERR on error
497  */
498 int
499 GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, int32_t i)
500 {
501   int32_t big;
502
503   big = htonl (i);
504   return GNUNET_BIO_write (h, &big, sizeof (int32_t));
505 }
506
507
508 /**
509  * Write an (u)int64_t.
510  *
511  * @param h hande to open file
512  * @param i 64-bit integer to write
513  * @return GNUNET_OK on success, GNUNET_SYSERR on error
514  */
515 int
516 GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, int64_t i)
517 {
518   int64_t big;
519
520   big = GNUNET_htonll (i);
521   return GNUNET_BIO_write (h, &big, sizeof (int64_t));
522 }
523
524
525 /* end of bio.c */