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