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