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/types.h> 34 35 #include <dirent.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <stdbool.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include "un-namespace.h" 43 44 #include "gen-private.h" 45 #include "telldir.h" 46 47 DIR * 48 __opendir2(const char *name, int flags) 49 { 50 int fd; 51 DIR *dir; 52 int saved_errno; 53 54 if ((flags & (__DTF_READALL | __DTF_SKIPREAD)) != 0) 55 return (NULL); 56 if ((fd = _open(name, O_DIRECTORY | O_RDONLY | 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 ssize_t ret; 268 int incr; 269 int saved_errno; 270 bool unionstack; 271 272 if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) 273 return (NULL); 274 275 dirp->dd_buf = NULL; 276 dirp->dd_fd = fd; 277 dirp->dd_flags = flags; 278 dirp->dd_loc = 0; 279 dirp->dd_lock = NULL; 280 dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR)); 281 LIST_INIT(&dirp->dd_td->td_locq); 282 dirp->dd_td->td_loccnt = 0; 283 dirp->dd_compat_de = NULL; 284 285 /* 286 * Use the system page size if that is a multiple of DIRBLKSIZ. 287 * Hopefully this can be a big win someday by allowing page 288 * trades to user space to be done by _getdirentries(). 289 */ 290 incr = getpagesize(); 291 if ((incr % DIRBLKSIZ) != 0) 292 incr = DIRBLKSIZ; 293 294 /* 295 * Determine whether this directory is the top of a union stack. 296 */ 297 unionstack = false; 298 if (flags & DTF_NODUP) { 299 unionstack = is_unionstack(fd); 300 } 301 302 if (unionstack) { 303 if (!_filldir(dirp, use_current_pos)) 304 goto fail; 305 dirp->dd_flags |= __DTF_READALL; 306 } else { 307 dirp->dd_len = incr; 308 dirp->dd_buf = malloc(dirp->dd_len); 309 if (dirp->dd_buf == NULL) 310 goto fail; 311 if (use_current_pos) { 312 /* 313 * Read the first batch of directory entries 314 * to prime dd_seek. This also checks if the 315 * fd passed to fdopendir() is a directory. 316 */ 317 ret = _getdirentries(dirp->dd_fd, 318 dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); 319 if (ret < 0) 320 goto fail; 321 dirp->dd_size = (size_t)ret; 322 dirp->dd_flags |= __DTF_SKIPREAD; 323 } else { 324 dirp->dd_size = 0; 325 dirp->dd_seek = 0; 326 } 327 } 328 329 return (dirp); 330 331 fail: 332 saved_errno = errno; 333 free(dirp->dd_buf); 334 free(dirp); 335 errno = saved_errno; 336 return (NULL); 337 } 338