Initial original import from: fuse-2.4.2-2.fc4
[captive.git] / src / libcaptive / client / giochannel-subrange.c
1 /* $Id$
2  * glib GIOChannel mapping subrange of parent 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-subrange.h"
23 #include <glib/gmessages.h>
24 #include "captive/macros.h"
25 #include "captive/storage.h"
26 #include <ctype.h>
27 #include "lib.h"        /* for captive_giochannel_setup() */
28
29
30 /* FIXME: fill 'err' */
31
32 struct captive_giochannel_subrange {
33         GIOChannel iochannel;
34         GIOChannel *giochannel_orig;    /* reffed by us */
35         guint64 offset; /* gint64 range; read start+offset from 'giochannel_orig' */
36         guint64 start,end;
37         };
38
39
40 G_LOCK_DEFINE_STATIC(giochannel_subrange_funcs);
41 static GIOFuncs giochannel_subrange_funcs;
42
43
44 static gboolean validate_giochannel_subrange(struct captive_giochannel_subrange *giochannel_subrange)
45 {
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);
52
53         return TRUE;
54 }
55
56
57 static GIOStatus captive_giochannel_subrange_io_read
58                 (GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err)
59 {
60 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
61 GIOStatus errgiostatus;
62
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);
66
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);
69
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 */
74                         err);   /* error */
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.
80          */
81         if (errgiostatus!=G_IO_STATUS_NORMAL) {
82                 errgiostatus=G_IO_STATUS_EOF;
83                 *bytes_read=0;
84                 }
85         else {
86                 errgiostatus=g_io_channel_read_chars(
87                                 giochannel_subrange->giochannel_orig,   /* channel */
88                                 buf,    /* buf */
89                                 count,  /* count */
90                                 bytes_read,     /* bytes_read */
91                                 err);   /* error */
92                 }
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);
96
97         giochannel_subrange->offset+=*bytes_read;
98         return (*bytes_read == 0 ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL);
99 }
100
101
102 static GIOStatus captive_giochannel_subrange_io_write
103                 (GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err)
104 {
105 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
106 GIOStatus errgiostatus;
107
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);
111
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);
114
115         g_return_val_if_fail(giochannel_subrange->start+giochannel_subrange->offset+count<=giochannel_subrange->end,
116                         G_IO_STATUS_ERROR);
117
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 */
122                         err);   /* error */
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 */
126                         buf,    /* buf */
127                         count,  /* count */
128                         bytes_written,  /* bytes_written */
129                         err);   /* error */
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);
132
133         giochannel_subrange->offset+=(*bytes_written);
134         return G_IO_STATUS_NORMAL;
135 }
136
137
138 static GIOStatus captive_giochannel_subrange_io_seek(GIOChannel *channel,gint64 offset,GSeekType type,GError **err)
139 {
140 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
141
142         g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);
143
144         switch (type) {
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);
149                 }
150         g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);      /* 'offset' overflow? */
151
152         return G_IO_STATUS_NORMAL;
153 }
154
155
156 static GIOStatus captive_giochannel_subrange_io_close(GIOChannel *channel,GError **err)
157 {
158 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
159 GIOStatus erriostatus;
160
161         g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);
162
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 */
167                                 NULL);  /* error */
168                 g_assert(erriostatus==G_IO_STATUS_NORMAL);
169
170                 g_io_channel_unref(giochannel_subrange->giochannel_orig);
171                 giochannel_subrange->giochannel_orig=NULL;
172                 }
173
174         return G_IO_STATUS_NORMAL;
175 }
176
177
178 static GSource* captive_giochannel_subrange_io_create_watch(GIOChannel *channel,GIOCondition condition)
179 {
180 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
181
182         g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),NULL);
183
184         g_return_val_if_reached(NULL);  /* FIXME: NOT IMPLEMENTED YET */
185 }
186
187
188 static void captive_giochannel_subrange_io_free(GIOChannel *channel)
189 {
190 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
191
192         /* After captive_giochannel_subrange_io_close() 'giochannel_subrange'
193          * may be no longer valid for validate_giochannel_subrange(giochannel_subrange).
194          */
195         g_return_if_fail(giochannel_subrange!=NULL);
196
197         g_assert(giochannel_subrange->giochannel_orig==NULL);
198
199         g_free(giochannel_subrange);
200 }
201
202
203 static GIOStatus captive_giochannel_subrange_io_set_flags(GIOChannel *channel,GIOFlags flags,GError **err)
204 {
205 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
206
207         g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR);
208
209         return g_io_channel_set_flags(giochannel_subrange->giochannel_orig,flags,err);
210 }
211
212
213 static GIOFlags captive_giochannel_subrange_io_get_flags(GIOChannel *channel)
214 {
215 struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel;
216
217         g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),0);
218
219         return g_io_channel_get_flags(giochannel_subrange->giochannel_orig);
220 }
221
222
223 struct captive_giochannel_subrange *captive_giochannel_subrange_new(GIOChannel *giochannel_orig,guint64 start,guint64 end)
224 {
225 struct captive_giochannel_subrange *giochannel_subrange;
226
227         g_return_val_if_fail(giochannel_orig!=NULL,NULL);
228         g_return_val_if_fail(start<=end,NULL);
229
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);
240
241         captive_giochannel_setup(giochannel_orig);
242
243         g_io_channel_ref(giochannel_orig);
244
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;
257
258         captive_giochannel_setup(&giochannel_subrange->iochannel);
259
260         return giochannel_subrange;
261 }
262
263
264 gboolean captive_giochannel_subrange_get_size(GIOChannel *giochannel,guint64 *size_return)
265 {
266 struct captive_giochannel_subrange *giochannel_subrange;
267
268         g_return_val_if_fail(giochannel!=NULL,FALSE);
269         g_return_val_if_fail(size_return!=NULL,FALSE);
270
271         if (giochannel->funcs!=&giochannel_subrange_funcs)
272                 return FALSE;
273         giochannel_subrange=(struct captive_giochannel_subrange *)giochannel;
274
275         *size_return=giochannel_subrange->end-giochannel_subrange->start;
276         return TRUE;
277 }