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