/* $Id$ * glib r/w GIOChannel buffered-over r/o GIOChannel for libcaptive * Copyright (C) 2003 Jan Kratochvil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; exactly version 2 of June 1991 is required * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "giochannel-blind.h" #include "reactos/internal/mm.h" /* for PAGE_SIZE */ #include #include #include "captive/macros.h" #include "captive/storage.h" #include #include #include #include "captive/libxml.h" #include #ifdef HAVE_LIBXML_BUFFERING #include #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 /* FIXME: fill 'err' */ struct captive_giochannel_blind { GIOChannel iochannel; GIOChannel *giochannel_orig; /* reffed by us */ guint64 offset; /* gint64 range */ guint64 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] */ }; G_LOCK_DEFINE_STATIC(giochannel_blind_funcs); static GIOFuncs giochannel_blind_funcs; static guint captive_giochannel_blind_hash_func(const guint64 *keyp) { g_return_val_if_fail(keyp!=NULL,0); return (*keyp)^((*keyp)>>23); } static gboolean captive_giochannel_blind_equal_func(const guint64 *ap,const guint64 *bp) { g_return_val_if_fail(ap!=NULL,FALSE); g_return_val_if_fail(bp!=NULL,FALSE); return (*ap)==(*bp); } static void captive_giochannel_blind_key_destroy_func(guint64 *keyp) { g_return_if_fail(keyp!=NULL); g_free(keyp); } static void captive_giochannel_blind_value_destroy_func(struct blind_block *blind_block) { g_return_if_fail(blind_block!=NULL); 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); /* '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); return TRUE; } static GIOStatus captive_giochannel_blind_io_read (GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err) { struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel; 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_return_val_if_fail(bytes_read!=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_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_nowoffset); transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count); 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 */ 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_orig, /* channel */ transfer_bottom, /* offset */ G_SEEK_SET, /* type */ err); /* error */ /* During seek in block device such as on URL file:///dev/hda1#captive-fastfat.sys-ro:/ * we will do llseek(2) on "/dev/hda1" device from captive_giochannel_size(). * Although we are allowed to seek behind EOF on regular files * at least linux-kernel-2.4.19-ac4/fs/block_dev.c/block_llseek() will give * EINVAL on seek behind EOF therefore it must be accepted without complaints by us. */ if (errgiostatus!=G_IO_STATUS_NORMAL) { errgiostatus=G_IO_STATUS_EOF; bytes_read=0; } else { errgiostatus=g_io_channel_read_chars( giochannel_blind->giochannel_orig, /* channel */ buf+transfer_bottom-giochannel_blind->offset, /* buf */ transfer_top-transfer_bottom, /* count */ &bytes_read, /* bytes_read */ err); /* error */ } 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); else break; } *bytes_read=maxread-giochannel_blind->offset; giochannel_blind->offset=maxread; return (*bytes_read == 0 ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL); } static GIOStatus captive_giochannel_blind_io_write (GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err) { struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel; 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(bytes_written!=NULL,G_IO_STATUS_ERROR); g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write(offset=0x%llX,count=0x%lX)",G_STRLOC, giochannel_blind->offset,(gulong)count); g_return_val_if_fail(giochannel_blind->offset+count<=giochannel_blind->size,G_IO_STATUS_ERROR); 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_nowoffset); transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count); if (!(blind_block=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now)) || !blind_block->data_written) { if (!blind_block) { guint64 *keyp; 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); /* Missing lower part of buffer? */ 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_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_orig, /* channel */ (gchar *)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? */ if (transfer_topgiochannel_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_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_orig, /* channel */ (gchar *)(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(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus); g_return_val_if_fail(bytes_read<=(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top),G_IO_STATUS_ERROR); g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR); if (bytes_read==window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top) 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_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-by-memcpy(window_now=0x%llX,dest=data+0x%lX,src=buf+0x%lX,n=0x%lX)", 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 *)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; giochannel_blind->offset+=(*bytes_written); return G_IO_STATUS_NORMAL; } static GIOStatus captive_giochannel_blind_io_seek(GIOChannel *channel,gint64 offset,GSeekType type,GError **err) { struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel; g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR); switch (type) { case G_SEEK_CUR: giochannel_blind->offset+= offset; break; case G_SEEK_SET: giochannel_blind->offset = offset; break; case G_SEEK_END: giochannel_blind->offset =giochannel_blind->size+offset; break; default: g_return_val_if_reached(G_IO_STATUS_ERROR); } g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR); /* 'offset' overflow? */ return G_IO_STATUS_NORMAL; } static GIOStatus captive_giochannel_blind_io_close(GIOChannel *channel,GError **err) { struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel; GIOStatus erriostatus; g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR); 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; return G_IO_STATUS_NORMAL; } static GSource* captive_giochannel_blind_io_create_watch(GIOChannel *channel,GIOCondition condition) { struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel; g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),NULL); g_return_val_if_reached(NULL); /* FIXME: NOT IMPLEMENTED YET */ } static void captive_giochannel_blind_io_free(GIOChannel *channel) { struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel; /* After captive_giochannel_blind_io_close() 'giochannel_blind' * may be no longer valid for validate_giochannel_blind(giochannel_blind). */ g_return_if_fail(giochannel_blind!=NULL); g_assert(giochannel_blind->giochannel_orig==NULL); g_assert(giochannel_blind->buffer_hash==NULL); g_free(giochannel_blind); } static GIOStatus captive_giochannel_blind_io_set_flags(GIOChannel *channel,GIOFlags flags,GError **err) { struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel; g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR); 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); } static GIOFlags captive_giochannel_blind_io_get_flags(GIOChannel *channel) { struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel; g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),0); 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_orig,gboolean writeable) { struct captive_giochannel_blind *giochannel_blind; /* '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_write =captive_giochannel_blind_io_write; giochannel_blind_funcs.io_seek =captive_giochannel_blind_io_seek; giochannel_blind_funcs.io_close =captive_giochannel_blind_io_close; giochannel_blind_funcs.io_create_watch=captive_giochannel_blind_io_create_watch; giochannel_blind_funcs.io_free =captive_giochannel_blind_io_free; giochannel_blind_funcs.io_set_flags =captive_giochannel_blind_io_set_flags; giochannel_blind_funcs.io_get_flags =captive_giochannel_blind_io_get_flags; G_UNLOCK(giochannel_blind_funcs); 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 */ g_io_channel_init(&giochannel_blind->iochannel); giochannel_blind->iochannel.funcs=&giochannel_blind_funcs; giochannel_blind->iochannel.is_seekable=TRUE; giochannel_blind->iochannel.is_readable=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_orig=giochannel_orig; giochannel_blind->offset=0; 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 */ (const gchar *)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,BAD_CAST "media",NULL); xmlNewProp(xml_media,BAD_CAST "size",BAD_CAST 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 */ (gchar *)(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;soffset); xmlNewProp(xml_media_read,BAD_CAST "offset",BAD_CAST 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 gchar *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(!strcmp(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,BAD_CAST "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 gchar *xml_name; xml_name=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader)); g_assert(xml_name!=NULL); if (!strcmp(xml_name,"block")) { guint64 offset; gsize bytes_written; const gchar *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,BAD_CAST "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(gdiochannel, /* 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 */ (const gchar *)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 gchar *xml_name; xml_name=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader)); /**/ if (!strcmp(xml_name,"media")) { scan_end=TRUE; /* proper cleanup */ } else if (!strcmp(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 */