added ATS addresstype information to unix
[oweals/gnunet.git] / src / fs / fs_unindex.c
1 /*
2      This file is part of GNUnet.
3      (C) 2003, 2004, 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 3, 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 /**
22  * @file fs/fs_unindex.c
23  * @author Krista Grothoff
24  * @author Christian Grothoff
25  * @brief Unindex file.
26  */
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_fs_service.h"
30 #include "gnunet_protocols.h"
31 #include "fs_api.h"
32 #include "fs_tree.h"
33
34 #define DEBUG_UNINDEX GNUNET_EXTRA_LOGGING
35
36 /**
37  * Function called by the tree encoder to obtain
38  * a block of plaintext data (for the lowest level
39  * of the tree).
40  *
41  * @param cls our publishing context
42  * @param offset identifies which block to get
43  * @param max (maximum) number of bytes to get; returning
44  *        fewer will also cause errors
45  * @param buf where to copy the plaintext buffer
46  * @param emsg location to store an error message (on error)
47  * @return number of bytes copied to buf, 0 on error
48  */
49 static size_t
50 unindex_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg)
51 {
52   struct GNUNET_FS_UnindexContext *uc = cls;
53   size_t pt_size;
54
55   pt_size = GNUNET_MIN (max, uc->file_size - offset);
56   if (offset != GNUNET_DISK_file_seek (uc->fh, offset, GNUNET_DISK_SEEK_SET))
57   {
58     *emsg = GNUNET_strdup (_("Failed to find given position in file"));
59     return 0;
60   }
61   if (pt_size != GNUNET_DISK_file_read (uc->fh, buf, pt_size))
62   {
63     *emsg = GNUNET_strdup (_("Failed to read file"));
64     return 0;
65   }
66   return pt_size;
67 }
68
69
70 /**
71  * Fill in all of the generic fields for
72  * an unindex event and call the callback.
73  *
74  * @param pi structure to fill in
75  * @param uc overall unindex context
76  * @param offset where we are in the file (for progress)
77  */
78 void
79 GNUNET_FS_unindex_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
80                                 struct GNUNET_FS_UnindexContext *uc,
81                                 uint64_t offset)
82 {
83   pi->value.unindex.uc = uc;
84   pi->value.unindex.cctx = uc->client_info;
85   pi->value.unindex.filename = uc->filename;
86   pi->value.unindex.size = uc->file_size;
87   pi->value.unindex.eta =
88       GNUNET_TIME_calculate_eta (uc->start_time, offset, uc->file_size);
89   pi->value.unindex.duration =
90       GNUNET_TIME_absolute_get_duration (uc->start_time);
91   pi->value.unindex.completed = offset;
92   uc->client_info = uc->h->upcb (uc->h->upcb_cls, pi);
93
94 }
95
96
97 /**
98  * Function called with information about our
99  * progress in computing the tree encoding.
100  *
101  * @param cls closure
102  * @param offset where are we in the file
103  * @param pt_block plaintext of the currently processed block
104  * @param pt_size size of pt_block
105  * @param depth depth of the block in the tree, 0 for DBLOCK
106  */
107 static void
108 unindex_progress (void *cls, uint64_t offset, const void *pt_block,
109                   size_t pt_size, unsigned int depth)
110 {
111   struct GNUNET_FS_UnindexContext *uc = cls;
112   struct GNUNET_FS_ProgressInfo pi;
113
114   pi.status = GNUNET_FS_STATUS_UNINDEX_PROGRESS;
115   pi.value.unindex.specifics.progress.data = pt_block;
116   pi.value.unindex.specifics.progress.offset = offset;
117   pi.value.unindex.specifics.progress.data_len = pt_size;
118   pi.value.unindex.specifics.progress.depth = depth;
119   GNUNET_FS_unindex_make_status_ (&pi, uc, offset);
120 }
121
122
123 /**
124  * We've encountered an error during
125  * unindexing.  Signal the client.
126  *
127  * @param uc context for the failed unindexing operation
128  */
129 static void
130 signal_unindex_error (struct GNUNET_FS_UnindexContext *uc)
131 {
132   struct GNUNET_FS_ProgressInfo pi;
133
134   pi.status = GNUNET_FS_STATUS_UNINDEX_ERROR;
135   pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
136   pi.value.unindex.specifics.error.message = uc->emsg;
137   GNUNET_FS_unindex_make_status_ (&pi, uc, 0);
138 }
139
140
141 /**
142  * Continuation called to notify client about result of the
143  * datastore removal operation.
144  *
145  * @param cls closure
146  * @param success GNUNET_SYSERR on failure
147  * @param msg NULL on success, otherwise an error message
148  */
149 static void
150 process_cont (void *cls, int success, const char *msg)
151 {
152   struct GNUNET_FS_UnindexContext *uc = cls;
153
154   if (success == GNUNET_SYSERR)
155   {
156     uc->emsg = GNUNET_strdup (msg);
157     signal_unindex_error (uc);
158     return;
159   }
160 #if DEBUG_UNINDEX
161   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
162               "Datastore REMOVE operation succeeded\n");
163 #endif
164   GNUNET_FS_tree_encoder_next (uc->tc);
165 }
166
167
168 /**
169  * Function called asking for the current (encoded)
170  * block to be processed.  After processing the
171  * client should either call "GNUNET_FS_tree_encode_next"
172  * or (on error) "GNUNET_FS_tree_encode_finish".
173  *
174  * @param cls closure
175  * @param chk content hash key for the block (key for lookup in the datastore)
176  * @param offset offset of the block
177  * @param depth depth of the block, 0 for DBLOCK
178  * @param type type of the block (IBLOCK or DBLOCK)
179  * @param block the (encrypted) block
180  * @param block_size size of block (in bytes)
181  */
182 static void
183 unindex_process (void *cls, const struct ContentHashKey *chk, uint64_t offset,
184                  unsigned int depth, enum GNUNET_BLOCK_Type type,
185                  const void *block, uint16_t block_size)
186 {
187   struct GNUNET_FS_UnindexContext *uc = cls;
188   uint32_t size;
189   const void *data;
190   struct OnDemandBlock odb;
191
192   if (type != GNUNET_BLOCK_TYPE_FS_DBLOCK)
193   {
194     size = block_size;
195     data = block;
196   }
197   else                          /* on-demand encoded DBLOCK */
198   {
199     size = sizeof (struct OnDemandBlock);
200     odb.offset = GNUNET_htonll (offset);
201     odb.file_id = uc->file_id;
202     data = &odb;
203   }
204 #if DEBUG_UNINDEX
205   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
206               "Sending REMOVE request to DATASTORE service\n");
207 #endif
208   GNUNET_DATASTORE_remove (uc->dsh, &chk->query, size, data, -2, 1,
209                            GNUNET_CONSTANTS_SERVICE_TIMEOUT, &process_cont, uc);
210 }
211
212
213 /**
214  * Function called with the response from the
215  * FS service to our unindexing request.
216  *
217  * @param cls closure, unindex context
218  * @param msg NULL on timeout, otherwise the response
219  */
220 static void
221 process_fs_response (void *cls, const struct GNUNET_MessageHeader *msg)
222 {
223   struct GNUNET_FS_UnindexContext *uc = cls;
224   struct GNUNET_FS_ProgressInfo pi;
225
226   if (uc->client != NULL)
227   {
228     GNUNET_CLIENT_disconnect (uc->client, GNUNET_NO);
229     uc->client = NULL;
230   }
231   if (uc->state != UNINDEX_STATE_FS_NOTIFY)
232   {
233     uc->state = UNINDEX_STATE_ERROR;
234     uc->emsg =
235         GNUNET_strdup (_("Unexpected time for a response from `fs' service."));
236     GNUNET_FS_unindex_sync_ (uc);
237     signal_unindex_error (uc);
238     return;
239   }
240   if (NULL == msg)
241   {
242     uc->state = UNINDEX_STATE_ERROR;
243     uc->emsg = GNUNET_strdup (_("Timeout waiting for `fs' service."));
244     GNUNET_FS_unindex_sync_ (uc);
245     signal_unindex_error (uc);
246     return;
247   }
248   if (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_UNINDEX_OK)
249   {
250     uc->state = UNINDEX_STATE_ERROR;
251     uc->emsg = GNUNET_strdup (_("Invalid response from `fs' service."));
252     GNUNET_FS_unindex_sync_ (uc);
253     signal_unindex_error (uc);
254     return;
255   }
256   uc->state = UNINDEX_STATE_COMPLETE;
257   pi.status = GNUNET_FS_STATUS_UNINDEX_COMPLETED;
258   pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
259   GNUNET_FS_unindex_sync_ (uc);
260   GNUNET_FS_unindex_make_status_ (&pi, uc, uc->file_size);
261 }
262
263
264 /**
265  * Function called when the tree encoder has
266  * processed all blocks.  Clean up.
267  *
268  * @param cls our unindexing context
269  * @param tc not used
270  */
271 static void
272 unindex_finish (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
273 {
274   struct GNUNET_FS_UnindexContext *uc = cls;
275   char *emsg;
276   struct GNUNET_FS_Uri *uri;
277   struct UnindexMessage req;
278
279   /* generate final progress message */
280   unindex_progress (uc, uc->file_size, NULL, 0, 0);
281   GNUNET_FS_tree_encoder_finish (uc->tc, &uri, &emsg);
282   uc->tc = NULL;
283   if (uri != NULL)
284     GNUNET_FS_uri_destroy (uri);
285   GNUNET_DISK_file_close (uc->fh);
286   uc->fh = NULL;
287   GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
288   uc->dsh = NULL;
289   uc->state = UNINDEX_STATE_FS_NOTIFY;
290   GNUNET_FS_unindex_sync_ (uc);
291   uc->client = GNUNET_CLIENT_connect ("fs", uc->h->cfg);
292   if (uc->client == NULL)
293   {
294     uc->state = UNINDEX_STATE_ERROR;
295     uc->emsg =
296         GNUNET_strdup (_("Failed to connect to FS service for unindexing."));
297     GNUNET_FS_unindex_sync_ (uc);
298     signal_unindex_error (uc);
299     return;
300   }
301 #if DEBUG_UNINDEX
302   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
303               "Sending UNINDEX message to FS service\n");
304 #endif
305   req.header.size = htons (sizeof (struct UnindexMessage));
306   req.header.type = htons (GNUNET_MESSAGE_TYPE_FS_UNINDEX);
307   req.reserved = 0;
308   req.file_id = uc->file_id;
309   GNUNET_break (GNUNET_OK ==
310                 GNUNET_CLIENT_transmit_and_get_response (uc->client,
311                                                          &req.header,
312                                                          GNUNET_CONSTANTS_SERVICE_TIMEOUT,
313                                                          GNUNET_YES,
314                                                          &process_fs_response,
315                                                          uc));
316 }
317
318
319 /**
320  * Connect to the datastore and remove the blocks.
321  *
322  * @param uc context for the unindex operation.
323  */
324 void
325 GNUNET_FS_unindex_do_remove_ (struct GNUNET_FS_UnindexContext *uc)
326 {
327   uc->dsh = GNUNET_DATASTORE_connect (uc->h->cfg);
328   if (NULL == uc->dsh)
329   {
330     uc->state = UNINDEX_STATE_ERROR;
331     uc->emsg = GNUNET_strdup (_("Failed to connect to `datastore' service."));
332     GNUNET_FS_unindex_sync_ (uc);
333     signal_unindex_error (uc);
334     return;
335   }
336   uc->fh =
337       GNUNET_DISK_file_open (uc->filename, GNUNET_DISK_OPEN_READ,
338                              GNUNET_DISK_PERM_NONE);
339   if (NULL == uc->fh)
340   {
341     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
342     uc->dsh = NULL;
343     uc->state = UNINDEX_STATE_ERROR;
344     uc->emsg = GNUNET_strdup (_("Failed to open file for unindexing."));
345     GNUNET_FS_unindex_sync_ (uc);
346     signal_unindex_error (uc);
347     return;
348   }
349   uc->tc =
350       GNUNET_FS_tree_encoder_create (uc->h, uc->file_size, uc, &unindex_reader,
351                                      &unindex_process, &unindex_progress,
352                                      &unindex_finish);
353   GNUNET_FS_tree_encoder_next (uc->tc);
354 }
355
356
357 /**
358  * Function called once the hash of the file
359  * that is being unindexed has been computed.
360  *
361  * @param cls closure, unindex context
362  * @param file_id computed hash, NULL on error
363  */
364 void
365 GNUNET_FS_unindex_process_hash_ (void *cls, const GNUNET_HashCode * file_id)
366 {
367   struct GNUNET_FS_UnindexContext *uc = cls;
368
369   uc->fhc = NULL;
370   if (uc->state != UNINDEX_STATE_HASHING)
371   {
372     GNUNET_FS_unindex_stop (uc);
373     return;
374   }
375   if (file_id == NULL)
376   {
377     uc->state = UNINDEX_STATE_ERROR;
378     uc->emsg = GNUNET_strdup (_("Failed to compute hash of file."));
379     GNUNET_FS_unindex_sync_ (uc);
380     signal_unindex_error (uc);
381     return;
382   }
383   uc->file_id = *file_id;
384   uc->state = UNINDEX_STATE_DS_REMOVE;
385   GNUNET_FS_unindex_sync_ (uc);
386   GNUNET_FS_unindex_do_remove_ (uc);
387 }
388
389
390 /**
391  * Create SUSPEND event for the given unindex operation
392  * and then clean up our state (without stop signal).
393  *
394  * @param cls the 'struct GNUNET_FS_UnindexContext' to signal for
395  */
396 void
397 GNUNET_FS_unindex_signal_suspend_ (void *cls)
398 {
399   struct GNUNET_FS_UnindexContext *uc = cls;
400   struct GNUNET_FS_ProgressInfo pi;
401
402   if (uc->fhc != NULL)
403   {
404     GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
405     uc->fhc = NULL;
406   }
407   if (uc->client != NULL)
408   {
409     GNUNET_CLIENT_disconnect (uc->client, GNUNET_NO);
410     uc->client = NULL;
411   }
412   if (NULL != uc->dsh)
413   {
414     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
415     uc->dsh = NULL;
416   }
417   if (NULL != uc->tc)
418   {
419     GNUNET_FS_tree_encoder_finish (uc->tc, NULL, NULL);
420     uc->tc = NULL;
421   }
422   if (uc->fh != NULL)
423   {
424     GNUNET_DISK_file_close (uc->fh);
425     uc->fh = NULL;
426   }
427   GNUNET_FS_end_top (uc->h, uc->top);
428   pi.status = GNUNET_FS_STATUS_UNINDEX_SUSPEND;
429   GNUNET_FS_unindex_make_status_ (&pi, uc,
430                                   (uc->state ==
431                                    UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
432   GNUNET_break (NULL == uc->client_info);
433   GNUNET_free (uc->filename);
434   GNUNET_free_non_null (uc->serialization);
435   GNUNET_free_non_null (uc->emsg);
436   GNUNET_free (uc);
437 }
438
439
440 /**
441  * Unindex a file.
442  *
443  * @param h handle to the file sharing subsystem
444  * @param filename file to unindex
445  * @param cctx initial value for the client context
446  * @return NULL on error, otherwise handle
447  */
448 struct GNUNET_FS_UnindexContext *
449 GNUNET_FS_unindex_start (struct GNUNET_FS_Handle *h, const char *filename,
450                          void *cctx)
451 {
452   struct GNUNET_FS_UnindexContext *ret;
453   struct GNUNET_FS_ProgressInfo pi;
454   uint64_t size;
455
456   if (GNUNET_OK != GNUNET_DISK_file_size (filename, &size, GNUNET_YES))
457     return NULL;
458   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_UnindexContext));
459   ret->h = h;
460   ret->filename = GNUNET_strdup (filename);
461   ret->start_time = GNUNET_TIME_absolute_get ();
462   ret->file_size = size;
463   ret->client_info = cctx;
464   GNUNET_FS_unindex_sync_ (ret);
465   pi.status = GNUNET_FS_STATUS_UNINDEX_START;
466   pi.value.unindex.eta = GNUNET_TIME_UNIT_FOREVER_REL;
467   GNUNET_FS_unindex_make_status_ (&pi, ret, 0);
468   ret->fhc =
469       GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_IDLE, filename,
470                                HASHING_BLOCKSIZE,
471                                &GNUNET_FS_unindex_process_hash_, ret);
472   ret->top = GNUNET_FS_make_top (h, &GNUNET_FS_unindex_signal_suspend_, ret);
473   return ret;
474 }
475
476
477 /**
478  * Clean up after completion of an unindex operation.
479  *
480  * @param uc handle
481  */
482 void
483 GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
484 {
485   struct GNUNET_FS_ProgressInfo pi;
486
487   if (uc->fhc != NULL)
488   {
489     GNUNET_CRYPTO_hash_file_cancel (uc->fhc);
490     uc->fhc = NULL;
491   }
492   if (uc->client != NULL)
493   {
494     GNUNET_CLIENT_disconnect (uc->client, GNUNET_NO);
495     uc->client = NULL;
496   }
497   if (NULL != uc->dsh)
498   {
499     GNUNET_DATASTORE_disconnect (uc->dsh, GNUNET_NO);
500     uc->dsh = NULL;
501   }
502   if (NULL != uc->tc)
503   {
504     GNUNET_FS_tree_encoder_finish (uc->tc, NULL, NULL);
505     uc->tc = NULL;
506   }
507   if (uc->fh != NULL)
508   {
509     GNUNET_DISK_file_close (uc->fh);
510     uc->fh = NULL;
511   }
512   GNUNET_FS_end_top (uc->h, uc->top);
513   if (uc->serialization != NULL)
514   {
515     GNUNET_FS_remove_sync_file_ (uc->h, GNUNET_FS_SYNC_PATH_MASTER_UNINDEX,
516                                  uc->serialization);
517     GNUNET_free (uc->serialization);
518     uc->serialization = NULL;
519   }
520   pi.status = GNUNET_FS_STATUS_UNINDEX_STOPPED;
521   pi.value.unindex.eta = GNUNET_TIME_UNIT_ZERO;
522   GNUNET_FS_unindex_make_status_ (&pi, uc,
523                                   (uc->state ==
524                                    UNINDEX_STATE_COMPLETE) ? uc->file_size : 0);
525   GNUNET_break (NULL == uc->client_info);
526   GNUNET_free (uc->filename);
527   GNUNET_free (uc);
528 }
529
530 /* end of fs_unindex.c */