123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- // Copyright 2011 Evan Shaw. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package mmap
- import (
- "errors"
- "os"
- "sync"
- "golang.org/x/sys/windows"
- )
- // mmap on Windows is a two-step process.
- // First, we call CreateFileMapping to get a handle.
- // Then, we call MapviewToFile to get an actual pointer into memory.
- // Because we want to emulate a POSIX-style mmap, we don't want to expose
- // the handle -- only the pointer. We also want to return only a byte slice,
- // not a struct, so it's convenient to manipulate.
- // We keep this map so that we can get back the original handle from the memory address.
- type addrinfo struct {
- file windows.Handle
- mapview windows.Handle
- writable bool
- }
- var handleLock sync.Mutex
- var handleMap = map[uintptr]*addrinfo{}
- func mmap(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) {
- flProtect := uint32(windows.PAGE_READONLY)
- dwDesiredAccess := uint32(windows.FILE_MAP_READ)
- writable := false
- switch {
- case prot© != 0:
- flProtect = windows.PAGE_WRITECOPY
- dwDesiredAccess = windows.FILE_MAP_COPY
- writable = true
- case prot&RDWR != 0:
- flProtect = windows.PAGE_READWRITE
- dwDesiredAccess = windows.FILE_MAP_WRITE
- writable = true
- }
- if prot&EXEC != 0 {
- flProtect <<= 4
- dwDesiredAccess |= windows.FILE_MAP_EXECUTE
- }
- // The maximum size is the area of the file, starting from 0,
- // that we wish to allow to be mappable. It is the sum of
- // the length the user requested, plus the offset where that length
- // is starting from. This does not map the data into memory.
- maxSizeHigh := uint32((off + int64(len)) >> 32)
- maxSizeLow := uint32((off + int64(len)) & 0xFFFFFFFF)
- // TODO: Do we need to set some security attributes? It might help portability.
- h, errno := windows.CreateFileMapping(windows.Handle(hfile), nil, flProtect, maxSizeHigh, maxSizeLow, nil)
- if h == 0 {
- return nil, os.NewSyscallError("CreateFileMapping", errno)
- }
- // Actually map a view of the data into memory. The view's size
- // is the length the user requested.
- fileOffsetHigh := uint32(off >> 32)
- fileOffsetLow := uint32(off & 0xFFFFFFFF)
- addr, errno := windows.MapViewOfFile(h, dwDesiredAccess, fileOffsetHigh, fileOffsetLow, uintptr(len))
- if addr == 0 {
- windows.CloseHandle(windows.Handle(h))
- return nil, os.NewSyscallError("MapViewOfFile", errno)
- }
- handleLock.Lock()
- handleMap[addr] = &addrinfo{
- file: windows.Handle(hfile),
- mapview: h,
- writable: writable,
- }
- handleLock.Unlock()
- m := MMap{}
- dh := m.header()
- dh.Data = addr
- dh.Len = len
- dh.Cap = dh.Len
- return m, nil
- }
- func (m MMap) flush() error {
- addr, len := m.addrLen()
- errno := windows.FlushViewOfFile(addr, len)
- if errno != nil {
- return os.NewSyscallError("FlushViewOfFile", errno)
- }
- handleLock.Lock()
- defer handleLock.Unlock()
- handle, ok := handleMap[addr]
- if !ok {
- // should be impossible; we would've errored above
- return errors.New("unknown base address")
- }
- if handle.writable && handle.file != windows.Handle(^uintptr(0)) {
- if err := windows.FlushFileBuffers(handle.file); err != nil {
- return os.NewSyscallError("FlushFileBuffers", err)
- }
- }
- return nil
- }
- func (m MMap) lock() error {
- addr, len := m.addrLen()
- errno := windows.VirtualLock(addr, len)
- return os.NewSyscallError("VirtualLock", errno)
- }
- func (m MMap) unlock() error {
- addr, len := m.addrLen()
- errno := windows.VirtualUnlock(addr, len)
- return os.NewSyscallError("VirtualUnlock", errno)
- }
- func (m MMap) unmap() error {
- err := m.flush()
- if err != nil {
- return err
- }
- addr := m.header().Data
- // Lock the UnmapViewOfFile along with the handleMap deletion.
- // As soon as we unmap the view, the OS is free to give the
- // same addr to another new map. We don't want another goroutine
- // to insert and remove the same addr into handleMap while
- // we're trying to remove our old addr/handle pair.
- handleLock.Lock()
- defer handleLock.Unlock()
- err = windows.UnmapViewOfFile(addr)
- if err != nil {
- return err
- }
- handle, ok := handleMap[addr]
- if !ok {
- // should be impossible; we would've errored above
- return errors.New("unknown base address")
- }
- delete(handleMap, addr)
- e := windows.CloseHandle(windows.Handle(handle.mapview))
- return os.NewSyscallError("CloseHandle", e)
- }
|