(root)/
tar-1.35/
gnu/
openat-proc.c
       1  /* Create /proc/self/fd-related names for subfiles of open directories.
       2  
       3     Copyright (C) 2006, 2009-2023 Free Software Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Written by Paul Eggert.  */
      19  
      20  #include <config.h>
      21  
      22  #include "openat-priv.h"
      23  
      24  #include <sys/types.h>
      25  #include <sys/stat.h>
      26  #include <fcntl.h>
      27  
      28  #include <stdio.h>
      29  #include <stdlib.h>
      30  #include <string.h>
      31  #include <unistd.h>
      32  
      33  #ifdef __KLIBC__ /* OS/2 */
      34  # include <InnoTekLIBC/backend.h>
      35  #endif
      36  #ifdef __MVS__ /* z/OS */
      37  # include <termios.h>
      38  #endif
      39  
      40  #include "intprops.h"
      41  
      42  /* Set BUF to the name of the subfile of the directory identified by
      43     FD, where the subfile is named FILE.  If successful, return BUF if
      44     the result fits in BUF, dynamically allocated memory otherwise.
      45     Return NULL (setting errno) on error.  */
      46  char *
      47  openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file)
      48  {
      49    char *result = buf;
      50    int dirlen;
      51  
      52    /* Make sure the caller gets ENOENT when appropriate.  */
      53    if (!*file)
      54      {
      55        buf[0] = '\0';
      56        return buf;
      57      }
      58  
      59  #if !(defined __KLIBC__ || defined __MVS__)
      60    /* Generic code for Linux, Solaris, and similar platforms.  */
      61  # define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/"
      62    {
      63      enum {
      64        PROC_SELF_FD_DIR_SIZE_BOUND
      65          = (sizeof PROC_SELF_FD_FORMAT - (sizeof "%d" - 1)
      66             + INT_STRLEN_BOUND (int))
      67      };
      68  
      69      static int proc_status = 0;
      70      if (! proc_status)
      71        {
      72          /* Set PROC_STATUS to a positive value if /proc/self/fd is
      73             reliable, and a negative value otherwise.  Solaris 10
      74             /proc/self/fd mishandles "..", and any file name might expand
      75             to ".." after symbolic link expansion, so avoid /proc/self/fd
      76             if it mishandles "..".  Solaris 10 has openat, but this
      77             problem is exhibited on code that built on Solaris 8 and
      78             running on Solaris 10.  */
      79  
      80          int proc_self_fd =
      81            open ("/proc/self/fd",
      82                  O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
      83          if (proc_self_fd < 0)
      84            proc_status = -1;
      85          else
      86            {
      87              /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
      88                 number of a file descriptor open on /proc/self/fd.  On Linux,
      89                 that name resolves to /proc/self/fd, which was opened above.
      90                 However, on Solaris, it may resolve to /proc/self/fd/fd, which
      91                 cannot exist, since all names in /proc/self/fd are numeric.  */
      92              char dotdot_buf[PROC_SELF_FD_DIR_SIZE_BOUND + sizeof "../fd" - 1];
      93              sprintf (dotdot_buf, PROC_SELF_FD_FORMAT "../fd", proc_self_fd);
      94              proc_status = access (dotdot_buf, F_OK) ? -1 : 1;
      95              close (proc_self_fd);
      96            }
      97        }
      98  
      99      if (proc_status < 0)
     100        return NULL;
     101      else
     102        {
     103          size_t bufsize = PROC_SELF_FD_DIR_SIZE_BOUND + strlen (file);
     104          if (OPENAT_BUFFER_SIZE < bufsize)
     105            {
     106              result = malloc (bufsize);
     107              if (! result)
     108                return NULL;
     109            }
     110  
     111          dirlen = sprintf (result, PROC_SELF_FD_FORMAT, fd);
     112        }
     113    }
     114  #else /* (defined __KLIBC__ || defined __MVS__), i.e. OS/2 or z/OS */
     115    /* OS/2 kLIBC provides a function to retrieve a path from a fd.  */
     116    {
     117      size_t bufsize;
     118  
     119  # ifdef __KLIBC__
     120      char dir[_MAX_PATH];
     121      if (__libc_Back_ioFHToPath (fd, dir, sizeof dir))
     122        return NULL;
     123  # endif
     124  # ifdef __MVS__
     125      char dir[_XOPEN_PATH_MAX];
     126      /* Documentation:
     127         https://www.ibm.com/docs/en/zos/2.2.0?topic=functions-w-ioctl-w-pioctl-control-devices */
     128      if (w_ioctl (fd, _IOCC_GPN, sizeof dir, dir) < 0)
     129        return NULL;
     130      /* Documentation:
     131         https://www.ibm.com/docs/en/zos/2.2.0?topic=functions-e2a-l-convert-characters-from-ebcdic-ascii */
     132      dirlen = __e2a_l (dir, strlen (dir));
     133      if (dirlen < 0 || dirlen >= sizeof dir)
     134        return NULL;
     135      dir[dirlen] = '\0';
     136  # endif
     137  
     138      dirlen = strlen (dir);
     139      bufsize = dirlen + 1 + strlen (file) + 1; /* 1 for '/', 1 for null */
     140      if (OPENAT_BUFFER_SIZE < bufsize)
     141        {
     142          result = malloc (bufsize);
     143          if (! result)
     144            return NULL;
     145        }
     146  
     147      strcpy (result, dir);
     148      result[dirlen++] = '/';
     149    }
     150  #endif
     151  
     152    strcpy (result + dirlen, file);
     153    return result;
     154  }