1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include "namespace.h" 33 #include <sys/param.h> 34 35 #include <dirent.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 #include "un-namespace.h" 42 43 #include "gen-private.h" 44 #include "telldir.h" 45 46 DIR * 47 __opendir2(const char *name, int flags) 48 { 49 int fd; 50 DIR *dir; 51 int saved_errno; 52 53 if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) 54 return (NULL); 55 if ((fd = _open(name, 56 O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1) 57 return (NULL); 58 59 dir = __opendir_common(fd, flags, false); 60 if (dir == NULL) { 61 saved_errno = errno; 62 _close(fd); 63 errno = saved_errno; 64 } 65 return (dir); 66 } 67 68 static int 69 opendir_compar(const void *p1, const void *p2) 70 { 71 72 return (strcmp((*(const struct dirent * const *)p1)->d_name, 73 (*(const struct dirent * const *)p2)->d_name)); 74 } 75 76 /* 77 * For a directory at the top of a unionfs stack, the entire directory's 78 * contents are read and cached locally until the next call to rewinddir(). 79 * For the fdopendir() case, the initial seek position must be preserved. 80 * For rewinddir(), the full directory should always be re-read from the 81 * beginning. 82 * 83 * If an error occurs, the existing buffer and state of 'dirp' is left 84 * unchanged. 85 */ 86 bool 87 _filldir(DIR *dirp, bool use_current_pos) 88 { 89 struct dirent **dpv; 90 char *buf, *ddptr, *ddeptr; 91 off_t pos; 92 int fd2, incr, len, n, saved_errno, space; 93 94 len = 0; 95 space = 0; 96 buf = NULL; 97 ddptr = NULL; 98 99 /* 100 * Use the system page size if that is a multiple of DIRBLKSIZ. 101 * Hopefully this can be a big win someday by allowing page 102 * trades to user space to be done by _getdirentries(). 103 */ 104 incr = getpagesize(); 105 if ((incr % DIRBLKSIZ) != 0) 106 incr = DIRBLKSIZ; 107 108 /* 109 * The strategy here is to read all the directory 110 * entries into a buffer, sort the buffer, and 111 * remove duplicate entries by setting the inode 112 * number to zero. 113 * 114 * We reopen the directory because _getdirentries() 115 * on a MNT_UNION mount modifies the open directory, 116 * making it refer to the lower directory after the 117 * upper directory's entries are exhausted. 118 * This would otherwise break software that uses 119 * the directory descriptor for fchdir or *at 120 * functions, such as fts.c. 121 */ 122 if ((fd2 = _openat(dirp->dd_fd, ".", O_RDONLY | O_CLOEXEC)) == -1) 123 return (false); 124 125 if (use_current_pos) { 126 pos = lseek(dirp->dd_fd, 0, SEEK_CUR); 127 if (pos == -1 || lseek(fd2, pos, SEEK_SET) == -1) { 128 saved_errno = errno; 129 _close(fd2); 130 errno = saved_errno; 131 return (false); 132 } 133 } 134 135 do { 136 /* 137 * Always make at least DIRBLKSIZ bytes 138 * available to _getdirentries 139 */ 140 if (space < DIRBLKSIZ) { 141 space += incr; 142 len += incr; 143 buf = reallocf(buf, len); 144 if (buf == NULL) { 145 saved_errno = errno; 146 _close(fd2); 147 errno = saved_errno; 148 return (false); 149 } 150 ddptr = buf + (len - space); 151 } 152 153 n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek); 154 if (n > 0) { 155 ddptr += n; 156 space -= n; 157 } 158 if (n < 0) { 159 saved_errno = errno; 160 _close(fd2); 161 errno = saved_errno; 162 return (false); 163 } 164 } while (n > 0); 165 _close(fd2); 166 167 ddeptr = ddptr; 168 169 /* 170 * There is now a buffer full of (possibly) duplicate 171 * names. 172 */ 173 dirp->dd_buf = buf; 174 175 /* 176 * Go round this loop twice... 177 * 178 * Scan through the buffer, counting entries. 179 * On the second pass, save pointers to each one. 180 * Then sort the pointers and remove duplicate names. 181 */ 182 for (dpv = NULL;;) { 183 n = 0; 184 ddptr = buf; 185 while (ddptr < ddeptr) { 186 struct dirent *dp; 187 188 dp = (struct dirent *) ddptr; 189 if ((long)dp & 03L) 190 break; 191 if ((dp->d_reclen <= 0) || 192 (dp->d_reclen > (ddeptr + 1 - ddptr))) 193 break; 194 ddptr += dp->d_reclen; 195 if (dp->d_fileno) { 196 if (dpv) 197 dpv[n] = dp; 198 n++; 199 } 200 } 201 202 if (dpv) { 203 struct dirent *xp; 204 205 /* 206 * This sort must be stable. 207 */ 208 mergesort(dpv, n, sizeof(*dpv), opendir_compar); 209 210 dpv[n] = NULL; 211 xp = NULL; 212 213 /* 214 * Scan through the buffer in sort order, 215 * zapping the inode number of any 216 * duplicate names. 217 */ 218 for (n = 0; dpv[n]; n++) { 219 struct dirent *dp = dpv[n]; 220 221 if ((xp == NULL) || 222 strcmp(dp->d_name, xp->d_name)) { 223 xp = dp; 224 } else { 225 dp->d_fileno = 0; 226 } 227 if (dp->d_type == DT_WHT && 228 (dirp->dd_flags & DTF_HIDEW)) 229 dp->d_fileno = 0; 230 } 231 232 free(dpv); 233 break; 234 } else { 235 dpv = malloc((n+1) * sizeof(struct dirent *)); 236 if (dpv == NULL) 237 break; 238 } 239 } 240 241 dirp->dd_len = len; 242 dirp->dd_size = ddptr - dirp->dd_buf; 243 return (true); 244 } 245 246 static bool 247 is_unionstack(int fd) 248 { 249 int unionstack; 250 251 unionstack = _fcntl(fd, F_ISUNIONSTACK, 0); 252 if (unionstack != -1) 253 return (unionstack); 254 255 /* 256 * Should not happen unless running on a kernel without the op, 257 * but no use rendering the system useless in such a case. 258 */ 259 return (0); 260 } 261 262 /* 263 * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 264 */ 265 DIR * 266 __opendir_common(int fd, int flags, bool use_current_pos) 267 { 268 DIR *dirp; 269 int incr; 270 int saved_errno; 271 bool unionstack; 272 273 if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 274 return (NULL); 275 276 dirp->dd_buf = NULL; 277 dirp->dd_fd = fd; 278 dirp->dd_flags = flags; 279 dirp->dd_loc = 0; 280 dirp->dd_lock = NULL; 281 dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 282 LIST_INIT(&dirp->dd_td->td_locq); 283 dirp->dd_td->td_loccnt = 0; 284 dirp->dd_compat_de = NULL; 285 286 /* 287 * Use the system page size if that is a multiple of DIRBLKSIZ. 288 * Hopefully this can be a big win someday by allowing page 289 * trades to user space to be done by _getdirentries(). 290 */ 291 incr = getpagesize(); 292 if ((incr % DIRBLKSIZ) != 0) 293 incr = DIRBLKSIZ; 294 295 /* 296 * Determine whether this directory is the top of a union stack. 297 */ 298 unionstack = false; 299 if (flags & DTF_NODUP) { 300 unionstack = is_unionstack(fd); 301 } 302 303 if (unionstack) { 304 if (!_filldir(dirp, use_current_pos)) 305 goto fail; 306 dirp->dd_flags |= __DTF_READALL; 307 } else { 308 dirp->dd_len = incr; 309 dirp->dd_buf = malloc(dirp->dd_len); 310 if (dirp->dd_buf == NULL) 311 goto fail; 312 if (use_current_pos) { 313 /* 314 * Read the first batch of directory entries 315 * to prime dd_seek. This also checks if the 316 * fd passed to fdopendir() is a directory. 317 */ 318 dirp->dd_size = _getdirentries(dirp->dd_fd, 319 dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); 320 if (dirp->dd_size < 0) { 321 if (errno == EINVAL) 322 errno = ENOTDIR; 323 goto fail; 324 } 325 dirp->dd_flags |= __DTF_SKIPREAD; 326 } else { 327 dirp->dd_size = 0; 328 dirp->dd_seek = 0; 329 } 330 } 331 332 return (dirp); 333 334 fail: 335 saved_errno = errno; 336 free(dirp->dd_buf); 337 free(dirp); 338 errno = saved_errno; 339 return (NULL); 340 } 341