(root)/
tar-1.35/
gnu/
symlinkat.c
       1  /* Create a symlink relative to an open directory.
       2     Copyright (C) 2009-2023 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  /* Specification.  */
      22  #include <unistd.h>
      23  
      24  #include <errno.h>
      25  #include <stdlib.h>
      26  
      27  #if HAVE_SYMLINKAT
      28  # undef symlinkat
      29  
      30  #include <fcntl.h>
      31  #include <sys/stat.h>
      32  #include <string.h>
      33  
      34  /* Create a symlink, but reject trailing slash.  */
      35  int
      36  rpl_symlinkat (char const *contents, int fd, char const *name)
      37  {
      38    size_t len = strlen (name);
      39    if (len && name[len - 1] == '/')
      40      {
      41        struct stat st;
      42        if (fstatat (fd, name, &st, AT_SYMLINK_NOFOLLOW) == 0
      43            || errno == EOVERFLOW)
      44          errno = EEXIST;
      45        return -1;
      46      }
      47    return symlinkat (contents, fd, name);
      48  }
      49  
      50  #elif !HAVE_SYMLINK
      51  /* Mingw lacks symlink, and it is more efficient to provide a trivial
      52     wrapper than to go through at-func.c to call rpl_symlink.  */
      53  
      54  int
      55  symlinkat (_GL_UNUSED char const *path1, _GL_UNUSED int fd,
      56             _GL_UNUSED char const *path2)
      57  {
      58    errno = ENOSYS;
      59    return -1;
      60  }
      61  
      62  #else /* HAVE_SYMLINK */
      63  
      64  /* Our openat helper functions expect the directory parameter first,
      65     not second.  These shims make life easier.  */
      66  
      67  /* Like symlink, but with arguments reversed.  */
      68  static int
      69  symlink_reversed (char const *file, char const *contents)
      70  {
      71    return symlink (contents, file);
      72  }
      73  
      74  /* Like symlinkat, but with arguments reversed.  */
      75  
      76  static int
      77  symlinkat_reversed (int fd, char const *file, char const *contents);
      78  
      79  # define AT_FUNC_NAME symlinkat_reversed
      80  # define AT_FUNC_F1 symlink_reversed
      81  # define AT_FUNC_POST_FILE_PARAM_DECLS , char const *contents
      82  # define AT_FUNC_POST_FILE_ARGS        , contents
      83  # include "at-func.c"
      84  # undef AT_FUNC_NAME
      85  # undef AT_FUNC_F1
      86  # undef AT_FUNC_POST_FILE_PARAM_DECLS
      87  # undef AT_FUNC_POST_FILE_ARGS
      88  
      89  /* Create a symlink FILE, in the directory open on descriptor FD,
      90     holding CONTENTS.  If possible, do it without changing the
      91     working directory.  Otherwise, resort to using save_cwd/fchdir,
      92     then symlink/restore_cwd.  If either the save_cwd or the restore_cwd
      93     fails, then give a diagnostic and exit nonzero.  */
      94  
      95  int
      96  symlinkat (char const *contents, int fd, char const *file)
      97  {
      98    return symlinkat_reversed (fd, file, contents);
      99  }
     100  
     101  #endif /* HAVE_SYMLINK */