libexplain
1.4.D001
|
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 published by 00008 * the Free Software Foundation; either version 3 of the License, or (at 00009 * 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/errno.h> 00021 #include <libexplain/ac/sys/param.h> 00022 #include <libexplain/ac/sys/stat.h> 00023 #include <libexplain/ac/unistd.h> 00024 00025 #include <libexplain/buffer/efault.h> 00026 #include <libexplain/buffer/eloop.h> 00027 #include <libexplain/buffer/emlink.h> 00028 #include <libexplain/buffer/enametoolong.h> 00029 #include <libexplain/buffer/enoent.h> 00030 #include <libexplain/buffer/enomem.h> 00031 #include <libexplain/buffer/enospc.h> 00032 #include <libexplain/buffer/enotdir.h> 00033 #include <libexplain/buffer/eperm.h> 00034 #include <libexplain/buffer/erofs.h> 00035 #include <libexplain/buffer/errno/generic.h> 00036 #include <libexplain/buffer/errno/path_resolution.h> 00037 #include <libexplain/buffer/errno/rename.h> 00038 #include <libexplain/buffer/exdev.h> 00039 #include <libexplain/buffer/file_type.h> 00040 #include <libexplain/buffer/gettext.h> 00041 #include <libexplain/buffer/mount_point.h> 00042 #include <libexplain/buffer/note/still_exists.h> 00043 #include <libexplain/buffer/path_to_pid.h> 00044 #include <libexplain/buffer/pathname.h> 00045 #include <libexplain/capability.h> 00046 #include <libexplain/count_directory_entries.h> 00047 #include <libexplain/dirname.h> 00048 #include <libexplain/explanation.h> 00049 #include <libexplain/have_permission.h> 00050 #include <libexplain/option.h> 00051 #include <libexplain/is_efault.h> 00052 #include <libexplain/pathname_is_a_directory.h> 00053 #include <libexplain/string_buffer.h> 00054 00055 00056 static int 00057 get_mode(const char *pathname) 00058 { 00059 struct stat st; 00060 00061 if (stat(pathname, &st) < 0) 00062 return S_IFREG; 00063 return st.st_mode; 00064 } 00065 00066 00067 static void 00068 explain_buffer_errno_rename_system_call(explain_string_buffer_t *sb, 00069 int errnum, const char *oldpath, const char *newpath) 00070 { 00071 (void)errnum; 00072 explain_string_buffer_printf(sb, "rename(oldpath = "); 00073 explain_buffer_pathname(sb, oldpath); 00074 explain_string_buffer_puts(sb, ", newpath = "); 00075 explain_buffer_pathname(sb, newpath); 00076 explain_string_buffer_putc(sb, ')'); 00077 } 00078 00079 00080 static void 00081 dir_vs_not_dir(explain_string_buffer_t *sb, const char *dir_caption, 00082 const char *not_dir_caption, const struct stat *not_dir_st) 00083 { 00084 explain_string_buffer_t ftype_sb; 00085 char ftype[FILE_TYPE_BUFFER_SIZE_MIN]; 00086 00087 explain_string_buffer_init(&ftype_sb, ftype, sizeof(ftype)); 00088 explain_buffer_file_type_st(&ftype_sb, not_dir_st); 00089 explain_string_buffer_printf_gettext 00090 ( 00091 sb, 00092 /* 00093 * xgettext: This message is used to explain an 00094 * EISDIR error reported by a rename(2) system call, 00095 * in the case where there is a file type mismatch. 00096 * 00097 * %1$s => the name of the source system call argument 00098 * %2$s => the name of the destination system call argument 00099 * %3$s => The file type of the destination, 00100 * e.g. "regular file" 00101 */ 00102 i18n("%s is a directory, but %s is a %s, not a directory"), 00103 dir_caption, 00104 not_dir_caption, 00105 ftype 00106 ); 00107 } 00108 00109 00110 static void 00111 dir_vs_not_dir2(explain_string_buffer_t *sb, const char *dir_caption, 00112 const char *not_dir_caption) 00113 { 00114 explain_string_buffer_printf_gettext 00115 ( 00116 sb, 00117 /* 00118 * xgettext: This message is used to explain an 00119 * EISDIR error reported by a rename(2) system call, 00120 * in the case where there is a file type mismatch, 00121 * but the precise file type of oldpath cannot be 00122 * determined. 00123 * 00124 * %1$s => the name of the source system call argument 00125 * %2$s => the name of the destination system call argument 00126 */ 00127 i18n("%s is an existing directory, but %s is not a directory"), 00128 dir_caption, 00129 not_dir_caption 00130 ); 00131 } 00132 00133 00134 static void 00135 explain_buffer_errno_rename_explanation(explain_string_buffer_t *sb, 00136 int errnum, const char *syscall_name, const char *oldpath, 00137 const char *newpath) 00138 { 00139 explain_final_t oldpath_final_component; 00140 explain_final_t newpath_final_component; 00141 00142 explain_final_init(&oldpath_final_component); 00143 oldpath_final_component.want_to_unlink = 1; 00144 oldpath_final_component.st_mode = get_mode(oldpath); 00145 explain_final_init(&newpath_final_component); 00146 newpath_final_component.must_exist = 0; 00147 newpath_final_component.want_to_create = 1; 00148 newpath_final_component.st_mode = oldpath_final_component.st_mode; 00149 00150 switch (errnum) 00151 { 00152 case EACCES: 00153 /* 00154 * Check specific requirements for renaming directories. 00155 * 00156 * Note that this is not the case for Solaris. 00157 * 00158 * FIXME: how do we ask pathconf if we need this test? 00159 * It is possible that this next test does not apply for any file 00160 * system that does not actually store "." and/or ".." in the 00161 * directory data, but generates them at getdent/readdir time. 00162 */ 00163 #ifndef __sun__ 00164 { 00165 struct stat oldpath_st; 00166 00167 /* 00168 * See if we have write permission on the oldpath containing 00169 * directory, so that we can update st_nlink for "." 00170 */ 00171 if 00172 ( 00173 stat(oldpath, &oldpath_st) >= 0 00174 && 00175 S_ISDIR(oldpath_st.st_mode) 00176 && 00177 !explain_have_write_permission 00178 ( 00179 &oldpath_st, 00180 &oldpath_final_component.id 00181 ) 00182 ) 00183 { 00184 explain_string_buffer_printf_gettext 00185 ( 00186 sb, 00187 /* 00188 * xgettext: This message is used to explan an 00189 * EACCES error reported by a rename(2) system 00190 * call. This is the generic explanation given when 00191 * renaming directories when path_resolution(7) is 00192 * unable to provide a more specific explanation. 00193 * 00194 * %1$s => The name of the offending system call argument. 00195 */ 00196 i18n("%s is a directory and does not allow write " 00197 "permission, this is needed to update the \"..\" " 00198 "directory entry"), 00199 "oldpath" 00200 ); 00201 break; 00202 } 00203 } 00204 #endif 00205 00206 /* 00207 * Check the paths themselves 00208 */ 00209 if 00210 ( 00211 explain_buffer_errno_path_resolution 00212 ( 00213 sb, 00214 errnum, 00215 oldpath, 00216 "oldpath", 00217 &oldpath_final_component 00218 ) 00219 && 00220 explain_buffer_errno_path_resolution 00221 ( 00222 sb, 00223 errnum, 00224 newpath, 00225 "newpath", 00226 &newpath_final_component 00227 ) 00228 ) 00229 { 00230 /* 00231 * Unable to find anything specific, give the generic 00232 * explanation. 00233 */ 00234 explain_buffer_gettext 00235 ( 00236 sb, 00237 /* 00238 * xgettext: This message is used to explan an EACCES 00239 * error reported by a rename(2) system call. This is 00240 * the generic explanation given when renaming things 00241 * other than directories when path_resolution(7) is 00242 * unable to provide a more specific explanation. 00243 */ 00244 i18n("write permission is denied for the directory " 00245 "containing oldpath or newpath; or, search permission " 00246 "is denied for one of the directory components of " 00247 "oldpath or newpath") 00248 ); 00249 } 00250 break; 00251 00252 case EBUSY: 00253 if (explain_buffer_path_users(sb, oldpath, "oldpath") > 0) 00254 break; 00255 if (explain_buffer_path_users(sb, newpath, "newpath") > 0) 00256 break; 00257 explain_buffer_gettext 00258 ( 00259 sb, 00260 /* 00261 * xgettext: This message is used to explain an EBUSY error 00262 * reported by a rename(2) system call. This is the generic 00263 * message given when a more specific explanation can not be 00264 * determined. 00265 */ 00266 i18n("oldpath or newpath is a directory that is in use " 00267 "by some process (perhaps as current working directory, " 00268 "or as root directory, or it was open for reading) or is " 00269 "in use by the system (for example as a mount point)") 00270 ); 00271 break; 00272 00273 case EFAULT: 00274 if (explain_is_efault_path(oldpath)) 00275 { 00276 explain_buffer_efault(sb, "oldpath"); 00277 break; 00278 } 00279 if (explain_is_efault_path(newpath)) 00280 { 00281 explain_buffer_efault(sb, "newpath"); 00282 break; 00283 } 00284 explain_buffer_efault(sb, "oldpath or newpath"); 00285 break; 00286 00287 case EINVAL: 00288 explain_string_buffer_printf_gettext 00289 ( 00290 sb, 00291 /* 00292 * xgettext: This message is used to explain an EINVAL error 00293 * reported by a rename(2) system call, in the case where an 00294 * attempt was made to make a directory a subdirectory of 00295 * itself 00296 * 00297 * %1$s => the name of the source system call argument 00298 * %2$s => the name of the destination system call argument 00299 */ 00300 i18n("%s contained a path prefix of %s; or, " 00301 "more generally, an attempt was made to make a directory a " 00302 "subdirectory of itself"), 00303 "newpath", 00304 "oldpath" 00305 ); 00306 break; 00307 00308 case EISDIR: 00309 { 00310 struct stat oldpath_st; 00311 00312 if (lstat(oldpath, &oldpath_st) >= 0) 00313 { 00314 dir_vs_not_dir(sb, "newpath", "oldpath", &oldpath_st); 00315 } 00316 else 00317 { 00318 dir_vs_not_dir2(sb, "newpath", "oldpath"); 00319 } 00320 } 00321 break; 00322 00323 case ELOOP: 00324 explain_buffer_eloop2 00325 ( 00326 sb, 00327 oldpath, 00328 "oldpath", 00329 &oldpath_final_component, 00330 newpath, 00331 "newpath", 00332 &newpath_final_component 00333 ); 00334 break; 00335 00336 case EMLINK: 00337 explain_buffer_emlink(sb, oldpath, newpath); 00338 break; 00339 00340 case ENAMETOOLONG: 00341 explain_buffer_enametoolong2 00342 ( 00343 sb, 00344 oldpath, 00345 "oldpath", 00346 &oldpath_final_component, 00347 newpath, 00348 "newpath", 00349 &newpath_final_component 00350 ); 00351 break; 00352 00353 case ENOENT: 00354 explain_buffer_enoent2 00355 ( 00356 sb, 00357 oldpath, 00358 "oldpath", 00359 &oldpath_final_component, 00360 newpath, 00361 "newpath", 00362 &newpath_final_component 00363 ); 00364 break; 00365 00366 case ENOMEM: 00367 explain_buffer_enomem_kernel(sb); 00368 break; 00369 00370 case ENOSPC: 00371 explain_buffer_enospc(sb, newpath, "newpath"); 00372 break; 00373 00374 case ENOTDIR: 00375 if (explain_pathname_is_a_directory(oldpath)) 00376 { 00377 struct stat newpath_st; 00378 00379 if 00380 ( 00381 stat(newpath, &newpath_st) >= 0 00382 && 00383 !S_ISDIR(newpath_st.st_mode) 00384 ) 00385 { 00386 dir_vs_not_dir(sb, "oldpath", "newpath", &newpath_st); 00387 break; 00388 } 00389 } 00390 00391 explain_buffer_enotdir2 00392 ( 00393 sb, 00394 oldpath, 00395 "oldpath", 00396 &oldpath_final_component, 00397 newpath, 00398 "newpath", 00399 &newpath_final_component 00400 ); 00401 break; 00402 00403 case ENOTEMPTY: 00404 case EEXIST: 00405 { 00406 int count; 00407 00408 explain_string_buffer_printf_gettext 00409 ( 00410 sb, 00411 /* 00412 * xgettext: This message is used to explain and 00413 * ENOTEMPTY or EEXIST error reported by a rename(2) 00414 * system call, in the case where both oldpath and 00415 * newpath are directpries, but newpath is not empty. 00416 * 00417 * %1$s => the name of the offending system call argument 00418 */ 00419 i18n("%s is not an empty directory; that is, it " 00420 "contains entries other than \".\" and \"..\""), 00421 "newpath" 00422 ); 00423 00424 count = explain_count_directory_entries(newpath); 00425 if (count > 0) 00426 explain_string_buffer_printf(sb, " (%d)", count); 00427 } 00428 break; 00429 00430 case EPERM: 00431 if 00432 ( 00433 explain_buffer_errno_path_resolution 00434 ( 00435 sb, 00436 errno, 00437 oldpath, 00438 "oldpath", 00439 &oldpath_final_component 00440 ) 00441 && 00442 explain_buffer_errno_path_resolution 00443 ( 00444 sb, 00445 errno, 00446 newpath, 00447 "newpath", 00448 &newpath_final_component 00449 ) 00450 ) 00451 { 00452 /* FIXME: this needs to be much more specific */ 00453 explain_buffer_eperm_unlink(sb, oldpath, "oldpath", syscall_name); 00454 explain_string_buffer_puts(sb, "; or, "); 00455 explain_buffer_eperm_unlink(sb, newpath, "newpath", syscall_name); 00456 } 00457 break; 00458 00459 case EROFS: 00460 explain_buffer_erofs(sb, newpath, "newpath"); 00461 break; 00462 00463 case EXDEV: 00464 explain_buffer_exdev(sb, oldpath, newpath, syscall_name); 00465 break; 00466 00467 default: 00468 explain_buffer_errno_generic(sb, errnum, syscall_name); 00469 break; 00470 } 00471 00472 /* 00473 * Let the user know where things stand after the failure. 00474 */ 00475 explain_buffer_note_if_still_exists(sb, oldpath, "oldpath"); 00476 explain_buffer_note_if_exists(sb, newpath, "newpath"); 00477 } 00478 00479 00480 void 00481 explain_buffer_errno_rename(explain_string_buffer_t *sb, int errnum, 00482 const char *oldpath, const char *newpath) 00483 { 00484 explain_explanation_t exp; 00485 00486 explain_explanation_init(&exp, errnum); 00487 explain_buffer_errno_rename_system_call 00488 ( 00489 &exp.system_call_sb, 00490 errnum, 00491 oldpath, 00492 newpath 00493 ); 00494 explain_buffer_errno_rename_explanation 00495 ( 00496 &exp.explanation_sb, 00497 errnum, 00498 "rename", 00499 oldpath, 00500 newpath 00501 ); 00502 explain_explanation_assemble(&exp, sb); 00503 } 00504 00505 00506 /* vim: set ts=8 sw=4 et : */