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