diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/abi/linux/file.go | 7 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/linux64.go | 2 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_read.go | 22 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_write.go | 66 |
4 files changed, 82 insertions, 15 deletions
diff --git a/pkg/abi/linux/file.go b/pkg/abi/linux/file.go index ac49ae9a6..ae33f4a4d 100644 --- a/pkg/abi/linux/file.go +++ b/pkg/abi/linux/file.go @@ -152,9 +152,10 @@ const ( // Values for preadv2/pwritev2. const ( - RWF_HIPRI = 0x0001 - RWF_DSYNC = 0X0002 - RWF_SYNC = 0x0004 + RWF_HIPRI = 0x00000001 + RWF_DSYNC = 0x00000002 + RWF_SYNC = 0x00000004 + RWF_VALID = RWF_HIPRI | RWF_DSYNC | RWF_SYNC ) // Stat represents struct stat. diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index cc5ebb955..e855590e6 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -377,7 +377,7 @@ var AMD64 = &kernel.SyscallTable{ // Syscalls after 325 are "backports" from versions of Linux after 4.4. // 326: CopyFileRange, 327: Preadv2, - // 328: Pwritev2, // Pwritev2, TODO + 328: Pwritev2, }, Emulate: map[usermem.Addr]uintptr{ diff --git a/pkg/sentry/syscalls/linux/sys_read.go b/pkg/sentry/syscalls/linux/sys_read.go index cbb9eb9f8..b6df4d9d4 100644 --- a/pkg/sentry/syscalls/linux/sys_read.go +++ b/pkg/sentry/syscalls/linux/sys_read.go @@ -188,14 +188,20 @@ func Preadv(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal } // Preadv2 implements linux syscall preadv2(2). +// TODO: Implement RWF_HIPRI functionality. func Preadv2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + // While the syscall is + // preadv2(int fd, struct iovec* iov, int iov_cnt, off_t offset, int flags) + // the linux internal call + // (https://elixir.bootlin.com/linux/v4.18/source/fs/read_write.c#L1248) + // splits the offset argument into a high/low value for compatibility with + // 32-bit architectures. The flags argument is the 5th argument. + fd := kdefs.FD(args[0].Int()) addr := args[1].Pointer() iovcnt := int(args[2].Int()) offset := args[3].Int64() - flags := int(args[4].Int()) - - validFlags := linux.RWF_HIPRI + flags := int(args[5].Int()) file := t.FDMap().GetFile(fd) if file == nil { @@ -219,14 +225,8 @@ func Preadv2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca } // Check flags field. - if flags != 0 { - if flags&^validFlags != 0 { - return 0, nil, syserror.EINVAL - } - // RWF_HIPRI must be called on a file with O_DIRECT flag set. - if flags&linux.RWF_HIPRI != 0 && !file.Flags().Direct { - return 0, nil, syserror.EINVAL - } + if flags&^linux.RWF_VALID != 0 { + return 0, nil, syserror.EOPNOTSUPP } // Read the iovecs that specify the destination of the read. diff --git a/pkg/sentry/syscalls/linux/sys_write.go b/pkg/sentry/syscalls/linux/sys_write.go index 08e263112..750a098cd 100644 --- a/pkg/sentry/syscalls/linux/sys_write.go +++ b/pkg/sentry/syscalls/linux/sys_write.go @@ -187,6 +187,72 @@ func Pwritev(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca return uintptr(n), nil, handleIOError(t, n != 0, err, kernel.ERESTARTSYS, "pwritev", file) } +// Pwritev2 implements linux syscall pwritev2(2). +// TODO: Implement RWF_HIPRI functionality. +// TODO: Implement O_SYNC and D_SYNC functionality. +func Pwritev2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + // While the syscall is + // pwritev2(int fd, struct iovec* iov, int iov_cnt, off_t offset, int flags) + // the linux internal call + // (https://elixir.bootlin.com/linux/v4.18/source/fs/read_write.c#L1354) + // splits the offset argument into a high/low value for compatibility with + // 32-bit architectures. The flags argument is the 5th argument. + + fd := kdefs.FD(args[0].Int()) + addr := args[1].Pointer() + iovcnt := int(args[2].Int()) + offset := args[3].Int64() + flags := int(args[5].Int()) + + if int(args[4].Int())&0x4 == 1 { + return 0, nil, syserror.EACCES + } + + file := t.FDMap().GetFile(fd) + if file == nil { + return 0, nil, syserror.EBADF + } + defer file.DecRef() + + // Check that the offset is legitimate. + if offset < -1 { + return 0, nil, syserror.EINVAL + } + + // Is writing at an offset supported? + if offset > -1 && !file.Flags().Pwrite { + return 0, nil, syserror.ESPIPE + } + + if flags&^linux.RWF_VALID != 0 { + return uintptr(flags), nil, syserror.EOPNOTSUPP + } + + // Check that the file is writeable. + if !file.Flags().Write { + return 0, nil, syserror.EBADF + } + + // Read the iovecs that specify the source of the write. + src, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{ + AddressSpaceActive: true, + }) + if err != nil { + return 0, nil, err + } + + // If pwritev2 is called with an offset of -1, writev is called. + if offset == -1 { + n, err := writev(t, file, src) + t.IOUsage().AccountWriteSyscall(n) + return uintptr(n), nil, handleIOError(t, n != 0, err, kernel.ERESTARTSYS, "pwritev2", file) + } + + n, err := pwritev(t, file, src, offset) + t.IOUsage().AccountWriteSyscall(n) + return uintptr(n), nil, handleIOError(t, n != 0, err, kernel.ERESTARTSYS, "pwritev2", file) +} + func writev(t *kernel.Task, f *fs.File, src usermem.IOSequence) (int64, error) { n, err := f.Writev(t, src) if err != syserror.ErrWouldBlock || f.Flags().NonBlocking { |