-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmmap.go
180 lines (150 loc) · 3.71 KB
/
mmap.go
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package cgommap
// #cgo CFLAGS: -g -Wall
// #include <string.h>
import "C"
import (
"errors"
"io"
"sync"
"unsafe"
)
const (
// The prot argument describes the desired memory protection of the
// mapping (and must not conflict with the open mode of the file).
PROT_EXEC_READ = iota + 1 // Allows views to be mapped for read-only, copy-on-write, or execute access.
PROT_EXEC_WRITE // Allows views to be mapped for read-only, copy-on-write, or execute access.
PROT_EXEC_READWRITE // Allows views to be mapped for read-only, copy-on-write, read/write, or execute access.
PROT_READ // Pages may be read.
PROT_WRITE // Pages may be written.
PROT_READWRITE // Pages may not be accessed.
)
// MMAP contains information about a memory mapped file
type MMAP struct {
addr uintptr
size int64
offset int64
readable bool
writeable bool
mtx sync.Mutex
}
// New opens a memory mapped file.
func New(length, offset int64, prot, flags int, fd uintptr) (m *MMAP, err error) {
if offset > length || offset < 0 {
return nil, errors.New("Invalid offset")
}
address, err := mmap(length, offset, prot, flags, fd)
if err != nil {
return nil, err
}
readable := false
writeable := false
switch prot {
case PROT_READ:
case PROT_EXEC_READ:
readable = true
break
case PROT_EXEC_WRITE:
case PROT_WRITE:
writeable = true
break
case PROT_EXEC_READWRITE:
case PROT_READWRITE:
writeable = true
readable = true
break
}
return &MMAP{
addr: address,
size: length,
readable: readable,
writeable: writeable,
}, nil
}
// Write buf to mmap
func (m *MMAP) Write(buf []byte) (writeLen int, err error) {
if !m.writeable {
return 0, errors.New("Invalid write: Insufficient permissions")
}
writeLen = len(buf)
// NB: Should we just return instead of partial Write?
if int64(writeLen) > m.size-m.offset {
writeLen = int(m.size - m.offset)
if writeLen == 0 {
return 0, io.EOF
}
err = errors.New("Partial Write")
}
C.memcpy(unsafe.Pointer(m.addr+uintptr(m.offset)), unsafe.Pointer(&buf[0]), C.size_t(writeLen))
m.mtx.Lock()
defer m.mtx.Unlock()
m.offset += int64(writeLen)
return writeLen, err
}
// Read from mmap into buf
func (m *MMAP) Read(buf []byte) (n int, err error) {
if !m.readable {
return 0, errors.New("Invalid read: Insufficient permissions")
}
toReadLen := m.size - m.offset
var sl = struct {
addr uintptr
len int
cap int
}{m.addr + uintptr(m.offset), 0, int(toReadLen)}
mmapBuf := *(*[]byte)(unsafe.Pointer(&sl))
copy(buf, mmapBuf[:toReadLen])
if toReadLen == 0 {
return 0, io.EOF
}
m.mtx.Lock()
defer m.mtx.Unlock()
m.offset += int64(toReadLen)
return int(toReadLen), err
}
// Seek mmap
func (m *MMAP) Seek(off int64, origin int) (newOffset int64, err error) {
switch origin {
case io.SeekCurrent:
newOffset = off + m.offset
break
case io.SeekStart:
newOffset = off
break
case io.SeekEnd:
newOffset = off + m.size - 1
break
default:
return m.offset, errors.New("Invalid origin")
}
if newOffset > m.size {
return m.offset, errors.New("Invalid Seek")
}
m.mtx.Lock()
defer m.mtx.Unlock()
m.offset = newOffset
return newOffset, nil
}
// Size of the mmap
func (m MMAP) Size() int64 {
return m.size
}
// Close will unmap the pages of memory
func (m *MMAP) Close() error {
if err := unmap(m.addr, m.size); err != nil {
return err
}
m = nil
return nil
}
// Lock the mapped memory
func (m MMAP) Lock() error {
return lock(m.addr, m.size)
}
// Unlock the mapped memory
func (m MMAP) Unlock() error {
return unlock(m.addr, m.size)
}
// Flush the mapped memory into the filesystem
func (m MMAP) Flush() error {
return flush(m.addr, m.size, MS_SYNC)
}