mmap_windows.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // Copyright 2011 Evan Shaw. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package mmap
  5. import (
  6. "errors"
  7. "os"
  8. "sync"
  9. "golang.org/x/sys/windows"
  10. )
  11. // mmap on Windows is a two-step process.
  12. // First, we call CreateFileMapping to get a handle.
  13. // Then, we call MapviewToFile to get an actual pointer into memory.
  14. // Because we want to emulate a POSIX-style mmap, we don't want to expose
  15. // the handle -- only the pointer. We also want to return only a byte slice,
  16. // not a struct, so it's convenient to manipulate.
  17. // We keep this map so that we can get back the original handle from the memory address.
  18. type addrinfo struct {
  19. file windows.Handle
  20. mapview windows.Handle
  21. writable bool
  22. }
  23. var handleLock sync.Mutex
  24. var handleMap = map[uintptr]*addrinfo{}
  25. func mmap(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) {
  26. flProtect := uint32(windows.PAGE_READONLY)
  27. dwDesiredAccess := uint32(windows.FILE_MAP_READ)
  28. writable := false
  29. switch {
  30. case prot&COPY != 0:
  31. flProtect = windows.PAGE_WRITECOPY
  32. dwDesiredAccess = windows.FILE_MAP_COPY
  33. writable = true
  34. case prot&RDWR != 0:
  35. flProtect = windows.PAGE_READWRITE
  36. dwDesiredAccess = windows.FILE_MAP_WRITE
  37. writable = true
  38. }
  39. if prot&EXEC != 0 {
  40. flProtect <<= 4
  41. dwDesiredAccess |= windows.FILE_MAP_EXECUTE
  42. }
  43. // The maximum size is the area of the file, starting from 0,
  44. // that we wish to allow to be mappable. It is the sum of
  45. // the length the user requested, plus the offset where that length
  46. // is starting from. This does not map the data into memory.
  47. maxSizeHigh := uint32((off + int64(len)) >> 32)
  48. maxSizeLow := uint32((off + int64(len)) & 0xFFFFFFFF)
  49. // TODO: Do we need to set some security attributes? It might help portability.
  50. h, errno := windows.CreateFileMapping(windows.Handle(hfile), nil, flProtect, maxSizeHigh, maxSizeLow, nil)
  51. if h == 0 {
  52. return nil, os.NewSyscallError("CreateFileMapping", errno)
  53. }
  54. // Actually map a view of the data into memory. The view's size
  55. // is the length the user requested.
  56. fileOffsetHigh := uint32(off >> 32)
  57. fileOffsetLow := uint32(off & 0xFFFFFFFF)
  58. addr, errno := windows.MapViewOfFile(h, dwDesiredAccess, fileOffsetHigh, fileOffsetLow, uintptr(len))
  59. if addr == 0 {
  60. windows.CloseHandle(windows.Handle(h))
  61. return nil, os.NewSyscallError("MapViewOfFile", errno)
  62. }
  63. handleLock.Lock()
  64. handleMap[addr] = &addrinfo{
  65. file: windows.Handle(hfile),
  66. mapview: h,
  67. writable: writable,
  68. }
  69. handleLock.Unlock()
  70. m := MMap{}
  71. dh := m.header()
  72. dh.Data = addr
  73. dh.Len = len
  74. dh.Cap = dh.Len
  75. return m, nil
  76. }
  77. func (m MMap) flush() error {
  78. addr, len := m.addrLen()
  79. errno := windows.FlushViewOfFile(addr, len)
  80. if errno != nil {
  81. return os.NewSyscallError("FlushViewOfFile", errno)
  82. }
  83. handleLock.Lock()
  84. defer handleLock.Unlock()
  85. handle, ok := handleMap[addr]
  86. if !ok {
  87. // should be impossible; we would've errored above
  88. return errors.New("unknown base address")
  89. }
  90. if handle.writable && handle.file != windows.Handle(^uintptr(0)) {
  91. if err := windows.FlushFileBuffers(handle.file); err != nil {
  92. return os.NewSyscallError("FlushFileBuffers", err)
  93. }
  94. }
  95. return nil
  96. }
  97. func (m MMap) lock() error {
  98. addr, len := m.addrLen()
  99. errno := windows.VirtualLock(addr, len)
  100. return os.NewSyscallError("VirtualLock", errno)
  101. }
  102. func (m MMap) unlock() error {
  103. addr, len := m.addrLen()
  104. errno := windows.VirtualUnlock(addr, len)
  105. return os.NewSyscallError("VirtualUnlock", errno)
  106. }
  107. func (m MMap) unmap() error {
  108. err := m.flush()
  109. if err != nil {
  110. return err
  111. }
  112. addr := m.header().Data
  113. // Lock the UnmapViewOfFile along with the handleMap deletion.
  114. // As soon as we unmap the view, the OS is free to give the
  115. // same addr to another new map. We don't want another goroutine
  116. // to insert and remove the same addr into handleMap while
  117. // we're trying to remove our old addr/handle pair.
  118. handleLock.Lock()
  119. defer handleLock.Unlock()
  120. err = windows.UnmapViewOfFile(addr)
  121. if err != nil {
  122. return err
  123. }
  124. handle, ok := handleMap[addr]
  125. if !ok {
  126. // should be impossible; we would've errored above
  127. return errors.New("unknown base address")
  128. }
  129. delete(handleMap, addr)
  130. e := windows.CloseHandle(windows.Handle(handle.mapview))
  131. return os.NewSyscallError("CloseHandle", e)
  132. }