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