1 /*- 2 * Copyright (c) 2017 M. Warner Losh <[email protected]> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 #define _WANT_FREEBSD11_DIRENT 28 29 #include "namespace.h" 30 #include <sys/param.h> 31 #include <sys/syscall.h> 32 #include "compat-ino64.h" 33 #include <dirent.h> 34 #include <errno.h> 35 #include <stddef.h> 36 #include <stdlib.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <unistd.h> 40 #include "libc_private.h" 41 42 static ssize_t 43 __cvt_dirents_from11(const char *de11, ssize_t len11, char *de, ssize_t len) 44 { 45 struct dirent *dst; 46 const struct freebsd11_dirent *src; 47 const char *edst, *esrc; 48 ssize_t rlen; 49 50 src = (const struct freebsd11_dirent *)de11; 51 dst = (struct dirent *)de; 52 esrc = de11 + len11; 53 edst = de + len; 54 while ((const char *)src < esrc && (const char *)dst < edst) { 55 rlen = roundup(offsetof(struct dirent, d_name) + src->d_namlen + 1, 8); 56 if ((const char *)dst + rlen >= edst) 57 break; 58 dst->d_fileno = src->d_fileno; 59 dst->d_off = 0; /* nothing uses it yet, so safe for now */ 60 dst->d_reclen = rlen; 61 dst->d_type = src->d_type; 62 dst->d_pad0 = 0; 63 dst->d_namlen = src->d_namlen; 64 dst->d_pad1 = 0; 65 memset(dst->d_name, 0, roundup(src->d_namlen + 1, 8)); 66 memcpy(dst->d_name, src->d_name, src->d_namlen); 67 dst = (struct dirent *)((char *)dst + rlen); 68 src = (const struct freebsd11_dirent *)((const char *)src + src->d_reclen); 69 } 70 return ((char *)dst - de); 71 } 72 73 #undef getdirentries 74 __weak_reference(_getdirentries, getdirentries); 75 76 #pragma weak _getdirentries 77 ssize_t 78 _getdirentries(int fd, char *buf, size_t nbytes, off_t *basep) 79 { 80 char *oldbuf; 81 size_t len; 82 ssize_t rv; 83 84 if (__getosreldate() >= INO64_FIRST) 85 return (__sys_getdirentries(fd, buf, nbytes, basep)); 86 87 /* 88 * Because the old system call returns entries that are smaller than the 89 * new, we could wind up in a situation where we have too many to fit in 90 * the buffer with the new encoding. So sacrifice a small bit of 91 * efficiency to ensure that never happens. We pick 1/4 the size round 92 * up to the next DIRBLKSIZ. This will guarnatee enough room exists in 93 * the dst buffer due to changes in efficiency in packing dirent 94 * entries. We don't check against minimum block size to avoid a lot of 95 * stat calls, we'll see if that's wise or not. 96 * TBD: Will this difference matter to lseek? 97 */ 98 len = roundup(nbytes / 4, DIRBLKSIZ); 99 oldbuf = malloc(len); 100 if (oldbuf == NULL) { 101 errno = EINVAL; /* ENOMEM not in possible list */ 102 return (-1); 103 } 104 rv = syscall(SYS_freebsd11_getdirentries, fd, oldbuf, len, basep); 105 if (rv == -1) { 106 free(oldbuf); 107 return (rv); 108 } 109 if (rv > 0) 110 rv = __cvt_dirents_from11(oldbuf, rv, buf, nbytes); 111 free(oldbuf); 112 113 return (rv); 114 } 115