2 * Workaround Linux kernel bug wrt accessing last device block 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 "iounixchannel.h" /* self */
23 #include <glib/gmessages.h>
24 #include <glib/giochannel.h>
25 #include <glib/gtypes.h>
29 #include <linux/hdreg.h>
30 #include <sys/ioctl.h>
31 #include "captive/macros.h"
33 #include "../client/lib.h" /* for captive_giochannel_setup(); FIXME: pathname */
34 #include "../client/giochannel-subrange.h"
35 #include "captive/storage.h" /* for captive_giochannel_size() */
39 #define ENSURE_BLOCK_SIZE 0x200 /* size of the NTFS 'superblock backup' */
43 gboolean start_bytes_ioctl_detect(int fd,guint64 *start_bytes_ioctlp)
45 struct hd_geometry hd_geometry;
46 #ifdef HDIO_GETGEO_BIG
47 struct hd_big_geometry hd_big_geometry;
49 unsigned long start_sectors_ioctl;
51 /* 'start' field is always 'unsigned long'.
52 * 'HDIO_GETGEO_BIG' is supported if 'HDIO_GETGEO' gets deprecated.
56 #ifdef HDIO_GETGEO_BIG
57 else if (!ioctl(fd,HDIO_GETGEO_BIG,&hd_big_geometry))
58 start_sectors_ioctl=hd_big_geometry.start;
60 else if (!ioctl(fd,HDIO_GETGEO,&hd_geometry))
61 start_sectors_ioctl=hd_geometry.start;
65 *start_bytes_ioctlp=((guint64)start_sectors_ioctl)*0x200;
69 static gboolean check_last_block(GIOChannel *iochannel,guint64 iochannel_size)
71 GIOStatus erriostatus;
72 gchar buf[ENSURE_BLOCK_SIZE];
75 g_return_val_if_fail(iochannel!=NULL,FALSE);
76 g_return_val_if_fail(iochannel_size>0,FALSE);
78 erriostatus=g_io_channel_seek_position(iochannel,iochannel_size-ENSURE_BLOCK_SIZE,G_SEEK_SET,
80 g_assert(erriostatus==G_IO_STATUS_NORMAL);
82 erriostatus=g_io_channel_read_chars(iochannel,
84 ENSURE_BLOCK_SIZE, /* count */
85 &bufgot, /* bytes_read */
87 /* During read on the end boundary of Linux kernel block device we will
88 * get GNOME_VFS_ERROR_IO at least from linux-kernel-2.4.19-ac4
89 * which will get mapped to G_IO_STATUS_ERROR by captive_gnomevfs_giognomevfs_io_read().
92 || (bufgot==ENSURE_BLOCK_SIZE && erriostatus==G_IO_STATUS_NORMAL)
93 || (bufgot==0 && erriostatus==G_IO_STATUS_EOF)
94 || (bufgot==0 && erriostatus==G_IO_STATUS_ERROR));
96 return (erriostatus==G_IO_STATUS_NORMAL);
99 /* No new reference is created; unref the result the same way as the input. */
100 GIOChannel *captive_storage_relastblock(GIOChannel *iochannel)
103 guint64 size_bytes_ioctl;
104 guint64 start_bytes_ioctl;
105 char linkbuf[0x1000],*linknum;
107 const gchar *iochannel_unix_new_mode;
108 const gchar *linkread_pathname;
109 const gchar *slashpart_prefix;
111 GIOFlags iochannel_flags;
112 GIOChannel *iochannel_unix_new;
113 int iochannel_unix_new_fd;
114 guint64 unix_new_start_bytes_ioctl,unix_new_size_bytes_ioctl;
115 GIOChannel *iochannel_subrange_new;
117 g_return_val_if_fail(iochannel!=NULL,NULL);
119 if (-1==(fd=captive_iounixchannel_get_fd(iochannel)))
122 if (ENSURE_BLOCK_SIZE>(size_bytes_ioctl=captive_giochannel_size_ioctl(iochannel)))
125 g_return_val_if_fail(g_io_channel_get_encoding(iochannel)==NULL,0);
127 if (!start_bytes_ioctl_detect(fd,&start_bytes_ioctl))
130 if (check_last_block(iochannel,size_bytes_ioctl))
133 linkread_pathname=captive_printf_alloca("/proc/self/fd/%d",fd);
135 linkread_pathname, /* path */
137 sizeof(linkbuf)); /* bufsiz */
138 if (!(linkgot>=1 && linkgot<=(int)sizeof(linkbuf)-1))
139 g_error(_("Unable to read: %s"),linkread_pathname);
140 linkbuf[linkgot]=0; /* readlink(2) does not '\0'-terminate it */
142 for (linknum=linkbuf+linkgot;linknum>linkbuf && isdigit(linknum[-1]);linknum--);
143 if (linknum>=linkbuf+linkgot)
144 g_error(_("Last block not readable although the link read has no trailing number: %s"),linkbuf);
147 /* /dev/ide/host0/bus0/target0/lun0/part1 -> /dev/ide/host0/bus0/target0/lun0/disc */
148 slashpart_prefix="/part";
149 if (linknum>linkbuf+strlen(slashpart_prefix) && !strcmp(linknum-strlen(slashpart_prefix),slashpart_prefix))
150 strcpy(linknum-strlen(slashpart_prefix),"/disc");
152 /* /dev/ataraid/d0p1 -> /dev/ataraid/d0 */
153 if (linknum>=linkbuf+2 && linknum[-1]=='p' && isdigit(linknum[-2]))
156 iochannel_flags=g_io_channel_get_flags(iochannel);
157 switch (iochannel_flags & (G_IO_FLAG_IS_READABLE|G_IO_FLAG_IS_WRITEABLE)) {
158 case G_IO_FLAG_IS_READABLE:
159 iochannel_unix_new_mode="r";
161 case G_IO_FLAG_IS_READABLE|G_IO_FLAG_IS_WRITEABLE:
162 iochannel_unix_new_mode="r+";
164 default: g_assert_not_reached();
167 if (!(iochannel_unix_new=g_io_channel_new_file(
168 linkbuf, /* filename */
169 iochannel_unix_new_mode, /* mode */
171 g_error(_("Parent partition \"%s\" not readable by mode \"%s\""),linkbuf,iochannel_unix_new_mode);
173 /* 'iochannel_unix_new' sanity checks: */
174 iochannel_unix_new_fd=captive_iounixchannel_get_fd(iochannel_unix_new);
175 g_assert(iochannel_unix_new_fd>=0);
176 errbool=start_bytes_ioctl_detect(iochannel_unix_new_fd,&unix_new_start_bytes_ioctl);
177 g_assert(errbool==TRUE);
178 g_assert(unix_new_start_bytes_ioctl==0);
179 unix_new_size_bytes_ioctl=captive_giochannel_size_ioctl(iochannel_unix_new);
180 g_assert(unix_new_size_bytes_ioctl>0);
181 if (!(unix_new_size_bytes_ioctl>=start_bytes_ioctl+size_bytes_ioctl))
182 g_error(_("Partition last block inaccessible and partition table incorrect: disc size %llu < part start %llu + part size %llu"),
183 (unsigned long long)unix_new_size_bytes_ioctl,
184 (unsigned long long)start_bytes_ioctl,
185 (unsigned long long)size_bytes_ioctl);
187 iochannel_subrange_new=(GIOChannel *)captive_giochannel_subrange_new(iochannel_unix_new,
188 start_bytes_ioctl, /* start */
189 start_bytes_ioctl+size_bytes_ioctl); /* end */
190 g_assert(iochannel_subrange_new!=NULL);
192 g_io_channel_unref(iochannel_unix_new); /* now reffed by 'iochannel_subrange_new' */
194 captive_giochannel_setup(iochannel_subrange_new);
195 g_io_channel_unref(iochannel);
197 if (!check_last_block(iochannel_subrange_new,size_bytes_ioctl))
198 g_error(_("Last block still not readable for the subrange of: %s"),linkbuf);
200 if (size_bytes_ioctl!=captive_giochannel_size(iochannel_subrange_new))
201 g_error(_("Invalid size of the subranged GIOChannel of: %s"),linkbuf);
203 return iochannel_subrange_new;