1 /* $NetBSD: vfs_syscalls_30.c,v 1.6 2005/12/11 12:19:56 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: vfs_syscalls_30.c,v 1.6 2005/12/11 12:19:56 christos Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/namei.h> 44 #include <sys/filedesc.h> 45 #include <sys/kernel.h> 46 #include <sys/file.h> 47 #include <sys/stat.h> 48 #include <sys/socketvar.h> 49 #include <sys/vnode.h> 50 #include <sys/mount.h> 51 #include <sys/proc.h> 52 #include <sys/uio.h> 53 #include <sys/dirent.h> 54 #include <sys/malloc.h> 55 56 #include <sys/sa.h> 57 #include <sys/syscallargs.h> 58 59 #include <compat/common/compat_util.h> 60 #include <compat/sys/stat.h> 61 #include <compat/sys/dirent.h> 62 63 static void cvtstat(struct stat13 *, const struct stat *); 64 65 /* 66 * Convert from a new to an old stat structure. 67 */ 68 static void 69 cvtstat(struct stat13 *ost, const struct stat *st) 70 { 71 72 ost->st_dev = st->st_dev; 73 ost->st_ino = (uint32_t)st->st_ino; 74 ost->st_mode = st->st_mode; 75 ost->st_nlink = st->st_nlink; 76 ost->st_uid = st->st_uid; 77 ost->st_gid = st->st_gid; 78 ost->st_rdev = st->st_rdev; 79 ost->st_atimespec = st->st_atimespec; 80 ost->st_mtimespec = st->st_mtimespec; 81 ost->st_ctimespec = st->st_ctimespec; 82 ost->st_birthtimespec = st->st_birthtimespec; 83 ost->st_size = st->st_size; 84 ost->st_blocks = st->st_blocks; 85 ost->st_blksize = st->st_blksize; 86 ost->st_flags = st->st_flags; 87 ost->st_gen = st->st_gen; 88 } 89 90 /* 91 * Get file status; this version follows links. 92 */ 93 /* ARGSUSED */ 94 int 95 compat_30_sys___stat13(struct lwp *l, void *v, register_t *retval) 96 { 97 struct compat_30_sys___stat13_args /* { 98 syscallarg(const char *) path; 99 syscallarg(struct stat13 *) ub; 100 } */ *uap = v; 101 struct stat sb; 102 struct stat13 osb; 103 int error; 104 struct nameidata nd; 105 106 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, 107 SCARG(uap, path), l); 108 if ((error = namei(&nd)) != 0) 109 return error; 110 error = vn_stat(nd.ni_vp, &sb, l); 111 vput(nd.ni_vp); 112 if (error) 113 return error; 114 cvtstat(&osb, &sb); 115 error = copyout(&osb, SCARG(uap, ub), sizeof (osb)); 116 return error; 117 } 118 119 120 /* 121 * Get file status; this version does not follow links. 122 */ 123 /* ARGSUSED */ 124 int 125 compat_30_sys___lstat13(struct lwp *l, void *v, register_t *retval) 126 { 127 struct compat_30_sys___lstat13_args /* { 128 syscallarg(const char *) path; 129 syscallarg(struct stat13 *) ub; 130 } */ *uap = v; 131 struct stat sb; 132 struct stat13 osb; 133 int error; 134 struct nameidata nd; 135 136 NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, 137 SCARG(uap, path), l); 138 if ((error = namei(&nd)) != 0) 139 return error; 140 error = vn_stat(nd.ni_vp, &sb, l); 141 vput(nd.ni_vp); 142 if (error) 143 return error; 144 cvtstat(&osb, &sb); 145 error = copyout(&osb, SCARG(uap, ub), sizeof (osb)); 146 return error; 147 } 148 149 /* 150 * Return status information about a file descriptor. 151 */ 152 /* ARGSUSED */ 153 int 154 compat_30_sys___fstat13(struct lwp *l, void *v, register_t *retval) 155 { 156 struct compat_30_sys___fstat13_args /* { 157 syscallarg(int) fd; 158 syscallarg(struct stat13 *) sb; 159 } */ *uap = v; 160 struct proc *p = l->l_proc; 161 int fd = SCARG(uap, fd); 162 struct filedesc *fdp = p->p_fd; 163 struct file *fp; 164 struct stat sb; 165 struct stat13 osb; 166 int error; 167 168 if ((fp = fd_getfile(fdp, fd)) == NULL) 169 return EBADF; 170 171 FILE_USE(fp); 172 error = (*fp->f_ops->fo_stat)(fp, &sb, l); 173 FILE_UNUSE(fp, l); 174 175 if (error) 176 return error; 177 cvtstat(&osb, &sb); 178 error = copyout(&osb, SCARG(uap, sb), sizeof (osb)); 179 return error; 180 } 181 182 /* 183 * Read a block of directory entries in a file system independent format. 184 */ 185 int 186 compat_30_sys_getdents(struct lwp *l, void *v, register_t *retval) 187 { 188 struct compat_30_sys_getdents_args /* { 189 syscallarg(int) fd; 190 syscallarg(char *) buf; 191 syscallarg(size_t) count; 192 } */ *uap = v; 193 struct proc *p = l->l_proc; 194 struct dirent *bdp; 195 struct vnode *vp; 196 caddr_t inp, tbuf; /* BSD-format */ 197 int len, reclen; /* BSD-format */ 198 caddr_t outp; /* NetBSD-3.0-format */ 199 int resid; 200 struct file *fp; 201 struct uio auio; 202 struct iovec aiov; 203 struct dirent12 idb; 204 off_t off; /* true file offset */ 205 int buflen, error, eofflag; 206 off_t *cookiebuf = NULL, *cookie; 207 int ncookies; 208 209 /* getvnode() will use the descriptor for us */ 210 if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) 211 return error; 212 213 if ((fp->f_flag & FREAD) == 0) { 214 error = EBADF; 215 goto out1; 216 } 217 218 vp = (struct vnode *)fp->f_data; 219 if (vp->v_type != VDIR) { 220 error = EINVAL; 221 goto out1; 222 } 223 224 buflen = min(MAXBSIZE, SCARG(uap, count)); 225 tbuf = malloc(buflen, M_TEMP, M_WAITOK); 226 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 227 off = fp->f_offset; 228 again: 229 aiov.iov_base = tbuf; 230 aiov.iov_len = buflen; 231 auio.uio_iov = &aiov; 232 auio.uio_iovcnt = 1; 233 auio.uio_rw = UIO_READ; 234 auio.uio_segflg = UIO_SYSSPACE; 235 auio.uio_lwp = NULL; 236 auio.uio_resid = buflen; 237 auio.uio_offset = off; 238 /* 239 * First we read into the malloc'ed buffer, then 240 * we massage it into user space, one record at a time. 241 */ 242 error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, 243 &ncookies); 244 if (error) 245 goto out; 246 247 inp = tbuf; 248 outp = SCARG(uap, buf); 249 resid = SCARG(uap, count); 250 if ((len = buflen - auio.uio_resid) == 0) 251 goto eof; 252 253 for (cookie = cookiebuf; len > 0; len -= reclen) { 254 bdp = (struct dirent *)inp; 255 reclen = bdp->d_reclen; 256 if (reclen & _DIRENT_ALIGN(bdp)) 257 panic("netbsd30_getdents: bad reclen %d", reclen); 258 if (cookie) 259 off = *cookie++; /* each entry points to the next */ 260 else 261 off += reclen; 262 if ((off >> 32) != 0) { 263 compat_offseterr(vp, "netbsd30_getdents"); 264 error = EINVAL; 265 goto out; 266 } 267 if (bdp->d_namlen >= sizeof(idb.d_name)) 268 idb.d_namlen = sizeof(idb.d_name) - 1; 269 else 270 idb.d_namlen = bdp->d_namlen; 271 idb.d_reclen = _DIRENT_SIZE(&idb); 272 if (reclen > len || resid < idb.d_reclen) { 273 /* entry too big for buffer, so just stop */ 274 outp++; 275 break; 276 } 277 /* 278 * Massage in place to make a NetBSD-3.0-shaped dirent 279 * (otherwise we have to worry about touching user memory 280 * outside of the copyout() call). 281 */ 282 idb.d_fileno = (u_int32_t)bdp->d_fileno; 283 idb.d_type = bdp->d_type; 284 (void)memcpy(idb.d_name, bdp->d_name, idb.d_namlen); 285 memset(idb.d_name + idb.d_namlen, 0, 286 idb.d_reclen - _DIRENT_NAMEOFF(&idb) - idb.d_namlen); 287 if ((error = copyout(&idb, outp, idb.d_reclen)) != 0) 288 goto out; 289 /* advance past this real entry */ 290 inp += reclen; 291 /* advance output past NetBSD-3.0-shaped entry */ 292 outp += idb.d_reclen; 293 resid -= idb.d_reclen; 294 } 295 296 /* if we squished out the whole block, try again */ 297 if (outp == SCARG(uap, buf)) 298 goto again; 299 fp->f_offset = off; /* update the vnode offset */ 300 301 eof: 302 *retval = SCARG(uap, count) - resid; 303 out: 304 VOP_UNLOCK(vp, 0); 305 if (cookiebuf) 306 free(cookiebuf, M_TEMP); 307 free(tbuf, M_TEMP); 308 out1: 309 FILE_UNUSE(fp, l); 310 return error; 311 } 312