aboutsummaryrefslogtreecommitdiff
path: root/src/fs/fs_directory.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-08-21 08:41:21 +0000
committerChristian Grothoff <christian@grothoff.org>2009-08-21 08:41:21 +0000
commit9a10e9c06a3b08c8ab73edb7d2093a6d452ecc05 (patch)
treed9ee186c9f61fdc90f128cf39ed113112b21e8ec /src/fs/fs_directory.c
parentab1934147f74c835b525864b770795f858e37b89 (diff)
downloadgnunet-9a10e9c06a3b08c8ab73edb7d2093a6d452ecc05.tar.gz
gnunet-9a10e9c06a3b08c8ab73edb7d2093a6d452ecc05.zip
fixes
Diffstat (limited to 'src/fs/fs_directory.c')
-rw-r--r--src/fs/fs_directory.c461
1 files changed, 461 insertions, 0 deletions
diff --git a/src/fs/fs_directory.c b/src/fs/fs_directory.c
new file mode 100644
index 000000000..1ee707cd1
--- /dev/null
+++ b/src/fs/fs_directory.c
@@ -0,0 +1,461 @@
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 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_directory.c
23 * @brief Helper functions for building directories.
24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - add support for embedded file data (use padding room!)
28 * - add directory builder API to gnunet_fs_service
29 * - modify directory builder API to support incremental
30 * generation of directories (to allow directories that
31 * would not fit into memory to be created)
32 * - modify directory processor API to support incremental
33 * iteration over FULL directories (without missing entries)
34 * to allow access to directories that do not fit entirely
35 * into memory
36 */
37#include "platform.h"
38#include "gnunet_fs_service.h"
39#include "fs.h"
40
41
42/**
43 * Does the meta-data claim that this is a directory?
44 * Checks if the mime-type is that of a GNUnet directory.
45 *
46 * @return GNUNET_YES if it is, GNUNET_NO if it is not, GNUNET_SYSERR if
47 * we have no mime-type information (treat as 'GNUNET_NO')
48 */
49int
50GNUNET_FS_meta_data_test_for_directory (const struct GNUNET_CONTAINER_MetaData *md)
51{
52 char *mime;
53 int ret;
54
55 mime = GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_MIMETYPE);
56 if (mime == NULL)
57 return GNUNET_SYSERR;
58 ret = (0 == strcmp (mime, GNUNET_FS_DIRECTORY_MIME)) ? GNUNET_YES : GNUNET_NO;
59 GNUNET_free (mime);
60 return ret;
61}
62
63
64/**
65 * Set the MIMETYPE information for the given
66 * metadata to "application/gnunet-directory".
67 *
68 * @param md metadata to add mimetype to
69 */
70void
71GNUNET_FS_meta_data_make_directory (struct GNUNET_CONTAINER_MetaData *md)
72{
73 char *mime;
74
75 mime = GNUNET_CONTAINER_meta_data_get_by_type (md, EXTRACTOR_MIMETYPE);
76 if (mime != NULL)
77 {
78 GNUNET_break (0 == strcmp (mime,
79 GNUNET_FS_DIRECTORY_MIME));
80 GNUNET_free (mime);
81 return;
82 }
83 GNUNET_CONTAINER_meta_data_insert (md,
84 EXTRACTOR_MIMETYPE,
85 GNUNET_FS_DIRECTORY_MIME);
86}
87
88
89/**
90 * Iterate over all entries in a directory. Note that directories
91 * are structured such that it is possible to iterate over the
92 * individual blocks as well as over the entire directory. Thus
93 * a client can call this function on the buffer in the
94 * GNUNET_FS_ProgressCallback. Also, directories can optionally
95 * include the contents of (small) files embedded in the directory
96 * itself; for those files, the processor may be given the
97 * contents of the file directly by this function.
98 * <p>
99 *
100 * Note that this function maybe called on parts of directories. Thus
101 * parser errors should not be reported _at all_ (with GNUNET_break).
102 * Still, if some entries can be recovered despite these parsing
103 * errors, the function should try to do this.
104 *
105 * @param size number of bytes in data
106 * @param data pointer to the beginning of the directory
107 * @param offset offset of data in the directory
108 * @param dep function to call on each entry
109 * @param dep_cls closure for dep
110 */
111void
112GNUNET_FS_directory_list_contents (size_t size,
113 const void *data,
114 uint64_t offset,
115 GNUNET_FS_DirectoryEntryProcessor dep,
116 void *dep_cls)
117{
118 const char *cdata = data;
119 char *emsg;
120 uint64_t pos;
121 uint64_t align;
122 uint32_t mdSize;
123 uint64_t epos;
124 struct GNUNET_FS_Uri *uri;
125 struct GNUNET_CONTAINER_MetaData *md;
126 char *filename;
127
128 pos = offset;
129 if ( (pos == 0) &&
130 (size >= 8 + sizeof (uint32_t)) &&
131 (0 == memcmp (cdata, GNUNET_FS_DIRECTORY_MAGIC, 8)) )
132 {
133 memcpy (&mdSize, &cdata[8], sizeof (uint32_t));
134 mdSize = ntohl (mdSize);
135 if (mdSize > size - 8 - sizeof (uint32_t))
136 {
137 /* invalid size */
138 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
139 _("Not a GNUnet directory.\n"));
140 return;
141 }
142 md = GNUNET_CONTAINER_meta_data_deserialize (&cdata[8 +
143 sizeof (uint32_t)],
144 mdSize);
145 if (md == NULL)
146 {
147 GNUNET_break (0);
148 return; /* malformed ! */
149 }
150 dep (dep_cls,
151 NULL,
152 NULL,
153 md,
154 0,
155 NULL);
156 GNUNET_CONTAINER_meta_data_destroy (md);
157 pos = 8 + sizeof (uint32_t) + mdSize;
158 }
159 while (pos < size)
160 {
161 /* find end of URI */
162 if (cdata[pos] == '\0')
163 {
164 /* URI is never empty, must be end of block,
165 skip to next alignment */
166 align =
167 ((pos / GNUNET_FS_DBLOCK_SIZE) + 1) * GNUNET_FS_DBLOCK_SIZE;
168 if (align == pos)
169 {
170 /* if we were already aligned, still skip a block! */
171 align += GNUNET_FS_DBLOCK_SIZE;
172 }
173 pos = align;
174 if (pos >= size)
175 {
176 /* malformed - or partial download... */
177 break;
178 }
179 }
180 epos = pos;
181 while ((epos < size) && (cdata[epos] != '\0'))
182 epos++;
183 if (epos >= size)
184 return; /* malformed - or partial download */
185
186 uri = GNUNET_FS_uri_parse (&cdata[pos], &emsg);
187 pos = epos + 1;
188 if (uri == NULL)
189 {
190 GNUNET_free (emsg);
191 pos--; /* go back to '\0' to force going to next alignment */
192 continue;
193 }
194 if (GNUNET_FS_uri_test_ksk (uri))
195 {
196 GNUNET_FS_uri_destroy (uri);
197 GNUNET_break (0);
198 return; /* illegal in directory! */
199 }
200
201 memcpy (&mdSize, &cdata[pos], sizeof (uint32_t));
202 mdSize = ntohl (mdSize);
203 pos += sizeof (uint32_t);
204 if (pos + mdSize > size)
205 {
206 GNUNET_FS_uri_destroy (uri);
207 return; /* malformed - or partial download */
208 }
209
210 md = GNUNET_CONTAINER_meta_data_deserialize (&cdata[pos], mdSize);
211 if (md == NULL)
212 {
213 GNUNET_FS_uri_destroy (uri);
214 GNUNET_break (0);
215 return; /* malformed ! */
216 }
217 pos += mdSize;
218 /* FIXME: add support for embedded data */
219 filename = GNUNET_CONTAINER_meta_data_get_by_type (md,
220 EXTRACTOR_FILENAME);
221 if (dep != NULL)
222 dep (dep_cls,
223 filename,
224 uri,
225 md,
226 0,
227 NULL);
228 GNUNET_free_non_null (filename);
229 GNUNET_CONTAINER_meta_data_destroy (md);
230 GNUNET_FS_uri_destroy (uri);
231 }
232}
233
234#if 0
235
236
237/**
238 * Given the start and end position of a block of
239 * data, return the end position of that data
240 * after alignment to the GNUNET_FS_DBLOCK_SIZE.
241 */
242static uint64_t
243do_align (uint64_t start_position,
244 uint64_t end_position)
245{
246 uint64_t align;
247
248 align = (end_position / GNUNET_FS_DBLOCK_SIZE) * GNUNET_FS_DBLOCK_SIZE;
249 if ((start_position < align) && (end_position > align))
250 return align + end_position - start_position;
251 return end_position;
252}
253
254
255/**
256 * Compute a permuation of the blocks to
257 * minimize the cost of alignment. Greedy packer.
258 *
259 * @param start starting position for the first block
260 * @param count size of the two arrays
261 * @param sizes the sizes of the individual blocks
262 * @param perm the permutation of the blocks (updated)
263 */
264static void
265block_align (uint64_t start,
266 unsigned int count,
267 const uint64_t *sizes,
268 unsigned int *perm)
269{
270 unsigned int i;
271 unsigned int j;
272 unsigned int tmp;
273 unsigned int best;
274 int64_t badness;
275 uint64_t cpos;
276 uint64_t cend;
277 int64_t cbad;
278 unsigned int cval;
279
280 cpos = start;
281 for (i = 0; i < count; i++)
282 {
283 start = cpos;
284 badness = 0x7FFFFFFF;
285 best = -1;
286 for (j = i; j < count; j++)
287 {
288 cval = perm[j];
289 cend = cpos + sizes[cval];
290 if (cpos % GNUNET_FS_DBLOCK_SIZE == 0)
291 {
292 /* prefer placing the largest blocks first */
293 cbad = -(cend % GNUNET_FS_DBLOCK_SIZE);
294 }
295 else
296 {
297 if (cpos / GNUNET_FS_DBLOCK_SIZE ==
298 cend / GNUNET_FS_DBLOCK_SIZE)
299 {
300 /* Data fits into the same block! Prefer small left-overs! */
301 cbad =
302 GNUNET_FS_DBLOCK_SIZE - cend % GNUNET_FS_DBLOCK_SIZE;
303 }
304 else
305 {
306 /* Would have to waste space to re-align, add big factor, this
307 case is a real loss (proportional to space wasted)! */
308 cbad =
309 GNUNET_FS_DBLOCK_SIZE * (GNUNET_FS_DBLOCK_SIZE -
310 cpos %
311 GNUNET_FS_DBLOCK_SIZE);
312 }
313 }
314 if (cbad < badness)
315 {
316 best = j;
317 badness = cbad;
318 }
319 }
320 tmp = perm[i];
321 perm[i] = perm[best];
322 perm[best] = tmp;
323 cpos += sizes[perm[i]];
324 cpos = do_align (start, cpos);
325 }
326}
327
328
329/**
330 * Create a directory. We allow packing more than one variable
331 * size entry into one block (and an entry could also span more
332 * than one block), but an entry that is smaller than a single
333 * block will never cross the block boundary. This is done to
334 * allow processing entries of a directory already even if the
335 * download is still partial.<p>
336 *
337 * The first block begins with the directories MAGIC signature,
338 * followed by the meta-data about the directory itself.<p>
339 *
340 * After that, the directory consists of block-aligned pairs
341 * of URIs (0-terminated strings) and serialized meta-data.
342 *
343 * @param data pointer set to the beginning of the directory
344 * @param len set to number of bytes in data
345 * @param count number of entries in uris and mds
346 * @param uris URIs of the files in the directory
347 * @param mds meta-data for the files (must match
348 * respective values at same offset in in uris)
349 * @param mdir meta-data for the directory
350 * @return GNUNET_OK on success, GNUNET_SYSERR on error
351 */
352int
353GNUNET_FS_directory_create (char **data,
354 size_t *len,
355 unsigned int count,
356 const struct GNUNET_FS_Uri **uris,
357 const struct GNUNET_CONTAINER_MetaData **mds,
358 const struct GNUNET_CONTAINER_MetaData *mdir)
359{
360 unsigned int i;
361 unsigned int j;
362 uint64_t psize;
363 uint64_t size;
364 uint64_t pos;
365 char **ucs;
366 int ret;
367 uint64_t *sizes;
368 unsigned int *perm;
369
370 for (i = 0; i < count; i++)
371 {
372 if (GNUNET_FS_uri_test_ksk (fis[i].uri))
373 {
374 GNUNET_break (0);
375 return GNUNET_SYSERR; /* illegal in directory! */
376 }
377 }
378 ucs = GNUNET_malloc (sizeof (char *) * count);
379 size = 8 + sizeof (unsigned int);
380 size += GNUNET_meta_data_get_serialized_size (meta, GNUNET_SERIALIZE_FULL);
381 sizes = GNUNET_malloc (count * sizeof (unsigned long long));
382 perm = GNUNET_malloc (count * sizeof (int));
383 for (i = 0; i < count; i++)
384 {
385 perm[i] = i;
386 ucs[i] = GNUNET_FS_uri_to_string (fis[i].uri);
387 GNUNET_assert (ucs[i] != NULL);
388 psize =
389 GNUNET_meta_data_get_serialized_size (fis[i].meta,
390 GNUNET_SERIALIZE_FULL);
391 if (psize == -1)
392 {
393 GNUNET_break (0);
394 GNUNET_free (sizes);
395 GNUNET_free (perm);
396 while (i >= 0)
397 GNUNET_free (ucs[i--]);
398 GNUNET_free (ucs);
399 return GNUNET_SYSERR;
400 }
401 sizes[i] = psize + sizeof (unsigned int) + strlen (ucs[i]) + 1;
402 }
403 /* permutate entries to minimize alignment cost */
404 block_align (size, count, sizes, perm);
405
406 /* compute final size with alignment */
407 for (i = 0; i < count; i++)
408 {
409 psize = size;
410 size += sizes[perm[i]];
411 size = do_align (psize, size);
412 }
413 *len = size;
414 *data = GNUNET_malloc (size);
415 memset (*data, 0, size);
416
417 pos = 8;
418 memcpy (*data, GNUNET_DIRECTORY_MAGIC, 8);
419
420 ret = GNUNET_CONTAINER_meta_data_serialize (meta,
421 &(*data)[pos +
422 sizeof (unsigned int)],
423 size - pos - sizeof (unsigned int),
424 GNUNET_SERIALIZE_FULL);
425 GNUNET_assert (ret != GNUNET_SYSERR);
426 ret = htonl (ret);
427 memcpy (&(*data)[pos], &ret, sizeof (unsigned int));
428 pos += ntohl (ret) + sizeof (unsigned int);
429
430 for (j = 0; j < count; j++)
431 {
432 i = perm[j];
433 psize = pos;
434 pos += sizes[i];
435 pos = do_align (psize, pos);
436 pos -= sizes[i]; /* go back to beginning */
437 memcpy (&(*data)[pos], ucs[i], strlen (ucs[i]) + 1);
438 pos += strlen (ucs[i]) + 1;
439 GNUNET_free (ucs[i]);
440 ret = GNUNET_CONTAINER_meta_data_serialize (mds[i],
441 &(*data)[pos +
442 sizeof (unsigned int)],
443 size - pos -
444 sizeof (unsigned int),
445 GNUNET_SERIALIZE_FULL);
446 GNUNET_assert (ret != GNUNET_SYSERR);
447 ret = htonl (ret);
448 memcpy (&(*data)[pos], &ret, sizeof (unsigned int));
449 pos += ntohl (ret) + sizeof (unsigned int);
450 }
451 GNUNET_free (sizes);
452 GNUNET_free (perm);
453 GNUNET_free (ucs);
454 GNUNET_assert (pos == size);
455 return GNUNET_OK;
456}
457
458
459#endif
460
461/* end of fs_directory.c */