stuff
[oweals/gnunet.git] / src / fs / fs_publish.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2003, 2004, 2005, 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 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_publish.c
23  * @brief publish a file or directory in GNUnet
24  * @see http://gnunet.org/encoding.php3
25  * @author Krista Bennett
26  * @author Christian Grothoff
27  */
28
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_fs_service.h"
32 #include "fs.h"
33
34 #define DEBUG_PUBLISH GNUNET_YES
35
36
37
38 #if 0
39
40 /**
41  * Append the given key and query to the iblock[level].  If
42  * iblock[level] is already full, compute its chk and push it to
43  * level+1 and clear the level.  iblocks is guaranteed to be big
44  * enough.
45  */
46 static int
47 pushBlock (struct GNUNET_ClientServerConnection *sock,
48            const GNUNET_EC_ContentHashKey * chk,
49            unsigned int level,
50            GNUNET_DatastoreValue ** iblocks,
51            unsigned int prio, GNUNET_CronTime expirationTime)
52 {
53   unsigned int size;
54   unsigned int present;
55   GNUNET_DatastoreValue *value;
56   GNUNET_EC_DBlock *db;
57   GNUNET_EC_ContentHashKey ichk;
58
59   size = ntohl (iblocks[level]->size);
60   GNUNET_GE_ASSERT (NULL, size > sizeof (GNUNET_DatastoreValue));
61   size -= sizeof (GNUNET_DatastoreValue);
62   GNUNET_GE_ASSERT (NULL,
63                     size - sizeof (GNUNET_EC_DBlock) <=
64                     GNUNET_ECRS_IBLOCK_SIZE);
65   present =
66     (size - sizeof (GNUNET_EC_DBlock)) / sizeof (GNUNET_EC_ContentHashKey);
67   db = (GNUNET_EC_DBlock *) & iblocks[level][1];
68   if (present == GNUNET_ECRS_CHK_PER_INODE)
69     {
70       GNUNET_EC_file_block_get_key (db, size, &ichk.key);
71       GNUNET_EC_file_block_get_query (db, size, &ichk.query);
72       if (GNUNET_OK != pushBlock (sock,
73                                   &ichk, level + 1, iblocks, prio,
74                                   expirationTime))
75         return GNUNET_SYSERR;
76       GNUNET_EC_file_block_encode (db, size, &ichk.query, &value);
77       if (value == NULL)
78         {
79           GNUNET_GE_BREAK (NULL, 0);
80           return GNUNET_SYSERR;
81         }
82       value->priority = htonl (prio);
83       value->expiration_time = GNUNET_htonll (expirationTime);
84       if (GNUNET_OK != GNUNET_FS_insert (sock, value))
85         {
86           GNUNET_free (value);
87           return GNUNET_SYSERR;
88         }
89       GNUNET_free (value);
90       size = sizeof (GNUNET_EC_DBlock); /* type */
91     }
92   /* append GNUNET_EC_ContentHashKey */
93   memcpy (&((char *) db)[size], chk, sizeof (GNUNET_EC_ContentHashKey));
94   size += sizeof (GNUNET_EC_ContentHashKey) + sizeof (GNUNET_DatastoreValue);
95   GNUNET_GE_ASSERT (NULL, size < GNUNET_MAX_BUFFER_SIZE);
96   iblocks[level]->size = htonl (size);
97
98   return GNUNET_OK;
99 }
100
101 /**
102  * Index or insert a file.
103  *
104  * @param priority what is the priority for OUR node to
105  *   keep this file available?  Use 0 for maximum anonymity and
106  *   minimum reliability...
107  * @param doIndex GNUNET_YES for index, GNUNET_NO for insertion,
108  *         GNUNET_SYSERR for simulation
109  * @param uri set to the URI of the uploaded file
110  * @return GNUNET_SYSERR if the upload failed (i.e. not enough space
111  *  or gnunetd not running)
112  */
113 int
114 GNUNET_ECRS_file_upload (struct GNUNET_GE_Context *ectx,
115                          struct GNUNET_GC_Configuration *cfg,
116                          const char *filename,
117                          int doIndex,
118                          unsigned int anonymityLevel,
119                          unsigned int priority,
120                          GNUNET_CronTime expirationTime,
121                          GNUNET_ECRS_UploadProgressCallback upcb,
122                          void *upcbClosure,
123                          GNUNET_ECRS_TestTerminate tt,
124                          void *ttClosure, struct GNUNET_ECRS_URI **uri)
125 {
126   unsigned long long filesize;
127   unsigned long long pos;
128   unsigned int treedepth;
129   int fd;
130   int i;
131   int ret;
132   unsigned int size;
133   GNUNET_DatastoreValue **iblocks;
134   GNUNET_DatastoreValue *dblock;
135   GNUNET_EC_DBlock *db;
136   GNUNET_DatastoreValue *value;
137   struct GNUNET_ClientServerConnection *sock;
138   GNUNET_HashCode fileId;
139   GNUNET_EC_ContentHashKey mchk;
140   GNUNET_CronTime eta;
141   GNUNET_CronTime start;
142   GNUNET_CronTime now;
143   GNUNET_EC_FileIdentifier fid;
144 #if DEBUG_UPLOAD
145   GNUNET_EncName enc;
146 #endif
147
148   GNUNET_GE_ASSERT (ectx, cfg != NULL);
149   start = GNUNET_get_time ();
150   memset (&mchk, 0, sizeof (GNUNET_EC_ContentHashKey));
151   if (GNUNET_YES != GNUNET_disk_file_test (ectx, filename))
152     {
153       GNUNET_GE_LOG (ectx,
154                      GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
155                      _("`%s' is not a file.\n"), filename);
156       return GNUNET_SYSERR;
157     }
158   if (GNUNET_OK !=
159       GNUNET_disk_file_size (ectx, filename, &filesize, GNUNET_YES))
160     {
161       GNUNET_GE_LOG (ectx,
162                      GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
163                      _("Cannot get size of file `%s'"), filename);
164
165       return GNUNET_SYSERR;
166     }
167   sock = GNUNET_client_connection_create (ectx, cfg);
168   if (sock == NULL)
169     {
170       GNUNET_GE_LOG (ectx,
171                      GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
172                      _("Failed to connect to gnunetd."));
173       return GNUNET_SYSERR;
174     }
175   eta = 0;
176   if (upcb != NULL)
177     upcb (filesize, 0, eta, upcbClosure);
178   if (doIndex == GNUNET_YES)
179     {
180       if (GNUNET_SYSERR == GNUNET_hash_file (ectx, filename, &fileId))
181         {
182           GNUNET_GE_LOG (ectx,
183                          GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
184                          _("Cannot hash `%s'.\n"), filename);
185
186           GNUNET_client_connection_destroy (sock);
187           return GNUNET_SYSERR;
188         }
189       if (GNUNET_YES == GNUNET_FS_test_indexed (sock, &fileId))
190         {
191           /* file already indexed; simulate only to get the URI! */
192           doIndex = GNUNET_SYSERR;
193         }
194     }
195   if (doIndex == GNUNET_YES)
196     {
197       now = GNUNET_get_time ();
198       eta = now + 2 * (now - start);
199       /* very rough estimate: GNUNET_hash reads once through the file,
200          we'll do that once more and write it.  But of course
201          the second read may be cached, and we have the encryption,
202          so a factor of two is really, really just a rough estimate */
203       start = now;
204       /* reset the counter since the formula later does not
205          take the time for GNUNET_hash_file into account */
206
207       switch (GNUNET_FS_prepare_to_index (sock, &fileId, filename))
208         {
209         case GNUNET_SYSERR:
210           GNUNET_GE_LOG (ectx,
211                          GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
212                          _("Initialization for indexing file `%s' failed.\n"),
213                          filename);
214           GNUNET_client_connection_destroy (sock);
215           return GNUNET_SYSERR;
216         case GNUNET_NO:
217           GNUNET_GE_LOG (ectx,
218                          GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
219                          _
220                          ("Indexing file `%s' failed. Suggestion: try to insert the file.\n"),
221                          filename);
222           GNUNET_client_connection_destroy (sock);
223           return GNUNET_SYSERR;
224         default:
225           break;
226         }
227     }
228   treedepth = GNUNET_ECRS_compute_depth (filesize);
229   fd = GNUNET_disk_file_open (ectx, filename, O_RDONLY | O_LARGEFILE);
230   if (fd == -1)
231     {
232       GNUNET_GE_LOG (ectx,
233                      GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
234                      _("Cannot open file `%s': `%s'"), filename,
235                      STRERROR (errno));
236
237       GNUNET_client_connection_destroy (sock);
238       return GNUNET_SYSERR;
239     }
240
241   dblock =
242     GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
243                    sizeof (GNUNET_EC_DBlock));
244   dblock->size =
245     htonl (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
246            sizeof (GNUNET_EC_DBlock));
247   dblock->anonymity_level = htonl (anonymityLevel);
248   dblock->priority = htonl (priority);
249   dblock->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
250   dblock->expiration_time = GNUNET_htonll (expirationTime);
251   db = (GNUNET_EC_DBlock *) & dblock[1];
252   db->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
253   iblocks =
254     GNUNET_malloc (sizeof (GNUNET_DatastoreValue *) * (treedepth + 1));
255   for (i = 0; i <= treedepth; i++)
256     {
257       iblocks[i] =
258         GNUNET_malloc (sizeof (GNUNET_DatastoreValue) +
259                        GNUNET_ECRS_IBLOCK_SIZE + sizeof (GNUNET_EC_DBlock));
260       iblocks[i]->size =
261         htonl (sizeof (GNUNET_DatastoreValue) + sizeof (GNUNET_EC_DBlock));
262       iblocks[i]->anonymity_level = htonl (anonymityLevel);
263       iblocks[i]->priority = htonl (priority);
264       iblocks[i]->type = htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
265       iblocks[i]->expiration_time = GNUNET_htonll (expirationTime);
266       ((GNUNET_EC_DBlock *) & iblocks[i][1])->type =
267         htonl (GNUNET_ECRS_BLOCKTYPE_DATA);
268     }
269
270   pos = 0;
271   while (pos < filesize)
272     {
273       if (upcb != NULL)
274         upcb (filesize, pos, eta, upcbClosure);
275       if (tt != NULL)
276         if (GNUNET_OK != tt (ttClosure))
277           goto FAILURE;
278       size = GNUNET_ECRS_DBLOCK_SIZE;
279       if (size > filesize - pos)
280         {
281           size = filesize - pos;
282           memset (&db[1], 0, GNUNET_ECRS_DBLOCK_SIZE);
283         }
284       GNUNET_GE_ASSERT (ectx,
285                         sizeof (GNUNET_DatastoreValue) + size +
286                         sizeof (GNUNET_EC_DBlock) < GNUNET_MAX_BUFFER_SIZE);
287       dblock->size =
288         htonl (sizeof (GNUNET_DatastoreValue) + size +
289                sizeof (GNUNET_EC_DBlock));
290       if (size != READ (fd, &db[1], size))
291         {
292           GNUNET_GE_LOG_STRERROR_FILE (ectx,
293                                        GNUNET_GE_ERROR | GNUNET_GE_BULK |
294                                        GNUNET_GE_ADMIN | GNUNET_GE_USER,
295                                        "READ", filename);
296           goto FAILURE;
297         }
298       if (tt != NULL)
299         if (GNUNET_OK != tt (ttClosure))
300           goto FAILURE;
301       GNUNET_EC_file_block_get_key (db, size + sizeof (GNUNET_EC_DBlock),
302                                     &mchk.key);
303       GNUNET_EC_file_block_get_query (db, size + sizeof (GNUNET_EC_DBlock),
304                                       &mchk.query);
305 #if DEBUG_UPLOAD
306       GNUNET_hash_to_enc (&mchk.query, &enc);
307       fprintf (stderr,
308                "Query for current block of size %u is `%s'\n", size,
309                (const char *) &enc);
310 #endif
311       if (doIndex == GNUNET_YES)
312         {
313           if (GNUNET_SYSERR == GNUNET_FS_index (sock, &fileId, dblock, pos))
314             {
315               GNUNET_GE_LOG (ectx,
316                              GNUNET_GE_ERROR | GNUNET_GE_BULK |
317                              GNUNET_GE_USER,
318                              _
319                              ("Indexing data of file `%s' failed at position %llu.\n"),
320                              filename, pos);
321               goto FAILURE;
322             }
323         }
324       else
325         {
326           value = NULL;
327           if (GNUNET_OK !=
328               GNUNET_EC_file_block_encode (db,
329                                            size + sizeof (GNUNET_EC_DBlock),
330                                            &mchk.query, &value))
331             {
332               GNUNET_GE_BREAK (ectx, 0);
333               goto FAILURE;
334             }
335           GNUNET_GE_ASSERT (ectx, value != NULL);
336           *value = *dblock;     /* copy options! */
337           if ((doIndex == GNUNET_NO) &&
338               (GNUNET_OK != (ret = GNUNET_FS_insert (sock, value))))
339             {
340               GNUNET_GE_BREAK (ectx, ret == GNUNET_NO);
341               GNUNET_free (value);
342               goto FAILURE;
343             }
344           GNUNET_free (value);
345         }
346       pos += size;
347       now = GNUNET_get_time ();
348       if (pos > 0)
349         {
350           eta = (GNUNET_CronTime) (start +
351                                    (((double) (now - start) / (double) pos))
352                                    * (double) filesize);
353         }
354       if (GNUNET_OK != pushBlock (sock, &mchk, 0,       /* dblocks are on level 0 */
355                                   iblocks, priority, expirationTime))
356         goto FAILURE;
357     }
358   if (tt != NULL)
359     if (GNUNET_OK != tt (ttClosure))
360       goto FAILURE;
361 #if DEBUG_UPLOAD
362   GNUNET_GE_LOG (ectx,
363                  GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
364                  "Tree depth is %u, walking up tree.\n", treedepth);
365 #endif
366   for (i = 0; i < treedepth; i++)
367     {
368       size = ntohl (iblocks[i]->size) - sizeof (GNUNET_DatastoreValue);
369       GNUNET_GE_ASSERT (ectx, size < GNUNET_MAX_BUFFER_SIZE);
370       if (size == sizeof (GNUNET_EC_DBlock))
371         {
372 #if DEBUG_UPLOAD
373           GNUNET_GE_LOG (ectx,
374                          GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
375                          "Level %u is empty\n", i);
376 #endif
377           continue;
378         }
379       db = (GNUNET_EC_DBlock *) & iblocks[i][1];
380       GNUNET_EC_file_block_get_key (db, size, &mchk.key);
381 #if DEBUG_UPLOAD
382       GNUNET_GE_LOG (ectx,
383                      GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
384                      "Computing query for %u bytes content.\n", size);
385 #endif
386       GNUNET_EC_file_block_get_query (db, size, &mchk.query);
387 #if DEBUG_UPLOAD
388       IF_GELOG (ectx,
389                 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
390                 GNUNET_hash_to_enc (&mchk.query, &enc));
391       GNUNET_GE_LOG (ectx,
392                      GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
393                      "Query for current block at level %u is `%s'.\n", i,
394                      &enc);
395 #endif
396       if (GNUNET_OK != pushBlock (sock,
397                                   &mchk, i + 1, iblocks, priority,
398                                   expirationTime))
399         {
400           GNUNET_GE_BREAK (ectx, 0);
401           goto FAILURE;
402         }
403       GNUNET_EC_file_block_encode (db, size, &mchk.query, &value);
404       if (value == NULL)
405         {
406           GNUNET_GE_BREAK (ectx, 0);
407           goto FAILURE;
408         }
409       value->expiration_time = GNUNET_htonll (expirationTime);
410       value->priority = htonl (priority);
411       if ((doIndex != GNUNET_SYSERR) &&
412           (GNUNET_SYSERR == GNUNET_FS_insert (sock, value)))
413         {
414           GNUNET_GE_BREAK (ectx, 0);
415           GNUNET_free (value);
416           goto FAILURE;
417         }
418       GNUNET_free (value);
419       GNUNET_free (iblocks[i]);
420       iblocks[i] = NULL;
421     }
422 #if DEBUG_UPLOAD
423   IF_GELOG (ectx,
424             GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
425             GNUNET_hash_to_enc (&mchk.query, &enc));
426   GNUNET_GE_LOG (ectx, GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
427                  "Query for top block is %s\n", &enc);
428 #endif
429   /* build URI */
430   fid.file_length = GNUNET_htonll (filesize);
431   db = (GNUNET_EC_DBlock *) & iblocks[treedepth][1];
432
433   fid.chk = *(GNUNET_EC_ContentHashKey *) & (db[1]);
434   *uri = GNUNET_malloc (sizeof (URI));
435   (*uri)->type = chk;
436   (*uri)->data.fi = fid;
437
438   /* free resources */
439   GNUNET_free_non_null (iblocks[treedepth]);
440   GNUNET_free (iblocks);
441   GNUNET_free (dblock);
442   if (upcb != NULL)
443     upcb (filesize, filesize, eta, upcbClosure);
444   CLOSE (fd);
445   GNUNET_client_connection_destroy (sock);
446   return GNUNET_OK;
447 FAILURE:
448   for (i = 0; i <= treedepth; i++)
449     GNUNET_free_non_null (iblocks[i]);
450   GNUNET_free (iblocks);
451   GNUNET_free (dblock);
452   CLOSE (fd);
453   GNUNET_client_connection_destroy (sock);
454   return GNUNET_SYSERR;
455 }
456
457 #endif 
458
459 /* end of fs_publish.c */