Blame lib/openat-proc.c

Packit 709fb3
/* Create /proc/self/fd-related names for subfiles of open directories.
Packit 709fb3
Packit 709fb3
   Copyright (C) 2006, 2009-2017 Free Software Foundation, Inc.
Packit 709fb3
Packit 709fb3
   This program is free software: you can redistribute it and/or modify
Packit 709fb3
   it under the terms of the GNU General Public License as published by
Packit 709fb3
   the Free Software Foundation; either version 3 of the License, or
Packit 709fb3
   (at your option) any later version.
Packit 709fb3
Packit 709fb3
   This program is distributed in the hope that it will be useful,
Packit 709fb3
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 709fb3
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 709fb3
   GNU General Public License for more details.
Packit 709fb3
Packit 709fb3
   You should have received a copy of the GNU General Public License
Packit 709fb3
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 709fb3
Packit 709fb3
/* Written by Paul Eggert.  */
Packit 709fb3
Packit 709fb3
#include <config.h>
Packit 709fb3
Packit 709fb3
#include "openat-priv.h"
Packit 709fb3
Packit 709fb3
#include <sys/types.h>
Packit 709fb3
#include <sys/stat.h>
Packit 709fb3
#include <fcntl.h>
Packit 709fb3
Packit 709fb3
#include <stdio.h>
Packit 709fb3
#include <stdlib.h>
Packit 709fb3
#include <string.h>
Packit 709fb3
#include <unistd.h>
Packit 709fb3
Packit 709fb3
#ifdef __KLIBC__
Packit 709fb3
# include <InnoTekLIBC/backend.h>
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
#include "intprops.h"
Packit 709fb3
Packit 709fb3
/* Set BUF to the name of the subfile of the directory identified by
Packit 709fb3
   FD, where the subfile is named FILE.  If successful, return BUF if
Packit 709fb3
   the result fits in BUF, dynamically allocated memory otherwise.
Packit 709fb3
   Return NULL (setting errno) on error.  */
Packit 709fb3
char *
Packit 709fb3
openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file)
Packit 709fb3
{
Packit 709fb3
  char *result = buf;
Packit 709fb3
  int dirlen;
Packit 709fb3
Packit 709fb3
  /* Make sure the caller gets ENOENT when appropriate.  */
Packit 709fb3
  if (!*file)
Packit 709fb3
    {
Packit 709fb3
      buf[0] = '\0';
Packit 709fb3
      return buf;
Packit 709fb3
    }
Packit 709fb3
Packit 709fb3
#ifndef __KLIBC__
Packit 709fb3
# define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/"
Packit 709fb3
  {
Packit 709fb3
    enum {
Packit 709fb3
      PROC_SELF_FD_DIR_SIZE_BOUND
Packit 709fb3
        = (sizeof PROC_SELF_FD_FORMAT - (sizeof "%d" - 1)
Packit 709fb3
           + INT_STRLEN_BOUND (int))
Packit 709fb3
    };
Packit 709fb3
Packit 709fb3
    static int proc_status = 0;
Packit 709fb3
    if (! proc_status)
Packit 709fb3
      {
Packit 709fb3
        /* Set PROC_STATUS to a positive value if /proc/self/fd is
Packit 709fb3
           reliable, and a negative value otherwise.  Solaris 10
Packit 709fb3
           /proc/self/fd mishandles "..", and any file name might expand
Packit 709fb3
           to ".." after symbolic link expansion, so avoid /proc/self/fd
Packit 709fb3
           if it mishandles "..".  Solaris 10 has openat, but this
Packit 709fb3
           problem is exhibited on code that built on Solaris 8 and
Packit 709fb3
           running on Solaris 10.  */
Packit 709fb3
Packit 709fb3
        int proc_self_fd = open ("/proc/self/fd",
Packit 709fb3
                                 O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
Packit 709fb3
        if (proc_self_fd < 0)
Packit 709fb3
          proc_status = -1;
Packit 709fb3
        else
Packit 709fb3
          {
Packit 709fb3
            /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
Packit 709fb3
               number of a file descriptor open on /proc/self/fd.  On Linux,
Packit 709fb3
               that name resolves to /proc/self/fd, which was opened above.
Packit 709fb3
               However, on Solaris, it may resolve to /proc/self/fd/fd, which
Packit 709fb3
               cannot exist, since all names in /proc/self/fd are numeric.  */
Packit 709fb3
            char dotdot_buf[PROC_SELF_FD_DIR_SIZE_BOUND + sizeof "../fd" - 1];
Packit 709fb3
            sprintf (dotdot_buf, PROC_SELF_FD_FORMAT "../fd", proc_self_fd);
Packit 709fb3
            proc_status = access (dotdot_buf, F_OK) ? -1 : 1;
Packit 709fb3
            close (proc_self_fd);
Packit 709fb3
          }
Packit 709fb3
      }
Packit 709fb3
Packit 709fb3
    if (proc_status < 0)
Packit 709fb3
      return NULL;
Packit 709fb3
    else
Packit 709fb3
      {
Packit 709fb3
        size_t bufsize = PROC_SELF_FD_DIR_SIZE_BOUND + strlen (file);
Packit 709fb3
        if (OPENAT_BUFFER_SIZE < bufsize)
Packit 709fb3
          {
Packit 709fb3
            result = malloc (bufsize);
Packit 709fb3
            if (! result)
Packit 709fb3
              return NULL;
Packit 709fb3
          }
Packit 709fb3
Packit 709fb3
        dirlen = sprintf (result, PROC_SELF_FD_FORMAT, fd);
Packit 709fb3
      }
Packit 709fb3
  }
Packit 709fb3
#else
Packit 709fb3
  /* OS/2 kLIBC provides a function to retrieve a path from a fd.  */
Packit 709fb3
  {
Packit 709fb3
    char dir[_MAX_PATH];
Packit 709fb3
    size_t bufsize;
Packit 709fb3
Packit 709fb3
    if (__libc_Back_ioFHToPath (fd, dir, sizeof dir))
Packit 709fb3
      return NULL;
Packit 709fb3
Packit 709fb3
    dirlen = strlen (dir);
Packit 709fb3
    bufsize = dirlen + 1 + strlen (file) + 1; /* 1 for '/', 1 for null */
Packit 709fb3
    if (OPENAT_BUFFER_SIZE < bufsize)
Packit 709fb3
      {
Packit 709fb3
        result = malloc (bufsize);
Packit 709fb3
        if (! result)
Packit 709fb3
          return NULL;
Packit 709fb3
      }
Packit 709fb3
Packit 709fb3
    strcpy (result, dir);
Packit 709fb3
    result[dirlen++] = '/';
Packit 709fb3
  }
Packit 709fb3
#endif
Packit 709fb3
Packit 709fb3
  strcpy (result + dirlen, file);
Packit 709fb3
  return result;
Packit 709fb3
}