github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/vfs/syncing_file_linux.go (about)

     1  // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  // +build linux,!arm
     6  
     7  package vfs
     8  
     9  import (
    10  	"sync/atomic"
    11  	"syscall"
    12  )
    13  
    14  type syncFileRange func(fd int, off int64, n int64, flags int) (err error)
    15  
    16  // sync_file_range depends on both the filesystem, and the broader kernel
    17  // support. In particular, Windows Subsystem for Linux does not support
    18  // sync_file_range, even when used with ext{2,3,4}. syncRangeSmokeTest performs
    19  // a test of of sync_file_range, returning false on ENOSYS, and true otherwise.
    20  func syncRangeSmokeTest(fd uintptr, fn syncFileRange) bool {
    21  	err := fn(int(fd), 0 /* offset */, 0 /* nbytes */, 0 /* flags */)
    22  	return err != syscall.ENOSYS
    23  }
    24  
    25  func isSyncRangeSupported(fd uintptr) bool {
    26  	var stat syscall.Statfs_t
    27  	if err := syscall.Fstatfs(int(fd), &stat); err != nil {
    28  		return false
    29  	}
    30  
    31  	// Whitelist which filesystems we allow using sync_file_range with as some
    32  	// filesystems treat that syscall as a noop (notably ZFS). A whitelist is
    33  	// used instead of a blacklist in order to have a more graceful failure mode
    34  	// in case a filesystem we haven't tested is encountered. Currently only
    35  	// ext2/3/4 are known to work properly.
    36  	const extMagic = 0xef53
    37  	switch stat.Type {
    38  	case extMagic:
    39  		return syncRangeSmokeTest(fd, syscall.SyncFileRange)
    40  	}
    41  	return false
    42  }
    43  
    44  func (f *syncingFile) init() {
    45  	if f.fd == 0 {
    46  		return
    47  	}
    48  	f.useSyncRange = isSyncRangeSupported(f.fd)
    49  	if f.useSyncRange {
    50  		f.syncTo = f.syncToRange
    51  	} else {
    52  		f.syncTo = f.syncToFdatasync
    53  	}
    54  }
    55  
    56  func (f *syncingFile) syncData() error {
    57  	if f.fd == 0 {
    58  		return f.File.Sync()
    59  	}
    60  	return syscall.Fdatasync(int(f.fd))
    61  }
    62  
    63  func (f *syncingFile) syncToFdatasync(_ int64) error {
    64  	f.ratchetSyncOffset(atomic.LoadInt64(&f.atomic.offset))
    65  	return f.syncData()
    66  }
    67  
    68  func (f *syncingFile) syncToRange(offset int64) error {
    69  	const (
    70  		waitBefore = 0x1
    71  		write      = 0x2
    72  		// waitAfter = 0x4
    73  	)
    74  
    75  	f.ratchetSyncOffset(offset)
    76  	return syscall.SyncFileRange(int(f.fd), 0, offset, write|waitBefore)
    77  }