xref: /netbsd-src/external/gpl3/gdb/dist/gnulib/import/openat-proc.c (revision 4b169a6ba595ae283ca507b26b15fdff40495b1c)
18dffb485Schristos /* Create /proc/self/fd-related names for subfiles of open directories.
28dffb485Schristos 
3*4b169a6bSchristos    Copyright (C) 2006, 2009-2022 Free Software Foundation, Inc.
48dffb485Schristos 
58dffb485Schristos    This program is free software: you can redistribute it and/or modify
68dffb485Schristos    it under the terms of the GNU General Public License as published by
7*4b169a6bSchristos    the Free Software Foundation, either version 3 of the License, or
88dffb485Schristos    (at your option) any later version.
98dffb485Schristos 
108dffb485Schristos    This program is distributed in the hope that it will be useful,
118dffb485Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
128dffb485Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
138dffb485Schristos    GNU General Public License for more details.
148dffb485Schristos 
158dffb485Schristos    You should have received a copy of the GNU General Public License
168dffb485Schristos    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
178dffb485Schristos 
188dffb485Schristos /* Written by Paul Eggert.  */
198dffb485Schristos 
208dffb485Schristos #include <config.h>
218dffb485Schristos 
228dffb485Schristos #include "openat-priv.h"
238dffb485Schristos 
248dffb485Schristos #include <sys/types.h>
258dffb485Schristos #include <sys/stat.h>
268dffb485Schristos #include <fcntl.h>
278dffb485Schristos 
288dffb485Schristos #include <stdio.h>
298dffb485Schristos #include <stdlib.h>
308dffb485Schristos #include <string.h>
318dffb485Schristos #include <unistd.h>
328dffb485Schristos 
338dffb485Schristos #ifdef __KLIBC__
348dffb485Schristos # include <InnoTekLIBC/backend.h>
358dffb485Schristos #endif
368dffb485Schristos 
378dffb485Schristos #include "intprops.h"
388dffb485Schristos 
398dffb485Schristos /* Set BUF to the name of the subfile of the directory identified by
408dffb485Schristos    FD, where the subfile is named FILE.  If successful, return BUF if
418dffb485Schristos    the result fits in BUF, dynamically allocated memory otherwise.
428dffb485Schristos    Return NULL (setting errno) on error.  */
438dffb485Schristos char *
openat_proc_name(char buf[OPENAT_BUFFER_SIZE],int fd,char const * file)448dffb485Schristos openat_proc_name (char buf[OPENAT_BUFFER_SIZE], int fd, char const *file)
458dffb485Schristos {
468dffb485Schristos   char *result = buf;
478dffb485Schristos   int dirlen;
488dffb485Schristos 
498dffb485Schristos   /* Make sure the caller gets ENOENT when appropriate.  */
508dffb485Schristos   if (!*file)
518dffb485Schristos     {
528dffb485Schristos       buf[0] = '\0';
538dffb485Schristos       return buf;
548dffb485Schristos     }
558dffb485Schristos 
568dffb485Schristos #ifndef __KLIBC__
578dffb485Schristos # define PROC_SELF_FD_FORMAT "/proc/self/fd/%d/"
588dffb485Schristos   {
598dffb485Schristos     enum {
608dffb485Schristos       PROC_SELF_FD_DIR_SIZE_BOUND
618dffb485Schristos         = (sizeof PROC_SELF_FD_FORMAT - (sizeof "%d" - 1)
628dffb485Schristos            + INT_STRLEN_BOUND (int))
638dffb485Schristos     };
648dffb485Schristos 
658dffb485Schristos     static int proc_status = 0;
668dffb485Schristos     if (! proc_status)
678dffb485Schristos       {
688dffb485Schristos         /* Set PROC_STATUS to a positive value if /proc/self/fd is
698dffb485Schristos            reliable, and a negative value otherwise.  Solaris 10
708dffb485Schristos            /proc/self/fd mishandles "..", and any file name might expand
718dffb485Schristos            to ".." after symbolic link expansion, so avoid /proc/self/fd
728dffb485Schristos            if it mishandles "..".  Solaris 10 has openat, but this
738dffb485Schristos            problem is exhibited on code that built on Solaris 8 and
748dffb485Schristos            running on Solaris 10.  */
758dffb485Schristos 
768dffb485Schristos         int proc_self_fd =
778dffb485Schristos           open ("/proc/self/fd",
788dffb485Schristos                 O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
798dffb485Schristos         if (proc_self_fd < 0)
808dffb485Schristos           proc_status = -1;
818dffb485Schristos         else
828dffb485Schristos           {
838dffb485Schristos             /* Detect whether /proc/self/fd/%i/../fd exists, where %i is the
848dffb485Schristos                number of a file descriptor open on /proc/self/fd.  On Linux,
858dffb485Schristos                that name resolves to /proc/self/fd, which was opened above.
868dffb485Schristos                However, on Solaris, it may resolve to /proc/self/fd/fd, which
878dffb485Schristos                cannot exist, since all names in /proc/self/fd are numeric.  */
888dffb485Schristos             char dotdot_buf[PROC_SELF_FD_DIR_SIZE_BOUND + sizeof "../fd" - 1];
898dffb485Schristos             sprintf (dotdot_buf, PROC_SELF_FD_FORMAT "../fd", proc_self_fd);
908dffb485Schristos             proc_status = access (dotdot_buf, F_OK) ? -1 : 1;
918dffb485Schristos             close (proc_self_fd);
928dffb485Schristos           }
938dffb485Schristos       }
948dffb485Schristos 
958dffb485Schristos     if (proc_status < 0)
968dffb485Schristos       return NULL;
978dffb485Schristos     else
988dffb485Schristos       {
998dffb485Schristos         size_t bufsize = PROC_SELF_FD_DIR_SIZE_BOUND + strlen (file);
1008dffb485Schristos         if (OPENAT_BUFFER_SIZE < bufsize)
1018dffb485Schristos           {
1028dffb485Schristos             result = malloc (bufsize);
1038dffb485Schristos             if (! result)
1048dffb485Schristos               return NULL;
1058dffb485Schristos           }
1068dffb485Schristos 
1078dffb485Schristos         dirlen = sprintf (result, PROC_SELF_FD_FORMAT, fd);
1088dffb485Schristos       }
1098dffb485Schristos   }
1108dffb485Schristos #else
1118dffb485Schristos   /* OS/2 kLIBC provides a function to retrieve a path from a fd.  */
1128dffb485Schristos   {
1138dffb485Schristos     char dir[_MAX_PATH];
1148dffb485Schristos     size_t bufsize;
1158dffb485Schristos 
1168dffb485Schristos     if (__libc_Back_ioFHToPath (fd, dir, sizeof dir))
1178dffb485Schristos       return NULL;
1188dffb485Schristos 
1198dffb485Schristos     dirlen = strlen (dir);
1208dffb485Schristos     bufsize = dirlen + 1 + strlen (file) + 1; /* 1 for '/', 1 for null */
1218dffb485Schristos     if (OPENAT_BUFFER_SIZE < bufsize)
1228dffb485Schristos       {
1238dffb485Schristos         result = malloc (bufsize);
1248dffb485Schristos         if (! result)
1258dffb485Schristos           return NULL;
1268dffb485Schristos       }
1278dffb485Schristos 
1288dffb485Schristos     strcpy (result, dir);
1298dffb485Schristos     result[dirlen++] = '/';
1308dffb485Schristos   }
1318dffb485Schristos #endif
1328dffb485Schristos 
1338dffb485Schristos   strcpy (result + dirlen, file);
1348dffb485Schristos   return result;
1358dffb485Schristos }
136