get rid of SOCKTYPE and FDTYPE
[oweals/gnunet.git] / src / util / crypto_hash_file.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-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 /**
22  * @file util/crypto_hash_file.c
23  * @brief incremental hashing of files
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include <gcrypt.h>
29
30 #define LOG(kind, ...) GNUNET_log_from (kind, "util-crypto-hash-file", \
31                                         __VA_ARGS__)
32
33 #define LOG_STRERROR_FILE(kind, syscall, \
34                           filename) GNUNET_log_from_strerror_file (kind, \
35                                                                    "util-crypto-hash-file", \
36                                                                    syscall, \
37                                                                    filename)
38
39
40 /**
41  * Context used when hashing a file.
42  */
43 struct GNUNET_CRYPTO_FileHashContext
44 {
45   /**
46    * Function to call upon completion.
47    */
48   GNUNET_CRYPTO_HashCompletedCallback callback;
49
50   /**
51    * Closure for callback.
52    */
53   void *callback_cls;
54
55   /**
56    * IO buffer.
57    */
58   unsigned char *buffer;
59
60   /**
61    * Name of the file we are hashing.
62    */
63   char *filename;
64
65   /**
66    * File descriptor.
67    */
68   struct GNUNET_DISK_FileHandle *fh;
69
70   /**
71    * Cummulated hash.
72    */
73   gcry_md_hd_t md;
74
75   /**
76    * Size of the file.
77    */
78   uint64_t fsize;
79
80   /**
81    * Current offset.
82    */
83   uint64_t offset;
84
85   /**
86    * Current task for hashing.
87    */
88   struct GNUNET_SCHEDULER_Task *task;
89
90   /**
91    * Priority we use.
92    */
93   enum GNUNET_SCHEDULER_Priority priority;
94
95   /**
96    * Blocksize.
97    */
98   size_t bsize;
99 };
100
101
102 /**
103  * Report result of hash computation to callback
104  * and free associated resources.
105  */
106 static void
107 file_hash_finish (struct GNUNET_CRYPTO_FileHashContext *fhc,
108                   const struct GNUNET_HashCode *res)
109 {
110   fhc->callback (fhc->callback_cls, res);
111   GNUNET_free (fhc->filename);
112   if (! GNUNET_DISK_handle_invalid (fhc->fh))
113     GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fhc->fh));
114   gcry_md_close (fhc->md);
115   GNUNET_free (fhc);             /* also frees fhc->buffer */
116 }
117
118
119 /**
120  * File hashing task.
121  *
122  * @param cls closure
123  */
124 static void
125 file_hash_task (void *cls)
126 {
127   struct GNUNET_CRYPTO_FileHashContext *fhc = cls;
128   struct GNUNET_HashCode *res;
129   size_t delta;
130   ssize_t sret;
131
132   fhc->task = NULL;
133   GNUNET_assert (fhc->offset <= fhc->fsize);
134   delta = fhc->bsize;
135   if (fhc->fsize - fhc->offset < delta)
136     delta = fhc->fsize - fhc->offset;
137   sret = GNUNET_DISK_file_read (fhc->fh,
138                                 fhc->buffer,
139                                 delta);
140   if ((sret < 0) ||
141       (delta != (size_t) sret))
142   {
143     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
144                        "read",
145                        fhc->filename);
146     file_hash_finish (fhc,
147                       NULL);
148     return;
149   }
150   gcry_md_write (fhc->md,
151                  fhc->buffer,
152                  delta);
153   fhc->offset += delta;
154   if (fhc->offset == fhc->fsize)
155   {
156     res = (struct GNUNET_HashCode *) gcry_md_read (fhc->md,
157                                                    GCRY_MD_SHA512);
158     file_hash_finish (fhc, res);
159     return;
160   }
161   fhc->task = GNUNET_SCHEDULER_add_with_priority (fhc->priority,
162                                                   &file_hash_task,
163                                                   fhc);
164 }
165
166
167 /**
168  * Compute the hash of an entire file.
169  *
170  * @param priority scheduling priority to use
171  * @param filename name of file to hash
172  * @param blocksize number of bytes to process in one task
173  * @param callback function to call upon completion
174  * @param callback_cls closure for @a callback
175  * @return NULL on (immediate) errror
176  */
177 struct GNUNET_CRYPTO_FileHashContext *
178 GNUNET_CRYPTO_hash_file (enum GNUNET_SCHEDULER_Priority priority,
179                          const char *filename,
180                          size_t blocksize,
181                          GNUNET_CRYPTO_HashCompletedCallback callback,
182                          void *callback_cls)
183 {
184   struct GNUNET_CRYPTO_FileHashContext *fhc;
185
186   GNUNET_assert (blocksize > 0);
187   fhc =
188     GNUNET_malloc (sizeof(struct GNUNET_CRYPTO_FileHashContext) + blocksize);
189   fhc->callback = callback;
190   fhc->callback_cls = callback_cls;
191   fhc->buffer = (unsigned char *) &fhc[1];
192   fhc->filename = GNUNET_strdup (filename);
193   if (GPG_ERR_NO_ERROR != gcry_md_open (&fhc->md, GCRY_MD_SHA512, 0))
194   {
195     GNUNET_break (0);
196     GNUNET_free (fhc);
197     return NULL;
198   }
199   fhc->bsize = blocksize;
200   if (GNUNET_OK !=
201       GNUNET_DISK_file_size (filename,
202                              &fhc->fsize,
203                              GNUNET_NO,
204                              GNUNET_YES))
205   {
206     GNUNET_free (fhc->filename);
207     GNUNET_free (fhc);
208     return NULL;
209   }
210   fhc->fh = GNUNET_DISK_file_open (filename,
211                                    GNUNET_DISK_OPEN_READ,
212                                    GNUNET_DISK_PERM_NONE);
213   if (! fhc->fh)
214   {
215     GNUNET_free (fhc->filename);
216     GNUNET_free (fhc);
217     return NULL;
218   }
219   fhc->priority = priority;
220   fhc->task = GNUNET_SCHEDULER_add_with_priority (priority,
221                                                   &file_hash_task,
222                                                   fhc);
223   return fhc;
224 }
225
226
227 /**
228  * Cancel a file hashing operation.
229  *
230  * @param fhc operation to cancel (callback must not yet have been invoked)
231  */
232 void
233 GNUNET_CRYPTO_hash_file_cancel (struct GNUNET_CRYPTO_FileHashContext *fhc)
234 {
235   GNUNET_SCHEDULER_cancel (fhc->task);
236   GNUNET_free (fhc->filename);
237   GNUNET_break (GNUNET_OK ==
238                 GNUNET_DISK_file_close (fhc->fh));
239   GNUNET_free (fhc);
240 }
241
242
243 /* end of crypto_hash_file.c */