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