exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / openat-proc.c
blob7ccb734f424d7b603a9a80c77974fad53ab0fd95
1 /* Create /proc/self/fd-related names for subfiles of open directories.
3 Copyright (C) 2006, 2009-2024 Free Software Foundation, Inc.
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.
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.
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/>. */
18 /* Written by Paul Eggert. */
20 #include <config.h>
22 #include "openat-priv.h"
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
33 #ifdef __KLIBC__ /* OS/2 */
34 # include <InnoTekLIBC/backend.h>
35 #endif
36 #ifdef __MVS__ /* z/OS */
37 # include <termios.h>
38 #endif
40 #include "intprops.h"
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)
49 char *result = buf;
50 int dirlen;
52 /* Make sure the caller gets ENOENT when appropriate. */
53 if (!*file)
55 buf[0] = '\0';
56 return buf;
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/"
63 enum {
64 PROC_SELF_FD_DIR_SIZE_BOUND
65 = (sizeof PROC_SELF_FD_FORMAT - (sizeof "%d" - 1)
66 + INT_STRLEN_BOUND (int))
69 static int proc_status = 0;
70 if (! proc_status)
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. */
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
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);
99 if (proc_status < 0)
100 return NULL;
101 else
103 size_t bufsize = PROC_SELF_FD_DIR_SIZE_BOUND + strlen (file);
104 if (OPENAT_BUFFER_SIZE < bufsize)
106 result = malloc (bufsize);
107 if (! result)
108 return NULL;
111 dirlen = sprintf (result, PROC_SELF_FD_FORMAT, fd);
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. */
117 size_t bufsize;
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
138 dirlen = strlen (dir);
139 bufsize = dirlen + 1 + strlen (file) + 1; /* 1 for '/', 1 for null */
140 if (OPENAT_BUFFER_SIZE < bufsize)
142 result = malloc (bufsize);
143 if (! result)
144 return NULL;
147 strcpy (result, dir);
148 result[dirlen++] = '/';
150 #endif
152 strcpy (result + dirlen, file);
153 return result;