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