libexplain  1.4.D001
libexplain/buffer/eio.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - Explain errno values returned by libc functions
00003  * Copyright (C) 2008-2011, 2013 Peter Miller
00004  * Written by Peter Miller <pmiller@opensource.org.au>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as
00008  * published by the Free Software Foundation; either version 3 of the
00009  * License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include <libexplain/ac/assert.h>
00021 #include <libexplain/ac/dirent.h>
00022 #include <libexplain/ac/fcntl.h>
00023 #include <libexplain/ac/limits.h> /* for PATH_MAX on solaris */
00024 #include <libexplain/ac/string.h>
00025 #include <libexplain/ac/sys/param.h> /* for PATH_MAX except solaris */
00026 #include <libexplain/ac/sys/stat.h>
00027 #include <libexplain/ac/unistd.h>
00028 
00029 #include <libexplain/ac/sys/mtio.h>
00030 #ifdef __linux__
00031 #include <libexplain/ac/linux/hdreg.h>
00032 #endif
00033 #include <libexplain/ac/termios.h>
00034 
00035 #include <libexplain/buffer/device_name.h>
00036 #include <libexplain/buffer/eio.h>
00037 #include <libexplain/buffer/file_type.h>
00038 #include <libexplain/buffer/gettext.h>
00039 #include <libexplain/dirname.h>
00040 
00041 
00042 static void
00043 possibly_as_a_result_of_a_preceding(explain_string_buffer_t *sb, int fildes)
00044 {
00045     int             flags;
00046 
00047     flags = fcntl(fildes, F_GETFL);
00048     if (flags < 0)
00049         flags = O_RDWR;
00050     explain_string_buffer_puts(sb, ", ");
00051     switch (flags & O_ACCMODE)
00052     {
00053     case O_RDONLY:
00054         explain_buffer_gettext
00055         (
00056             sb,
00057             /*
00058              * xgettext: This message is used when explaining an EIO
00059              * error, for a file open only for reading.
00060              */
00061             i18n("possibly as a result of a preceding read(2) system call")
00062         );
00063         break;
00064 
00065     case O_WRONLY:
00066         explain_buffer_gettext
00067         (
00068             sb,
00069             /*
00070              * xgettext: This message is used when explaining an EIO
00071              * error, for a file open only for writing.
00072              */
00073             i18n("possibly as a result of a preceding write(2) system call")
00074         );
00075         break;
00076 
00077     default:
00078         explain_buffer_gettext
00079         (
00080             sb,
00081             /*
00082              * xgettext: This message is used when explaining an EIO
00083              * error, for a file open for both reading and writing.
00084              */
00085             i18n("possibly as a result of a preceding read(2) or "
00086             "write(2) system call")
00087         );
00088         break;
00089     }
00090 }
00091 
00092 
00093 static void
00094 explain_buffer_eio_generic(explain_string_buffer_t *sb, int fildes)
00095 {
00096     explain_buffer_gettext
00097     (
00098         sb,
00099         /*
00100          * xgettext: This message is used when explaining an EIO error.
00101          * Such errors are usually related to the underlying hardware of
00102          * the device being used, or the special device that contains
00103          * the file system the file is stored in.
00104          */
00105         i18n("a low-level I/O error occurred, probably in hardware")
00106     );
00107     possibly_as_a_result_of_a_preceding(sb, fildes);
00108 }
00109 
00110 
00111 void
00112 explain_buffer_eio(explain_string_buffer_t *sb)
00113 {
00114     explain_buffer_eio_generic(sb, -1);
00115 }
00116 
00117 
00118 static int
00119 dev_stat(dev_t dev, struct stat *st, explain_string_buffer_t *dev_buf)
00120 {
00121     return explain_buffer_device_name(dev_buf, dev, st);
00122 }
00123 
00124 
00125 static void
00126 a_low_level_io_error_occurred(explain_string_buffer_t *sb,
00127     const char *device_path, const struct stat *st)
00128 {
00129     char            ftype[FILE_TYPE_BUFFER_SIZE_MIN];
00130     explain_string_buffer_t ftype_sb;
00131 
00132     explain_string_buffer_init(&ftype_sb, ftype, sizeof(ftype));
00133     if (device_path[0])
00134     {
00135         explain_string_buffer_puts_quoted(&ftype_sb, device_path);
00136         explain_string_buffer_putc(&ftype_sb, ' ');
00137     }
00138     explain_buffer_file_type_st(&ftype_sb, st);
00139 
00140     explain_string_buffer_printf_gettext
00141     (
00142         sb,
00143         /*
00144          * xgettext: This message is used when explaining an EIO error.
00145          * Such errors are usually related to the underlying hardware of
00146          * the device being used, or the special device that contains
00147          * the file system the file is stored in.
00148          *
00149          * %1$s => The device named and device's file type
00150          */
00151         i18n("a low-level I/O error occurred in the %s"),
00152         ftype
00153     );
00154 }
00155 
00156 
00157 static void
00158 explain_buffer_eio_stat(explain_string_buffer_t *sb, int fildes,
00159     struct stat *st)
00160 {
00161     char            dev_path[150];
00162     explain_string_buffer_t dev_buf;
00163 
00164     explain_string_buffer_init(&dev_buf, dev_path, sizeof(dev_path));
00165     assert(dev_path[0] == '\0');
00166     switch (st->st_mode & S_IFMT)
00167     {
00168     case S_IFDIR:
00169     case S_IFREG:
00170         if (dev_stat(st->st_dev, st, &dev_buf) < 0)
00171         {
00172             explain_buffer_eio(sb);
00173             return;
00174         }
00175         break;
00176 
00177     default:
00178         dev_stat(st->st_rdev, st, &dev_buf);
00179         /* no problem if it failed, use the st we were given */
00180         break;
00181     }
00182 
00183     switch (st->st_mode & S_IFMT)
00184     {
00185     case S_IFBLK:
00186     case S_IFCHR:
00187         a_low_level_io_error_occurred(sb, dev_path, st);
00188         possibly_as_a_result_of_a_preceding(sb, fildes);
00189         break;
00190 
00191     default:
00192         explain_buffer_eio_generic(sb, fildes);
00193         break;
00194     }
00195 }
00196 
00197 
00198 void
00199 explain_buffer_eio_fildes(explain_string_buffer_t *sb, int fildes)
00200 {
00201     struct stat     st;
00202 
00203     if (fstat(fildes, &st) < 0)
00204         explain_buffer_eio(sb);
00205     else
00206         explain_buffer_eio_stat(sb, fildes, &st);
00207 }
00208 
00209 
00210 void
00211 explain_buffer_eio_path(explain_string_buffer_t *sb, const char *path)
00212 {
00213     struct stat     st;
00214 
00215     if (stat(path, &st) < 0 && lstat(path, &st) < 0)
00216         explain_buffer_eio(sb);
00217     else
00218         explain_buffer_eio_stat(sb, -1, &st);
00219 }
00220 
00221 
00222 void
00223 explain_buffer_eio_path_dirname(explain_string_buffer_t *sb,
00224     const char *path)
00225 {
00226     char            path_dir[PATH_MAX + 1];
00227 
00228     explain_dirname(path_dir, path, sizeof(path_dir));
00229     explain_buffer_eio_path(sb, path_dir);
00230 }
00231 
00232 
00233 /* vim: set ts=8 sw=4 et : */