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