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