-cleaner version of sharetree trim code; should be fast enough to run synchrnously
[oweals/gnunet.git] / src / fs / fs_file_information.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2011 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 3, 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  * - metadata filename clean up code
28  * - metadata/ksk generation for directories from contained files
29  */
30 #include "platform.h"
31 #include <extractor.h>
32 #include "gnunet_fs_service.h"
33 #include "fs_api.h"
34 #include "fs_tree.h"
35
36
37 /**
38  * Add meta data that libextractor finds to our meta data
39  * container.
40  *
41  * @param cls closure, our meta data container
42  * @param plugin_name name of the plugin that produced this value;
43  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
44  *        used in the main libextractor library and yielding
45  *        meta data).
46  * @param type libextractor-type describing the meta data
47  * @param format basic format information about data
48  * @param data_mime_type mime-type of data (not of the original file);
49  *        can be NULL (if mime-type is not known)
50  * @param data actual meta-data found
51  * @param data_len number of bytes in data
52  * @return always 0 to continue extracting
53  */
54 static int
55 add_to_md (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
56            enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
57            const char *data, size_t data_len)
58 {
59   struct GNUNET_CONTAINER_MetaData *md = cls;
60
61   (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
62                                             data_mime_type, data, data_len);
63   return 0;
64 }
65
66
67 /**
68  * Extract meta-data from a file.
69  *
70  * @return GNUNET_SYSERR on error, otherwise the number
71  *   of meta-data items obtained
72  */
73 int
74 GNUNET_FS_meta_data_extract_from_file (struct GNUNET_CONTAINER_MetaData *md,
75                                        const char *filename,
76                                        struct EXTRACTOR_PluginList *extractors)
77 {
78   int old;
79
80   if (filename == NULL)
81     return GNUNET_SYSERR;
82   if (extractors == NULL)
83     return 0;
84   old = GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL);
85   GNUNET_assert (old >= 0);
86   EXTRACTOR_extract (extractors, filename, NULL, 0, &add_to_md, md);
87   return (GNUNET_CONTAINER_meta_data_iterate (md, NULL, NULL) - old);
88 }
89
90
91
92 /**
93  * Obtain the name under which this file information
94  * structure is stored on disk.  Only works for top-level
95  * file information structures.
96  *
97  * @param s structure to get the filename for
98  * @return NULL on error, otherwise filename that
99  *         can be passed to "GNUNET_FS_file_information_recover"
100  *         to read this fi-struct from disk.
101  */
102 const char *
103 GNUNET_FS_file_information_get_id (struct GNUNET_FS_FileInformation *s)
104 {
105   if (NULL != s->dir)
106     return NULL;
107   return s->serialization;
108 }
109
110 /**
111  * Obtain the filename from the file information structure.
112  *
113  * @param s structure to get the filename for
114  * @return "filename" field of the structure (can be NULL)
115  */
116 const char *
117 GNUNET_FS_file_information_get_filename (struct GNUNET_FS_FileInformation *s)
118 {
119   return s->filename;
120 }
121
122
123 /**
124  * Set the filename in the file information structure.
125  * If filename was already set, frees it before setting the new one.
126  * Makes a copy of the argument.
127  *
128  * @param s structure to get the filename for
129  * @param filename filename to set
130  */
131 void
132 GNUNET_FS_file_information_set_filename (struct GNUNET_FS_FileInformation *s,
133                                          const char *filename)
134 {
135   GNUNET_free_non_null (s->filename);
136   if (filename)
137     s->filename = GNUNET_strdup (filename);
138   else
139     s->filename = NULL;
140 }
141
142 /**
143  * Create an entry for a file in a publish-structure.
144  *
145  * @param h handle to the file sharing subsystem
146  * @param client_info initial value for the client-info value for this entry
147  * @param filename name of the file or directory to publish
148  * @param keywords under which keywords should this file be available
149  *         directly; can be NULL
150  * @param meta metadata for the file
151  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
152  *                GNUNET_SYSERR for simulation
153  * @param bo block options
154  * @return publish structure entry for the file
155  */
156 struct GNUNET_FS_FileInformation *
157 GNUNET_FS_file_information_create_from_file (struct GNUNET_FS_Handle *h,
158                                              void *client_info,
159                                              const char *filename,
160                                              const struct GNUNET_FS_Uri
161                                              *keywords,
162                                              const struct
163                                              GNUNET_CONTAINER_MetaData *meta,
164                                              int do_index,
165                                              const struct GNUNET_FS_BlockOptions
166                                              *bo)
167 {
168   struct FileInfo *fi;
169   struct stat sbuf;
170   struct GNUNET_FS_FileInformation *ret;
171   const char *fn;
172   const char *ss;
173
174 #if WINDOWS
175   char fn_conv[MAX_PATH];
176 #endif
177
178   if (0 != STAT (filename, &sbuf))
179   {
180     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
181     return NULL;
182   }
183   fi = GNUNET_FS_make_file_reader_context_ (filename);
184   if (fi == NULL)
185   {
186     GNUNET_break (0);
187     return NULL;
188   }
189   ret =
190       GNUNET_FS_file_information_create_from_reader (h, client_info,
191                                                      sbuf.st_size,
192                                                      &GNUNET_FS_data_reader_file_,
193                                                      fi, keywords, meta,
194                                                      do_index, bo);
195   if (ret == NULL)
196     return NULL;
197   ret->h = h;
198   ret->filename = GNUNET_strdup (filename);
199 #if !WINDOWS
200   fn = filename;
201 #else
202   plibc_conv_to_win_path (filename, fn_conv);
203   fn = fn_conv;
204 #endif
205   while (NULL != (ss = strstr (fn, DIR_SEPARATOR_STR)))
206     fn = ss + 1;
207 #if !WINDOWS
208   GNUNET_CONTAINER_meta_data_insert (ret->meta, "<gnunet>",
209                                      EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
210                                      EXTRACTOR_METAFORMAT_C_STRING,
211                                      "text/plain", fn, strlen (fn) + 1);
212 #else
213   GNUNET_CONTAINER_meta_data_insert (ret->meta, "<gnunet>",
214                                      EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
215                                      EXTRACTOR_METAFORMAT_UTF8,
216                                      "text/plain", fn, strlen (fn) + 1);
217 #endif
218   return ret;
219 }
220
221
222 /**
223  * Create an entry for a file in a publish-structure.
224  *
225  * @param h handle to the file sharing subsystem
226  * @param client_info initial value for the client-info value for this entry
227  * @param length length of the file
228  * @param data data for the file (should not be used afterwards by
229  *        the caller; callee will "free")
230  * @param keywords under which keywords should this file be available
231  *         directly; can be NULL
232  * @param meta metadata for the file
233  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
234  *                GNUNET_SYSERR for simulation
235  * @param bo block options
236  * @return publish structure entry for the file
237  */
238 struct GNUNET_FS_FileInformation *
239 GNUNET_FS_file_information_create_from_data (struct GNUNET_FS_Handle *h,
240                                              void *client_info, uint64_t length,
241                                              void *data,
242                                              const struct GNUNET_FS_Uri
243                                              *keywords,
244                                              const struct
245                                              GNUNET_CONTAINER_MetaData *meta,
246                                              int do_index,
247                                              const struct GNUNET_FS_BlockOptions
248                                              *bo)
249 {
250   if (GNUNET_YES == do_index)
251   {
252     GNUNET_break (0);
253     return NULL;
254   }
255   return GNUNET_FS_file_information_create_from_reader (h, client_info, length,
256                                                         &GNUNET_FS_data_reader_copy_,
257                                                         data, keywords, meta,
258                                                         do_index, bo);
259 }
260
261
262 /**
263  * Create an entry for a file in a publish-structure.
264  *
265  * @param h handle to the file sharing subsystem
266  * @param client_info initial value for the client-info value for this entry
267  * @param length length of the file
268  * @param reader function that can be used to obtain the data for the file
269  * @param reader_cls closure for "reader"
270  * @param keywords under which keywords should this file be available
271  *         directly; can be NULL
272  * @param meta metadata for the file
273  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
274  *                GNUNET_SYSERR for simulation
275  * @param bo block options
276  * @return publish structure entry for the file
277  */
278 struct GNUNET_FS_FileInformation *
279 GNUNET_FS_file_information_create_from_reader (struct GNUNET_FS_Handle *h,
280                                                void *client_info,
281                                                uint64_t length,
282                                                GNUNET_FS_DataReader reader,
283                                                void *reader_cls,
284                                                const struct GNUNET_FS_Uri
285                                                *keywords,
286                                                const struct
287                                                GNUNET_CONTAINER_MetaData *meta,
288                                                int do_index,
289                                                const struct
290                                                GNUNET_FS_BlockOptions *bo)
291 {
292   struct GNUNET_FS_FileInformation *ret;
293
294   if ((GNUNET_YES == do_index) && (reader != &GNUNET_FS_data_reader_file_))
295   {
296     GNUNET_break (0);
297     return NULL;
298   }
299   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
300   ret->h = h;
301   ret->client_info = client_info;
302   ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
303   if (ret->meta == NULL)
304     ret->meta = GNUNET_CONTAINER_meta_data_create ();
305   ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup (keywords);
306   ret->data.file.reader = reader;
307   ret->data.file.reader_cls = reader_cls;
308   ret->data.file.do_index = do_index;
309   ret->data.file.file_size = length;
310   ret->bo = *bo;
311   return ret;
312 }
313
314
315 /**
316  * Test if a given entry represents a directory.
317  *
318  * @param ent check if this FI represents a directory
319  * @return GNUNET_YES if so, GNUNET_NO if not
320  */
321 int
322 GNUNET_FS_file_information_is_directory (const struct GNUNET_FS_FileInformation
323                                          *ent)
324 {
325   return ent->is_directory;
326 }
327
328
329 /**
330  * Create an entry for an empty directory in a publish-structure.
331  * This function should be used by applications for which the
332  * use of "GNUNET_FS_file_information_create_from_directory"
333  * is not appropriate.
334  *
335  * @param h handle to the file sharing subsystem
336  * @param client_info initial value for the client-info value for this entry
337  * @param meta metadata for the directory
338  * @param keywords under which keywords should this directory be available
339  *         directly; can be NULL
340  * @param bo block options
341  * @param filename name of the directory; can be NULL
342  * @return publish structure entry for the directory , NULL on error
343  */
344 struct GNUNET_FS_FileInformation *
345 GNUNET_FS_file_information_create_empty_directory (struct GNUNET_FS_Handle *h,
346                                                    void *client_info,
347                                                    const struct GNUNET_FS_Uri
348                                                    *keywords,
349                                                    const struct
350                                                    GNUNET_CONTAINER_MetaData
351                                                    *meta,
352                                                    const struct
353                                                    GNUNET_FS_BlockOptions *bo,
354                                                    const char *filename)
355 {
356   struct GNUNET_FS_FileInformation *ret;
357
358   ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
359   ret->h = h;
360   ret->client_info = client_info;
361   ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
362   ret->keywords = GNUNET_FS_uri_dup (keywords);
363   ret->bo = *bo;
364   ret->is_directory = GNUNET_YES;
365   if (filename != NULL)
366     ret->filename = GNUNET_strdup (filename);
367   return ret;
368 }
369
370
371 /**
372  * Add an entry to a directory in a publish-structure.  Clients
373  * should never modify publish structures that were passed to
374  * "GNUNET_FS_publish_start" already.
375  *
376  * @param dir the directory
377  * @param ent the entry to add; the entry must not have been
378  *            added to any other directory at this point and
379  *            must not include "dir" in its structure
380  * @return GNUNET_OK on success, GNUNET_SYSERR on error
381  */
382 int
383 GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir,
384                                 struct GNUNET_FS_FileInformation *ent)
385 {
386   if ((ent->dir != NULL) || (ent->next != NULL) || (!dir->is_directory))
387   {
388     GNUNET_break (0);
389     return GNUNET_SYSERR;
390   }
391   ent->dir = dir;
392   ent->next = dir->data.dir.entries;
393   dir->data.dir.entries = ent;
394   dir->data.dir.dir_size = 0;
395   return GNUNET_OK;
396 }
397
398
399 /**
400  * Inspect a file or directory in a publish-structure.  Clients
401  * should never modify publish structures that were passed to
402  * "GNUNET_FS_publish_start" already.  When called on a directory,
403  * this function will FIRST call "proc" with information about
404  * the directory itself and then for each of the files in the
405  * directory (but not for files in subdirectories).  When called
406  * on a file, "proc" will be called exactly once (with information
407  * about the specific file).
408  *
409  * @param dir the directory
410  * @param proc function to call on each entry
411  * @param proc_cls closure for proc
412  */
413 void
414 GNUNET_FS_file_information_inspect (struct GNUNET_FS_FileInformation *dir,
415                                     GNUNET_FS_FileInformationProcessor proc,
416                                     void *proc_cls)
417 {
418   struct GNUNET_FS_FileInformation *pos;
419   int no;
420
421   no = GNUNET_NO;
422   if (GNUNET_OK !=
423       proc (proc_cls, dir,
424             (dir->is_directory) ? dir->data.dir.dir_size : dir->data.
425             file.file_size, dir->meta, &dir->keywords, &dir->bo,
426             (dir->is_directory) ? &no : &dir->data.file.do_index,
427             &dir->client_info))
428     return;
429   if (!dir->is_directory)
430     return;
431   pos = dir->data.dir.entries;
432   while (pos != NULL)
433   {
434     no = GNUNET_NO;
435     if (GNUNET_OK !=
436         proc (proc_cls, pos,
437               (pos->is_directory) ? pos->data.dir.dir_size : pos->data.
438               file.file_size, pos->meta, &pos->keywords, &pos->bo,
439               (pos->is_directory) ? &no : &pos->data.file.do_index,
440               &pos->client_info))
441       break;
442     pos = pos->next;
443   }
444 }
445
446
447 /**
448  * Destroy publish-structure.  Clients should never destroy publish
449  * structures that were passed to "GNUNET_FS_publish_start" already.
450  *
451  * @param fi structure to destroy
452  * @param cleaner function to call on each entry in the structure
453  *        (useful to clean up client_info); can be NULL; return
454  *        values are ignored
455  * @param cleaner_cls closure for cleaner
456  */
457 void
458 GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi,
459                                     GNUNET_FS_FileInformationProcessor cleaner,
460                                     void *cleaner_cls)
461 {
462   struct GNUNET_FS_FileInformation *pos;
463   int no;
464
465   no = GNUNET_NO;
466   if (fi->is_directory)
467   {
468     /* clean up directory */
469     while (NULL != (pos = fi->data.dir.entries))
470     {
471       fi->data.dir.entries = pos->next;
472       GNUNET_FS_file_information_destroy (pos, cleaner, cleaner_cls);
473     }
474     /* clean up client-info */
475     if (NULL != cleaner)
476       cleaner (cleaner_cls, fi, fi->data.dir.dir_size, fi->meta, &fi->keywords,
477                &fi->bo, &no, &fi->client_info);
478     GNUNET_free_non_null (fi->data.dir.dir_data);
479   }
480   else
481   {
482     /* call clean-up function of the reader */
483     if (fi->data.file.reader != NULL)
484       fi->data.file.reader (fi->data.file.reader_cls, 0, 0, NULL, NULL);
485     /* clean up client-info */
486     if (NULL != cleaner)
487       cleaner (cleaner_cls, fi, fi->data.file.file_size, fi->meta,
488                &fi->keywords, &fi->bo, &fi->data.file.do_index,
489                &fi->client_info);
490   }
491   GNUNET_free_non_null (fi->filename);
492   GNUNET_free_non_null (fi->emsg);
493   GNUNET_free_non_null (fi->chk_uri);
494   /* clean up serialization */
495   if ((NULL != fi->serialization) && (0 != UNLINK (fi->serialization)))
496     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
497                               fi->serialization);
498   if (NULL != fi->keywords)
499     GNUNET_FS_uri_destroy (fi->keywords);
500   if (NULL != fi->meta)
501     GNUNET_CONTAINER_meta_data_destroy (fi->meta);
502   GNUNET_free_non_null (fi->serialization);
503   if (fi->te != NULL)
504   {
505     GNUNET_FS_tree_encoder_finish (fi->te, NULL, NULL);
506     fi->te = NULL;
507   }
508   GNUNET_free (fi);
509 }
510
511
512 /* end of fs_file_information.c */