| 1 | /* $NetBSD: linux_file.c,v 1.115 2015/03/01 13:19:39 njoly Exp $ */ |
| 2 | |
| 3 | /*- |
| 4 | * Copyright (c) 1995, 1998, 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 | * Functions in multiarch: |
| 34 | * linux_sys_llseek : linux_llseek.c |
| 35 | */ |
| 36 | |
| 37 | #include <sys/cdefs.h> |
| 38 | __KERNEL_RCSID(0, "$NetBSD: linux_file.c,v 1.115 2015/03/01 13:19:39 njoly Exp $" ); |
| 39 | |
| 40 | #include <sys/param.h> |
| 41 | #include <sys/systm.h> |
| 42 | #include <sys/namei.h> |
| 43 | #include <sys/proc.h> |
| 44 | #include <sys/file.h> |
| 45 | #include <sys/fcntl.h> |
| 46 | #include <sys/stat.h> |
| 47 | #include <sys/filedesc.h> |
| 48 | #include <sys/ioctl.h> |
| 49 | #include <sys/kernel.h> |
| 50 | #include <sys/mount.h> |
| 51 | #include <sys/namei.h> |
| 52 | #include <sys/vnode.h> |
| 53 | #include <sys/tty.h> |
| 54 | #include <sys/socketvar.h> |
| 55 | #include <sys/conf.h> |
| 56 | #include <sys/pipe.h> |
| 57 | |
| 58 | #include <sys/syscallargs.h> |
| 59 | #include <sys/vfs_syscalls.h> |
| 60 | |
| 61 | #include <compat/linux/common/linux_types.h> |
| 62 | #include <compat/linux/common/linux_signal.h> |
| 63 | #include <compat/linux/common/linux_fcntl.h> |
| 64 | #include <compat/linux/common/linux_util.h> |
| 65 | #include <compat/linux/common/linux_machdep.h> |
| 66 | #include <compat/linux/common/linux_ipc.h> |
| 67 | #include <compat/linux/common/linux_sem.h> |
| 68 | |
| 69 | #include <compat/linux/linux_syscallargs.h> |
| 70 | |
| 71 | static int bsd_to_linux_ioflags(int); |
| 72 | #ifndef __amd64__ |
| 73 | static void bsd_to_linux_stat(struct stat *, struct linux_stat *); |
| 74 | #endif |
| 75 | |
| 76 | conv_linux_flock(linux, flock) |
| 77 | |
| 78 | /* |
| 79 | * Some file-related calls are handled here. The usual flag conversion |
| 80 | * an structure conversion is done, and alternate emul path searching. |
| 81 | */ |
| 82 | |
| 83 | /* |
| 84 | * The next two functions convert between the Linux and NetBSD values |
| 85 | * of the flags used in open(2) and fcntl(2). |
| 86 | */ |
| 87 | int |
| 88 | linux_to_bsd_ioflags(int lflags) |
| 89 | { |
| 90 | int res = 0; |
| 91 | |
| 92 | res |= cvtto_bsd_mask(lflags, LINUX_O_WRONLY, O_WRONLY); |
| 93 | res |= cvtto_bsd_mask(lflags, LINUX_O_RDONLY, O_RDONLY); |
| 94 | res |= cvtto_bsd_mask(lflags, LINUX_O_RDWR, O_RDWR); |
| 95 | |
| 96 | res |= cvtto_bsd_mask(lflags, LINUX_O_CREAT, O_CREAT); |
| 97 | res |= cvtto_bsd_mask(lflags, LINUX_O_EXCL, O_EXCL); |
| 98 | res |= cvtto_bsd_mask(lflags, LINUX_O_NOCTTY, O_NOCTTY); |
| 99 | res |= cvtto_bsd_mask(lflags, LINUX_O_TRUNC, O_TRUNC); |
| 100 | res |= cvtto_bsd_mask(lflags, LINUX_O_APPEND, O_APPEND); |
| 101 | res |= cvtto_bsd_mask(lflags, LINUX_O_NONBLOCK, O_NONBLOCK); |
| 102 | res |= cvtto_bsd_mask(lflags, LINUX_O_NDELAY, O_NDELAY); |
| 103 | res |= cvtto_bsd_mask(lflags, LINUX_O_SYNC, O_FSYNC); |
| 104 | res |= cvtto_bsd_mask(lflags, LINUX_FASYNC, O_ASYNC); |
| 105 | res |= cvtto_bsd_mask(lflags, LINUX_O_DIRECT, O_DIRECT); |
| 106 | res |= cvtto_bsd_mask(lflags, LINUX_O_DIRECTORY, O_DIRECTORY); |
| 107 | res |= cvtto_bsd_mask(lflags, LINUX_O_NOFOLLOW, O_NOFOLLOW); |
| 108 | res |= cvtto_bsd_mask(lflags, LINUX_O_CLOEXEC, O_CLOEXEC); |
| 109 | |
| 110 | return res; |
| 111 | } |
| 112 | |
| 113 | static int |
| 114 | bsd_to_linux_ioflags(int bflags) |
| 115 | { |
| 116 | int res = 0; |
| 117 | |
| 118 | res |= cvtto_linux_mask(bflags, O_WRONLY, LINUX_O_WRONLY); |
| 119 | res |= cvtto_linux_mask(bflags, O_RDONLY, LINUX_O_RDONLY); |
| 120 | res |= cvtto_linux_mask(bflags, O_RDWR, LINUX_O_RDWR); |
| 121 | |
| 122 | res |= cvtto_linux_mask(bflags, O_CREAT, LINUX_O_CREAT); |
| 123 | res |= cvtto_linux_mask(bflags, O_EXCL, LINUX_O_EXCL); |
| 124 | res |= cvtto_linux_mask(bflags, O_NOCTTY, LINUX_O_NOCTTY); |
| 125 | res |= cvtto_linux_mask(bflags, O_TRUNC, LINUX_O_TRUNC); |
| 126 | res |= cvtto_linux_mask(bflags, O_APPEND, LINUX_O_APPEND); |
| 127 | res |= cvtto_linux_mask(bflags, O_NONBLOCK, LINUX_O_NONBLOCK); |
| 128 | res |= cvtto_linux_mask(bflags, O_NDELAY, LINUX_O_NDELAY); |
| 129 | res |= cvtto_linux_mask(bflags, O_FSYNC, LINUX_O_SYNC); |
| 130 | res |= cvtto_linux_mask(bflags, O_ASYNC, LINUX_FASYNC); |
| 131 | res |= cvtto_linux_mask(bflags, O_DIRECT, LINUX_O_DIRECT); |
| 132 | res |= cvtto_linux_mask(bflags, O_DIRECTORY, LINUX_O_DIRECTORY); |
| 133 | res |= cvtto_linux_mask(bflags, O_NOFOLLOW, LINUX_O_NOFOLLOW); |
| 134 | res |= cvtto_linux_mask(bflags, O_CLOEXEC, LINUX_O_CLOEXEC); |
| 135 | |
| 136 | return res; |
| 137 | } |
| 138 | |
| 139 | /* |
| 140 | * creat(2) is an obsolete function, but it's present as a Linux |
| 141 | * system call, so let's deal with it. |
| 142 | * |
| 143 | * Note: On the Alpha this doesn't really exist in Linux, but it's defined |
| 144 | * in syscalls.master anyway so this doesn't have to be special cased. |
| 145 | * |
| 146 | * Just call open(2) with the TRUNC, CREAT and WRONLY flags. |
| 147 | */ |
| 148 | int |
| 149 | linux_sys_creat(struct lwp *l, const struct linux_sys_creat_args *uap, register_t *retval) |
| 150 | { |
| 151 | /* { |
| 152 | syscallarg(const char *) path; |
| 153 | syscallarg(linux_umode_t) mode; |
| 154 | } */ |
| 155 | struct sys_open_args oa; |
| 156 | |
| 157 | SCARG(&oa, path) = SCARG(uap, path); |
| 158 | SCARG(&oa, flags) = O_CREAT | O_TRUNC | O_WRONLY; |
| 159 | SCARG(&oa, mode) = SCARG(uap, mode); |
| 160 | |
| 161 | return sys_open(l, &oa, retval); |
| 162 | } |
| 163 | |
| 164 | static void |
| 165 | linux_open_ctty(struct lwp *l, int flags, int fd) |
| 166 | { |
| 167 | struct proc *p = l->l_proc; |
| 168 | |
| 169 | /* |
| 170 | * this bit from sunos_misc.c (and svr4_fcntl.c). |
| 171 | * If we are a session leader, and we don't have a controlling |
| 172 | * terminal yet, and the O_NOCTTY flag is not set, try to make |
| 173 | * this the controlling terminal. |
| 174 | */ |
| 175 | if (!(flags & O_NOCTTY) && SESS_LEADER(p) && !(p->p_lflag & PL_CONTROLT)) { |
| 176 | file_t *fp; |
| 177 | |
| 178 | fp = fd_getfile(fd); |
| 179 | |
| 180 | /* ignore any error, just give it a try */ |
| 181 | if (fp != NULL) { |
| 182 | if (fp->f_type == DTYPE_VNODE) { |
| 183 | (fp->f_ops->fo_ioctl) (fp, TIOCSCTTY, NULL); |
| 184 | } |
| 185 | fd_putfile(fd); |
| 186 | } |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | /* |
| 191 | * open(2). Take care of the different flag values, and let the |
| 192 | * NetBSD syscall do the real work. See if this operation |
| 193 | * gives the current process a controlling terminal. |
| 194 | * (XXX is this necessary?) |
| 195 | */ |
| 196 | int |
| 197 | linux_sys_open(struct lwp *l, const struct linux_sys_open_args *uap, register_t *retval) |
| 198 | { |
| 199 | /* { |
| 200 | syscallarg(const char *) path; |
| 201 | syscallarg(int) flags; |
| 202 | syscallarg(linux_umode_t) mode; |
| 203 | } */ |
| 204 | int error, fl; |
| 205 | struct sys_open_args boa; |
| 206 | |
| 207 | fl = linux_to_bsd_ioflags(SCARG(uap, flags)); |
| 208 | |
| 209 | SCARG(&boa, path) = SCARG(uap, path); |
| 210 | SCARG(&boa, flags) = fl; |
| 211 | SCARG(&boa, mode) = SCARG(uap, mode); |
| 212 | |
| 213 | if ((error = sys_open(l, &boa, retval))) |
| 214 | return (error == EFTYPE) ? ELOOP : error; |
| 215 | |
| 216 | linux_open_ctty(l, fl, *retval); |
| 217 | return 0; |
| 218 | } |
| 219 | |
| 220 | int |
| 221 | linux_sys_openat(struct lwp *l, const struct linux_sys_openat_args *uap, register_t *retval) |
| 222 | { |
| 223 | /* { |
| 224 | syscallarg(int) fd; |
| 225 | syscallarg(const char *) path; |
| 226 | syscallarg(int) flags; |
| 227 | syscallarg(linux_umode_t) mode; |
| 228 | } */ |
| 229 | int error, fl; |
| 230 | struct sys_openat_args boa; |
| 231 | |
| 232 | fl = linux_to_bsd_ioflags(SCARG(uap, flags)); |
| 233 | |
| 234 | SCARG(&boa, fd) = SCARG(uap, fd); |
| 235 | SCARG(&boa, path) = SCARG(uap, path); |
| 236 | SCARG(&boa, oflags) = fl; |
| 237 | SCARG(&boa, mode) = SCARG(uap, mode); |
| 238 | |
| 239 | if ((error = sys_openat(l, &boa, retval))) |
| 240 | return (error == EFTYPE) ? ELOOP : error; |
| 241 | |
| 242 | linux_open_ctty(l, fl, *retval); |
| 243 | return 0; |
| 244 | } |
| 245 | |
| 246 | /* |
| 247 | * Most actions in the fcntl() call are straightforward; simply |
| 248 | * pass control to the NetBSD system call. A few commands need |
| 249 | * conversions after the actual system call has done its work, |
| 250 | * because the flag values and lock structure are different. |
| 251 | */ |
| 252 | int |
| 253 | linux_sys_fcntl(struct lwp *l, const struct linux_sys_fcntl_args *uap, register_t *retval) |
| 254 | { |
| 255 | /* { |
| 256 | syscallarg(int) fd; |
| 257 | syscallarg(int) cmd; |
| 258 | syscallarg(void *) arg; |
| 259 | } */ |
| 260 | struct proc *p = l->l_proc; |
| 261 | int fd, cmd, error; |
| 262 | u_long val; |
| 263 | void *arg; |
| 264 | struct sys_fcntl_args fca; |
| 265 | file_t *fp; |
| 266 | struct vnode *vp; |
| 267 | struct vattr va; |
| 268 | long pgid; |
| 269 | struct pgrp *pgrp; |
| 270 | struct tty *tp; |
| 271 | |
| 272 | fd = SCARG(uap, fd); |
| 273 | cmd = SCARG(uap, cmd); |
| 274 | arg = SCARG(uap, arg); |
| 275 | |
| 276 | switch (cmd) { |
| 277 | |
| 278 | case LINUX_F_DUPFD: |
| 279 | cmd = F_DUPFD; |
| 280 | break; |
| 281 | |
| 282 | case LINUX_F_GETFD: |
| 283 | cmd = F_GETFD; |
| 284 | break; |
| 285 | |
| 286 | case LINUX_F_SETFD: |
| 287 | cmd = F_SETFD; |
| 288 | break; |
| 289 | |
| 290 | case LINUX_F_GETFL: |
| 291 | SCARG(&fca, fd) = fd; |
| 292 | SCARG(&fca, cmd) = F_GETFL; |
| 293 | SCARG(&fca, arg) = arg; |
| 294 | if ((error = sys_fcntl(l, &fca, retval))) |
| 295 | return error; |
| 296 | retval[0] = bsd_to_linux_ioflags(retval[0]); |
| 297 | return 0; |
| 298 | |
| 299 | case LINUX_F_SETFL: { |
| 300 | file_t *fp1 = NULL; |
| 301 | |
| 302 | val = linux_to_bsd_ioflags((unsigned long)SCARG(uap, arg)); |
| 303 | /* |
| 304 | * Linux seems to have same semantics for sending SIGIO to the |
| 305 | * read side of socket, but slightly different semantics |
| 306 | * for SIGIO to the write side. Rather than sending the SIGIO |
| 307 | * every time it's possible to write (directly) more data, it |
| 308 | * only sends SIGIO if last write(2) failed due to insufficient |
| 309 | * memory to hold the data. This is compatible enough |
| 310 | * with NetBSD semantics to not do anything about the |
| 311 | * difference. |
| 312 | * |
| 313 | * Linux does NOT send SIGIO for pipes. Deal with socketpair |
| 314 | * ones and DTYPE_PIPE ones. For these, we don't set |
| 315 | * the underlying flags (we don't pass O_ASYNC flag down |
| 316 | * to sys_fcntl()), but set the FASYNC flag for file descriptor, |
| 317 | * so that F_GETFL would report the ASYNC i/o is on. |
| 318 | */ |
| 319 | if (val & O_ASYNC) { |
| 320 | if (((fp1 = fd_getfile(fd)) == NULL)) |
| 321 | return (EBADF); |
| 322 | if (((fp1->f_type == DTYPE_SOCKET) && fp1->f_data |
| 323 | && ((struct socket *)fp1->f_data)->so_state & SS_ISAPIPE) |
| 324 | || (fp1->f_type == DTYPE_PIPE)) |
| 325 | val &= ~O_ASYNC; |
| 326 | else { |
| 327 | /* not a pipe, do not modify anything */ |
| 328 | fd_putfile(fd); |
| 329 | fp1 = NULL; |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | SCARG(&fca, fd) = fd; |
| 334 | SCARG(&fca, cmd) = F_SETFL; |
| 335 | SCARG(&fca, arg) = (void *) val; |
| 336 | |
| 337 | error = sys_fcntl(l, &fca, retval); |
| 338 | |
| 339 | /* Now set the FASYNC flag for pipes */ |
| 340 | if (fp1) { |
| 341 | if (!error) { |
| 342 | mutex_enter(&fp1->f_lock); |
| 343 | fp1->f_flag |= FASYNC; |
| 344 | mutex_exit(&fp1->f_lock); |
| 345 | } |
| 346 | fd_putfile(fd); |
| 347 | } |
| 348 | |
| 349 | return (error); |
| 350 | } |
| 351 | |
| 352 | case LINUX_F_GETLK: |
| 353 | do_linux_getlk(fd, cmd, arg, linux, flock); |
| 354 | |
| 355 | case LINUX_F_SETLK: |
| 356 | case LINUX_F_SETLKW: |
| 357 | do_linux_setlk(fd, cmd, arg, linux, flock, LINUX_F_SETLK); |
| 358 | |
| 359 | case LINUX_F_SETOWN: |
| 360 | case LINUX_F_GETOWN: |
| 361 | /* |
| 362 | * We need to route fcntl() for tty descriptors around normal |
| 363 | * fcntl(), since NetBSD tty TIOC{G,S}PGRP semantics is too |
| 364 | * restrictive for Linux F_{G,S}ETOWN. For non-tty descriptors, |
| 365 | * this is not a problem. |
| 366 | */ |
| 367 | if ((fp = fd_getfile(fd)) == NULL) |
| 368 | return EBADF; |
| 369 | |
| 370 | /* Check it's a character device vnode */ |
| 371 | if (fp->f_type != DTYPE_VNODE |
| 372 | || (vp = (struct vnode *)fp->f_data) == NULL |
| 373 | || vp->v_type != VCHR) { |
| 374 | fd_putfile(fd); |
| 375 | |
| 376 | not_tty: |
| 377 | /* Not a tty, proceed with common fcntl() */ |
| 378 | cmd = cmd == LINUX_F_SETOWN ? F_SETOWN : F_GETOWN; |
| 379 | break; |
| 380 | } |
| 381 | |
| 382 | vn_lock(vp, LK_SHARED | LK_RETRY); |
| 383 | error = VOP_GETATTR(vp, &va, l->l_cred); |
| 384 | VOP_UNLOCK(vp); |
| 385 | |
| 386 | fd_putfile(fd); |
| 387 | |
| 388 | if (error) |
| 389 | return error; |
| 390 | |
| 391 | if ((tp = cdev_tty(va.va_rdev)) == NULL) |
| 392 | goto not_tty; |
| 393 | |
| 394 | /* set tty pg_id appropriately */ |
| 395 | mutex_enter(proc_lock); |
| 396 | if (cmd == LINUX_F_GETOWN) { |
| 397 | retval[0] = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PGID; |
| 398 | mutex_exit(proc_lock); |
| 399 | return 0; |
| 400 | } |
| 401 | if ((long)arg <= 0) { |
| 402 | pgid = -(long)arg; |
| 403 | } else { |
| 404 | struct proc *p1 = proc_find((long)arg); |
| 405 | if (p1 == NULL) { |
| 406 | mutex_exit(proc_lock); |
| 407 | return (ESRCH); |
| 408 | } |
| 409 | pgid = (long)p1->p_pgrp->pg_id; |
| 410 | } |
| 411 | pgrp = pgrp_find(pgid); |
| 412 | if (pgrp == NULL || pgrp->pg_session != p->p_session) { |
| 413 | mutex_exit(proc_lock); |
| 414 | return EPERM; |
| 415 | } |
| 416 | tp->t_pgrp = pgrp; |
| 417 | mutex_exit(proc_lock); |
| 418 | return 0; |
| 419 | |
| 420 | case LINUX_F_DUPFD_CLOEXEC: |
| 421 | cmd = F_DUPFD_CLOEXEC; |
| 422 | break; |
| 423 | |
| 424 | default: |
| 425 | return EOPNOTSUPP; |
| 426 | } |
| 427 | |
| 428 | SCARG(&fca, fd) = fd; |
| 429 | SCARG(&fca, cmd) = cmd; |
| 430 | SCARG(&fca, arg) = arg; |
| 431 | |
| 432 | return sys_fcntl(l, &fca, retval); |
| 433 | } |
| 434 | |
| 435 | #if !defined(__amd64__) |
| 436 | /* |
| 437 | * Convert a NetBSD stat structure to a Linux stat structure. |
| 438 | * Only the order of the fields and the padding in the structure |
| 439 | * is different. linux_fakedev is a machine-dependent function |
| 440 | * which optionally converts device driver major/minor numbers |
| 441 | * (XXX horrible, but what can you do against code that compares |
| 442 | * things against constant major device numbers? sigh) |
| 443 | */ |
| 444 | static void |
| 445 | bsd_to_linux_stat(struct stat *bsp, struct linux_stat *lsp) |
| 446 | { |
| 447 | |
| 448 | lsp->lst_dev = linux_fakedev(bsp->st_dev, 0); |
| 449 | lsp->lst_ino = bsp->st_ino; |
| 450 | lsp->lst_mode = (linux_mode_t)bsp->st_mode; |
| 451 | if (bsp->st_nlink >= (1 << 15)) |
| 452 | lsp->lst_nlink = (1 << 15) - 1; |
| 453 | else |
| 454 | lsp->lst_nlink = (linux_nlink_t)bsp->st_nlink; |
| 455 | lsp->lst_uid = bsp->st_uid; |
| 456 | lsp->lst_gid = bsp->st_gid; |
| 457 | lsp->lst_rdev = linux_fakedev(bsp->st_rdev, 1); |
| 458 | lsp->lst_size = bsp->st_size; |
| 459 | lsp->lst_blksize = bsp->st_blksize; |
| 460 | lsp->lst_blocks = bsp->st_blocks; |
| 461 | lsp->lst_atime = bsp->st_atime; |
| 462 | lsp->lst_mtime = bsp->st_mtime; |
| 463 | lsp->lst_ctime = bsp->st_ctime; |
| 464 | #ifdef LINUX_STAT_HAS_NSEC |
| 465 | lsp->lst_atime_nsec = bsp->st_atimensec; |
| 466 | lsp->lst_mtime_nsec = bsp->st_mtimensec; |
| 467 | lsp->lst_ctime_nsec = bsp->st_ctimensec; |
| 468 | #endif |
| 469 | } |
| 470 | |
| 471 | /* |
| 472 | * The stat functions below are plain sailing. stat and lstat are handled |
| 473 | * by one function to avoid code duplication. |
| 474 | */ |
| 475 | int |
| 476 | linux_sys_fstat(struct lwp *l, const struct linux_sys_fstat_args *uap, register_t *retval) |
| 477 | { |
| 478 | /* { |
| 479 | syscallarg(int) fd; |
| 480 | syscallarg(linux_stat *) sp; |
| 481 | } */ |
| 482 | struct linux_stat tmplst; |
| 483 | struct stat tmpst; |
| 484 | int error; |
| 485 | |
| 486 | error = do_sys_fstat(SCARG(uap, fd), &tmpst); |
| 487 | if (error != 0) |
| 488 | return error; |
| 489 | bsd_to_linux_stat(&tmpst, &tmplst); |
| 490 | |
| 491 | return copyout(&tmplst, SCARG(uap, sp), sizeof tmplst); |
| 492 | } |
| 493 | |
| 494 | static int |
| 495 | linux_stat1(const struct linux_sys_stat_args *uap, register_t *retval, int flags) |
| 496 | { |
| 497 | struct linux_stat tmplst; |
| 498 | struct stat tmpst; |
| 499 | int error; |
| 500 | |
| 501 | error = do_sys_stat(SCARG(uap, path), flags, &tmpst); |
| 502 | if (error != 0) |
| 503 | return error; |
| 504 | |
| 505 | bsd_to_linux_stat(&tmpst, &tmplst); |
| 506 | |
| 507 | return copyout(&tmplst, SCARG(uap, sp), sizeof tmplst); |
| 508 | } |
| 509 | |
| 510 | int |
| 511 | linux_sys_stat(struct lwp *l, const struct linux_sys_stat_args *uap, register_t *retval) |
| 512 | { |
| 513 | /* { |
| 514 | syscallarg(const char *) path; |
| 515 | syscallarg(struct linux_stat *) sp; |
| 516 | } */ |
| 517 | |
| 518 | return linux_stat1(uap, retval, FOLLOW); |
| 519 | } |
| 520 | |
| 521 | /* Note: this is "newlstat" in the Linux sources */ |
| 522 | /* (we don't bother with the old lstat currently) */ |
| 523 | int |
| 524 | linux_sys_lstat(struct lwp *l, const struct linux_sys_lstat_args *uap, register_t *retval) |
| 525 | { |
| 526 | /* { |
| 527 | syscallarg(const char *) path; |
| 528 | syscallarg(struct linux_stat *) sp; |
| 529 | } */ |
| 530 | |
| 531 | return linux_stat1((const void *)uap, retval, NOFOLLOW); |
| 532 | } |
| 533 | #endif /* !__amd64__ */ |
| 534 | |
| 535 | /* |
| 536 | * The following syscalls are mostly here because of the alternate path check. |
| 537 | */ |
| 538 | |
| 539 | int |
| 540 | linux_sys_linkat(struct lwp *l, const struct linux_sys_linkat_args *uap, register_t *retval) |
| 541 | { |
| 542 | /* { |
| 543 | syscallarg(int) fd1; |
| 544 | syscallarg(const char *) name1; |
| 545 | syscallarg(int) fd2; |
| 546 | syscallarg(const char *) name2; |
| 547 | syscallarg(int) flags; |
| 548 | } */ |
| 549 | int fd1 = SCARG(uap, fd1); |
| 550 | const char *name1 = SCARG(uap, name1); |
| 551 | int fd2 = SCARG(uap, fd2); |
| 552 | const char *name2 = SCARG(uap, name2); |
| 553 | int follow; |
| 554 | |
| 555 | follow = SCARG(uap, flags) & LINUX_AT_SYMLINK_FOLLOW; |
| 556 | |
| 557 | return do_sys_linkat(l, fd1, name1, fd2, name2, follow, retval); |
| 558 | } |
| 559 | |
| 560 | static int |
| 561 | linux_unlink_dircheck(const char *path) |
| 562 | { |
| 563 | struct nameidata nd; |
| 564 | struct pathbuf *pb; |
| 565 | int error; |
| 566 | |
| 567 | /* |
| 568 | * Linux returns EISDIR if unlink(2) is called on a directory. |
| 569 | * We return EPERM in such cases. To emulate correct behaviour, |
| 570 | * check if the path points to directory and return EISDIR if this |
| 571 | * is the case. |
| 572 | * |
| 573 | * XXX this should really not copy in the path buffer twice... |
| 574 | */ |
| 575 | error = pathbuf_copyin(path, &pb); |
| 576 | if (error) { |
| 577 | return error; |
| 578 | } |
| 579 | NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | TRYEMULROOT, pb); |
| 580 | if (namei(&nd) == 0) { |
| 581 | struct stat sb; |
| 582 | |
| 583 | if (vn_stat(nd.ni_vp, &sb) == 0 |
| 584 | && S_ISDIR(sb.st_mode)) |
| 585 | error = EISDIR; |
| 586 | |
| 587 | vput(nd.ni_vp); |
| 588 | } |
| 589 | pathbuf_destroy(pb); |
| 590 | return error ? error : EPERM; |
| 591 | } |
| 592 | |
| 593 | int |
| 594 | linux_sys_unlink(struct lwp *l, const struct linux_sys_unlink_args *uap, register_t *retval) |
| 595 | { |
| 596 | /* { |
| 597 | syscallarg(const char *) path; |
| 598 | } */ |
| 599 | int error; |
| 600 | |
| 601 | error = sys_unlink(l, (const void *)uap, retval); |
| 602 | if (error == EPERM) |
| 603 | error = linux_unlink_dircheck(SCARG(uap, path)); |
| 604 | |
| 605 | return error; |
| 606 | } |
| 607 | |
| 608 | int |
| 609 | linux_sys_unlinkat(struct lwp *l, const struct linux_sys_unlinkat_args *uap, register_t *retval) |
| 610 | { |
| 611 | /* { |
| 612 | syscallarg(int) fd; |
| 613 | syscallarg(const char *) path; |
| 614 | syscallarg(int) flag; |
| 615 | } */ |
| 616 | struct sys_unlinkat_args ua; |
| 617 | int error; |
| 618 | |
| 619 | SCARG(&ua, fd) = SCARG(uap, fd); |
| 620 | SCARG(&ua, path) = SCARG(uap, path); |
| 621 | SCARG(&ua, flag) = linux_to_bsd_atflags(SCARG(uap, flag)); |
| 622 | |
| 623 | error = sys_unlinkat(l, &ua, retval); |
| 624 | if (error == EPERM) |
| 625 | error = linux_unlink_dircheck(SCARG(uap, path)); |
| 626 | |
| 627 | return error; |
| 628 | } |
| 629 | |
| 630 | int |
| 631 | linux_sys_mknod(struct lwp *l, const struct linux_sys_mknod_args *uap, register_t *retval) |
| 632 | { |
| 633 | /* { |
| 634 | syscallarg(const char *) path; |
| 635 | syscallarg(linux_umode_t) mode; |
| 636 | syscallarg(unsigned) dev; |
| 637 | } */ |
| 638 | struct linux_sys_mknodat_args ua; |
| 639 | |
| 640 | SCARG(&ua, fd) = LINUX_AT_FDCWD; |
| 641 | SCARG(&ua, path) = SCARG(uap, path); |
| 642 | SCARG(&ua, mode) = SCARG(uap, mode); |
| 643 | SCARG(&ua, dev) = SCARG(uap, dev); |
| 644 | |
| 645 | return linux_sys_mknodat(l, &ua, retval); |
| 646 | } |
| 647 | |
| 648 | int |
| 649 | linux_sys_mknodat(struct lwp *l, const struct linux_sys_mknodat_args *uap, register_t *retval) |
| 650 | { |
| 651 | /* { |
| 652 | syscallarg(int) fd; |
| 653 | syscallarg(const char *) path; |
| 654 | syscallarg(linux_umode_t) mode; |
| 655 | syscallarg(unsigned) dev; |
| 656 | } */ |
| 657 | |
| 658 | /* |
| 659 | * BSD handles FIFOs separately |
| 660 | */ |
| 661 | if (S_ISFIFO(SCARG(uap, mode))) { |
| 662 | struct sys_mkfifoat_args bma; |
| 663 | |
| 664 | SCARG(&bma, fd) = SCARG(uap, fd); |
| 665 | SCARG(&bma, path) = SCARG(uap, path); |
| 666 | SCARG(&bma, mode) = SCARG(uap, mode); |
| 667 | return sys_mkfifoat(l, &bma, retval); |
| 668 | } else { |
| 669 | |
| 670 | /* |
| 671 | * Linux device numbers uses 8 bits for minor and 8 bits |
| 672 | * for major. Due to how we map our major and minor, |
| 673 | * this just fits into our dev_t. Just mask off the |
| 674 | * upper 16bit to remove any random junk. |
| 675 | */ |
| 676 | |
| 677 | return do_sys_mknodat(l, SCARG(uap, fd), SCARG(uap, path), |
| 678 | SCARG(uap, mode), SCARG(uap, dev) & 0xffff, retval, |
| 679 | UIO_USERSPACE); |
| 680 | } |
| 681 | } |
| 682 | |
| 683 | int |
| 684 | linux_sys_fchmodat(struct lwp *l, const struct linux_sys_fchmodat_args *uap, register_t *retval) |
| 685 | { |
| 686 | /* { |
| 687 | syscallarg(int) fd; |
| 688 | syscallarg(const char *) path; |
| 689 | syscallarg(linux_umode_t) mode; |
| 690 | } */ |
| 691 | |
| 692 | return do_sys_chmodat(l, SCARG(uap, fd), SCARG(uap, path), |
| 693 | SCARG(uap, mode), AT_SYMLINK_FOLLOW); |
| 694 | } |
| 695 | |
| 696 | int |
| 697 | linux_sys_fchownat(struct lwp *l, const struct linux_sys_fchownat_args *uap, register_t *retval) |
| 698 | { |
| 699 | /* { |
| 700 | syscallarg(int) fd; |
| 701 | syscallarg(const char *) path; |
| 702 | syscallarg(uid_t) owner; |
| 703 | syscallarg(gid_t) group; |
| 704 | syscallarg(int) flag; |
| 705 | } */ |
| 706 | int flag; |
| 707 | |
| 708 | flag = linux_to_bsd_atflags(SCARG(uap, flag)); |
| 709 | return do_sys_chownat(l, SCARG(uap, fd), SCARG(uap, path), |
| 710 | SCARG(uap, owner), SCARG(uap, group), flag); |
| 711 | } |
| 712 | |
| 713 | int |
| 714 | linux_sys_faccessat(struct lwp *l, const struct linux_sys_faccessat_args *uap, register_t *retval) |
| 715 | { |
| 716 | /* { |
| 717 | syscallarg(int) fd; |
| 718 | syscallarg(const char *) path; |
| 719 | syscallarg(int) amode; |
| 720 | } */ |
| 721 | |
| 722 | return do_sys_accessat(l, SCARG(uap, fd), SCARG(uap, path), |
| 723 | SCARG(uap, amode), AT_SYMLINK_FOLLOW); |
| 724 | } |
| 725 | |
| 726 | /* |
| 727 | * This is just fsync() for now (just as it is in the Linux kernel) |
| 728 | * Note: this is not implemented under Linux on Alpha and Arm |
| 729 | * but should still be defined in our syscalls.master. |
| 730 | * (syscall #148 on the arm) |
| 731 | */ |
| 732 | int |
| 733 | linux_sys_fdatasync(struct lwp *l, const struct linux_sys_fdatasync_args *uap, register_t *retval) |
| 734 | { |
| 735 | /* { |
| 736 | syscallarg(int) fd; |
| 737 | } */ |
| 738 | |
| 739 | return sys_fsync(l, (const void *)uap, retval); |
| 740 | } |
| 741 | |
| 742 | /* |
| 743 | * pread(2). |
| 744 | */ |
| 745 | int |
| 746 | linux_sys_pread(struct lwp *l, const struct linux_sys_pread_args *uap, register_t *retval) |
| 747 | { |
| 748 | /* { |
| 749 | syscallarg(int) fd; |
| 750 | syscallarg(void *) buf; |
| 751 | syscallarg(size_t) nbyte; |
| 752 | syscallarg(off_t) offset; |
| 753 | } */ |
| 754 | struct sys_pread_args pra; |
| 755 | |
| 756 | SCARG(&pra, fd) = SCARG(uap, fd); |
| 757 | SCARG(&pra, buf) = SCARG(uap, buf); |
| 758 | SCARG(&pra, nbyte) = SCARG(uap, nbyte); |
| 759 | SCARG(&pra, PAD) = 0; |
| 760 | SCARG(&pra, offset) = SCARG(uap, offset); |
| 761 | |
| 762 | return sys_pread(l, &pra, retval); |
| 763 | } |
| 764 | |
| 765 | /* |
| 766 | * pwrite(2). |
| 767 | */ |
| 768 | int |
| 769 | linux_sys_pwrite(struct lwp *l, const struct linux_sys_pwrite_args *uap, register_t *retval) |
| 770 | { |
| 771 | /* { |
| 772 | syscallarg(int) fd; |
| 773 | syscallarg(void *) buf; |
| 774 | syscallarg(size_t) nbyte; |
| 775 | syscallarg(off_t) offset; |
| 776 | } */ |
| 777 | struct sys_pwrite_args pra; |
| 778 | |
| 779 | SCARG(&pra, fd) = SCARG(uap, fd); |
| 780 | SCARG(&pra, buf) = SCARG(uap, buf); |
| 781 | SCARG(&pra, nbyte) = SCARG(uap, nbyte); |
| 782 | SCARG(&pra, PAD) = 0; |
| 783 | SCARG(&pra, offset) = SCARG(uap, offset); |
| 784 | |
| 785 | return sys_pwrite(l, &pra, retval); |
| 786 | } |
| 787 | |
| 788 | int |
| 789 | linux_sys_dup3(struct lwp *l, const struct linux_sys_dup3_args *uap, |
| 790 | register_t *retval) |
| 791 | { |
| 792 | /* { |
| 793 | syscallarg(int) from; |
| 794 | syscallarg(int) to; |
| 795 | syscallarg(int) flags; |
| 796 | } */ |
| 797 | int flags; |
| 798 | |
| 799 | flags = linux_to_bsd_ioflags(SCARG(uap, flags)); |
| 800 | if ((flags & ~O_CLOEXEC) != 0) |
| 801 | return EINVAL; |
| 802 | |
| 803 | if (SCARG(uap, from) == SCARG(uap, to)) |
| 804 | return EINVAL; |
| 805 | |
| 806 | return dodup(l, SCARG(uap, from), SCARG(uap, to), flags, retval); |
| 807 | } |
| 808 | |
| 809 | |
| 810 | int |
| 811 | linux_to_bsd_atflags(int lflags) |
| 812 | { |
| 813 | int bflags = 0; |
| 814 | |
| 815 | if (lflags & LINUX_AT_SYMLINK_NOFOLLOW) |
| 816 | bflags |= AT_SYMLINK_NOFOLLOW; |
| 817 | if (lflags & LINUX_AT_REMOVEDIR) |
| 818 | bflags |= AT_REMOVEDIR; |
| 819 | if (lflags & LINUX_AT_SYMLINK_FOLLOW) |
| 820 | bflags |= AT_SYMLINK_FOLLOW; |
| 821 | |
| 822 | return bflags; |
| 823 | } |
| 824 | |
| 825 | |
| 826 | #define LINUX_NOT_SUPPORTED(fun) \ |
| 827 | int \ |
| 828 | fun(struct lwp *l, const struct fun##_args *uap, register_t *retval) \ |
| 829 | { \ |
| 830 | return EOPNOTSUPP; \ |
| 831 | } |
| 832 | |
| 833 | LINUX_NOT_SUPPORTED(linux_sys_setxattr) |
| 834 | LINUX_NOT_SUPPORTED(linux_sys_lsetxattr) |
| 835 | LINUX_NOT_SUPPORTED(linux_sys_fsetxattr) |
| 836 | |
| 837 | LINUX_NOT_SUPPORTED(linux_sys_getxattr) |
| 838 | LINUX_NOT_SUPPORTED(linux_sys_lgetxattr) |
| 839 | LINUX_NOT_SUPPORTED(linux_sys_fgetxattr) |
| 840 | |
| 841 | LINUX_NOT_SUPPORTED(linux_sys_listxattr) |
| 842 | LINUX_NOT_SUPPORTED(linux_sys_llistxattr) |
| 843 | LINUX_NOT_SUPPORTED(linux_sys_flistxattr) |
| 844 | |
| 845 | LINUX_NOT_SUPPORTED(linux_sys_removexattr) |
| 846 | LINUX_NOT_SUPPORTED(linux_sys_lremovexattr) |
| 847 | LINUX_NOT_SUPPORTED(linux_sys_fremovexattr) |
| 848 | |