+Generates --bug-pathname resources file for sandbox crash bugreport
[captive.git] / src / libcaptive / client / giochannel-blind.c
1 /* $Id$
2  * glib r/w GIOChannel buffered-over r/o GIOChannel for libcaptive
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
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
8  * 
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.
13  * 
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
17  */
18
19
20 #include "config.h"
21
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
32
33 /* CONFIG: */
34 #define GIOCHANNEL_BLIND_BLOCK_SIZE (PAGE_SIZE)
35
36
37 /* FIXME: fill 'err' */
38
39 struct captive_giochannel_blind {
40         GIOChannel iochannel;
41         GIOChannel *giochannel_orig;
42         guint64 offset; /* gint64 range */
43         guint64 size;
44         GHashTable *buffer_hash;        /* (guint64 *) -> (struct blind_block *)  (guint8[GIOCHANNEL_BLIND_BLOCK_SIZE]) */
45         };
46
47 struct blind_block {
48         guint64 offset;
49         gboolean was_read,was_written;
50         guint8 *data_written;   /* [GIOCHANNEL_BLIND_BLOCK_SIZE] */
51         };
52
53
54 G_LOCK_DEFINE_STATIC(giochannel_blind_funcs);
55 static GIOFuncs giochannel_blind_funcs;
56
57
58 static guint captive_giochannel_blind_hash_func(const guint64 *keyp)
59 {
60         g_return_val_if_fail(keyp!=NULL,0);
61
62         return (*keyp)^((*keyp)>>23);
63 }
64
65 static gboolean captive_giochannel_blind_equal_func(const guint64 *ap,const guint64 *bp)
66 {
67         g_return_val_if_fail(ap!=NULL,FALSE);
68         g_return_val_if_fail(bp!=NULL,FALSE);
69
70         return (*ap)==(*bp);
71 }
72
73 static void captive_giochannel_blind_key_destroy_func(guint64 *keyp)
74 {
75         g_return_if_fail(keyp!=NULL);
76
77         g_free(keyp);
78 }
79
80 static void captive_giochannel_blind_value_destroy_func(struct blind_block *blind_block)
81 {
82         g_return_if_fail(blind_block!=NULL);
83
84         g_free(blind_block->data_written);
85         g_free(blind_block);
86 }
87
88
89 static gboolean validate_giochannel_blind(struct captive_giochannel_blind *giochannel_blind)
90 {
91         g_return_val_if_fail(giochannel_blind->iochannel.funcs==&giochannel_blind_funcs,FALSE);
92         g_return_val_if_fail(giochannel_blind!=NULL,FALSE);
93         g_return_val_if_fail(giochannel_blind->giochannel_orig!=NULL,FALSE);
94         g_return_val_if_fail((gint64)giochannel_blind->offset>=0,FALSE);        /* gint64 overflow stored in guint64 */
95         g_return_val_if_fail(giochannel_blind->buffer_hash!=NULL,FALSE);
96
97         return TRUE;
98 }
99
100
101 static GIOStatus captive_giochannel_blind_io_read
102                 (GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err)
103 {
104 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
105 guint64 window_bottom,window_top,window_now;
106 guint64 transfer_bottom,transfer_top;
107 GIOStatus errgiostatus;
108 guint64 maxread;        /* maximum offset of end of data we successfuly read */
109 struct blind_block *blind_block;
110
111         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
112         g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
113         g_return_val_if_fail(bytes_read!=NULL,G_IO_STATUS_ERROR);
114
115         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read(offset=0x%llX,count=0x%lX)",G_STRLOC,
116                         giochannel_blind->offset,(gulong)count);
117
118         window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
119         window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
120         maxread=giochannel_blind->offset;
121
122         for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
123 gsize bytes_read;
124
125                 transfer_bottom=MAX(window_now,giochannel_blind->offset);
126                 transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
127                 if ((blind_block=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now)) && blind_block->data_written) {
128                         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)",
129                                         G_STRLOC,
130                                         (guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(gulong)(transfer_bottom-window_now),
131                                         (gulong)(transfer_top-transfer_bottom));
132                         memcpy(
133                                         buf+transfer_bottom-giochannel_blind->offset,   /* dest */
134                                         blind_block->data_written+transfer_bottom-window_now,   /* src */
135                                         transfer_top-transfer_bottom);  /* n */
136                         blind_block->was_read=TRUE;
137                         maxread=transfer_top;
138                         continue;
139                         }
140                 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,
141                                 (guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(guint64)transfer_bottom,
142                                 (gulong)(transfer_top-transfer_bottom));
143                 errgiostatus=g_io_channel_seek_position(
144                                 giochannel_blind->giochannel_orig,      /* channel */
145                                 transfer_bottom,        /* offset */
146                                 G_SEEK_SET,     /* type */
147                                 err);   /* error */
148                 /* During seek in block device such as on URL file:///dev/hda1#captive-fastfat.sys-ro:/
149                  * we will do llseek(2) on "/dev/hda1" device from captive_giochannel_size().
150                  * Although we are allowed to seek behind EOF on regular files
151                  * at least linux-kernel-2.4.19-ac4/fs/block_dev.c/block_llseek() will give
152                  * EINVAL on seek behind EOF therefore it must be accepted without complaints by us.
153                  */
154                 if (errgiostatus!=G_IO_STATUS_NORMAL) {
155                         errgiostatus=G_IO_STATUS_EOF;
156                         bytes_read=0;
157                         }
158                 else {
159                         errgiostatus=g_io_channel_read_chars(
160                                         giochannel_blind->giochannel_orig,      /* channel */
161                                         buf+transfer_bottom-giochannel_blind->offset,   /* buf */
162                                         transfer_top-transfer_bottom,   /* count */
163                                         &bytes_read,    /* bytes_read */
164                                         err);   /* error */
165                         }
166                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
167                 g_return_val_if_fail(bytes_read<=(transfer_top-transfer_bottom),G_IO_STATUS_ERROR);
168                 g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
169                 if (bytes_read) {
170                         if (!blind_block) {
171 guint64 *keyp;
172
173                                 captive_new(blind_block);
174                                 blind_block->offset=window_now;
175                                 blind_block->was_read=FALSE;
176                                 blind_block->was_written=FALSE;
177                                 blind_block->data_written=NULL;
178                                 captive_new(keyp);
179                                 *keyp=window_now;
180                                 g_hash_table_insert(
181                                                 giochannel_blind->buffer_hash,  /* hash_table */
182                                                 keyp,   /* key */
183                                                 blind_block);   /* value */
184                                 }
185                         blind_block->was_read=TRUE;
186                         }
187                 maxread=transfer_bottom+bytes_read;
188                 if (bytes_read==transfer_top-transfer_bottom)
189                         g_return_val_if_fail(transfer_bottom+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
190                 else
191                         break;
192                 }
193
194         *bytes_read=maxread-giochannel_blind->offset;
195         giochannel_blind->offset=maxread;
196         return (*bytes_read == 0 ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL);
197 }
198
199
200 static GIOStatus captive_giochannel_blind_io_write
201                 (GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err)
202 {
203 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
204 guint64 window_bottom,window_top,window_now;
205 guint64 transfer_bottom,transfer_top;
206 GIOStatus errgiostatus;
207 struct blind_block *blind_block;
208
209         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
210         g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
211         g_return_val_if_fail(bytes_written!=NULL,G_IO_STATUS_ERROR);
212
213         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write(offset=0x%llX,count=0x%lX)",G_STRLOC,
214                         giochannel_blind->offset,(gulong)count);
215
216         g_return_val_if_fail(giochannel_blind->offset+count<=giochannel_blind->size,G_IO_STATUS_ERROR);
217
218         g_return_val_if_fail(giochannel_blind->iochannel.is_writeable==TRUE,G_IO_STATUS_ERROR);
219
220         window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
221         window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
222
223         for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
224 gsize bytes_read;
225
226                 transfer_bottom=MAX(window_now,giochannel_blind->offset);
227                 transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
228                 if (!(blind_block=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now)) || !blind_block->data_written) {
229                         if (!blind_block) {
230 guint64 *keyp;
231
232                                 captive_new(blind_block);
233                                 blind_block->offset=window_now;
234                                 blind_block->was_read=FALSE;
235                                 blind_block->was_written=FALSE;
236                                 captive_new(keyp);
237                                 *keyp=window_now;
238                                 g_hash_table_insert(
239                                                 giochannel_blind->buffer_hash,  /* hash_table */
240                                                 keyp,   /* key */
241                                                 blind_block);   /* value */
242                                 }
243                         blind_block->data_written=g_malloc(GIOCHANNEL_BLIND_BLOCK_SIZE);
244                         g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-new-mem(window_now=0x%llX)",G_STRLOC,
245                                         (guint64)window_now);
246
247                         /* Missing lower part of buffer? */
248                         if (transfer_bottom>window_now) {
249                                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-mem-read-lower(seek=0x%llX,count=0x%lX)",G_STRLOC,
250                                                 (guint64)window_now,(gulong)(transfer_bottom-window_now));
251                                 errgiostatus=g_io_channel_seek_position(
252                                                 giochannel_blind->giochannel_orig,      /* channel */
253                                                 window_now,     /* offset */
254                                                 G_SEEK_SET,     /* type */
255                                                 err);   /* error */
256                                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
257                                 errgiostatus=g_io_channel_read_chars(
258                                                 giochannel_blind->giochannel_orig,      /* channel */
259                                                 blind_block->data_written,      /* buf */
260                                                 transfer_bottom-window_now,     /* count */
261                                                 &bytes_read,    /* bytes_read */
262                                                 err);   /* error */
263                                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
264                                 g_return_val_if_fail(bytes_read==(transfer_bottom-window_now),G_IO_STATUS_ERROR);
265                                 }
266
267                         /* Missing upper part of buffer? */
268                         if (transfer_top<window_now+GIOCHANNEL_BLIND_BLOCK_SIZE) {
269                                 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,
270                                                 (gulong)(transfer_top-window_now),(guint64)transfer_top,
271                                                 (gulong)(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top));
272                                 errgiostatus=g_io_channel_seek_position(
273                                                 giochannel_blind->giochannel_orig,      /* channel */
274                                                 transfer_top,   /* offset */
275                                                 G_SEEK_SET,     /* type */
276                                                 err);   /* error */
277                                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
278                                 errgiostatus=g_io_channel_read_chars(
279                                                 giochannel_blind->giochannel_orig,      /* channel */
280                                                 blind_block->data_written+transfer_top-window_now,      /* buf */
281                                                 window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top,    /* count */
282                                                 &bytes_read,    /* bytes_read */
283                                                 err);   /* error */
284                                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
285                                 g_return_val_if_fail(bytes_read<=(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top),G_IO_STATUS_ERROR);
286                                 g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
287                                 if (bytes_read==window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top)
288                                         g_return_val_if_fail(transfer_top+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
289                                 else
290                                         g_return_val_if_fail(transfer_top+bytes_read==giochannel_blind->size,G_IO_STATUS_ERROR);        /* EOF hit */
291                                 }
292
293                         }
294                 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)",
295                                 G_STRLOC,
296                                 (guint64)window_now,(gulong)(transfer_bottom-window_now),(gulong)(transfer_bottom-giochannel_blind->offset),
297                                 (gulong)(transfer_top-transfer_bottom));
298                 g_assert(blind_block); g_assert(blind_block->data_written);
299                 memcpy(
300                                 ((char *)blind_block->data_written)+transfer_bottom-window_now, /* dest */
301                                 buf+transfer_bottom-giochannel_blind->offset,   /* src */
302                                 transfer_top-transfer_bottom);  /* n */
303                 blind_block->was_written=TRUE;
304                 }
305
306         *bytes_written=count;
307         giochannel_blind->offset+=(*bytes_written);
308         return G_IO_STATUS_NORMAL;
309 }
310
311
312 static GIOStatus captive_giochannel_blind_io_seek(GIOChannel *channel,gint64 offset,GSeekType type,GError **err)
313 {
314 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
315
316         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
317
318         switch (type) {
319                 case G_SEEK_CUR: giochannel_blind->offset+=                       offset; break;
320                 case G_SEEK_SET: giochannel_blind->offset =                       offset; break;
321                 case G_SEEK_END: giochannel_blind->offset =giochannel_blind->size+offset; break;
322                 default: g_return_val_if_reached(G_IO_STATUS_ERROR);
323                 }
324         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);    /* 'offset' overflow? */
325
326         return G_IO_STATUS_NORMAL;
327 }
328
329
330 static GIOStatus captive_giochannel_blind_io_close(GIOChannel *channel,GError **err)
331 {
332 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
333 GIOStatus erriostatus;
334
335         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
336
337         /* We are not authorized to destroy 'giochannel_blind->giochannel_orig'. */
338         erriostatus=g_io_channel_flush(
339                         giochannel_blind->giochannel_orig,      /* channel */
340                         NULL);  /* error */
341         g_assert(erriostatus==G_IO_STATUS_NORMAL);
342         giochannel_blind->giochannel_orig=NULL;
343
344         g_hash_table_destroy(giochannel_blind->buffer_hash);
345         giochannel_blind->buffer_hash=NULL;
346
347         return G_IO_STATUS_NORMAL;
348 }
349
350
351 static GSource* captive_giochannel_blind_io_create_watch(GIOChannel *channel,GIOCondition condition)
352 {
353 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
354
355         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),NULL);
356
357         g_return_val_if_reached(NULL);  /* FIXME: NOT IMPLEMENTED YET */
358 }
359
360
361 static void captive_giochannel_blind_io_free(GIOChannel *channel)
362 {
363 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
364
365         /* After captive_giochannel_blind_io_close() 'giochannel_blind'
366          * may be no longer valid for validate_giochannel_blind(giochannel_blind).
367          */
368         g_return_if_fail(giochannel_blind!=NULL);
369
370         g_assert(giochannel_blind->giochannel_orig==NULL);
371         g_assert(giochannel_blind->buffer_hash==NULL);
372
373         g_free(giochannel_blind);
374 }
375
376
377 static GIOStatus captive_giochannel_blind_io_set_flags(GIOChannel *channel,GIOFlags flags,GError **err)
378 {
379 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
380
381         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
382
383         return g_io_channel_set_flags(giochannel_blind->giochannel_orig,(flags&~G_IO_FLAG_IS_WRITEABLE),err);
384 }
385
386
387 static GIOFlags captive_giochannel_blind_io_get_flags(GIOChannel *channel)
388 {
389 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
390
391         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),0);
392
393         return g_io_channel_get_flags(giochannel_blind->giochannel_orig) | G_IO_FLAG_IS_WRITEABLE;
394 }
395
396
397 struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_orig,gboolean writeable)
398 {
399 struct captive_giochannel_blind *giochannel_blind;
400 GIOStatus erriostatus;
401
402         g_return_val_if_fail(giochannel_orig!=NULL,NULL);
403
404         G_LOCK(giochannel_blind_funcs);
405         giochannel_blind_funcs.io_read        =captive_giochannel_blind_io_read;
406         giochannel_blind_funcs.io_write       =captive_giochannel_blind_io_write;
407         giochannel_blind_funcs.io_seek        =captive_giochannel_blind_io_seek;
408         giochannel_blind_funcs.io_close       =captive_giochannel_blind_io_close;
409         giochannel_blind_funcs.io_create_watch=captive_giochannel_blind_io_create_watch;
410         giochannel_blind_funcs.io_free        =captive_giochannel_blind_io_free;
411         giochannel_blind_funcs.io_set_flags   =captive_giochannel_blind_io_set_flags;
412         giochannel_blind_funcs.io_get_flags   =captive_giochannel_blind_io_get_flags;
413         G_UNLOCK(giochannel_blind_funcs);
414
415         erriostatus=g_io_channel_set_encoding(giochannel_orig,
416                         NULL,   /* encoding; force binary data */
417                         NULL);  /* error */
418         g_assert(erriostatus==G_IO_STATUS_NORMAL);
419
420         captive_new(giochannel_blind);
421         g_assert(G_STRUCT_OFFSET(struct captive_giochannel_blind,iochannel)==0);        /* safely re-type-able */
422         g_io_channel_init(&giochannel_blind->iochannel);
423         giochannel_blind->iochannel.funcs=&giochannel_blind_funcs;
424         giochannel_blind->iochannel.is_seekable=TRUE;
425         giochannel_blind->iochannel.is_readable=TRUE;
426         /* readonly captive_giochannel_blind can be used to track read access. */
427         giochannel_blind->iochannel.is_writeable=writeable;
428         giochannel_blind->iochannel.close_on_unref=TRUE;        /* run g_io_channel_shutdown() flush on last unref */
429         giochannel_blind->giochannel_orig=giochannel_orig;
430         giochannel_blind->offset=0;
431         giochannel_blind->size=captive_giochannel_size(giochannel_orig);
432         giochannel_blind->buffer_hash=g_hash_table_new_full(
433                         (GHashFunc)captive_giochannel_blind_hash_func,  /* hash_func */
434                         (GEqualFunc)captive_giochannel_blind_equal_func,        /* key_equal_func */
435                         (GDestroyNotify)captive_giochannel_blind_key_destroy_func,      /* key_destroy_func */
436                         (GDestroyNotify)captive_giochannel_blind_value_destroy_func);   /* value_destroy_func */
437
438         return giochannel_blind;
439 }
440
441
442 gboolean captive_giochannel_blind_get_size(GIOChannel *giochannel,guint64 *size_return)
443 {
444 struct captive_giochannel_blind *giochannel_blind;
445
446         g_return_val_if_fail(giochannel!=NULL,FALSE);
447         g_return_val_if_fail(size_return!=NULL,FALSE);
448
449         if (giochannel->funcs!=&giochannel_blind_funcs)
450                 return FALSE;
451         giochannel_blind=(struct captive_giochannel_blind *)giochannel;
452
453         *size_return=giochannel_blind->size;
454         return TRUE;
455 }
456
457
458 typedef void (*sorted_array_filter)
459                 (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */);
460
461 static void captive_giochannel_blind_written_as_sorted_array_foreach
462                 (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */)
463 {
464         g_return_if_fail(keyp!=NULL);
465         g_return_if_fail(blind_block!=NULL);
466         g_return_if_fail(rpp!=NULL);
467
468         if (!blind_block->data_written)
469                 return;
470
471         *((*rpp)++)=blind_block;
472 }
473
474 static void captive_giochannel_blind_read_as_sorted_array_foreach
475                 (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */)
476 {
477         g_return_if_fail(keyp!=NULL);
478         g_return_if_fail(blind_block!=NULL);
479         g_return_if_fail(rpp!=NULL);
480
481         if (!blind_block->was_read)
482                 return;
483
484         *((*rpp)++)=blind_block;
485 }
486
487 static int captive_giochannel_blind_as_sorted_array_compat
488                 (const struct blind_block *const *ap,const struct blind_block *const *bp)
489 {
490         g_return_val_if_fail(ap!=NULL,0);
491         g_return_val_if_fail(*ap!=NULL,0);
492         g_return_val_if_fail(bp!=NULL,0);
493         g_return_val_if_fail(*bp!=NULL,0);
494
495         return ((*ap)->offset>(*bp)->offset) - ((*bp)->offset>(*ap)->offset);
496 }
497
498 static struct blind_block **captive_giochannel_blind_as_sorted_array
499                 (struct captive_giochannel_blind *giochannel_blind,sorted_array_filter filter_func)
500 {
501 guint hash_size;
502 struct blind_block **r,**rp;
503
504         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
505
506         hash_size=g_hash_table_size(giochannel_blind->buffer_hash);
507         captive_newn(r,hash_size+1);
508         rp=r;
509         g_hash_table_foreach(giochannel_blind->buffer_hash,(GHFunc)filter_func,&rp);
510         g_assert(rp<=r+hash_size);
511         *rp=NULL;
512         qsort(r,rp-r,sizeof(*r),(int (*)(const void *,const void *))captive_giochannel_blind_as_sorted_array_compat);
513
514         return r;
515 }
516
517 GIOStatus captive_giochannel_blind_commit(GIOChannel *giochannel)
518 {
519 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)giochannel;
520 struct blind_block **blind_block_array,**blind_blockp;
521 GIOStatus errgiostatus;
522
523         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
524
525         errgiostatus=g_io_channel_flush(
526                         giochannel,     /* channel */
527                         NULL);  /* error */
528         g_assert(errgiostatus==G_IO_STATUS_NORMAL);
529
530         blind_block_array=captive_giochannel_blind_as_sorted_array
531                         (giochannel_blind,captive_giochannel_blind_written_as_sorted_array_foreach);
532
533         for (blind_blockp=blind_block_array;*blind_blockp;blind_blockp++) {
534 struct blind_block *blind_block=*blind_blockp;
535 gsize bytes_written;
536
537                 g_assert(blind_block->data_written!=NULL);
538
539                 errgiostatus=g_io_channel_seek_position(
540                                 giochannel_blind->giochannel_orig,      /* channel */
541                                 blind_block->offset,    /* offset */
542                                 G_SEEK_SET,     /* type */
543                                 NULL);  /* error */
544                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
545                 errgiostatus=g_io_channel_write_chars(
546                                 giochannel_blind->giochannel_orig,      /* channel */
547                                 blind_block->data_written,      /* buf */
548                                 GIOCHANNEL_BLIND_BLOCK_SIZE,    /* count */
549                                 &bytes_written, /* bytes_written */
550                                 NULL);  /* error */
551                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
552                 g_return_val_if_fail(bytes_written==GIOCHANNEL_BLIND_BLOCK_SIZE,G_IO_STATUS_ERROR);
553
554                 g_free(blind_block->data_written);
555                 blind_block->data_written=NULL;
556                 }
557
558         g_free(blind_block_array);
559
560         errgiostatus=g_io_channel_flush(
561                         giochannel_blind->giochannel_orig,      /* channel */
562                         NULL);  /* error */
563         g_assert(errgiostatus==G_IO_STATUS_NORMAL);
564
565         return G_IO_STATUS_NORMAL;
566 }
567
568
569 xmlNode *captive_giochannel_blind_readreport_to_xml(xmlNode *xml_parent,GIOChannel *giochannel)
570 {
571 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)giochannel;
572 struct blind_block **blind_block_array,**blind_blockp;
573 GIOStatus errgiostatus;
574 guint8 data_read[1+GIOCHANNEL_BLIND_BLOCK_SIZE];        /* '1+' for leading stub to prevent shorter output of BN_bn2hex() */
575 xmlNode *xml_media;
576
577         g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),NULL);
578
579         errgiostatus=g_io_channel_flush(
580                         giochannel,     /* channel */
581                         NULL);  /* error */
582         g_assert(errgiostatus==G_IO_STATUS_NORMAL);
583
584         xml_media=xmlNewTextChild(xml_parent,NULL,"media",NULL);
585         xmlNewProp(xml_media,"size",captive_printf_alloca("%" G_GUINT64_FORMAT,giochannel_blind->size));
586
587         blind_block_array=captive_giochannel_blind_as_sorted_array
588                         (giochannel_blind,captive_giochannel_blind_read_as_sorted_array_foreach);
589
590         for (blind_blockp=blind_block_array;*blind_blockp;blind_blockp++) {
591 struct blind_block *blind_block=*blind_blockp;
592 gsize bytes_read;
593 xmlNode *xml_media_read;
594 gchar offset_string[64];
595 BIGNUM *bignum;
596 char *hex,*s;
597 gchar hex_out[0
598                 +1 /* leading '\n' */
599                 +GIOCHANNEL_BLIND_BLOCK_SIZE*2/64*(64+1)        /* each line of 64 characters has EOL '\n' */
600                 +1],*gd;        /* terminating '\0' */
601
602                 errgiostatus=g_io_channel_seek_position(
603                                 giochannel_blind->giochannel_orig,      /* channel */
604                                 blind_block->offset,    /* offset */
605                                 G_SEEK_SET,     /* type */
606                                 NULL);  /* error */
607                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,NULL);
608                 errgiostatus=g_io_channel_read_chars(
609                                 giochannel_blind->giochannel_orig,      /* channel */
610                                 data_read+1,    /* buf */
611                                 GIOCHANNEL_BLIND_BLOCK_SIZE,    /* count */
612                                 &bytes_read,    /* bytes_read */
613                                 NULL);  /* error */
614                 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,NULL);
615                 g_return_val_if_fail(bytes_read==GIOCHANNEL_BLIND_BLOCK_SIZE,NULL);
616
617                 /* Convert binary block to 'hex' and reformat line-wrap it to 'hex_out'. */
618                 data_read[0]=0xFF;      /* stub to prevent shorter output of BN_bn2hex() */
619                 bignum=BN_bin2bn(data_read,1+GIOCHANNEL_BLIND_BLOCK_SIZE,NULL);
620                 hex=BN_bn2hex(bignum);
621                 BN_free(bignum);
622                 g_assert(strlen(hex)==2*(1+GIOCHANNEL_BLIND_BLOCK_SIZE));
623                 gd=hex_out;
624                 *gd++='\n';
625                 for (s=hex+2;s<hex+2+2*GIOCHANNEL_BLIND_BLOCK_SIZE;s+=64,gd+=64+1) {
626                         memcpy(gd,s,64);
627                         gd[64]='\n';
628                         }
629                 OPENSSL_free(hex);
630                 *gd++=0;
631                 g_assert(s==hex+2+2*GIOCHANNEL_BLIND_BLOCK_SIZE);
632                 g_assert(gd==hex_out+sizeof(hex_out));
633                 xml_media_read=xmlNewTextChild(xml_media,NULL,"block",hex_out);
634                 {
635                         g_snprintf(offset_string,sizeof(offset_string),"%" G_GUINT64_FORMAT,blind_block->offset);
636                         xmlNewProp(xml_media_read,"offset",offset_string);
637                         }
638                 }
639
640         g_free(blind_block_array);
641
642         return xml_media;
643 }