| /* |
| * 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; |
| } |