#include "config.h"
#include "captive/storage.h" /* self */
+#include "../client/giochannel-blind.h" /* for captive_giochannel_blind_get_size() */
+#include "../sandbox/client-CaptiveIOChannel.h" /* for captive_io_channel_get_size() */
#include <glib/gmessages.h>
#include <glib/gtypes.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <linux/types.h> /* for __u64 for u64 for BLKGETSIZE64 */
+#define u64 __u64
+#include <linux/fs.h> /* for BLKGETSIZE64 */
-guint64 captive_giochannel_size(GIOChannel *iochannel)
+static guint64 size_blind(GIOChannel *iochannel)
+{
+guint64 r;
+
+ g_return_val_if_fail(iochannel!=NULL,0);
+
+ if (!captive_giochannel_blind_get_size(iochannel,&r))
+ return 0;
+
+ return r;
+}
+
+
+static guint64 size_sandbox(GIOChannel *iochannel)
+{
+guint64 r;
+
+ g_return_val_if_fail(iochannel!=NULL,0);
+
+ if (!captive_io_channel_get_size(iochannel,&r))
+ return 0;
+
+ return r;
+}
+
+
+static GIOChannel *iochannel_null;
+
+static int iounixchannel_get_fd(GIOChannel *iochannel)
+{
+int r;
+
+ g_return_val_if_fail(iochannel!=NULL,-1);
+
+ if (!iochannel_null) {
+int fd;
+
+ fd=open("/dev/null",O_RDONLY);
+ g_return_val_if_fail(fd!=-1,-1);
+ iochannel_null=g_io_channel_unix_new(fd);
+ g_return_val_if_fail(iochannel_null!=NULL,-1);
+ }
+
+ if (iochannel->funcs!=iochannel_null->funcs) {
+ /* Not a UNIX file descriptor */
+ return -1;
+ }
+
+ /* It is forbidden to callg_io_channel_unix_get_fd()
+ * if you are not sure it is a 'GIOUnixChannel'.
+ */
+ r=g_io_channel_unix_get_fd(iochannel);
+ g_return_val_if_fail(r!=-1,-1);
+
+ return r;
+}
+
+
+static guint64 size_ioctl(GIOChannel *iochannel)
+{
+int fd;
+guint64 r;
+#ifndef BLKGETSIZE64
+long r_long;
+#endif
+
+ g_return_val_if_fail(iochannel!=NULL,0);
+
+ if (-1==(fd=iounixchannel_get_fd(iochannel)))
+ return 0;
+
+#ifdef BLKGETSIZE64
+ if (ioctl(fd,BLKGETSIZE64,&r))
+ return 0;
+#else
+ if (ioctl(fd,BLKGETSIZE,&r_long))
+ return 0;
+ if (r_long<0)
+ return 0;
+ r=((guint64)512)*r_long;
+#endif
+
+ return r;
+}
+
+
+static guint64 size_seek(GIOChannel *iochannel)
+{
+int fd;
+off_t offset_orig,offset;
+
+ g_return_val_if_fail(iochannel!=NULL,0);
+
+ /* We may need '_FILE_OFFSET_BITS=64'.
+ * Setting '__USE_FILE_OFFSET64' did not help.
+ * Done by 'AC_SYS_LARGEFILE' of configure.in.
+ */
+ g_return_val_if_fail(sizeof(offset)==sizeof(guint64),0);
+
+ if (-1==(fd=iounixchannel_get_fd(iochannel)))
+ return 0;
+
+ if (-1==(offset_orig=lseek(fd,0,SEEK_CUR)))
+ return 0;
+ g_return_val_if_fail(offset_orig>=0,0);
+ offset=lseek(fd,0,SEEK_END);
+ if (offset_orig!=lseek(fd,offset_orig,SEEK_SET))
+ g_assert_not_reached();
+
+ if (-1==offset)
+ return 0;
+ g_return_val_if_fail(offset>=0,0);
+
+ return offset;
+}
+
+
+
+static guint64 size_read(GIOChannel *iochannel)
{
-gint fd;
gint64 low,high,mid;
GIOStatus erriostatus;
gchar bufchar;
/* Default "UTF-8" encoding is not much usable for us */
g_return_val_if_fail(g_io_channel_get_encoding(iochannel)==NULL,0);
- fd=g_io_channel_unix_get_fd(iochannel);
- g_return_val_if_fail(fd!=-1,0);
-
- /* FIXME: TODO: ioctl() detection */
-
/* low ==high: low (high)
* low+1==high: mid==low; NORMAL: no change: high
* low+1==high: mid==low; EOF : high=mid => 'low==high' case
*/
for (low=0,
high=
- G_MAXINT; /* FIXME: 'G_MAXINT64' fails on g_io_channel_seek_position() */
+ G_MAXINT64;
low+1<high;) {
mid=low+(high-low)/2; /* beware of 'gint64' overflow! */
sizeof(bufchar), /* count */
&bufchargot, /* bytes_read */
NULL); /* error */
- g_assert(erriostatus==G_IO_STATUS_NORMAL || erriostatus==G_IO_STATUS_EOF);
+ /* During read on the end boundary of Linux kernel block device we will
+ * get GNOME_VFS_ERROR_IO at least from linux-kernel-2.4.19-ac4
+ * which will get mapped to G_IO_STATUS_ERROR by captive_gnomevfs_giognomevfs_io_read().
+ */
+ g_assert(0
+ || erriostatus==G_IO_STATUS_NORMAL
+ || erriostatus==G_IO_STATUS_EOF
+ || erriostatus==G_IO_STATUS_ERROR);
g_assert(0
|| (erriostatus==G_IO_STATUS_NORMAL && bufchargot==1)
- || (erriostatus==G_IO_STATUS_EOF && bufchargot==0));
+ || (erriostatus==G_IO_STATUS_EOF && bufchargot==0)
+ || (erriostatus==G_IO_STATUS_ERROR && bufchargot==0));
}
if (erriostatus==G_IO_STATUS_NORMAL)
g_assert(high>=0);
return high;
}
+
+
+guint64 captive_giochannel_size(GIOChannel *iochannel)
+{
+guint64 r;
+
+ if ((r=size_blind(iochannel)))
+ return r;
+ if ((r=size_sandbox(iochannel)))
+ return r;
+ if ((r=size_ioctl(iochannel)))
+ return r;
+ if ((r=size_seek(iochannel)))
+ return r;
+ if ((r=size_read(iochannel)))
+ return r;
+
+ return r;
+}