github.com/ooni/psiphon/tunnel-core@v0.0.0-20230105123940-fe12a24c96ee/oovendor/bolt/bolt_windows.go (about)

     1  package bolt
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"syscall"
     7  	"time"
     8  	"unsafe"
     9  )
    10  
    11  // LockFileEx code derived from golang build filemutex_windows.go @ v1.5.1
    12  var (
    13  	modkernel32      = syscall.NewLazyDLL("kernel32.dll")
    14  	procLockFileEx   = modkernel32.NewProc("LockFileEx")
    15  	procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
    16  )
    17  
    18  const (
    19  	// see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
    20  	flagLockExclusive       = 2
    21  	flagLockFailImmediately = 1
    22  
    23  	// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
    24  	errLockViolation syscall.Errno = 0x21
    25  )
    26  
    27  func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
    28  	r, _, err := procLockFileEx.Call(uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
    29  	if r == 0 {
    30  		return err
    31  	}
    32  	return nil
    33  }
    34  
    35  func unlockFileEx(h syscall.Handle, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
    36  	r, _, err := procUnlockFileEx.Call(uintptr(h), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)), 0)
    37  	if r == 0 {
    38  		return err
    39  	}
    40  	return nil
    41  }
    42  
    43  // fdatasync flushes written data to a file descriptor.
    44  func fdatasync(db *DB) error {
    45  	return db.file.Sync()
    46  }
    47  
    48  // flock acquires an advisory lock on a file descriptor.
    49  func flock(db *DB, exclusive bool, timeout time.Duration) error {
    50  	var t time.Time
    51  	if timeout != 0 {
    52  		t = time.Now()
    53  	}
    54  	var flag uint32 = flagLockFailImmediately
    55  	if exclusive {
    56  		flag |= flagLockExclusive
    57  	}
    58  	for {
    59  		// Fix for https://github.com/etcd-io/bbolt/issues/121. Use byte-range
    60  		// -1..0 as the lock on the database file.
    61  		var m1 uint32 = (1 << 32) - 1 // -1 in a uint32
    62  		err := lockFileEx(syscall.Handle(db.file.Fd()), flag, 0, 1, 0, &syscall.Overlapped{
    63  			Offset:     m1,
    64  			OffsetHigh: m1,
    65  		})
    66  
    67  		if err == nil {
    68  			return nil
    69  		} else if err != errLockViolation {
    70  			return err
    71  		}
    72  
    73  		// If we timed oumercit then return an error.
    74  		if timeout != 0 && time.Since(t) > timeout-flockRetryTimeout {
    75  			return ErrTimeout
    76  		}
    77  
    78  		// Wait for a bit and try again.
    79  		time.Sleep(flockRetryTimeout)
    80  	}
    81  }
    82  
    83  // funlock releases an advisory lock on a file descriptor.
    84  func funlock(db *DB) error {
    85  	var m1 uint32 = (1 << 32) - 1 // -1 in a uint32
    86  	err := unlockFileEx(syscall.Handle(db.file.Fd()), 0, 1, 0, &syscall.Overlapped{
    87  		Offset:     m1,
    88  		OffsetHigh: m1,
    89  	})
    90  	return err
    91  }
    92  
    93  // mmap memory maps a DB's data file.
    94  // Based on: https://github.com/edsrzf/mmap-go
    95  func mmap(db *DB, sz int) error {
    96  	if !db.readOnly {
    97  		// Truncate the database to the size of the mmap.
    98  		if err := db.file.Truncate(int64(sz)); err != nil {
    99  			return fmt.Errorf("truncate: %s", err)
   100  		}
   101  	}
   102  
   103  	// Open a file mapping handle.
   104  	sizelo := uint32(sz >> 32)
   105  	sizehi := uint32(sz) & 0xffffffff
   106  	h, errno := syscall.CreateFileMapping(syscall.Handle(db.file.Fd()), nil, syscall.PAGE_READONLY, sizelo, sizehi, nil)
   107  	if h == 0 {
   108  		return os.NewSyscallError("CreateFileMapping", errno)
   109  	}
   110  
   111  	// Create the memory map.
   112  	addr, errno := syscall.MapViewOfFile(h, syscall.FILE_MAP_READ, 0, 0, uintptr(sz))
   113  	if addr == 0 {
   114  		return os.NewSyscallError("MapViewOfFile", errno)
   115  	}
   116  
   117  	// Close mapping handle.
   118  	if err := syscall.CloseHandle(syscall.Handle(h)); err != nil {
   119  		return os.NewSyscallError("CloseHandle", err)
   120  	}
   121  
   122  	// Convert to a byte array.
   123  	db.data = ((*[maxMapSize]byte)(unsafe.Pointer(addr)))
   124  	db.datasz = sz
   125  
   126  	return nil
   127  }
   128  
   129  // munmap unmaps a pointer from a file.
   130  // Based on: https://github.com/edsrzf/mmap-go
   131  func munmap(db *DB) error {
   132  	if db.data == nil {
   133  		return nil
   134  	}
   135  
   136  	addr := (uintptr)(unsafe.Pointer(&db.data[0]))
   137  	if err := syscall.UnmapViewOfFile(addr); err != nil {
   138  		return os.NewSyscallError("UnmapViewOfFile", err)
   139  	}
   140  	return nil
   141  }