root/maint/gnulib/lib/at-func2.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. at_func2

   1 /* Define 2-FD at-style functions like linkat or renameat.
   2    Copyright (C) 2006, 2009-2021 Free Software Foundation, Inc.
   3 
   4    This program is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU General Public License as published by
   6    the Free Software Foundation; either version 3 of the License, or
   7    (at your option) any later version.
   8 
   9    This program is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU General Public License for more details.
  13 
  14    You should have received a copy of the GNU General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* written by Jim Meyering and Eric Blake */
  18 
  19 #include <config.h>
  20 
  21 #include "openat-priv.h"
  22 
  23 #include <errno.h>
  24 #include <stdlib.h>
  25 #include <string.h>
  26 #include <unistd.h>
  27 
  28 #include "filename.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
  29 #include "filenamecat.h"
  30 #include "openat.h"
  31 #include "same-inode.h"
  32 #include "save-cwd.h"
  33 
  34 /* Call FUNC to operate on a pair of files, where FILE1 is relative to FD1,
  35    and FILE2 is relative to FD2.  If possible, do it without changing the
  36    working directory.  Otherwise, resort to using save_cwd/fchdir,
  37    FUNC, restore_cwd (up to two times).  If either the save_cwd or the
  38    restore_cwd fails, then give a diagnostic and exit nonzero.  */
  39 int
  40 at_func2 (int fd1, char const *file1,
     /* [previous][next][first][last][top][bottom][index][help] */
  41           int fd2, char const *file2,
  42           int (*func) (char const *file1, char const *file2))
  43 {
  44   struct saved_cwd saved_cwd;
  45   int saved_errno;
  46   int err;
  47   char *file1_alt;
  48   char *file2_alt;
  49   struct stat st1;
  50   struct stat st2;
  51 
  52   /* There are 16 possible scenarios, based on whether an fd is
  53      AT_FDCWD or real, and whether a file is absolute or relative:
  54 
  55          fd1  file1 fd2  file2  action
  56      0   cwd  abs   cwd  abs    direct call
  57      1   cwd  abs   cwd  rel    direct call
  58      2   cwd  abs   fd   abs    direct call
  59      3   cwd  abs   fd   rel    chdir to fd2
  60      4   cwd  rel   cwd  abs    direct call
  61      5   cwd  rel   cwd  rel    direct call
  62      6   cwd  rel   fd   abs    direct call
  63      7   cwd  rel   fd   rel    convert file1 to abs, then case 3
  64      8   fd   abs   cwd  abs    direct call
  65      9   fd   abs   cwd  rel    direct call
  66      10  fd   abs   fd   abs    direct call
  67      11  fd   abs   fd   rel    chdir to fd2
  68      12  fd   rel   cwd  abs    chdir to fd1
  69      13  fd   rel   cwd  rel    convert file2 to abs, then case 12
  70      14  fd   rel   fd   abs    chdir to fd1
  71      15a fd1  rel   fd1  rel    chdir to fd1
  72      15b fd1  rel   fd2  rel    chdir to fd1, then case 7
  73 
  74      Try some optimizations to reduce fd to AT_FDCWD, or to at least
  75      avoid converting an absolute name or doing a double chdir.  */
  76 
  77   if ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1))
  78       && (fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2)))
  79     return func (file1, file2); /* Case 0-2, 4-6, 8-10.  */
  80 
  81   /* If /proc/self/fd works, we don't need any stat or chdir.  */
  82   {
  83     char proc_buf1[OPENAT_BUFFER_SIZE];
  84     char *proc_file1 = ((fd1 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file1))
  85                         ? (char *) file1
  86                         : openat_proc_name (proc_buf1, fd1, file1));
  87     if (proc_file1)
  88       {
  89         char proc_buf2[OPENAT_BUFFER_SIZE];
  90         char *proc_file2 = ((fd2 == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file2))
  91                             ? (char *) file2
  92                             : openat_proc_name (proc_buf2, fd2, file2));
  93         if (proc_file2)
  94           {
  95             int proc_result = func (proc_file1, proc_file2);
  96             int proc_errno = errno;
  97             if (proc_file1 != proc_buf1 && proc_file1 != file1)
  98               free (proc_file1);
  99             if (proc_file2 != proc_buf2 && proc_file2 != file2)
 100               free (proc_file2);
 101             /* If the syscall succeeds, or if it fails with an unexpected
 102                errno value, then return right away.  Otherwise, fall through
 103                and resort to using save_cwd/restore_cwd.  */
 104             if (0 <= proc_result)
 105               return proc_result;
 106             if (! EXPECTED_ERRNO (proc_errno))
 107               {
 108                 errno = proc_errno;
 109                 return proc_result;
 110               }
 111           }
 112         else if (proc_file1 != proc_buf1 && proc_file1 != file1)
 113           free (proc_file1);
 114       }
 115   }
 116 
 117   /* Cases 3, 7, 11-15 remain.  Time to normalize directory fds, if
 118      possible.  */
 119   if (IS_ABSOLUTE_FILE_NAME (file1))
 120     fd1 = AT_FDCWD; /* Case 11 reduced to 3.  */
 121   else if (IS_ABSOLUTE_FILE_NAME (file2))
 122     fd2 = AT_FDCWD; /* Case 14 reduced to 12.  */
 123 
 124   /* Cases 3, 7, 12, 13, 15 remain.  */
 125 
 126   if (fd1 == AT_FDCWD) /* Cases 3, 7.  */
 127     {
 128       if (stat (".", &st1) == -1 || fstat (fd2, &st2) == -1)
 129         return -1;
 130       if (!S_ISDIR (st2.st_mode))
 131         {
 132           errno = ENOTDIR;
 133           return -1;
 134         }
 135       if (SAME_INODE (st1, st2)) /* Reduced to cases 1, 5.  */
 136         return func (file1, file2);
 137     }
 138   else if (fd2 == AT_FDCWD) /* Cases 12, 13.  */
 139     {
 140       if (stat (".", &st2) == -1 || fstat (fd1, &st1) == -1)
 141         return -1;
 142       if (!S_ISDIR (st1.st_mode))
 143         {
 144           errno = ENOTDIR;
 145           return -1;
 146         }
 147       if (SAME_INODE (st1, st2)) /* Reduced to cases 4, 5.  */
 148         return func (file1, file2);
 149     }
 150   else if (fd1 != fd2) /* Case 15b.  */
 151     {
 152       if (fstat (fd1, &st1) == -1 || fstat (fd2, &st2) == -1)
 153         return -1;
 154       if (!S_ISDIR (st1.st_mode) || !S_ISDIR (st2.st_mode))
 155         {
 156           errno = ENOTDIR;
 157           return -1;
 158         }
 159       if (SAME_INODE (st1, st2)) /* Reduced to case 15a.  */
 160         {
 161           fd2 = fd1;
 162           if (stat (".", &st1) == 0 && SAME_INODE (st1, st2))
 163             return func (file1, file2); /* Further reduced to case 5.  */
 164         }
 165     }
 166   else /* Case 15a.  */
 167     {
 168       if (fstat (fd1, &st1) == -1)
 169         return -1;
 170       if (!S_ISDIR (st1.st_mode))
 171         {
 172           errno = ENOTDIR;
 173           return -1;
 174         }
 175       if (stat (".", &st2) == 0 && SAME_INODE (st1, st2))
 176         return func (file1, file2); /* Reduced to case 5.  */
 177     }
 178 
 179   /* Catch invalid arguments before changing directories.  */
 180   if (file1[0] == '\0' || file2[0] == '\0')
 181     {
 182       errno = ENOENT;
 183       return -1;
 184     }
 185 
 186   /* Cases 3, 7, 12, 13, 15a, 15b remain.  With all reductions in
 187      place, it is time to start changing directories.  */
 188 
 189   if (save_cwd (&saved_cwd) != 0)
 190     openat_save_fail (errno);
 191 
 192   if (fd1 != AT_FDCWD && fd2 != AT_FDCWD && fd1 != fd2) /* Case 15b.  */
 193     {
 194       if (fchdir (fd1) != 0)
 195         {
 196           saved_errno = errno;
 197           free_cwd (&saved_cwd);
 198           errno = saved_errno;
 199           return -1;
 200         }
 201       fd1 = AT_FDCWD; /* Reduced to case 7.  */
 202     }
 203 
 204   /* Cases 3, 7, 12, 13, 15a remain.  Convert one relative name to
 205      absolute, if necessary.  */
 206 
 207   file1_alt = (char *) file1;
 208   file2_alt = (char *) file2;
 209 
 210   if (fd1 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file1)) /* Case 7.  */
 211     {
 212       /* It would be nicer to use:
 213          file1_alt = file_name_concat (xgetcwd (), file1, NULL);
 214          but libraries should not call xalloc_die.  */
 215       char *cwd = getcwd (NULL, 0);
 216       if (!cwd)
 217         {
 218           saved_errno = errno;
 219           free_cwd (&saved_cwd);
 220           errno = saved_errno;
 221           return -1;
 222         }
 223       file1_alt = mfile_name_concat (cwd, file1, NULL);
 224       if (!file1_alt)
 225         {
 226           saved_errno = errno;
 227           free (cwd);
 228           free_cwd (&saved_cwd);
 229           errno = saved_errno;
 230           return -1;
 231         }
 232       free (cwd); /* Reduced to case 3.  */
 233     }
 234   else if (fd2 == AT_FDCWD && !IS_ABSOLUTE_FILE_NAME (file2)) /* Case 13.  */
 235     {
 236       char *cwd = getcwd (NULL, 0);
 237       if (!cwd)
 238         {
 239           saved_errno = errno;
 240           free_cwd (&saved_cwd);
 241           errno = saved_errno;
 242           return -1;
 243         }
 244       file2_alt = mfile_name_concat (cwd, file2, NULL);
 245       if (!file2_alt)
 246         {
 247           saved_errno = errno;
 248           free (cwd);
 249           free_cwd (&saved_cwd);
 250           errno = saved_errno;
 251           return -1;
 252         }
 253       free (cwd); /* Reduced to case 12.  */
 254     }
 255 
 256   /* Cases 3, 12, 15a remain.  Change to the correct directory.  */
 257   if (fchdir (fd1 == AT_FDCWD ? fd2 : fd1) != 0)
 258     {
 259       saved_errno = errno;
 260       free_cwd (&saved_cwd);
 261       if (file1 != file1_alt)
 262         free (file1_alt);
 263       else if (file2 != file2_alt)
 264         free (file2_alt);
 265       errno = saved_errno;
 266       return -1;
 267     }
 268 
 269   /* Finally safe to perform the user's function, then clean up.  */
 270 
 271   err = func (file1_alt, file2_alt);
 272   saved_errno = (err < 0 ? errno : 0);
 273 
 274   if (file1 != file1_alt)
 275     free (file1_alt);
 276   else if (file2 != file2_alt)
 277     free (file2_alt);
 278 
 279   if (restore_cwd (&saved_cwd) != 0)
 280     openat_restore_fail (errno);
 281 
 282   free_cwd (&saved_cwd);
 283 
 284   if (saved_errno)
 285     errno = saved_errno;
 286   return err;
 287 }
 288 #undef CALL_FUNC
 289 #undef FUNC_RESULT

/* [previous][next][first][last][top][bottom][index][help] */