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