1 /* @(#)fdopendir.c 1.2 18/07/16 Copyright 2011-2018 J. Schilling */ 2 /* 3 * Emulate the behavior of fdopendir(fd) 4 * 5 * Note that emulation methods that do not use the /proc filesystem are 6 * not MT safe. In the non-MT-safe case, we do: 7 * 8 * savewd()/fchdir()/opendir(".")/restorewd() 9 * 10 * Since the /proc method may fail with ENAMETOOLONG, we need to fall back 11 * to the fchdir() method in case of long path names and as a result are 12 * not MT-safe with long path names. 13 * 14 * Errors may force us to abort the program as our caller is not expected 15 * to know that we do more than a simple open() here and that the 16 * working directory may be changed by us. 17 * 18 * Copyright (c) 2011-2018 J. Schilling 19 */ 20 /* 21 * The contents of this file are subject to the terms of the 22 * Common Development and Distribution License, Version 1.0 only 23 * (the "License"). You may not use this file except in compliance 24 * with the License. 25 * 26 * See the file CDDL.Schily.txt in this distribution for details. 27 * A copy of the CDDL is also available via the Internet at 28 * http://www.opensource.org/licenses/cddl1.txt 29 * 30 * When distributing Covered Code, include this CDDL HEADER in each 31 * file and include the License file CDDL.Schily.txt from this distribution. 32 */ 33 34 #include <schily/unistd.h> 35 #include <schily/types.h> 36 #include <schily/fcntl.h> 37 #include <schily/dirent.h> 38 #include <schily/maxpath.h> 39 #include <schily/errno.h> 40 #include <schily/standard.h> 41 #include <schily/schily.h> 42 #include "at-defs.h" 43 44 #ifndef HAVE_FDOPENDIR 45 46 #ifndef ENAMETOOLONG 47 #define ENAMETOOLONG EINVAL 48 #endif 49 50 EXPORT DIR * fdopendir(fd)51fdopendir(fd) 52 int fd; 53 { 54 int n; 55 DIR *ret = 0; 56 char buf[PATH_MAX]; 57 char *proc_name; 58 struct save_wd save_wd; 59 60 if ((proc_name = proc_fd2name(buf, fd, ".")) != NULL) { 61 /* 62 * The next opendir() frequently results in ENAMETOOLONG 63 */ 64 ret = opendir(proc_name); 65 if (ret != (DIR *)NULL) { 66 close(fd); 67 return (ret); 68 } 69 if (NON_PROCFS_ERRNO(errno)) 70 return (ret); 71 } 72 73 /* 74 * /proc open failed or /proc not available on this platform. 75 * Give it a chance using fchdir(). But the following code is 76 * not MT-safe! 77 */ 78 79 if (savewd(&save_wd) < 0) { 80 /* 81 * We abort here as the caller may not know that we are forced 82 * to savewd/fchdir/restorewd here and misinterpret errno. 83 */ 84 savewd_abort(geterrno()); 85 /* NOTREACHED */ 86 return ((DIR *)NULL); 87 } 88 if (fd >= 0 && save_wd.fd == fd) { 89 /* 90 * If we just opened "fd" with the same number in savewd(), fd 91 * must have been illegal when calling openat(); 92 */ 93 closewd(&save_wd); 94 seterrno(EBADF); 95 return ((DIR *)NULL); 96 } 97 if ((n = fchdir(fd)) < 0) { 98 int err = geterrno(); 99 /* 100 * In case that fchdir() is emulated via chdir() and we use a 101 * multi hop chdir(), we may be on an undefined intermediate 102 * directory. Try to return to last working directory and if 103 * this fails, abort for security. 104 */ 105 if (n == -2 && restorewd(&save_wd) < 0) { 106 restorewd_abort(geterrno()); 107 /* NOTREACHED */ 108 } 109 closewd(&save_wd); 110 seterrno(err); 111 return ((DIR *)NULL); 112 } 113 114 ret = opendir("."); /* The actual call() */ 115 if (ret != (DIR *)NULL) 116 close(fd); 117 118 if (restorewd(&save_wd) < 0) { 119 int err = geterrno(); 120 121 closedir(ret); 122 closewd(&save_wd); 123 restorewd_abort(err); 124 /* NOTREACHED */ 125 seterrno(err); 126 return ((DIR *)NULL); 127 } 128 closewd(&save_wd); 129 130 return (ret); 131 } 132 133 #endif /* HAVE_FDOPENDIR */ 134