diff options
-rw-r--r-- | pkg/abi/linux/fs.go | 7 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/linux64.go | 2 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_sync.go | 63 |
3 files changed, 71 insertions, 1 deletions
diff --git a/pkg/abi/linux/fs.go b/pkg/abi/linux/fs.go index 7817bfb52..0b1c9f3db 100644 --- a/pkg/abi/linux/fs.go +++ b/pkg/abi/linux/fs.go @@ -73,3 +73,10 @@ type Statfs struct { // Spare is unused. Spare [4]uint64 } + +// Sync_file_range flags, from include/uapi/linux/fs.h +const ( + SYNC_FILE_RANGE_WAIT_BEFORE = 1 + SYNC_FILE_RANGE_WRITE = 2 + SYNC_FILE_RANGE_WAIT_AFTER = 4 +) diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index 13084c0ef..9912ab2b5 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -325,7 +325,7 @@ var AMD64 = &kernel.SyscallTable{ 274: syscalls.Error(syscall.ENOSYS), // GetRobustList, obsolete // 275: Splice, TODO // 276: Tee, TODO - // 277: SyncFileRange, TODO + 277: SyncFileRange, // 278: Vmsplice, TODO 279: syscalls.CapError(linux.CAP_SYS_NICE), // MovePages, requires cap_sys_nice (mostly) 280: Utimensat, diff --git a/pkg/sentry/syscalls/linux/sys_sync.go b/pkg/sentry/syscalls/linux/sys_sync.go index 826c6869d..68488330f 100644 --- a/pkg/sentry/syscalls/linux/sys_sync.go +++ b/pkg/sentry/syscalls/linux/sys_sync.go @@ -15,6 +15,7 @@ package linux import ( + "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/sentry/arch" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" "gvisor.googlesource.com/gvisor/pkg/sentry/kernel" @@ -73,3 +74,65 @@ func Fdatasync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys err := file.Fsync(t, 0, fs.FileMaxOffset, fs.SyncData) return 0, nil, syserror.ConvertIntr(err, kernel.ERESTARTSYS) } + +// SyncFileRange implements linux syscall sync_file_rage(2) +func SyncFileRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + var err error + + offset := args[1].Int64() + nbytes := args[2].Int64() + uflags := args[3].Uint() + + if offset < 0 || offset+nbytes < offset { + return 0, nil, syserror.EINVAL + } + + if uflags&^(linux.SYNC_FILE_RANGE_WAIT_BEFORE| + linux.SYNC_FILE_RANGE_WRITE| + linux.SYNC_FILE_RANGE_WAIT_AFTER) != 0 { + return 0, nil, syserror.EINVAL + } + + if nbytes == 0 { + nbytes = fs.FileMaxOffset + } + + fd := kdefs.FD(args[0].Int()) + file := t.FDMap().GetFile(fd) + if file == nil { + return 0, nil, syserror.EBADF + } + defer file.DecRef() + + // SYNC_FILE_RANGE_WAIT_BEFORE waits upon write-out of all pages in the + // specified range that have already been submitted to the device + // driver for write-out before performing any write. + if uflags&linux.SYNC_FILE_RANGE_WAIT_BEFORE != 0 && + uflags&linux.SYNC_FILE_RANGE_WAIT_AFTER == 0 { + t.Kernel().EmitUnimplementedEvent(t) + return 0, nil, syserror.ENOSYS + } + + // SYNC_FILE_RANGE_WRITE initiates write-out of all dirty pages in the + // specified range which are not presently submitted write-out. + // + // It looks impossible to implement this functionality without a + // massive rework of the vfs subsystem. file.Fsync() take a file lock + // for the entire operation, so even if it is running in a go routing, + // it blocks other file operations instead of flushing data in the + // background. + // + // It should be safe to skipped this flag while nobody uses + // SYNC_FILE_RANGE_WAIT_BEFORE. + + // SYNC_FILE_RANGE_WAIT_AFTER waits upon write-out of all pages in the + // range after performing any write. + // + // In Linux, sync_file_range() doesn't writes out the file's + // meta-data, but fdatasync() does if a file size is changed. + if uflags&linux.SYNC_FILE_RANGE_WAIT_AFTER != 0 { + err = file.Fsync(t, offset, fs.FileMaxOffset, fs.SyncData) + } + + return 0, nil, syserror.ConvertIntr(err, kernel.ERESTARTSYS) +} |