1 /* $NetBSD: vfs_syscalls_30.c,v 1.41 2020/01/31 09:01:23 maxv Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2008 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: vfs_syscalls_30.c,v 1.41 2020/01/31 09:01:23 maxv Exp $"); 33 34 #if defined(_KERNEL_OPT) 35 #include "opt_compat_netbsd.h" 36 #endif 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/namei.h> 41 #include <sys/filedesc.h> 42 #include <sys/kernel.h> 43 #include <sys/file.h> 44 #include <sys/stat.h> 45 #include <sys/socketvar.h> 46 #include <sys/vnode.h> 47 #include <sys/mount.h> 48 #include <sys/proc.h> 49 #include <sys/uio.h> 50 #include <sys/dirent.h> 51 #include <sys/malloc.h> 52 #include <sys/kauth.h> 53 #include <sys/vfs_syscalls.h> 54 #include <sys/syscall.h> 55 #include <sys/syscallvar.h> 56 #include <sys/syscallargs.h> 57 58 #include <compat/common/compat_mod.h> 59 #include <compat/common/compat_util.h> 60 61 #include <compat/sys/stat.h> 62 #include <compat/sys/dirent.h> 63 #include <compat/sys/mount.h> 64 #include <compat/sys/statvfs.h> 65 66 static void cvtstat(struct stat13 *, const struct stat *); 67 68 static const struct syscall_package vfs_syscalls_30_syscalls[] = { 69 { SYS_compat_30___fhstat30, 0, (sy_call_t *)compat_30_sys___fhstat30 }, 70 { SYS_compat_30___fstat13, 0, (sy_call_t *)compat_30_sys___fstat13 }, 71 { SYS_compat_30___lstat13, 0, (sy_call_t *)compat_30_sys___lstat13 }, 72 { SYS_compat_30___stat13, 0, (sy_call_t *)compat_30_sys___stat13 }, 73 { SYS_compat_30_fhopen, 0, (sy_call_t *)compat_30_sys_fhopen }, 74 { SYS_compat_30_fhstat, 0, (sy_call_t *)compat_30_sys_fhstat }, 75 { SYS_compat_30_fhstatvfs1, 0, (sy_call_t *)compat_30_sys_fhstatvfs1 }, 76 { SYS_compat_30_getdents, 0, (sy_call_t *)compat_30_sys_getdents }, 77 { SYS_compat_30_getfh, 0, (sy_call_t *)compat_30_sys_getfh }, 78 { 0,0, NULL } 79 }; 80 81 /* 82 * Convert from a new to an old stat structure. 83 */ 84 static void 85 cvtstat(struct stat13 *ost, const struct stat *st) 86 { 87 88 ost->st_dev = st->st_dev; 89 ost->st_ino = (uint32_t)st->st_ino; 90 ost->st_mode = st->st_mode; 91 ost->st_nlink = st->st_nlink; 92 ost->st_uid = st->st_uid; 93 ost->st_gid = st->st_gid; 94 ost->st_rdev = st->st_rdev; 95 timespec_to_timespec50(&st->st_atimespec, &ost->st_atimespec); 96 timespec_to_timespec50(&st->st_mtimespec, &ost->st_mtimespec); 97 timespec_to_timespec50(&st->st_ctimespec, &ost->st_ctimespec); 98 timespec_to_timespec50(&st->st_birthtimespec, &ost->st_birthtimespec); 99 ost->st_size = st->st_size; 100 ost->st_blocks = st->st_blocks; 101 ost->st_blksize = st->st_blksize; 102 ost->st_flags = st->st_flags; 103 ost->st_gen = st->st_gen; 104 } 105 106 /* 107 * Get file status; this version follows links. 108 */ 109 /* ARGSUSED */ 110 int 111 compat_30_sys___stat13(struct lwp *l, 112 const struct compat_30_sys___stat13_args *uap, register_t *retval) 113 { 114 /* { 115 syscallarg(const char *) path; 116 syscallarg(struct stat13 *) ub; 117 } */ 118 struct stat sb; 119 struct stat13 osb; 120 int error; 121 122 error = do_sys_stat(SCARG(uap, path), FOLLOW, &sb); 123 if (error) 124 return error; 125 cvtstat(&osb, &sb); 126 error = copyout(&osb, SCARG(uap, ub), sizeof (osb)); 127 return error; 128 } 129 130 131 /* 132 * Get file status; this version does not follow links. 133 */ 134 /* ARGSUSED */ 135 int 136 compat_30_sys___lstat13(struct lwp *l, 137 const struct compat_30_sys___lstat13_args *uap, register_t *retval) 138 { 139 /* { 140 syscallarg(const char *) path; 141 syscallarg(struct stat13 *) ub; 142 } */ 143 struct stat sb; 144 struct stat13 osb; 145 int error; 146 147 error = do_sys_stat(SCARG(uap, path), NOFOLLOW, &sb); 148 if (error) 149 return error; 150 cvtstat(&osb, &sb); 151 error = copyout(&osb, SCARG(uap, ub), sizeof (osb)); 152 return error; 153 } 154 155 /* ARGSUSED */ 156 int 157 compat_30_sys_fhstat(struct lwp *l, 158 const struct compat_30_sys_fhstat_args *uap, register_t *retval) 159 { 160 /* { 161 syscallarg(const struct compat_30_fhandle *) fhp; 162 syscallarg(struct stat13 *) sb; 163 } */ 164 struct stat sb; 165 struct stat13 osb; 166 int error; 167 struct compat_30_fhandle fh; 168 struct mount *mp; 169 struct vnode *vp; 170 171 /* 172 * Must be super user 173 */ 174 if ((error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE, 175 0, NULL, NULL, NULL))) 176 return (error); 177 178 if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fh))) != 0) 179 return (error); 180 181 if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) 182 return (ESTALE); 183 if (mp->mnt_op->vfs_fhtovp == NULL) 184 return EOPNOTSUPP; 185 error = VFS_FHTOVP(mp, (struct fid*)&fh.fh_fid, LK_EXCLUSIVE, &vp); 186 if (error != 0) 187 return (error); 188 error = vn_stat(vp, &sb); 189 vput(vp); 190 if (error) 191 return (error); 192 cvtstat(&osb, &sb); 193 error = copyout(&osb, SCARG(uap, sb), sizeof(osb)); 194 return (error); 195 } 196 197 /* 198 * Return status information about a file descriptor. 199 */ 200 /* ARGSUSED */ 201 int 202 compat_30_sys___fstat13(struct lwp *l, 203 const struct compat_30_sys___fstat13_args *uap, register_t *retval) 204 { 205 /* { 206 syscallarg(int) fd; 207 syscallarg(struct stat13 *) sb; 208 } */ 209 struct stat sb; 210 struct stat13 osb; 211 int error; 212 213 error = do_sys_fstat(SCARG(uap, fd), &sb); 214 if (error) 215 return error; 216 cvtstat(&osb, &sb); 217 error = copyout(&osb, SCARG(uap, sb), sizeof (osb)); 218 return error; 219 } 220 221 /* 222 * Read a block of directory entries in a file system independent format. 223 */ 224 int 225 compat_30_sys_getdents(struct lwp *l, 226 const struct compat_30_sys_getdents_args *uap, register_t *retval) 227 { 228 /* { 229 syscallarg(int) fd; 230 syscallarg(char *) buf; 231 syscallarg(size_t) count; 232 } */ 233 struct dirent *bdp; 234 struct vnode *vp; 235 char *inp, *tbuf; /* BSD-format */ 236 int len, reclen; /* BSD-format */ 237 char *outp; /* NetBSD-3.0-format */ 238 int resid; 239 struct file *fp; 240 struct uio auio; 241 struct iovec aiov; 242 struct dirent12 idb; 243 off_t off; /* true file offset */ 244 int buflen, error, eofflag; 245 off_t *cookiebuf = NULL, *cookie; 246 int ncookies; 247 248 /* fd_getvnode() will use the descriptor for us */ 249 if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) 250 return error; 251 252 if ((fp->f_flag & FREAD) == 0) { 253 error = EBADF; 254 goto out1; 255 } 256 257 vp = fp->f_vnode; 258 if (vp->v_type != VDIR) { 259 error = EINVAL; 260 goto out1; 261 } 262 263 buflen = uimin(MAXBSIZE, SCARG(uap, count)); 264 tbuf = malloc(buflen, M_TEMP, M_WAITOK); 265 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 266 off = fp->f_offset; 267 again: 268 aiov.iov_base = tbuf; 269 aiov.iov_len = buflen; 270 auio.uio_iov = &aiov; 271 auio.uio_iovcnt = 1; 272 auio.uio_rw = UIO_READ; 273 auio.uio_resid = buflen; 274 auio.uio_offset = off; 275 UIO_SETUP_SYSSPACE(&auio); 276 /* 277 * First we read into the malloc'ed buffer, then 278 * we massage it into user space, one record at a time. 279 */ 280 error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, 281 &ncookies); 282 if (error) 283 goto out; 284 285 inp = tbuf; 286 outp = SCARG(uap, buf); 287 resid = SCARG(uap, count); 288 if ((len = buflen - auio.uio_resid) == 0) 289 goto eof; 290 291 for (cookie = cookiebuf; len > 0; len -= reclen) { 292 bdp = (struct dirent *)inp; 293 reclen = bdp->d_reclen; 294 if (reclen & _DIRENT_ALIGN(bdp)) 295 panic("netbsd30_getdents: bad reclen %d", reclen); 296 if (cookie) 297 off = *cookie++; /* each entry points to the next */ 298 else 299 off += reclen; 300 if ((off >> 32) != 0) { 301 compat_offseterr(vp, "netbsd30_getdents"); 302 error = EINVAL; 303 goto out; 304 } 305 if (bdp->d_namlen >= sizeof(idb.d_name)) 306 idb.d_namlen = sizeof(idb.d_name) - 1; 307 else 308 idb.d_namlen = bdp->d_namlen; 309 idb.d_reclen = _DIRENT_SIZE(&idb); 310 if (reclen > len || resid < idb.d_reclen) { 311 /* entry too big for buffer, so just stop */ 312 outp++; 313 break; 314 } 315 /* 316 * Massage in place to make a NetBSD-3.0-shaped dirent 317 * (otherwise we have to worry about touching user memory 318 * outside of the copyout() call). 319 */ 320 idb.d_fileno = (u_int32_t)bdp->d_fileno; 321 idb.d_type = bdp->d_type; 322 (void)memcpy(idb.d_name, bdp->d_name, idb.d_namlen); 323 memset(idb.d_name + idb.d_namlen, 0, 324 idb.d_reclen - _DIRENT_NAMEOFF(&idb) - idb.d_namlen); 325 if ((error = copyout(&idb, outp, idb.d_reclen)) != 0) 326 goto out; 327 /* advance past this real entry */ 328 inp += reclen; 329 /* advance output past NetBSD-3.0-shaped entry */ 330 outp += idb.d_reclen; 331 resid -= idb.d_reclen; 332 } 333 334 /* if we squished out the whole block, try again */ 335 if (outp == SCARG(uap, buf)) { 336 if (cookiebuf) 337 free(cookiebuf, M_TEMP); 338 cookiebuf = NULL; 339 goto again; 340 } 341 fp->f_offset = off; /* update the vnode offset */ 342 343 eof: 344 *retval = SCARG(uap, count) - resid; 345 out: 346 VOP_UNLOCK(vp); 347 if (cookiebuf) 348 free(cookiebuf, M_TEMP); 349 free(tbuf, M_TEMP); 350 out1: 351 fd_putfile(SCARG(uap, fd)); 352 return error; 353 } 354 355 /* 356 * Get file handle system call 357 */ 358 int 359 compat_30_sys_getfh(struct lwp *l, const struct compat_30_sys_getfh_args *uap, 360 register_t *retval) 361 { 362 /* { 363 syscallarg(char *) fname; 364 syscallarg(struct compat_30_fhandle *) fhp; 365 } */ 366 struct vnode *vp; 367 struct compat_30_fhandle fh; 368 int error; 369 struct pathbuf *pb; 370 struct nameidata nd; 371 size_t sz; 372 373 /* 374 * Must be super user 375 */ 376 error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE, 377 0, NULL, NULL, NULL); 378 if (error) 379 return (error); 380 381 error = pathbuf_copyin(SCARG(uap, fname), &pb); 382 if (error) { 383 return error; 384 } 385 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb); 386 error = namei(&nd); 387 pathbuf_destroy(pb); 388 if (error) 389 return error; 390 vp = nd.ni_vp; 391 392 sz = sizeof(struct compat_30_fhandle); 393 error = vfs_composefh(vp, (void *)&fh, &sz); 394 vput(vp); 395 if (sz != FHANDLE_SIZE_COMPAT) { 396 error = EINVAL; 397 } 398 if (error) 399 return (error); 400 error = copyout(&fh, SCARG(uap, fhp), sizeof(struct compat_30_fhandle)); 401 return (error); 402 } 403 404 /* 405 * Open a file given a file handle. 406 * 407 * Check permissions, allocate an open file structure, 408 * and call the device open routine if any. 409 */ 410 int 411 compat_30_sys_fhopen(struct lwp *l, 412 const struct compat_30_sys_fhopen_args *uap, register_t *retval) 413 { 414 /* { 415 syscallarg(const fhandle_t *) fhp; 416 syscallarg(int) flags; 417 } */ 418 419 return dofhopen(l, SCARG(uap, fhp), FHANDLE_SIZE_COMPAT, 420 SCARG(uap, flags), retval); 421 } 422 423 /* ARGSUSED */ 424 int 425 compat_30_sys___fhstat30(struct lwp *l, 426 const struct compat_30_sys___fhstat30_args *uap_30, register_t *retval) 427 { 428 /* { 429 syscallarg(const fhandle_t *) fhp; 430 syscallarg(struct stat30 *) sb; 431 } */ 432 struct stat sb; 433 struct stat13 osb; 434 int error; 435 436 error = do_fhstat(l, SCARG(uap_30, fhp), FHANDLE_SIZE_COMPAT, &sb); 437 if (error) 438 return error; 439 cvtstat(&osb, &sb); 440 error = copyout(&osb, SCARG(uap_30, sb), sizeof (osb)); 441 return error; 442 } 443 444 /* ARGSUSED */ 445 int 446 compat_30_sys_fhstatvfs1(struct lwp *l, 447 const struct compat_30_sys_fhstatvfs1_args *uap, register_t *retval) 448 { 449 /* { 450 syscallarg(const fhandle_t *) fhp; 451 syscallarg(struct statvfs90 *) buf; 452 syscallarg(int) flags; 453 } */ 454 struct statvfs *sb = STATVFSBUF_GET(); 455 int error = do_fhstatvfs(l, SCARG(uap, fhp), FHANDLE_SIZE_COMPAT, 456 sb, SCARG(uap, flags)); 457 458 if (!error) { 459 error = statvfs_to_statvfs90_copy(sb, SCARG(uap, buf), 460 sizeof(struct statvfs90)); 461 } 462 463 STATVFSBUF_PUT(sb); 464 465 return error; 466 } 467 468 int 469 vfs_syscalls_30_init(void) 470 { 471 472 return syscall_establish(NULL, vfs_syscalls_30_syscalls); 473 } 474 475 int 476 vfs_syscalls_30_fini(void) 477 { 478 479 return syscall_disestablish(NULL, vfs_syscalls_30_syscalls); 480 } 481