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