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 /* 247 * Return true if the file descriptor is associated with a file from a 248 * union file system or from a file system mounted with the union flag. 249 */ 250 static bool 251 is_unionstack(int fd) 252 { 253 /* 254 * This call shouldn't fail, but if it does, just assume that the 255 * answer is no. 256 */ 257 return (_fcntl(fd, F_ISUNIONSTACK, 0) > 0); 258 } 259 260 /* 261 * Common routine for opendir(3), __opendir2(3) and fdopendir(3). 262 */ 263 DIR * 264 __opendir_common(int fd, int flags, bool use_current_pos) 265 { 266 DIR *dirp; 267 int incr; 268 int saved_errno; 269 bool unionstack; 270 271 if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 272 return (NULL); 273 274 dirp->dd_buf = NULL; 275 dirp->dd_fd = fd; 276 dirp->dd_flags = flags; 277 dirp->dd_loc = 0; 278 dirp->dd_lock = NULL; 279 dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 280 LIST_INIT(&dirp->dd_td->td_locq); 281 dirp->dd_td->td_loccnt = 0; 282 dirp->dd_compat_de = NULL; 283 284 /* 285 * Use the system page size if that is a multiple of DIRBLKSIZ. 286 * Hopefully this can be a big win someday by allowing page 287 * trades to user space to be done by _getdirentries(). 288 */ 289 incr = getpagesize(); 290 if ((incr % DIRBLKSIZ) != 0) 291 incr = DIRBLKSIZ; 292 293 /* 294 * Determine whether this directory is the top of a union stack. 295 */ 296 unionstack = false; 297 if (flags & DTF_NODUP) { 298 unionstack = is_unionstack(fd); 299 } 300 301 if (unionstack) { 302 if (!_filldir(dirp, use_current_pos)) 303 goto fail; 304 dirp->dd_flags |= __DTF_READALL; 305 } else { 306 dirp->dd_len = incr; 307 dirp->dd_buf = malloc(dirp->dd_len); 308 if (dirp->dd_buf == NULL) 309 goto fail; 310 if (use_current_pos) { 311 /* 312 * Read the first batch of directory entries 313 * to prime dd_seek. This also checks if the 314 * fd passed to fdopendir() is a directory. 315 */ 316 dirp->dd_size = _getdirentries(dirp->dd_fd, 317 dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); 318 if (dirp->dd_size < 0) { 319 if (errno == EINVAL) 320 errno = ENOTDIR; 321 goto fail; 322 } 323 dirp->dd_flags |= __DTF_SKIPREAD; 324 } else { 325 dirp->dd_size = 0; 326 dirp->dd_seek = 0; 327 } 328 } 329 330 return (dirp); 331 332 fail: 333 saved_errno = errno; 334 free(dirp->dd_buf); 335 free(dirp); 336 errno = saved_errno; 337 return (NULL); 338 } 339