Remove win32 and cygwin support
[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      SPDX-License-Identifier: AGPL3.0-or-later
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 #if HAVE_EXTRACTOR_H
28 #include <extractor.h>
29 #endif
30 #include "gnunet_fs_service.h"
31 #include "fs_api.h"
32 #include "fs_tree.h"
33
34
35 /**
36  * Obtain the name under which this file information
37  * structure is stored on disk.  Only works for top-level
38  * file information structures.
39  *
40  * @param s structure to get the filename for
41  * @return NULL on error, otherwise filename that
42  *         can be used to read this fi-struct from disk.
43  */
44 const char *
45 GNUNET_FS_file_information_get_id(struct GNUNET_FS_FileInformation *s)
46 {
47   if (NULL != s->dir)
48     return NULL;
49   return s->serialization;
50 }
51
52 /**
53  * Obtain the filename from the file information structure.
54  *
55  * @param s structure to get the filename for
56  * @return "filename" field of the structure (can be NULL)
57  */
58 const char *
59 GNUNET_FS_file_information_get_filename(struct GNUNET_FS_FileInformation *s)
60 {
61   return s->filename;
62 }
63
64
65 /**
66  * Set the filename in the file information structure.
67  * If filename was already set, frees it before setting the new one.
68  * Makes a copy of the argument.
69  *
70  * @param s structure to get the filename for
71  * @param filename filename to set
72  */
73 void
74 GNUNET_FS_file_information_set_filename(struct GNUNET_FS_FileInformation *s,
75                                         const char *filename)
76 {
77   GNUNET_free_non_null(s->filename);
78   if (filename)
79     s->filename = GNUNET_strdup(filename);
80   else
81     s->filename = NULL;
82 }
83
84
85 /**
86  * Create an entry for a file in a publish-structure.
87  *
88  * @param h handle to the file sharing subsystem
89  * @param client_info initial value for the client-info value for this entry
90  * @param filename name of the file or directory to publish
91  * @param keywords under which keywords should this file be available
92  *         directly; can be NULL
93  * @param meta metadata for the file
94  * @param do_index #GNUNET_YES for index, #GNUNET_NO for insertion,
95  *                #GNUNET_SYSERR for simulation
96  * @param bo block options
97  * @return publish structure entry for the file
98  */
99 struct GNUNET_FS_FileInformation *
100 GNUNET_FS_file_information_create_from_file(
101   struct GNUNET_FS_Handle *h,
102   void *client_info,
103   const char *filename,
104   const struct GNUNET_FS_Uri *keywords,
105   const struct GNUNET_CONTAINER_MetaData *meta,
106   int do_index,
107   const struct GNUNET_FS_BlockOptions *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   /* FIXME: should include_symbolic_links be GNUNET_NO or GNUNET_YES here? */
116   if (GNUNET_OK !=
117       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,
130                                                   client_info,
131                                                   fsize,
132                                                   &GNUNET_FS_data_reader_file_,
133                                                   fi,
134                                                   keywords,
135                                                   meta,
136                                                   do_index,
137                                                   bo);
138   if (ret == NULL)
139     return NULL;
140   ret->h = h;
141   ret->filename = GNUNET_strdup(filename);
142   fn = filename;
143   while (NULL != (ss = strstr(fn, DIR_SEPARATOR_STR)))
144     fn = ss + 1;
145 /* FIXME: If we assume that on other platforms CRT is UTF-8-aware, then
146  * this should be changed to EXTRACTOR_METAFORMAT_UTF8
147  */
148   GNUNET_CONTAINER_meta_data_insert(ret->meta,
149                                     "<gnunet>",
150                                     EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
151                                     EXTRACTOR_METAFORMAT_C_STRING,
152                                     "text/plain",
153                                     fn,
154                                     strlen(fn) + 1);
155   return ret;
156 }
157
158
159 /**
160  * Create an entry for a file in a publish-structure.
161  *
162  * @param h handle to the file sharing subsystem
163  * @param client_info initial value for the client-info value for this entry
164  * @param length length of the file
165  * @param data data for the file (should not be used afterwards by
166  *        the caller; callee will "free")
167  * @param keywords under which keywords should this file be available
168  *         directly; can be NULL
169  * @param meta metadata for the file
170  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
171  *                GNUNET_SYSERR for simulation
172  * @param bo block options
173  * @return publish structure entry for the file
174  */
175 struct GNUNET_FS_FileInformation *
176 GNUNET_FS_file_information_create_from_data(
177   struct GNUNET_FS_Handle *h,
178   void *client_info,
179   uint64_t length,
180   void *data,
181   const struct GNUNET_FS_Uri *keywords,
182   const struct GNUNET_CONTAINER_MetaData *meta,
183   int do_index,
184   const struct GNUNET_FS_BlockOptions *bo)
185 {
186   if (GNUNET_YES == do_index)
187     {
188       GNUNET_break(0);
189       return NULL;
190     }
191   return GNUNET_FS_file_information_create_from_reader(h,
192                                                        client_info,
193                                                        length,
194                                                        &GNUNET_FS_data_reader_copy_,
195                                                        data,
196                                                        keywords,
197                                                        meta,
198                                                        do_index,
199                                                        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(
221   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 *keywords,
227   const struct GNUNET_CONTAINER_MetaData *meta,
228   int do_index,
229   const struct GNUNET_FS_BlockOptions *bo)
230 {
231   struct GNUNET_FS_FileInformation *ret;
232
233   if ((GNUNET_YES == do_index) && (reader != &GNUNET_FS_data_reader_file_))
234     {
235       GNUNET_break(0);
236       return NULL;
237     }
238   ret = GNUNET_new(struct GNUNET_FS_FileInformation);
239   ret->h = h;
240   ret->client_info = client_info;
241   ret->meta = GNUNET_CONTAINER_meta_data_duplicate(meta);
242   if (ret->meta == NULL)
243     ret->meta = GNUNET_CONTAINER_meta_data_create();
244   ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup(keywords);
245   ret->data.file.reader = reader;
246   ret->data.file.reader_cls = reader_cls;
247   ret->data.file.do_index = do_index;
248   ret->data.file.file_size = length;
249   ret->bo = *bo;
250   return ret;
251 }
252
253
254 /**
255  * Test if a given entry represents a directory.
256  *
257  * @param ent check if this FI represents a directory
258  * @return #GNUNET_YES if so, #GNUNET_NO if not
259  */
260 int
261 GNUNET_FS_file_information_is_directory(
262   const struct GNUNET_FS_FileInformation *ent)
263 {
264   return ent->is_directory;
265 }
266
267
268 /**
269  * Create an entry for an empty directory in a publish-structure.
270  *
271  * @param h handle to the file sharing subsystem
272  * @param client_info initial value for the client-info value for this entry
273  * @param meta metadata for the directory
274  * @param keywords under which keywords should this directory be available
275  *         directly; can be NULL
276  * @param bo block options
277  * @param filename name of the directory; can be NULL
278  * @return publish structure entry for the directory , NULL on error
279  */
280 struct GNUNET_FS_FileInformation *
281 GNUNET_FS_file_information_create_empty_directory(
282   struct GNUNET_FS_Handle *h,
283   void *client_info,
284   const struct GNUNET_FS_Uri *keywords,
285   const struct GNUNET_CONTAINER_MetaData *meta,
286   const struct GNUNET_FS_BlockOptions *bo,
287   const char *filename)
288 {
289   struct GNUNET_FS_FileInformation *ret;
290
291   ret = GNUNET_new(struct GNUNET_FS_FileInformation);
292   ret->h = h;
293   ret->client_info = client_info;
294   ret->meta = GNUNET_CONTAINER_meta_data_duplicate(meta);
295   ret->keywords = GNUNET_FS_uri_dup(keywords);
296   ret->bo = *bo;
297   ret->is_directory = GNUNET_YES;
298   if (filename != NULL)
299     ret->filename = GNUNET_strdup(filename);
300   return ret;
301 }
302
303
304 /**
305  * Add an entry to a directory in a publish-structure.  Clients
306  * should never modify publish structures that were passed to
307  * #GNUNET_FS_publish_start already.
308  *
309  * @param dir the directory
310  * @param ent the entry to add; the entry must not have been
311  *            added to any other directory at this point and
312  *            must not include @a dir in its structure
313  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
314  */
315 int
316 GNUNET_FS_file_information_add(struct GNUNET_FS_FileInformation *dir,
317                                struct GNUNET_FS_FileInformation *ent)
318 {
319   if ((ent->dir != NULL) || (ent->next != NULL) ||
320       (dir->is_directory != GNUNET_YES))
321     {
322       GNUNET_break(0);
323       return GNUNET_SYSERR;
324     }
325   ent->dir = dir;
326   ent->next = dir->data.dir.entries;
327   dir->data.dir.entries = ent;
328   dir->data.dir.dir_size = 0;
329   return GNUNET_OK;
330 }
331
332
333 /**
334  * Inspect a file or directory in a publish-structure.  Clients
335  * should never modify publish structures that were passed to
336  * #GNUNET_FS_publish_start already.  When called on a directory,
337  * this function will FIRST call @a proc with information about
338  * the directory itself and then for each of the files in the
339  * directory (but not for files in subdirectories).  When called
340  * on a file, @a proc will be called exactly once (with information
341  * about the specific file).
342  *
343  * @param dir the directory
344  * @param proc function to call on each entry
345  * @param proc_cls closure for @a proc
346  */
347 void
348 GNUNET_FS_file_information_inspect(struct GNUNET_FS_FileInformation *dir,
349                                    GNUNET_FS_FileInformationProcessor proc,
350                                    void *proc_cls)
351 {
352   struct GNUNET_FS_FileInformation *pos;
353   int no;
354
355   no = GNUNET_NO;
356   if (GNUNET_OK !=
357       proc(proc_cls,
358            dir,
359            (dir->is_directory == GNUNET_YES) ? dir->data.dir.dir_size
360            : dir->data.file.file_size,
361            dir->meta,
362            &dir->keywords,
363            &dir->bo,
364            (dir->is_directory == GNUNET_YES) ? &no : &dir->data.file.do_index,
365            &dir->client_info))
366     return;
367   if (dir->is_directory != GNUNET_YES)
368     return;
369   pos = dir->data.dir.entries;
370   while (pos != NULL)
371     {
372       no = GNUNET_NO;
373       if (GNUNET_OK !=
374           proc(proc_cls,
375                pos,
376                (pos->is_directory == GNUNET_YES) ? pos->data.dir.dir_size
377                : pos->data.file.file_size,
378                pos->meta,
379                &pos->keywords,
380                &pos->bo,
381                (pos->is_directory == GNUNET_YES) ? &no
382                : &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,
420                 fi,
421                 fi->data.dir.dir_size,
422                 fi->meta,
423                 &fi->keywords,
424                 &fi->bo,
425                 &no,
426                 &fi->client_info);
427       GNUNET_free_non_null(fi->data.dir.dir_data);
428     }
429   else
430     {
431       /* call clean-up function of the reader */
432       if (NULL != fi->data.file.reader)
433         {
434           (void)fi->data.file.reader(fi->data.file.reader_cls, 0, 0, NULL, NULL);
435           fi->data.file.reader = NULL;
436         }
437       /* clean up client-info */
438       if (NULL != cleaner)
439         cleaner(cleaner_cls,
440                 fi,
441                 fi->data.file.file_size,
442                 fi->meta,
443                 &fi->keywords,
444                 &fi->bo,
445                 &fi->data.file.do_index,
446                 &fi->client_info);
447     }
448   GNUNET_free_non_null(fi->filename);
449   GNUNET_free_non_null(fi->emsg);
450   if (NULL != fi->sks_uri)
451     GNUNET_FS_uri_destroy(fi->sks_uri);
452   if (NULL != fi->chk_uri)
453     GNUNET_FS_uri_destroy(fi->chk_uri);
454   /* clean up serialization */
455   if ((NULL != fi->serialization) && (0 != unlink(fi->serialization)))
456     GNUNET_log_strerror_file(GNUNET_ERROR_TYPE_WARNING,
457                              "unlink",
458                              fi->serialization);
459   if (NULL != fi->keywords)
460     GNUNET_FS_uri_destroy(fi->keywords);
461   if (NULL != fi->meta)
462     GNUNET_CONTAINER_meta_data_destroy(fi->meta);
463   GNUNET_free_non_null(fi->serialization);
464   if (NULL != fi->te)
465     {
466       GNUNET_FS_tree_encoder_finish(fi->te, NULL);
467       fi->te = NULL;
468     }
469   GNUNET_free(fi);
470 }
471
472
473 /* end of fs_file_information.c */