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