2 * glib r/w GIOChannel buffered-over r/o GIOChannel for libcaptive
3 * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "giochannel-blind.h"
23 #include "reactos/internal/mm.h" /* for PAGE_SIZE */
24 #include <glib/ghash.h>
25 #include <glib/gmessages.h>
26 #include "captive/macros.h"
27 #include "captive/storage.h"
28 #include <openssl/bn.h>
29 #include <openssl/crypto.h>
30 #include <libxml/tree.h>
31 #include "captive/libxml.h"
33 #ifdef HAVE_LIBXML_BUFFERING
34 #include <libxml/xmlreader.h>
39 /* It should be the divisor of all offsets/sizes written by W32 filesystems.
41 #define GIOCHANNEL_BLIND_BLOCK_SIZE 512
44 /* FIXME: fill 'err' */
46 struct captive_giochannel_blind {
48 GIOChannel *giochannel_orig;
49 guint64 offset; /* gint64 range */
51 GHashTable *buffer_hash; /* (guint64 *) -> (struct blind_block *) (guint8[GIOCHANNEL_BLIND_BLOCK_SIZE]) */
56 gboolean was_read,was_written;
57 guint8 *data_written; /* [GIOCHANNEL_BLIND_BLOCK_SIZE] */
61 G_LOCK_DEFINE_STATIC(giochannel_blind_funcs);
62 static GIOFuncs giochannel_blind_funcs;
65 static guint captive_giochannel_blind_hash_func(const guint64 *keyp)
67 g_return_val_if_fail(keyp!=NULL,0);
69 return (*keyp)^((*keyp)>>23);
72 static gboolean captive_giochannel_blind_equal_func(const guint64 *ap,const guint64 *bp)
74 g_return_val_if_fail(ap!=NULL,FALSE);
75 g_return_val_if_fail(bp!=NULL,FALSE);
80 static void captive_giochannel_blind_key_destroy_func(guint64 *keyp)
82 g_return_if_fail(keyp!=NULL);
87 static void captive_giochannel_blind_value_destroy_func(struct blind_block *blind_block)
89 g_return_if_fail(blind_block!=NULL);
91 g_free(blind_block->data_written);
96 static gboolean validate_giochannel_blind(struct captive_giochannel_blind *giochannel_blind)
98 g_return_val_if_fail(giochannel_blind->iochannel.funcs==&giochannel_blind_funcs,FALSE);
99 g_return_val_if_fail(giochannel_blind!=NULL,FALSE);
100 /* 'giochannel_blind->giochannel_orig' may be NULL. */
101 g_return_val_if_fail((gint64)giochannel_blind->offset>=0,FALSE); /* gint64 overflow stored in guint64 */
102 g_return_val_if_fail(giochannel_blind->buffer_hash!=NULL,FALSE);
108 static GIOStatus captive_giochannel_blind_io_read
109 (GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err)
111 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
112 guint64 window_bottom,window_top,window_now;
113 guint64 transfer_bottom,transfer_top;
114 GIOStatus errgiostatus;
115 guint64 maxread; /* maximum offset of end of data we successfuly read */
116 struct blind_block *blind_block;
118 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
119 g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
120 g_return_val_if_fail(bytes_read!=NULL,G_IO_STATUS_ERROR);
122 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read(offset=0x%llX,count=0x%lX)",G_STRLOC,
123 giochannel_blind->offset,(gulong)count);
125 window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
126 window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
127 maxread=giochannel_blind->offset;
129 for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
132 transfer_bottom=MAX(window_now,giochannel_blind->offset);
133 transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
134 if ((blind_block=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now)) && blind_block->data_written) {
135 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)",
137 (guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(gulong)(transfer_bottom-window_now),
138 (gulong)(transfer_top-transfer_bottom));
140 buf+transfer_bottom-giochannel_blind->offset, /* dest */
141 blind_block->data_written+transfer_bottom-window_now, /* src */
142 transfer_top-transfer_bottom); /* n */
143 blind_block->was_read=TRUE;
144 maxread=transfer_top;
147 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,
148 (guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(guint64)transfer_bottom,
149 (gulong)(transfer_top-transfer_bottom));
150 if (!giochannel_blind->giochannel_orig) {
151 g_error("%s: Missing block at offset 0x%llX",G_STRLOC,(unsigned long long)window_now);
152 g_return_val_if_reached(G_IO_STATUS_ERROR);
154 errgiostatus=g_io_channel_seek_position(
155 giochannel_blind->giochannel_orig, /* channel */
156 transfer_bottom, /* offset */
157 G_SEEK_SET, /* type */
159 /* During seek in block device such as on URL file:///dev/hda1#captive-fastfat.sys-ro:/
160 * we will do llseek(2) on "/dev/hda1" device from captive_giochannel_size().
161 * Although we are allowed to seek behind EOF on regular files
162 * at least linux-kernel-2.4.19-ac4/fs/block_dev.c/block_llseek() will give
163 * EINVAL on seek behind EOF therefore it must be accepted without complaints by us.
165 if (errgiostatus!=G_IO_STATUS_NORMAL) {
166 errgiostatus=G_IO_STATUS_EOF;
170 errgiostatus=g_io_channel_read_chars(
171 giochannel_blind->giochannel_orig, /* channel */
172 buf+transfer_bottom-giochannel_blind->offset, /* buf */
173 transfer_top-transfer_bottom, /* count */
174 &bytes_read, /* bytes_read */
177 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
178 g_return_val_if_fail(bytes_read<=(transfer_top-transfer_bottom),G_IO_STATUS_ERROR);
179 g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
184 captive_new(blind_block);
185 blind_block->offset=window_now;
186 blind_block->was_read=FALSE;
187 blind_block->was_written=FALSE;
188 blind_block->data_written=NULL;
192 giochannel_blind->buffer_hash, /* hash_table */
194 blind_block); /* value */
196 blind_block->was_read=TRUE;
198 maxread=transfer_bottom+bytes_read;
199 if (bytes_read==transfer_top-transfer_bottom)
200 g_return_val_if_fail(transfer_bottom+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
205 *bytes_read=maxread-giochannel_blind->offset;
206 giochannel_blind->offset=maxread;
207 return (*bytes_read == 0 ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL);
211 static GIOStatus captive_giochannel_blind_io_write
212 (GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err)
214 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
215 guint64 window_bottom,window_top,window_now;
216 guint64 transfer_bottom,transfer_top;
217 GIOStatus errgiostatus;
218 struct blind_block *blind_block;
220 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
221 g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
222 g_return_val_if_fail(bytes_written!=NULL,G_IO_STATUS_ERROR);
224 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write(offset=0x%llX,count=0x%lX)",G_STRLOC,
225 giochannel_blind->offset,(gulong)count);
227 g_return_val_if_fail(giochannel_blind->offset+count<=giochannel_blind->size,G_IO_STATUS_ERROR);
229 g_return_val_if_fail(giochannel_blind->iochannel.is_writeable==TRUE,G_IO_STATUS_ERROR);
231 window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
232 window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
234 for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
237 transfer_bottom=MAX(window_now,giochannel_blind->offset);
238 transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
239 if (!(blind_block=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now)) || !blind_block->data_written) {
243 captive_new(blind_block);
244 blind_block->offset=window_now;
245 blind_block->was_read=FALSE;
246 blind_block->was_written=FALSE;
250 giochannel_blind->buffer_hash, /* hash_table */
252 blind_block); /* value */
254 blind_block->data_written=g_malloc(GIOCHANNEL_BLIND_BLOCK_SIZE);
255 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-new-mem(window_now=0x%llX)",G_STRLOC,
256 (guint64)window_now);
258 /* Missing lower part of buffer? */
259 if (transfer_bottom>window_now) {
260 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-mem-read-lower(seek=0x%llX,count=0x%lX)",G_STRLOC,
261 (guint64)window_now,(gulong)(transfer_bottom-window_now));
262 if (!giochannel_blind->giochannel_orig) {
263 g_error("Missing block for partial read at offset 0x%llX",(unsigned long long)window_now);
264 g_assert_not_reached();
266 errgiostatus=g_io_channel_seek_position(
267 giochannel_blind->giochannel_orig, /* channel */
268 window_now, /* offset */
269 G_SEEK_SET, /* type */
271 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
272 errgiostatus=g_io_channel_read_chars(
273 giochannel_blind->giochannel_orig, /* channel */
274 blind_block->data_written, /* buf */
275 transfer_bottom-window_now, /* count */
276 &bytes_read, /* bytes_read */
278 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
279 g_return_val_if_fail(bytes_read==(transfer_bottom-window_now),G_IO_STATUS_ERROR);
280 blind_block->was_read=TRUE; /* FIXME: Support non-block-aligned buffers. */
283 /* Missing upper part of buffer? */
284 if (transfer_top<window_now+GIOCHANNEL_BLIND_BLOCK_SIZE) {
285 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,
286 (gulong)(transfer_top-window_now),(guint64)transfer_top,
287 (gulong)(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top));
288 if (!giochannel_blind->giochannel_orig) {
289 g_error("Missing block for partial read at offset 0x%llX",(unsigned long long)window_now);
290 g_assert_not_reached();
292 errgiostatus=g_io_channel_seek_position(
293 giochannel_blind->giochannel_orig, /* channel */
294 transfer_top, /* offset */
295 G_SEEK_SET, /* type */
297 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
298 errgiostatus=g_io_channel_read_chars(
299 giochannel_blind->giochannel_orig, /* channel */
300 blind_block->data_written+transfer_top-window_now, /* buf */
301 window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top, /* count */
302 &bytes_read, /* bytes_read */
304 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
305 g_return_val_if_fail(bytes_read<=(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top),G_IO_STATUS_ERROR);
306 g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
307 if (bytes_read==window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top)
308 g_return_val_if_fail(transfer_top+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
310 g_return_val_if_fail(transfer_top+bytes_read==giochannel_blind->size,G_IO_STATUS_ERROR); /* EOF hit */
311 blind_block->was_read=TRUE; /* FIXME: Support non-block-aligned buffers. */
315 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)",
317 (guint64)window_now,(gulong)(transfer_bottom-window_now),(gulong)(transfer_bottom-giochannel_blind->offset),
318 (gulong)(transfer_top-transfer_bottom));
319 g_assert(blind_block); g_assert(blind_block->data_written);
321 ((char *)blind_block->data_written)+transfer_bottom-window_now, /* dest */
322 buf+transfer_bottom-giochannel_blind->offset, /* src */
323 transfer_top-transfer_bottom); /* n */
324 blind_block->was_written=TRUE;
327 *bytes_written=count;
328 giochannel_blind->offset+=(*bytes_written);
329 return G_IO_STATUS_NORMAL;
333 static GIOStatus captive_giochannel_blind_io_seek(GIOChannel *channel,gint64 offset,GSeekType type,GError **err)
335 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
337 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
340 case G_SEEK_CUR: giochannel_blind->offset+= offset; break;
341 case G_SEEK_SET: giochannel_blind->offset = offset; break;
342 case G_SEEK_END: giochannel_blind->offset =giochannel_blind->size+offset; break;
343 default: g_return_val_if_reached(G_IO_STATUS_ERROR);
345 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR); /* 'offset' overflow? */
347 return G_IO_STATUS_NORMAL;
351 static GIOStatus captive_giochannel_blind_io_close(GIOChannel *channel,GError **err)
353 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
354 GIOStatus erriostatus;
356 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
358 if (giochannel_blind->giochannel_orig) {
359 /* We are not authorized to destroy 'giochannel_blind->giochannel_orig'. */
360 erriostatus=g_io_channel_flush(
361 giochannel_blind->giochannel_orig, /* channel */
363 g_assert(erriostatus==G_IO_STATUS_NORMAL);
364 giochannel_blind->giochannel_orig=NULL;
367 g_hash_table_destroy(giochannel_blind->buffer_hash);
368 giochannel_blind->buffer_hash=NULL;
370 return G_IO_STATUS_NORMAL;
374 static GSource* captive_giochannel_blind_io_create_watch(GIOChannel *channel,GIOCondition condition)
376 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
378 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),NULL);
380 g_return_val_if_reached(NULL); /* FIXME: NOT IMPLEMENTED YET */
384 static void captive_giochannel_blind_io_free(GIOChannel *channel)
386 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
388 /* After captive_giochannel_blind_io_close() 'giochannel_blind'
389 * may be no longer valid for validate_giochannel_blind(giochannel_blind).
391 g_return_if_fail(giochannel_blind!=NULL);
393 g_assert(giochannel_blind->giochannel_orig==NULL);
394 g_assert(giochannel_blind->buffer_hash==NULL);
396 g_free(giochannel_blind);
400 static GIOStatus captive_giochannel_blind_io_set_flags(GIOChannel *channel,GIOFlags flags,GError **err)
402 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
404 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
406 if (!giochannel_blind->giochannel_orig)
407 return G_IO_STATUS_NORMAL;
409 return g_io_channel_set_flags(giochannel_blind->giochannel_orig,(flags&~G_IO_FLAG_IS_WRITEABLE),err);
413 static GIOFlags captive_giochannel_blind_io_get_flags(GIOChannel *channel)
415 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
417 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),0);
419 if (!giochannel_blind->giochannel_orig)
420 return G_IO_FLAG_IS_READABLE | G_IO_FLAG_IS_WRITEABLE | G_IO_FLAG_IS_SEEKABLE;
422 return g_io_channel_get_flags(giochannel_blind->giochannel_orig) | G_IO_FLAG_IS_WRITEABLE;
425 void captive_giochannel_setup(GIOChannel *giochannel)
427 GIOStatus erriostatus;
429 g_return_if_fail(giochannel!=NULL);
431 if (g_io_channel_get_encoding(giochannel)) {
432 if (!g_io_channel_get_buffered(giochannel)) /* Prevent: Need to have NULL encoding to set the buffering state ... */
433 g_io_channel_set_buffered(giochannel,TRUE); /* Prevent: Need to set the channel buffered before setting the encoding. */
434 erriostatus=g_io_channel_set_encoding(giochannel,
435 NULL, /* encoding; force binary data */
437 g_assert(erriostatus==G_IO_STATUS_NORMAL);
439 erriostatus=g_io_channel_flush(giochannel,
441 g_assert(erriostatus==G_IO_STATUS_NORMAL);
442 g_io_channel_set_buffered(giochannel,FALSE);
446 struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_orig,gboolean writeable)
448 struct captive_giochannel_blind *giochannel_blind;
450 /* 'giochannel_orig' may be NULL if no fallback capability exists. */
452 G_LOCK(giochannel_blind_funcs);
453 giochannel_blind_funcs.io_read =captive_giochannel_blind_io_read;
454 giochannel_blind_funcs.io_write =captive_giochannel_blind_io_write;
455 giochannel_blind_funcs.io_seek =captive_giochannel_blind_io_seek;
456 giochannel_blind_funcs.io_close =captive_giochannel_blind_io_close;
457 giochannel_blind_funcs.io_create_watch=captive_giochannel_blind_io_create_watch;
458 giochannel_blind_funcs.io_free =captive_giochannel_blind_io_free;
459 giochannel_blind_funcs.io_set_flags =captive_giochannel_blind_io_set_flags;
460 giochannel_blind_funcs.io_get_flags =captive_giochannel_blind_io_get_flags;
461 G_UNLOCK(giochannel_blind_funcs);
464 captive_giochannel_setup(giochannel_orig);
466 captive_new(giochannel_blind);
467 g_assert(G_STRUCT_OFFSET(struct captive_giochannel_blind,iochannel)==0); /* safely re-type-able */
468 g_io_channel_init(&giochannel_blind->iochannel);
469 giochannel_blind->iochannel.funcs=&giochannel_blind_funcs;
470 giochannel_blind->iochannel.is_seekable=TRUE;
471 giochannel_blind->iochannel.is_readable=TRUE;
472 /* readonly captive_giochannel_blind can be used to track read access. */
473 giochannel_blind->iochannel.is_writeable=writeable;
474 giochannel_blind->iochannel.close_on_unref=TRUE; /* run g_io_channel_shutdown() flush on last unref */
475 giochannel_blind->giochannel_orig=giochannel_orig;
476 giochannel_blind->offset=0;
477 giochannel_blind->size=(!giochannel_orig ? 0 : captive_giochannel_size(giochannel_orig));
478 giochannel_blind->buffer_hash=g_hash_table_new_full(
479 (GHashFunc)captive_giochannel_blind_hash_func, /* hash_func */
480 (GEqualFunc)captive_giochannel_blind_equal_func, /* key_equal_func */
481 (GDestroyNotify)captive_giochannel_blind_key_destroy_func, /* key_destroy_func */
482 (GDestroyNotify)captive_giochannel_blind_value_destroy_func); /* value_destroy_func */
484 captive_giochannel_setup(&giochannel_blind->iochannel);
486 return giochannel_blind;
490 gboolean captive_giochannel_blind_get_size(GIOChannel *giochannel,guint64 *size_return)
492 struct captive_giochannel_blind *giochannel_blind;
494 g_return_val_if_fail(giochannel!=NULL,FALSE);
495 g_return_val_if_fail(size_return!=NULL,FALSE);
497 if (giochannel->funcs!=&giochannel_blind_funcs)
499 giochannel_blind=(struct captive_giochannel_blind *)giochannel;
501 *size_return=giochannel_blind->size;
506 typedef void (*sorted_array_filter)
507 (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */);
509 static void captive_giochannel_blind_written_as_sorted_array_foreach
510 (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */)
512 g_return_if_fail(keyp!=NULL);
513 g_return_if_fail(blind_block!=NULL);
514 g_return_if_fail(rpp!=NULL);
516 if (!blind_block->data_written)
519 *((*rpp)++)=blind_block;
522 static void captive_giochannel_blind_read_as_sorted_array_foreach
523 (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */)
525 g_return_if_fail(keyp!=NULL);
526 g_return_if_fail(blind_block!=NULL);
527 g_return_if_fail(rpp!=NULL);
529 if (!blind_block->was_read)
532 *((*rpp)++)=blind_block;
535 static int captive_giochannel_blind_as_sorted_array_compat
536 (const struct blind_block *const *ap,const struct blind_block *const *bp)
538 g_return_val_if_fail(ap!=NULL,0);
539 g_return_val_if_fail(*ap!=NULL,0);
540 g_return_val_if_fail(bp!=NULL,0);
541 g_return_val_if_fail(*bp!=NULL,0);
543 return ((*ap)->offset>(*bp)->offset) - ((*bp)->offset>(*ap)->offset);
546 static struct blind_block **captive_giochannel_blind_as_sorted_array
547 (struct captive_giochannel_blind *giochannel_blind,sorted_array_filter filter_func)
550 struct blind_block **r,**rp;
552 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
554 hash_size=g_hash_table_size(giochannel_blind->buffer_hash);
555 captive_newn(r,hash_size+1);
557 g_hash_table_foreach(giochannel_blind->buffer_hash,(GHFunc)filter_func,&rp);
558 g_assert(rp<=r+hash_size);
560 qsort(r,rp-r,sizeof(*r),(int (*)(const void *,const void *))captive_giochannel_blind_as_sorted_array_compat);
565 GIOStatus captive_giochannel_blind_commit(GIOChannel *giochannel)
567 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)giochannel;
568 struct blind_block **blind_block_array,**blind_blockp;
569 GIOStatus errgiostatus;
571 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
572 g_return_val_if_fail(giochannel_blind->giochannel_orig!=NULL,G_IO_STATUS_ERROR);
574 errgiostatus=g_io_channel_flush(
575 giochannel, /* channel */
577 g_assert(errgiostatus==G_IO_STATUS_NORMAL);
579 blind_block_array=captive_giochannel_blind_as_sorted_array
580 (giochannel_blind,captive_giochannel_blind_written_as_sorted_array_foreach);
582 for (blind_blockp=blind_block_array;*blind_blockp;blind_blockp++) {
583 struct blind_block *blind_block=*blind_blockp;
586 g_assert(blind_block->data_written!=NULL);
588 errgiostatus=g_io_channel_seek_position(
589 giochannel_blind->giochannel_orig, /* channel */
590 blind_block->offset, /* offset */
591 G_SEEK_SET, /* type */
593 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
594 errgiostatus=g_io_channel_write_chars(
595 giochannel_blind->giochannel_orig, /* channel */
596 blind_block->data_written, /* buf */
597 GIOCHANNEL_BLIND_BLOCK_SIZE, /* count */
598 &bytes_written, /* bytes_written */
600 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
601 g_return_val_if_fail(bytes_written==GIOCHANNEL_BLIND_BLOCK_SIZE,G_IO_STATUS_ERROR);
603 g_free(blind_block->data_written);
604 blind_block->data_written=NULL;
607 g_free(blind_block_array);
609 errgiostatus=g_io_channel_flush(
610 giochannel_blind->giochannel_orig, /* channel */
612 g_assert(errgiostatus==G_IO_STATUS_NORMAL);
614 return G_IO_STATUS_NORMAL;
618 xmlNode *captive_giochannel_blind_readreport_to_xml(xmlNode *xml_parent,GIOChannel *giochannel)
620 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)giochannel;
621 struct blind_block **blind_block_array,**blind_blockp;
622 GIOStatus errgiostatus;
623 guint8 data_read[1+GIOCHANNEL_BLIND_BLOCK_SIZE]; /* '1+' for leading stub to prevent shorter output of BN_bn2hex() */
626 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),NULL);
627 g_return_val_if_fail(giochannel_blind->giochannel_orig!=NULL,G_IO_STATUS_ERROR);
629 errgiostatus=g_io_channel_flush(
630 giochannel, /* channel */
632 g_assert(errgiostatus==G_IO_STATUS_NORMAL);
634 xml_media=xmlNewTextChild(xml_parent,NULL,"media",NULL);
635 xmlNewProp(xml_media,"size",captive_printf_alloca("%" G_GUINT64_FORMAT,giochannel_blind->size));
637 blind_block_array=captive_giochannel_blind_as_sorted_array
638 (giochannel_blind,captive_giochannel_blind_read_as_sorted_array_foreach);
640 for (blind_blockp=blind_block_array;*blind_blockp;blind_blockp++) {
641 struct blind_block *blind_block=*blind_blockp;
643 xmlNode *xml_media_read;
644 gchar offset_string[64];
648 +1 /* leading '\n' */
649 +GIOCHANNEL_BLIND_BLOCK_SIZE*2/64*(64+1) /* each line of 64 characters has EOL '\n' */
650 +1],*gd; /* terminating '\0' */
652 errgiostatus=g_io_channel_seek_position(
653 giochannel_blind->giochannel_orig, /* channel */
654 blind_block->offset, /* offset */
655 G_SEEK_SET, /* type */
657 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,NULL);
658 errgiostatus=g_io_channel_read_chars(
659 giochannel_blind->giochannel_orig, /* channel */
660 data_read+1, /* buf */
661 GIOCHANNEL_BLIND_BLOCK_SIZE, /* count */
662 &bytes_read, /* bytes_read */
664 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,NULL);
665 g_return_val_if_fail(bytes_read==GIOCHANNEL_BLIND_BLOCK_SIZE,NULL);
667 /* Convert binary block to 'hex' and reformat line-wrap it to 'hex_out'. */
668 data_read[0]=0xFF; /* stub to prevent shorter output of BN_bn2hex() */
669 bignum=BN_bin2bn(data_read,1+GIOCHANNEL_BLIND_BLOCK_SIZE,NULL);
670 hex=BN_bn2hex(bignum);
672 g_assert(strlen(hex)==2*(1+GIOCHANNEL_BLIND_BLOCK_SIZE));
675 for (s=hex+2;s<hex+2+2*GIOCHANNEL_BLIND_BLOCK_SIZE;s+=64,gd+=64+1) {
681 g_assert(s==hex+2+2*GIOCHANNEL_BLIND_BLOCK_SIZE);
682 g_assert(gd==hex_out+sizeof(hex_out));
683 xml_media_read=xmlNewTextChild(xml_media,NULL,"block",hex_out);
685 g_snprintf(offset_string,sizeof(offset_string),"%" G_GUINT64_FORMAT,blind_block->offset);
686 xmlNewProp(xml_media_read,"offset",offset_string);
690 g_free(blind_block_array);
695 #ifdef HAVE_LIBXML_BUFFERING
696 struct captive_giochannel_blind *captive_giochannel_blind_new_from_xml(xmlTextReader *xml_reader)
698 struct captive_giochannel_blind *r;
699 const xmlChar *xml_name;
701 GIOStatus erriostatus;
703 struct captive_libxml_string_drop_stack *drop_stack=NULL;
705 g_return_val_if_fail(xml_reader!=NULL,NULL);
706 g_return_val_if_fail(xmlTextReaderNodeType(xml_reader)==CAPTIVE_XML_TEXT_READER_NODE_TYPE_START,NULL);
707 xml_name=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader));
708 g_return_val_if_fail(xml_name!=NULL,NULL);
709 g_return_val_if_fail(!xmlStrcmp(xml_name,"media"),NULL);
711 r=captive_giochannel_blind_new(
712 NULL, /* giochannel_orig */
713 TRUE); /* writeable */
714 r->size=captive_libxml_sscanf_gint64(captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,"size")));
720 errint=xmlTextReaderRead(xml_reader);
722 switch ((got_type=xmlTextReaderNodeType(xml_reader))) {
723 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_COMMENT:
725 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT: /* Even empty nodes have some '#text'. */
727 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_START: {
728 const xmlChar *xml_name;
730 xml_name=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader));
731 g_assert(xml_name!=NULL);
732 if (!xmlStrcmp(xml_name,"block")) {
735 const xmlChar *xml_text_hex_in,*xml_char_s;
737 int bignum_num_bytes;
738 gchar bin_hex[2*GIOCHANNEL_BLIND_BLOCK_SIZE+1],*gd;
739 unsigned char bin_out[GIOCHANNEL_BLIND_BLOCK_SIZE];
741 offset=captive_libxml_sscanf_gint64(
742 captive_libxml_string_drop(&drop_stack,xmlTextReaderGetAttribute(xml_reader,"offset")));
744 errint=xmlTextReaderRead(xml_reader);
746 errint=xmlTextReaderNodeType(xml_reader);
747 g_assert(errint==CAPTIVE_XML_TEXT_READER_NODE_TYPE_TEXT);
748 xml_text_hex_in=captive_libxml_string_drop(&drop_stack,xmlTextReaderValue(xml_reader));
749 g_assert(xml_text_hex_in!=NULL);
751 /* Convert binary block from hex line-wrapped 'xml_text_hex_in'. */
753 for (xml_char_s=xml_text_hex_in;*xml_char_s;xml_char_s++)
754 if (!isspace(*xml_char_s)) {
755 g_assert(gd<bin_hex+2*GIOCHANNEL_BLIND_BLOCK_SIZE);
760 errint=BN_hex2bn(&bignum,bin_hex);
761 g_assert(errint==2*GIOCHANNEL_BLIND_BLOCK_SIZE);
762 g_assert(bignum!=NULL);
764 /* Leading zeroes are ommited by BN_bn2bin(). */
765 bignum_num_bytes=BN_num_bytes(bignum);
766 g_assert(bignum_num_bytes<=GIOCHANNEL_BLIND_BLOCK_SIZE);
767 memset(bin_out,0,GIOCHANNEL_BLIND_BLOCK_SIZE-bignum_num_bytes);
768 errint=BN_bn2bin(bignum,bin_out+(GIOCHANNEL_BLIND_BLOCK_SIZE-bignum_num_bytes));
769 g_assert(errint==bignum_num_bytes);
772 erriostatus=g_io_channel_seek_position(
773 &r->iochannel, /* channel */
775 G_SEEK_SET, /* type */
777 g_assert(erriostatus==G_IO_STATUS_NORMAL);
778 erriostatus=g_io_channel_write_chars(
779 &r->iochannel, /* channel */
781 GIOCHANNEL_BLIND_BLOCK_SIZE, /* count */
782 &bytes_written, /* bytes_written */
784 g_assert(erriostatus==G_IO_STATUS_NORMAL);
785 g_assert(bytes_written==GIOCHANNEL_BLIND_BLOCK_SIZE);
787 else g_error("Unknown START node: %s",xml_name);
789 case CAPTIVE_XML_TEXT_READER_NODE_TYPE_END: {
790 const xmlChar *xml_name;
792 xml_name=captive_libxml_string_drop(&drop_stack,xmlTextReaderName(xml_reader));
793 /**/ if (!xmlStrcmp(xml_name,"media")) {
794 scan_end=TRUE; /* proper cleanup */
796 else if (!xmlStrcmp(xml_name,"block")) {
798 else g_error("Unknown END node: %s",xml_name);
801 g_error("Unexpected xmlTextReaderNodeType() type %d",got_type);
802 g_assert_not_reached();
804 captive_libxml_string_drop_flush(&drop_stack);
807 g_assert(drop_stack==NULL);
811 #endif /* HAVE_LIBXML_BUFFERING */