fixing drq code
[oweals/gnunet.git] / src / fs / fs_file_information.c
1 /*
2      This file is part of GNUnet.
3      (C) 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_file_information.c
23  * @brief  Manage information for publishing directory hierarchies
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - serialization/deserialization (& deserialization API)
28  * - metadata filename clean up code
29  * - metadata/ksk generation for directories from contained files
30  */
31 #include "platform.h"
32 #include <extractor.h>
33 #include "gnunet_fs_service.h"
34 #include "fs.h"
35 #include "fs_tree.h"
36
37
38 /**
39  * Create a temporary file on disk to store the current
40  * state of "fi" in.
41  *
42  * @param fi file information to sync with disk
43  */
44 void
45 GNUNET_FS_file_information_sync (struct GNUNET_FS_FileInformation * fi)
46 {
47   if (NULL == fi->serialization)
48     {
49       fi->serialization = NULL; // FIXME -- need cfg!
50     }
51   // FIXME...
52 }
53
54
55 /**
56  * Load file information from the file to which
57  * it was sync'ed.
58  *
59  * @param fn name of the file to use
60  * @return NULL on error
61  */
62 struct GNUNET_FS_FileInformation *
63 GNUNET_FS_file_information_recover (const char *fn)
64 {
65   struct GNUNET_FS_FileInformation *ret;
66   ret = NULL;
67   // FIXME!
68   return ret;
69 }
70
71
72 /**
73  * Obtain the name under which this file information
74  * structure is stored on disk.  Only works for top-level
75  * file information structures.
76  *
77  * @param s structure to get the filename for
78  * @return NULL on error, otherwise filename that
79  *         can be passed to "GNUNET_FS_file_information_recover"
80  *         to read this fi-struct from disk.
81  */
82 const char *
83 GNUNET_FS_file_information_get_id (struct GNUNET_FS_FileInformation *s)
84 {
85   if (NULL != s->dir)
86     return NULL;
87   return s->serialization;
88 }
89
90
91 /**
92  * Closure for "data_reader_file".
93  */
94 struct FileInfo
95 {
96   /**
97    * Name of the file to read.
98    */
99   char *filename;
100
101   /**
102    * File descriptor, NULL if it has not yet been opened.
103    */
104   struct GNUNET_DISK_FileHandle *fd;
105 };
106
107
108 /**
109  * Function that provides data by reading from a file.
110  *
111  * @param cls closure (points to the file information)
112  * @param offset offset to read from; it is possible
113  *            that the caller might need to go backwards
114  *            a bit at times
115  * @param max maximum number of bytes that should be 
116  *            copied to buf; readers are not allowed
117  *            to provide less data unless there is an error;
118  *            a value of "0" will be used at the end to allow
119  *            the reader to clean up its internal state
120  * @param buf where the reader should write the data
121  * @param emsg location for the reader to store an error message
122  * @return number of bytes written, usually "max", 0 on error
123  */
124 static size_t
125 data_reader_file(void *cls, 
126                  uint64_t offset,
127                  size_t max, 
128                  void *buf,
129                  char **emsg)
130 {
131   struct FileInfo *fi = cls;
132   ssize_t ret;
133
134   if (max == 0)
135     {
136       if (fi->fd != NULL)
137         GNUNET_DISK_file_close (fi->fd);
138       GNUNET_free (fi->filename);
139       GNUNET_free (fi);
140       return 0;
141     }  
142   if (fi->fd == NULL)
143     {
144       fi->fd = GNUNET_DISK_file_open (fi->filename,
145                                       GNUNET_DISK_OPEN_READ,
146                                       GNUNET_DISK_PERM_NONE);
147       if (fi->fd == NULL)
148         {
149           GNUNET_asprintf (emsg, 
150                            _("Could not open file `%s': %s"),
151                            fi->filename,
152                            STRERROR (errno));
153           return 0;
154         }
155     }
156   GNUNET_DISK_file_seek (fi->fd, offset, GNUNET_DISK_SEEK_SET);
157   ret = GNUNET_DISK_file_read (fi->fd, buf, max);
158   if (ret == -1)
159     {
160       GNUNET_asprintf (emsg, 
161                        _("Could not read file `%s': %s"),
162                        fi->filename,
163                        STRERROR (errno));
164       return 0;
165     }
166   if (ret != max)
167     {
168       GNUNET_asprintf (emsg, 
169                        _("Short read reading from file `%s'!"),
170                        fi->filename);
171       return 0;
172     }
173   return max;
174 }
175
176
177 /**
178  * Create an entry for a file in a publish-structure.
179  *
180  * @param client_info initial value for the client-info value for this entry
181  * @param filename name of the file or directory to publish
182  * @param keywords under which keywords should this file be available
183  *         directly; can be NULL
184  * @param meta metadata for the file
185  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
186  *                GNUNET_SYSERR for simulation
187  * @param anonymity what is the desired anonymity level for sharing?
188  * @param priority what is the priority for OUR node to
189  *   keep this file available?  Use 0 for maximum anonymity and
190  *   minimum reliability...
191  * @param expirationTime when should this content expire?
192  * @return publish structure entry for the file
193  */
194 struct GNUNET_FS_FileInformation *
195 GNUNET_FS_file_information_create_from_file (void *client_info,
196                                              const char *filename,
197                                              const struct GNUNET_FS_Uri *keywords,
198                                              const struct GNUNET_CONTAINER_MetaData *meta,
199                                              int do_index,
200                                              uint32_t anonymity,
201                                              uint32_t priority,
202                                              struct GNUNET_TIME_Absolute expirationTime)
203 {
204   struct FileInfo *fi;
205   struct stat sbuf;
206   struct GNUNET_FS_FileInformation *ret;
207
208   if (0 != STAT (filename, &sbuf))
209     {
210       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
211                                 "stat",
212                                 filename);
213       return NULL;
214     }
215   fi = GNUNET_malloc (sizeof(struct FileInfo));
216   fi->filename = GNUNET_strdup (filename);
217   ret = GNUNET_FS_file_information_create_from_reader (client_info,
218                                                        sbuf.st_size,
219                                                        &data_reader_file,
220                                                        fi,
221                                                        keywords,
222                                                        meta,
223                                                        do_index,
224                                                        anonymity,
225                                                        priority,
226                                                        expirationTime);
227   ret->data.file.filename = GNUNET_strdup (filename);
228   return ret;
229 }
230
231
232 /**
233  * Function that provides data by copying from a buffer.
234  *
235  * @param cls closure (points to the buffer)
236  * @param offset offset to read from; it is possible
237  *            that the caller might need to go backwards
238  *            a bit at times
239  * @param max maximum number of bytes that should be 
240  *            copied to buf; readers are not allowed
241  *            to provide less data unless there is an error;
242  *            a value of "0" will be used at the end to allow
243  *            the reader to clean up its internal state
244  * @param buf where the reader should write the data
245  * @param emsg location for the reader to store an error message
246  * @return number of bytes written, usually "max", 0 on error
247  */
248 static size_t
249 data_reader_copy(void *cls, 
250                  uint64_t offset,
251                  size_t max, 
252                  void *buf,
253                  char **emsg)
254 {
255   char *data = cls;
256
257   if (max == 0)
258     {
259       GNUNET_free (data);
260       return 0;
261     }  
262   memcpy (buf, &data[offset], max);
263   return max;
264 }
265
266
267 /**
268  * Create an entry for a file in a publish-structure.
269  *
270  * @param client_info initial value for the client-info value for this entry
271  * @param length length of the file
272  * @param data data for the file (should not be used afterwards by
273  *        the caller; callee will "free")
274  * @param keywords under which keywords should this file be available
275  *         directly; can be NULL
276  * @param meta metadata for the file
277  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
278  *                GNUNET_SYSERR for simulation
279  * @param anonymity what is the desired anonymity level for sharing?
280  * @param priority what is the priority for OUR node to
281  *   keep this file available?  Use 0 for maximum anonymity and
282  *   minimum reliability...
283  * @param expirationTime when should this content expire?
284  * @return publish structure entry for the file
285  */
286 struct GNUNET_FS_FileInformation *
287 GNUNET_FS_file_information_create_from_data (void *client_info,
288                                              uint64_t length,
289                                              void *data,
290                                              const struct GNUNET_FS_Uri *keywords,
291                                              const struct GNUNET_CONTAINER_MetaData *meta,
292                                              int do_index,
293                                              uint32_t anonymity,
294                                              uint32_t priority,
295                                              struct GNUNET_TIME_Absolute expirationTime)
296 {
297   return GNUNET_FS_file_information_create_from_reader (client_info,
298                                                         length,
299                                                         &data_reader_copy,
300                                                         data,
301                                                         keywords,
302                                                         meta,
303                                                         do_index,
304                                                         anonymity,
305                                                         priority,
306                                                         expirationTime);
307 }
308
309
310 /**
311  * Create an entry for a file in a publish-structure.
312  *
313  * @param client_info initial value for the client-info value for this entry
314  * @param length length of the file
315  * @param reader function that can be used to obtain the data for the file 
316  * @param reader_cls closure for "reader"
317  * @param keywords under which keywords should this file be available
318  *         directly; can be NULL
319  * @param meta metadata for the file
320  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
321  *                GNUNET_SYSERR for simulation
322  * @param anonymity what is the desired anonymity level for sharing?
323  * @param priority what is the priority for OUR node to
324  *   keep this file available?  Use 0 for maximum anonymity and
325  *   minimum reliability...
326  * @param expirationTime when should this content expire?
327  * @return publish structure entry for the file
328  */
329 struct GNUNET_FS_FileInformation *
330 GNUNET_FS_file_information_create_from_reader (void *client_info,
331                                                uint64_t length,
332                                                GNUNET_FS_DataReader reader,
333                                                void *reader_cls,
334                                                const struct GNUNET_FS_Uri *keywords,
335                                                const struct GNUNET_CONTAINER_MetaData *meta,
336                                                int do_index,
337                                                uint32_t anonymity,
338                                                uint32_t priority,
339                                                struct GNUNET_TIME_Absolute expirationTime)
340 {
341   struct GNUNET_FS_FileInformation *ret;
342
343   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
344   ret->client_info = client_info;
345   ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
346   ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup (keywords);
347   ret->expirationTime = expirationTime;
348   ret->data.file.reader = reader; 
349   ret->data.file.reader_cls = reader_cls;
350   ret->data.file.do_index = do_index;
351   ret->data.file.file_size = length;
352   ret->anonymity = anonymity;
353   ret->priority = priority;
354   GNUNET_FS_file_information_sync (ret);
355   return ret;
356 }
357
358
359 /**
360  * Closure for "dir_scan_cb".
361  */
362 struct DirScanCls 
363 {
364   /**
365    * Metadata extractors to use.
366    */
367   struct EXTRACTOR_PluginList *extractors;
368
369   /**
370    * Function to call on each directory entry.
371    */
372   GNUNET_FS_FileProcessor proc;
373   
374   /**
375    * Closure for proc.
376    */
377   void *proc_cls;
378
379   /**
380    * Scanner to use for subdirectories.
381    */
382   GNUNET_FS_DirectoryScanner scanner;
383
384   /**
385    * Closure for scanner.
386    */
387   void *scanner_cls;
388
389   /**
390    * Set to an error message (if any).
391    */
392   char *emsg; 
393
394   /**
395    * Should files be indexed?
396    */ 
397   int do_index;
398
399   /**
400    * Desired anonymity level.
401    */
402   uint32_t anonymity;
403
404   /**
405    * Desired publishing priority.
406    */
407   uint32_t priority;
408
409   /**
410    * Expiration time for publication.
411    */
412   struct GNUNET_TIME_Absolute expiration;
413 };
414
415
416 /**
417  * Function called on each entry in a file to
418  * cause default-publishing.
419  * @param cls closure (struct DirScanCls)
420  * @param filename name of the file to be published
421  * @return GNUNET_OK on success, GNUNET_SYSERR to abort
422  */
423 static int
424 dir_scan_cb (void *cls,
425              const char *filename)
426 {
427   struct DirScanCls *dsc = cls;  
428   struct stat sbuf;
429   struct GNUNET_FS_FileInformation *fi;
430   struct GNUNET_FS_Uri *ksk_uri;
431   struct GNUNET_FS_Uri *keywords;
432   struct GNUNET_CONTAINER_MetaData *meta;
433
434   if (0 != STAT (filename, &sbuf))
435     {
436       GNUNET_asprintf (&dsc->emsg,
437                        _("`%s' failed on file `%s': %s"),
438                        "stat",
439                        filename,
440                        STRERROR (errno));
441       return GNUNET_SYSERR;
442     }
443   if (S_ISDIR (sbuf.st_mode))
444     {
445       fi = GNUNET_FS_file_information_create_from_directory (NULL,
446                                                              filename,
447                                                              dsc->scanner,
448                                                              dsc->scanner_cls,
449                                                              dsc->do_index,
450                                                              dsc->anonymity,
451                                                              dsc->priority,
452                                                              dsc->expiration,
453                                                              &dsc->emsg);
454       if (NULL == fi)
455         {
456           GNUNET_assert (NULL != dsc->emsg);
457           return GNUNET_SYSERR;
458         }
459     }
460   else
461     {
462       meta = GNUNET_CONTAINER_meta_data_create ();
463       GNUNET_CONTAINER_meta_data_extract_from_file (meta,
464                                                     filename,
465                                                     dsc->extractors);
466       // FIXME: remove path from filename in metadata!
467       keywords = GNUNET_FS_uri_ksk_create_from_meta_data (meta);
468       ksk_uri = GNUNET_FS_uri_ksk_canonicalize (keywords);
469       fi = GNUNET_FS_file_information_create_from_file (NULL,
470                                                         filename,
471                                                         ksk_uri,
472                                                         meta,
473                                                         dsc->do_index,
474                                                         dsc->anonymity,
475                                                         dsc->priority,
476                                                         dsc->expiration);
477       GNUNET_CONTAINER_meta_data_destroy (meta);
478       GNUNET_FS_uri_destroy (keywords);
479       GNUNET_FS_uri_destroy (ksk_uri);
480     }
481   dsc->proc (dsc->proc_cls,
482              filename,
483              fi);
484   return GNUNET_OK;
485 }
486
487
488 /**
489  * Simple, useful default implementation of a directory scanner
490  * (GNUNET_FS_DirectoryScanner).  This implementation expects to get a
491  * UNIX filename, will publish all files in the directory except hidden
492  * files (those starting with a ".").  Metadata will be extracted
493  * using GNU libextractor; the specific list of plugins should be
494  * specified in "cls", passing NULL will disable (!)  metadata
495  * extraction.  Keywords will be derived from the metadata and be
496  * subject to default canonicalization.  This is strictly a
497  * convenience function.
498  *
499  * @param cls must be of type "struct EXTRACTOR_Extractor*"
500  * @param dirname name of the directory to scan
501  * @param do_index should files be indexed or inserted
502  * @param anonymity desired anonymity level
503  * @param priority priority for publishing
504  * @param expirationTime expiration for publication
505  * @param proc function called on each entry
506  * @param proc_cls closure for proc
507  * @param emsg where to store an error message (on errors)
508  * @return GNUNET_OK on success
509  */
510 int
511 GNUNET_FS_directory_scanner_default (void *cls,
512                                      const char *dirname,
513                                      int do_index,
514                                      uint32_t anonymity,
515                                      uint32_t priority,
516                                      struct GNUNET_TIME_Absolute expirationTime,
517                                      GNUNET_FS_FileProcessor proc,
518                                      void *proc_cls,
519                                      char **emsg)
520 {
521   struct EXTRACTOR_PluginList *ex = cls;
522   struct DirScanCls dsc;
523
524   dsc.extractors = ex;
525   dsc.proc = proc;
526   dsc.proc_cls = proc_cls;
527   dsc.scanner = &GNUNET_FS_directory_scanner_default;
528   dsc.scanner_cls = cls;
529   dsc.do_index = do_index;
530   dsc.anonymity = anonymity;
531   dsc.priority = priority;
532   dsc.expiration = expirationTime;
533   if (-1 == GNUNET_DISK_directory_scan (dirname,
534                                         &dir_scan_cb,
535                                         &dsc))
536     {
537       GNUNET_assert (NULL != dsc.emsg);
538       *emsg = dsc.emsg;
539       return GNUNET_SYSERR;
540     }
541   return GNUNET_OK;
542 }
543
544
545 /**
546  * Closure for dirproc function.
547  */
548 struct EntryProcCls
549 {
550   /**
551    * Linked list of directory entries that is being
552    * created.
553    */
554   struct GNUNET_FS_FileInformation *entries;
555
556 };
557
558
559 /**
560  * Function that processes a directory entry that
561  * was obtained from the scanner.
562  * @param cls our closure
563  * @param filename name of the file (unused, why there???)
564  * @param fi information for publishing the file
565  */
566 static void
567 dirproc (void *cls,
568          const char *filename,
569          struct GNUNET_FS_FileInformation *fi)
570 {
571   struct EntryProcCls *dc = cls;
572
573   GNUNET_assert (fi->next == NULL);
574   GNUNET_assert (fi->dir == NULL);
575   fi->next = dc->entries;
576   dc->entries = fi;
577 }
578
579
580 /**
581  * Create a publish-structure from an existing file hierarchy, inferring
582  * and organizing keywords and metadata as much as possible.  This
583  * function primarily performs the recursive build and re-organizes
584  * keywords and metadata; for automatically getting metadata
585  * extraction, scanning of directories and creation of the respective
586  * GNUNET_FS_FileInformation entries the default scanner should be
587  * passed (GNUNET_FS_directory_scanner_default).  This is strictly a
588  * convenience function.
589  *
590  * @param client_info initial value for the client-info value for this entry
591  * @param filename name of the top-level file or directory
592  * @param scanner function used to get a list of files in a directory
593  * @param scanner_cls closure for scanner
594  * @param do_index should files in the hierarchy be indexed?
595  * @param anonymity what is the desired anonymity level for sharing?
596  * @param priority what is the priority for OUR node to
597  *   keep this file available?  Use 0 for maximum anonymity and
598  *   minimum reliability...
599  * @param expirationTime when should this content expire?
600  * @param emsg where to store an error message
601  * @return publish structure entry for the directory, NULL on error
602  */
603 struct GNUNET_FS_FileInformation *
604 GNUNET_FS_file_information_create_from_directory (void *client_info,
605                                                   const char *filename,
606                                                   GNUNET_FS_DirectoryScanner scanner,
607                                                   void *scanner_cls,
608                                                   int do_index,
609                                                   uint32_t anonymity,
610                                                   uint32_t priority,
611                                                   struct GNUNET_TIME_Absolute expirationTime,
612                                                   char **emsg)
613 {
614   struct GNUNET_FS_FileInformation *ret;
615   struct EntryProcCls dc;
616   struct GNUNET_FS_Uri *ksk;
617   struct GNUNET_CONTAINER_MetaData *meta;
618
619   dc.entries = NULL;
620   meta = GNUNET_CONTAINER_meta_data_create ();
621   GNUNET_FS_meta_data_make_directory (meta);
622   
623   scanner (scanner_cls,
624            filename,
625            do_index,
626            anonymity,
627            priority,
628            expirationTime,
629            &dirproc,
630            &dc,
631            emsg);
632   ksk = NULL; // FIXME...
633   // FIXME: create meta!
634   ret = GNUNET_FS_file_information_create_empty_directory (client_info,
635                                                            ksk,
636                                                            meta,
637                                                            anonymity,
638                                                            priority,
639                                                            expirationTime);
640   GNUNET_CONTAINER_meta_data_destroy (meta);
641   ret->data.dir.entries = dc.entries;
642   while (dc.entries != NULL)
643     {
644       dc.entries->dir = ret;
645       GNUNET_FS_file_information_sync (dc.entries);
646       dc.entries = dc.entries->next;
647     }
648   GNUNET_FS_file_information_sync (ret);
649   return ret;
650 }
651
652
653 /**
654  * Create an entry for an empty directory in a publish-structure.
655  * This function should be used by applications for which the
656  * use of "GNUNET_FS_file_information_create_from_directory"
657  * is not appropriate.
658  *
659  * @param client_info initial value for the client-info value for this entry
660  * @param meta metadata for the directory
661  * @param keywords under which keywords should this directory be available
662  *         directly; can be NULL
663  * @param anonymity what is the desired anonymity level for sharing?
664  * @param priority what is the priority for OUR node to
665  *   keep this file available?  Use 0 for maximum anonymity and
666  *   minimum reliability...
667  * @param expirationTime when should this content expire?
668  * @return publish structure entry for the directory , NULL on error
669  */
670 struct GNUNET_FS_FileInformation *
671 GNUNET_FS_file_information_create_empty_directory (void *client_info,
672                                                    const struct GNUNET_FS_Uri *keywords,
673                                                    const struct GNUNET_CONTAINER_MetaData *meta,
674                                                    uint32_t anonymity,
675                                                    uint32_t priority,
676                                                    struct GNUNET_TIME_Absolute expirationTime)
677 {
678   struct GNUNET_FS_FileInformation *ret;
679
680   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
681   ret->client_info = client_info;
682   ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
683   ret->keywords = GNUNET_FS_uri_dup (keywords);
684   ret->expirationTime = expirationTime;
685   ret->is_directory = GNUNET_YES;
686   ret->anonymity = anonymity;
687   ret->priority = priority;
688   GNUNET_FS_file_information_sync (ret);
689   return ret;
690 }
691
692
693 /**
694  * Add an entry to a directory in a publish-structure.  Clients
695  * should never modify publish structures that were passed to
696  * "GNUNET_FS_publish_start" already.
697  *
698  * @param dir the directory
699  * @param ent the entry to add; the entry must not have been
700  *            added to any other directory at this point and 
701  *            must not include "dir" in its structure
702  * @return GNUNET_OK on success, GNUNET_SYSERR on error
703  */
704 int
705 GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir,
706                                 struct GNUNET_FS_FileInformation *ent)
707 {
708   if ( (ent->dir != NULL) ||
709        (ent->next != NULL) ||
710        (! dir->is_directory) )
711     {
712       GNUNET_break (0);
713       return GNUNET_SYSERR;
714     }
715   ent->dir = dir;
716   ent->next = dir->data.dir.entries;
717   dir->data.dir.entries = ent;
718   dir->data.dir.dir_size = 0;
719   GNUNET_FS_file_information_sync (ent);
720   GNUNET_FS_file_information_sync (dir);
721   return GNUNET_OK;
722 }
723
724
725 /**
726  * Inspect a file or directory in a publish-structure.  Clients
727  * should never modify publish structures that were passed to
728  * "GNUNET_FS_publish_start" already.  When called on a directory,
729  * this function will FIRST call "proc" with information about
730  * the directory itself and then for each of the files in the
731  * directory (but not for files in subdirectories).  When called
732  * on a file, "proc" will be called exactly once (with information
733  * about the specific file).
734  *
735  * @param dir the directory
736  * @param proc function to call on each entry
737  * @param proc_cls closure for proc
738  */
739 void
740 GNUNET_FS_file_information_inspect (struct GNUNET_FS_FileInformation *dir,
741                                     GNUNET_FS_FileInformationProcessor proc,
742                                     void *proc_cls)
743 {
744   struct GNUNET_FS_FileInformation *pos;
745
746   if (dir->is_directory)
747     {
748       proc (proc_cls, 
749             dir,
750             dir->data.dir.dir_size,
751             dir->meta,
752             &dir->keywords,
753             &dir->anonymity,
754             &dir->priority,
755             &dir->expirationTime,
756             &dir->client_info);
757       pos = dir->data.dir.entries;
758       while (pos != NULL)
759         {
760           proc (proc_cls, 
761                 pos,
762                 (pos->is_directory) ? pos->data.dir.dir_size : pos->data.file.file_size,
763                 pos->meta,
764                 &pos->keywords,
765                 &pos->anonymity,
766                 &pos->priority,
767                 &pos->expirationTime,
768                 &pos->client_info);
769           pos = pos->next;
770         }
771     }
772   else
773     {
774       proc (proc_cls, 
775             dir,
776             dir->data.file.file_size,
777             dir->meta,
778             &dir->keywords,
779             &dir->anonymity,
780             &dir->priority,
781             &dir->expirationTime,
782             &dir->client_info);
783     }
784 }
785
786
787 /**
788  * Destroy publish-structure.  Clients should never destroy publish
789  * structures that were passed to "GNUNET_FS_publish_start" already.
790  *
791  * @param fi structure to destroy
792  * @param cleaner function to call on each entry in the structure
793  *        (useful to clean up client_info); can be NULL; return
794  *        values are ignored
795  * @param cleaner_cls closure for cleaner
796  */
797 void
798 GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi,
799                                     GNUNET_FS_FileInformationProcessor cleaner,
800                                     void *cleaner_cls)
801 {
802   struct GNUNET_FS_FileInformation *pos;
803
804   if (fi->is_directory)
805     {
806       /* clean up directory */
807       while (NULL != (pos = fi->data.dir.entries))
808         {
809           fi->data.dir.entries = pos->next;
810           GNUNET_FS_file_information_destroy (pos, cleaner, cleaner_cls);
811         }
812       /* clean up client-info */
813       if (NULL != cleaner)
814         cleaner (cleaner_cls, 
815                  fi,
816                  fi->data.dir.dir_size,
817                  fi->meta,
818                  &fi->keywords,
819                  &fi->anonymity,
820                  &fi->priority,
821                  &fi->expirationTime,
822                  &fi->client_info);
823       GNUNET_free_non_null (fi->data.dir.dir_data);
824       GNUNET_free_non_null (fi->data.dir.dirname);
825     }
826   else
827     {
828       /* call clean-up function of the reader */
829       fi->data.file.reader (fi->data.file.reader_cls, 0, 0, NULL, NULL);
830       /* clean up client-info */
831       if (NULL != cleaner)
832         cleaner (cleaner_cls, 
833                  fi,
834                  fi->data.file.file_size,
835                  fi->meta,
836                  &fi->keywords,
837                  &fi->anonymity,
838                  &fi->priority,
839                  &fi->expirationTime,
840                  &fi->client_info);
841     }
842   GNUNET_free_non_null (fi->emsg);
843   GNUNET_free_non_null (fi->chk_uri);
844   /* clean up serialization */
845   if ( (NULL != fi->serialization) &&
846        (0 != UNLINK (fi->serialization)) )
847     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
848                               "unlink",
849                               fi->serialization);
850   GNUNET_FS_uri_destroy (fi->keywords);
851   GNUNET_CONTAINER_meta_data_destroy (fi->meta);
852   GNUNET_free_non_null (fi->serialization);
853   if (fi->te != NULL)
854     {
855       GNUNET_FS_tree_encoder_finish (fi->te,
856                                      NULL, NULL);
857       fi->te = NULL;
858     }
859   GNUNET_free (fi);
860 }
861
862
863 /* end of fs_file_information.c */