libexplain  1.4.D001
libexplain/buffer/errno/mmap.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - Explain errno values returned by libc functions
00003  * Copyright (C) 2010, 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 of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
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/fcntl.h>
00021 #include <libexplain/ac/sys/mman.h>
00022 #include <libexplain/ac/sys/resource.h>
00023 #include <libexplain/ac/sys/stat.h>
00024 
00025 #include <libexplain/buffer/eacces.h>
00026 #include <libexplain/buffer/eagain.h>
00027 #include <libexplain/buffer/ebadf.h>
00028 #include <libexplain/buffer/einval.h>
00029 #include <libexplain/buffer/enfile.h>
00030 #include <libexplain/buffer/enodev.h>
00031 #include <libexplain/buffer/enomem.h>
00032 #include <libexplain/buffer/eperm.h>
00033 #include <libexplain/buffer/errno/generic.h>
00034 #include <libexplain/buffer/errno/mmap.h>
00035 #include <libexplain/buffer/etxtbsy.h>
00036 #include <libexplain/buffer/fildes.h>
00037 #include <libexplain/buffer/gettext.h>
00038 #include <libexplain/buffer/int.h>
00039 #include <libexplain/buffer/mmap_flags.h>
00040 #include <libexplain/buffer/mmap_prot.h>
00041 #include <libexplain/buffer/mount_point.h>
00042 #include <libexplain/buffer/must_be_multiple_of_page_size.h>
00043 #include <libexplain/buffer/off_t.h>
00044 #include <libexplain/buffer/open_flags.h>
00045 #include <libexplain/buffer/pointer.h>
00046 #include <libexplain/buffer/size_t.h>
00047 #include <libexplain/buffer/wrong_file_type.h>
00048 #include <libexplain/explanation.h>
00049 #include <libexplain/getpagesize.h>
00050 #include <libexplain/parse_bits.h>
00051 
00052 
00053 static void
00054 explain_buffer_errno_mmap_system_call(explain_string_buffer_t *sb, int errnum,
00055     void *data, size_t data_size, int prot, int flags, int fildes, off_t offset)
00056 {
00057     (void)errnum;
00058     explain_string_buffer_puts(sb, "mmap(data = ");
00059     explain_buffer_pointer(sb, data);
00060     explain_string_buffer_puts(sb, ", data_size = ");
00061     explain_buffer_size_t(sb, data_size);
00062     explain_string_buffer_puts(sb, ", prot = ");
00063     explain_buffer_mmap_prot(sb, prot);
00064     explain_string_buffer_puts(sb, ", flags = ");
00065     explain_buffer_mmap_flags(sb, flags);
00066     explain_string_buffer_puts(sb, ", fildes = ");
00067 #ifdef MAP_ANONYMOUS
00068     if (flags & MAP_ANONYMOUS)
00069         explain_buffer_int(sb, fildes);
00070     else
00071 #endif
00072 #ifdef MAP_ANON
00073     if (flags & MAP_ANON)
00074         explain_buffer_int(sb, fildes);
00075     else
00076 #endif
00077         explain_buffer_fildes(sb, fildes);
00078     explain_string_buffer_puts(sb, ", offset = ");
00079     explain_buffer_off_t(sb, offset);
00080     explain_string_buffer_putc(sb, ')');
00081 }
00082 
00083 
00084 void
00085 explain_buffer_errno_mmap_explanation(explain_string_buffer_t *sb, int errnum,
00086     const char *syscall_name, void *data, size_t data_size, int prot, int flags,
00087     int fildes, off_t offset)
00088 {
00089     /*
00090      * http://www.opengroup.org/onlinepubs/009695399/functions/mmap.html
00091      */
00092     switch (errnum)
00093     {
00094     case EACCES:
00095         {
00096             struct stat st;
00097             if (fstat(fildes, &st) == 0 && !S_ISREG(st.st_mode))
00098             {
00099                 /*
00100                  * The file descriptor refers to a non-regular file.
00101                  */
00102                 explain_buffer_wrong_file_type_st
00103                 (
00104                     sb,
00105                     &st,
00106                     "fildes",
00107                     S_IFREG
00108                 );
00109                 break;
00110             }
00111         }
00112 
00113         /*
00114          * Or MAP_PRIVATE was requested, but fd is not open for reading.
00115          */
00116 #ifdef MAP_PRIVATE
00117         if (flags & MAP_PRIVATE)
00118         {
00119             int omode = fcntl(fildes, F_GETFD, 0);
00120             if (omode >= 0 && (omode & O_ACCMODE) == O_WRONLY)
00121             {
00122                 explain_buffer_ebadf_not_open_for_reading(sb, "fildes", omode);
00123                 break;
00124             }
00125         }
00126 #endif
00127 
00128 #ifdef PROT_WRITE
00129         if (prot & PROT_WRITE)
00130         {
00131             int omode = fcntl(fildes, F_GETFD, 0);
00132             if (omode >= 0)
00133             {
00134 #ifdef MAP_SHARED
00135                 if (flags & MAP_SHARED)
00136                 {
00137                     /*
00138                      * Or MAP_SHARED was requested and PROT_WRITE is set, but
00139                      * fildes is not open in read/write (O_RDWR) mode.
00140                      */
00141                     if ((omode & O_ACCMODE) != O_RDWR)
00142                     {
00143                         explain_buffer_gettext
00144                         (
00145                             sb,
00146                             /*
00147                              * xgettext: This message is used when an attempt is
00148                              * made to mmap shared access to a file descriptor
00149                              * that was not opened for both reading and writing.
00150                              * The actual open mode will be printed separately.
00151                              */
00152                             i18n("the file descriptor is not open for both "
00153                                 "reading and writing")
00154                         );
00155                         explain_string_buffer_puts(sb, " (");
00156                         explain_buffer_open_flags(sb, omode);
00157                         explain_string_buffer_putc(sb, ')');
00158                         break;
00159                     }
00160                 }
00161 #endif
00162 
00163                 /*
00164                  * Or PROT_WRITE is set, but the file is append-only.
00165                  */
00166                 if (omode & O_APPEND)
00167                 {
00168                     explain_buffer_gettext
00169                     (
00170                         sb,
00171                         /*
00172                          * xgettext: This message is used when an attempt is
00173                          * made to mmap write access to a file descriptor that
00174                          * is opened for append only.  The actual open mode will
00175                          * be printed separately.
00176                          */
00177                         i18n("the file descriptor is open for append")
00178                     );
00179                     explain_string_buffer_puts(sb, " (");
00180                     explain_buffer_open_flags(sb, omode);
00181                     explain_string_buffer_putc(sb, ')');
00182                     break;
00183                 }
00184             }
00185         }
00186 #endif
00187         goto generic;
00188 
00189     case EAGAIN:
00190 #if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK
00191     case EWOULDBLOCK:
00192 #endif
00193         /*
00194          * the file has been locked, or too much  memory  has  been  locked
00195          */
00196 #ifdef MAP_LOCKED
00197         if (flags & MAP_LOCKED)
00198         {
00199             if (explain_buffer_enomem_rlimit_exceeded(sb, data_size))
00200                 return;
00201         }
00202 #endif
00203         /* FIXME: locked by whom? */
00204         explain_buffer_gettext
00205         (
00206             sb,
00212             i18n("the file is locked")
00213         );
00214         break;
00215 
00216     case EBADF:
00217         /*
00218          * fildes is not a valid file descriptor (and MAP_ANONYMOUS was
00219          * not set).
00220          */
00221         explain_buffer_ebadf(sb, fildes, "fildes");
00222         /* assume this is an actual error, and do not check MAP_ANONYMOUS */
00223         break;
00224 
00225     case EINVAL:
00226         /*
00227          * We don't like addr, length, or offset (e.g., they are too large,
00228          * or not aligned on a page boundary).
00229          *
00230          * or (since Linux 2.6.12) length was 0
00231          */
00232 #ifdef HAVE_GETPAGESIZE
00233         {
00234             int             page_size;
00235 
00236             page_size = explain_getpagesize();
00237             if (page_size > 0)
00238             {
00239                 unsigned        mask;
00240 
00241                 mask = (page_size - 1);
00242                 if ((unsigned long)data & mask)
00243                 {
00244                     explain_buffer_must_be_multiple_of_page_size(sb, "data");
00245                     break;
00246                 }
00247                 if ((unsigned long)data_size & mask)
00248                 {
00249                     explain_buffer_must_be_multiple_of_page_size
00250                     (
00251                         sb,
00252                         "data_size"
00253                     );
00254                     break;
00255                 }
00256                 if ((unsigned long)offset & mask)
00257                 {
00258                     explain_buffer_must_be_multiple_of_page_size(sb, "offset");
00259                     break;
00260                 }
00261             }
00262         }
00263 #endif
00264         if (data_size == 0)
00265         {
00266             explain_buffer_einval_too_small(sb, "data_size", data_size);
00267             break;
00268         }
00269 
00270         /*
00271          * or flags  contained neither MAP_PRIVATE or MAP_SHARED, or contained
00272          * both of these values.
00273          */
00274         switch (flags & (MAP_PRIVATE | MAP_SHARED))
00275         {
00276         case 0:
00277         case MAP_PRIVATE | MAP_SHARED:
00278             explain_buffer_gettext
00279             (
00280                 sb,
00281                 /*
00282                  * xgettext: This message is used to explain in EINVAL error
00283                  * reported by mmap(2), in the case where the flags did not
00284                  * contain exactly one of MAP_PRIVATE or MAP_SHARED.
00285                  */
00286                 i18n("you must specify exactly one of MAP_PRIVATE or "
00287                     "MAP_SHARED")
00288             );
00289             return;
00290 
00291         default:
00292             break;
00293         }
00294         goto generic;
00295 
00296     case ENFILE:
00297         /*
00298          * The system limit on the total number of open files has been reached.
00299          */
00300         explain_buffer_enfile(sb);
00301         break;
00302 
00303 #ifdef ENOTSUP
00304     case ENOTSUP:
00305 #endif
00306 #ifdef EOPNOTSUP
00307     case EOPNOTSUP:
00308 #endif
00309     case ENODEV:
00310         explain_buffer_gettext
00311         (
00312             sb,
00313             /*
00314              * xgettext: This message is used to explain in ENODEV error
00315              * reported by mmap(2), in the case where the underlying
00316              * file system of the specified file does not support memory
00317              * mapping.
00318              */
00319             i18n("the underlying file system does not support memory mapping")
00320         );
00321         explain_buffer_mount_point_fd(sb, fildes);
00322         break;
00323 
00324     case ENOMEM:
00325         /*
00326          * No memory is available, or
00327          * the process's maximum number of mappings would have been exceeded.
00328          */
00329         if (explain_buffer_enomem_rlimit_exceeded(sb, data_size))
00330             return;
00331         explain_buffer_enomem_kernel(sb);
00332         break;
00333 
00334     case EPERM:
00335 #ifdef PROT_EXEC
00336         if (prot & PROT_EXEC)
00337         {
00338             explain_buffer_gettext
00339             (
00340                 sb,
00347                 i18n("the underlying file system does not permit execution")
00348             );
00349             explain_buffer_mount_point_fd(sb, fildes);
00350             break;
00351         }
00352 #endif
00353         goto generic;
00354 
00355     case ETXTBSY:
00356 #ifdef MAP_DENYWRITE
00357         if (flags & MAP_DENYWRITE)
00358         {
00359             int omode = fcntl(fildes, F_GETFD, 0);
00360             if (omode >= 0)
00361             {
00362                 switch (omode & O_ACCMODE)
00363                 {
00364                 case O_WRONLY:
00365                 case O_RDWR:
00366                     explain_buffer_gettext
00367                     (
00368                         sb,
00376                         i18n("the mapping flag MAP_DENYWRITE is incompatible "
00377                             "with the open mode of the file descriptor")
00378                     );
00379                     explain_string_buffer_puts(sb, " (");
00380                     explain_buffer_open_flags(sb, omode);
00381                     explain_string_buffer_putc(sb, ')');
00382                     return;
00383 
00384                 default:
00385                     break;
00386                 }
00387             }
00388         }
00389 #endif
00390         goto generic;
00391 
00392     default:
00393         generic:
00394         explain_buffer_errno_generic(sb, errnum, syscall_name);
00395         break;
00396     }
00397 }
00398 
00399 
00400 void
00401 explain_buffer_errno_mmap(explain_string_buffer_t *sb, int errnum, void *data,
00402     size_t data_size, int prot, int flags, int fildes, off_t offset)
00403 {
00404     explain_explanation_t exp;
00405 
00406     explain_explanation_init(&exp, errnum);
00407     explain_buffer_errno_mmap_system_call(&exp.system_call_sb, errnum, data,
00408         data_size, prot, flags, fildes, offset);
00409     explain_buffer_errno_mmap_explanation(&exp.explanation_sb, errnum, "mmap",
00410         data, data_size, prot, flags, fildes, offset);
00411     explain_explanation_assemble(&exp, sb);
00412 }
00413 
00414 
00415 /* vim: set ts=8 sw=4 et : */