libexplain  1.4.D001
libexplain/buffer/errno/linkat.c
Go to the documentation of this file.
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 : */