libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 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/string.h> 00022 #include <libexplain/ac/sys/stat.h> 00023 #include <libexplain/ac/unistd.h> 00024 00025 #include <libexplain/buffer/ebadf.h> 00026 #include <libexplain/buffer/eexist.h> 00027 #include <libexplain/buffer/efault.h> 00028 #include <libexplain/buffer/eio.h> 00029 #include <libexplain/buffer/eloop.h> 00030 #include <libexplain/buffer/emlink.h> 00031 #include <libexplain/buffer/enametoolong.h> 00032 #include <libexplain/buffer/enoent.h> 00033 #include <libexplain/buffer/enomem.h> 00034 #include <libexplain/buffer/enospc.h> 00035 #include <libexplain/buffer/enotdir.h> 00036 #include <libexplain/buffer/erofs.h> 00037 #include <libexplain/buffer/errno/generic.h> 00038 #include <libexplain/buffer/errno/linkat.h> 00039 #include <libexplain/buffer/errno/path_resolution.h> 00040 #include <libexplain/buffer/exdev.h> 00041 #include <libexplain/buffer/fildes.h> 00042 #include <libexplain/buffer/pathname.h> 00043 #include <libexplain/buffer/utimensat_flags.h> 00044 #include <libexplain/explanation.h> 00045 #include <libexplain/fildes_is_dot.h> 00046 #include <libexplain/is_efault.h> 00047 00048 00049 static void 00050 explain_buffer_errno_linkat_system_call(explain_string_buffer_t *sb, int errnum, 00051 int old_fildes, const char *old_path, int new_fildes, const char *new_path, 00052 int flags) 00053 { 00054 (void)errnum; 00055 explain_string_buffer_puts(sb, "linkat(old_fildes = "); 00056 explain_buffer_fildes(sb, old_fildes); 00057 explain_string_buffer_puts(sb, ", old_path = "); 00058 explain_buffer_pathname(sb, old_path); 00059 explain_string_buffer_puts(sb, ", new_fildes = "); 00060 explain_buffer_fildes(sb, new_fildes); 00061 explain_string_buffer_puts(sb, ", new_path = "); 00062 explain_buffer_pathname(sb, new_path); 00063 explain_string_buffer_puts(sb, ", flags = "); 00064 explain_buffer_utimensat_flags(sb, flags); 00065 explain_string_buffer_putc(sb, ')'); 00066 } 00067 00068 00069 static int 00070 is_a_directory(int fildes) 00071 { 00072 struct stat st; 00073 00074 if (fildes == AT_FDCWD) 00075 return 1; 00076 if (fstat(fildes, &st) < 0) 00077 return -1; 00078 return !!S_ISDIR(st.st_mode); 00079 } 00080 00081 00082 static int 00083 get_mode_at(int fildes, const char *pathname) 00084 { 00085 struct stat st; 00086 00087 if (fstatat(fildes, pathname, &st, 0) < 0) 00088 return S_IFREG; 00089 return st.st_mode; 00090 } 00091 00092 00093 static void 00094 resolve(int fildes, const char *path, char *full, size_t full_size) 00095 { 00096 explain_string_buffer_t sb; 00097 explain_string_buffer_init(&sb, full, full_size); 00098 if (path[0] == '/' || explain_fildes_is_dot(fildes)) 00099 { 00100 explain_string_buffer_puts(&sb, path); 00101 return; 00102 } 00103 if (getcwd(full, full_size)) 00104 sb.position += strlen(full); 00105 else 00106 explain_string_buffer_putc(&sb, '.'); 00107 explain_string_buffer_path_join(&sb, path); 00108 } 00109 00110 00111 00112 void 00113 explain_buffer_errno_linkat_explanation(explain_string_buffer_t *sb, int errnum, 00114 const char *syscall_name, int old_fildes, const char *old_path, 00115 int new_fildes, const char *new_path, int flags) 00116 { 00117 explain_final_t oldpath_fc; 00118 explain_final_t newpath_fc; 00119 char old_path_full[PATH_MAX]; 00120 char new_path_full[PATH_MAX]; 00121 00122 resolve(old_fildes, old_path, old_path_full, sizeof(old_path_full)); 00123 resolve(new_fildes, new_path, new_path_full, sizeof(new_path_full)); 00124 00125 explain_final_init(&oldpath_fc); 00126 explain_final_init(&newpath_fc); 00127 oldpath_fc.must_exist = 1; 00128 if (flags & AT_SYMLINK_FOLLOW) 00129 oldpath_fc.follow_symlink = 1; 00130 newpath_fc.must_exist = 0; 00131 newpath_fc.want_to_create = 1; 00132 newpath_fc.st_mode = get_mode_at(old_fildes, old_path); 00133 00134 switch (errnum) 00135 { 00136 case EBADF: 00137 if (0 == is_a_directory(old_fildes)) 00138 { 00139 explain_buffer_enotdir_fd(sb, old_fildes, "old_fildes"); 00140 return; 00141 } 00142 if (0 == is_a_directory(new_fildes)) 00143 { 00144 explain_buffer_enotdir_fd(sb, new_fildes, "new_fildes"); 00145 return; 00146 } 00147 /* No idea... */ 00148 break; 00149 00150 case ENOTDIR: 00151 if (!is_a_directory(old_fildes)) 00152 { 00153 explain_buffer_enotdir_fd(sb, old_fildes, "old_fildes"); 00154 return; 00155 } 00156 if (!is_a_directory(new_fildes)) 00157 { 00158 explain_buffer_enotdir_fd(sb, new_fildes, "new_fildes"); 00159 return; 00160 } 00161 00162 explain_buffer_enotdir2 00163 ( 00164 sb, 00165 old_path_full, 00166 "old_path", 00167 &oldpath_fc, 00168 new_path_full, 00169 "new_path", 00170 &newpath_fc 00171 ); 00172 return; 00173 00174 case EACCES: 00175 if 00176 ( 00177 explain_buffer_errno_path_resolution 00178 ( 00179 sb, 00180 errnum, 00181 old_path_full, 00182 "old_path", 00183 &oldpath_fc 00184 ) 00185 && 00186 explain_buffer_errno_path_resolution 00187 ( 00188 sb, 00189 errnum, 00190 new_path_full, 00191 "new_path", 00192 &newpath_fc 00193 ) 00194 ) 00195 00196 /* 00197 * Unable to pin point an exact cause, go with the generic 00198 * explanation. 00199 */ 00200 explain_string_buffer_puts 00201 ( 00202 sb, 00203 "write access to the directory containing newpath is " 00204 "denied, or search permission is denied for one of the " 00205 "directories in the path prefix of oldpath or newpath " 00206 ); 00207 return; 00208 00209 case EEXIST: 00210 explain_buffer_eexist(sb, new_path); 00211 return; 00212 00213 case EFAULT: 00214 if (explain_is_efault_path(old_path)) 00215 { 00216 explain_buffer_efault(sb, "old_path"); 00217 return; 00218 } 00219 if (explain_is_efault_path(new_path)) 00220 { 00221 explain_buffer_efault(sb, "new_path"); 00222 return; 00223 } 00224 explain_buffer_efault(sb, "old_path or new_path"); 00225 return; 00226 00227 case EIO: 00228 explain_buffer_eio_path(sb, old_path); 00229 return; 00230 00231 case ELOOP: 00232 explain_buffer_eloop2 00233 ( 00234 sb, 00235 old_path_full, 00236 "ol_path", 00237 &oldpath_fc, 00238 new_path_full, 00239 "ne_path", 00240 &newpath_fc 00241 ); 00242 return; 00243 00244 case EMLINK: 00245 explain_buffer_emlink(sb, old_path_full, new_path_full); 00246 return; 00247 00248 case ENAMETOOLONG: 00249 explain_buffer_enametoolong2 00250 ( 00251 sb, 00252 old_path_full, 00253 "olds path", 00254 &oldpath_fc, 00255 new_path_full, 00256 "new_path", 00257 &newpath_fc 00258 ); 00259 return; 00260 00261 case ENOENT: 00262 explain_buffer_enoent2 00263 ( 00264 sb, 00265 old_path_full, 00266 "old_path", 00267 &oldpath_fc, 00268 new_path_full, 00269 "new_path", 00270 &newpath_fc 00271 ); 00272 return; 00273 00274 case ENOMEM: 00275 explain_buffer_enomem_kernel(sb); 00276 return; 00277 00278 case ENOSPC: 00279 explain_buffer_enospc(sb, new_path_full, "new_path"); 00280 return; 00281 00282 case EPERM: 00283 { 00284 struct stat oldpath_st; 00285 00286 if (stat(old_path_full, &oldpath_st) >= 0) 00287 { 00288 if (S_ISDIR(oldpath_st.st_mode)) 00289 { 00290 explain_string_buffer_puts 00291 ( 00292 sb, 00293 "old_path is a directory and it is not possible " 00294 "to make hard links to directories" 00295 ); 00296 explain_string_buffer_puts(sb->footnotes, "; "); 00297 explain_string_buffer_puts 00298 ( 00299 sb->footnotes, 00300 "have you considered using a symbolic link?" 00301 ); 00302 } 00303 else 00304 { 00305 explain_string_buffer_puts 00306 ( 00307 sb, 00308 "the file system containing oldpath and newpath " 00309 "does not support the creation of hard links" 00310 ); 00311 } 00312 } 00313 else 00314 { 00315 /* 00316 * Unable to pin point a specific cause, 00317 * issue the generic explanation. 00318 */ 00319 explain_string_buffer_puts 00320 ( 00321 sb, 00322 "old_path is a directory; or, the file system " 00323 "containing old_path and new_path does not support " 00324 "the creation of hard links" 00325 ); 00326 } 00327 return; 00328 } 00329 00330 case EROFS: 00331 explain_buffer_erofs(sb, old_path_full, "old_path"); 00332 return; 00333 00334 case EXDEV: 00335 explain_buffer_exdev(sb, old_path_full, new_path_full, "link"); 00336 return; 00337 00338 default: 00339 /* No idea... */ 00340 break; 00341 } 00342 explain_buffer_errno_generic(sb, errnum, syscall_name); 00343 } 00344 00345 00346 void 00347 explain_buffer_errno_linkat(explain_string_buffer_t *sb, int errnum, 00348 int old_fildes, const char *old_path, int new_fildes, const char *new_path, 00349 int flags) 00350 { 00351 explain_explanation_t exp; 00352 00353 explain_explanation_init(&exp, errnum); 00354 explain_buffer_errno_linkat_system_call 00355 ( 00356 &exp.system_call_sb, 00357 errnum, 00358 old_fildes, 00359 old_path, 00360 new_fildes, 00361 new_path, 00362 flags 00363 ); 00364 explain_buffer_errno_linkat_explanation 00365 ( 00366 &exp.explanation_sb, 00367 errnum, 00368 "linkat", 00369 old_fildes, 00370 old_path, 00371 new_fildes, 00372 new_path, 00373 flags 00374 ); 00375 explain_explanation_assemble(&exp, sb); 00376 } 00377 00378 00379 /* vim: set ts=8 sw=4 et : */