1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * Portions of this source code were derived from Berkeley 4.3 BSD 31 * under license from the Regents of the University of California. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/isa_defs.h> 36 #include <sys/types.h> 37 #include <sys/inttypes.h> 38 #include <sys/sysmacros.h> 39 #include <sys/cred.h> 40 #include <sys/dirent.h> 41 #include <sys/systm.h> 42 #include <sys/errno.h> 43 #include <sys/vnode.h> 44 #include <sys/file.h> 45 #include <sys/mode.h> 46 #include <sys/uio.h> 47 #include <sys/filio.h> 48 #include <sys/debug.h> 49 #include <sys/kmem.h> 50 #include <sys/cmn_err.h> 51 52 #if defined(_SYSCALL32_IMPL) || defined(_ILP32) 53 54 /* 55 * Get directory entries in a file system-independent format. 56 * 57 * The 32-bit version of this function now allocates a buffer to grab the 58 * directory entries in dirent64 formats from VOP_READDIR routines. 59 * The dirent64 structures are converted to dirent32 structures and 60 * copied to the user space. 61 * 62 * Both 32-bit and 64-bit versions of libc use getdents64() and therefore 63 * we don't expect any major performance impact due to the extra kmem_alloc's 64 * and copying done in this routine. 65 */ 66 67 /* 68 * Native 32-bit system call for non-large-file applications. 69 */ 70 int 71 getdents32(int fd, void *buf, size_t count) 72 { 73 vnode_t *vp; 74 file_t *fp; 75 struct uio auio; 76 struct iovec aiov; 77 register int error; 78 int sink; 79 char *newbuf; 80 char *obuf; 81 int bufsize; 82 int osize, nsize; 83 struct dirent64 *dp; 84 struct dirent32 *op; 85 86 if (count < sizeof (struct dirent32)) 87 return (set_errno(EINVAL)); 88 89 if ((fp = getf(fd)) == NULL) 90 return (set_errno(EBADF)); 91 vp = fp->f_vnode; 92 if (vp->v_type != VDIR) { 93 releasef(fd); 94 return (set_errno(ENOTDIR)); 95 } 96 if (!(fp->f_flag & FREAD)) { 97 releasef(fd); 98 return (set_errno(EBADF)); 99 } 100 101 /* 102 * Don't let the user overcommit kernel resources. 103 */ 104 if (count > MAXGETDENTS_SIZE) 105 count = MAXGETDENTS_SIZE; 106 107 bufsize = count; 108 newbuf = kmem_alloc(bufsize, KM_SLEEP); 109 obuf = kmem_alloc(bufsize, KM_SLEEP); 110 111 aiov.iov_base = newbuf; 112 aiov.iov_len = count; 113 auio.uio_iov = &aiov; 114 auio.uio_iovcnt = 1; 115 auio.uio_loffset = fp->f_offset; 116 auio.uio_segflg = UIO_SYSSPACE; 117 auio.uio_resid = count; 118 auio.uio_fmode = 0; 119 auio.uio_extflg = UIO_COPY_CACHED; 120 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 121 error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0); 122 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 123 if (error) 124 goto out; 125 count = count - auio.uio_resid; 126 fp->f_offset = auio.uio_loffset; 127 128 dp = (struct dirent64 *)newbuf; 129 op = (struct dirent32 *)obuf; 130 osize = 0; 131 nsize = 0; 132 133 while (nsize < count) { 134 uint32_t reclen, namlen; 135 136 /* 137 * This check ensures that the 64 bit d_ino and d_off 138 * fields will fit into their 32 bit equivalents. 139 * 140 * Although d_off is a signed value, the check is done 141 * against the full 32 bits because certain file systems, 142 * NFS for one, allow directory cookies to use the full 143 * 32 bits. We use uint64_t because there is no exact 144 * unsigned analog to the off64_t type of dp->d_off. 145 */ 146 if (dp->d_ino > (ino64_t)UINT32_MAX || 147 dp->d_off > (uint64_t)UINT32_MAX) { 148 error = EOVERFLOW; 149 goto out; 150 } 151 op->d_ino = (ino32_t)dp->d_ino; 152 op->d_off = (off32_t)dp->d_off; 153 namlen = strlen(dp->d_name); 154 reclen = DIRENT32_RECLEN(namlen); 155 op->d_reclen = (uint16_t)reclen; 156 157 /* use strncpy(9f) to zero out uninitialized bytes */ 158 159 (void) strncpy(op->d_name, dp->d_name, 160 DIRENT32_NAMELEN(reclen)); 161 nsize += (uint_t)dp->d_reclen; 162 osize += (uint_t)op->d_reclen; 163 dp = (struct dirent64 *)((char *)dp + (uint_t)dp->d_reclen); 164 op = (struct dirent32 *)((char *)op + (uint_t)op->d_reclen); 165 } 166 167 ASSERT(osize <= count); 168 ASSERT((char *)op <= (char *)obuf + bufsize); 169 ASSERT((char *)dp <= (char *)newbuf + bufsize); 170 171 if ((error = copyout(obuf, buf, osize)) < 0) 172 error = EFAULT; 173 out: 174 kmem_free(newbuf, bufsize); 175 kmem_free(obuf, bufsize); 176 177 if (error) { 178 releasef(fd); 179 return (set_errno(error)); 180 } 181 182 releasef(fd); 183 return (osize); 184 } 185 186 #endif /* _SYSCALL32 || _ILP32 */ 187 188 int 189 getdents64(int fd, void *buf, size_t count) 190 { 191 vnode_t *vp; 192 file_t *fp; 193 struct uio auio; 194 struct iovec aiov; 195 register int error; 196 int sink; 197 198 if (count < sizeof (struct dirent64)) 199 return (set_errno(EINVAL)); 200 201 /* 202 * Don't let the user overcommit kernel resources. 203 */ 204 if (count > MAXGETDENTS_SIZE) 205 count = MAXGETDENTS_SIZE; 206 207 if ((fp = getf(fd)) == NULL) 208 return (set_errno(EBADF)); 209 vp = fp->f_vnode; 210 if (vp->v_type != VDIR) { 211 releasef(fd); 212 return (set_errno(ENOTDIR)); 213 } 214 if (!(fp->f_flag & FREAD)) { 215 releasef(fd); 216 return (set_errno(EBADF)); 217 } 218 aiov.iov_base = buf; 219 aiov.iov_len = count; 220 auio.uio_iov = &aiov; 221 auio.uio_iovcnt = 1; 222 auio.uio_loffset = fp->f_offset; 223 auio.uio_segflg = UIO_USERSPACE; 224 auio.uio_resid = count; 225 auio.uio_fmode = 0; 226 auio.uio_extflg = UIO_COPY_CACHED; 227 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 228 error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0); 229 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 230 if (error) { 231 releasef(fd); 232 return (set_errno(error)); 233 } 234 count = count - auio.uio_resid; 235 fp->f_offset = auio.uio_loffset; 236 releasef(fd); 237 return (count); 238 } 239