8e6f4263609f8003cff1980d91100d9911960c2b
[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   if (ret->meta == NULL)
347     ret->meta = GNUNET_CONTAINER_meta_data_create ();
348   ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup (keywords);
349   ret->expirationTime = expirationTime;
350   ret->data.file.reader = reader; 
351   ret->data.file.reader_cls = reader_cls;
352   ret->data.file.do_index = do_index;
353   ret->data.file.file_size = length;
354   ret->anonymity = anonymity;
355   ret->priority = priority;
356   GNUNET_FS_file_information_sync (ret);
357   return ret;
358 }
359
360
361 /**
362  * Closure for "dir_scan_cb".
363  */
364 struct DirScanCls 
365 {
366   /**
367    * Metadata extractors to use.
368    */
369   struct EXTRACTOR_PluginList *extractors;
370
371   /**
372    * Function to call on each directory entry.
373    */
374   GNUNET_FS_FileProcessor proc;
375   
376   /**
377    * Closure for proc.
378    */
379   void *proc_cls;
380
381   /**
382    * Scanner to use for subdirectories.
383    */
384   GNUNET_FS_DirectoryScanner scanner;
385
386   /**
387    * Closure for scanner.
388    */
389   void *scanner_cls;
390
391   /**
392    * Set to an error message (if any).
393    */
394   char *emsg; 
395
396   /**
397    * Should files be indexed?
398    */ 
399   int do_index;
400
401   /**
402    * Desired anonymity level.
403    */
404   uint32_t anonymity;
405
406   /**
407    * Desired publishing priority.
408    */
409   uint32_t priority;
410
411   /**
412    * Expiration time for publication.
413    */
414   struct GNUNET_TIME_Absolute expiration;
415 };
416
417
418 /**
419  * Function called on each entry in a file to
420  * cause default-publishing.
421  * @param cls closure (struct DirScanCls)
422  * @param filename name of the file to be published
423  * @return GNUNET_OK on success, GNUNET_SYSERR to abort
424  */
425 static int
426 dir_scan_cb (void *cls,
427              const char *filename)
428 {
429   struct DirScanCls *dsc = cls;  
430   struct stat sbuf;
431   struct GNUNET_FS_FileInformation *fi;
432   struct GNUNET_FS_Uri *ksk_uri;
433   struct GNUNET_FS_Uri *keywords;
434   struct GNUNET_CONTAINER_MetaData *meta;
435
436   if (0 != STAT (filename, &sbuf))
437     {
438       GNUNET_asprintf (&dsc->emsg,
439                        _("`%s' failed on file `%s': %s"),
440                        "stat",
441                        filename,
442                        STRERROR (errno));
443       return GNUNET_SYSERR;
444     }
445   if (S_ISDIR (sbuf.st_mode))
446     {
447       fi = GNUNET_FS_file_information_create_from_directory (NULL,
448                                                              filename,
449                                                              dsc->scanner,
450                                                              dsc->scanner_cls,
451                                                              dsc->do_index,
452                                                              dsc->anonymity,
453                                                              dsc->priority,
454                                                              dsc->expiration,
455                                                              &dsc->emsg);
456       if (NULL == fi)
457         {
458           GNUNET_assert (NULL != dsc->emsg);
459           return GNUNET_SYSERR;
460         }
461     }
462   else
463     {
464       meta = GNUNET_CONTAINER_meta_data_create ();
465       GNUNET_CONTAINER_meta_data_extract_from_file (meta,
466                                                     filename,
467                                                     dsc->extractors);
468       // FIXME: remove path from filename in metadata!
469       keywords = GNUNET_FS_uri_ksk_create_from_meta_data (meta);
470       ksk_uri = GNUNET_FS_uri_ksk_canonicalize (keywords);
471       fi = GNUNET_FS_file_information_create_from_file (NULL,
472                                                         filename,
473                                                         ksk_uri,
474                                                         meta,
475                                                         dsc->do_index,
476                                                         dsc->anonymity,
477                                                         dsc->priority,
478                                                         dsc->expiration);
479       GNUNET_CONTAINER_meta_data_destroy (meta);
480       GNUNET_FS_uri_destroy (keywords);
481       GNUNET_FS_uri_destroy (ksk_uri);
482     }
483   dsc->proc (dsc->proc_cls,
484              filename,
485              fi);
486   return GNUNET_OK;
487 }
488
489
490 /**
491  * Simple, useful default implementation of a directory scanner
492  * (GNUNET_FS_DirectoryScanner).  This implementation expects to get a
493  * UNIX filename, will publish all files in the directory except hidden
494  * files (those starting with a ".").  Metadata will be extracted
495  * using GNU libextractor; the specific list of plugins should be
496  * specified in "cls", passing NULL will disable (!)  metadata
497  * extraction.  Keywords will be derived from the metadata and be
498  * subject to default canonicalization.  This is strictly a
499  * convenience function.
500  *
501  * @param cls must be of type "struct EXTRACTOR_Extractor*"
502  * @param dirname name of the directory to scan
503  * @param do_index should files be indexed or inserted
504  * @param anonymity desired anonymity level
505  * @param priority priority for publishing
506  * @param expirationTime expiration for publication
507  * @param proc function called on each entry
508  * @param proc_cls closure for proc
509  * @param emsg where to store an error message (on errors)
510  * @return GNUNET_OK on success
511  */
512 int
513 GNUNET_FS_directory_scanner_default (void *cls,
514                                      const char *dirname,
515                                      int do_index,
516                                      uint32_t anonymity,
517                                      uint32_t priority,
518                                      struct GNUNET_TIME_Absolute expirationTime,
519                                      GNUNET_FS_FileProcessor proc,
520                                      void *proc_cls,
521                                      char **emsg)
522 {
523   struct EXTRACTOR_PluginList *ex = cls;
524   struct DirScanCls dsc;
525
526   dsc.extractors = ex;
527   dsc.proc = proc;
528   dsc.proc_cls = proc_cls;
529   dsc.scanner = &GNUNET_FS_directory_scanner_default;
530   dsc.scanner_cls = cls;
531   dsc.do_index = do_index;
532   dsc.anonymity = anonymity;
533   dsc.priority = priority;
534   dsc.expiration = expirationTime;
535   if (-1 == GNUNET_DISK_directory_scan (dirname,
536                                         &dir_scan_cb,
537                                         &dsc))
538     {
539       GNUNET_assert (NULL != dsc.emsg);
540       *emsg = dsc.emsg;
541       return GNUNET_SYSERR;
542     }
543   return GNUNET_OK;
544 }
545
546
547 /**
548  * Closure for dirproc function.
549  */
550 struct EntryProcCls
551 {
552   /**
553    * Linked list of directory entries that is being
554    * created.
555    */
556   struct GNUNET_FS_FileInformation *entries;
557
558 };
559
560
561 /**
562  * Function that processes a directory entry that
563  * was obtained from the scanner.
564  * @param cls our closure
565  * @param filename name of the file (unused, why there???)
566  * @param fi information for publishing the file
567  */
568 static void
569 dirproc (void *cls,
570          const char *filename,
571          struct GNUNET_FS_FileInformation *fi)
572 {
573   struct EntryProcCls *dc = cls;
574
575   GNUNET_assert (fi->next == NULL);
576   GNUNET_assert (fi->dir == NULL);
577   fi->next = dc->entries;
578   dc->entries = fi;
579 }
580
581
582 /**
583  * Create a publish-structure from an existing file hierarchy, inferring
584  * and organizing keywords and metadata as much as possible.  This
585  * function primarily performs the recursive build and re-organizes
586  * keywords and metadata; for automatically getting metadata
587  * extraction, scanning of directories and creation of the respective
588  * GNUNET_FS_FileInformation entries the default scanner should be
589  * passed (GNUNET_FS_directory_scanner_default).  This is strictly a
590  * convenience function.
591  *
592  * @param client_info initial value for the client-info value for this entry
593  * @param filename name of the top-level file or directory
594  * @param scanner function used to get a list of files in a directory
595  * @param scanner_cls closure for scanner
596  * @param do_index should files in the hierarchy be indexed?
597  * @param anonymity what is the desired anonymity level for sharing?
598  * @param priority what is the priority for OUR node to
599  *   keep this file available?  Use 0 for maximum anonymity and
600  *   minimum reliability...
601  * @param expirationTime when should this content expire?
602  * @param emsg where to store an error message
603  * @return publish structure entry for the directory, NULL on error
604  */
605 struct GNUNET_FS_FileInformation *
606 GNUNET_FS_file_information_create_from_directory (void *client_info,
607                                                   const char *filename,
608                                                   GNUNET_FS_DirectoryScanner scanner,
609                                                   void *scanner_cls,
610                                                   int do_index,
611                                                   uint32_t anonymity,
612                                                   uint32_t priority,
613                                                   struct GNUNET_TIME_Absolute expirationTime,
614                                                   char **emsg)
615 {
616   struct GNUNET_FS_FileInformation *ret;
617   struct EntryProcCls dc;
618   struct GNUNET_FS_Uri *ksk;
619   struct GNUNET_CONTAINER_MetaData *meta;
620
621   
622
623   dc.entries = NULL;
624   meta = GNUNET_CONTAINER_meta_data_create ();
625   GNUNET_FS_meta_data_make_directory (meta);
626   
627   scanner (scanner_cls,
628            filename,
629            do_index,
630            anonymity,
631            priority,
632            expirationTime,
633            &dirproc,
634            &dc,
635            emsg);
636   ksk = NULL; // FIXME...
637   // FIXME: create meta!
638   ret = GNUNET_FS_file_information_create_empty_directory (client_info,
639                                                            ksk,
640                                                            meta,
641                                                            anonymity,
642                                                            priority,
643                                                            expirationTime);
644   GNUNET_CONTAINER_meta_data_destroy (meta);
645   ret->data.dir.entries = dc.entries;
646   while (dc.entries != NULL)
647     {
648       dc.entries->dir = ret;
649       GNUNET_FS_file_information_sync (dc.entries);
650       dc.entries = dc.entries->next;
651     }
652   GNUNET_FS_file_information_sync (ret);
653   return ret;
654 }
655
656
657 /**
658  * Create an entry for an empty directory in a publish-structure.
659  * This function should be used by applications for which the
660  * use of "GNUNET_FS_file_information_create_from_directory"
661  * is not appropriate.
662  *
663  * @param client_info initial value for the client-info value for this entry
664  * @param meta metadata for the directory
665  * @param keywords under which keywords should this directory be available
666  *         directly; can be NULL
667  * @param anonymity what is the desired anonymity level for sharing?
668  * @param priority what is the priority for OUR node to
669  *   keep this file available?  Use 0 for maximum anonymity and
670  *   minimum reliability...
671  * @param expirationTime when should this content expire?
672  * @return publish structure entry for the directory , NULL on error
673  */
674 struct GNUNET_FS_FileInformation *
675 GNUNET_FS_file_information_create_empty_directory (void *client_info,
676                                                    const struct GNUNET_FS_Uri *keywords,
677                                                    const struct GNUNET_CONTAINER_MetaData *meta,
678                                                    uint32_t anonymity,
679                                                    uint32_t priority,
680                                                    struct GNUNET_TIME_Absolute expirationTime)
681 {
682   struct GNUNET_FS_FileInformation *ret;
683
684   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
685   ret->client_info = client_info;
686   ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
687   ret->keywords = GNUNET_FS_uri_dup (keywords);
688   ret->expirationTime = expirationTime;
689   ret->is_directory = GNUNET_YES;
690   ret->anonymity = anonymity;
691   ret->priority = priority;
692   GNUNET_FS_file_information_sync (ret);
693   return ret;
694 }
695
696
697 /**
698  * Add an entry to a directory in a publish-structure.  Clients
699  * should never modify publish structures that were passed to
700  * "GNUNET_FS_publish_start" already.
701  *
702  * @param dir the directory
703  * @param ent the entry to add; the entry must not have been
704  *            added to any other directory at this point and 
705  *            must not include "dir" in its structure
706  * @return GNUNET_OK on success, GNUNET_SYSERR on error
707  */
708 int
709 GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir,
710                                 struct GNUNET_FS_FileInformation *ent)
711 {
712   if ( (ent->dir != NULL) ||
713        (ent->next != NULL) ||
714        (! dir->is_directory) )
715     {
716       GNUNET_break (0);
717       return GNUNET_SYSERR;
718     }
719   ent->dir = dir;
720   ent->next = dir->data.dir.entries;
721   dir->data.dir.entries = ent;
722   dir->data.dir.dir_size = 0;
723   GNUNET_FS_file_information_sync (ent);
724   GNUNET_FS_file_information_sync (dir);
725   return GNUNET_OK;
726 }
727
728
729 /**
730  * Inspect a file or directory in a publish-structure.  Clients
731  * should never modify publish structures that were passed to
732  * "GNUNET_FS_publish_start" already.  When called on a directory,
733  * this function will FIRST call "proc" with information about
734  * the directory itself and then for each of the files in the
735  * directory (but not for files in subdirectories).  When called
736  * on a file, "proc" will be called exactly once (with information
737  * about the specific file).
738  *
739  * @param dir the directory
740  * @param proc function to call on each entry
741  * @param proc_cls closure for proc
742  */
743 void
744 GNUNET_FS_file_information_inspect (struct GNUNET_FS_FileInformation *dir,
745                                     GNUNET_FS_FileInformationProcessor proc,
746                                     void *proc_cls)
747 {
748   struct GNUNET_FS_FileInformation *pos;
749
750   if (dir->is_directory)
751     {
752       proc (proc_cls, 
753             dir,
754             dir->data.dir.dir_size,
755             dir->meta,
756             &dir->keywords,
757             &dir->anonymity,
758             &dir->priority,
759             &dir->expirationTime,
760             &dir->client_info);
761       pos = dir->data.dir.entries;
762       while (pos != NULL)
763         {
764           proc (proc_cls, 
765                 pos,
766                 (pos->is_directory) ? pos->data.dir.dir_size : pos->data.file.file_size,
767                 pos->meta,
768                 &pos->keywords,
769                 &pos->anonymity,
770                 &pos->priority,
771                 &pos->expirationTime,
772                 &pos->client_info);
773           pos = pos->next;
774         }
775     }
776   else
777     {
778       proc (proc_cls, 
779             dir,
780             dir->data.file.file_size,
781             dir->meta,
782             &dir->keywords,
783             &dir->anonymity,
784             &dir->priority,
785             &dir->expirationTime,
786             &dir->client_info);
787     }
788 }
789
790
791 /**
792  * Destroy publish-structure.  Clients should never destroy publish
793  * structures that were passed to "GNUNET_FS_publish_start" already.
794  *
795  * @param fi structure to destroy
796  * @param cleaner function to call on each entry in the structure
797  *        (useful to clean up client_info); can be NULL; return
798  *        values are ignored
799  * @param cleaner_cls closure for cleaner
800  */
801 void
802 GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi,
803                                     GNUNET_FS_FileInformationProcessor cleaner,
804                                     void *cleaner_cls)
805 {
806   struct GNUNET_FS_FileInformation *pos;
807
808   if (fi->is_directory)
809     {
810       /* clean up directory */
811       while (NULL != (pos = fi->data.dir.entries))
812         {
813           fi->data.dir.entries = pos->next;
814           GNUNET_FS_file_information_destroy (pos, cleaner, cleaner_cls);
815         }
816       /* clean up client-info */
817       if (NULL != cleaner)
818         cleaner (cleaner_cls, 
819                  fi,
820                  fi->data.dir.dir_size,
821                  fi->meta,
822                  &fi->keywords,
823                  &fi->anonymity,
824                  &fi->priority,
825                  &fi->expirationTime,
826                  &fi->client_info);
827       GNUNET_free_non_null (fi->data.dir.dir_data);
828       GNUNET_free_non_null (fi->data.dir.dirname);
829     }
830   else
831     {
832       /* call clean-up function of the reader */
833       fi->data.file.reader (fi->data.file.reader_cls, 0, 0, NULL, NULL);
834       /* clean up client-info */
835       if (NULL != cleaner)
836         cleaner (cleaner_cls, 
837                  fi,
838                  fi->data.file.file_size,
839                  fi->meta,
840                  &fi->keywords,
841                  &fi->anonymity,
842                  &fi->priority,
843                  &fi->expirationTime,
844                  &fi->client_info);
845     }
846   GNUNET_free_non_null (fi->emsg);
847   GNUNET_free_non_null (fi->chk_uri);
848   /* clean up serialization */
849   if ( (NULL != fi->serialization) &&
850        (0 != UNLINK (fi->serialization)) )
851     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
852                               "unlink",
853                               fi->serialization);
854   if (NULL != fi->keywords)
855     GNUNET_FS_uri_destroy (fi->keywords);
856   GNUNET_CONTAINER_meta_data_destroy (fi->meta);
857   GNUNET_free_non_null (fi->serialization);
858   if (fi->te != NULL)
859     {
860       GNUNET_FS_tree_encoder_finish (fi->te,
861                                      NULL, NULL);
862       fi->te = NULL;
863     }
864   GNUNET_free (fi);
865 }
866
867
868 /* end of fs_file_information.c */