first step to remove plibc
[oweals/gnunet.git] / src / util / bio.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2006, 2009, 2013 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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_util_lib.h"
27
28 #define LOG(kind, ...) GNUNET_log_from (kind, "util-bio", __VA_ARGS__)
29
30 #ifndef PATH_MAX
31 /**
32  * Assumed maximum path length (for source file names).
33  */
34 #define PATH_MAX 4096
35 #endif
36
37
38 /**
39  * Size for I/O buffers.
40  */
41 #define BIO_BUFFER_SIZE 65536
42
43 /**
44  * Maximum size allowed for meta data written/read from disk.
45  * File-sharing limits to 64k, so this should be rather generous.
46  */
47 #define MAX_META_DATA (1024 * 1024)
48
49
50 /**
51  * Handle for buffered reading.
52  */
53 struct GNUNET_BIO_ReadHandle
54 {
55   /**
56    * Underlying file abstraction.
57    */
58   struct GNUNET_DISK_FileHandle *fd;
59
60   /**
61    * Error message, NULL if there were no errors.
62    */
63   char *emsg;
64
65   /**
66    * I/O buffer.  Allocated at the end of the struct, do not free!
67    */
68   char *buffer;
69
70   /**
71    * Number of bytes available in read @e buffer.
72    */
73   size_t have;
74
75   /**
76    * Total size of @e buffer.
77    */
78   size_t size;
79
80   /**
81    * Current read offset in @e buffer.
82    */
83   off_t pos;
84 };
85
86
87 /**
88  * Open a file for reading.
89  *
90  * @param fn file name to be opened
91  * @return IO handle on success, NULL on error
92  */
93 struct GNUNET_BIO_ReadHandle *
94 GNUNET_BIO_read_open (const char *fn)
95 {
96   struct GNUNET_DISK_FileHandle *fd;
97   struct GNUNET_BIO_ReadHandle *h;
98
99   fd = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
100   if (NULL == fd)
101     return NULL;
102   h = GNUNET_malloc (sizeof (struct GNUNET_BIO_ReadHandle) + BIO_BUFFER_SIZE);
103   h->buffer = (char *) &h[1];
104   h->size = BIO_BUFFER_SIZE;
105   h->fd = fd;
106   return h;
107 }
108
109
110 /**
111  * Close an open file.  Reports if any errors reading
112  * from the file were encountered.
113  *
114  * @param h file handle
115  * @param emsg set to the error message
116  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
117  */
118 int
119 GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg)
120 {
121   int err;
122
123   err = (NULL == h->emsg) ? GNUNET_OK : GNUNET_SYSERR;
124   if (emsg != NULL)
125     *emsg = h->emsg;
126   else
127     GNUNET_free_non_null (h->emsg);
128   GNUNET_DISK_file_close (h->fd);
129   GNUNET_free (h);
130   return err;
131 }
132
133
134 /**
135  * Read the contents of a binary file into a buffer.
136  *
137  * @param h handle to an open file
138  * @param what describes what is being read (for error message creation)
139  * @param result the buffer to write the result to
140  * @param len the number of bytes to read
141  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
142  */
143 int
144 GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h,
145                  const char *what,
146                  void *result,
147                  size_t len)
148 {
149   char *dst = result;
150   size_t min;
151   size_t pos;
152   ssize_t ret;
153
154   if (NULL != h->emsg)
155     return GNUNET_SYSERR;
156   pos = 0;
157   do
158   {
159     /* first, use buffer */
160     min = h->have - h->pos;
161     if (min > 0)
162     {
163       if (min > len - pos)
164         min = len - pos;
165       GNUNET_memcpy (&dst[pos], &h->buffer[h->pos], min);
166       h->pos += min;
167       pos += min;
168     }
169     if (pos == len)
170       return GNUNET_OK; /* done! */
171     GNUNET_assert (((off_t) h->have) == h->pos);
172     /* fill buffer */
173     ret = GNUNET_DISK_file_read (h->fd, h->buffer, h->size);
174     if (-1 == ret)
175     {
176       GNUNET_asprintf (&h->emsg,
177                        _ ("Error reading `%s': %s"),
178                        what,
179                        strerror (errno));
180       return GNUNET_SYSERR;
181     }
182     if (0 == ret)
183     {
184       GNUNET_asprintf (&h->emsg,
185                        _ ("Error reading `%s': %s"),
186                        what,
187                        _ ("End of file"));
188       return GNUNET_SYSERR;
189     }
190     h->pos = 0;
191     h->have = ret;
192   } while (pos < len); /* should always be true */
193   return GNUNET_OK;
194 }
195
196
197 /**
198  * Read the contents of a binary file into a buffer.
199  *
200  * @param h handle to an open file
201  * @param file name of the source file
202  * @param line line number in the source file
203  * @param result the buffer to write the result to
204  * @param len the number of bytes to read
205  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
206  */
207 int
208 GNUNET_BIO_read_fn (struct GNUNET_BIO_ReadHandle *h,
209                     const char *file,
210                     int line,
211                     void *result,
212                     size_t len)
213 {
214   char what[PATH_MAX + 1024];
215
216   GNUNET_snprintf (what, sizeof (what), "%s:%d", file, line);
217   return GNUNET_BIO_read (h, what, result, len);
218 }
219
220
221 /**
222  * Read 0-terminated string from a file.
223  *
224  * @param h handle to an open file
225  * @param what describes what is being read (for error message creation)
226  * @param result the buffer to store a pointer to the (allocated) string to
227  *        (note that *result could be set to NULL as well)
228  * @param max_length maximum allowed length for the string
229  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
230  */
231 int
232 GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h,
233                         const char *what,
234                         char **result,
235                         size_t max_length)
236 {
237   char *buf;
238   uint32_t big;
239
240   if (GNUNET_OK != GNUNET_BIO_read_int32 (h, &big))
241   {
242     GNUNET_free_non_null (h->emsg);
243     GNUNET_asprintf (&h->emsg, _ ("Error reading length of string `%s'"), what);
244     return GNUNET_SYSERR;
245   }
246   if (0 == big)
247   {
248     *result = NULL;
249     return GNUNET_OK;
250   }
251   if (big > max_length)
252   {
253     GNUNET_asprintf (&h->emsg,
254                      _ ("String `%s' longer than allowed (%u > %u)"),
255                      what,
256                      big,
257                      max_length);
258     return GNUNET_SYSERR;
259   }
260   buf = GNUNET_malloc (big);
261   *result = buf;
262   buf[--big] = '\0';
263   if (0 == big)
264     return GNUNET_OK;
265   if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, big))
266   {
267     GNUNET_free (buf);
268     *result = NULL;
269     return GNUNET_SYSERR;
270   }
271   return GNUNET_OK;
272 }
273
274
275 /**
276  * Read metadata container from a file.
277  *
278  * @param h handle to an open file
279  * @param what describes what is being read (for error message creation)
280  * @param result the buffer to store a pointer to the (allocated) metadata
281  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
282  */
283 int
284 GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h,
285                            const char *what,
286                            struct GNUNET_CONTAINER_MetaData **result)
287 {
288   uint32_t size;
289   char *buf;
290   struct GNUNET_CONTAINER_MetaData *meta;
291
292   if (GNUNET_OK != GNUNET_BIO_read_int32 (h, (int32_t *) &size))
293     return GNUNET_SYSERR;
294   if (size == 0)
295   {
296     *result = NULL;
297     return GNUNET_OK;
298   }
299   if (size > MAX_META_DATA)
300   {
301     GNUNET_asprintf (&h->emsg,
302                      _ ("Serialized metadata `%s' larger than allowed (%u>%u)"),
303                      what,
304                      size,
305                      MAX_META_DATA);
306     return GNUNET_SYSERR;
307   }
308   buf = GNUNET_malloc (size);
309   if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, size))
310   {
311     GNUNET_free (buf);
312     return GNUNET_SYSERR;
313   }
314   meta = GNUNET_CONTAINER_meta_data_deserialize (buf, size);
315   if (NULL == meta)
316   {
317     GNUNET_free (buf);
318     GNUNET_asprintf (&h->emsg, _ ("Metadata `%s' failed to deserialize"), what);
319     return GNUNET_SYSERR;
320   }
321   GNUNET_free (buf);
322   *result = meta;
323   return GNUNET_OK;
324 }
325
326
327 /**
328  * Read an (u)int32_t.
329  *
330  * @param h hande to open file
331  * @param file name of the source file
332  * @param line line number in the source file
333  * @param i address of 32-bit integer to read
334  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
335  */
336 int
337 GNUNET_BIO_read_int32__ (struct GNUNET_BIO_ReadHandle *h,
338                          const char *file,
339                          int line,
340                          int32_t *i)
341 {
342   int32_t big;
343
344   if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof (int32_t)))
345     return GNUNET_SYSERR;
346   *i = ntohl (big);
347   return GNUNET_OK;
348 }
349
350
351 /**
352  * Read an (u)int64_t.
353  *
354  * @param h hande to open file
355  * @param file name of the source file
356  * @param line line number in the source file
357  * @param i address of 64-bit integer to read
358  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
359  */
360 int
361 GNUNET_BIO_read_int64__ (struct GNUNET_BIO_ReadHandle *h,
362                          const char *file,
363                          int line,
364                          int64_t *i)
365 {
366   int64_t big;
367
368   if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof (int64_t)))
369     return GNUNET_SYSERR;
370   *i = GNUNET_ntohll (big);
371   return GNUNET_OK;
372 }
373
374
375 /**
376  * Handle for buffered writing.
377  */
378 struct GNUNET_BIO_WriteHandle
379 {
380   /**
381    * Underlying file handle.
382    */
383   struct GNUNET_DISK_FileHandle *fd;
384
385   /**
386    * I/O buffer.  Do not free, allocated at the end of the struct.
387    */
388   char *buffer;
389
390   /**
391    * Number of bytes already in @e buffer.
392    */
393   size_t have;
394
395   /**
396    * Total size of @e buffer.
397    */
398   size_t size;
399 };
400
401
402 /**
403  * Open a file for writing.
404  *
405  * @param fn file name to be opened
406  * @return IO handle on success, NULL on error
407  */
408 struct GNUNET_BIO_WriteHandle *
409 GNUNET_BIO_write_open (const char *fn)
410 {
411   struct GNUNET_DISK_FileHandle *fd;
412   struct GNUNET_BIO_WriteHandle *h;
413
414   fd =
415     GNUNET_DISK_file_open (fn,
416                            GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE |
417                              GNUNET_DISK_OPEN_CREATE,
418                            GNUNET_DISK_PERM_USER_READ |
419                              GNUNET_DISK_PERM_USER_WRITE);
420   if (NULL == fd)
421     return NULL;
422   h = GNUNET_malloc (sizeof (struct GNUNET_BIO_WriteHandle) + BIO_BUFFER_SIZE);
423   h->buffer = (char *) &h[1];
424   h->size = BIO_BUFFER_SIZE;
425   h->fd = fd;
426   return h;
427 }
428
429
430 /**
431  * Close an open file for writing.
432  *
433  * @param h file handle
434  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
435  */
436 int
437 GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h)
438 {
439   int ret;
440
441   ret = GNUNET_SYSERR;
442   if ((NULL != h->fd) && (GNUNET_OK == (ret = GNUNET_BIO_flush (h))))
443     GNUNET_DISK_file_close (h->fd);
444   GNUNET_free (h);
445   return ret;
446 }
447
448
449 /**
450  * Force a buffered writer to flush its buffer
451  *
452  * @param h the writer handle
453  * @return #GNUNET_OK upon success.  Upon failure #GNUNET_SYSERR is returned and
454  *           the file is closed
455  */
456 int
457 GNUNET_BIO_flush (struct GNUNET_BIO_WriteHandle *h)
458 {
459   ssize_t ret;
460
461   ret = GNUNET_DISK_file_write (h->fd, h->buffer, h->have);
462   if (ret != (ssize_t) h->have)
463   {
464     GNUNET_DISK_file_close (h->fd);
465     h->fd = NULL;
466     return GNUNET_SYSERR; /* error */
467   }
468   h->have = 0;
469   return GNUNET_OK;
470 }
471
472
473 /**
474  * Write a buffer to a file.
475  *
476  * @param h handle to open file
477  * @param buffer the data to write
478  * @param n number of bytes to write
479  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
480  */
481 int
482 GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h,
483                   const void *buffer,
484                   size_t n)
485 {
486   const char *src = buffer;
487   size_t min;
488   size_t pos;
489
490   if (NULL == h->fd)
491     return GNUNET_SYSERR;
492   pos = 0;
493   do
494   {
495     /* first, just use buffer */
496     min = h->size - h->have;
497     if (min > n - pos)
498       min = n - pos;
499     GNUNET_memcpy (&h->buffer[h->have], &src[pos], min);
500     pos += min;
501     h->have += min;
502     if (pos == n)
503       return GNUNET_OK; /* done */
504     GNUNET_assert (h->have == h->size);
505     if (GNUNET_OK != GNUNET_BIO_flush (h))
506       return GNUNET_SYSERR; /* error */
507   } while (pos < n); /* should always be true */
508   GNUNET_break (0);
509   return GNUNET_OK;
510 }
511
512
513 /**
514  * Write a string to a file.
515  *
516  * @param h handle to open file
517  * @param s string to write (can be NULL)
518  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
519  */
520 int
521 GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h, const char *s)
522 {
523   uint32_t slen;
524
525   slen = (uint32_t) ((s == NULL) ? 0 : strlen (s) + 1);
526   if (GNUNET_OK != GNUNET_BIO_write_int32 (h, slen))
527     return GNUNET_SYSERR;
528   if (0 != slen)
529     return GNUNET_BIO_write (h, s, slen - 1);
530   return GNUNET_OK;
531 }
532
533
534 /**
535  * Write metadata container to a file.
536  *
537  * @param h handle to open file
538  * @param m metadata to write
539  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
540  */
541 int
542 GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h,
543                             const struct GNUNET_CONTAINER_MetaData *m)
544 {
545   ssize_t size;
546   char *buf;
547
548   if (m == NULL)
549     return GNUNET_BIO_write_int32 (h, 0);
550   buf = NULL;
551   size = GNUNET_CONTAINER_meta_data_serialize (
552     m,
553     &buf,
554     MAX_META_DATA,
555     GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
556   if (size == -1)
557   {
558     GNUNET_free (buf);
559     return GNUNET_SYSERR;
560   }
561   if ((GNUNET_OK != GNUNET_BIO_write_int32 (h, (uint32_t) size)) ||
562       (GNUNET_OK != GNUNET_BIO_write (h, buf, size)))
563   {
564     GNUNET_free (buf);
565     return GNUNET_SYSERR;
566   }
567   GNUNET_free (buf);
568   return GNUNET_OK;
569 }
570
571
572 /**
573  * Write an (u)int32_t.
574  *
575  * @param h hande to open file
576  * @param i 32-bit integer to write
577  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
578  */
579 int
580 GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, int32_t i)
581 {
582   int32_t big;
583
584   big = htonl (i);
585   return GNUNET_BIO_write (h, &big, sizeof (int32_t));
586 }
587
588
589 /**
590  * Write an (u)int64_t.
591  *
592  * @param h hande to open file
593  * @param i 64-bit integer to write
594  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
595  */
596 int
597 GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, int64_t i)
598 {
599   int64_t big;
600
601   big = GNUNET_htonll (i);
602   return GNUNET_BIO_write (h, &big, sizeof (int64_t));
603 }
604
605
606 /* end of bio.c */