libexplain  1.4.D001
libexplain/buffer/errno/writev.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - Explain errno values returned by libc functions
00003  * Copyright (C) 2009-2011, 2013 Peter Miller
00004  *
00005  * This program is free software; you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License as published by
00007  * the Free Software Foundation; either version 3 of the License, or (at
00008  * your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,but
00011  * WITHOUT ANY WARRANTY; without even the implied warranty
00012  * ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser
00013  * General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public License
00016  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include <libexplain/ac/errno.h>
00020 #include <libexplain/ac/stdio.h>
00021 #include <libexplain/ac/sys/uio.h>
00022 #include <libexplain/ac/unistd.h> /* for sysconf() on Solaris */
00023 
00024 #include <libexplain/buffer/efault.h>
00025 #include <libexplain/buffer/eintr.h>
00026 #include <libexplain/buffer/einval.h>
00027 #include <libexplain/buffer/enosys.h>
00028 #include <libexplain/buffer/errno/write.h>
00029 #include <libexplain/buffer/errno/writev.h>
00030 #include <libexplain/buffer/fildes.h>
00031 #include <libexplain/buffer/int.h>
00032 #include <libexplain/buffer/iovec.h>
00033 #include <libexplain/buffer/long_long.h>
00034 #include <libexplain/buffer/ssize_t.h>
00035 #include <libexplain/explanation.h>
00036 #include <libexplain/option.h>
00037 #include <libexplain/is_efault.h>
00038 
00039 
00040 static void
00041 explain_buffer_errno_writev_system_call(explain_string_buffer_t *sb, int errnum,
00042     int fildes, const struct iovec *data, int data_size)
00043 {
00044     (void)errnum;
00045     explain_string_buffer_puts(sb, "writev(fildes = ");
00046     explain_buffer_fildes(sb, fildes);
00047     explain_string_buffer_puts(sb, ", data = ");
00048     explain_buffer_iovec(sb, data, data_size);
00049     explain_string_buffer_puts(sb, ", data_size = ");
00050     explain_buffer_int(sb, data_size);
00051     explain_string_buffer_putc(sb, ')');
00052 }
00053 
00054 
00055 void
00056 explain_buffer_errno_writev_explanation(explain_string_buffer_t *sb, int errnum,
00057     const char *syscall_name, int fildes, const struct iovec *data,
00058     int data_size)
00059 {
00060     void            *data2;
00061     size_t          data2_size;
00062     int             j;
00063 
00064     /*
00065      * http://www.opengroup.org/onlinepubs/009695399/functions/writev.html
00066      */
00067     switch (errnum)
00068     {
00069     case EFAULT:
00070         if (explain_is_efault_pointer(data, data_size * sizeof(*data)))
00071         {
00072             explain_buffer_efault(sb, "data");
00073             return;
00074         }
00075         for (j = 0; j < data_size; ++j)
00076         {
00077             const struct iovec *p = data + j;
00078             if (explain_is_efault_pointer(p->iov_base, p->iov_len))
00079             {
00080                 char            buffer[60];
00081 
00082                 snprintf(buffer, sizeof(buffer), "data[%d].iov_base", j);
00083                 explain_buffer_efault(sb, buffer);
00084                 return;
00085             }
00086         }
00087         return;
00088 
00089     case EINTR:
00090         explain_buffer_eintr(sb, syscall_name);
00091         return;
00092 
00093     case EINVAL:
00094         if (data_size < 0)
00095         {
00096             explain_buffer_einval_too_small(sb, "data_size", data_size);
00097             return;
00098         }
00099 
00100         /*
00101          * Linux writev(2) says
00102          * "POSIX.1-2001 allows an implementation to place a limit
00103          * on the number of items that can be passed in data.  An
00104          * implementation can advertise its limit by defining IOV_MAX
00105          * in <limits.h> or at run time via the return value from
00106          * sysconf(_SC_IOV_MAX).
00107          *
00108          * "On Linux, the limit advertised by these mechanisms is 1024,
00109          * which is the true kernel limit.  However, the glibc wrapper
00110          * functions do some extra work [to hide it]."
00111          */
00112         {
00113             long            iov_max;
00114 
00115             iov_max = -1;
00116 #ifdef _SC_IOV_MAX
00117             iov_max = sysconf(_SC_IOV_MAX);
00118 #endif
00119 #if IOV_MAX > 0
00120             if (iov_max <= 0)
00121                 iov_max = IOV_MAX;
00122 #endif
00123             if (iov_max > 0 && data_size > iov_max)
00124             {
00125                 explain_buffer_einval_too_large2(sb, "data_size", iov_max);
00126                 return;
00127             }
00128         }
00129 
00130         /*
00131          * The total size must fit into a ssize_t return value.
00132          */
00133         {
00134             ssize_t max = (~(size_t)0) >> 1;
00135             long long total = 0;
00136             for (j = 0; j < data_size; ++j)
00137                 total += data[j].iov_len;
00138             if (total > max)
00139             {
00140                 explain_buffer_einval_too_large(sb, "data[*].iov_len");
00141                 if (explain_option_dialect_specific())
00142                 {
00143                     explain_string_buffer_puts(sb, " (");
00144                     explain_buffer_long_long(sb, total);
00145                     explain_string_buffer_puts(sb, " > ");
00146                     explain_buffer_ssize_t(sb, max);
00147                     explain_string_buffer_putc(sb, ')');
00148                 }
00149                 return;
00150             }
00151         }
00152         break;
00153 
00154     case ENOSYS:
00155 #if defined(EOPNOTSUPP) && EOPNOTSUPP != ENOSYS
00156     case EOPNOTSUPP:
00157 #endif
00158         explain_buffer_enosys_vague(sb, syscall_name);
00159         return;
00160 
00161     default:
00162         break;
00163     }
00164 
00165     /*
00166      * We know the size fits, because we would otherwise get an EINVAL
00167      * error, and that would have been handled already.
00168      */
00169     data2 = (data_size > 0 ? data[0].iov_base : 0);
00170     data2_size = 0;
00171     for (j = 0; j < data_size; ++j)
00172         data2_size += data[j].iov_len;
00173     explain_buffer_errno_write_explanation
00174     (
00175         sb,
00176         errnum,
00177         syscall_name,
00178         fildes,
00179         data2,
00180         data2_size
00181     );
00182 }
00183 
00184 
00185 void
00186 explain_buffer_errno_writev(explain_string_buffer_t *sb, int errnum, int fildes,
00187     const struct iovec *data, int data_size)
00188 {
00189     explain_explanation_t exp;
00190 
00191     explain_explanation_init(&exp, errnum);
00192     explain_buffer_errno_writev_system_call(&exp.system_call_sb, errnum, fildes,
00193         data, data_size);
00194     explain_buffer_errno_writev_explanation(&exp.explanation_sb, errnum,
00195         "writev", fildes, data, data_size);
00196     explain_explanation_assemble(&exp, sb);
00197 }
00198 
00199 
00200 /* vim: set ts=8 sw=4 et : */