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