2 * Detect media size of given GIOChannel for libcaptive
3 * Copyright (C) 2002 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 "captive/storage.h" /* self */
23 #include "../client/giochannel-blind.h" /* for captive_giochannel_blind_get_size() */
24 #include "../sandbox/client-CaptiveIOChannel.h" /* for captive_io_channel_get_size() */
25 #include <glib/gmessages.h>
26 #include <glib/gtypes.h>
28 #include <sys/ioctl.h>
30 #include <linux/types.h> /* for __u64 for u64 for BLKGETSIZE64 */
32 #include <linux/fs.h> /* for BLKGETSIZE64 */
35 static guint64 size_blind(GIOChannel *iochannel)
39 g_return_val_if_fail(iochannel!=NULL,0);
41 if (!captive_giochannel_blind_get_size(iochannel,&r))
48 static guint64 size_sandbox(GIOChannel *iochannel)
52 g_return_val_if_fail(iochannel!=NULL,0);
54 if (!captive_io_channel_get_size(iochannel,&r))
61 static GIOChannel *iochannel_null;
63 static int iounixchannel_get_fd(GIOChannel *iochannel)
67 g_return_val_if_fail(iochannel!=NULL,-1);
69 if (!iochannel_null) {
72 fd=open("/dev/null",O_RDONLY);
73 g_return_val_if_fail(fd!=-1,-1);
74 iochannel_null=g_io_channel_unix_new(fd);
75 g_return_val_if_fail(iochannel_null!=NULL,-1);
78 if (iochannel->funcs!=iochannel_null->funcs) {
79 /* Not a UNIX file descriptor */
83 /* It is forbidden to callg_io_channel_unix_get_fd()
84 * if you are not sure it is a 'GIOUnixChannel'.
86 r=g_io_channel_unix_get_fd(iochannel);
87 g_return_val_if_fail(r!=-1,-1);
93 static guint64 size_ioctl(GIOChannel *iochannel)
101 g_return_val_if_fail(iochannel!=NULL,0);
103 if (-1==(fd=iounixchannel_get_fd(iochannel)))
107 if (ioctl(fd,BLKGETSIZE64,&r))
110 if (ioctl(fd,BLKGETSIZE,&r_long))
114 r=((guint64)512)*r_long;
121 static guint64 size_seek(GIOChannel *iochannel)
124 off_t offset_orig,offset;
126 g_return_val_if_fail(iochannel!=NULL,0);
128 /* We may need '_FILE_OFFSET_BITS=64'.
129 * Setting '__USE_FILE_OFFSET64' did not help.
130 * Done by 'AC_SYS_LARGEFILE' of configure.in.
132 g_return_val_if_fail(sizeof(offset)==sizeof(guint64),0);
134 if (-1==(fd=iounixchannel_get_fd(iochannel)))
137 if (-1==(offset_orig=lseek(fd,0,SEEK_CUR)))
139 g_return_val_if_fail(offset_orig>=0,0);
140 offset=lseek(fd,0,SEEK_END);
141 if (offset_orig!=lseek(fd,offset_orig,SEEK_SET))
142 g_assert_not_reached();
146 g_return_val_if_fail(offset>=0,0);
153 static guint64 size_read(GIOChannel *iochannel)
156 GIOStatus erriostatus;
160 g_return_val_if_fail(iochannel!=NULL,0);
161 /* Default "UTF-8" encoding is not much usable for us */
162 g_return_val_if_fail(g_io_channel_get_encoding(iochannel)==NULL,0);
164 /* low ==high: low (high)
165 * low+1==high: mid==low; NORMAL: no change: high
166 * low+1==high: mid==low; EOF : high=mid => 'low==high' case
172 mid=low+(high-low)/2; /* beware of 'gint64' overflow! */
174 erriostatus=g_io_channel_seek_position(iochannel,mid,G_SEEK_SET,
176 /* During seek in block device such as on URL file:///dev/hda1#captive-fastfat.sys-ro:/
177 * we will do llseek(2) on "/dev/hda1" device from captive_giochannel_size().
178 * Although we are allowed to seek behind EOF on regular files
179 * at least linux-kernel-2.4.19-ac4/fs/block_dev.c/block_llseek() will give
180 * EINVAL on seek behind EOF therefore it must be accepted without complaints by us.
182 if (erriostatus!=G_IO_STATUS_NORMAL) {
183 erriostatus=G_IO_STATUS_EOF;
187 g_assert(sizeof(bufchar)==1);
188 erriostatus=g_io_channel_read_chars(iochannel,
190 sizeof(bufchar), /* count */
191 &bufchargot, /* bytes_read */
193 /* During read on the end boundary of Linux kernel block device we will
194 * get GNOME_VFS_ERROR_IO at least from linux-kernel-2.4.19-ac4
195 * which will get mapped to G_IO_STATUS_ERROR by captive_gnomevfs_giognomevfs_io_read().
198 || erriostatus==G_IO_STATUS_NORMAL
199 || erriostatus==G_IO_STATUS_EOF
200 || erriostatus==G_IO_STATUS_ERROR);
202 || (erriostatus==G_IO_STATUS_NORMAL && bufchargot==1)
203 || (erriostatus==G_IO_STATUS_EOF && bufchargot==0)
204 || (erriostatus==G_IO_STATUS_ERROR && bufchargot==0));
207 if (erriostatus==G_IO_STATUS_NORMAL)
218 guint64 captive_giochannel_size(GIOChannel *iochannel)
222 if ((r=size_blind(iochannel)))
224 if ((r=size_sandbox(iochannel)))
226 if ((r=size_ioctl(iochannel)))
228 if ((r=size_seek(iochannel)))
230 if ((r=size_read(iochannel)))