blob: 36149313cd0d795bfea49d356e1bea28a42b9e7f [file] [log] [blame] [edit]
/*
* Copyright 2015 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/
#include <stdio.h>
#include <sys/mman.h>
#include <emscripten.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/io.h>
#include <errno.h>
int main() {
EM_ASM(
FS.mkdir('yolo');
#if NODEFS
FS.mount(NODEFS, { root: '.' }, 'yolo');
#endif
FS.writeFile('yolo/in.txt', 'mmap ftw!');
);
// Use mmap to read in.txt
{
const char* path = "yolo/in.txt";
int fd = open(path, O_RDONLY);
assert(fd != -1);
int filesize = 9;
char* map = (char*)mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
assert(map != MAP_FAILED);
printf("yolo/in.txt content=");
for (int i = 0; i < filesize; i++) {
printf("%c", map[i]);
}
printf("\n");
int rc = munmap(map, filesize);
assert(rc == 0);
close(fd);
}
// Use mmap to write out.txt
{
const char* text = "written mmap";
const char* path = "yolo/out.txt";
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
assert(fd != -1);
size_t textsize = strlen(text) + 1; // + \0 null character
assert(lseek(fd, textsize - 1, SEEK_SET) != -1);
// need to write something first to allow us to mmap
assert(write(fd, "", 1) != -1);
char *map = (char*)mmap(0, textsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
assert(map != MAP_FAILED);
for (size_t i = 0; i < textsize; i++) {
map[i] = text[i];
}
assert(msync(map, textsize, MS_SYNC) != -1);
assert(munmap(map, textsize) != -1);
close(fd);
}
{
FILE* fd = fopen("yolo/out.txt", "r");
if (fd == NULL) {
printf("failed to open yolo/out.txt\n");
return 1;
}
char buffer[15];
memset(buffer, 0, 15);
fread(buffer, 1, 14, fd);
printf("yolo/out.txt content=%s\n", buffer);
fclose(fd);
}
// Use mmap to read out.txt and modify the contents in memory,
// but make sure it's not overwritten on munmap
{
const char* readonlytext = "readonly mmap\0";
const char* text = "write mmap\0";
const char* path = "yolo/outreadonly.txt";
size_t readonlytextsize = strlen(readonlytext);
size_t textsize = strlen(text);
int fd = open(path, O_RDWR | O_CREAT, (mode_t)0600);
// write contents to the file ( we don't want this to be overwritten on munmap )
assert(write(fd, readonlytext, readonlytextsize) != -1);
close(fd);
fd = open(path, O_RDWR);
char *map = (char*)mmap(0, textsize, PROT_READ, MAP_SHARED, fd, 0);
assert(map != MAP_FAILED);
for (size_t i = 0; i < textsize; i++) {
map[i] = text[i];
}
assert(munmap(map, textsize) != -1);
close(fd);
}
{
FILE* fd = fopen("yolo/outreadonly.txt", "r");
if (fd == NULL) {
printf("failed to open yolo/outreadonly.txt\n");
return 1;
}
char buffer[16];
memset(buffer, 0, 16);
fread(buffer, 1, 15, fd);
printf("yolo/outreadonly.txt content=%s\n", buffer);
fclose(fd);
}
// MAP_PRIVATE
{
const char* text = "written mmap";
const char* path = "yolo/private.txt";
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
assert(fd != -1);
size_t textsize = strlen(text) + 1; // + \0 null character
assert(lseek(fd, textsize - 1, SEEK_SET) != -1);
// need to write something first to allow us to mmap
assert(write(fd, "", 1) != -1);
char *map = (char*)mmap(0, textsize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
assert(map != MAP_FAILED);
for (size_t i = 0; i < textsize; i++) {
map[i] = text[i];
}
assert(msync(map, textsize, MS_SYNC) != -1);
assert(munmap(map, textsize) != -1);
close(fd);
}
{
FILE* fd = fopen("yolo/private.txt", "r");
if (fd == NULL) {
printf("failed to open yolo/private.txt\n");
return 1;
}
char buffer[15];
memset(buffer, 0, 15);
fread(buffer, 1, 14, fd);
printf("yolo/private.txt content=%s\n", buffer);
fclose(fd);
}
// MAP_SHARED with offset
{
const char* text = "written shared mmap with offset";
const char* path = "yolo/sharedoffset.txt";
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
assert(fd != -1);
size_t textsize = strlen(text) + 1; // + \0 null character
// offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).
size_t offset = sysconf(_SC_PAGE_SIZE) * 2;
assert(lseek(fd, textsize + offset - 1, SEEK_SET) != -1);
// need to write something first to allow us to mmap
assert(write(fd, "", 1) != -1);
char *map;
map = (char*)mmap(0, textsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset - 1);
// assert failure if offset is not a multiple of page size
assert(map == MAP_FAILED);
map = (char*)mmap(0, textsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
assert(map != MAP_FAILED);
for (size_t i = 0; i < textsize; i++) {
map[i] = text[i];
}
assert(msync(map, textsize, MS_SYNC) != -1);
assert(munmap(map, textsize) != -1);
close(fd);
}
// mmap with a address will fail
{
const char* path = "yolo/private.txt";
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600);
assert(fd != -1);
size_t map_size = 1 << 16;
// Reserve some address space in which to perform the experiment
char *alloc = (char*)mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert(alloc != MAP_FAILED);
char *addr = (char*)mmap((void*)alloc, map_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
assert(addr == MAP_FAILED && errno == EINVAL); // Emscripten
//assert(addr == alloc); // Native environments
assert(munmap(alloc, map_size) != -1);
close(fd);
}
{
FILE* fd = fopen("yolo/sharedoffset.txt", "r");
if (fd == NULL) {
printf("failed to open yolo/sharedoffset.txt\n");
return 1;
}
size_t offset = sysconf(_SC_PAGE_SIZE) * 2;
char buffer[offset + 33];
memset(buffer, 0, offset + 33);
fread(buffer, 1, offset + 32, fd);
// expect text written from mmap operation to appear at offset in the file
printf("yolo/sharedoffset.txt content=%s %zu\n", buffer + offset, offset);
fclose(fd);
}
#if !defined(NODEFS) && !defined(NODERAWFS)
/**
* MMAP to an 'over-allocated' file
*
* When appending to a file, the buffer size is increased in chunks, and so the actual length
* of the file could be less than the buffer size.
*
* When using mmap for an over-allocated file, we have to make sure that content from the buffer
* is not written beyond the allocated memory area for the mmap operation.
*/
{
int fd = open("yolo/overallocatedfile.txt", O_RDWR | O_CREAT, (mode_t)0600);
assert(fd != -1);
const size_t textsize = 33;
// multiple calls to write so that the file will be over-allocated
for (int n = 0; n < textsize; n++) {
assert(write(fd, "a", 1) != -1);
}
EM_ASM_({
const stream = FS.streams.find(stream => stream.path.indexOf('yolo/overallocatedfile.txt') >= 0);
assert(stream.node.usedBytes === $0,
'Used bytes on the over-allocated file (' + stream.node.usedBytes + ') ' +
'should be 33'
);
assert(stream.node.contents.length > stream.node.usedBytes,
'Used bytes on the over-allocated file (' + stream.node.usedBytes + ') ' +
'should be less than the length of the content buffer (' + stream.node.contents.length + ')'
);
stream.node.contents[stream.node.usedBytes] = 98; // 'b', we don't want to see this in the mmap area
}, textsize);
char *map = (char*)mmap(NULL, textsize, PROT_READ, 0, fd, 0);
assert(map[textsize-1] == 'a');
// Assert that content from the over-allocated file buffer is not written beyond the allocated memory for the map
#if !__has_feature(address_sanitizer) // the following is invalid, and asan complains rightfully
assert(map[textsize] != 'b');
#endif
close(fd);
}
#endif
return 0;
}