*
* Note that this function maybe called on parts of directories. Thus
* parser errors should not be reported _at all_ (with GNUNET_break).
* Still, if some entries can be recovered despite these parsing
* errors, the function should try to do this.
*
* @param size number of bytes in data
* @param data pointer to the beginning of the directory
* @param offset offset of data in the directory
* @param dep function to call on each entry
* @param dep_cls closure for @a dep
* @return #GNUNET_OK if this could be a block in a directory,
* #GNUNET_NO if this could be part of a directory (but not 100% OK)
* #GNUNET_SYSERR if @a data does not represent a directory
*/
int
GNUNET_FS_directory_list_contents (size_t size,
const void *data,
uint64_t offset,
GNUNET_FS_DirectoryEntryProcessor dep,
void *dep_cls)
{
struct GetFullDataClosure full_data;
const char *cdata = data;
char *emsg;
uint64_t pos;
uint64_t align;
uint32_t mdSize;
uint64_t epos;
struct GNUNET_FS_Uri *uri;
struct GNUNET_CONTAINER_MetaData *md;
char *filename;
if ((offset == 0) &&
((size < 8 + sizeof (uint32_t)) ||
(0 != memcmp (cdata,
GNUNET_FS_DIRECTORY_MAGIC,
8))))
return GNUNET_SYSERR;
pos = offset;
if (offset == 0)
{
GNUNET_memcpy (&mdSize,
&cdata[8],
sizeof (uint32_t));
mdSize = ntohl (mdSize);
if (mdSize > size - 8 - sizeof (uint32_t))
{
/* invalid size */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
_("MAGIC mismatch. This is not a GNUnet directory.\n"));
return GNUNET_SYSERR;
}
md = GNUNET_CONTAINER_meta_data_deserialize (&cdata[8 + sizeof (uint32_t)],
mdSize);
if (md == NULL)
{
GNUNET_break (0);
return GNUNET_SYSERR; /* malformed ! */
}
dep (dep_cls,
NULL,
NULL,
md,
0,
NULL);
GNUNET_CONTAINER_meta_data_destroy (md);
pos = 8 + sizeof (uint32_t) + mdSize;
}
while (pos < size)
{
/* find end of URI */
if (cdata[pos] == '\0')
{
/* URI is never empty, must be end of block,
* skip to next alignment */
align = ((pos / DBLOCK_SIZE) + 1) * DBLOCK_SIZE;
if (align == pos)
{
/* if we were already aligned, still skip a block! */
align += DBLOCK_SIZE;
}
pos = align;
if (pos >= size)
{
/* malformed - or partial download... */
break;
}
}
epos = pos;
while ((epos < size) && (cdata[epos] != '\0'))
epos++;
if (epos >= size)
return GNUNET_NO; /* malformed - or partial download */
uri = GNUNET_FS_uri_parse (&cdata[pos], &emsg);
pos = epos + 1;
if (NULL == uri)
{
GNUNET_free (emsg);
pos--; /* go back to '\0' to force going to next alignment */
continue;
}
if (GNUNET_FS_uri_test_ksk (uri))
{
GNUNET_FS_uri_destroy (uri);
GNUNET_break (0);
return GNUNET_NO; /* illegal in directory! */
}
GNUNET_memcpy (&mdSize,
&cdata[pos],
sizeof (uint32_t));
mdSize = ntohl (mdSize);
pos += sizeof (uint32_t);
if (pos + mdSize > size)
{
GNUNET_FS_uri_destroy (uri);
return GNUNET_NO; /* malformed - or partial download */
}
md = GNUNET_CONTAINER_meta_data_deserialize (&cdata[pos],
mdSize);
if (NULL == md)
{
GNUNET_FS_uri_destroy (uri);
GNUNET_break (0);
return GNUNET_NO; /* malformed ! */
}
pos += mdSize;
filename =
GNUNET_CONTAINER_meta_data_get_by_type (md,
EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
full_data.size = 0;
full_data.data = NULL;
GNUNET_CONTAINER_meta_data_iterate (md,
&find_full_data,
&full_data);
if (NULL != dep)
{
dep (dep_cls,
filename,
uri,
md,
full_data.size,
full_data.data);
}
GNUNET_free_non_null (full_data.data);
GNUNET_free_non_null (filename);
GNUNET_CONTAINER_meta_data_destroy (md);
GNUNET_FS_uri_destroy (uri);
}
return GNUNET_OK;
}
/**
* Entries in the directory (builder).
*/
struct BuilderEntry
{
/**
* This is a linked list.
*/
struct BuilderEntry *next;
/**
* Length of this entry.
*/
size_t len;
};
/**
* Internal state of a directory builder.
*/
struct GNUNET_FS_DirectoryBuilder
{
/**
* Meta-data for the directory itself.
*/
struct GNUNET_CONTAINER_MetaData *meta;
/**
* Head of linked list of entries.
*/
struct BuilderEntry *head;
/**
* Number of entires in the directory.
*/
unsigned int count;
};
/**
* Create a directory builder.
*
* @param mdir metadata for the directory
*/
struct GNUNET_FS_DirectoryBuilder *
GNUNET_FS_directory_builder_create (const struct GNUNET_CONTAINER_MetaData
*mdir)
{
struct GNUNET_FS_DirectoryBuilder *ret;
ret = GNUNET_new (struct GNUNET_FS_DirectoryBuilder);
if (mdir != NULL)
ret->meta = GNUNET_CONTAINER_meta_data_duplicate (mdir);
else
ret->meta = GNUNET_CONTAINER_meta_data_create ();
GNUNET_FS_meta_data_make_directory (ret->meta);
return ret;
}
/**
* Add an entry to a directory.
*
* @param bld directory to extend
* @param uri uri of the entry (must not be a KSK)
* @param md metadata of the entry
* @param data raw data of the entry, can be NULL, otherwise
* data must point to exactly the number of bytes specified
* by the uri which must be of type LOC or CHK
*/
void
GNUNET_FS_directory_builder_add (struct GNUNET_FS_DirectoryBuilder *bld,
const struct GNUNET_FS_Uri *uri,
const struct GNUNET_CONTAINER_MetaData *md,
const void *data)
{
struct GNUNET_FS_Uri *curi;
struct BuilderEntry *e;
uint64_t fsize;
uint32_t big;
ssize_t ret;
size_t mds;
size_t mdxs;
char *uris;
char *ser;
char *sptr;
size_t slen;
struct GNUNET_CONTAINER_MetaData *meta;
const struct GNUNET_CONTAINER_MetaData *meta_use;
GNUNET_assert (!GNUNET_FS_uri_test_ksk (uri));
if (NULL != data)
{
GNUNET_assert (!GNUNET_FS_uri_test_sks (uri));
if (GNUNET_FS_uri_test_chk (uri))
{
fsize = GNUNET_FS_uri_chk_get_file_size (uri);
}
else
{
curi = GNUNET_FS_uri_loc_get_uri (uri);
GNUNET_assert (NULL != curi);
fsize = GNUNET_FS_uri_chk_get_file_size (curi);
GNUNET_FS_uri_destroy (curi);
}
}
else
{
fsize = 0; /* not given */
}
if (fsize > MAX_INLINE_SIZE)
fsize = 0; /* too large */
uris = GNUNET_FS_uri_to_string (uri);
slen = strlen (uris) + 1;
mds = GNUNET_CONTAINER_meta_data_get_serialized_size (md);
meta_use = md;
meta = NULL;
if (fsize > 0)
{
meta = GNUNET_CONTAINER_meta_data_duplicate (md);
GNUNET_CONTAINER_meta_data_insert (meta, "