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