2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
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.
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.
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.
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
30 #include "gnunet_util_lib.h"
31 #include "gnunet_fs_service.h"
34 #define DEBUG_PUBLISH GNUNET_YES
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
47 pushBlock (struct GNUNET_ClientServerConnection *sock,
48 const GNUNET_EC_ContentHashKey * chk,
50 GNUNET_DatastoreValue ** iblocks,
51 unsigned int prio, GNUNET_CronTime expirationTime)
55 GNUNET_DatastoreValue *value;
57 GNUNET_EC_ContentHashKey ichk;
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);
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)
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,
76 GNUNET_EC_file_block_encode (db, size, &ichk.query, &value);
79 GNUNET_GE_BREAK (NULL, 0);
82 value->priority = htonl (prio);
83 value->expiration_time = GNUNET_htonll (expirationTime);
84 if (GNUNET_OK != GNUNET_FS_insert (sock, value))
90 size = sizeof (GNUNET_EC_DBlock); /* type */
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);
102 * Index or insert a file.
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)
114 GNUNET_ECRS_file_upload (struct GNUNET_GE_Context *ectx,
115 struct GNUNET_GC_Configuration *cfg,
116 const char *filename,
118 unsigned int anonymityLevel,
119 unsigned int priority,
120 GNUNET_CronTime expirationTime,
121 GNUNET_ECRS_UploadProgressCallback upcb,
123 GNUNET_ECRS_TestTerminate tt,
124 void *ttClosure, struct GNUNET_ECRS_URI **uri)
126 unsigned long long filesize;
127 unsigned long long pos;
128 unsigned int treedepth;
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;
141 GNUNET_CronTime start;
143 GNUNET_EC_FileIdentifier fid;
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))
154 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
155 _("`%s' is not a file.\n"), filename);
156 return GNUNET_SYSERR;
159 GNUNET_disk_file_size (ectx, filename, &filesize, GNUNET_YES))
162 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
163 _("Cannot get size of file `%s'"), filename);
165 return GNUNET_SYSERR;
167 sock = GNUNET_client_connection_create (ectx, cfg);
171 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
172 _("Failed to connect to gnunetd."));
173 return GNUNET_SYSERR;
177 upcb (filesize, 0, eta, upcbClosure);
178 if (doIndex == GNUNET_YES)
180 if (GNUNET_SYSERR == GNUNET_hash_file (ectx, filename, &fileId))
183 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
184 _("Cannot hash `%s'.\n"), filename);
186 GNUNET_client_connection_destroy (sock);
187 return GNUNET_SYSERR;
189 if (GNUNET_YES == GNUNET_FS_test_indexed (sock, &fileId))
191 /* file already indexed; simulate only to get the URI! */
192 doIndex = GNUNET_SYSERR;
195 if (doIndex == GNUNET_YES)
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 */
204 /* reset the counter since the formula later does not
205 take the time for GNUNET_hash_file into account */
207 switch (GNUNET_FS_prepare_to_index (sock, &fileId, filename))
211 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
212 _("Initialization for indexing file `%s' failed.\n"),
214 GNUNET_client_connection_destroy (sock);
215 return GNUNET_SYSERR;
218 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
220 ("Indexing file `%s' failed. Suggestion: try to insert the file.\n"),
222 GNUNET_client_connection_destroy (sock);
223 return GNUNET_SYSERR;
228 treedepth = GNUNET_ECRS_compute_depth (filesize);
229 fd = GNUNET_disk_file_open (ectx, filename, O_RDONLY | O_LARGEFILE);
233 GNUNET_GE_ERROR | GNUNET_GE_BULK | GNUNET_GE_USER,
234 _("Cannot open file `%s': `%s'"), filename,
237 GNUNET_client_connection_destroy (sock);
238 return GNUNET_SYSERR;
242 GNUNET_malloc (sizeof (GNUNET_DatastoreValue) + GNUNET_ECRS_DBLOCK_SIZE +
243 sizeof (GNUNET_EC_DBlock));
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);
254 GNUNET_malloc (sizeof (GNUNET_DatastoreValue *) * (treedepth + 1));
255 for (i = 0; i <= treedepth; i++)
258 GNUNET_malloc (sizeof (GNUNET_DatastoreValue) +
259 GNUNET_ECRS_IBLOCK_SIZE + sizeof (GNUNET_EC_DBlock));
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);
271 while (pos < filesize)
274 upcb (filesize, pos, eta, upcbClosure);
276 if (GNUNET_OK != tt (ttClosure))
278 size = GNUNET_ECRS_DBLOCK_SIZE;
279 if (size > filesize - pos)
281 size = filesize - pos;
282 memset (&db[1], 0, GNUNET_ECRS_DBLOCK_SIZE);
284 GNUNET_GE_ASSERT (ectx,
285 sizeof (GNUNET_DatastoreValue) + size +
286 sizeof (GNUNET_EC_DBlock) < GNUNET_MAX_BUFFER_SIZE);
288 htonl (sizeof (GNUNET_DatastoreValue) + size +
289 sizeof (GNUNET_EC_DBlock));
290 if (size != READ (fd, &db[1], size))
292 GNUNET_GE_LOG_STRERROR_FILE (ectx,
293 GNUNET_GE_ERROR | GNUNET_GE_BULK |
294 GNUNET_GE_ADMIN | GNUNET_GE_USER,
299 if (GNUNET_OK != tt (ttClosure))
301 GNUNET_EC_file_block_get_key (db, size + sizeof (GNUNET_EC_DBlock),
303 GNUNET_EC_file_block_get_query (db, size + sizeof (GNUNET_EC_DBlock),
306 GNUNET_hash_to_enc (&mchk.query, &enc);
308 "Query for current block of size %u is `%s'\n", size,
309 (const char *) &enc);
311 if (doIndex == GNUNET_YES)
313 if (GNUNET_SYSERR == GNUNET_FS_index (sock, &fileId, dblock, pos))
316 GNUNET_GE_ERROR | GNUNET_GE_BULK |
319 ("Indexing data of file `%s' failed at position %llu.\n"),
328 GNUNET_EC_file_block_encode (db,
329 size + sizeof (GNUNET_EC_DBlock),
330 &mchk.query, &value))
332 GNUNET_GE_BREAK (ectx, 0);
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))))
340 GNUNET_GE_BREAK (ectx, ret == GNUNET_NO);
347 now = GNUNET_get_time ();
350 eta = (GNUNET_CronTime) (start +
351 (((double) (now - start) / (double) pos))
352 * (double) filesize);
354 if (GNUNET_OK != pushBlock (sock, &mchk, 0, /* dblocks are on level 0 */
355 iblocks, priority, expirationTime))
359 if (GNUNET_OK != tt (ttClosure))
363 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
364 "Tree depth is %u, walking up tree.\n", treedepth);
366 for (i = 0; i < treedepth; i++)
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))
374 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
375 "Level %u is empty\n", i);
379 db = (GNUNET_EC_DBlock *) & iblocks[i][1];
380 GNUNET_EC_file_block_get_key (db, size, &mchk.key);
383 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
384 "Computing query for %u bytes content.\n", size);
386 GNUNET_EC_file_block_get_query (db, size, &mchk.query);
389 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
390 GNUNET_hash_to_enc (&mchk.query, &enc));
392 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
393 "Query for current block at level %u is `%s'.\n", i,
396 if (GNUNET_OK != pushBlock (sock,
397 &mchk, i + 1, iblocks, priority,
400 GNUNET_GE_BREAK (ectx, 0);
403 GNUNET_EC_file_block_encode (db, size, &mchk.query, &value);
406 GNUNET_GE_BREAK (ectx, 0);
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)))
414 GNUNET_GE_BREAK (ectx, 0);
419 GNUNET_free (iblocks[i]);
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);
430 fid.file_length = GNUNET_htonll (filesize);
431 db = (GNUNET_EC_DBlock *) & iblocks[treedepth][1];
433 fid.chk = *(GNUNET_EC_ContentHashKey *) & (db[1]);
434 *uri = GNUNET_malloc (sizeof (URI));
436 (*uri)->data.fi = fid;
439 GNUNET_free_non_null (iblocks[treedepth]);
440 GNUNET_free (iblocks);
441 GNUNET_free (dblock);
443 upcb (filesize, filesize, eta, upcbClosure);
445 GNUNET_client_connection_destroy (sock);
448 for (i = 0; i <= treedepth; i++)
449 GNUNET_free_non_null (iblocks[i]);
450 GNUNET_free (iblocks);
451 GNUNET_free (dblock);
453 GNUNET_client_connection_destroy (sock);
454 return GNUNET_SYSERR;
459 /* end of fs_publish.c */