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