libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2008-2011, 2013 Peter Miller 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU Lesser General Public License as 00007 * published by the Free Software Foundation; either version 3 of the 00008 * License, or (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser 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/assert.h> 00020 #include <libexplain/ac/dirent.h> 00021 #include <libexplain/ac/errno.h> 00022 #include <libexplain/ac/limits.h> /* for PATH_MAX on Solaris */ 00023 #include <libexplain/ac/stdlib.h> 00024 #include <libexplain/ac/string.h> 00025 #include <libexplain/ac/sys/param.h> /* for PATH_MAX except Solaris */ 00026 #include <libexplain/ac/sys/stat.h> 00027 00028 #include <libexplain/buffer/get_current_directory.h> 00029 #include <libexplain/buffer/gettext.h> 00030 #include <libexplain/buffer/errno/fstat.h> 00031 #include <libexplain/buffer/errno/stat.h> 00032 #include <libexplain/buffer/errno/path_resolution.h> 00033 #include <libexplain/fileinfo.h> 00034 #include <libexplain/is_same_inode.h> 00035 #include <libexplain/name_max.h> 00036 00037 00055 static int 00056 recursive_pwd(explain_string_buffer_t *sb, explain_string_buffer_t *dot, 00057 const struct stat *dot_st, explain_string_buffer_t *result) 00058 { 00059 DIR *dp; 00060 struct stat dotdot_st; 00061 00062 explain_string_buffer_puts(dot, "/.."); 00063 dp = opendir(dot->message); 00064 if (!dp) 00065 { 00066 int errnum; 00067 explain_final_t final_component; 00068 int ok; 00069 00070 errnum = errno; 00071 explain_final_init(&final_component); 00072 final_component.want_to_read = 1; 00073 final_component.must_be_a_st_mode = 1; 00074 final_component.st_mode = S_IFDIR; 00075 ok = 00076 explain_buffer_errno_path_resolution 00077 ( 00078 sb, 00079 errnum, 00080 dot->message, 00081 "pathname", 00082 &final_component 00083 ); 00084 return (ok ? 0 : -1); 00085 } 00086 #ifdef HAVE_DIRFD 00087 if (fstat(dirfd(dp), &dotdot_st) < 0) 00088 { 00089 explain_buffer_errno_fstat_explanation 00090 ( 00091 sb, 00092 errno, 00093 "fstat", 00094 dirfd(dp), 00095 &dotdot_st 00096 ); 00097 closedir(dp); 00098 return -1; 00099 } 00100 #else 00101 if (stat(dot->message, &dotdot_st) < 0) 00102 { 00103 explain_buffer_errno_stat_explanation 00104 ( 00105 sb, 00106 errno, 00107 "stat", 00108 dot->message, 00109 &dotdot_st 00110 ); 00111 closedir(dp); 00112 return -1; 00113 } 00114 #endif 00115 if (explain_is_same_inode(dot_st, &dotdot_st)) 00116 { 00117 closedir(dp); 00118 explain_string_buffer_putc(result, '/'); 00119 return 0; 00120 } 00121 for (;;) 00122 { 00123 size_t old_pos; 00124 struct dirent *dep; 00125 struct stat dirent_st; 00126 00127 dep = readdir(dp); 00128 if (!dep) 00129 break; 00130 if (0 == strcmp(dep->d_name, ".")) 00131 continue; 00132 if (0 == strcmp(dep->d_name, "..")) 00133 continue; 00134 old_pos = dot->position; 00135 explain_string_buffer_path_join(dot, dep->d_name); 00136 if (lstat(dot->message, &dirent_st) < 0) 00137 { 00138 int errnum; 00139 explain_final_t final_component; 00140 int ok; 00141 00142 errnum = errno; 00143 explain_final_init(&final_component); 00144 ok = 00145 explain_buffer_errno_path_resolution 00146 ( 00147 sb, 00148 errnum, 00149 dot->message, 00150 "pathname", 00151 &final_component 00152 ); 00153 closedir(dp); 00154 return (ok ? 0 : -1); 00155 } 00156 explain_string_buffer_truncate(dot, old_pos); 00157 if (explain_is_same_inode(dot_st, &dirent_st)) 00158 { 00159 char name[NAME_MAX + 1]; 00160 00161 explain_strendcpy(name, dep->d_name, name + sizeof(name)); 00162 closedir(dp); 00163 /* 00164 * Now we recurse up the directory tree until we find the 00165 * root. This is mostly tail recursion, but factoring that 00166 * out makes the code ugly, let's hope the compiler notices 00167 * and does something clever. 00168 */ 00169 if (recursive_pwd(sb, dot, &dotdot_st, result) < 0) 00170 { 00171 return -1; 00172 } 00173 explain_string_buffer_path_join(result, name); 00174 return 0; 00175 } 00176 } 00177 closedir(dp); 00178 00179 /* 00180 * No such directory. 00181 */ 00182 { 00183 explain_string_buffer_t msg1_sb; 00184 explain_string_buffer_t msg2_sb; 00185 char msg1[PATH_MAX + 1]; 00186 char msg2[PATH_MAX + 5]; 00187 00188 explain_string_buffer_init(&msg1_sb, msg1, sizeof(msg1)); 00189 explain_string_buffer_puts(&msg1_sb, dot->message); 00190 explain_string_buffer_puts(&msg1_sb, "/.."); 00191 00192 explain_string_buffer_init(&msg2_sb, msg2, sizeof(msg2)); 00193 explain_string_buffer_puts_quoted(&msg2_sb, msg1); 00194 00195 explain_string_buffer_init(&msg1_sb, msg1, sizeof(msg1)); 00196 explain_string_buffer_puts_quoted(&msg1_sb, dot->message); 00197 00198 explain_string_buffer_printf_gettext 00199 ( 00200 sb, 00201 /* 00202 * xgettext: This message is used when getcwd() is trying 00203 * to reconstruct the problem, and discovers that the 00204 * backwards ".." chain is broken. 00205 */ 00206 i18n("there is no directory entry in %s that has the same " 00207 "inode number as %s; this means that the directory has " 00208 "been unlinked"), 00209 msg2, 00210 msg1 00211 ); 00212 if (0 == strcmp(result->message, "/") && dot_st->st_ino != 2) 00213 { 00214 explain_string_buffer_puts(sb, ", "); 00215 explain_buffer_gettext 00216 ( 00217 sb, 00218 /* 00219 * xgettext: This message is used when getcwd() is 00220 * trying to reconstruct the problem, and discovers that 00221 * the process is probably running inside a chroot jail, 00222 * and that the current directory is actually ouside 00223 * that chroot jail. 00224 */ 00225 i18n("or is outside your chroot jail") 00226 ); 00227 } 00228 } 00229 return -1; 00230 } 00231 00232 00233 int 00234 explain_buffer_get_current_directory(explain_string_buffer_t *sb, 00235 char *data, size_t data_size) 00236 { 00237 assert(data_size > PATH_MAX); 00238 00239 /* 00240 * The first thing to try is the PWD environment variable, which is 00241 * maintained by many of the shell interpreters. 00242 */ 00243 { 00244 const char *pwd; 00245 00246 pwd = getenv("PWD"); 00247 if (pwd && pwd[0] == '/') 00248 { 00249 struct stat dot_st; 00250 struct stat pwd_st; 00251 00252 if 00253 ( 00254 lstat(".", &dot_st) >= 0 00255 && 00256 lstat(pwd, &pwd_st) >= 0 00257 && 00258 explain_is_same_inode(&dot_st, &pwd_st) 00259 ) 00260 { 00261 explain_strendcpy(data, pwd, data + data_size); 00262 return 0; 00263 } 00264 } 00265 } 00266 00267 /* 00268 * The next thing to try is the /proc file system. 00269 */ 00270 if (explain_fileinfo_self_cwd(data, data_size)) 00271 { 00272 return 0; 00273 } 00274 00275 /* 00276 * If all else fails, do it the slow way. 00277 */ 00278 { 00279 explain_string_buffer_t dot_sb; 00280 explain_string_buffer_t result_sb; 00281 struct stat dot_st; 00282 char dot[PATH_MAX * 2 + 1]; 00283 00284 if (lstat(".", &dot_st) < 0) 00285 { 00286 int errnum; 00287 explain_final_t final_component; 00288 int ok; 00289 00290 errnum = errno; 00291 explain_final_init(&final_component); 00292 final_component.must_be_a_st_mode = 1; 00293 final_component.st_mode = S_IFDIR; 00294 ok = 00295 explain_buffer_errno_path_resolution 00296 ( 00297 sb, 00298 errnum, 00299 ".", 00300 "pathname", 00301 &final_component 00302 ); 00303 return (ok ? 0 : -1); 00304 } 00305 explain_string_buffer_init(&dot_sb, dot, sizeof(dot)); 00306 explain_string_buffer_putc(&dot_sb, '.'); 00307 explain_string_buffer_init(&result_sb, data, data_size); 00308 return recursive_pwd(sb, &dot_sb, &dot_st, &result_sb); 00309 } 00310 } 00311 00312 00313 /* vim: set ts=8 sw=4 et : */