1 /* $NetBSD: vfs_syscalls_30.c,v 1.45 2022/03/12 20:46:03 riastradh 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.45 2022/03/12 20:46:03 riastradh 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 const struct syscall_package vfs_syscalls_30_syscalls[] = { 67 { SYS_compat_30___fhstat30, 0, (sy_call_t *)compat_30_sys___fhstat30 }, 68 { SYS_compat_30___fstat13, 0, (sy_call_t *)compat_30_sys___fstat13 }, 69 { SYS_compat_30___lstat13, 0, (sy_call_t *)compat_30_sys___lstat13 }, 70 { SYS_compat_30___stat13, 0, (sy_call_t *)compat_30_sys___stat13 }, 71 { SYS_compat_30_fhopen, 0, (sy_call_t *)compat_30_sys_fhopen }, 72 { SYS_compat_30_fhstat, 0, (sy_call_t *)compat_30_sys_fhstat }, 73 { SYS_compat_30_fhstatvfs1, 0, (sy_call_t *)compat_30_sys_fhstatvfs1 }, 74 { SYS_compat_30_getdents, 0, (sy_call_t *)compat_30_sys_getdents }, 75 { SYS_compat_30_getfh, 0, (sy_call_t *)compat_30_sys_getfh }, 76 { 0,0, NULL } 77 }; 78 79 /* 80 * Convert from a new to an old stat structure. 81 */ 82 static void 83 cvtstat(struct stat13 *ost, const struct stat *st) 84 { 85 86 /* Handle any padding. */ 87 memset(ost, 0, sizeof(*ost)); 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 return copyout(&osb, SCARG(uap, ub), sizeof(osb)); 127 } 128 129 130 /* 131 * Get file status; this version does not follow links. 132 */ 133 /* ARGSUSED */ 134 int 135 compat_30_sys___lstat13(struct lwp *l, 136 const struct compat_30_sys___lstat13_args *uap, register_t *retval) 137 { 138 /* { 139 syscallarg(const char *) path; 140 syscallarg(struct stat13 *) ub; 141 } */ 142 struct stat sb; 143 struct stat13 osb; 144 int error; 145 146 error = do_sys_stat(SCARG(uap, path), NOFOLLOW, &sb); 147 if (error) 148 return error; 149 cvtstat(&osb, &sb); 150 return copyout(&osb, SCARG(uap, ub), sizeof(osb)); 151 } 152 153 /* ARGSUSED */ 154 int 155 compat_30_sys_fhstat(struct lwp *l, 156 const struct compat_30_sys_fhstat_args *uap, register_t *retval) 157 { 158 /* { 159 syscallarg(const struct compat_30_fhandle *) fhp; 160 syscallarg(struct stat13 *) sb; 161 } */ 162 struct stat sb; 163 struct stat13 osb; 164 int error; 165 166 error = do_fhstat(l, SCARG(uap, fhp), sizeof(*SCARG(uap, fhp)), &sb); 167 if (error) 168 return error; 169 cvtstat(&osb, &sb); 170 return copyout(&osb, SCARG(uap, sb), sizeof(osb)); 171 } 172 173 /* 174 * Return status information about a file descriptor. 175 */ 176 /* ARGSUSED */ 177 int 178 compat_30_sys___fstat13(struct lwp *l, 179 const struct compat_30_sys___fstat13_args *uap, register_t *retval) 180 { 181 /* { 182 syscallarg(int) fd; 183 syscallarg(struct stat13 *) sb; 184 } */ 185 struct stat sb; 186 struct stat13 osb; 187 int error; 188 189 error = do_sys_fstat(SCARG(uap, fd), &sb); 190 if (error) 191 return error; 192 cvtstat(&osb, &sb); 193 return copyout(&osb, SCARG(uap, sb), sizeof(osb)); 194 } 195 196 /* 197 * Read a block of directory entries in a file system independent format. 198 */ 199 int 200 compat_30_sys_getdents(struct lwp *l, 201 const struct compat_30_sys_getdents_args *uap, register_t *retval) 202 { 203 /* { 204 syscallarg(int) fd; 205 syscallarg(char *) buf; 206 syscallarg(size_t) count; 207 } */ 208 struct dirent *bdp; 209 struct vnode *vp; 210 char *inp, *tbuf; /* BSD-format */ 211 int len, reclen; /* BSD-format */ 212 char *outp; /* NetBSD-3.0-format */ 213 int resid; 214 struct file *fp; 215 struct uio auio; 216 struct iovec aiov; 217 struct dirent12 idb; 218 off_t off; /* true file offset */ 219 int buflen, error, eofflag; 220 off_t *cookiebuf = NULL, *cookie; 221 int ncookies; 222 bool any = false; 223 224 /* fd_getvnode() will use the descriptor for us */ 225 if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) 226 return error; 227 228 if ((fp->f_flag & FREAD) == 0) { 229 error = EBADF; 230 goto out1; 231 } 232 233 vp = fp->f_vnode; 234 if (vp->v_type != VDIR) { 235 error = EINVAL; 236 goto out1; 237 } 238 239 buflen = uimin(MAXBSIZE, SCARG(uap, count)); 240 tbuf = malloc(buflen, M_TEMP, M_WAITOK); 241 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 242 off = fp->f_offset; 243 again: 244 aiov.iov_base = tbuf; 245 aiov.iov_len = buflen; 246 auio.uio_iov = &aiov; 247 auio.uio_iovcnt = 1; 248 auio.uio_rw = UIO_READ; 249 auio.uio_resid = buflen; 250 auio.uio_offset = off; 251 UIO_SETUP_SYSSPACE(&auio); 252 /* 253 * First we read into the malloc'ed buffer, then 254 * we massage it into user space, one record at a time. 255 */ 256 error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, 257 &ncookies); 258 if (error) 259 goto out; 260 261 inp = tbuf; 262 outp = SCARG(uap, buf); 263 resid = SCARG(uap, count); 264 if ((len = buflen - auio.uio_resid) == 0) 265 goto eof; 266 267 for (cookie = cookiebuf; len > 0; len -= reclen) { 268 bdp = (struct dirent *)inp; 269 reclen = bdp->d_reclen; 270 if (reclen & _DIRENT_ALIGN(bdp)) 271 panic("%s: bad reclen %d", __func__, reclen); 272 if (cookie) 273 off = *cookie++; /* each entry points to the next */ 274 else 275 off += reclen; 276 if ((off >> 32) != 0) { 277 compat_offseterr(vp, "netbsd30_getdents"); 278 error = EINVAL; 279 goto out; 280 } 281 memset(&idb, 0, sizeof(idb)); 282 if (bdp->d_namlen >= sizeof(idb.d_name)) 283 idb.d_namlen = sizeof(idb.d_name) - 1; 284 else 285 idb.d_namlen = bdp->d_namlen; 286 idb.d_reclen = _DIRENT_SIZE(&idb); 287 if (reclen > len || resid < idb.d_reclen) { 288 /* entry too big for buffer, so just stop */ 289 any = true; 290 break; 291 } 292 /* 293 * Massage in place to make a NetBSD-3.0-shaped dirent 294 * (otherwise we have to worry about touching user memory 295 * outside of the copyout() call). 296 */ 297 idb.d_fileno = (u_int32_t)bdp->d_fileno; 298 idb.d_type = bdp->d_type; 299 (void)memcpy(idb.d_name, bdp->d_name, idb.d_namlen); 300 memset(idb.d_name + idb.d_namlen, 0, 301 idb.d_reclen - _DIRENT_NAMEOFF(&idb) - idb.d_namlen); 302 if ((error = copyout(&idb, outp, idb.d_reclen)) != 0) 303 goto out; 304 /* advance past this real entry */ 305 inp += reclen; 306 /* advance output past NetBSD-3.0-shaped entry */ 307 outp += idb.d_reclen; 308 resid -= idb.d_reclen; 309 any = true; 310 } 311 312 /* if we squished out the whole block, try again */ 313 if (!any) { 314 if (cookiebuf) 315 free(cookiebuf, M_TEMP); 316 cookiebuf = NULL; 317 goto again; 318 } 319 fp->f_offset = off; /* update the vnode offset */ 320 321 eof: 322 *retval = SCARG(uap, count) - resid; 323 out: 324 VOP_UNLOCK(vp); 325 if (cookiebuf) 326 free(cookiebuf, M_TEMP); 327 free(tbuf, M_TEMP); 328 out1: 329 fd_putfile(SCARG(uap, fd)); 330 return error; 331 } 332 333 /* 334 * Get file handle system call 335 */ 336 int 337 compat_30_sys_getfh(struct lwp *l, const struct compat_30_sys_getfh_args *uap, 338 register_t *retval) 339 { 340 /* { 341 syscallarg(char *) fname; 342 syscallarg(struct compat_30_fhandle *) fhp; 343 } */ 344 struct vnode *vp; 345 struct compat_30_fhandle fh; 346 int error; 347 struct pathbuf *pb; 348 struct nameidata nd; 349 size_t sz; 350 351 /* 352 * Must be super user 353 */ 354 error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FILEHANDLE, 355 0, NULL, NULL, NULL); 356 if (error) 357 return (error); 358 359 error = pathbuf_copyin(SCARG(uap, fname), &pb); 360 if (error) { 361 return error; 362 } 363 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb); 364 error = namei(&nd); 365 pathbuf_destroy(pb); 366 if (error) 367 return error; 368 vp = nd.ni_vp; 369 370 sz = sizeof(struct compat_30_fhandle); 371 error = vfs_composefh(vp, (void *)&fh, &sz); 372 vput(vp); 373 CTASSERT(FHANDLE_SIZE_COMPAT == sizeof(struct compat_30_fhandle)); 374 if (sz != FHANDLE_SIZE_COMPAT) { 375 error = EINVAL; 376 } 377 if (error) 378 return error; 379 return copyout(&fh, SCARG(uap, fhp), sizeof(fh)); 380 } 381 382 /* 383 * Open a file given a file handle. 384 * 385 * Check permissions, allocate an open file structure, 386 * and call the device open routine if any. 387 */ 388 int 389 compat_30_sys_fhopen(struct lwp *l, 390 const struct compat_30_sys_fhopen_args *uap, register_t *retval) 391 { 392 /* { 393 syscallarg(const fhandle_t *) fhp; 394 syscallarg(int) flags; 395 } */ 396 397 return dofhopen(l, SCARG(uap, fhp), FHANDLE_SIZE_COMPAT, 398 SCARG(uap, flags), retval); 399 } 400 401 /* ARGSUSED */ 402 int 403 compat_30_sys___fhstat30(struct lwp *l, 404 const struct compat_30_sys___fhstat30_args *uap_30, register_t *retval) 405 { 406 /* { 407 syscallarg(const fhandle_t *) fhp; 408 syscallarg(struct stat30 *) sb; 409 } */ 410 struct stat sb; 411 struct stat13 osb; 412 int error; 413 414 error = do_fhstat(l, SCARG(uap_30, fhp), FHANDLE_SIZE_COMPAT, &sb); 415 if (error) 416 return error; 417 cvtstat(&osb, &sb); 418 return copyout(&osb, SCARG(uap_30, sb), sizeof(osb)); 419 } 420 421 /* ARGSUSED */ 422 int 423 compat_30_sys_fhstatvfs1(struct lwp *l, 424 const struct compat_30_sys_fhstatvfs1_args *uap, register_t *retval) 425 { 426 /* { 427 syscallarg(const fhandle_t *) fhp; 428 syscallarg(struct statvfs90 *) buf; 429 syscallarg(int) flags; 430 } */ 431 struct statvfs *sb = STATVFSBUF_GET(); 432 int error = do_fhstatvfs(l, SCARG(uap, fhp), FHANDLE_SIZE_COMPAT, 433 sb, SCARG(uap, flags)); 434 435 if (!error) { 436 error = statvfs_to_statvfs90_copy(sb, SCARG(uap, buf), 437 sizeof(struct statvfs90)); 438 } 439 440 STATVFSBUF_PUT(sb); 441 442 return error; 443 } 444 445 int 446 vfs_syscalls_30_init(void) 447 { 448 449 return syscall_establish(NULL, vfs_syscalls_30_syscalls); 450 } 451 452 int 453 vfs_syscalls_30_fini(void) 454 { 455 456 return syscall_disestablish(NULL, vfs_syscalls_30_syscalls); 457 } 458