Implemented '--blind' write mode
authorshort <>
Sun, 2 Feb 2003 01:41:52 +0000 (01:41 +0000)
committershort <>
Sun, 2 Feb 2003 01:41:52 +0000 (01:41 +0000)
 - +captive_giochannel_blind GIOChannel ro->rw buffered interface

src/client/libcaptive-gnomevfs/giognomevfs.c
src/libcaptive/client/Makefile.am
src/libcaptive/client/giochannel-blind.c [new file with mode: 0644]
src/libcaptive/client/giochannel-blind.h [new file with mode: 0644]
src/libcaptive/client/init.c

index 5cab786..32c8280 100644 (file)
@@ -232,7 +232,8 @@ gboolean errbool;
                        (GIOChannel *)giognomevfs);     /* image_iochannel */
        G_UNLOCK(libcaptive);
        g_assert(errbool==TRUE);
-       g_assert(captive_image_iochannel==(GIOChannel *)giognomevfs);
+       /* It may not be 'giognomevfs' as it may be wrapped by 'captive_giochannel_blind'. */
+       g_assert(captive_image_iochannel!=NULL);
 
        *giognomevfsp=giognomevfs;
        return GNOME_VFS_OK;
index a9c2763..4209464 100644 (file)
@@ -27,5 +27,7 @@ libclient_la_SOURCES= \
                directory.c \
                file.c \
                file_info.c \
+               giochannel-blind.c \
+               giochannel-blind.h \
                result.c \
                result.h
diff --git a/src/libcaptive/client/giochannel-blind.c b/src/libcaptive/client/giochannel-blind.c
new file mode 100644 (file)
index 0000000..639b9c0
--- /dev/null
@@ -0,0 +1,385 @@
+/* $Id$
+ * glib r/w GIOChannel buffered-over r/o GIOChannel for libcaptive
+ * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ * 
+ * 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 <glib/ghash.h>
+#include <glib/gmessages.h>
+#include "captive/macros.h"
+#include "captive/storage.h"
+
+
+#define GIOCHANNEL_BLIND_BLOCK_SIZE (PAGE_SIZE)
+
+/* FIXME: fill 'err' */
+
+struct captive_giochannel_blind {
+       GIOChannel iochannel;
+       GIOChannel *giochannel_ro;
+       guint64 offset; /* gint64 range */
+       guint64 size;
+       GHashTable *buffer_hash;        /* (guint64 *) -> (guint8[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 *key)
+{
+       g_return_val_if_fail(key!=NULL,0);
+
+       return (*key)^((*key)>>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 *key)
+{
+       g_return_if_fail(key!=NULL);
+
+       g_free(key);
+}
+
+static void captive_giochannel_blind_value_destroy_func(void *data)
+{
+       g_return_if_fail(data!=NULL);
+
+       g_free(data);
+}
+
+
+static gboolean validate_giochannel_blind(struct captive_giochannel_blind *giochannel_blind)
+{
+       g_return_val_if_fail(giochannel_blind!=NULL,FALSE);
+       g_return_val_if_fail(giochannel_blind->giochannel_ro!=NULL,FALSE);
+       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;
+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 */
+
+       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_DOWN(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
+       window_top=CAPTIVE_ROUND_UP(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) {
+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))) {
+                       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 */
+                                       transfer_top-transfer_bottom);  /* n */
+                       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));
+               errgiostatus=g_io_channel_seek_position(
+                               giochannel_blind->giochannel_ro,        /* channel */
+                               transfer_bottom,        /* 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 */
+                               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);
+               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;
+gpointer data;
+guint64 window_bottom,window_top,window_now;
+guint64 transfer_bottom,transfer_top;
+GIOStatus errgiostatus;
+
+       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);
+
+       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);
+
+       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))) {
+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 */
+                       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));
+                               errgiostatus=g_io_channel_seek_position(
+                                               giochannel_blind->giochannel_ro,        /* 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 */
+                                               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);
+                               }
+
+                       /* Missing upper part of buffer? */
+                       if (transfer_top<window_now+GIOCHANNEL_BLIND_BLOCK_SIZE) {
+                               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));
+                               errgiostatus=g_io_channel_seek_position(
+                                               giochannel_blind->giochannel_ro,        /* 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 */
+                                               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 */
+                               }
+
+                       }
+               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));
+               memcpy(
+                               ((char *)data)+transfer_bottom-window_now,      /* dest */
+                               buf+transfer_bottom-giochannel_blind->offset,   /* src */
+                               transfer_top-transfer_bottom);  /* n */
+               }
+
+       *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);
+
+       /* 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;
+
+       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_ro==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);
+
+       return g_io_channel_set_flags(giochannel_blind->giochannel_ro,(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);
+
+       return g_io_channel_get_flags(giochannel_blind->giochannel_ro) | G_IO_FLAG_IS_WRITEABLE;
+}
+
+
+struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_ro)
+{
+struct captive_giochannel_blind *giochannel_blind;
+GIOStatus erriostatus;
+
+       g_return_val_if_fail(giochannel_ro!=NULL,NULL);
+
+       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);
+
+       erriostatus=g_io_channel_set_encoding(giochannel_ro,
+                       NULL,   /* encoding; force binary data */
+                       NULL);  /* error */
+       g_assert(erriostatus==G_IO_STATUS_NORMAL);
+
+       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;
+       giochannel_blind->iochannel.is_writeable=TRUE;
+       giochannel_blind->iochannel.close_on_unref=TRUE;        /* run g_io_channel_shutdown() flush on last unref */
+       giochannel_blind->giochannel_ro=giochannel_ro;
+       giochannel_blind->offset=0;
+       giochannel_blind->size=captive_giochannel_size(giochannel_ro);
+       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 */
+
+       return giochannel_blind;
+}
diff --git a/src/libcaptive/client/giochannel-blind.h b/src/libcaptive/client/giochannel-blind.h
new file mode 100644 (file)
index 0000000..466862a
--- /dev/null
@@ -0,0 +1,36 @@
+/* $Id$
+ * Include file for glib r/w GIOChannel buffered-over r/o GIOChannel for libcaptive
+ * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ * 
+ * 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
+ */
+
+
+#ifndef _CAPTIVE_GNOMEVFS_GIOCHANNEL_BLIND_H
+#define _CAPTIVE_GNOMEVFS_GIOCHANNEL_BLIND_H 1
+
+
+#include <glib/giochannel.h>
+
+
+G_BEGIN_DECLS
+
+struct captive_giochannel_blind;
+
+struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_ro);
+
+G_END_DECLS
+
+
+#endif /* _CAPTIVE_GNOMEVFS_GIOCHANNEL_BLIND_H */
index f90f4ba..202f38c 100644 (file)
@@ -43,6 +43,7 @@
 #include <glib/gstrfuncs.h>
 #include <glib/glist.h>
 #include <glib/gutils.h>       /* for g_atexit() */
+#include "giochannel-blind.h"
 
 
 /* Are we initialized? */
@@ -62,6 +63,8 @@ gchar *captive_option_filesystem;
 enum captive_option_rwmode captive_option_rwmode=CAPTIVE_OPTION_RWMODE_BLIND;
 enum captive_option_media captive_option_media=CAPTIVE_OPTION_MEDIA_DISK;
 GIOChannel *captive_image_iochannel;
+/* Non-blinded original GIOChannel for flushing during shutdown. */
+static GIOChannel *captive_image_iochannel_orig;
 guint64 captive_image_size;
 static GList *captive_load_module;
 
@@ -367,7 +370,12 @@ args_err:
 
        g_return_val_if_fail(image_iochannel!=NULL,FALSE);
 
-       captive_image_iochannel=image_iochannel;
+       if (captive_option_rwmode==CAPTIVE_OPTION_RWMODE_BLIND)
+               captive_image_iochannel=(GIOChannel *)captive_giochannel_blind_new(image_iochannel);
+       else
+               captive_image_iochannel=image_iochannel;
+       captive_image_iochannel_orig=image_iochannel;
+
        /* Do not initialize 'captive_image_size' by captive_giochannel_size() here
         * as we yet need to do g_io_channel_set_encoding().
         */
@@ -393,6 +401,8 @@ args_err:
  */
 gboolean captive_shutdown(void)
 {
+GIOStatus erriostatus;
+
        g_return_val_if_fail(active==TRUE,FALSE);
        g_return_val_if_fail(captive_image_iochannel!=NULL,FALSE);
 
@@ -402,6 +412,20 @@ gboolean captive_shutdown(void)
        IoShutdownRegisteredFileSystems();
        IoShutdownRegisteredDevices();
 
+       /* libcaptive is not authorized to shutdown 'captive_image_channel'. */
+       erriostatus=g_io_channel_flush(
+                       captive_image_iochannel,        /* channel */
+                       NULL);  /* error */
+       g_assert(erriostatus==G_IO_STATUS_NORMAL);
+
+       /* 'captive_image_iochannel' may be blinded wrapper. */
+       if (captive_image_iochannel!=captive_image_iochannel_orig) {
+               erriostatus=g_io_channel_flush(
+                               captive_image_iochannel_orig,   /* channel */
+                               NULL);  /* error */
+               g_assert(erriostatus==G_IO_STATUS_NORMAL);
+               }
+
        captive_image_iochannel=NULL;
 
        active=FALSE;