2 * glib GIOChannel mapping subrange of parent 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-subrange.h"
23 #include <glib/gmessages.h>
24 #include "captive/macros.h"
25 #include "captive/storage.h"
27 #include "lib.h" /* for captive_giochannel_setup() */
30 /* FIXME: fill 'err' */
32 struct captive_giochannel_subrange {
34 GIOChannel *giochannel_orig; /* reffed by us */
35 guint64 offset; /* gint64 range; read start+offset from 'giochannel_orig' */
40 G_LOCK_DEFINE_STATIC(giochannel_subrange_funcs);
41 static GIOFuncs giochannel_subrange_funcs;
44 static gboolean validate_giochannel_subrange(struct captive_giochannel_subrange *giochannel_subrange)
46 g_return_val_if_fail(giochannel_subrange->iochannel.funcs==&giochannel_subrange_funcs,FALSE);
47 g_return_val_if_fail(giochannel_subrange!=NULL,FALSE);
48 g_return_val_if_fail(giochannel_subrange->giochannel_orig!=NULL,FALSE);
49 g_return_val_if_fail((gint64)giochannel_subrange->offset>=0,FALSE); /* gint64 overflow stored in guint64 */
50 g_return_val_if_fail(giochannel_subrange->start<=giochannel_subrange->end,FALSE);
51 g_return_val_if_fail(giochannel_subrange->offset<=(giochannel_subrange->end-giochannel_subrange->start),FALSE);
57 static GIOStatus captive_giochannel_subrange_io_read
58 (GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err)
60 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
61 GIOStatus errgiostatus;
63 g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);
64 g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
65 g_return_val_if_fail(bytes_read!=NULL,G_IO_STATUS_ERROR);
67 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read(offset=0x%llX,count=0x%lX)",G_STRLOC,
68 giochannel_subrange->offset,(gulong)count);
70 errgiostatus=g_io_channel_seek_position(
71 giochannel_subrange->giochannel_orig, /* channel */
72 giochannel_subrange->start+giochannel_subrange->offset, /* offset */
73 G_SEEK_SET, /* type */
75 /* During seek in block device such as on URL file:///dev/hda1#captive-fastfat.sys-ro:/
76 * we will do llseek(2) on "/dev/hda1" device from captive_giochannel_size().
77 * Although we are allowed to seek behind EOF on regular files
78 * at least linux-kernel-2.4.19-ac4/fs/block_dev.c/block_llseek() will give
79 * EINVAL on seek behind EOF therefore it must be accepted without complaints by us.
81 if (errgiostatus!=G_IO_STATUS_NORMAL) {
82 errgiostatus=G_IO_STATUS_EOF;
86 errgiostatus=g_io_channel_read_chars(
87 giochannel_subrange->giochannel_orig, /* channel */
90 bytes_read, /* bytes_read */
93 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
94 g_return_val_if_fail(*bytes_read<=count,G_IO_STATUS_ERROR);
95 g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
97 giochannel_subrange->offset+=*bytes_read;
98 return (*bytes_read == 0 ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL);
102 static GIOStatus captive_giochannel_subrange_io_write
103 (GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err)
105 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
106 GIOStatus errgiostatus;
108 g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);
109 g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
110 g_return_val_if_fail(bytes_written!=NULL,G_IO_STATUS_ERROR);
112 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write(offset=0x%llX,count=0x%lX)",G_STRLOC,
113 giochannel_subrange->offset,(gulong)count);
115 g_return_val_if_fail(giochannel_subrange->start+giochannel_subrange->offset+count<=giochannel_subrange->end,
118 errgiostatus=g_io_channel_seek_position(
119 giochannel_subrange->giochannel_orig, /* channel */
120 giochannel_subrange->start+giochannel_subrange->offset, /* offset */
121 G_SEEK_SET, /* type */
123 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
124 errgiostatus=g_io_channel_write_chars(
125 giochannel_subrange->giochannel_orig, /* channel */
128 bytes_written, /* bytes_written */
130 g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
131 g_return_val_if_fail(*bytes_written==count,G_IO_STATUS_ERROR);
133 giochannel_subrange->offset+=(*bytes_written);
134 return G_IO_STATUS_NORMAL;
138 static GIOStatus captive_giochannel_subrange_io_seek(GIOChannel *channel,gint64 offset,GSeekType type,GError **err)
140 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
142 g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);
145 case G_SEEK_CUR: giochannel_subrange->offset+=offset; break;
146 case G_SEEK_SET: giochannel_subrange->offset =offset; break;
147 case G_SEEK_END: giochannel_subrange->offset =(giochannel_subrange->end-giochannel_subrange->start)+offset; break;
148 default: g_return_val_if_reached(G_IO_STATUS_ERROR);
150 g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR); /* 'offset' overflow? */
152 return G_IO_STATUS_NORMAL;
156 static GIOStatus captive_giochannel_subrange_io_close(GIOChannel *channel,GError **err)
158 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
159 GIOStatus erriostatus;
161 g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);
163 if (giochannel_subrange->giochannel_orig) {
164 /* Just a sanity if 'giochannel_orig' is already falsely reffed a bit more... */
165 erriostatus=g_io_channel_flush(
166 giochannel_subrange->giochannel_orig, /* channel */
168 g_assert(erriostatus==G_IO_STATUS_NORMAL);
170 g_io_channel_unref(giochannel_subrange->giochannel_orig);
171 giochannel_subrange->giochannel_orig=NULL;
174 return G_IO_STATUS_NORMAL;
178 static GSource* captive_giochannel_subrange_io_create_watch(GIOChannel *channel,GIOCondition condition)
180 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
182 g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),NULL);
184 g_return_val_if_reached(NULL); /* FIXME: NOT IMPLEMENTED YET */
188 static void captive_giochannel_subrange_io_free(GIOChannel *channel)
190 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
192 /* After captive_giochannel_subrange_io_close() 'giochannel_subrange'
193 * may be no longer valid for validate_giochannel_subrange(giochannel_subrange).
195 g_return_if_fail(giochannel_subrange!=NULL);
197 g_assert(giochannel_subrange->giochannel_orig==NULL);
199 g_free(giochannel_subrange);
203 static GIOStatus captive_giochannel_subrange_io_set_flags(GIOChannel *channel,GIOFlags flags,GError **err)
205 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
207 g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);
209 return g_io_channel_set_flags(giochannel_subrange->giochannel_orig,flags,err);
213 static GIOFlags captive_giochannel_subrange_io_get_flags(GIOChannel *channel)
215 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
217 g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),0);
219 return g_io_channel_get_flags(giochannel_subrange->giochannel_orig);
223 struct captive_giochannel_subrange *captive_giochannel_subrange_new(GIOChannel *giochannel_orig,guint64 start,guint64 end)
225 struct captive_giochannel_subrange *giochannel_subrange;
227 g_return_val_if_fail(giochannel_orig!=NULL,NULL);
228 g_return_val_if_fail(start<=end,NULL);
230 G_LOCK(giochannel_subrange_funcs);
231 giochannel_subrange_funcs.io_read =captive_giochannel_subrange_io_read;
232 giochannel_subrange_funcs.io_write =captive_giochannel_subrange_io_write;
233 giochannel_subrange_funcs.io_seek =captive_giochannel_subrange_io_seek;
234 giochannel_subrange_funcs.io_close =captive_giochannel_subrange_io_close;
235 giochannel_subrange_funcs.io_create_watch=captive_giochannel_subrange_io_create_watch;
236 giochannel_subrange_funcs.io_free =captive_giochannel_subrange_io_free;
237 giochannel_subrange_funcs.io_set_flags =captive_giochannel_subrange_io_set_flags;
238 giochannel_subrange_funcs.io_get_flags =captive_giochannel_subrange_io_get_flags;
239 G_UNLOCK(giochannel_subrange_funcs);
241 captive_giochannel_setup(giochannel_orig);
243 g_io_channel_ref(giochannel_orig);
245 captive_new(giochannel_subrange);
246 g_assert(G_STRUCT_OFFSET(struct captive_giochannel_subrange,iochannel)==0); /* safely re-type-able */
247 g_io_channel_init(&giochannel_subrange->iochannel);
248 giochannel_subrange->iochannel.funcs=&giochannel_subrange_funcs;
249 giochannel_subrange->iochannel.is_seekable =!!(g_io_channel_get_flags(giochannel_orig) & G_IO_FLAG_IS_SEEKABLE);
250 giochannel_subrange->iochannel.is_readable =!!(g_io_channel_get_flags(giochannel_orig) & G_IO_FLAG_IS_READABLE);
251 giochannel_subrange->iochannel.is_writeable=!!(g_io_channel_get_flags(giochannel_orig) & G_IO_FLAG_IS_WRITEABLE);
252 giochannel_subrange->iochannel.close_on_unref=TRUE; /* run g_io_channel_shutdown() flush on last unref */
253 giochannel_subrange->giochannel_orig=giochannel_orig;
254 giochannel_subrange->offset=0;
255 giochannel_subrange->start=start;
256 giochannel_subrange->end=end;
258 captive_giochannel_setup(&giochannel_subrange->iochannel);
260 return giochannel_subrange;
264 gboolean captive_giochannel_subrange_get_size(GIOChannel *giochannel,guint64 *size_return)
266 struct captive_giochannel_subrange *giochannel_subrange;
268 g_return_val_if_fail(giochannel!=NULL,FALSE);
269 g_return_val_if_fail(size_return!=NULL,FALSE);
271 if (giochannel->funcs!=&giochannel_subrange_funcs)
273 giochannel_subrange=(struct captive_giochannel_subrange *)giochannel;
275 *size_return=giochannel_subrange->end-giochannel_subrange->start;