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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 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 97 /* 98 * Don't let the user overcommit kernel resources. 99 */ 100 if (count > MAXGETDENTS_SIZE) 101 count = MAXGETDENTS_SIZE; 102 103 bufsize = count; 104 newbuf = kmem_alloc(bufsize, KM_SLEEP); 105 obuf = kmem_alloc(bufsize, KM_SLEEP); 106 107 aiov.iov_base = newbuf; 108 aiov.iov_len = count; 109 auio.uio_iov = &aiov; 110 auio.uio_iovcnt = 1; 111 auio.uio_loffset = fp->f_offset; 112 auio.uio_segflg = UIO_SYSSPACE; 113 auio.uio_resid = count; 114 auio.uio_fmode = 0; 115 auio.uio_extflg = UIO_COPY_CACHED; 116 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 117 error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0); 118 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 119 if (error) 120 goto out; 121 count = count - auio.uio_resid; 122 fp->f_offset = auio.uio_loffset; 123 124 dp = (struct dirent64 *)newbuf; 125 op = (struct dirent32 *)obuf; 126 osize = 0; 127 nsize = 0; 128 129 while (nsize < count) { 130 uint32_t reclen, namlen; 131 132 /* 133 * This check ensures that the 64 bit d_ino and d_off 134 * fields will fit into their 32 bit equivalents. 135 * 136 * Although d_off is a signed value, the check is done 137 * against the full 32 bits because certain file systems, 138 * NFS for one, allow directory cookies to use the full 139 * 32 bits. We use uint64_t because there is no exact 140 * unsigned analog to the off64_t type of dp->d_off. 141 */ 142 if (dp->d_ino > (ino64_t)UINT32_MAX || 143 dp->d_off > (uint64_t)UINT32_MAX) { 144 error = EOVERFLOW; 145 goto out; 146 } 147 op->d_ino = (ino32_t)dp->d_ino; 148 op->d_off = (off32_t)dp->d_off; 149 namlen = strlen(dp->d_name); 150 reclen = DIRENT32_RECLEN(namlen); 151 op->d_reclen = (uint16_t)reclen; 152 153 /* use strncpy(9f) to zero out uninitialized bytes */ 154 155 (void) strncpy(op->d_name, dp->d_name, 156 DIRENT32_NAMELEN(reclen)); 157 nsize += (uint_t)dp->d_reclen; 158 osize += (uint_t)op->d_reclen; 159 dp = (struct dirent64 *)((char *)dp + (uint_t)dp->d_reclen); 160 op = (struct dirent32 *)((char *)op + (uint_t)op->d_reclen); 161 } 162 163 ASSERT(osize <= count); 164 ASSERT((char *)op <= (char *)obuf + bufsize); 165 ASSERT((char *)dp <= (char *)newbuf + bufsize); 166 167 if ((error = copyout(obuf, buf, osize)) < 0) 168 error = EFAULT; 169 out: 170 kmem_free(newbuf, bufsize); 171 kmem_free(obuf, bufsize); 172 173 if (error) { 174 releasef(fd); 175 return (set_errno(error)); 176 } 177 178 releasef(fd); 179 return (osize); 180 } 181 182 #endif /* _SYSCALL32 || _ILP32 */ 183 184 int 185 getdents64(int fd, void *buf, size_t count) 186 { 187 vnode_t *vp; 188 file_t *fp; 189 struct uio auio; 190 struct iovec aiov; 191 register int error; 192 int sink; 193 194 if (count < sizeof (struct dirent64)) 195 return (set_errno(EINVAL)); 196 197 /* 198 * Don't let the user overcommit kernel resources. 199 */ 200 if (count > MAXGETDENTS_SIZE) 201 count = MAXGETDENTS_SIZE; 202 203 if ((fp = getf(fd)) == NULL) 204 return (set_errno(EBADF)); 205 vp = fp->f_vnode; 206 if (vp->v_type != VDIR) { 207 releasef(fd); 208 return (set_errno(ENOTDIR)); 209 } 210 aiov.iov_base = buf; 211 aiov.iov_len = count; 212 auio.uio_iov = &aiov; 213 auio.uio_iovcnt = 1; 214 auio.uio_loffset = fp->f_offset; 215 auio.uio_segflg = UIO_USERSPACE; 216 auio.uio_resid = count; 217 auio.uio_fmode = 0; 218 auio.uio_extflg = UIO_COPY_CACHED; 219 (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); 220 error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0); 221 VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); 222 if (error) { 223 releasef(fd); 224 return (set_errno(error)); 225 } 226 count = count - auio.uio_resid; 227 fp->f_offset = auio.uio_loffset; 228 releasef(fd); 229 return (count); 230 } 231