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