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