#include <glib/gmessages.h>
#include "captive/macros.h"
#include "captive/storage.h"
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <libxml/tree.h>
+#include "captive/libxml.h"
+#include <ctype.h>
+#ifdef HAVE_LIBXML_BUFFERING
+#include <libxml/xmlreader.h>
+#endif
+#include "lib.h" /* for captive_giochannel_setup(); FIXME: pathname */
+
+
+/* CONFIG: */
+/* It should be the divisor of all offsets/sizes written by W32 filesystems.
+ */
+#define GIOCHANNEL_BLIND_BLOCK_SIZE 512
-#define GIOCHANNEL_BLIND_BLOCK_SIZE (PAGE_SIZE)
-
/* FIXME: fill 'err' */
struct captive_giochannel_blind {
GIOChannel iochannel;
- GIOChannel *giochannel_ro;
+ GIOChannel *giochannel_orig; /* reffed by us */
guint64 offset; /* gint64 range */
guint64 size;
- GHashTable *buffer_hash; /* (guint64 *) -> (guint8[GIOCHANNEL_BLIND_BLOCK_SIZE]) */
+ GHashTable *buffer_hash; /* (guint64 *) -> (struct blind_block *) (guint8[GIOCHANNEL_BLIND_BLOCK_SIZE]) */
+ };
+
+struct blind_block {
+ guint64 offset;
+ gboolean was_read,was_written;
+ guint8 *data_written; /* [GIOCHANNEL_BLIND_BLOCK_SIZE] */
};
static GIOFuncs giochannel_blind_funcs;
-static guint captive_giochannel_blind_hash_func(const guint64 *key)
+static guint captive_giochannel_blind_hash_func(const guint64 *keyp)
{
- g_return_val_if_fail(key!=NULL,0);
+ g_return_val_if_fail(keyp!=NULL,0);
- return (*key)^((*key)>>23);
+ return (*keyp)^((*keyp)>>23);
}
static gboolean captive_giochannel_blind_equal_func(const guint64 *ap,const guint64 *bp)
return (*ap)==(*bp);
}
-static void captive_giochannel_blind_key_destroy_func(guint64 *key)
+static void captive_giochannel_blind_key_destroy_func(guint64 *keyp)
{
- g_return_if_fail(key!=NULL);
+ g_return_if_fail(keyp!=NULL);
- g_free(key);
+ g_free(keyp);
}
-static void captive_giochannel_blind_value_destroy_func(void *data)
+static void captive_giochannel_blind_value_destroy_func(struct blind_block *blind_block)
{
- g_return_if_fail(data!=NULL);
+ g_return_if_fail(blind_block!=NULL);
- g_free(data);
+ g_free(blind_block->data_written);
+ g_free(blind_block);
}
static gboolean validate_giochannel_blind(struct captive_giochannel_blind *giochannel_blind)
{
+ g_return_val_if_fail(giochannel_blind->iochannel.funcs==&giochannel_blind_funcs,FALSE);
g_return_val_if_fail(giochannel_blind!=NULL,FALSE);
- g_return_val_if_fail(giochannel_blind->giochannel_ro!=NULL,FALSE);
+ /* 'giochannel_blind->giochannel_orig' may be NULL. */
g_return_val_if_fail((gint64)giochannel_blind->offset>=0,FALSE); /* gint64 overflow stored in guint64 */
g_return_val_if_fail(giochannel_blind->buffer_hash!=NULL,FALSE);
(GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err)
{
struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
-gpointer data;
guint64 window_bottom,window_top,window_now;
guint64 transfer_bottom,transfer_top;
GIOStatus errgiostatus;
guint64 maxread; /* maximum offset of end of data we successfuly read */
+struct blind_block *blind_block;
g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read(offset=0x%llX,count=0x%lX)",G_STRLOC,
giochannel_blind->offset,(gulong)count);
- window_bottom=CAPTIVE_ROUND_DOWN(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
- window_top=CAPTIVE_ROUND_UP(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
+ window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
+ window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
maxread=giochannel_blind->offset;
for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
transfer_bottom=MAX(window_now,giochannel_blind->offset);
transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
- if ((data=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now))) {
+ if ((blind_block=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now)) && blind_block->data_written) {
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read-from-memcpy(window_now=0x%llX,dest=buf+0x%lX,src=data+0x%lX,n=0x%lX)",
G_STRLOC,
(guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(gulong)(transfer_bottom-window_now),
(gulong)(transfer_top-transfer_bottom));
memcpy(
buf+transfer_bottom-giochannel_blind->offset, /* dest */
- data+transfer_bottom-window_now, /* src */
+ blind_block->data_written+transfer_bottom-window_now, /* src */
transfer_top-transfer_bottom); /* n */
+ blind_block->was_read=TRUE;
maxread=transfer_top;
continue;
}
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read-from-io(window_now=0x%llX,buf=buf+0x%lX,seek=0x%llX,count=0x%lX)",G_STRLOC,
(guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(guint64)transfer_bottom,
(gulong)(transfer_top-transfer_bottom));
+ if (!giochannel_blind->giochannel_orig) {
+ g_error("%s: Missing block at offset 0x%llX",G_STRLOC,(unsigned long long)window_now);
+ g_return_val_if_reached(G_IO_STATUS_ERROR);
+ }
errgiostatus=g_io_channel_seek_position(
- giochannel_blind->giochannel_ro, /* channel */
+ giochannel_blind->giochannel_orig, /* channel */
transfer_bottom, /* offset */
G_SEEK_SET, /* type */
err); /* error */
}
else {
errgiostatus=g_io_channel_read_chars(
- giochannel_blind->giochannel_ro, /* channel */
+ giochannel_blind->giochannel_orig, /* channel */
buf+transfer_bottom-giochannel_blind->offset, /* buf */
transfer_top-transfer_bottom, /* count */
&bytes_read, /* bytes_read */
g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
g_return_val_if_fail(bytes_read<=(transfer_top-transfer_bottom),G_IO_STATUS_ERROR);
g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
+ if (bytes_read) {
+ if (!blind_block) {
+guint64 *keyp;
+
+ captive_new(blind_block);
+ blind_block->offset=window_now;
+ blind_block->was_read=FALSE;
+ blind_block->was_written=FALSE;
+ blind_block->data_written=NULL;
+ captive_new(keyp);
+ *keyp=window_now;
+ g_hash_table_insert(
+ giochannel_blind->buffer_hash, /* hash_table */
+ keyp, /* key */
+ blind_block); /* value */
+ }
+ blind_block->was_read=TRUE;
+ }
maxread=transfer_bottom+bytes_read;
if (bytes_read==transfer_top-transfer_bottom)
g_return_val_if_fail(transfer_bottom+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
(GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err)
{
struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
-gpointer data;
guint64 window_bottom,window_top,window_now;
guint64 transfer_bottom,transfer_top;
GIOStatus errgiostatus;
+struct blind_block *blind_block;
g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
g_return_val_if_fail(giochannel_blind->offset+count<=giochannel_blind->size,G_IO_STATUS_ERROR);
- window_bottom=CAPTIVE_ROUND_DOWN(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
- window_top=CAPTIVE_ROUND_UP(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
+ g_return_val_if_fail(giochannel_blind->iochannel.is_writeable==TRUE,G_IO_STATUS_ERROR);
+
+ window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
+ window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
gsize bytes_read;
transfer_bottom=MAX(window_now,giochannel_blind->offset);
transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
- if (!(data=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now))) {
+ if (!(blind_block=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now)) || !blind_block->data_written) {
+ if (!blind_block) {
guint64 *keyp;
- data=g_malloc(GIOCHANNEL_BLIND_BLOCK_SIZE);
- captive_new(keyp);
- *keyp=window_now;
- g_hash_table_insert(
- giochannel_blind->buffer_hash, /* hash_table */
- keyp, /* key */
- data); /* value */
+ captive_new(blind_block);
+ blind_block->offset=window_now;
+ blind_block->was_read=FALSE;
+ blind_block->was_written=FALSE;
+ captive_new(keyp);
+ *keyp=window_now;
+ g_hash_table_insert(
+ giochannel_blind->buffer_hash, /* hash_table */
+ keyp, /* key */
+ blind_block); /* value */
+ }
+ blind_block->data_written=g_malloc(GIOCHANNEL_BLIND_BLOCK_SIZE);
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-new-mem(window_now=0x%llX)",G_STRLOC,
(guint64)window_now);
if (transfer_bottom>window_now) {
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-mem-read-lower(seek=0x%llX,count=0x%lX)",G_STRLOC,
(guint64)window_now,(gulong)(transfer_bottom-window_now));
+ if (!giochannel_blind->giochannel_orig) {
+ g_error("Missing block for partial read at offset 0x%llX",(unsigned long long)window_now);
+ g_assert_not_reached();
+ }
errgiostatus=g_io_channel_seek_position(
- giochannel_blind->giochannel_ro, /* channel */
+ giochannel_blind->giochannel_orig, /* channel */
window_now, /* offset */
G_SEEK_SET, /* type */
err); /* error */
g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
errgiostatus=g_io_channel_read_chars(
- giochannel_blind->giochannel_ro, /* channel */
- data, /* buf */
+ giochannel_blind->giochannel_orig, /* channel */
+ blind_block->data_written, /* buf */
transfer_bottom-window_now, /* count */
&bytes_read, /* bytes_read */
err); /* error */
g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
g_return_val_if_fail(bytes_read==(transfer_bottom-window_now),G_IO_STATUS_ERROR);
+ blind_block->was_read=TRUE; /* FIXME: Support non-block-aligned buffers. */
}
/* Missing upper part of buffer? */
g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-mem-read-upper(buf=buf+0x%lX,seek=0x%llX,count=0x%lX)",G_STRLOC,
(gulong)(transfer_top-window_now),(guint64)transfer_top,
(gulong)(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top));
+ if (!giochannel_blind->giochannel_orig) {
+ g_error("Missing block for partial read at offset 0x%llX",(unsigned long long)window_now);
+ g_assert_not_reached();
+ }
errgiostatus=g_io_channel_seek_position(
- giochannel_blind->giochannel_ro, /* channel */
+ giochannel_blind->giochannel_orig, /* channel */
transfer_top, /* offset */
G_SEEK_SET, /* type */
err); /* error */
g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
errgiostatus=g_io_channel_read_chars(
- giochannel_blind->giochannel_ro, /* channel */
- data+transfer_top-window_now, /* buf */
+ giochannel_blind->giochannel_orig, /* channel */
+ blind_block->data_written+transfer_top-window_now, /* buf */
window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top, /* count */
&bytes_read, /* bytes_read */
err); /* error */
g_return_val_if_fail(transfer_top+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
else
g_return_val_if_fail(transfer_top+bytes_read==giochannel_blind->size,G_IO_STATUS_ERROR); /* EOF hit */
+ blind_block->was_read=TRUE; /* FIXME: Support non-block-aligned buffers. */
}
}
G_STRLOC,
(guint64)window_now,(gulong)(transfer_bottom-window_now),(gulong)(transfer_bottom-giochannel_blind->offset),
(gulong)(transfer_top-transfer_bottom));
+ g_assert(blind_block); g_assert(blind_block->data_written);
memcpy(
- ((char *)data)+transfer_bottom-window_now, /* dest */
+ ((char *)blind_block->data_written)+transfer_bottom-window_now, /* dest */
buf+transfer_bottom-giochannel_blind->offset, /* src */
transfer_top-transfer_bottom); /* n */
+ blind_block->was_written=TRUE;
}
*bytes_written=count;
g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
- /* We are not authorized to destroy 'giochannel_blind->giochannel_ro'. */
- erriostatus=g_io_channel_flush(
- giochannel_blind->giochannel_ro, /* channel */
- NULL); /* error */
- g_assert(erriostatus==G_IO_STATUS_NORMAL);
- giochannel_blind->giochannel_ro=NULL;
+ if (giochannel_blind->giochannel_orig) {
+ /* Just a sanity if 'giochannel_orig' is already falsely reffed a bit more... */
+ erriostatus=g_io_channel_flush(
+ giochannel_blind->giochannel_orig, /* channel */
+ NULL); /* error */
+ g_assert(erriostatus==G_IO_STATUS_NORMAL);
+
+ g_io_channel_unref(giochannel_blind->giochannel_orig);
+ giochannel_blind->giochannel_orig=NULL;
+ }
g_hash_table_destroy(giochannel_blind->buffer_hash);
giochannel_blind->buffer_hash=NULL;
*/
g_return_if_fail(giochannel_blind!=NULL);
- g_assert(giochannel_blind->giochannel_ro==NULL);
+ g_assert(giochannel_blind->giochannel_orig==NULL);
g_assert(giochannel_blind->buffer_hash==NULL);
g_free(giochannel_blind);
g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
- return g_io_channel_set_flags(giochannel_blind->giochannel_ro,(flags&~G_IO_FLAG_IS_WRITEABLE),err);
+ if (!giochannel_blind->giochannel_orig)
+ return G_IO_STATUS_NORMAL;
+
+ return g_io_channel_set_flags(giochannel_blind->giochannel_orig,(flags&~G_IO_FLAG_IS_WRITEABLE),err);
}
g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),0);
- return g_io_channel_get_flags(giochannel_blind->giochannel_ro) | G_IO_FLAG_IS_WRITEABLE;
+ if (!giochannel_blind->giochannel_orig)
+ return G_IO_FLAG_IS_READABLE | G_IO_FLAG_IS_WRITEABLE | G_IO_FLAG_IS_SEEKABLE;
+
+ return g_io_channel_get_flags(giochannel_blind->giochannel_orig) | G_IO_FLAG_IS_WRITEABLE;
}
-struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_ro)
+struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_orig,gboolean writeable)
{
struct captive_giochannel_blind *giochannel_blind;
-GIOStatus erriostatus;
- g_return_val_if_fail(giochannel_ro!=NULL,NULL);
+ /* 'giochannel_orig' may be NULL if no fallback capability exists. */
G_LOCK(giochannel_blind_funcs);
giochannel_blind_funcs.io_read =captive_giochannel_blind_io_read;
giochannel_blind_funcs.io_get_flags =captive_giochannel_blind_io_get_flags;
G_UNLOCK(giochannel_blind_funcs);
- erriostatus=g_io_channel_set_encoding(giochannel_ro,
- NULL, /* encoding; force binary data */
- NULL); /* error */
- g_assert(erriostatus==G_IO_STATUS_NORMAL);
+ if (giochannel_orig)
+ captive_giochannel_setup(giochannel_orig);
+
+ g_io_channel_ref(giochannel_orig);
captive_new(giochannel_blind);
g_assert(G_STRUCT_OFFSET(struct captive_giochannel_blind,iochannel)==0); /* safely re-type-able */
giochannel_blind->iochannel.funcs=&giochannel_blind_funcs;
giochannel_blind->iochannel.is_seekable=TRUE;
giochannel_blind->iochannel.is_readable=TRUE;
- giochannel_blind->iochannel.is_writeable=TRUE;
+ /* readonly captive_giochannel_blind can be used to track read access. */
+ giochannel_blind->iochannel.is_writeable=writeable;
giochannel_blind->iochannel.close_on_unref=TRUE; /* run g_io_channel_shutdown() flush on last unref */
- giochannel_blind->giochannel_ro=giochannel_ro;
+ giochannel_blind->giochannel_orig=giochannel_orig;
giochannel_blind->offset=0;
- giochannel_blind->size=captive_giochannel_size(giochannel_ro);
+ giochannel_blind->size=(!giochannel_orig ? 0 : captive_giochannel_size(giochannel_orig));
giochannel_blind->buffer_hash=g_hash_table_new_full(
(GHashFunc)captive_giochannel_blind_hash_func, /* hash_func */
(GEqualFunc)captive_giochannel_blind_equal_func, /* key_equal_func */
(GDestroyNotify)captive_giochannel_blind_key_destroy_func, /* key_destroy_func */
(GDestroyNotify)captive_giochannel_blind_value_destroy_func); /* value_destroy_func */
+ captive_giochannel_setup(&giochannel_blind->iochannel);
+
return giochannel_blind;
}
+
+
+gboolean captive_giochannel_blind_get_size(GIOChannel *giochannel,guint64 *size_return)
+{
+struct captive_giochannel_blind *giochannel_blind;
+
+ g_return_val_if_fail(giochannel!=NULL,FALSE);
+ g_return_val_if_fail(size_return!=NULL,FALSE);
+
+ if (giochannel->funcs!=&giochannel_blind_funcs)
+ return FALSE;
+ giochannel_blind=(struct captive_giochannel_blind *)giochannel;
+
+ *size_return=giochannel_blind->size;
+ return TRUE;
+}
+
+
+typedef void (*sorted_array_filter)
+ (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */);
+
+static void captive_giochannel_blind_written_as_sorted_array_foreach
+ (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */)
+{
+ g_return_if_fail(keyp!=NULL);
+ g_return_if_fail(blind_block!=NULL);
+ g_return_if_fail(rpp!=NULL);
+
+ if (!blind_block->data_written)
+ return;
+
+ *((*rpp)++)=blind_block;
+}
+
+static void captive_giochannel_blind_read_as_sorted_array_foreach
+ (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */)
+{
+ g_return_if_fail(keyp!=NULL);
+ g_return_if_fail(blind_block!=NULL);
+ g_return_if_fail(rpp!=NULL);
+
+ if (!blind_block->was_read)
+ return;
+
+ *((*rpp)++)=blind_block;
+}
+
+static int captive_giochannel_blind_as_sorted_array_compat
+ (const struct blind_block *const *ap,const struct blind_block *const *bp)
+{
+ g_return_val_if_fail(ap!=NULL,0);
+ g_return_val_if_fail(*ap!=NULL,0);
+ g_return_val_if_fail(bp!=NULL,0);
+ g_return_val_if_fail(*bp!=NULL,0);
+
+ return ((*ap)->offset>(*bp)->offset) - ((*bp)->offset>(*ap)->offset);
+}
+
+static struct blind_block **captive_giochannel_blind_as_sorted_array
+ (struct captive_giochannel_blind *giochannel_blind,sorted_array_filter filter_func)
+{
+guint hash_size;
+struct blind_block **r,**rp;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+
+ hash_size=g_hash_table_size(giochannel_blind->buffer_hash);
+ captive_newn(r,hash_size+1);
+ rp=r;
+ g_hash_table_foreach(giochannel_blind->buffer_hash,(GHFunc)filter_func,&rp);
+ g_assert(rp<=r+hash_size);
+ *rp=NULL;
+ qsort(r,rp-r,sizeof(*r),(int (*)(const void *,const void *))captive_giochannel_blind_as_sorted_array_compat);
+
+ return r;
+}
+
+GIOStatus captive_giochannel_blind_commit(GIOChannel *giochannel)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)giochannel;
+struct blind_block **blind_block_array,**blind_blockp;
+GIOStatus errgiostatus;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+ g_return_val_if_fail(giochannel_blind->giochannel_orig!=NULL,G_IO_STATUS_ERROR);
+
+ errgiostatus=g_io_channel_flush(
+ giochannel, /* channel */
+ NULL); /* error */
+ g_assert(errgiostatus==G_IO_STATUS_NORMAL);
+
+ blind_block_array=captive_giochannel_blind_as_sorted_array
+ (giochannel_blind,captive_giochannel_blind_written_as_sorted_array_foreach);
+
+ for (blind_blockp=blind_block_array;*blind_blockp;blind_blockp++) {
+struct blind_block *blind_block=*blind_blockp;
+gsize bytes_written;
+
+ g_assert(blind_block->data_written!=NULL);
+
+ errgiostatus=g_io_channel_seek_position(
+ giochannel_blind->giochannel_orig, /* channel */
+ blind_block->offset, /* offset */
+ G_SEEK_SET, /* type */
+ NULL); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
+ errgiostatus=g_io_channel_write_chars(
+ giochannel_blind->giochannel_orig, /* channel */
+ blind_block->data_written, /* buf */
+ GIOCHANNEL_BLIND_BLOCK_SIZE, /* count */
+ &bytes_written, /* bytes_written */
+ NULL); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
+ g_return_val_if_fail(bytes_written==GIOCHANNEL_BLIND_BLOCK_SIZE,G_IO_STATUS_ERROR);
+
+ g_free(blind_block->data_written);
+ blind_block->data_written=NULL;
+ }
+
+ g_free(blind_block_array);
+
+ errgiostatus=g_io_channel_flush(
+ giochannel_blind->giochannel_orig, /* channel */
+ NULL); /* error */
+ g_assert(errgiostatus==G_IO_STATUS_NORMAL);
+
+ return G_IO_STATUS_NORMAL;
+}
+
+
+xmlNode *captive_giochannel_blind_readreport_to_xml(xmlNode *xml_parent,GIOChannel *giochannel)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)giochannel;
+struct blind_block **blind_block_array,**blind_blockp;
+GIOStatus errgiostatus;
+guint8 data_read[1+GIOCHANNEL_BLIND_BLOCK_SIZE]; /* '1+' for leading stub to prevent shorter output of BN_bn2hex() */
+xmlNode *xml_media;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),NULL);
+ g_return_val_if_fail(giochannel_blind->giochannel_orig!=NULL,G_IO_STATUS_ERROR);
+
+ errgiostatus=g_io_channel_flush(
+ giochannel, /* channel */
+ NULL); /* error */
+ g_assert(errgiostatus==G_IO_STATUS_NORMAL);
+
+ xml_media=xmlNewTextChild(xml_parent,NULL,"media",NULL);
+ xmlNewProp(xml_media,"size",captive_printf_alloca("%" G_GUINT64_FORMAT,giochannel_blind->size));
+
+ blind_block_array=captive_giochannel_blind_as_sorted_array
+ (giochannel_blind,captive_giochannel_blind_read_as_sorted_array_foreach);
+
+ for (blind_blockp=blind_block_array;*blind_blockp;blind_blockp++) {
+struct blind_block *blind_block=*blind_blockp;
+gsize bytes_read;
+xmlNode *xml_media_read;
+gchar offset_string[64];
+BIGNUM *bignum;
+char *hex,*s;
+gchar hex_out[0
+ +1 /* leading '\n' */
+ +GIOCHANNEL_BLIND_BLOCK_SIZE*2/64*(64+1) /* each line of 64 characters has EOL '\n' */
+ +1],*gd; /* terminating '\0' */
+
+ errgiostatus=g_io_channel_seek_position(
+ giochannel_blind->giochannel_orig, /* channel */
+ blind_block->offset, /* offset */
+ G_SEEK_SET, /* type */
+ NULL); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,NULL);
+ errgiostatus=g_io_channel_read_chars(
+ giochannel_blind->giochannel_orig, /* channel */
+ data_read+1, /* buf */
+ GIOCHANNEL_BLIND_BLOCK_SIZE, /* count */
+ &bytes_read, /* bytes_read */
+ NULL); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,NULL);
+ g_return_val_if_fail(bytes_read==GIOCHANNEL_BLIND_BLOCK_SIZE,NULL);
+
+ /* Convert binary block to 'hex' and reformat line-wrap it to 'hex_out'. */
+ data_read[0]=0xFF; /* stub to prevent shorter output of BN_bn2hex() */
+ bignum=BN_bin2bn(data_read,1+GIOCHANNEL_BLIND_BLOCK_SIZE,NULL);
+ hex=BN_bn2hex(bignum);
+ BN_free(bignum);
+ g_assert(strlen(hex)==2*(1+GIOCHANNEL_BLIND_BLOCK_SIZE));
+ gd=hex_out;
+ *gd++='\n';
+ for (s=hex+2;s<hex+2+2*GIOCHANNEL_BLIND_BLOCK_SIZE;s+=64,gd+=64+1) {
+ memcpy(gd,s,64);
+ gd[64]='\n';
+ }
+ OPENSSL_free(hex);
+ *gd++=0;
+ g_assert(s==hex+2+2*GIOCHANNEL_BLIND_BLOCK_SIZE);
+ g_assert(gd==hex_out+sizeof(hex_out));
+ xml_media_read=xmlNewTextChild(xml_media,NULL,"block",hex_out);
+ {
+ g_snprintf(offset_string,sizeof(offset_string),"%" G_GUINT64_FORMAT,blind_block->offset);
+ xmlNewProp(xml_media_read,"offset",offset_string);
+ }
+ }
+
+ g_free(blind_block_array);
+
+ return xml_media;
+}
+
+#ifdef HAVE_LIBXML_BUFFERING
+struct captive_giochannel_blind *captive_giochannel_blind_new_from_xml(xmlTextReader *xml_reader)
+{
+struct captive_giochannel_blind *r;
+const xmlChar *xml_name;
+int errint;
+GIOStatus erriostatus;
+gboolean scan_end;
+struct captive_libxml_string_drop_stack *drop_stack=NULL;
+
+ g_return_val_if_fail(xml_reader!=NULL,NULL);
+ g_return_val_if_fail(xmlTextReaderNodeType(xml_reader)==CAPTIVE_XML_TEXT_READER_NODE_TYPE_START,NULL);
+ xml_name=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader));
+ g_return_val_if_fail(xml_name!=NULL,NULL);
+ g_return_val_if_fail(!xmlStrcmp(xml_name,"media"),NULL);
+
+ r=captive_giochannel_blind_new(
+ NULL, /* giochannel_orig */
+ TRUE); /* writeable */
+ r->size=captive_libxml_sscanf_gint64(captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,"size")));
+
+ scan_end=FALSE;
+ do {
+int got_type;
+
+ errint=xmlTextReaderRead(xml_reader);
+ g_assert(errint==1);
+ switch ((got_type=xmlTextReaderNodeType(xml_reader))) {
+ case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT:
+ break;
+ case CAPTIVE_XML_TEXT_READER_NODE_TYPE_SIGNIFICANT_WHITESPACE:
+ break;
+ case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */
+ break;
+ case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: {
+const xmlChar *xml_name;
+
+ xml_name=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader));
+ g_assert(xml_name!=NULL);
+ if (!xmlStrcmp(xml_name,"block")) {
+guint64 offset;
+gsize bytes_written;
+const xmlChar *xml_text_hex_in,*xml_char_s;
+BIGNUM *bignum;
+int bignum_num_bytes;
+gchar bin_hex[2*GIOCHANNEL_BLIND_BLOCK_SIZE+1],*gd;
+unsigned char bin_out[GIOCHANNEL_BLIND_BLOCK_SIZE];
+
+ offset=captive_libxml_sscanf_gint64(
+ captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,"offset")));
+
+ errint=xmlTextReaderRead(xml_reader);
+ g_assert(errint==1);
+ errint=xmlTextReaderNodeType(xml_reader);
+ g_assert(errint==CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT);
+ xml_text_hex_in=captive_libxml_string_drop(&drop_stack,xmlTextReaderValue(xml_reader));
+ g_assert(xml_text_hex_in!=NULL);
+
+ /* Convert binary block from hex line-wrapped 'xml_text_hex_in'. */
+ gd=bin_hex;
+ for (xml_char_s=xml_text_hex_in;*xml_char_s;xml_char_s++)
+ if (!isspace(*xml_char_s)) {
+ g_assert(gd<bin_hex+2*GIOCHANNEL_BLIND_BLOCK_SIZE);
+ *gd++=*xml_char_s;
+ }
+ *gd=0;
+ bignum=NULL;
+ errint=BN_hex2bn(&bignum,bin_hex);
+ g_assert(errint==2*GIOCHANNEL_BLIND_BLOCK_SIZE);
+ g_assert(bignum!=NULL);
+
+ /* Leading zeroes are ommited by BN_bn2bin(). */
+ bignum_num_bytes=BN_num_bytes(bignum);
+ g_assert(bignum_num_bytes<=GIOCHANNEL_BLIND_BLOCK_SIZE);
+ memset(bin_out,0,GIOCHANNEL_BLIND_BLOCK_SIZE-bignum_num_bytes);
+ errint=BN_bn2bin(bignum,bin_out+(GIOCHANNEL_BLIND_BLOCK_SIZE-bignum_num_bytes));
+ g_assert(errint==bignum_num_bytes);
+ BN_free(bignum);
+
+ erriostatus=g_io_channel_seek_position(
+ &r->iochannel, /* channel */
+ offset, /* offset */
+ G_SEEK_SET, /* type */
+ NULL); /* error */
+ g_assert(erriostatus==G_IO_STATUS_NORMAL);
+ erriostatus=g_io_channel_write_chars(
+ &r->iochannel, /* channel */
+ bin_out, /* buf */
+ GIOCHANNEL_BLIND_BLOCK_SIZE, /* count */
+ &bytes_written, /* bytes_written */
+ NULL); /* error */
+ g_assert(erriostatus==G_IO_STATUS_NORMAL);
+ g_assert(bytes_written==GIOCHANNEL_BLIND_BLOCK_SIZE);
+ }
+ else g_error("Unknown START node: %s",xml_name);
+ } break;
+ case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: {
+const xmlChar *xml_name;
+
+ xml_name=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader));
+ /**/ if (!xmlStrcmp(xml_name,"media")) {
+ scan_end=TRUE; /* proper cleanup */
+ }
+ else if (!xmlStrcmp(xml_name,"block")) {
+ }
+ else g_error("Unknown END node: %s",xml_name);
+ } break;
+ default:
+ g_error("Unexpected xmlTextReaderNodeType() type %d",got_type);
+ g_assert_not_reached();
+ }
+ captive_libxml_string_drop_flush(&drop_stack);
+ } while (!scan_end);
+
+ g_assert(drop_stack==NULL);
+
+ return r;
+}
+#endif /* HAVE_LIBXML_BUFFERING */