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