libexplain
1.4.D001
|
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 : */