1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <sys/mman.h>
8
9#include <errno.h>
10#include <fcntl.h>
11#include <stdio.h>
12#include <string.h>
13#include <pthread.h>
14
15#include <OS.h>
16
17#include <runtime_loader/runtime_loader.h>
18#include <errno_private.h>
19#include <syscall_utils.h>
20#include <syscalls.h>
21#include <vm_defs.h>
22
23
24static const char* kSharedMemoryDir = "/var/shared_memory/";
25
26
27static bool
28append_string(char*& path, size_t& bytesLeft, const char* toAppend, size_t size)
29{
30	if (bytesLeft <= size)
31		return false;
32
33	memcpy(path, toAppend, size);
34	path += size;
35	path[0] = '\0';
36	bytesLeft -= size;
37
38	return true;
39}
40
41
42static bool
43append_string(char*& path, size_t& bytesLeft, const char* toAppend)
44{
45	return append_string(path, bytesLeft, toAppend, strlen(toAppend));
46}
47
48
49static status_t
50shm_name_to_path(const char* name, char* path, size_t pathSize)
51{
52	if (name == NULL)
53		return B_BAD_VALUE;
54
55	// skip leading slashes
56	while (*name == '/')
57		name++;
58
59	if (*name == '\0')
60		return B_BAD_VALUE;
61
62	// create the path; replace occurrences of '/' by "%s" and '%' by "%%"
63	if (!append_string(path, pathSize, kSharedMemoryDir))
64		return ENAMETOOLONG;
65
66	while (const char* found = strpbrk(name, "%/")) {
67		// append section that doesn't need escaping
68		if (found != name) {
69			if (!append_string(path, pathSize, name, found - name))
70				return ENAMETOOLONG;
71		}
72
73		// append escaped char
74		const char* append = (*found == '%' ? "%%" : "%s");
75		if (!append_string(path, pathSize, append, 2))
76			return ENAMETOOLONG;
77		name = found + 1;
78	}
79
80	// append remaining string
81	if (!append_string(path, pathSize, name))
82		return ENAMETOOLONG;
83
84	return B_OK;
85}
86
87
88// #pragma mark -
89
90
91void*
92mmap(void* address, size_t length, int protection, int flags, int fd,
93	off_t offset)
94{
95	// offset and length must be page-aligned
96	if (length == 0 || offset % B_PAGE_SIZE != 0) {
97		__set_errno(B_BAD_VALUE);
98		return MAP_FAILED;
99	}
100
101	// check anonymous mapping
102	if ((flags & MAP_ANONYMOUS) != 0) {
103		fd = -1;
104	} else if (fd < 0) {
105		__set_errno(EBADF);
106		return MAP_FAILED;
107	}
108
109	// either MAP_SHARED or MAP_PRIVATE must be specified
110	if (((flags & MAP_SHARED) != 0) == ((flags & MAP_PRIVATE) != 0)) {
111		__set_errno(B_BAD_VALUE);
112		return MAP_FAILED;
113	}
114
115	// translate mapping, address specification, and protection
116	int mapping = (flags & MAP_SHARED) != 0
117		? REGION_NO_PRIVATE_MAP : REGION_PRIVATE_MAP;
118
119	uint32 addressSpec;
120	if ((flags & MAP_FIXED) != 0)
121		addressSpec = B_EXACT_ADDRESS;
122	else if (address != NULL)
123		addressSpec = B_BASE_ADDRESS;
124	else
125		addressSpec = B_RANDOMIZED_ANY_ADDRESS;
126
127	uint32 areaProtection = 0;
128	if ((protection & PROT_READ) != 0)
129		areaProtection |= B_READ_AREA;
130	if ((protection & PROT_WRITE) != 0)
131		areaProtection |= B_WRITE_AREA;
132	if ((protection & PROT_EXEC) != 0)
133		areaProtection |= B_EXECUTE_AREA;
134
135	if ((flags & MAP_NORESERVE) != 0)
136		areaProtection |= B_OVERCOMMITTING_AREA;
137
138	// create a name for this area based on calling image
139	void* addr = __builtin_return_address(0);
140	char* imageName;
141	char areaName[B_OS_NAME_LENGTH];
142	status_t status = __gRuntimeLoader->get_nearest_symbol_at_address(
143		addr, NULL, NULL, &imageName, NULL, NULL, NULL, NULL);
144	if (status == B_OK)
145		snprintf(areaName, sizeof(areaName), "%s mmap area", imageName);
146	else
147		strlcpy(areaName, "mmap area", sizeof(areaName));
148
149	// ask the kernel to map
150	area_id area = _kern_map_file(areaName, &address, addressSpec,
151		length, areaProtection, mapping, true, fd, offset);
152	if (area < 0) {
153		__set_errno(area);
154		return MAP_FAILED;
155	}
156
157	return address;
158}
159
160
161int
162munmap(void* address, size_t length)
163{
164	RETURN_AND_SET_ERRNO(_kern_unmap_memory(address, length));
165}
166
167
168int
169mprotect(void* address, size_t length, int protection)
170{
171	RETURN_AND_SET_ERRNO(_kern_set_memory_protection(address, length,
172		protection));
173}
174
175
176int
177msync(void* address, size_t length, int flags)
178{
179	RETURN_AND_SET_ERRNO_TEST_CANCEL(_kern_sync_memory(address, length, flags));
180}
181
182
183int
184madvise(void* address, size_t length, int advice)
185{
186	RETURN_AND_SET_ERRNO(_kern_memory_advice(address, length, advice));
187}
188
189
190int
191posix_madvise(void* address, size_t length, int advice)
192{
193	return madvise(address, length, advice);
194}
195
196
197int
198mlock(const void* address, size_t length)
199{
200	RETURN_AND_SET_ERRNO(_kern_mlock(address, length));
201}
202
203
204int
205munlock(const void* address, size_t length)
206{
207	RETURN_AND_SET_ERRNO(_kern_munlock(address, length));
208}
209
210
211int
212shm_open(const char* name, int openMode, mode_t permissions)
213{
214	char path[PATH_MAX];
215	status_t error = shm_name_to_path(name, path, sizeof(path));
216	if (error != B_OK)
217		RETURN_AND_SET_ERRNO(error);
218
219	return open(path, openMode | O_CLOEXEC, permissions);
220}
221
222
223int
224shm_unlink(const char* name)
225{
226	char path[PATH_MAX];
227	status_t error = shm_name_to_path(name, path, sizeof(path));
228	if (error != B_OK)
229		RETURN_AND_SET_ERRNO(error);
230
231	return unlink(path);
232}
233