libexplain  1.4.D001
libexplain/buffer/errno/mount.c
Go to the documentation of this file.
00001 /*
00002  * libexplain - Explain errno values returned by libc functions
00003  * Copyright (C) 2013, 2014 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/ctype.h>
00020 #include <libexplain/ac/errno.h>
00021 #include <libexplain/ac/fcntl.h>
00022 #include <libexplain/ac/mntent.h>
00023 #include <libexplain/ac/stdio.h>
00024 #include <libexplain/ac/stdlib.h>
00025 #include <libexplain/ac/string.h>
00026 #include <libexplain/ac/sys/mount.h>
00027 #include <libexplain/ac/sys/stat.h> /* for major()/minor() except Solaris */
00028 #include <libexplain/ac/sys/statvfs.h>
00029 #include <libexplain/ac/sys/sysmacros.h> /* for major()/minor() on Solaris */
00030 #include <libexplain/ac/unistd.h>
00031 
00032 #include <libexplain/buffer/eacces.h>
00033 #include <libexplain/buffer/ebusy.h>
00034 #include <libexplain/buffer/efault.h>
00035 #include <libexplain/buffer/einval.h>
00036 #include <libexplain/buffer/eloop.h>
00037 #include <libexplain/buffer/emfile.h>
00038 #include <libexplain/buffer/enametoolong.h>
00039 #include <libexplain/buffer/enodev.h>
00040 #include <libexplain/buffer/enoent.h>
00041 #include <libexplain/buffer/enomem.h>
00042 #include <libexplain/buffer/enotblk.h>
00043 #include <libexplain/buffer/enotdir.h>
00044 #include <libexplain/buffer/enxio.h>
00045 #include <libexplain/buffer/eperm.h>
00046 #include <libexplain/buffer/is_the_null_pointer.h>
00047 #include <libexplain/buffer/errno/generic.h>
00048 #include <libexplain/buffer/errno/mount.h>
00049 #include <libexplain/buffer/errno/path_resolution.h>
00050 #include <libexplain/buffer/gettext.h>
00051 #include <libexplain/buffer/mount_flags.h>
00052 #include <libexplain/buffer/mount_point.h>
00053 #include <libexplain/buffer/pathname.h>
00054 #include <libexplain/explanation.h>
00055 #include <libexplain/fileinfo.h>
00056 #include <libexplain/fstrcmp.h>
00057 #include <libexplain/is_efault.h>
00058 #include <libexplain/option.h>
00059 
00060 
00061 static void
00062 explain_buffer_errno_mount_system_call(explain_string_buffer_t *sb, int errnum,
00063     const char *source, const char *target, const char *file_systems_type,
00064     unsigned long flags, const void *data)
00065 {
00066     (void)errnum;
00067     explain_string_buffer_puts(sb, "mount(source = ");
00068     explain_buffer_pathname(sb, source);
00069     explain_string_buffer_puts(sb, ", target = ");
00070     explain_buffer_pathname(sb, target);
00071     explain_string_buffer_puts(sb, ", file_system_type = ");
00072     explain_buffer_pathname(sb, file_systems_type);
00073     explain_string_buffer_puts(sb, ", flags = ");
00074     explain_buffer_mount_flags(sb, flags);
00075 
00076     /*
00077      * From mount(2) man page:
00078      * "The data argument is interpreted differently by the different
00079      * file systems.  Typically it is a string of comma-separated
00080      * options understood by this file system.  See mount(8) for details
00081      * of the options available for each filesystem type."
00082      */
00083     explain_string_buffer_puts(sb, ", data = ");
00084     explain_buffer_pathname(sb, data);
00085     explain_string_buffer_putc(sb, ')');
00086 }
00087 
00088 
00089 static int
00090 is_a_known_file_system_type(const char *fs_type, char **fuzzy)
00091 {
00092     FILE *fp;
00093 
00094     if (!fs_type)
00095         return 0;
00096     if (explain_is_efault_path(fs_type))
00097         return 0;
00098     if (!*fs_type)
00099         return 0;
00100 
00101     fp = fopen("/proc/filesystems", "r");
00102     if (fp)
00103     {
00104         double best_w = 0.6;
00105         char *best_name = 0;
00106         for(;;)
00107         {
00108             char line[80];
00109             char *col2;
00110             char *col2_end;
00111 
00112             if (!fgets(line, sizeof(line), fp))
00113                 break;
00114             col2 = strchr(line, '\t');
00115             if (!col2)
00116                 continue;
00117             ++col2;
00118 
00119             col2_end = strchr(col2, '\n');
00120             if (col2_end)
00121                 *col2_end = '\0';
00122 
00123             if (0 == strcmp(fs_type, col2))
00124                 return 1;
00125 
00126             /*
00127              * do a fuzzy match to improve error messages for command-line users
00128              */
00129             {
00130                 double w = explain_fstrcmp(fs_type, col2);
00131                 if (w > best_w)
00132                 {
00133                     best_w = w;
00134                     if (best_name)
00135                         free(best_name);
00136                     best_name = strdup(col2);
00137                 }
00138             }
00139         }
00140         fclose(fp);
00141 
00142         if (best_name && fuzzy)
00143         {
00144             *fuzzy = best_name;
00145         }
00146     }
00147 
00148     /*
00149      * Linux is civilised, I have no ide how to get at this data on any
00150      * other Posix o/s.
00151      */
00152     return -1;
00153 }
00154 
00155 
00156 static int
00157 target_is_already_mounted(const char *target)
00158 {
00159     FILE            *fp;
00160 
00161     fp = setmntent(MOUNTED, "r");
00162     if (!fp)
00163         return 0;
00164     for (;;)
00165     {
00166         const char      *dir;
00167 #if GETMNTENT_NARGS == 2
00168         struct mnttab   mdata;
00169 
00170         if (getmntent(fp, &mdata) != 0)
00171             break;
00172         dir = mdata.mnt_mountp;
00173 #else
00174         struct mntent   *mnt;
00175 
00176         /* FIXME: use getmntent_r if available */
00177         mnt = getmntent(fp);
00178         if (!mnt)
00179             break;
00180         dir = mnt->mnt_dir;
00181 #endif
00182         /* FIXME: using the device info from stat(2) would be more accurate */
00183         if (0 == strcmp(target, dir))
00184         {
00185             endmntent(fp);
00186             return 1;
00187         }
00188     }
00189     endmntent(fp);
00190     return 0;
00191 }
00192 
00193 
00194 static int
00195 source_is_already_mounted(const char *source)
00196 {
00197     FILE            *fp;
00198 
00199     fp = setmntent(MOUNTED, "r");
00200     if (!fp)
00201         return -1;
00202     for (;;)
00203     {
00204         const char      *dev;
00205 #if GETMNTENT_NARGS == 2
00206         struct mnttab   mdata;
00207 
00208         if (getmntent(fp, &mdata) != 0)
00209             break;
00210         dev = mdata.mnt_device;
00211 #else
00212         struct mntent   *mnt;
00213 
00214         /* FIXME: use getmntent_r if available */
00215         mnt = getmntent(fp);
00216         if (!mnt)
00217             break;
00218         dev = mnt->mnt_fsname;
00219 #endif
00220         /* FIXME: using the device info from stat(2) would be more accurate */
00221         if (0 == strcmp(source, dev))
00222         {
00223             endmntent(fp);
00224             return 1;
00225         }
00226     }
00227     endmntent(fp);
00228     return 0;
00229 }
00230 
00231 
00232 static int
00233 source_mounted_on_target(const char *source, const char *target)
00234 {
00235     FILE            *fp;
00236 
00237     fp = setmntent(MOUNTED, "r");
00238     if (!fp)
00239         return -1;
00240     for (;;)
00241     {
00242         const char      *dev;
00243         const char      *dir;
00244 #if GETMNTENT_NARGS == 2
00245         struct mnttab   mdata;
00246 
00247         if (getmntent(fp, &mdata) != 0)
00248             break;
00249         dev = mdata.mnt_device;
00250         dir = mdata.mnt_dir
00251 #else
00252         struct mntent   *mnt;
00253 
00254         /* FIXME: use getmntent_r if available */
00255         mnt = getmntent(fp);
00256         if (!mnt)
00257             break;
00258         dev = mnt->mnt_fsname;
00259         dir = mnt->mnt_dir;
00260 #endif
00261         /* FIXME: using the device info from stat(2) would be more accurate */
00262         if (0 == strcmp(source, dev) && 0 == strcmp(target, dir))
00263         {
00264             endmntent(fp);
00265             return 1;
00266         }
00267     }
00268     endmntent(fp);
00269     return 0;
00270 }
00271 
00272 
00273 static int
00274 source_is_in_partition_table(const char *source)
00275 {
00276     int found;
00277     struct stat st;
00278     FILE *fp;
00279 
00280     if (stat(source, &st) < 0)
00281         return -1;
00282     fp = fopen("/proc/partitions", "r");
00283     if (!fp)
00284         return -1;
00285     found = 0;
00286     for (;;)
00287     {
00288         char line[100];
00289         char *lp;
00290         char *ep;
00291         unsigned maj;
00292         unsigned min;
00293         unsigned dev;
00294         unsigned long blocks;
00295 
00296         if (!fgets(line, sizeof(line), fp))
00297             break;
00298         if (memcmp(line, "major", 5))
00299             continue;
00300         if (line[0] == '\n')
00301             continue;
00302 
00303          lp = line;
00304          ep = 0;
00305          maj = strtoul(lp, &ep, 0);
00306          lp = ep;
00307          min = strtoul(lp, &ep, 0);
00308          lp = ep;
00309          blocks = strtoul(lp, &ep, 0);
00310          lp = ep;
00311 
00312          while (isspace((unsigned char)*lp))
00313              ++lp;
00314          dev = makedev(maj, min);
00315          if (dev == st.st_rdev)
00316              ++found;
00317         (void)blocks;
00318     }
00319     return found;
00320 }
00321 
00322 
00323 static int
00324 file_system_type_needs_block_special_device( const char *file_system_type)
00325 {
00326     FILE *fp = fopen("/proc/filesystems", "r");
00327     if (!fp)
00328         return -1;
00329     for (;;)
00330     {
00331         char line[100];
00332         char *end;
00333         char *column1;
00334         char *column2;
00335         char *tab;
00336 
00337         if (!fgets(line, sizeof(line), fp))
00338             break;
00339         end = line + strlen(line);
00340         while (end > line && isspace((unsigned char)(end[-1])))
00341             *--end = '\0';
00342         column1 = line;
00343         tab = strchr(line, '\t');
00344         if (tab)
00345         {
00346             *tab++ = 0;
00347         }
00348         column2 = tab;
00349         if (0 == strcmp(column2, file_system_type))
00350         {
00351             fclose(fp);
00352             return (0 != strcmp(column1, "nodev"));
00353         }
00354     }
00355     fclose(fp);
00356     return 0;
00357 }
00358 
00359 
00360 static int
00361 is_a_block_special_device(const char *source)
00362 {
00363     struct stat st;
00364     if (stat(source, &st) < 0)
00365         return -1;
00366     return !!S_ISBLK(st.st_mode);
00367 }
00368 
00369 
00370 void
00371 explain_buffer_errno_mount_explanation(explain_string_buffer_t *sb, int errnum,
00372     const char *syscall_name, const char *source, const char *target,
00373     const char *file_system_type, unsigned long flags, const void *data)
00374 {
00375    explain_final_t source_fc;
00376    explain_final_t target_fc;
00377 
00378    explain_final_init(&source_fc);
00379    source_fc.must_be_a_st_mode = 1;
00380    source_fc.st_mode = S_IFBLK;
00381    source_fc.must_exist = 1;
00382 
00383    explain_final_init(&target_fc);
00384    target_fc.must_be_a_st_mode = 1;
00385    target_fc.st_mode = S_IFDIR;
00386    target_fc.must_exist = 1;
00387 
00388 #ifdef MS_MGC_MSK
00389    if ((flags & MS_MGC_MSK) & MS_MGC_VAL)
00390        flags &= ~MS_MGC_MSK;
00391 #endif
00392 
00393     switch (errnum)
00394     {
00395     case EACCES:
00396         /*
00397          * A component of a path was not searchable.
00398          * See also path_resolution(7).
00399          */
00400         if
00401         (
00402             0 ==
00403             explain_buffer_errno_path_resolution
00404             (
00405                 sb,
00406                 errnum,
00407                 source,
00408                 "source",
00409                 &source_fc
00410             )
00411         )
00412             return;
00413         if
00414         (
00415             0 ==
00416             explain_buffer_errno_path_resolution
00417             (
00418                 sb,
00419                 errnum,
00420                 target,
00421                 "target",
00422                 &target_fc
00423             )
00424         )
00425             return;
00426 
00427         /*
00428          * Or, mounting a read-only file system was attempted without
00429          * giving the MS_RDONLY flag.
00430          */
00431 #ifdef MS_RDONLY
00432         /* FIXME: this is Linux specific, add more systems? */
00433         if (!(flags & MS_RDONLY))
00434         {
00435 #ifdef BLKROGET
00436             int fd = open(source, O_RDONLY, 0);
00437             if (fd >= 0)
00438             {
00439                 if (ioctl(fd, BLKROGET, 0) > 0)
00440                 {
00441                     explain_buffer_gettext
00442                     (
00443                         sb,
00444                         /* FIXME: i18n */
00445                         "mounting a read-only source device was attempted "
00446                         "without giving the MS_RDONLY flag "
00447                     );
00448                     return;
00449                 }
00450             }
00451             close(fd);
00452 #endif
00453         }
00454 #endif
00455 
00456         /*
00457          * Or, the block device source is located on a file system
00458          * mounted with the MS_NODEV option.
00459          */
00460 #ifdef MS_NODEV
00461         /* FIXME: this is Linux specific, add more systems? */
00462         {
00463             struct statvfs info;
00464             if (statvfs(source, &info) >  0)
00465             {
00466                 if (info.f_flag & MS_NODEV)
00467                 {
00468                      explain_buffer_gettext
00469                      (
00470                         sb,
00471                         "the source block device is located on a file "
00472                         "system mounted with the MS_NODEV option"
00473                      );
00474                      if (explain_option_dialect_specific())
00475                      {
00476                         explain_buffer_mount_point(sb, source);
00477                      }
00478                      return;
00479                 }
00480             }
00481         }
00482 #endif
00483 
00484         /* no idea */
00485         explain_buffer_eacces(sb, target, "target", &target_fc);
00486         return;
00487 
00488     case EBUSY:
00489         /*
00490          * source is already mounted
00491          */
00492         if (source_is_already_mounted(source))
00493         {
00494             explain_buffer_ebusy_path(sb, source, "source", syscall_name);
00495             return;
00496         }
00497 
00498         /*
00499          * Or, it cannot be remounted read-only, because it still holds
00500          * files open for writing.
00501          */
00502 #ifdef MS_REMOUNT
00503         {
00504             unsigned long flags2 = MS_RDONLY | MS_REMOUNT;
00505             if ((flags & flags2) == flags2)
00506             {
00507                 explain_buffer_gettext
00508                 (
00509                     sb,
00510                     /* FIXME: i18n */
00511                     "source cannot be remounted read-only, because it "
00512                     "still holds files open for writing"
00513                 );
00514                 return;
00515             }
00516         }
00517 #endif
00518         if (target_is_already_mounted(target))
00519         {
00520             explain_buffer_gettext
00521             (
00522                 sb,
00523                 /* FIXME: i18n */
00524                 "target is already in use as a mount point"
00525             );
00526             /* FIXME: tell th user *which* block device */
00527             return;
00528         }
00529 
00530         /*
00531          * Or,
00532          * it cannot be mounted on target because target is still busy
00533          *    - (it is the working directory of some thread,
00534          *    - the mount point of another device,
00535          *    - has open files, etc.).
00536          */
00537         if (explain_fileinfo_dir_tree_in_use(target))
00538         {
00539             explain_buffer_gettext
00540             (
00541                 sb,
00542                 /* FIXME: i18n */
00543                 "target is still busy with open files or working diretories "
00544             );
00545             return;
00546         }
00547 
00548         /* no idea */
00549         break;
00550 
00551     case EFAULT:
00552         /*
00553          * One of the pointer arguments points outside the user address
00554          * space.
00555          */
00556         if (!source)
00557         {
00558             explain_buffer_is_the_null_pointer(sb, "source");
00559             return;
00560         }
00561         if (explain_is_efault_path(source))
00562         {
00563             explain_buffer_efault(sb, "source");
00564             return;
00565         }
00566 
00567         if (!target)
00568         {
00569             explain_buffer_is_the_null_pointer(sb, "target");
00570             return;
00571         }
00572         if (explain_is_efault_path(target))
00573         {
00574             explain_buffer_efault(sb, "target");
00575             return;
00576         }
00577 
00578         if (!file_system_type)
00579         {
00580             explain_buffer_is_the_null_pointer(sb, "file_system_type");
00581             return;
00582         }
00583         if (explain_is_efault_path(file_system_type))
00584         {
00585             explain_buffer_efault(sb, "file_system_type");
00586             return;
00587         }
00588 
00589         if (!data)
00590         {
00591             explain_buffer_is_the_null_pointer(sb, "data");
00592             return;
00593         }
00594         if (explain_is_efault_path(data))
00595         {
00596             explain_buffer_efault(sb, "data");
00597             return;
00598         }
00599         /* No idea... */
00600         break;
00601 
00602     case EINVAL:
00603         if (!target)
00604         {
00605             explain_buffer_is_the_null_pointer(sb, "target");
00606             return;
00607         }
00608         if (!*target)
00609         {
00610             explain_buffer_gettext
00611             (
00612                 sb,
00613                 "target is the empty string"
00614             );
00615             return;
00616         }
00617 
00618         /*
00619          * Or, a remount (MS_REMOUNT) was attempted, but source was not
00620          * already mounted on target.
00621          */
00622 #ifdef MS_REMOUNT
00623         if ((flags & MS_REMOUNT) && !source_mounted_on_target(source, target))
00624         {
00625             explain_buffer_gettext
00626             (
00627                 sb,
00628                 /* FIXME: i18n */
00629                 "a remount was attempted when source was not already mounted "
00630                 "on target"
00631             );
00632             return;
00633         }
00634 #endif
00635 
00636         /*
00637          * Or, a move (MS_MOVE) was attempted, but source was not a
00638          * mount point, or was '/'.
00639          */
00640 #ifdef HAVE_SYS_MOUNT_MS_MOVE
00641         if (flags & MS_MOVE)
00642         {
00643              if (0 == strcmp(target, "/"))
00644              {
00645                 explain_buffer_gettext
00646                 (
00647                     sb,
00648                     /* FIXME: i18n */
00649                     "it is forbidden to move root"
00650                 );
00651                 return;
00652              }
00653              if (!target_is_already_mounted(target))
00654              {
00655                  explain_buffer_gettext
00656                  (
00657                     sb,
00658                     /* FIXME: i18n */
00659                     "a move was attempted when target was not a mount point"
00660                 );
00661                 return;
00662             }
00663         }
00664 #endif
00665 
00666         /*
00667          * (we have ruled out most everything else)
00668          * Pure guess:
00669          * source had an invalid superblock.
00670          */
00671         explain_buffer_gettext
00672         (
00673             sb,
00674             /* FIXME: i18n */
00675             "source had an invalid superblock"
00676         );
00677         return;
00678 
00679     case ELOOP:
00680         if
00681         (
00682             0 ==
00683             explain_buffer_errno_path_resolution
00684             (
00685                 sb,
00686                 errnum,
00687                 source,
00688                 "source",
00689                 &source_fc
00690             )
00691         )
00692             return;
00693         if
00694         (
00695             0 ==
00696             explain_buffer_errno_path_resolution
00697             (
00698                 sb,
00699                 errnum,
00700                 target,
00701                 "target",
00702                 &target_fc
00703             )
00704         )
00705             return;
00706 
00707         /* No idea... */
00708         break;
00709 
00710 
00711     case EMFILE:
00712         /*
00713          * (In case no block device is required:)
00714          * Table of dummy devices is full.
00715          */
00716         if (is_a_block_special_device(source) == 0)
00717         {
00718             explain_buffer_gettext
00719             (
00720                 sb,
00721                 /* FIXME: i18n */
00722                 "kernel table of dummy devices is full"
00723             );
00724             return;
00725         }
00726 
00727         /* No idea... */
00728         break;
00729 
00730     case ENAMETOOLONG:
00731         /*
00732          * A pathname was longer than MAXPATHLEN.
00733          */
00734         if (0 == explain_buffer_errno_path_resolution(sb, errnum, source,
00735                     "source", &source_fc))
00736             return;
00737         if (0 == explain_buffer_errno_path_resolution(sb, errnum, target,
00738                     "target", &target_fc))
00739             return;
00740         /* No idea... */
00741         break;
00742 
00743     case ENODEV:
00744         explain_buffer_enodev(sb);
00745         break;
00746 
00747     case ENOENT:
00748         /*
00749          * file_system_type not configured in the kernel.
00750          */
00751 
00752         {
00753             char *fuzzy = 0;
00754             if (is_a_known_file_system_type(file_system_type, &fuzzy) == 0)
00755             {
00756                 /* how about a fuzzy match, for access via the command line */
00757                 explain_buffer_gettext
00758                 (
00759                     sb,
00760                     /* FIXME: i18n */
00761                     "the file_system_type is not available in this kernel, "
00762                     "there may be no such file system, or there is, but it "
00763                     "was not compiled into this kernel"
00764                 );
00765                 if (fuzzy)
00766                 {
00767                     char ftext[100];
00768                     explain_string_buffer_t ftext_sb;
00769                     explain_string_buffer_init(&ftext_sb, ftext, sizeof(ftext));
00770                     explain_string_buffer_puts_quoted(&ftext_sb, fuzzy);
00771                     explain_string_buffer_printf_gettext
00772                     (
00773                         sb->footnotes,
00774                         /* FIXME: i18n */
00775                         "did you mean the %s file system type instead?",
00776                         ftext
00777                     );
00778                     explain_string_buffer_puts(sb->footnotes, "; ");
00779                     explain_string_buffer_puts(sb->footnotes, ftext);
00780                 }
00781                 return;
00782             }
00783         }
00784 
00785         if (0 == explain_buffer_errno_path_resolution(sb, errnum, source,
00786                      "source", &source_fc))
00787             return;
00788         if (0 == explain_buffer_errno_path_resolution(sb, errnum, target,
00789                      "target", &target_fc))
00790             return;
00791 
00792         /* No idea... */
00793         break;
00794 
00795     case ENOMEM:
00796         /*
00797          * The kernel could not allocate a free page to copy filenames
00798          * or data into.
00799          */
00800         explain_buffer_enomem_kernel(sb);
00801         return;
00802 
00803     case ENOTBLK:
00804         /*
00805          * source is not a block device (and a device was required).
00806          */
00807         if
00808         (
00809             file_system_type_needs_block_special_device(file_system_type)
00810         &&
00811             is_a_block_special_device(source) == 0
00812         )
00813         {
00814             explain_buffer_gettext
00815             (
00816                 sb,
00817                 "the file_system_type requires that the source must be a "
00818                 "block special device"
00819             );
00820             return;
00821         }
00822 
00823         if
00824         (
00825             file_system_type_needs_block_special_device(file_system_type)
00826         &&
00827             source_is_in_partition_table(source) == 0
00828         )
00829         {
00830             explain_buffer_gettext
00831             (
00832                 sb,
00833                 "the source block special device is not present "
00834                 "in the system partition table "
00835             );
00836             return;
00837         }
00838         explain_buffer_enotblk(sb, source, "source");
00839         return;
00840 
00841     case ENOTDIR:
00842         /*
00843          * target, or a prefix of source, is not a directory.
00844          */
00845         if (explain_buffer_errno_path_resolution(sb, errnum, source,
00846                 "source", &source_fc))
00847             return;
00848         if (explain_buffer_errno_path_resolution(sb, errnum, target,
00849                 "target", &target_fc))
00850             return;
00851 
00852         /* No idea... */
00853         break;
00854 
00855     case ENXIO:
00856         /*
00857          * The minor device number may refer to a non-existent partition
00858          * (it is usually multiplexed with the disk number)
00859          */
00860         if (source_is_in_partition_table(source) == 0)
00861         {
00862             explain_buffer_gettext
00863             (
00864                 sb,
00865                 "the source block special device is not present "
00866                 "in the system partition table "
00867             );
00868             return;
00869         }
00870 
00871         /*
00872          * The major number of the block device source is out of range.
00873          */
00874         explain_buffer_gettext
00875         (
00876             sb,
00877             "the major number of the source block device is out of range"
00878         );
00879         if (explain_option_dialect_specific())
00880         {
00881             /*
00882              * FIXME: print the range (n/n), but just how the heck do we
00883              * find the in-range limit?
00884              */
00885             struct stat st;
00886             if (stat(source, &st) >= 0)
00887             {
00888                 explain_string_buffer_printf(sb, " (%d)", major(st.st_rdev));
00889             }
00890         }
00891         return;
00892 
00893     case EPERM:
00894         /*
00895          * The caller does not have the required privileges.
00896          */
00897         explain_buffer_eperm_cap_sys_admin(sb, syscall_name);
00898         return;
00899 
00900     default:
00901         /* No idea... */
00902         break;
00903     }
00904     explain_buffer_errno_generic(sb, errnum, syscall_name);
00905 }
00906 
00907 
00908 void
00909 explain_buffer_errno_mount(explain_string_buffer_t *sb, int errnum, const char
00910     *source, const char *target, const char *file_systems_type, unsigned long
00911     flags, const void *data)
00912 {
00913     explain_explanation_t exp;
00914 
00915     explain_explanation_init(&exp, errnum);
00916     explain_buffer_errno_mount_system_call(&exp.system_call_sb, errnum, source,
00917         target, file_systems_type, flags, data);
00918     explain_buffer_errno_mount_explanation(&exp.explanation_sb, errnum, "mount",
00919         source, target, file_systems_type, flags, data);
00920     explain_explanation_assemble(&exp, sb);
00921 }
00922 
00923 
00924 /* vim: set ts=8 sw=4 et : */