1 /* $NetBSD: linux_file64.c,v 1.64 2021/09/23 06:56:27 ryo Exp $ */ 2 3 /*- 4 * Copyright (c) 1995, 1998, 2000, 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Frank van der Linden and Eric Haszlakiewicz. 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 32 /* 33 * Linux 64bit filesystem calls. Used on 32bit archs, not used on 64bit ones. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: linux_file64.c,v 1.64 2021/09/23 06:56:27 ryo Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/namei.h> 42 #include <sys/proc.h> 43 #include <sys/dirent.h> 44 #include <sys/file.h> 45 #include <sys/stat.h> 46 #include <sys/filedesc.h> 47 #include <sys/ioctl.h> 48 #include <sys/kernel.h> 49 #include <sys/mount.h> 50 #include <sys/malloc.h> 51 #include <sys/namei.h> 52 #include <sys/vfs_syscalls.h> 53 #include <sys/vnode.h> 54 #include <sys/tty.h> 55 #include <sys/conf.h> 56 57 #include <sys/syscallargs.h> 58 59 #include <compat/linux/common/linux_types.h> 60 #include <compat/linux/common/linux_signal.h> 61 #include <compat/linux/common/linux_fcntl.h> 62 #include <compat/linux/common/linux_util.h> 63 #include <compat/linux/common/linux_machdep.h> 64 #include <compat/linux/common/linux_dirent.h> 65 #include <compat/linux/common/linux_ipc.h> 66 #include <compat/linux/common/linux_sem.h> 67 68 #include <compat/linux/linux_syscallargs.h> 69 70 static void bsd_to_linux_stat(struct stat *, struct linux_stat64 *); 71 72 /* 73 * Convert a NetBSD stat structure to a Linux stat structure. 74 * Only the order of the fields and the padding in the structure 75 * is different. linux_fakedev is a machine-dependent function 76 * which optionally converts device driver major/minor numbers 77 * (XXX horrible, but what can you do against code that compares 78 * things against constant major device numbers? sigh) 79 */ 80 static void 81 bsd_to_linux_stat(struct stat *bsp, struct linux_stat64 *lsp) 82 { 83 memset(lsp, 0, sizeof(*lsp)); 84 lsp->lst_dev = linux_fakedev(bsp->st_dev, 0); 85 lsp->lst_ino = bsp->st_ino; 86 lsp->lst_mode = (linux_mode_t)bsp->st_mode; 87 if (bsp->st_nlink >= (1 << 15)) 88 lsp->lst_nlink = (1 << 15) - 1; 89 else 90 lsp->lst_nlink = (linux_nlink_t)bsp->st_nlink; 91 lsp->lst_uid = bsp->st_uid; 92 lsp->lst_gid = bsp->st_gid; 93 lsp->lst_rdev = linux_fakedev(bsp->st_rdev, 1); 94 lsp->lst_size = bsp->st_size; 95 lsp->lst_blksize = bsp->st_blksize; 96 lsp->lst_blocks = bsp->st_blocks; 97 lsp->lst_atime = bsp->st_atime; 98 lsp->lst_mtime = bsp->st_mtime; 99 lsp->lst_ctime = bsp->st_ctime; 100 # ifdef LINUX_STAT64_HAS_NSEC 101 lsp->lst_atime_nsec = bsp->st_atimensec; 102 lsp->lst_mtime_nsec = bsp->st_mtimensec; 103 lsp->lst_ctime_nsec = bsp->st_ctimensec; 104 # endif 105 # if LINUX_STAT64_HAS_BROKEN_ST_INO 106 lsp->__lst_ino = (linux_ino_t) bsp->st_ino; 107 # endif 108 } 109 110 /* 111 * The stat functions below are plain sailing. stat and lstat are handled 112 * by one function to avoid code duplication. 113 */ 114 int 115 linux_sys_fstat64(struct lwp *l, const struct linux_sys_fstat64_args *uap, register_t *retval) 116 { 117 /* { 118 syscallarg(int) fd; 119 syscallarg(struct linux_stat64 *) sp; 120 } */ 121 struct linux_stat64 tmplst; 122 struct stat tmpst; 123 int error; 124 125 error = do_sys_fstat(SCARG(uap, fd), &tmpst); 126 if (error != 0) 127 return error; 128 129 bsd_to_linux_stat(&tmpst, &tmplst); 130 131 return copyout(&tmplst, SCARG(uap, sp), sizeof tmplst); 132 } 133 134 #if !defined(__aarch64__) 135 static int 136 linux_do_stat64(struct lwp *l, const struct linux_sys_stat64_args *uap, register_t *retval, int flags) 137 { 138 struct linux_stat64 tmplst; 139 struct stat tmpst; 140 int error; 141 142 error = do_sys_stat(SCARG(uap, path), flags, &tmpst); 143 if (error != 0) 144 return error; 145 146 bsd_to_linux_stat(&tmpst, &tmplst); 147 148 return copyout(&tmplst, SCARG(uap, sp), sizeof tmplst); 149 } 150 151 int 152 linux_sys_stat64(struct lwp *l, const struct linux_sys_stat64_args *uap, register_t *retval) 153 { 154 /* { 155 syscallarg(const char *) path; 156 syscallarg(struct linux_stat64 *) sp; 157 } */ 158 159 return linux_do_stat64(l, uap, retval, FOLLOW); 160 } 161 162 int 163 linux_sys_lstat64(struct lwp *l, const struct linux_sys_lstat64_args *uap, register_t *retval) 164 { 165 /* { 166 syscallarg(const char *) path; 167 syscallarg(struct linux_stat64 *) sp; 168 } */ 169 170 return linux_do_stat64(l, (const void *)uap, retval, NOFOLLOW); 171 } 172 #endif 173 174 int 175 linux_sys_fstatat64(struct lwp *l, const struct linux_sys_fstatat64_args *uap, register_t *retval) 176 { 177 /* { 178 syscallarg(int) fd; 179 syscallarg(const char *) path; 180 syscallarg(struct linux_stat64 *) sp; 181 syscallarg(int) flag; 182 } */ 183 struct linux_stat64 tmplst; 184 struct stat tmpst; 185 struct vnode *vp; 186 int error, nd_flag, fd; 187 uint8_t c; 188 189 if (SCARG(uap, flag) & LINUX_AT_EMPTY_PATH) { 190 /* 191 * If path is null string: 192 */ 193 error = ufetch_8(SCARG(uap, path), &c); 194 if (error != 0) 195 return error; 196 if (c == '\0') { 197 fd = SCARG(uap, fd); 198 if (fd == AT_FDCWD) { 199 /* 200 * operate on current directory 201 */ 202 vp = l->l_proc->p_cwdi->cwdi_cdir; 203 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 204 error = vn_stat(vp, &tmpst); 205 VOP_UNLOCK(vp); 206 } else { 207 /* 208 * operate on fd 209 */ 210 error = do_sys_fstat(fd, &tmpst); 211 } 212 if (error != 0) 213 return error; 214 goto done; 215 } 216 } 217 218 if (SCARG(uap, flag) & LINUX_AT_SYMLINK_NOFOLLOW) 219 nd_flag = NOFOLLOW; 220 else 221 nd_flag = FOLLOW; 222 223 error = do_sys_statat(l, SCARG(uap, fd), SCARG(uap, path), nd_flag, &tmpst); 224 if (error != 0) 225 return error; 226 227 done: 228 bsd_to_linux_stat(&tmpst, &tmplst); 229 230 return copyout(&tmplst, SCARG(uap, sp), sizeof tmplst); 231 } 232 233 #ifndef __alpha__ 234 int 235 linux_sys_truncate64(struct lwp *l, const struct linux_sys_truncate64_args *uap, register_t *retval) 236 { 237 /* { 238 syscallarg(const char *) path; 239 syscallarg(off_t) length; 240 } */ 241 struct sys_truncate_args ta; 242 243 /* Linux doesn't have the 'pad' pseudo-parameter */ 244 SCARG(&ta, path) = SCARG(uap, path); 245 SCARG(&ta, PAD) = 0; 246 SCARG(&ta, length) = SCARG(uap, length); 247 248 return sys_truncate(l, &ta, retval); 249 } 250 251 int 252 linux_sys_ftruncate64(struct lwp *l, const struct linux_sys_ftruncate64_args *uap, register_t *retval) 253 { 254 /* { 255 syscallarg(unsigned int) fd; 256 syscallarg(off_t) length; 257 } */ 258 struct sys_ftruncate_args ta; 259 260 /* Linux doesn't have the 'pad' pseudo-parameter */ 261 SCARG(&ta, fd) = SCARG(uap, fd); 262 SCARG(&ta, PAD) = 0; 263 SCARG(&ta, length) = SCARG(uap, length); 264 265 return sys_ftruncate(l, &ta, retval); 266 } 267 #endif /* __alpha__ */ 268 269 /* 270 * Linux 'readdir' call. This code is mostly taken from the 271 * SunOS getdents call (see compat/sunos/sunos_misc.c), though 272 * an attempt has been made to keep it a little cleaner. 273 * 274 * The d_off field contains the offset of the next valid entry, 275 * unless the older Linux getdents(2), which used to have it set 276 * to the offset of the entry itself. This function also doesn't 277 * need to deal with the old count == 1 glibc problem. 278 * 279 * Read in BSD-style entries, convert them, and copy them out. 280 * 281 * Note that this doesn't handle union-mounted filesystems. 282 */ 283 int 284 linux_sys_getdents64(struct lwp *l, const struct linux_sys_getdents64_args *uap, register_t *retval) 285 { 286 /* { 287 syscallarg(int) fd; 288 syscallarg(struct linux_dirent64 *) dent; 289 syscallarg(unsigned int) count; 290 } */ 291 struct dirent *bdp; 292 struct vnode *vp; 293 char *inp, *tbuf; /* BSD-format */ 294 int len, reclen; /* BSD-format */ 295 char *outp; /* Linux-format */ 296 int resid, linux_reclen = 0; /* Linux-format */ 297 file_t *fp; 298 struct uio auio; 299 struct iovec aiov; 300 struct linux_dirent64 idb; 301 off_t off; /* true file offset */ 302 int buflen, error, eofflag, nbytes; 303 struct vattr va; 304 off_t *cookiebuf = NULL, *cookie; 305 int ncookies; 306 307 /* fd_getvnode() will use the descriptor for us */ 308 if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) 309 return (error); 310 311 if ((fp->f_flag & FREAD) == 0) { 312 error = EBADF; 313 goto out1; 314 } 315 316 vp = (struct vnode *)fp->f_data; 317 if (vp->v_type != VDIR) { 318 error = ENOTDIR; 319 goto out1; 320 } 321 322 vn_lock(vp, LK_SHARED | LK_RETRY); 323 error = VOP_GETATTR(vp, &va, l->l_cred); 324 VOP_UNLOCK(vp); 325 if (error) 326 goto out1; 327 328 nbytes = SCARG(uap, count); 329 buflen = uimin(MAXBSIZE, nbytes); 330 if (buflen < va.va_blocksize) 331 buflen = va.va_blocksize; 332 tbuf = malloc(buflen, M_TEMP, M_WAITOK); 333 334 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 335 off = fp->f_offset; 336 again: 337 aiov.iov_base = tbuf; 338 aiov.iov_len = buflen; 339 auio.uio_iov = &aiov; 340 auio.uio_iovcnt = 1; 341 auio.uio_rw = UIO_READ; 342 auio.uio_resid = buflen; 343 auio.uio_offset = off; 344 UIO_SETUP_SYSSPACE(&auio); 345 /* 346 * First we read into the malloc'ed buffer, then 347 * we massage it into user space, one record at a time. 348 */ 349 error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &cookiebuf, 350 &ncookies); 351 if (error) 352 goto out; 353 354 inp = tbuf; 355 outp = (void *)SCARG(uap, dent); 356 resid = nbytes; 357 if ((len = buflen - auio.uio_resid) == 0) 358 goto eof; 359 360 for (cookie = cookiebuf; len > 0; len -= reclen) { 361 bdp = (struct dirent *)inp; 362 reclen = bdp->d_reclen; 363 if (reclen & 3) { 364 error = EIO; 365 goto out; 366 } 367 if (bdp->d_fileno == 0) { 368 inp += reclen; /* it is a hole; squish it out */ 369 if (cookie) 370 off = *cookie++; 371 else 372 off += reclen; 373 continue; 374 } 375 linux_reclen = LINUX_RECLEN(&idb, bdp->d_namlen); 376 if (reclen > len || resid < linux_reclen) { 377 /* entry too big for buffer, so just stop */ 378 outp++; 379 break; 380 } 381 if (cookie) 382 off = *cookie++; /* each entry points to next */ 383 else 384 off += reclen; 385 /* 386 * Massage in place to make a Linux-shaped dirent (otherwise 387 * we have to worry about touching user memory outside of 388 * the copyout() call). 389 */ 390 memset(&idb, 0, sizeof(idb)); 391 idb.d_ino = bdp->d_fileno; 392 idb.d_type = bdp->d_type; 393 idb.d_off = off; 394 idb.d_reclen = (u_short)linux_reclen; 395 memcpy(idb.d_name, bdp->d_name, MIN(sizeof(idb.d_name), 396 bdp->d_namlen + 1)); 397 if ((error = copyout((void *)&idb, outp, linux_reclen))) 398 goto out; 399 /* advance past this real entry */ 400 inp += reclen; 401 /* advance output past Linux-shaped entry */ 402 outp += linux_reclen; 403 resid -= linux_reclen; 404 } 405 406 /* if we squished out the whole block, try again */ 407 if (outp == (void *)SCARG(uap, dent)) { 408 if (cookiebuf) 409 free(cookiebuf, M_TEMP); 410 cookiebuf = NULL; 411 goto again; 412 } 413 fp->f_offset = off; /* update the vnode offset */ 414 415 eof: 416 *retval = nbytes - resid; 417 out: 418 VOP_UNLOCK(vp); 419 if (cookiebuf) 420 free(cookiebuf, M_TEMP); 421 free(tbuf, M_TEMP); 422 out1: 423 fd_putfile(SCARG(uap, fd)); 424 return error; 425 } 426