hxing
[oweals/gnunet.git] / src / fs / fs_unindex.c
1 /*
2      This file is part of GNUnet.
3      (C) 2003, 2004, 2006 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 2, 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 Bennett
24  * @author Christian Grothoff
25  * @brief Unindex file.
26  *
27  * TODO:
28  * - code cleanup (share more with upload.c)
29  */
30
31 #include "platform.h"
32 #include "gnunet_fs_service.h"
33 #include "fs.h"
34
35 /**
36  * Iterate over all indexed files.
37  *
38  * @param h handle to the file sharing subsystem
39  * @param iterator function to call on each indexed file
40  * @param iterator_cls closure for iterator
41  */
42 void 
43 GNUNET_FS_get_indexed_files (struct GNUNET_FS_Handle *h,
44                              GNUNET_FS_IndexedFileProcessor iterator,
45                              void *iterator_cls)
46 {
47 }
48
49
50 /**
51  * Unindex a file.
52  *
53  * @param h handle to the file sharing subsystem
54  * @param filename file to unindex
55  * @return NULL on error, otherwise handle 
56  */
57 struct GNUNET_FS_UnindexContext *
58 GNUNET_FS_unindex (struct GNUNET_FS_Handle *h,
59                    const char *filename)
60 {
61   return NULL;
62 }
63
64
65 /**
66  * Clean up after completion of an unindex operation.
67  *
68  * @param uc handle
69  */
70 void
71 GNUNET_FS_unindex_stop (struct GNUNET_FS_UnindexContext *uc)
72 {
73 }
74
75
76
77 #if 0
78
79 #define STRICT_CHECKS GNUNET_NO
80
81 /**
82  * Append the given key and query to the iblock[level].
83  * If iblock[level] is already full, compute its chk
84  * and push it to level+1.  iblocks is guaranteed to
85  * be big enough.
86  *
87  * This function matches exactly upload.c::pushBlock,
88  * except in the call to 'GNUNET_FS_delete'.  TODO: refactor
89  * to avoid code duplication (move to block.c, pass
90  * GNUNET_FS_delete as argument!).
91  */
92 static int
93 pushBlock (struct GNUNET_ClientServerConnection *sock,
94            const GNUNET_EC_ContentHashKey * chk, unsigned int level,
95            GNUNET_DatastoreValue ** iblocks)
96 {
97   unsigned int size;
98   unsigned int present;
99   GNUNET_DatastoreValue *value;
100   GNUNET_EC_DBlock *db;
101   GNUNET_EC_ContentHashKey ichk;
102
103   size = ntohl (iblocks[level]->size) - sizeof (GNUNET_DatastoreValue);
104   present =
105     (size - sizeof (GNUNET_EC_DBlock)) / sizeof (GNUNET_EC_ContentHashKey);
106   db = (GNUNET_EC_DBlock *) & iblocks[level][1];
107   if (present == GNUNET_ECRS_CHK_PER_INODE)
108     {
109       GNUNET_EC_file_block_get_key (db, size, &ichk.key);
110       GNUNET_EC_file_block_get_query (db, size, &ichk.query);
111       if (GNUNET_OK != pushBlock (sock, &ichk, level + 1, iblocks))
112         {
113           GNUNET_GE_BREAK (NULL, 0);
114           return GNUNET_SYSERR;
115         }
116       GNUNET_EC_file_block_encode (db, size, &ichk.query, &value);
117 #if STRICT_CHECKS
118       if (GNUNET_SYSERR == GNUNET_FS_delete (sock, value))
119         {
120           GNUNET_free (value);
121           GNUNET_GE_BREAK (NULL, 0);
122           return GNUNET_SYSERR;
123         }
124 #else
125       GNUNET_FS_delete (sock, value);
126 #endif
127       GNUNET_free (value);
128       size = sizeof (GNUNET_EC_DBlock);
129     }
130   /* append GNUNET_EC_ContentHashKey */
131   memcpy (&((char *) db)[size], chk, sizeof (GNUNET_EC_ContentHashKey));
132   iblocks[level]->size = htonl (size +
133                                 sizeof (GNUNET_EC_ContentHashKey) +
134                                 sizeof (GNUNET_DatastoreValue));
135   return GNUNET_OK;
136 }
137
138
139
140 /**
141  * Undo sym-linking operation:
142  * a) check if we have a symlink
143  * b) delete symbolic link
144  */
145 static int
146 undoSymlinking (struct GNUNET_GE_Context *ectx,
147                 const char *fn,
148                 const GNUNET_HashCode * fileId,
149                 struct GNUNET_ClientServerConnection *sock)
150 {
151   GNUNET_EncName enc;
152   char *serverDir;
153   char *serverFN;
154   struct stat buf;
155
156 #ifndef S_ISLNK
157   if (1)
158     return GNUNET_OK;           /* symlinks do not exist? */
159 #endif
160   if (0 != LSTAT (fn, &buf))
161     {
162       GNUNET_GE_LOG_STRERROR_FILE (ectx,
163                                    GNUNET_GE_ERROR | GNUNET_GE_BULK |
164                                    GNUNET_GE_USER | GNUNET_GE_ADMIN, "stat",
165                                    fn);
166       return GNUNET_SYSERR;
167     }
168 #ifdef S_ISLNK
169   if (!S_ISLNK (buf.st_mode))
170     return GNUNET_OK;
171 #endif
172   serverDir =
173     GNUNET_get_daemon_configuration_value (sock, "FS", "INDEX-DIRECTORY");
174   if (serverDir == NULL)
175     return GNUNET_OK;
176   serverFN = GNUNET_malloc (strlen (serverDir) + 2 + sizeof (GNUNET_EncName));
177   strcpy (serverFN, serverDir);
178   GNUNET_free (serverDir);
179   if (serverFN[strlen (serverFN) - 1] != DIR_SEPARATOR)
180     strcat (serverFN, DIR_SEPARATOR_STR);
181   GNUNET_hash_to_enc (fileId, &enc);
182   strcat (serverFN, (char *) &enc);
183
184   if (0 != UNLINK (serverFN))
185     {
186       GNUNET_GE_LOG_STRERROR_FILE (ectx,
187                                    GNUNET_GE_ERROR | GNUNET_GE_BULK |
188                                    GNUNET_GE_USER | GNUNET_GE_ADMIN, "unlink",
189                                    serverFN);
190       GNUNET_free (serverFN);
191       return GNUNET_SYSERR;
192     }
193   GNUNET_free (serverFN);
194   return GNUNET_OK;
195 }
196
197
198
199 /**
200  * Unindex a file.
201  *
202  * @return GNUNET_SYSERR if the unindexing failed (i.e. not indexed)
203  */
204 int
205 GNUNET_ECRS_file_unindex (struct GNUNET_GE_Context *ectx,
206                           struct GNUNET_GC_Configuration *cfg,
207                           const char *filename,
208                           GNUNET_ECRS_UploadProgressCallback upcb,
209                           void *upcbClosure, GNUNET_ECRS_TestTerminate tt,
210                           void *ttClosure)
211 {
212   unsigned long long filesize;
213   unsigned long long pos;
214   unsigned int treedepth;
215   int fd;
216   int i;
217   unsigned int size;
218   GNUNET_DatastoreValue **iblocks;
219   GNUNET_DatastoreValue *dblock;
220   GNUNET_EC_DBlock *db;
221   GNUNET_DatastoreValue *value;
222   struct GNUNET_ClientServerConnection *sock;
223   GNUNET_HashCode fileId;
224   GNUNET_EC_ContentHashKey chk;
225   GNUNET_CronTime eta;
226   GNUNET_CronTime start;
227   GNUNET_CronTime now;
228   int wasIndexed;
229
230   start = GNUNET_get_time ();
231   if (GNUNET_YES != GNUNET_disk_file_test (ectx, filename))
232     {
233       GNUNET_GE_BREAK (ectx, 0);
234       return GNUNET_SYSERR;
235     }
236   if (GNUNET_OK !=
237       GNUNET_disk_file_size (ectx, filename, &filesize, GNUNET_YES))
238     return GNUNET_SYSERR;
239   sock = GNUNET_client_connection_create (ectx, cfg);
240   if (sock == NULL)
241     return GNUNET_SYSERR;
242   eta = 0;
243   if (upcb != NULL)
244     upcb (filesize, 0, eta, upcbClosure);
245   if (GNUNET_SYSERR == GNUNET_hash_file (ectx, filename, &fileId))
246     {
247       GNUNET_client_connection_destroy (sock);
248       GNUNET_GE_BREAK (ectx, 0);
249       return GNUNET_SYSERR;
250     }
251   now = GNUNET_get_time ();
252   eta = now + 2 * (now - start);
253   /* very rough estimate: GNUNET_hash reads once through the file,
254      we'll do that once more and write it.  But of course
255      the second read may be cached, and we have the encryption,
256      so a factor of two is really, really just a rough estimate */
257   start = now;
258   /* reset the counter since the formula later does not
259      take the time for GNUNET_hash_file into account */
260   treedepth = GNUNET_ECRS_compute_depth (filesize);
261
262   /* Test if file is indexed! */
263   wasIndexed = GNUNET_FS_test_indexed (sock, &fileId);
264
265   fd = GNUNET_disk_file_open (ectx, filename, O_RDONLY | O_LARGEFILE);
266   if (fd == -1)
267     {
268       GNUNET_client_connection_destroy (sock);
269       return GNUNET_SYSERR;
270     }
271   dblock =
272     GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
273                    sizeof (GNUNET_EC_DBlock));
274   dblock->size =
275     htonl (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
276            sizeof (GNUNET_EC_DBlock));
277   dblock->anonymity_level = htonl (0);
278   dblock->priority = htonl (0);
279   dblock->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
280   dblock->expiration_time = GNUNET_htonll (0);
281   db = (GNUNET_EC_DBlock *) & dblock[1];
282   db->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
283   iblocks =
284     GNUNET_malloc (sizeof (GNUNET_DatastoreValue *) * (treedepth + 1));
285   for (i = 0; i <= treedepth; i++)
286     {
287       iblocks[i] =
288         GNUNET_malloc (sizeof (GNUNET_DatastoreValue) +
289                        GNUNET_ECRS_IBLOCK_SIZE + sizeof (GNUNET_EC_DBlock));
290       iblocks[i]->size =
291         htonl (sizeof (GNUNET_DatastoreValue) + sizeof (GNUNET_EC_DBlock));
292       iblocks[i]->anonymity_level = htonl (0);
293       iblocks[i]->priority = htonl (0);
294       iblocks[i]->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
295       iblocks[i]->expiration_time = GNUNET_htonll (0);
296       ((GNUNET_EC_DBlock *) & iblocks[i][1])->type =
297         htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
298     }
299
300   pos = 0;
301   while (pos < filesize)
302     {
303       if (upcb != NULL)
304         upcb (filesize, pos, eta, upcbClosure);
305       if (tt != NULL)
306         if (GNUNET_OK != tt (ttClosure))
307           goto FAILURE;
308       size = GNUNET_ECRS_DBLOCK_SIZE;
309       if (size > filesize - pos)
310         {
311           size = filesize - pos;
312           memset (&db[1], 0, GNUNET_ECRS_DBLOCK_SIZE);
313         }
314       dblock->size =
315         htonl (sizeof (GNUNET_DatastoreValue) + size +
316                sizeof (GNUNET_EC_DBlock));
317       if (size != READ (fd, &db[1], size))
318         {
319           GNUNET_GE_LOG_STRERROR_FILE (ectx,
320                                        GNUNET_GE_ERROR | GNUNET_GE_USER |
321                                        GNUNET_GE_ADMIN | GNUNET_GE_BULK,
322                                        "READ", filename);
323           goto FAILURE;
324         }
325       if (tt != NULL)
326         if (GNUNET_OK != tt (ttClosure))
327           goto FAILURE;
328       GNUNET_EC_file_block_get_key (db, size + sizeof (GNUNET_EC_DBlock),
329                                     &chk.key);
330       GNUNET_EC_file_block_get_query (db, size + sizeof (GNUNET_EC_DBlock),
331                                       &chk.query);
332       if (GNUNET_OK != pushBlock (sock, &chk, 0,        /* dblocks are on level 0 */
333                                   iblocks))
334         {
335           GNUNET_GE_BREAK (ectx, 0);
336           goto FAILURE;
337         }
338       if (!wasIndexed)
339         {
340           if (GNUNET_OK ==
341               GNUNET_EC_file_block_encode (db, size, &chk.query, &value))
342             {
343               *value = *dblock; /* copy options! */
344 #if STRICT_CHECKS
345               if (GNUNET_OK != GNUNET_FS_delete (sock, value))
346                 {
347                   GNUNET_free (value);
348                   GNUNET_GE_BREAK (ectx, 0);
349                   goto FAILURE;
350                 }
351 #else
352               GNUNET_FS_delete (sock, value);
353 #endif
354               GNUNET_free (value);
355             }
356           else
357             {
358               goto FAILURE;
359             }
360         }
361       pos += size;
362       now = GNUNET_get_time ();
363       eta = (GNUNET_CronTime) (start +
364                                (((double) (now - start) / (double) pos))
365                                * (double) filesize);
366     }
367   if (tt != NULL)
368     if (GNUNET_OK != tt (ttClosure))
369       goto FAILURE;
370   for (i = 0; i < treedepth; i++)
371     {
372       size = ntohl (iblocks[i]->size) - sizeof (GNUNET_DatastoreValue);
373       db = (GNUNET_EC_DBlock *) & iblocks[i][1];
374       GNUNET_EC_file_block_get_key (db, size, &chk.key);
375       GNUNET_EC_file_block_get_query (db, size, &chk.query);
376       if (GNUNET_OK != pushBlock (sock, &chk, i + 1, iblocks))
377         {
378           GNUNET_GE_BREAK (ectx, 0);
379           goto FAILURE;
380         }
381       GNUNET_EC_file_block_encode (db, size, &chk.query, &value);
382 #if STRICT_CHECKS
383       if (GNUNET_OK != GNUNET_FS_delete (sock, value))
384         {
385           GNUNET_free (value);
386           GNUNET_GE_BREAK (ectx, 0);
387           goto FAILURE;
388         }
389 #else
390       GNUNET_FS_delete (sock, value);
391 #endif
392       GNUNET_free (value);
393       GNUNET_free (iblocks[i]);
394       iblocks[i] = NULL;
395     }
396
397   if (wasIndexed)
398     {
399       if (GNUNET_OK == undoSymlinking (ectx, filename, &fileId, sock))
400         {
401           if (GNUNET_OK !=
402               GNUNET_FS_unindex (sock, GNUNET_ECRS_DBLOCK_SIZE, &fileId))
403             {
404               GNUNET_GE_BREAK (ectx, 0);
405               goto FAILURE;
406             }
407         }
408       else
409         {
410           GNUNET_GE_BREAK (ectx, 0);
411           goto FAILURE;
412         }
413     }
414   GNUNET_free (iblocks[treedepth]);
415   /* free resources */
416   GNUNET_free (iblocks);
417   GNUNET_free (dblock);
418   CLOSE (fd);
419   GNUNET_client_connection_destroy (sock);
420   return GNUNET_OK;
421 FAILURE:
422   for (i = 0; i <= treedepth; i++)
423     GNUNET_free_non_null (iblocks[i]);
424   GNUNET_free (iblocks);
425   GNUNET_free (dblock);
426   CLOSE (fd);
427   GNUNET_client_connection_destroy (sock);
428   return GNUNET_SYSERR;
429 }
430
431 #endif 
432
433 /* end of fs_unindex.c */