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