2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
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 2, or (at your
8 option) any later version.
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.
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.
22 * @file fs/fs_file_information.c
23 * @brief Manage information for publishing directory hierarchies
24 * @author Christian Grothoff
27 * - serialization/deserialization (& deserialization API)
28 * - metadata filename clean up code
29 * - metadata/ksk generation for directories from contained files
32 #include <extractor.h>
33 #include "gnunet_fs_service.h"
38 * Create a temporary file on disk to store the current
42 GNUNET_FS_file_information_sync (struct GNUNET_FS_FileInformation * fi)
44 if (NULL == fi->serialization)
46 fi->serialization = NULL; // FIXME -- need cfg!
53 * Load file information from the file to which
56 * @param fn name of the file to use
57 * @return NULL on error
59 struct GNUNET_FS_FileInformation *
60 GNUNET_FS_file_information_recover (const char *fn)
62 struct GNUNET_FS_FileInformation *ret;
70 * Obtain the name under which this file information
71 * structure is stored on disk. Only works for top-level
72 * file information structures.
74 * @param s structure to get the filename for
75 * @return NULL on error, otherwise filename that
76 * can be passed to "GNUNET_FS_file_information_recover"
77 * to read this fi-struct from disk.
80 GNUNET_FS_file_information_get_id (struct GNUNET_FS_FileInformation *s)
84 return s->serialization;
89 * Closure for "data_reader_file".
94 * Name of the file to read.
99 * File descriptor, NULL if it has not yet been opened.
101 struct GNUNET_DISK_FileHandle *fd;
106 * Function that provides data by reading from a file.
108 * @param cls closure (points to the file information)
109 * @param offset offset to read from; it is possible
110 * that the caller might need to go backwards
112 * @param max maximum number of bytes that should be
113 * copied to buf; readers are not allowed
114 * to provide less data unless there is an error;
115 * a value of "0" will be used at the end to allow
116 * the reader to clean up its internal state
117 * @param buf where the reader should write the data
118 * @param emsg location for the reader to store an error message
119 * @return number of bytes written, usually "max", 0 on error
122 data_reader_file(void *cls,
128 struct FileInfo *fi = cls;
134 GNUNET_DISK_file_close (fi->fd);
135 GNUNET_free (fi->filename);
141 fi->fd = GNUNET_DISK_file_open (fi->filename,
142 GNUNET_DISK_OPEN_READ);
145 GNUNET_asprintf (emsg,
146 _("Could not open file `%s': %s"),
152 GNUNET_DISK_file_seek (fi->fd, offset, GNUNET_DISK_SEEK_SET);
153 ret = GNUNET_DISK_file_read (fi->fd, buf, max);
156 GNUNET_asprintf (emsg,
157 _("Could not read file `%s': %s"),
164 GNUNET_asprintf (emsg,
165 _("Short read reading from file `%s'!"),
174 * Create an entry for a file in a publish-structure.
176 * @param client_info initial value for the client-info value for this entry
177 * @param filename name of the file or directory to publish
178 * @param keywords under which keywords should this file be available
179 * directly; can be NULL
180 * @param meta metadata for the file
181 * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
182 * GNUNET_SYSERR for simulation
183 * @param anonymity what is the desired anonymity level for sharing?
184 * @param priority what is the priority for OUR node to
185 * keep this file available? Use 0 for maximum anonymity and
186 * minimum reliability...
187 * @param expirationTime when should this content expire?
188 * @return publish structure entry for the file
190 struct GNUNET_FS_FileInformation *
191 GNUNET_FS_file_information_create_from_file (void *client_info,
192 const char *filename,
193 const struct GNUNET_FS_Uri *keywords,
194 const struct GNUNET_CONTAINER_MetaData *meta,
198 struct GNUNET_TIME_Absolute expirationTime)
203 if (0 != STAT (filename, &sbuf))
205 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
210 fi = GNUNET_malloc (sizeof(struct FileInfo));
211 fi->filename = GNUNET_strdup (filename);
212 return GNUNET_FS_file_information_create_from_reader (client_info,
226 * Function that provides data by copying from a buffer.
228 * @param cls closure (points to the buffer)
229 * @param offset offset to read from; it is possible
230 * that the caller might need to go backwards
232 * @param max maximum number of bytes that should be
233 * copied to buf; readers are not allowed
234 * to provide less data unless there is an error;
235 * a value of "0" will be used at the end to allow
236 * the reader to clean up its internal state
237 * @param buf where the reader should write the data
238 * @param emsg location for the reader to store an error message
239 * @return number of bytes written, usually "max", 0 on error
242 data_reader_copy(void *cls,
254 memcpy (buf, &data[offset], max);
260 * Create an entry for a file in a publish-structure.
262 * @param client_info initial value for the client-info value for this entry
263 * @param length length of the file
264 * @param data data for the file (should not be used afterwards by
265 * the caller; caller will "free")
266 * @param keywords under which keywords should this file be available
267 * directly; can be NULL
268 * @param meta metadata for the file
269 * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
270 * GNUNET_SYSERR for simulation
271 * @param anonymity what is the desired anonymity level for sharing?
272 * @param priority what is the priority for OUR node to
273 * keep this file available? Use 0 for maximum anonymity and
274 * minimum reliability...
275 * @param expirationTime when should this content expire?
276 * @return publish structure entry for the file
278 struct GNUNET_FS_FileInformation *
279 GNUNET_FS_file_information_create_from_data (void *client_info,
282 const struct GNUNET_FS_Uri *keywords,
283 const struct GNUNET_CONTAINER_MetaData *meta,
287 struct GNUNET_TIME_Absolute expirationTime)
289 return GNUNET_FS_file_information_create_from_reader (client_info,
303 * Create an entry for a file in a publish-structure.
305 * @param client_info initial value for the client-info value for this entry
306 * @param length length of the file
307 * @param reader function that can be used to obtain the data for the file
308 * @param reader_cls closure for "reader"
309 * @param keywords under which keywords should this file be available
310 * directly; can be NULL
311 * @param meta metadata for the file
312 * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
313 * GNUNET_SYSERR for simulation
314 * @param anonymity what is the desired anonymity level for sharing?
315 * @param priority what is the priority for OUR node to
316 * keep this file available? Use 0 for maximum anonymity and
317 * minimum reliability...
318 * @param expirationTime when should this content expire?
319 * @return publish structure entry for the file
321 struct GNUNET_FS_FileInformation *
322 GNUNET_FS_file_information_create_from_reader (void *client_info,
324 GNUNET_FS_DataReader reader,
326 const struct GNUNET_FS_Uri *keywords,
327 const struct GNUNET_CONTAINER_MetaData *meta,
331 struct GNUNET_TIME_Absolute expirationTime)
333 struct GNUNET_FS_FileInformation *ret;
335 ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
336 ret->client_info = client_info;
337 ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
338 ret->keywords = (keywords == NULL) ? NULL : GNUNET_FS_uri_dup (keywords);
339 ret->expirationTime = expirationTime;
340 ret->data.file.reader = reader;
341 ret->data.file.reader_cls = reader_cls;
342 ret->data.file.do_index = do_index;
343 ret->anonymity = anonymity;
344 ret->priority = priority;
345 GNUNET_FS_file_information_sync (ret);
351 * Closure for "dir_scan_cb".
356 * Metadata extractors to use.
358 struct EXTRACTOR_Extractor *extractors;
361 * Function to call on each directory entry.
363 GNUNET_FS_FileProcessor proc;
371 * Scanner to use for subdirectories.
373 GNUNET_FS_DirectoryScanner scanner;
376 * Closure for scanner.
381 * Set to an error message (if any).
386 * Should files be indexed?
391 * Desired anonymity level.
396 * Desired publishing priority.
401 * Expiration time for publication.
403 struct GNUNET_TIME_Absolute expiration;
408 * Function called on each entry in a file to
409 * cause default-publishing.
410 * @param cls closure (struct DirScanCls)
411 * @param filename name of the file to be published
412 * @return GNUNET_OK on success, GNUNET_SYSERR to abort
415 dir_scan_cb (void *cls,
416 const char *filename)
418 struct DirScanCls *dsc = cls;
420 struct GNUNET_FS_FileInformation *fi;
421 struct GNUNET_FS_Uri *ksk_uri;
422 struct GNUNET_FS_Uri *keywords;
423 struct GNUNET_CONTAINER_MetaData *meta;
425 if (0 != STAT (filename, &sbuf))
427 GNUNET_asprintf (&dsc->emsg,
428 _("`%s' failed on file `%s': %s"),
432 return GNUNET_SYSERR;
434 if (S_ISDIR (sbuf.st_mode))
436 fi = GNUNET_FS_file_information_create_from_directory (NULL,
447 GNUNET_assert (NULL != dsc->emsg);
448 return GNUNET_SYSERR;
453 meta = GNUNET_CONTAINER_meta_data_create ();
454 GNUNET_CONTAINER_meta_data_extract_from_file (meta,
457 // FIXME: remove path from filename in metadata!
458 keywords = GNUNET_FS_uri_ksk_create_from_meta_data (meta);
459 ksk_uri = GNUNET_FS_uri_ksk_canonicalize (keywords);
460 fi = GNUNET_FS_file_information_create_from_file (NULL,
468 GNUNET_CONTAINER_meta_data_destroy (meta);
469 GNUNET_FS_uri_destroy (keywords);
470 GNUNET_FS_uri_destroy (ksk_uri);
472 dsc->proc (dsc->proc_cls,
480 * Simple, useful default implementation of a directory scanner
481 * (GNUNET_FS_DirectoryScanner). This implementation expects to get a
482 * UNIX filename, will publish all files in the directory except hidden
483 * files (those starting with a "."). Metadata will be extracted
484 * using GNU libextractor; the specific list of plugins should be
485 * specified in "cls", passing NULL will disable (!) metadata
486 * extraction. Keywords will be derived from the metadata and be
487 * subject to default canonicalization. This is strictly a
488 * convenience function.
490 * @param cls must be of type "struct EXTRACTOR_Extractor*"
491 * @param dirname name of the directory to scan
492 * @param do_index should files be indexed or inserted
493 * @param anonymity desired anonymity level
494 * @param priority priority for publishing
495 * @param expirationTime expiration for publication
496 * @param proc function called on each entry
497 * @param proc_cls closure for proc
498 * @param emsg where to store an error message (on errors)
499 * @return GNUNET_OK on success
502 GNUNET_FS_directory_scanner_default (void *cls,
507 struct GNUNET_TIME_Absolute expirationTime,
508 GNUNET_FS_FileProcessor proc,
512 struct EXTRACTOR_Extractor *ex = cls;
513 struct DirScanCls dsc;
517 dsc.proc_cls = proc_cls;
518 dsc.scanner = &GNUNET_FS_directory_scanner_default;
519 dsc.scanner_cls = cls;
520 dsc.do_index = do_index;
521 dsc.anonymity = anonymity;
522 dsc.priority = priority;
523 dsc.expiration = expirationTime;
524 if (-1 == GNUNET_DISK_directory_scan (dirname,
528 GNUNET_assert (NULL != dsc.emsg);
530 return GNUNET_SYSERR;
537 * Closure for dirproc function.
542 * Linked list of directory entries that is being
545 struct GNUNET_FS_FileInformation *entries;
551 * Function that processes a directory entry that
552 * was obtained from the scanner.
553 * @param cls our closure
554 * @param filename name of the file (unused, why there???)
555 * @param fi information for publishing the file
559 const char *filename,
560 struct GNUNET_FS_FileInformation *fi)
562 struct EntryProcCls *dc = cls;
564 GNUNET_assert (fi->next == NULL);
565 GNUNET_assert (fi->dir == NULL);
566 fi->next = dc->entries;
572 * Create a publish-structure from an existing file hierarchy, inferring
573 * and organizing keywords and metadata as much as possible. This
574 * function primarily performs the recursive build and re-organizes
575 * keywords and metadata; for automatically getting metadata
576 * extraction, scanning of directories and creation of the respective
577 * GNUNET_FS_FileInformation entries the default scanner should be
578 * passed (GNUNET_FS_directory_scanner_default). This is strictly a
579 * convenience function.
581 * @param client_info initial value for the client-info value for this entry
582 * @param filename name of the top-level file or directory
583 * @param scanner function used to get a list of files in a directory
584 * @param scanner_cls closure for scanner
585 * @param do_index should files in the hierarchy be indexed?
586 * @param anonymity what is the desired anonymity level for sharing?
587 * @param priority what is the priority for OUR node to
588 * keep this file available? Use 0 for maximum anonymity and
589 * minimum reliability...
590 * @param expirationTime when should this content expire?
591 * @param emsg where to store an error message
592 * @return publish structure entry for the directory, NULL on error
594 struct GNUNET_FS_FileInformation *
595 GNUNET_FS_file_information_create_from_directory (void *client_info,
596 const char *filename,
597 GNUNET_FS_DirectoryScanner scanner,
602 struct GNUNET_TIME_Absolute expirationTime,
605 struct GNUNET_FS_FileInformation *ret;
606 struct EntryProcCls dc;
607 struct GNUNET_FS_Uri *ksk;
608 struct GNUNET_CONTAINER_MetaData *meta;
611 meta = GNUNET_CONTAINER_meta_data_create ();
612 GNUNET_FS_meta_data_make_directory (meta);
614 scanner (scanner_cls,
623 ksk = NULL; // FIXME...
624 // FIXME: create meta!
625 ret = GNUNET_FS_file_information_create_empty_directory (client_info,
631 ret->data.dir.entries = dc.entries;
632 while (dc.entries != NULL)
634 dc.entries->dir = ret;
635 GNUNET_FS_file_information_sync (dc.entries);
636 dc.entries = dc.entries->next;
638 GNUNET_FS_file_information_sync (ret);
644 * Create an entry for an empty directory in a publish-structure.
645 * This function should be used by applications for which the
646 * use of "GNUNET_FS_file_information_create_from_directory"
647 * is not appropriate.
649 * @param client_info initial value for the client-info value for this entry
650 * @param meta metadata for the directory
651 * @param keywords under which keywords should this directory be available
652 * directly; can be NULL
653 * @param anonymity what is the desired anonymity level for sharing?
654 * @param priority what is the priority for OUR node to
655 * keep this file available? Use 0 for maximum anonymity and
656 * minimum reliability...
657 * @param expirationTime when should this content expire?
658 * @return publish structure entry for the directory , NULL on error
660 struct GNUNET_FS_FileInformation *
661 GNUNET_FS_file_information_create_empty_directory (void *client_info,
662 const struct GNUNET_CONTAINER_MetaData *meta,
663 const struct GNUNET_FS_Uri *keywords,
666 struct GNUNET_TIME_Absolute expirationTime)
668 struct GNUNET_FS_FileInformation *ret;
670 ret = GNUNET_malloc (sizeof (struct GNUNET_FS_FileInformation));
671 ret->client_info = client_info;
672 ret->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
673 ret->keywords = GNUNET_FS_uri_dup (keywords);
674 ret->expirationTime = expirationTime;
675 ret->is_directory = GNUNET_YES;
676 ret->anonymity = anonymity;
677 ret->priority = priority;
678 GNUNET_FS_file_information_sync (ret);
684 * Add an entry to a directory in a publish-structure. Clients
685 * should never modify publish structures that were passed to
686 * "GNUNET_FS_publish_start" already.
688 * @param dir the directory
689 * @param ent the entry to add; the entry must not have been
690 * added to any other directory at this point and
691 * must not include "dir" in its structure
692 * @return GNUNET_OK on success, GNUNET_SYSERR on error
695 GNUNET_FS_file_information_add (struct GNUNET_FS_FileInformation *dir,
696 struct GNUNET_FS_FileInformation *ent)
698 if ( (ent->dir != NULL) ||
699 (ent->next != NULL) ||
700 (! dir->is_directory) )
703 return GNUNET_SYSERR;
706 ent->next = dir->data.dir.entries;
707 dir->data.dir.entries = ent;
708 dir->data.dir.dir_size = 0;
709 GNUNET_FS_file_information_sync (ent);
710 GNUNET_FS_file_information_sync (dir);
716 * Inspect a file or directory in a publish-structure. Clients
717 * should never modify publish structures that were passed to
718 * "GNUNET_FS_publish_start" already. When called on a directory,
719 * this function will FIRST call "proc" with information about
720 * the directory itself and then for each of the files in the
721 * directory (but not for files in subdirectories). When called
722 * on a file, "proc" will be called exactly once (with information
723 * about the specific file).
725 * @param dir the directory
726 * @param proc function to call on each entry
727 * @param proc_cls closure for proc
730 GNUNET_FS_file_information_inspect (struct GNUNET_FS_FileInformation *dir,
731 GNUNET_FS_FileInformationProcessor proc,
734 struct GNUNET_FS_FileInformation *pos;
736 if (dir->is_directory)
740 dir->data.dir.dir_size,
745 &dir->expirationTime,
747 pos = dir->data.dir.entries;
752 pos->data.dir.dir_size,
757 &pos->expirationTime,
766 dir->data.file.file_size,
771 &dir->expirationTime,
778 * Destroy publish-structure. Clients should never destroy publish
779 * structures that were passed to "GNUNET_FS_publish_start" already.
781 * @param fi structure to destroy
782 * @param cleaner function to call on each entry in the structure
783 * (useful to clean up client_info); can be NULL; return
785 * @param cleaner_cls closure for cleaner
788 GNUNET_FS_file_information_destroy (struct GNUNET_FS_FileInformation *fi,
789 GNUNET_FS_FileInformationProcessor cleaner,
792 struct GNUNET_FS_FileInformation *pos;
794 if (fi->is_directory)
796 /* clean up directory */
797 while (NULL != (pos = fi->data.dir.entries))
799 fi->data.dir.entries = pos->next;
800 GNUNET_FS_file_information_destroy (pos, cleaner, cleaner_cls);
802 /* clean up client-info */
803 cleaner (cleaner_cls,
805 fi->data.dir.dir_size,
812 GNUNET_free_non_null (fi->data.dir.dir_data);
813 GNUNET_free (fi->data.dir.dirname);
817 /* call clean-up function of the reader */
818 fi->data.file.reader (fi->data.file.reader_cls, 0, 0, NULL, NULL);
819 /* clean up client-info */
820 cleaner (cleaner_cls,
822 fi->data.file.file_size,
830 GNUNET_free_non_null (fi->emsg);
831 /* clean up serialization */
832 if (0 != UNLINK (fi->serialization))
833 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
836 GNUNET_FS_uri_destroy (fi->keywords);
837 GNUNET_CONTAINER_meta_data_destroy (fi->meta);
838 GNUNET_free (fi->serialization);
843 /* end of fs_file_information.c */