/* $Id$ * glib GIOChannel mapping subrange of parent GIOChannel for libcaptive * Copyright (C) 2003 Jan Kratochvil * * 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-subrange.h" #include #include "captive/macros.h" #include "captive/storage.h" #include #include "lib.h" /* for captive_giochannel_setup() */ /* FIXME: fill 'err' */ struct captive_giochannel_subrange { GIOChannel iochannel; GIOChannel *giochannel_orig; /* reffed by us */ guint64 offset; /* gint64 range; read start+offset from 'giochannel_orig' */ guint64 start,end; }; G_LOCK_DEFINE_STATIC(giochannel_subrange_funcs); static GIOFuncs giochannel_subrange_funcs; static gboolean validate_giochannel_subrange(struct captive_giochannel_subrange *giochannel_subrange) { g_return_val_if_fail(giochannel_subrange->iochannel.funcs==&giochannel_subrange_funcs,FALSE); g_return_val_if_fail(giochannel_subrange!=NULL,FALSE); g_return_val_if_fail(giochannel_subrange->giochannel_orig!=NULL,FALSE); g_return_val_if_fail((gint64)giochannel_subrange->offset>=0,FALSE); /* gint64 overflow stored in guint64 */ g_return_val_if_fail(giochannel_subrange->start<=giochannel_subrange->end,FALSE); g_return_val_if_fail(giochannel_subrange->offset<=(giochannel_subrange->end-giochannel_subrange->start),FALSE); return TRUE; } static GIOStatus captive_giochannel_subrange_io_read (GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err) { struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel; GIOStatus errgiostatus; g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),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_subrange->offset,(gulong)count); errgiostatus=g_io_channel_seek_position( giochannel_subrange->giochannel_orig, /* channel */ giochannel_subrange->start+giochannel_subrange->offset, /* offset */ G_SEEK_SET, /* type */ err); /* error */ /* During seek in block device such as on URL file:///dev/hda1#captive-fastfat.sys-ro:/ * we will do llseek(2) on "/dev/hda1" device from captive_giochannel_size(). * Although we are allowed to seek behind EOF on regular files * at least linux-kernel-2.4.19-ac4/fs/block_dev.c/block_llseek() will give * EINVAL on seek behind EOF therefore it must be accepted without complaints by us. */ if (errgiostatus!=G_IO_STATUS_NORMAL) { errgiostatus=G_IO_STATUS_EOF; *bytes_read=0; } else { errgiostatus=g_io_channel_read_chars( giochannel_subrange->giochannel_orig, /* channel */ buf, /* buf */ count, /* 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<=count,G_IO_STATUS_ERROR); g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR); giochannel_subrange->offset+=*bytes_read; return (*bytes_read == 0 ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL); } static GIOStatus captive_giochannel_subrange_io_write (GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err) { struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel; GIOStatus errgiostatus; g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),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_subrange->offset,(gulong)count); g_return_val_if_fail(giochannel_subrange->start+giochannel_subrange->offset+count<=giochannel_subrange->end, G_IO_STATUS_ERROR); errgiostatus=g_io_channel_seek_position( giochannel_subrange->giochannel_orig, /* channel */ giochannel_subrange->start+giochannel_subrange->offset, /* offset */ G_SEEK_SET, /* type */ err); /* error */ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus); errgiostatus=g_io_channel_write_chars( giochannel_subrange->giochannel_orig, /* channel */ buf, /* buf */ count, /* count */ bytes_written, /* bytes_written */ err); /* error */ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus); g_return_val_if_fail(*bytes_written==count,G_IO_STATUS_ERROR); giochannel_subrange->offset+=(*bytes_written); return G_IO_STATUS_NORMAL; } static GIOStatus captive_giochannel_subrange_io_seek(GIOChannel *channel,gint64 offset,GSeekType type,GError **err) { struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel; g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR); switch (type) { case G_SEEK_CUR: giochannel_subrange->offset+=offset; break; case G_SEEK_SET: giochannel_subrange->offset =offset; break; case G_SEEK_END: giochannel_subrange->offset =(giochannel_subrange->end-giochannel_subrange->start)+offset; break; default: g_return_val_if_reached(G_IO_STATUS_ERROR); } g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR); /* 'offset' overflow? */ return G_IO_STATUS_NORMAL; } static GIOStatus captive_giochannel_subrange_io_close(GIOChannel *channel,GError **err) { struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel; GIOStatus erriostatus; g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR); if (giochannel_subrange->giochannel_orig) { /* Just a sanity if 'giochannel_orig' is already falsely reffed a bit more... */ erriostatus=g_io_channel_flush( giochannel_subrange->giochannel_orig, /* channel */ NULL); /* error */ g_assert(erriostatus==G_IO_STATUS_NORMAL); g_io_channel_unref(giochannel_subrange->giochannel_orig); giochannel_subrange->giochannel_orig=NULL; } return G_IO_STATUS_NORMAL; } static GSource* captive_giochannel_subrange_io_create_watch(GIOChannel *channel,GIOCondition condition) { struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel; g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),NULL); g_return_val_if_reached(NULL); /* FIXME: NOT IMPLEMENTED YET */ } static void captive_giochannel_subrange_io_free(GIOChannel *channel) { struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel; /* After captive_giochannel_subrange_io_close() 'giochannel_subrange' * may be no longer valid for validate_giochannel_subrange(giochannel_subrange). */ g_return_if_fail(giochannel_subrange!=NULL); g_assert(giochannel_subrange->giochannel_orig==NULL); g_free(giochannel_subrange); } static GIOStatus captive_giochannel_subrange_io_set_flags(GIOChannel *channel,GIOFlags flags,GError **err) { struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel; g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),G_IO_STATUS_ERROR); return g_io_channel_set_flags(giochannel_subrange->giochannel_orig,flags,err); } static GIOFlags captive_giochannel_subrange_io_get_flags(GIOChannel *channel) { struct captive_giochannel_subrange *giochannel_subrange=(struct captive_giochannel_subrange *)channel; g_return_val_if_fail(validate_giochannel_subrange(giochannel_subrange),0); return g_io_channel_get_flags(giochannel_subrange->giochannel_orig); } struct captive_giochannel_subrange *captive_giochannel_subrange_new(GIOChannel *giochannel_orig,guint64 start,guint64 end) { struct captive_giochannel_subrange *giochannel_subrange; g_return_val_if_fail(giochannel_orig!=NULL,NULL); g_return_val_if_fail(start<=end,NULL); G_LOCK(giochannel_subrange_funcs); giochannel_subrange_funcs.io_read =captive_giochannel_subrange_io_read; giochannel_subrange_funcs.io_write =captive_giochannel_subrange_io_write; giochannel_subrange_funcs.io_seek =captive_giochannel_subrange_io_seek; giochannel_subrange_funcs.io_close =captive_giochannel_subrange_io_close; giochannel_subrange_funcs.io_create_watch=captive_giochannel_subrange_io_create_watch; giochannel_subrange_funcs.io_free =captive_giochannel_subrange_io_free; giochannel_subrange_funcs.io_set_flags =captive_giochannel_subrange_io_set_flags; giochannel_subrange_funcs.io_get_flags =captive_giochannel_subrange_io_get_flags; G_UNLOCK(giochannel_subrange_funcs); captive_giochannel_setup(giochannel_orig); g_io_channel_ref(giochannel_orig); captive_new(giochannel_subrange); g_assert(G_STRUCT_OFFSET(struct captive_giochannel_subrange,iochannel)==0); /* safely re-type-able */ g_io_channel_init(&giochannel_subrange->iochannel); giochannel_subrange->iochannel.funcs=&giochannel_subrange_funcs; giochannel_subrange->iochannel.is_seekable =!!(g_io_channel_get_flags(giochannel_orig) & G_IO_FLAG_IS_SEEKABLE); giochannel_subrange->iochannel.is_readable =!!(g_io_channel_get_flags(giochannel_orig) & G_IO_FLAG_IS_READABLE); giochannel_subrange->iochannel.is_writeable=!!(g_io_channel_get_flags(giochannel_orig) & G_IO_FLAG_IS_WRITEABLE); giochannel_subrange->iochannel.close_on_unref=TRUE; /* run g_io_channel_shutdown() flush on last unref */ giochannel_subrange->giochannel_orig=giochannel_orig; giochannel_subrange->offset=0; giochannel_subrange->start=start; giochannel_subrange->end=end; captive_giochannel_setup(&giochannel_subrange->iochannel); return giochannel_subrange; } gboolean captive_giochannel_subrange_get_size(GIOChannel *giochannel,guint64 *size_return) { struct captive_giochannel_subrange *giochannel_subrange; g_return_val_if_fail(giochannel!=NULL,FALSE); g_return_val_if_fail(size_return!=NULL,FALSE); if (giochannel->funcs!=&giochannel_subrange_funcs) return FALSE; giochannel_subrange=(struct captive_giochannel_subrange *)giochannel; *size_return=giochannel_subrange->end-giochannel_subrange->start; return TRUE; }