github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/sys_sync.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package linux
    16  
    17  import (
    18  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    19  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    20  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    21  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    23  	"github.com/SagerNet/gvisor/pkg/syserror"
    24  )
    25  
    26  // LINT.IfChange
    27  
    28  // Sync implements linux system call sync(2).
    29  func Sync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    30  	t.MountNamespace().SyncAll(t)
    31  	// Sync is always successful.
    32  	return 0, nil, nil
    33  }
    34  
    35  // Syncfs implements linux system call syncfs(2).
    36  func Syncfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    37  	fd := args[0].Int()
    38  
    39  	file := t.GetFile(fd)
    40  	if file == nil {
    41  		return 0, nil, linuxerr.EBADF
    42  	}
    43  	defer file.DecRef(t)
    44  
    45  	// Use "sync-the-world" for now, it's guaranteed that fd is at least
    46  	// on the root filesystem.
    47  	return Sync(t, args)
    48  }
    49  
    50  // Fsync implements linux syscall fsync(2).
    51  func Fsync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    52  	fd := args[0].Int()
    53  
    54  	file := t.GetFile(fd)
    55  	if file == nil {
    56  		return 0, nil, linuxerr.EBADF
    57  	}
    58  	defer file.DecRef(t)
    59  
    60  	err := file.Fsync(t, 0, fs.FileMaxOffset, fs.SyncAll)
    61  	return 0, nil, syserror.ConvertIntr(err, syserror.ERESTARTSYS)
    62  }
    63  
    64  // Fdatasync implements linux syscall fdatasync(2).
    65  //
    66  // At the moment, it just calls Fsync, which is a big hammer, but correct.
    67  func Fdatasync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    68  	fd := args[0].Int()
    69  
    70  	file := t.GetFile(fd)
    71  	if file == nil {
    72  		return 0, nil, linuxerr.EBADF
    73  	}
    74  	defer file.DecRef(t)
    75  
    76  	err := file.Fsync(t, 0, fs.FileMaxOffset, fs.SyncData)
    77  	return 0, nil, syserror.ConvertIntr(err, syserror.ERESTARTSYS)
    78  }
    79  
    80  // SyncFileRange implements linux syscall sync_file_rage(2)
    81  func SyncFileRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
    82  	var err error
    83  
    84  	fd := args[0].Int()
    85  	offset := args[1].Int64()
    86  	nbytes := args[2].Int64()
    87  	uflags := args[3].Uint()
    88  
    89  	if offset < 0 || offset+nbytes < offset {
    90  		return 0, nil, linuxerr.EINVAL
    91  	}
    92  
    93  	if uflags&^(linux.SYNC_FILE_RANGE_WAIT_BEFORE|
    94  		linux.SYNC_FILE_RANGE_WRITE|
    95  		linux.SYNC_FILE_RANGE_WAIT_AFTER) != 0 {
    96  		return 0, nil, linuxerr.EINVAL
    97  	}
    98  
    99  	if nbytes == 0 {
   100  		nbytes = fs.FileMaxOffset
   101  	}
   102  
   103  	file := t.GetFile(fd)
   104  	if file == nil {
   105  		return 0, nil, linuxerr.EBADF
   106  	}
   107  	defer file.DecRef(t)
   108  
   109  	// SYNC_FILE_RANGE_WAIT_BEFORE waits upon write-out of all pages in the
   110  	// specified range that have already been submitted to the device
   111  	// driver for write-out before performing any write.
   112  	if uflags&linux.SYNC_FILE_RANGE_WAIT_BEFORE != 0 &&
   113  		uflags&linux.SYNC_FILE_RANGE_WAIT_AFTER == 0 {
   114  		t.Kernel().EmitUnimplementedEvent(t)
   115  		return 0, nil, syserror.ENOSYS
   116  	}
   117  
   118  	// SYNC_FILE_RANGE_WRITE initiates write-out of all dirty pages in the
   119  	// specified range which are not presently submitted write-out.
   120  	//
   121  	// It looks impossible to implement this functionality without a
   122  	// massive rework of the vfs subsystem. file.Fsync() take a file lock
   123  	// for the entire operation, so even if it is running in a go routing,
   124  	// it blocks other file operations instead of flushing data in the
   125  	// background.
   126  	//
   127  	// It should be safe to skipped this flag while nobody uses
   128  	// SYNC_FILE_RANGE_WAIT_BEFORE.
   129  	_ = nbytes
   130  
   131  	// SYNC_FILE_RANGE_WAIT_AFTER waits upon write-out of all pages in the
   132  	// range after performing any write.
   133  	//
   134  	// In Linux, sync_file_range() doesn't writes out the  file's
   135  	// meta-data, but fdatasync() does if a file size is changed.
   136  	if uflags&linux.SYNC_FILE_RANGE_WAIT_AFTER != 0 {
   137  		err = file.Fsync(t, offset, fs.FileMaxOffset, fs.SyncData)
   138  	}
   139  
   140  	return 0, nil, syserror.ConvertIntr(err, syserror.ERESTARTSYS)
   141  }
   142  
   143  // LINT.ThenChange(vfs2/sync.go)