1 /* @(#)at-base2.c 1.2 18/07/16 Copyright 2011-2018 J. Schilling */ 2 /* 3 * Emulate the behavior of FUNC_NAME(fd1, name1, fd2, name2, flag) 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()/open(name)/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 #ifndef ENAMETOOLONG 35 #define ENAMETOOLONG EINVAL 36 #endif 37 38 EXPORT FUNC_RESULT 39 FUNC_NAME(fd1, name1, fd2, name2 KR_ARGS) 40 int fd1; 41 const char *name1; 42 int fd2; 43 const char *name2; 44 KR_DECL 45 { 46 int n; 47 BOOL same_fd = FALSE; 48 FUNC_RESULT ret = 0; 49 char buf1[PATH_MAX]; 50 char buf2[PATH_MAX]; 51 char *proc_name1; 52 char *proc_name2; 53 struct save_wd save_wd; 54 55 FLAG_CHECK(); 56 57 /* 58 * There are 17 cases to implement: 59 * 60 * case fd1 name1 fd2 name2 61 * 1 AT_CWD absolute AT_CWD absolute call xxx(name1, name2) 62 * 2 AT_CWD absolute AT_CWD relative call xxx(name1, name2) 63 * 3 AT_CWD relative AT_CWD absolute call xxx(name1, name2) 64 * 4 AT_CWD relative AT_CWD relative call xxx(name1, name2) 65 * 5 AT_CWD absolute fd absolute call xxx(name1, name2) 66 * 6 AT_CWD relative fd absolute call xxx(name1, name2) 67 * 7 fd absolute AT_CWD absolute call xxx(name1, name2) 68 * 8 fd absolute AT_CWD relative call xxx(name1, name2) 69 * 9 fd absolute fd absolute call xxx(name1, name2) 70 * 71 * 10 AT_CWD absolute fd relative chdir to fd2; call xxx(name1, name2) 72 * 11 AT_CWD relative fd relative convert name1 to abs; case 10 73 * 12 fd relative AT_CWD absolute chdir to fd1; call xxx(name1, name2) 74 * 13 fd relative AT_CWD relative convert name2 to abs; case 12 75 * 76 * 14 fd absolute fd relative chdir to fd2; call xxx(name1, name2) 77 * 15 fd relative fd absolute chdir to fd1; call xxx(name1, name2) 78 * 79 * 16 fd1 relative fd1 relative chdir to fd1; call xxx(name1, name2) 80 * 17 fd relative fd relative chdir to fd1; case 11 81 */ 82 83 /* 84 * Case 1..9 85 */ 86 if ((fd1 == AT_FDCWD || ABS_NAME(name1)) && 87 (fd2 == AT_FDCWD || ABS_NAME(name2))) 88 return (FUNC_CALL(name1, name2)); 89 90 /* 91 * If the procfs interface works, we can catch all other cases here: 92 */ 93 if ((proc_name1 = proc_fd2name(buf1, fd1, name1)) != NULL && 94 (proc_name2 = proc_fd2name(buf2, fd2, name2)) != NULL) { 95 /* 96 * The next FUNC_CALL() frequently results in ENAMETOOLONG 97 */ 98 ret = FUNC_CALL(proc_name1, proc_name2); 99 if (ret >= 0 || NON_PROCFS_ERRNO(errno)) 100 return (ret); 101 } 102 103 /* 104 * /proc open failed or /proc not available on this platform. 105 * Give it a chance using fchdir(). But the following code is 106 * not MT-safe! 107 */ 108 109 if (savewd(&save_wd) < 0) { 110 /* 111 * We abort here as the caller may not know that we are forced 112 * to savewd/fchdir/restorewd here and misinterpret errno. 113 */ 114 savewd_abort(geterrno()); 115 /* NOTREACHED */ 116 return (-1); 117 } 118 if ((fd1 >= 0 && save_wd.fd == fd1) || 119 (fd2 >= 0 && save_wd.fd == fd2)) { 120 /* 121 * If we just opened "fd" with the same number in savewd(), 122 * fd1/fd2 must have been illegal when calling us(); 123 */ 124 closewd(&save_wd); 125 seterrno(EBADF); 126 return (-1); 127 } 128 if (!ABS_NAME(name1) && !ABS_NAME(name2)) { 129 /* 130 * Check for case 16 131 */ 132 if (fd1 == fd2) { 133 same_fd = TRUE; 134 } 135 #ifndef _MSC_VER 136 /* 137 * Are there other non-cooperative platforms where stat() 138 * does not allow to compare files? 139 */ 140 else { 141 struct stat sb1; 142 struct stat sb2; 143 144 if (fstat(fd1, &sb1) >= 0 && 145 fstat(fd2, &sb2) >= 0) { 146 if (sb1.st_dev == sb2.st_dev && 147 sb1.st_ino == sb2.st_ino) 148 same_fd = TRUE; 149 } 150 } 151 #endif 152 } 153 case_10_12: 154 if (ABS_NAME(name1) || ABS_NAME(name2) || same_fd) { 155 /* 156 * Handle case 10, 12, 14, 15, 16 157 */ 158 int fd; 159 160 if (ABS_NAME(name1)) 161 fd = fd2; 162 else 163 fd = fd1; 164 if ((n = fchdir(fd)) < 0) { 165 int err = geterrno(); 166 /* 167 * In case that fchdir() is emulated via chdir() 168 * and we use a multi hop chdir(), we may be on 169 * an undefined intermediate directory. Try to 170 * return to last working directory and if this 171 * fails, abort for security. 172 */ 173 if (n == -2 && restorewd(&save_wd) < 0) { 174 restorewd_abort(geterrno()); 175 /* NOTREACHED */ 176 } 177 closewd(&save_wd); 178 seterrno(err); 179 return (-1); 180 } 181 } else { 182 /* 183 * Handle case 11, 13, 17 184 */ 185 if (fd1 == AT_FDCWD) { 186 case_11: 187 /* 188 * Existing file: follow all but last component, abort 189 * if the file does not exist. 190 */ 191 if (absfpath(name1, buf1, sizeof (buf1), 192 RSPF_EXIST | RSPF_NOFOLLOW_LAST) == NULL) { 193 ret = -1; 194 goto fail; 195 } 196 name1 = buf1; 197 goto case_10_12; 198 } else if (fd2 == AT_FDCWD) { 199 /* 200 * New file: may not exist, 201 * use absnpath() -> absfpath(... 0) 202 */ 203 if (absnpath(name2, buf2, sizeof (buf2)) == NULL) { 204 ret = -1; 205 goto fail; 206 } 207 name2 = buf2; 208 goto case_10_12; 209 } else { 210 if ((n = fchdir(fd1)) < 0) { 211 int err = geterrno(); 212 /* 213 * In case that fchdir() is emulated via chdir() 214 * and we use a multi hop chdir(), we may be on 215 * an undefined intermediate directory. Try to 216 * return to last working directory and if this 217 * fails, abort for security. 218 */ 219 if (n == -2 && restorewd(&save_wd) < 0) { 220 restorewd_abort(geterrno()); 221 /* NOTREACHED */ 222 } 223 closewd(&save_wd); 224 seterrno(err); 225 return (-1); 226 } 227 goto case_11; 228 } 229 } 230 231 ret = FUNC_CALL(name1, name2); /* The actual call() */ 232 233 fail: 234 if (restorewd(&save_wd) < 0) { 235 int err = geterrno(); 236 237 closewd(&save_wd); 238 restorewd_abort(err); 239 /* NOTREACHED */ 240 seterrno(err); 241 return (-1); 242 } 243 closewd(&save_wd); 244 245 return (ret); 246 } 247