Skip to content

Commit 0320c04

Browse files
authored
Implement mmap and msync for NODERAWFS (#13052)
(this commit fixes also a TypeError in $syscallMunmap, which was hit by this test case)
1 parent d0d12c7 commit 0320c04

File tree

7 files changed

+92
-44
lines changed

7 files changed

+92
-44
lines changed

src/library_memfs.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,10 @@ mergeInto(LibraryManager.library, {
353353
stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length);
354354
},
355355
mmap: function(stream, address, length, position, prot, flags) {
356-
// We don't currently support location hints for the address of the mapping
357-
assert(address === 0);
358-
356+
if (address !== 0) {
357+
// We don't currently support location hints for the address of the mapping
358+
throw new FS.ErrnoError({{{ cDefine('EINVAL') }}});
359+
}
359360
if (!FS.isFile(stream.node.mode)) {
360361
throw new FS.ErrnoError({{{ cDefine('ENODEV') }}});
361362
}

src/library_nodefs.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,10 @@ mergeInto(LibraryManager.library, {
296296
return position;
297297
},
298298
mmap: function(stream, address, length, position, prot, flags) {
299-
// We don't currently support location hints for the address of the mapping
300-
assert(address === 0);
301-
299+
if (address !== 0) {
300+
// We don't currently support location hints for the address of the mapping
301+
throw new FS.ErrnoError({{{ cDefine('EINVAL') }}});
302+
}
302303
if (!FS.isFile(stream.node.mode)) {
303304
throw new FS.ErrnoError({{{ cDefine('ENODEV') }}});
304305
}

src/library_noderawfs.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
mergeInto(LibraryManager.library, {
8-
$NODERAWFS__deps: ['$ERRNO_CODES', '$FS', '$NODEFS'],
8+
$NODERAWFS__deps: ['$ERRNO_CODES', '$FS', '$NODEFS', '$mmapAlloc'],
99
$NODERAWFS__postset: 'if (ENVIRONMENT_IS_NODE) {' +
1010
'var _wrapNodeError = function(func) { return function() { try { return func.apply(this, arguments) } catch (e) { if (!e.code) throw e; throw new FS.ErrnoError(ERRNO_CODES[e.code]); } } };' +
1111
'var VFS = Object.assign({}, FS);' +
@@ -122,10 +122,31 @@ mergeInto(LibraryManager.library, {
122122
allocate: function() {
123123
throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP);
124124
},
125-
mmap: function() {
126-
throw new FS.ErrnoError(ERRNO_CODES.ENODEV);
125+
mmap: function(stream, address, length, position, prot, flags) {
126+
if (stream.stream_ops) {
127+
// this stream is created by in-memory filesystem
128+
return VFS.mmap(stream, address, length, position, prot, flags);
129+
}
130+
if (address !== 0) {
131+
// We don't currently support location hints for the address of the mapping
132+
throw new FS.ErrnoError({{{ cDefine('EINVAL') }}});
133+
}
134+
135+
var ptr = mmapAlloc(length);
136+
FS.read(stream, HEAP8, ptr, length, position);
137+
return { ptr: ptr, allocated: true };
127138
},
128-
msync: function() {
139+
msync: function(stream, buffer, offset, length, mmapFlags) {
140+
if (stream.stream_ops) {
141+
// this stream is created by in-memory filesystem
142+
return VFS.msync(stream, buffer, offset, length, mmapFlags);
143+
}
144+
if (mmapFlags & {{{ cDefine('MAP_PRIVATE') }}}) {
145+
// MAP_PRIVATE calls need not to be synced back to underlying fs
146+
return 0;
147+
}
148+
149+
FS.write(stream, buffer, 0, length, offset);
129150
return 0;
130151
},
131152
munmap: function() {

src/library_syscall.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,12 @@ var SyscallsLibrary = {
279279
if (len === info.len) {
280280
#if FILESYSTEM && SYSCALLS_REQUIRE_FILESYSTEM
281281
var stream = FS.getStream(info.fd);
282-
if (info.prot & {{{ cDefine('PROT_WRITE') }}}) {
283-
SYSCALLS.doMsync(addr, stream, len, info.flags, info.offset);
282+
if (stream) {
283+
if (info.prot & {{{ cDefine('PROT_WRITE') }}}) {
284+
SYSCALLS.doMsync(addr, stream, len, info.flags, info.offset);
285+
}
286+
FS.munmap(stream);
284287
}
285-
FS.munmap(stream);
286288
#else
287289
#if ASSERTIONS
288290
// Without FS support, only anonymous mappings are supported.

tests/fs/test_mmap.c

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,28 @@
1616
#include <sys/stat.h>
1717
#include <unistd.h>
1818
#include <sys/io.h>
19-
#include <sys/mman.h>
19+
#include <errno.h>
2020

2121
int main() {
2222
EM_ASM(
2323
FS.mkdir('yolo');
2424
#if NODEFS
2525
FS.mount(NODEFS, { root: '.' }, 'yolo');
2626
#endif
27-
FS.writeFile('/yolo/in.txt', 'mmap ftw!');
27+
FS.writeFile('yolo/in.txt', 'mmap ftw!');
2828
);
2929

3030
// Use mmap to read in.txt
3131
{
32-
const char* path = "/yolo/in.txt";
32+
const char* path = "yolo/in.txt";
3333
int fd = open(path, O_RDONLY);
3434
assert(fd != -1);
3535

3636
int filesize = 9;
3737
char* map = (char*)mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
3838
assert(map != MAP_FAILED);
3939

40-
printf("/yolo/in.txt content=");
40+
printf("yolo/in.txt content=");
4141
for (int i = 0; i < filesize; i++) {
4242
printf("%c", map[i]);
4343
}
@@ -52,7 +52,7 @@ int main() {
5252
// Use mmap to write out.txt
5353
{
5454
const char* text = "written mmap";
55-
const char* path = "/yolo/out.txt";
55+
const char* path = "yolo/out.txt";
5656

5757
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
5858
assert(fd != -1);
@@ -78,15 +78,15 @@ int main() {
7878
}
7979

8080
{
81-
FILE* fd = fopen("/yolo/out.txt", "r");
81+
FILE* fd = fopen("yolo/out.txt", "r");
8282
if (fd == NULL) {
83-
printf("failed to open /yolo/out.txt\n");
83+
printf("failed to open yolo/out.txt\n");
8484
return 1;
8585
}
8686
char buffer[15];
8787
memset(buffer, 0, 15);
8888
fread(buffer, 1, 14, fd);
89-
printf("/yolo/out.txt content=%s\n", buffer);
89+
printf("yolo/out.txt content=%s\n", buffer);
9090
fclose(fd);
9191
}
9292

@@ -95,7 +95,7 @@ int main() {
9595
{
9696
const char* readonlytext = "readonly mmap\0";
9797
const char* text = "write mmap\0";
98-
const char* path = "/yolo/outreadonly.txt";
98+
const char* path = "yolo/outreadonly.txt";
9999
size_t readonlytextsize = strlen(readonlytext);
100100
size_t textsize = strlen(text);
101101

@@ -117,22 +117,22 @@ int main() {
117117
}
118118

119119
{
120-
FILE* fd = fopen("/yolo/outreadonly.txt", "r");
120+
FILE* fd = fopen("yolo/outreadonly.txt", "r");
121121
if (fd == NULL) {
122-
printf("failed to open /yolo/outreadonly.txt\n");
122+
printf("failed to open yolo/outreadonly.txt\n");
123123
return 1;
124124
}
125125
char buffer[16];
126126
memset(buffer, 0, 16);
127127
fread(buffer, 1, 15, fd);
128-
printf("/yolo/outreadonly.txt content=%s\n", buffer);
128+
printf("yolo/outreadonly.txt content=%s\n", buffer);
129129
fclose(fd);
130130
}
131131

132132
// MAP_PRIVATE
133133
{
134134
const char* text = "written mmap";
135-
const char* path = "/yolo/private.txt";
135+
const char* path = "yolo/private.txt";
136136

137137
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
138138
assert(fd != -1);
@@ -158,22 +158,22 @@ int main() {
158158
}
159159

160160
{
161-
FILE* fd = fopen("/yolo/private.txt", "r");
161+
FILE* fd = fopen("yolo/private.txt", "r");
162162
if (fd == NULL) {
163-
printf("failed to open /yolo/private.txt\n");
163+
printf("failed to open yolo/private.txt\n");
164164
return 1;
165165
}
166166
char buffer[15];
167167
memset(buffer, 0, 15);
168168
fread(buffer, 1, 14, fd);
169-
printf("/yolo/private.txt content=%s\n", buffer);
169+
printf("yolo/private.txt content=%s\n", buffer);
170170
fclose(fd);
171171
}
172172

173173
// MAP_SHARED with offset
174174
{
175175
const char* text = "written shared mmap with offset";
176-
const char* path = "/yolo/sharedoffset.txt";
176+
const char* path = "yolo/sharedoffset.txt";
177177

178178
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
179179
assert(fd != -1);
@@ -206,10 +206,31 @@ int main() {
206206
close(fd);
207207
}
208208

209+
// mmap with a address will fail
210+
{
211+
const char* path = "yolo/private.txt";
212+
213+
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
214+
assert(fd != -1);
215+
216+
size_t map_size = 1 << 16;
217+
218+
// Reserve some address space in which to perform the experiment
219+
char *alloc = (char*)mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
220+
assert(alloc != MAP_FAILED);
221+
222+
char *addr = (char*)mmap((void*)alloc, map_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
223+
assert(addr == MAP_FAILED && errno == EINVAL); // Emscripten
224+
//assert(addr == alloc); // Native environments
225+
226+
assert(munmap(alloc, map_size) != -1);
227+
close(fd);
228+
}
229+
209230
{
210-
FILE* fd = fopen("/yolo/sharedoffset.txt", "r");
231+
FILE* fd = fopen("yolo/sharedoffset.txt", "r");
211232
if (fd == NULL) {
212-
printf("failed to open /yolo/sharedoffset.txt\n");
233+
printf("failed to open yolo/sharedoffset.txt\n");
213234
return 1;
214235
}
215236
size_t offset = sysconf(_SC_PAGE_SIZE) * 2;
@@ -218,11 +239,11 @@ int main() {
218239
memset(buffer, 0, offset + 33);
219240
fread(buffer, 1, offset + 32, fd);
220241
// expect text written from mmap operation to appear at offset in the file
221-
printf("/yolo/sharedoffset.txt content=%s %zu\n", buffer + offset, offset);
242+
printf("yolo/sharedoffset.txt content=%s %zu\n", buffer + offset, offset);
222243
fclose(fd);
223244
}
224245

225-
#if !defined(NODEFS)
246+
#if !defined(NODEFS) && !defined(NODERAWFS)
226247
/**
227248
* MMAP to an 'over-allocated' file
228249
*
@@ -233,7 +254,7 @@ int main() {
233254
* is not written beyond the allocated memory area for the mmap operation.
234255
*/
235256
{
236-
int fd = open("/yolo/overallocatedfile.txt", O_RDWR | O_CREAT, (mode_t)0600);
257+
int fd = open("yolo/overallocatedfile.txt", O_RDWR | O_CREAT, (mode_t)0600);
237258
assert(fd != -1);
238259

239260
const size_t textsize = 33;
@@ -244,13 +265,13 @@ int main() {
244265
}
245266

246267
EM_ASM_({
247-
const stream = FS.streams.find(stream => stream.path.indexOf('/yolo/overallocatedfile.txt')>=0);
268+
const stream = FS.streams.find(stream => stream.path.indexOf('yolo/overallocatedfile.txt') >= 0);
248269
assert(stream.node.usedBytes === $0,
249-
'Used bytes on the over-allocated file (' + stream.node.usedBytes+ ') ' +
270+
'Used bytes on the over-allocated file (' + stream.node.usedBytes + ') ' +
250271
'should be 33'
251272
);
252273
assert(stream.node.contents.length > stream.node.usedBytes,
253-
'Used bytes on the over-allocated file (' + stream.node.usedBytes+ ') ' +
274+
'Used bytes on the over-allocated file (' + stream.node.usedBytes + ') ' +
254275
'should be less than the length of the content buffer (' + stream.node.contents.length + ')'
255276
);
256277
stream.node.contents[stream.node.usedBytes] = 98; // 'b', we don't want to see this in the mmap area

tests/fs/test_mmap.out

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/yolo/in.txt content=mmap ftw!
2-
/yolo/out.txt content=written mmap
3-
/yolo/outreadonly.txt content=readonly mmap
4-
/yolo/private.txt content=
5-
/yolo/sharedoffset.txt content=written shared mmap with offset 32768
1+
yolo/in.txt content=mmap ftw!
2+
yolo/out.txt content=written mmap
3+
yolo/outreadonly.txt content=readonly mmap
4+
yolo/private.txt content=
5+
yolo/sharedoffset.txt content=written shared mmap with offset 32768

tests/test_core.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5132,10 +5132,12 @@ def test_fs_append(self):
51325132
def test_fs_mmap(self):
51335133
self.uses_es6 = True
51345134
orig_compiler_opts = self.emcc_args[:]
5135-
for fs in ['MEMFS', 'NODEFS']:
5135+
for fs in ['MEMFS', 'NODEFS', 'NODERAWFS']:
51365136
self.emcc_args = orig_compiler_opts + ['-D' + fs]
51375137
if fs == 'NODEFS':
51385138
self.emcc_args += ['-lnodefs.js']
5139+
if fs == 'NODERAWFS':
5140+
self.emcc_args += ['-lnodefs.js', '-lnoderawfs.js']
51395141
self.do_run_in_out_file_test('tests', 'fs', 'test_mmap.c')
51405142

51415143
@also_with_noderawfs

0 commit comments

Comments
 (0)