1/* fchdir replacement. 2 Copyright (C) 2006, 2007 Free Software Foundation, Inc. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2, or (at your option) 7 any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software Foundation, 16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 17 18#include <config.h> 19 20/* Specification. */ 21#include <unistd.h> 22 23#include <errno.h> 24#include <fcntl.h> 25#include <stdarg.h> 26#include <stdlib.h> 27#include <string.h> 28#include <sys/types.h> 29#include <sys/stat.h> 30#include <dirent.h> 31 32#include "canonicalize.h" 33#include "dirfd.h" 34 35/* This replacement assumes that a directory is not renamed while opened 36 through a file descriptor. */ 37 38/* Array of file descriptors opened. If it points to a directory, it stores 39 info about this directory; otherwise it stores an errno value of ENOTDIR. */ 40typedef struct 41{ 42 char *name; /* Absolute name of the directory, or NULL. */ 43 int saved_errno; /* If name == NULL: The error code describing the failure 44 reason. */ 45} dir_info_t; 46static dir_info_t *dirs; 47static size_t dirs_allocated; 48 49/* Try to ensure dirs has enough room for a slot at index fd. */ 50static void 51ensure_dirs_slot (size_t fd) 52{ 53 if (fd >= dirs_allocated) 54 { 55 size_t new_allocated; 56 dir_info_t *new_dirs; 57 size_t i; 58 59 new_allocated = 2 * dirs_allocated + 1; 60 if (new_allocated <= fd) 61 new_allocated = fd + 1; 62 new_dirs = 63 (dirs != NULL 64 ? (dir_info_t *) realloc (dirs, new_allocated * sizeof (dir_info_t)) 65 : (dir_info_t *) malloc (new_allocated * sizeof (dir_info_t))); 66 if (new_dirs != NULL) 67 { 68 for (i = dirs_allocated; i < new_allocated; i++) 69 { 70 new_dirs[i].name = NULL; 71 new_dirs[i].saved_errno = ENOTDIR; 72 } 73 dirs = new_dirs; 74 dirs_allocated = new_allocated; 75 } 76 } 77} 78 79/* Override open() and close(), to keep track of the open file descriptors. */ 80 81int 82close (int fd) 83#undef close 84{ 85 int retval = close (fd); 86 87 if (retval >= 0 && fd >= 0 && fd < dirs_allocated) 88 { 89 if (dirs[fd].name != NULL) 90 free (dirs[fd].name); 91 dirs[fd].name = NULL; 92 dirs[fd].saved_errno = ENOTDIR; 93 } 94 return retval; 95} 96 97int 98open (const char *filename, int flags, ...) 99#undef open 100{ 101 mode_t mode; 102 int fd; 103 struct stat statbuf; 104 105 mode = 0; 106 if (flags & O_CREAT) 107 { 108 va_list arg; 109 va_start (arg, flags); 110 111 /* If mode_t is narrower than int, use the promoted type (int), 112 not mode_t. Use sizeof to guess whether mode_t is narrower; 113 we don't know of any practical counterexamples. */ 114 mode = (sizeof (mode_t) < sizeof (int) 115 ? va_arg (arg, int) 116 : va_arg (arg, mode_t)); 117 118 va_end (arg); 119 } 120 fd = open (filename, flags, mode); 121 if (fd >= 0) 122 { 123 ensure_dirs_slot (fd); 124 if (fd < dirs_allocated 125 && fstat (fd, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode)) 126 { 127 dirs[fd].name = canonicalize_file_name (filename); 128 if (dirs[fd].name == NULL) 129 dirs[fd].saved_errno = errno; 130 } 131 } 132 return fd; 133} 134 135/* Override opendir() and closedir(), to keep track of the open file 136 descriptors. Needed because there is a function dirfd(). */ 137 138int 139closedir (DIR *dp) 140#undef closedir 141{ 142 int fd = dirfd (dp); 143 int retval = closedir (dp); 144 145 if (retval >= 0 && fd >= 0 && fd < dirs_allocated) 146 { 147 if (dirs[fd].name != NULL) 148 free (dirs[fd].name); 149 dirs[fd].name = NULL; 150 dirs[fd].saved_errno = ENOTDIR; 151 } 152 return retval; 153} 154 155DIR * 156opendir (const char *filename) 157#undef opendir 158{ 159 DIR *dp; 160 161 dp = opendir (filename); 162 if (dp != NULL) 163 { 164 int fd = dirfd (dp); 165 if (fd >= 0) 166 { 167 ensure_dirs_slot (fd); 168 if (fd < dirs_allocated) 169 { 170 dirs[fd].name = canonicalize_file_name (filename); 171 if (dirs[fd].name == NULL) 172 dirs[fd].saved_errno = errno; 173 } 174 } 175 } 176 return dp; 177} 178 179/* Override dup() and dup2(), to keep track of open file descriptors. */ 180 181int 182dup (int oldfd) 183#undef dup 184{ 185 int newfd = dup (oldfd); 186 187 if (oldfd >= 0 && newfd >= 0) 188 { 189 ensure_dirs_slot (newfd); 190 if (newfd < dirs_allocated) 191 { 192 if (oldfd < dirs_allocated) 193 { 194 if (dirs[oldfd].name != NULL) 195 { 196 dirs[newfd].name = strdup (dirs[oldfd].name); 197 if (dirs[newfd].name == NULL) 198 dirs[newfd].saved_errno = ENOMEM; 199 } 200 else 201 { 202 dirs[newfd].name = NULL; 203 dirs[newfd].saved_errno = dirs[oldfd].saved_errno; 204 } 205 } 206 else 207 { 208 dirs[newfd].name = NULL; 209 dirs[newfd].saved_errno = ENOMEM; 210 } 211 } 212 } 213 return newfd; 214} 215 216int 217dup2 (int oldfd, int newfd) 218#undef dup2 219{ 220 int retval = dup2 (oldfd, newfd); 221 222 if (retval >= 0 && oldfd >= 0 && newfd >= 0 && newfd != oldfd) 223 { 224 ensure_dirs_slot (newfd); 225 if (newfd < dirs_allocated) 226 { 227 if (oldfd < dirs_allocated) 228 { 229 if (dirs[oldfd].name != NULL) 230 { 231 dirs[newfd].name = strdup (dirs[oldfd].name); 232 if (dirs[newfd].name == NULL) 233 dirs[newfd].saved_errno = ENOMEM; 234 } 235 else 236 { 237 dirs[newfd].name = NULL; 238 dirs[newfd].saved_errno = dirs[oldfd].saved_errno; 239 } 240 } 241 else 242 { 243 dirs[newfd].name = NULL; 244 dirs[newfd].saved_errno = ENOMEM; 245 } 246 } 247 } 248 return retval; 249} 250 251/* Implement fchdir() in terms of chdir(). */ 252 253int 254fchdir (int fd) 255{ 256 if (fd >= 0) 257 { 258 if (fd < dirs_allocated) 259 { 260 if (dirs[fd].name != NULL) 261 return chdir (dirs[fd].name); 262 else 263 { 264 errno = dirs[fd].saved_errno; 265 return -1; 266 } 267 } 268 else 269 { 270 errno = ENOMEM; 271 return -1; 272 } 273 } 274 else 275 { 276 errno = EBADF; 277 return -1; 278 } 279} 280