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