From e6f019594e80e0987a12f103a03bca6a4e7138dd Mon Sep 17 00:00:00 2001 From: Fabricio Voznika Date: Tue, 15 Nov 2022 11:51:50 -0800 Subject: [PATCH] Add read/write syscalls to trace points Closes #8092 PiperOrigin-RevId: 488719448 --- examples/seccheck/server.cc | 1 + pkg/sentry/seccheck/metadata_amd64.go | 54 +++++++ pkg/sentry/seccheck/metadata_arm64.go | 54 +++++++ pkg/sentry/seccheck/points/common.proto | 1 + pkg/sentry/seccheck/points/syscall.proto | 17 ++- pkg/sentry/syscalls/linux/linux64.go | 36 ++--- pkg/sentry/syscalls/linux/points.go | 176 +++++++++++++++++++++++ test/trace/trace_test.go | 40 +++++- test/trace/workload/workload.cc | 76 ++++++++++ 9 files changed, 435 insertions(+), 20 deletions(-) diff --git a/examples/seccheck/server.cc b/examples/seccheck/server.cc index 42f604092..0b491e93a 100644 --- a/examples/seccheck/server.cc +++ b/examples/seccheck/server.cc @@ -113,6 +113,7 @@ std::vector dispatchers = { unpackSyscall<::gvisor::syscall::InotifyAddWatch>, unpackSyscall<::gvisor::syscall::InotifyRmWatch>, unpackSyscall<::gvisor::syscall::SocketPair>, + unpackSyscall<::gvisor::syscall::Write>, }; void unpack(absl::string_view buf) { diff --git a/pkg/sentry/seccheck/metadata_amd64.go b/pkg/sentry/seccheck/metadata_amd64.go index c7e405592..a3004e8a2 100644 --- a/pkg/sentry/seccheck/metadata_amd64.go +++ b/pkg/sentry/seccheck/metadata_amd64.go @@ -24,6 +24,12 @@ func init() { Name: "fd_path", }, }) + addSyscallPoint(1, "write", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) addSyscallPoint(2, "open", nil) addSyscallPoint(3, "close", []FieldDesc{ { @@ -31,6 +37,30 @@ func init() { Name: "fd_path", }, }) + addSyscallPoint(17, "pread64", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(18, "pwrite64", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(19, "readv", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(20, "writev", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) addSyscallPoint(41, "socket", nil) addSyscallPoint(42, "connect", []FieldDesc{ { @@ -169,6 +199,30 @@ func init() { }, }) addSyscallPoint(53, "socketpair", nil) + addSyscallPoint(295, "preadv", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(296, "pwritev", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(327, "preadv2", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(328, "pwritev2", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) const lastSyscallInTable = 441 for i := 0; i <= lastSyscallInTable; i++ { diff --git a/pkg/sentry/seccheck/metadata_arm64.go b/pkg/sentry/seccheck/metadata_arm64.go index 7179044ec..1a00eb304 100644 --- a/pkg/sentry/seccheck/metadata_arm64.go +++ b/pkg/sentry/seccheck/metadata_arm64.go @@ -24,6 +24,48 @@ func init() { Name: "fd_path", }, }) + addSyscallPoint(64, "write", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(65, "readv", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(66, "writev", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(67, "pread64", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(68, "pwrite64", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(69, "preadv", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(70, "pwritev", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) addSyscallPoint(57, "close", []FieldDesc{ { ID: FieldSyscallPath, @@ -145,6 +187,18 @@ func init() { }, }) addSyscallPoint(199, "socketpair", nil) + addSyscallPoint(286, "preadv2", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) + addSyscallPoint(287, "pwritev2", []FieldDesc{ + { + ID: FieldSyscallPath, + Name: "fd_path", + }, + }) const lastSyscallInTable = 441 for i := 0; i <= lastSyscallInTable; i++ { diff --git a/pkg/sentry/seccheck/points/common.proto b/pkg/sentry/seccheck/points/common.proto index 19e144f8e..4fb41f03e 100644 --- a/pkg/sentry/seccheck/points/common.proto +++ b/pkg/sentry/seccheck/points/common.proto @@ -131,5 +131,6 @@ enum MessageType { MESSAGE_SYSCALL_INOTIFY_ADD_WATCH = 31; MESSAGE_SYSCALL_INOTIFY_RM_WATCH = 32; MESSAGE_SYSCALL_SOCKETPAIR = 33; + MESSAGE_SYSCALL_WRITE = 34; } // LINT.ThenChange(../../../../examples/seccheck/server.cc) diff --git a/pkg/sentry/seccheck/points/syscall.proto b/pkg/sentry/seccheck/points/syscall.proto index 634e6646f..70de1d0ec 100644 --- a/pkg/sentry/seccheck/points/syscall.proto +++ b/pkg/sentry/seccheck/points/syscall.proto @@ -61,6 +61,21 @@ message Read { int64 fd = 4; string fd_path = 5; uint64 count = 6; + bool has_offset = 7; + int64 offset = 8; + uint32 flags = 9; +} + +message Write { + gvisor.common.ContextData context_data = 1; + Exit exit = 2; + uint64 sysno = 3; + int64 fd = 4; + string fd_path = 5; + uint64 count = 6; + bool has_offset = 7; + int64 offset = 8; + uint32 flags = 9; } message Connect { @@ -267,7 +282,7 @@ message InotifyInit { int32 flags = 4; } -message InotifyAddWatch{ +message InotifyAddWatch { gvisor.common.ContextData context_data = 1; Exit exit = 2; uint64 sysno = 3; diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index a3afeee69..6883ee1a7 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -53,7 +53,7 @@ var AMD64 = &kernel.SyscallTable{ AuditNumber: linux.AUDIT_ARCH_X86_64, Table: map[uintptr]kernel.Syscall{ 0: syscalls.SupportedPoint("read", Read, PointRead), - 1: syscalls.Supported("write", Write), + 1: syscalls.SupportedPoint("write", Write, PointWrite), 2: syscalls.SupportedPoint("open", Open, PointOpen), 3: syscalls.SupportedPoint("close", Close, PointClose), 4: syscalls.Supported("stat", Stat), @@ -69,10 +69,10 @@ var AMD64 = &kernel.SyscallTable{ 14: syscalls.Supported("rt_sigprocmask", RtSigprocmask), 15: syscalls.Supported("rt_sigreturn", RtSigreturn), 16: syscalls.Supported("ioctl", Ioctl), - 17: syscalls.Supported("pread64", Pread64), - 18: syscalls.Supported("pwrite64", Pwrite64), - 19: syscalls.Supported("readv", Readv), - 20: syscalls.Supported("writev", Writev), + 17: syscalls.SupportedPoint("pread64", Pread64, PointPread64), + 18: syscalls.SupportedPoint("pwrite64", Pwrite64, PointPwrite64), + 19: syscalls.SupportedPoint("readv", Readv, PointReadv), + 20: syscalls.SupportedPoint("writev", Writev, PointWritev), 21: syscalls.Supported("access", Access), 22: syscalls.SupportedPoint("pipe", Pipe, PointPipe), 23: syscalls.Supported("select", Select), @@ -347,8 +347,8 @@ var AMD64 = &kernel.SyscallTable{ 292: syscalls.SupportedPoint("dup3", Dup3, PointDup3), 293: syscalls.SupportedPoint("pipe2", Pipe2, PointPipe2), 294: syscalls.PartiallySupportedPoint("inotify_init1", InotifyInit1, PointInotifyInit1, "inotify events are only available inside the sandbox.", nil), - 295: syscalls.Supported("preadv", Preadv), - 296: syscalls.Supported("pwritev", Pwritev), + 295: syscalls.SupportedPoint("preadv", Preadv, PointPreadv), + 296: syscalls.SupportedPoint("pwritev", Pwritev, PointPwritev), 297: syscalls.Supported("rt_tgsigqueueinfo", RtTgsigqueueinfo), 298: syscalls.ErrorWithEvent("perf_event_open", linuxerr.ENODEV, "No support for perf counters", nil), 299: syscalls.Supported("recvmmsg", RecvMMsg), @@ -382,8 +382,8 @@ var AMD64 = &kernel.SyscallTable{ // Syscalls implemented after 325 are "backports" from versions // of Linux after 4.4. 326: syscalls.ErrorWithEvent("copy_file_range", linuxerr.ENOSYS, "", nil), - 327: syscalls.Supported("preadv2", Preadv2), - 328: syscalls.Supported("pwritev2", Pwritev2), + 327: syscalls.SupportedPoint("preadv2", Preadv2, PointPreadv2), + 328: syscalls.SupportedPoint("pwritev2", Pwritev2, PointPwritev2), 329: syscalls.ErrorWithEvent("pkey_mprotect", linuxerr.ENOSYS, "", nil), 330: syscalls.ErrorWithEvent("pkey_alloc", linuxerr.ENOSYS, "", nil), 331: syscalls.ErrorWithEvent("pkey_free", linuxerr.ENOSYS, "", nil), @@ -495,13 +495,13 @@ var ARM64 = &kernel.SyscallTable{ 61: syscalls.Supported("getdents64", Getdents64), 62: syscalls.Supported("lseek", Lseek), 63: syscalls.SupportedPoint("read", Read, PointRead), - 64: syscalls.Supported("write", Write), - 65: syscalls.Supported("readv", Readv), - 66: syscalls.Supported("writev", Writev), - 67: syscalls.Supported("pread64", Pread64), - 68: syscalls.Supported("pwrite64", Pwrite64), - 69: syscalls.Supported("preadv", Preadv), - 70: syscalls.Supported("pwritev", Pwritev), + 64: syscalls.SupportedPoint("write", Write, PointWrite), + 65: syscalls.SupportedPoint("readv", Readv, PointReadv), + 66: syscalls.SupportedPoint("writev", Writev, PointWritev), + 67: syscalls.SupportedPoint("pread64", Pread64, PointPread64), + 68: syscalls.SupportedPoint("pwrite64", Pwrite64, PointPwrite64), + 69: syscalls.SupportedPoint("preadv", Preadv, PointPreadv), + 70: syscalls.SupportedPoint("pwritev", Pwritev, PointPwritev), 71: syscalls.Supported("sendfile", Sendfile), 72: syscalls.Supported("pselect", Pselect), 73: syscalls.Supported("ppoll", Ppoll), @@ -703,8 +703,8 @@ var ARM64 = &kernel.SyscallTable{ // Syscalls after 284 are "backports" from versions of Linux after 4.4. 285: syscalls.ErrorWithEvent("copy_file_range", linuxerr.ENOSYS, "", nil), - 286: syscalls.Supported("preadv2", Preadv2), - 287: syscalls.Supported("pwritev2", Pwritev2), + 286: syscalls.SupportedPoint("preadv2", Preadv2, PointPreadv2), + 287: syscalls.SupportedPoint("pwritev2", Pwritev2, PointPwritev2), 288: syscalls.ErrorWithEvent("pkey_mprotect", linuxerr.ENOSYS, "", nil), 289: syscalls.ErrorWithEvent("pkey_alloc", linuxerr.ENOSYS, "", nil), 290: syscalls.ErrorWithEvent("pkey_free", linuxerr.ENOSYS, "", nil), diff --git a/pkg/sentry/syscalls/linux/points.go b/pkg/sentry/syscalls/linux/points.go index e585a3e77..f6f29bd92 100644 --- a/pkg/sentry/syscalls/linux/points.go +++ b/pkg/sentry/syscalls/linux/points.go @@ -24,6 +24,7 @@ import ( "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/seccheck" pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto" + "gvisor.dev/gvisor/pkg/usermem" ) func newExitMaybe(info kernel.SyscallInfo) *pb.Exit { @@ -58,6 +59,14 @@ func getFilePath(t *kernel.Task, fd int32) string { return path } +func getIovecSize(t *kernel.Task, addr hostarch.Addr, iovcnt int) uint64 { + dst, err := t.IovecsIOSequence(addr, iovcnt, usermem.IOOpts{AddressSpaceActive: true}) + if err != nil { + return 0 + } + return uint64(dst.NumBytes()) +} + // PointOpen converts open(2) syscall to proto. func PointOpen(t *kernel.Task, _ seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { p := &pb.Open{ @@ -167,6 +176,173 @@ func PointRead(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData return p, pb.MessageType_MESSAGE_SYSCALL_READ } +// PointPread64 converts pread64(2) syscall to proto. +func PointPread64(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Read{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: uint64(info.Args[2].SizeT()), + HasOffset: true, + Offset: info.Args[3].Int64(), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_READ +} + +// PointReadv converts readv(2) syscall to proto. +func PointReadv(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Read{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_READ +} + +// PointPreadv converts preadv(2) syscall to proto. +func PointPreadv(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Read{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())), + HasOffset: true, + Offset: info.Args[3].Int64(), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_READ +} + +// PointPreadv2 converts preadv2(2) syscall to proto. +func PointPreadv2(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Read{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())), + HasOffset: true, + Offset: info.Args[3].Int64(), + Flags: info.Args[5].Uint(), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_READ +} + +// PointWrite converts write(2) syscall to proto. +func PointWrite(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Write{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: uint64(info.Args[2].SizeT()), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_WRITE +} + +// PointPwrite64 converts pwrite64(2) syscall to proto. +func PointPwrite64(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Write{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: uint64(info.Args[2].SizeT()), + HasOffset: true, + Offset: info.Args[3].Int64(), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_WRITE +} + +// PointWritev converts writev(2) syscall to proto. +func PointWritev(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Write{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_WRITE +} + +// PointPwritev converts pwritev(2) syscall to proto. +func PointPwritev(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Write{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())), + HasOffset: true, + Offset: info.Args[3].Int64(), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_WRITE +} + +// PointPwritev2 converts pwritev2(2) syscall to proto. +func PointPwritev2(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { + p := &pb.Write{ + ContextData: cxtData, + Sysno: uint64(info.Sysno), + Fd: int64(info.Args[0].Int()), + Count: getIovecSize(t, info.Args[1].Pointer(), int(info.Args[2].Int())), + HasOffset: true, + Offset: info.Args[3].Int64(), + Flags: info.Args[5].Uint(), + } + if fields.Local.Contains(seccheck.FieldSyscallPath) { + p.FdPath = getFilePath(t, int32(p.Fd)) + } + + p.Exit = newExitMaybe(info) + + return p, pb.MessageType_MESSAGE_SYSCALL_WRITE +} + // PointSocket converts socket(2) syscall to proto. func PointSocket(_ *kernel.Task, _ seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) { p := &pb.Socket{ diff --git a/test/trace/trace_test.go b/test/trace/trace_test.go index 174e1ed2a..fdf10fadd 100644 --- a/test/trace/trace_test.go +++ b/test/trace/trace_test.go @@ -108,6 +108,7 @@ func matchPoints(t *testing.T, msgs []test.Message) { pb.MessageType_MESSAGE_SYSCALL_RAW: {checker: checkSyscallRaw}, pb.MessageType_MESSAGE_SYSCALL_READ: {checker: checkSyscallRead}, pb.MessageType_MESSAGE_SYSCALL_SOCKET: {checker: checkSyscallSocket}, + pb.MessageType_MESSAGE_SYSCALL_WRITE: {checker: checkSyscallWrite}, // TODO(gvisor.dev/issue/4805): Add validation for these messages. pb.MessageType_MESSAGE_SYSCALL_ACCEPT: {checker: checkTODO}, @@ -291,7 +292,44 @@ func checkSyscallRead(msg test.Message) error { } if p.Fd < 0 { // Although negative FD is possible, it doesn't happen in the test. - return fmt.Errorf("reading negative FD: %d", p.Fd) + return fmt.Errorf("read negative FD: %d", p.Fd) + } + if p.HasOffset { + // Workload always uses 20 for read offsets (account for partial reads). + if lower, upper := int64(20), int64(120); p.Offset < lower && p.Offset > upper { + return fmt.Errorf("invalid offset, want: [%d, %d], got: %d", lower, upper, p.Offset) + } + } else if p.Offset != 0 { + return fmt.Errorf("offset should be 0: %+v", &p) + } + if p.Flags != 0 && p.Flags != unix.RWF_HIPRI { + return fmt.Errorf("invalid flag value, want: 0 || RWF_HIPRI, got: %+x", p.Flags) + } + return nil +} + +func checkSyscallWrite(msg test.Message) error { + p := pb.Write{} + if err := proto.Unmarshal(msg.Msg, &p); err != nil { + return err + } + if err := checkContextData(p.ContextData); err != nil { + return err + } + if p.Fd < 0 { + // Although negative FD is possible, it doesn't happen in the test. + return fmt.Errorf("write negative FD: %d", p.Fd) + } + if p.HasOffset { + // Workload always uses 10 for write offsets (account for partial writes). + if lower, upper := int64(10), int64(110); p.Offset < lower && p.Offset > upper { + return fmt.Errorf("invalid offset, want: [%d, %d], got: %d", lower, upper, p.Offset) + } + } else if p.Offset != 0 { + return fmt.Errorf("offset should be 0: %+v", &p) + } + if p.Flags != 0 && p.Flags != unix.RWF_HIPRI { + return fmt.Errorf("invalid flag value, want: 0 || RWF_HIPRI, got: %+x", p.Flags) } return nil } diff --git a/test/trace/workload/workload.cc b/test/trace/workload/workload.cc index 457cafb5b..b738cf52f 100644 --- a/test/trace/workload/workload.cc +++ b/test/trace/workload/workload.cc @@ -115,12 +115,88 @@ void runSocket() { } } +void runReadWrite() { + const std::string path = "read-write.txt"; + auto fd_or = Open(path, O_RDWR | O_CREAT, 0644); + if (!fd_or.ok()) { + err(1, "open(O_CREAT): %s", fd_or.error().ToString().c_str()); + } + auto cleaup = absl::MakeCleanup([path] { unlink(path.c_str()); }); + + auto fd = std::move(fd_or.ValueOrDie()); + + // Test different flavors of write. + char msg[] = "hello world"; + if (WriteFd(fd.get(), msg, ABSL_ARRAYSIZE(msg)) < 0) { + err(1, "write"); + } + if (PwriteFd(fd.get(), msg, ABSL_ARRAYSIZE(msg), 10) < 0) { + err(1, "pwrite"); + } + + struct iovec write_vecs[] = { + { + .iov_base = msg, + .iov_len = ABSL_ARRAYSIZE(msg), + }, + { + .iov_base = msg, + .iov_len = ABSL_ARRAYSIZE(msg) / 2, + }, + }; + if (writev(fd.get(), write_vecs, ABSL_ARRAYSIZE(write_vecs)) < 0) { + err(1, "writev"); + } + if (pwritev(fd.get(), write_vecs, ABSL_ARRAYSIZE(write_vecs), 10) < 0) { + err(1, "pwritev"); + } + if (pwritev2(fd.get(), write_vecs, ABSL_ARRAYSIZE(write_vecs), 10, + RWF_HIPRI) < 0) { + err(1, "pwritev2"); + } + + // Rewind the file and test different flavors of read. + if (lseek(fd.get(), 0, SEEK_SET) < 0) { + err(1, "seek(0)"); + } + char buf[1024]; + if (ReadFd(fd.get(), buf, ABSL_ARRAYSIZE(buf)) < 0) { + err(1, "read"); + } + if (PreadFd(fd.get(), buf, ABSL_ARRAYSIZE(buf), 20) < 0) { + err(1, "read"); + } + + // Reuse same buffer, since it's not using the result anyways. + struct iovec read_vecs[] = { + { + .iov_base = buf, + .iov_len = ABSL_ARRAYSIZE(msg), + }, + { + .iov_base = buf, + .iov_len = ABSL_ARRAYSIZE(msg) / 2, + }, + }; + if (readv(fd.get(), read_vecs, ABSL_ARRAYSIZE(read_vecs)) < 0) { + err(1, "writev"); + } + if (preadv(fd.get(), read_vecs, ABSL_ARRAYSIZE(read_vecs), 20) < 0) { + err(1, "pwritev"); + } + if (preadv2(fd.get(), read_vecs, ABSL_ARRAYSIZE(read_vecs), 20, RWF_HIPRI) < + 0) { + err(1, "pwritev2"); + } +} + } // namespace testing } // namespace gvisor int main(int argc, char** argv) { ::gvisor::testing::runForkExecve(); ::gvisor::testing::runSocket(); + ::gvisor::testing::runReadWrite(); return 0; }