root/maint/gnulib/lib/linkat.c

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

DEFINITIONS

This source file includes following definitions.
  1. link_immediate
  2. link_follow
  3. solaris_optimized_link_immediate
  4. solaris_optimized_link_follow
  5. linkat
  6. linkat_follow
  7. rpl_linkat

   1 /* Create a hard link relative to open directories.
   2    Copyright (C) 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 Eric Blake */
  18 
  19 #include <config.h>
  20 
  21 #include <unistd.h>
  22 
  23 #include <errno.h>
  24 #include <fcntl.h>
  25 #include <limits.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 #include <sys/stat.h>
  29 
  30 #include "areadlink.h"
  31 #include "dirname.h"
  32 #include "eloop-threshold.h"
  33 #include "filenamecat.h"
  34 #include "openat-priv.h"
  35 
  36 #if !HAVE_LINKAT || LINKAT_SYMLINK_NOTSUP
  37 
  38 /* Create a link.  If FILE1 is a symlink, either create a hardlink to
  39    that symlink, or fake it by creating an identical symlink.  */
  40 # if LINK_FOLLOWS_SYMLINKS == 0
  41 #  define link_immediate link
  42 # else
  43 static int
  44 link_immediate (char const *file1, char const *file2)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46   char *target = areadlink (file1);
  47   if (target)
  48     {
  49       /* A symlink cannot be modified in-place.  Therefore, creating
  50          an identical symlink behaves like a hard link to a symlink,
  51          except for incorrect st_ino and st_nlink.  However, we must
  52          be careful of EXDEV.  */
  53       struct stat st1;
  54       struct stat st2;
  55       char *dir = mdir_name (file2);
  56       if (!dir)
  57         {
  58           free (target);
  59           errno = ENOMEM;
  60           return -1;
  61         }
  62       if (lstat (file1, &st1) == 0 && stat (dir, &st2) == 0)
  63         {
  64           if (st1.st_dev == st2.st_dev)
  65             {
  66               int result = symlink (target, file2);
  67               free (target);
  68               free (dir);
  69               return result;
  70             }
  71           free (target);
  72           free (dir);
  73           errno = EXDEV;
  74           return -1;
  75         }
  76       free (target);
  77       free (dir);
  78     }
  79   if (errno == ENOMEM)
  80     return -1;
  81   return link (file1, file2);
  82 }
  83 # endif /* LINK_FOLLOWS_SYMLINKS == 0 */
  84 
  85 /* Create a link.  If FILE1 is a symlink, create a hardlink to the
  86    canonicalized file.  */
  87 # if 0 < LINK_FOLLOWS_SYMLINKS
  88 #  define link_follow link
  89 # else
  90 static int
  91 link_follow (char const *file1, char const *file2)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93   char *name = (char *) file1;
  94   char *target;
  95   int result;
  96   int i = __eloop_threshold ();
  97 
  98   /* Using realpath or canonicalize_file_name is too heavy-handed: we
  99      don't need an absolute name, and we don't need to resolve
 100      intermediate symlinks, just the basename of each iteration.  */
 101   while (i-- && (target = areadlink (name)))
 102     {
 103       if (IS_ABSOLUTE_FILE_NAME (target))
 104         {
 105           if (name != file1)
 106             free (name);
 107           name = target;
 108         }
 109       else
 110         {
 111           char *dir = mdir_name (name);
 112           if (name != file1)
 113             free (name);
 114           if (!dir)
 115             {
 116               free (target);
 117               errno = ENOMEM;
 118               return -1;
 119             }
 120           name = mfile_name_concat (dir, target, NULL);
 121           free (dir);
 122           free (target);
 123           if (!name)
 124             {
 125               errno = ENOMEM;
 126               return -1;
 127             }
 128         }
 129     }
 130   if (i < 0)
 131     {
 132       target = NULL;
 133       errno = ELOOP;
 134     }
 135   if (!target && errno != EINVAL)
 136     {
 137       if (name != file1)
 138         free (name);
 139       return -1;
 140     }
 141   result = link (name, file2);
 142   if (name != file1)
 143     free (name);
 144   return result;
 145 }
 146 # endif /* 0 < LINK_FOLLOWS_SYMLINKS */
 147 
 148 /* On Solaris, link() doesn't follow symlinks by default, but does so as soon
 149    as a library or executable takes part in the program that has been compiled
 150    with "c99" or "cc -xc99=all" or "cc ... /usr/lib/values-xpg4.o ...".  */
 151 # if LINK_FOLLOWS_SYMLINKS == -1
 152 
 153 /* Reduce the penalty of link_immediate and link_follow by incorporating the
 154    knowledge that link()'s behaviour depends on the __xpg4 variable.  */
 155 extern int __xpg4;
 156 
 157 static int
 158 solaris_optimized_link_immediate (char const *file1, char const *file2)
     /* [previous][next][first][last][top][bottom][index][help] */
 159 {
 160   if (__xpg4 == 0)
 161     return link (file1, file2);
 162   return link_immediate (file1, file2);
 163 }
 164 
 165 static int
 166 solaris_optimized_link_follow (char const *file1, char const *file2)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168   if (__xpg4 != 0)
 169     return link (file1, file2);
 170   return link_follow (file1, file2);
 171 }
 172 
 173 #  define link_immediate solaris_optimized_link_immediate
 174 #  define link_follow solaris_optimized_link_follow
 175 
 176 # endif
 177 
 178 #endif /* !HAVE_LINKAT || LINKAT_SYMLINK_NOTSUP  */
 179 
 180 #if !HAVE_LINKAT
 181 
 182 /* Create a link to FILE1, in the directory open on descriptor FD1, to FILE2,
 183    in the directory open on descriptor FD2.  If FILE1 is a symlink, FLAG
 184    controls whether to dereference FILE1 first.  If possible, do it without
 185    changing the working directory.  Otherwise, resort to using
 186    save_cwd/fchdir, then rename/restore_cwd.  If either the save_cwd or
 187    the restore_cwd fails, then give a diagnostic and exit nonzero.  */
 188 
 189 int
 190 linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
     /* [previous][next][first][last][top][bottom][index][help] */
 191 {
 192   if (flag & ~AT_SYMLINK_FOLLOW)
 193     {
 194       errno = EINVAL;
 195       return -1;
 196     }
 197   return at_func2 (fd1, file1, fd2, file2,
 198                    flag ? link_follow : link_immediate);
 199 }
 200 
 201 #else /* HAVE_LINKAT */
 202 
 203 # undef linkat
 204 
 205 /* Create a link.  If FILE1 is a symlink, create a hardlink to the
 206    canonicalized file.  */
 207 
 208 static int
 209 linkat_follow (int fd1, char const *file1, int fd2, char const *file2)
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211   char *name = (char *) file1;
 212   char *target;
 213   int result;
 214   int i = __eloop_threshold ();
 215 
 216   /* There is no realpathat.  */
 217   while (i-- && (target = areadlinkat (fd1, name)))
 218     {
 219       if (IS_ABSOLUTE_FILE_NAME (target))
 220         {
 221           if (name != file1)
 222             free (name);
 223           name = target;
 224         }
 225       else
 226         {
 227           char *dir = mdir_name (name);
 228           if (name != file1)
 229             free (name);
 230           if (!dir)
 231             {
 232               free (target);
 233               errno = ENOMEM;
 234               return -1;
 235             }
 236           name = mfile_name_concat (dir, target, NULL);
 237           free (dir);
 238           free (target);
 239           if (!name)
 240             {
 241               errno = ENOMEM;
 242               return -1;
 243             }
 244         }
 245     }
 246   if (i < 0)
 247     {
 248       target = NULL;
 249       errno = ELOOP;
 250     }
 251   if (!target && errno != EINVAL)
 252     {
 253       if (name != file1)
 254         free (name);
 255       return -1;
 256     }
 257   result = linkat (fd1, name, fd2, file2, 0);
 258   if (name != file1)
 259     free (name);
 260   return result;
 261 }
 262 
 263 
 264 /* Like linkat, but guarantee that AT_SYMLINK_FOLLOW works even on
 265    older Linux kernels.  */
 266 
 267 int
 268 rpl_linkat (int fd1, char const *file1, int fd2, char const *file2, int flag)
     /* [previous][next][first][last][top][bottom][index][help] */
 269 {
 270   if (flag & ~AT_SYMLINK_FOLLOW)
 271     {
 272       errno = EINVAL;
 273       return -1;
 274     }
 275 
 276 # if LINKAT_TRAILING_SLASH_BUG
 277   /* Reject trailing slashes on non-directories.  */
 278   {
 279     size_t len1 = strlen (file1);
 280     size_t len2 = strlen (file2);
 281     if ((len1 && file1[len1 - 1] == '/')
 282         || (len2 && file2[len2 - 1] == '/'))
 283       {
 284         /* Let linkat() decide whether hard-linking directories is legal.
 285            If fstatat() fails, then linkat() should fail for the same reason;
 286            if fstatat() succeeds, require a directory.  */
 287         struct stat st;
 288         if (fstatat (fd1, file1, &st, flag ? 0 : AT_SYMLINK_NOFOLLOW))
 289           return -1;
 290         if (!S_ISDIR (st.st_mode))
 291           {
 292             errno = ENOTDIR;
 293             return -1;
 294           }
 295       }
 296   }
 297 # endif
 298 
 299   if (!flag)
 300     {
 301       int result = linkat (fd1, file1, fd2, file2, flag);
 302 # if LINKAT_SYMLINK_NOTSUP
 303       /* OS X 10.10 has linkat() but it doesn't support
 304          hardlinks to symlinks.  Fallback to our emulation
 305          in that case.  */
 306       if (result == -1 && (errno == ENOTSUP || errno == EOPNOTSUPP))
 307         return at_func2 (fd1, file1, fd2, file2, link_immediate);
 308 # endif
 309       return result;
 310     }
 311 
 312   /* Cache the information on whether the system call really works.  */
 313   {
 314     static int have_follow_really; /* 0 = unknown, 1 = yes, -1 = no */
 315     if (0 <= have_follow_really)
 316     {
 317       int result = linkat (fd1, file1, fd2, file2, flag);
 318       if (!(result == -1 && errno == EINVAL))
 319         {
 320           have_follow_really = 1;
 321           return result;
 322         }
 323       have_follow_really = -1;
 324     }
 325   }
 326   return linkat_follow (fd1, file1, fd2, file2);
 327 }
 328 
 329 #endif /* HAVE_LINKAT */

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