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"
30 #define GIOCHANNEL_BLIND_BLOCK_SIZE (PAGE_SIZE)
32 /* FIXME: fill 'err' */
34 struct captive_giochannel_blind {
36 GIOChannel *giochannel_ro;
37 guint64 offset; /* gint64 range */
39 GHashTable *buffer_hash; /* (guint64 *) -> (guint8[GIOCHANNEL_BLIND_BLOCK_SIZE]) */
43 G_LOCK_DEFINE_STATIC(giochannel_blind_funcs);
44 static GIOFuncs giochannel_blind_funcs;
47 static guint captive_giochannel_blind_hash_func(const guint64 *key)
49 g_return_val_if_fail(key!=NULL,0);
51 return (*key)^((*key)>>23);
54 static gboolean captive_giochannel_blind_equal_func(const guint64 *ap,const guint64 *bp)
56 g_return_val_if_fail(ap!=NULL,FALSE);
57 g_return_val_if_fail(bp!=NULL,FALSE);
62 static void captive_giochannel_blind_key_destroy_func(guint64 *key)
64 g_return_if_fail(key!=NULL);
69 static void captive_giochannel_blind_value_destroy_func(void *data)
71 g_return_if_fail(data!=NULL);
77 static gboolean validate_giochannel_blind(struct captive_giochannel_blind *giochannel_blind)
79 g_return_val_if_fail(giochannel_blind!=NULL,FALSE);
80 g_return_val_if_fail(giochannel_blind->giochannel_ro!=NULL,FALSE);
81 g_return_val_if_fail((gint64)giochannel_blind->offset>=0,FALSE); /* gint64 overflow stored in guint64 */
82 g_return_val_if_fail(giochannel_blind->buffer_hash!=NULL,FALSE);
88 static GIOStatus captive_giochannel_blind_io_read
89 (GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err)
91 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
93 guint64 window_bottom,window_top,window_now;
94 guint64 transfer_bottom,transfer_top;
95 GIOStatus errgiostatus;
96 guint64 maxread; /* maximum offset of end of data we successfuly read */
98 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
99 g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
100 g_return_val_if_fail(bytes_read!=NULL,G_IO_STATUS_ERROR);
102 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read(offset=0x%llX,count=0x%lX)",G_STRLOC,
103 giochannel_blind->offset,(gulong)count);
105 window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
106 window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
107 maxread=giochannel_blind->offset;
109 for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
112 transfer_bottom=MAX(window_now,giochannel_blind->offset);
113 transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
114 if ((data=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now))) {
115 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)",
117 (guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(gulong)(transfer_bottom-window_now),
118 (gulong)(transfer_top-transfer_bottom));
120 buf+transfer_bottom-giochannel_blind->offset, /* dest */
121 data+transfer_bottom-window_now, /* src */
122 transfer_top-transfer_bottom); /* n */
123 maxread=transfer_top;
126 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,
127 (guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(guint64)transfer_bottom,
128 (gulong)(transfer_top-transfer_bottom));
129 errgiostatus=g_io_channel_seek_position(
130 giochannel_blind->giochannel_ro, /* channel */
131 transfer_bottom, /* offset */
132 G_SEEK_SET, /* type */
134 /* During seek in block device such as on URL file:///dev/hda1#captive-fastfat.sys-ro:/
135 * we will do llseek(2) on "/dev/hda1" device from captive_giochannel_size().
136 * Although we are allowed to seek behind EOF on regular files
137 * at least linux-kernel-2.4.19-ac4/fs/block_dev.c/block_llseek() will give
138 * EINVAL on seek behind EOF therefore it must be accepted without complaints by us.
140 if (errgiostatus!=G_IO_STATUS_NORMAL) {
141 errgiostatus=G_IO_STATUS_EOF;
145 errgiostatus=g_io_channel_read_chars(
146 giochannel_blind->giochannel_ro, /* channel */
147 buf+transfer_bottom-giochannel_blind->offset, /* buf */
148 transfer_top-transfer_bottom, /* count */
149 &bytes_read, /* bytes_read */
152 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
153 g_return_val_if_fail(bytes_read<=(transfer_top-transfer_bottom),G_IO_STATUS_ERROR);
154 g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
155 maxread=transfer_bottom+bytes_read;
156 if (bytes_read==transfer_top-transfer_bottom)
157 g_return_val_if_fail(transfer_bottom+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
162 *bytes_read=maxread-giochannel_blind->offset;
163 giochannel_blind->offset=maxread;
164 return (*bytes_read == 0 ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL);
168 static GIOStatus captive_giochannel_blind_io_write
169 (GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err)
171 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
173 guint64 window_bottom,window_top,window_now;
174 guint64 transfer_bottom,transfer_top;
175 GIOStatus errgiostatus;
177 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
178 g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
179 g_return_val_if_fail(bytes_written!=NULL,G_IO_STATUS_ERROR);
181 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write(offset=0x%llX,count=0x%lX)",G_STRLOC,
182 giochannel_blind->offset,(gulong)count);
184 g_return_val_if_fail(giochannel_blind->offset+count<=giochannel_blind->size,G_IO_STATUS_ERROR);
186 window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
187 window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
189 for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
192 transfer_bottom=MAX(window_now,giochannel_blind->offset);
193 transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
194 if (!(data=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now))) {
197 data=g_malloc(GIOCHANNEL_BLIND_BLOCK_SIZE);
201 giochannel_blind->buffer_hash, /* hash_table */
204 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-new-mem(window_now=0x%llX)",G_STRLOC,
205 (guint64)window_now);
207 /* Missing lower part of buffer? */
208 if (transfer_bottom>window_now) {
209 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-mem-read-lower(seek=0x%llX,count=0x%lX)",G_STRLOC,
210 (guint64)window_now,(gulong)(transfer_bottom-window_now));
211 errgiostatus=g_io_channel_seek_position(
212 giochannel_blind->giochannel_ro, /* channel */
213 window_now, /* offset */
214 G_SEEK_SET, /* type */
216 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
217 errgiostatus=g_io_channel_read_chars(
218 giochannel_blind->giochannel_ro, /* channel */
220 transfer_bottom-window_now, /* count */
221 &bytes_read, /* bytes_read */
223 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
224 g_return_val_if_fail(bytes_read==(transfer_bottom-window_now),G_IO_STATUS_ERROR);
227 /* Missing upper part of buffer? */
228 if (transfer_top<window_now+GIOCHANNEL_BLIND_BLOCK_SIZE) {
229 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,
230 (gulong)(transfer_top-window_now),(guint64)transfer_top,
231 (gulong)(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top));
232 errgiostatus=g_io_channel_seek_position(
233 giochannel_blind->giochannel_ro, /* channel */
234 transfer_top, /* offset */
235 G_SEEK_SET, /* type */
237 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
238 errgiostatus=g_io_channel_read_chars(
239 giochannel_blind->giochannel_ro, /* channel */
240 data+transfer_top-window_now, /* buf */
241 window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top, /* count */
242 &bytes_read, /* bytes_read */
244 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
245 g_return_val_if_fail(bytes_read<=(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top),G_IO_STATUS_ERROR);
246 g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
247 if (bytes_read==window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top)
248 g_return_val_if_fail(transfer_top+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
250 g_return_val_if_fail(transfer_top+bytes_read==giochannel_blind->size,G_IO_STATUS_ERROR); /* EOF hit */
254 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)",
256 (guint64)window_now,(gulong)(transfer_bottom-window_now),(gulong)(transfer_bottom-giochannel_blind->offset),
257 (gulong)(transfer_top-transfer_bottom));
259 ((char *)data)+transfer_bottom-window_now, /* dest */
260 buf+transfer_bottom-giochannel_blind->offset, /* src */
261 transfer_top-transfer_bottom); /* n */
264 *bytes_written=count;
265 giochannel_blind->offset+=(*bytes_written);
266 return G_IO_STATUS_NORMAL;
270 static GIOStatus captive_giochannel_blind_io_seek(GIOChannel *channel,gint64 offset,GSeekType type,GError **err)
272 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
274 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
277 case G_SEEK_CUR: giochannel_blind->offset+= offset; break;
278 case G_SEEK_SET: giochannel_blind->offset = offset; break;
279 case G_SEEK_END: giochannel_blind->offset =giochannel_blind->size+offset; break;
280 default: g_return_val_if_reached(G_IO_STATUS_ERROR);
282 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR); /* 'offset' overflow? */
284 return G_IO_STATUS_NORMAL;
288 static GIOStatus captive_giochannel_blind_io_close(GIOChannel *channel,GError **err)
290 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
291 GIOStatus erriostatus;
293 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
295 /* We are not authorized to destroy 'giochannel_blind->giochannel_ro'. */
296 erriostatus=g_io_channel_flush(
297 giochannel_blind->giochannel_ro, /* channel */
299 g_assert(erriostatus==G_IO_STATUS_NORMAL);
300 giochannel_blind->giochannel_ro=NULL;
302 g_hash_table_destroy(giochannel_blind->buffer_hash);
303 giochannel_blind->buffer_hash=NULL;
305 return G_IO_STATUS_NORMAL;
309 static GSource* captive_giochannel_blind_io_create_watch(GIOChannel *channel,GIOCondition condition)
311 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
313 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),NULL);
315 g_return_val_if_reached(NULL); /* FIXME: NOT IMPLEMENTED YET */
319 static void captive_giochannel_blind_io_free(GIOChannel *channel)
321 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
323 /* After captive_giochannel_blind_io_close() 'giochannel_blind'
324 * may be no longer valid for validate_giochannel_blind(giochannel_blind).
326 g_return_if_fail(giochannel_blind!=NULL);
328 g_assert(giochannel_blind->giochannel_ro==NULL);
329 g_assert(giochannel_blind->buffer_hash==NULL);
331 g_free(giochannel_blind);
335 static GIOStatus captive_giochannel_blind_io_set_flags(GIOChannel *channel,GIOFlags flags,GError **err)
337 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
339 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
341 return g_io_channel_set_flags(giochannel_blind->giochannel_ro,(flags&~G_IO_FLAG_IS_WRITEABLE),err);
345 static GIOFlags captive_giochannel_blind_io_get_flags(GIOChannel *channel)
347 struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
349 g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),0);
351 return g_io_channel_get_flags(giochannel_blind->giochannel_ro) | G_IO_FLAG_IS_WRITEABLE;
355 struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_ro)
357 struct captive_giochannel_blind *giochannel_blind;
358 GIOStatus erriostatus;
360 g_return_val_if_fail(giochannel_ro!=NULL,NULL);
362 G_LOCK(giochannel_blind_funcs);
363 giochannel_blind_funcs.io_read =captive_giochannel_blind_io_read;
364 giochannel_blind_funcs.io_write =captive_giochannel_blind_io_write;
365 giochannel_blind_funcs.io_seek =captive_giochannel_blind_io_seek;
366 giochannel_blind_funcs.io_close =captive_giochannel_blind_io_close;
367 giochannel_blind_funcs.io_create_watch=captive_giochannel_blind_io_create_watch;
368 giochannel_blind_funcs.io_free =captive_giochannel_blind_io_free;
369 giochannel_blind_funcs.io_set_flags =captive_giochannel_blind_io_set_flags;
370 giochannel_blind_funcs.io_get_flags =captive_giochannel_blind_io_get_flags;
371 G_UNLOCK(giochannel_blind_funcs);
373 erriostatus=g_io_channel_set_encoding(giochannel_ro,
374 NULL, /* encoding; force binary data */
376 g_assert(erriostatus==G_IO_STATUS_NORMAL);
378 captive_new(giochannel_blind);
379 g_assert(G_STRUCT_OFFSET(struct captive_giochannel_blind,iochannel)==0); /* safely re-type-able */
380 g_io_channel_init(&giochannel_blind->iochannel);
381 giochannel_blind->iochannel.funcs=&giochannel_blind_funcs;
382 giochannel_blind->iochannel.is_seekable=TRUE;
383 giochannel_blind->iochannel.is_readable=TRUE;
384 giochannel_blind->iochannel.is_writeable=TRUE;
385 giochannel_blind->iochannel.close_on_unref=TRUE; /* run g_io_channel_shutdown() flush on last unref */
386 giochannel_blind->giochannel_ro=giochannel_ro;
387 giochannel_blind->offset=0;
388 giochannel_blind->size=captive_giochannel_size(giochannel_ro);
389 giochannel_blind->buffer_hash=g_hash_table_new_full(
390 (GHashFunc)captive_giochannel_blind_hash_func, /* hash_func */
391 (GEqualFunc)captive_giochannel_blind_equal_func, /* key_equal_func */
392 (GDestroyNotify)captive_giochannel_blind_key_destroy_func, /* key_destroy_func */
393 (GDestroyNotify)captive_giochannel_blind_value_destroy_func); /* value_destroy_func */
395 return giochannel_blind;