aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/edsrzf/mmap-go/mmap_windows.go
blob: c3d2d02d3f1b918fb1b1faf9efb44f14f96555b9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// 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"
    "syscall"
)

// 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.
var handleLock sync.Mutex
var handleMap = map[uintptr]syscall.Handle{}

func mmap(len int, prot, flags, hfile uintptr, off int64) ([]byte, error) {
    flProtect := uint32(syscall.PAGE_READONLY)
    dwDesiredAccess := uint32(syscall.FILE_MAP_READ)
    switch {
    case prot&COPY != 0:
        flProtect = syscall.PAGE_WRITECOPY
        dwDesiredAccess = syscall.FILE_MAP_COPY
    case prot&RDWR != 0:
        flProtect = syscall.PAGE_READWRITE
        dwDesiredAccess = syscall.FILE_MAP_WRITE
    }
    if prot&EXEC != 0 {
        flProtect <<= 4
        dwDesiredAccess |= syscall.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 := syscall.CreateFileMapping(syscall.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 := syscall.MapViewOfFile(h, dwDesiredAccess, fileOffsetHigh, fileOffsetLow, uintptr(len))
    if addr == 0 {
        return nil, os.NewSyscallError("MapViewOfFile", errno)
    }
    handleLock.Lock()
    handleMap[addr] = h
    handleLock.Unlock()

    m := MMap{}
    dh := m.header()
    dh.Data = addr
    dh.Len = len
    dh.Cap = dh.Len

    return m, nil
}

func flush(addr, len uintptr) error {
    errno := syscall.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")
    }

    errno = syscall.FlushFileBuffers(handle)
    return os.NewSyscallError("FlushFileBuffers", errno)
}

func lock(addr, len uintptr) error {
    errno := syscall.VirtualLock(addr, len)
    return os.NewSyscallError("VirtualLock", errno)
}

func unlock(addr, len uintptr) error {
    errno := syscall.VirtualUnlock(addr, len)
    return os.NewSyscallError("VirtualUnlock", errno)
}

func unmap(addr, len uintptr) error {
    flush(addr, len)
    // 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 := syscall.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 := syscall.CloseHandle(syscall.Handle(handle))
    return os.NewSyscallError("CloseHandle", e)
}