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. */
22 #include "openat-priv.h"
24 #include <sys/types.h>
33 #ifdef __KLIBC__ /* OS/2 */
34 # include <InnoTekLIBC/backend.h>
36 #ifdef __MVS__ /* z/OS */
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. */
47 openat_proc_name (char buf
[OPENAT_BUFFER_SIZE
], int fd
, char const *file
)
52 /* Make sure the caller gets ENOENT when appropriate. */
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/"
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;
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. */
81 open ("/proc/self/fd",
82 O_SEARCH
| O_DIRECTORY
| O_NOCTTY
| O_NONBLOCK
| O_CLOEXEC
);
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;
103 size_t bufsize
= PROC_SELF_FD_DIR_SIZE_BOUND
+ strlen (file
);
104 if (OPENAT_BUFFER_SIZE
< bufsize
)
106 result
= malloc (bufsize
);
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. */
121 if (__libc_Back_ioFHToPath (fd
, dir
, sizeof dir
))
125 char dir
[_XOPEN_PATH_MAX
];
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)
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
)
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
);
147 strcpy (result
, dir
);
148 result
[dirlen
++] = '/';
152 strcpy (result
+ dirlen
, file
);