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/filio.h> 51 #include <sys/debug.h> 52 #include <sys/kmem.h> 53 #include <sys/cmn_err.h> 54 55 #if defined(_SYSCALL32_IMPL) || defined(_ILP32) 56 57 /* 58 * Get directory entries in a file system-independent format. 59 * 60 * The 32-bit version of this function now allocates a buffer to grab the 61 * directory entries in dirent64 formats from VOP_READDIR routines. 62 * The dirent64 structures are converted to dirent32 structures and 63 * copied to the user space. 64 * 65 * Both 32-bit and 64-bit versions of libc use getdents64() and therefore 66 * we don't expect any major performance impact due to the extra kmem_alloc's 67 * and copying done in this routine. 68 */ 69 70 #define MAXGETDENTS_SIZE (64 * 1024) 71 72 /* 73 * Native 32-bit system call for non-large-file applications. 74 */ 75 int 76 getdents32(int fd, void *buf, size_t count) 77 { 78 vnode_t *vp; 79 file_t *fp; 80 struct uio auio; 81 struct iovec aiov; 82 register int error; 83 int sink; 84 char *newbuf; 85 char *obuf; 86 int bufsize; 87 int osize, nsize; 88 struct dirent64 *dp; 89 struct dirent32 *op; 90 91 if (count < sizeof (struct dirent32)) 92 return (set_errno(EINVAL)); 93 94 if ((fp = getf(fd)) == NULL) 95 return (set_errno(EBADF)); 96 vp = fp->f_vnode; 97 if (vp->v_type != VDIR) { 98 releasef(fd); 99 return (set_errno(ENOTDIR)); 100 } 101 102 /* 103 * Don't let the user overcommit kernel resources. 104 */ 105 if (count > MAXGETDENTS_SIZE) 106 count = MAXGETDENTS_SIZE; 107 108 bufsize = count; 109 newbuf = kmem_alloc(bufsize, KM_SLEEP); 110 obuf = kmem_alloc(bufsize, KM_SLEEP); 111 112 aiov.iov_base = newbuf; 113 aiov.iov_len = count; 114 auio.uio_iov = &aiov; 115 auio.uio_iovcnt = 1; 116 auio.uio_loffset = fp->f_offset; 117 auio.uio_segflg = UIO_SYSSPACE; 118 auio.uio_resid = count; 119 auio.uio_fmode = 0; 120 auio.uio_extflg = UIO_COPY_CACHED; 121 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 122 error = VOP_READDIR(vp, &auio, fp->f_cred, &sink); 123 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 124 if (error) 125 goto out; 126 count = count - auio.uio_resid; 127 fp->f_offset = auio.uio_loffset; 128 129 dp = (struct dirent64 *)newbuf; 130 op = (struct dirent32 *)obuf; 131 osize = 0; 132 nsize = 0; 133 134 while (nsize < count) { 135 uint32_t reclen, namlen; 136 137 /* 138 * This check ensures that the 64 bit d_ino and d_off 139 * fields will fit into their 32 bit equivalents. 140 * 141 * Although d_off is a signed value, the check is done 142 * against the full 32 bits because certain file systems, 143 * NFS for one, allow directory cookies to use the full 144 * 32 bits. We use uint64_t because there is no exact 145 * unsigned analog to the off64_t type of dp->d_off. 146 */ 147 if (dp->d_ino > (ino64_t)UINT32_MAX || 148 dp->d_off > (uint64_t)UINT32_MAX) { 149 error = EOVERFLOW; 150 goto out; 151 } 152 op->d_ino = (ino32_t)dp->d_ino; 153 op->d_off = (off32_t)dp->d_off; 154 namlen = strlen(dp->d_name); 155 reclen = DIRENT32_RECLEN(namlen); 156 op->d_reclen = (uint16_t)reclen; 157 158 /* use strncpy(9f) to zero out uninitialized bytes */ 159 160 (void) strncpy(op->d_name, dp->d_name, 161 DIRENT32_NAMELEN(reclen)); 162 nsize += (uint_t)dp->d_reclen; 163 osize += (uint_t)op->d_reclen; 164 dp = (struct dirent64 *)((char *)dp + (uint_t)dp->d_reclen); 165 op = (struct dirent32 *)((char *)op + (uint_t)op->d_reclen); 166 } 167 168 ASSERT(osize <= count); 169 ASSERT((char *)op <= (char *)obuf + bufsize); 170 ASSERT((char *)dp <= (char *)newbuf + bufsize); 171 172 if ((error = copyout(obuf, buf, osize)) < 0) 173 error = EFAULT; 174 out: 175 kmem_free(newbuf, bufsize); 176 kmem_free(obuf, bufsize); 177 178 if (error) { 179 releasef(fd); 180 return (set_errno(error)); 181 } 182 183 releasef(fd); 184 return (osize); 185 } 186 187 #endif /* _SYSCALL32 || _ILP32 */ 188 189 int 190 getdents64(int fd, void *buf, size_t count) 191 { 192 vnode_t *vp; 193 file_t *fp; 194 struct uio auio; 195 struct iovec aiov; 196 register int error; 197 int sink; 198 199 if (count < sizeof (struct dirent64)) 200 return (set_errno(EINVAL)); 201 202 /* 203 * Don't let the user overcommit kernel resources. 204 */ 205 if (count > MAXGETDENTS_SIZE) 206 count = MAXGETDENTS_SIZE; 207 208 if ((fp = getf(fd)) == NULL) 209 return (set_errno(EBADF)); 210 vp = fp->f_vnode; 211 if (vp->v_type != VDIR) { 212 releasef(fd); 213 return (set_errno(ENOTDIR)); 214 } 215 aiov.iov_base = buf; 216 aiov.iov_len = count; 217 auio.uio_iov = &aiov; 218 auio.uio_iovcnt = 1; 219 auio.uio_loffset = fp->f_offset; 220 auio.uio_segflg = UIO_USERSPACE; 221 auio.uio_resid = count; 222 auio.uio_fmode = 0; 223 auio.uio_extflg = UIO_COPY_CACHED; 224 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 225 error = VOP_READDIR(vp, &auio, fp->f_cred, &sink); 226 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 227 if (error) { 228 releasef(fd); 229 return (set_errno(error)); 230 } 231 count = count - auio.uio_resid; 232 fp->f_offset = auio.uio_loffset; 233 releasef(fd); 234 return (count); 235 } 236