File System API — Emscripten 4.0.9-git (dev) documentation (original) (raw)

File operations in Emscripten are provided by the FS library. It is used internally for all of Emscripten’s libc and libcxx file I/O.

Note

The API is inspired by the Linux/POSIX File System API, with each presenting a very similar interface.

The underlying behaviour is also similar, except where differences between the native and browser environments make this unreasonable. For example, user and group permissions are defined but ignored in FS.open().

Emscripten predominantly compiles code that uses synchronous file I/O, so the majority of the FS member functions offer a synchronous interface (with errors being reported by raising exceptions of type FS.ErrnoError).

File data in Emscripten is partitioned by mounted file systems. Several file systems are provided. An instance of MEMFS is mounted to / by default. The subdirectories /home/web_user and /tmp are also created automatically, in addition to several other special devices and streams (e.g. /dev/null, /dev/random, /dev/stdin, /proc/self/fd); see FS.staticInit() in the FS library for full details. Instances of NODEFS and IDBFS can be mounted to other directories if your application needs to persist data.

The automatic tests in test/test_core.py (search for test_files) contain many examples of how to use this API. The tutorial also shows how to pre-load a file so that it can be read from compiled C/C++.

A high level overview of the way File Systems work in Emscripten-ported code is provided in the File System Overview.

New File System: WasmFS

Note

Current Status: Stable, but not yet feature-complete with the old FS.

WasmFS is a high-performance, fully-multithreaded, WebAssembly-based file system layer for Emscripten that will replace the existing JavaScript version.

The JavaScript-based file system was originally written before pthreads were supported and when it was more optimal to write code in JS. As a result it has overhead in pthreads builds because we must proxy to the main thread where all filesystem operations are done. WasmFS, instead, is compiled to Wasm and has full multithreading support. It also aims to be more modular and extensible.

Design Doc Link

GitHub Tracking Issue

Differences you may notice with the original JS filesystem include:

Including File System Support

Emscripten decides whether to include file system support automatically. Many programs don’t need files, and file system support is not negligible in size, so Emscripten avoids including it when it doesn’t see a reason to. That means that if your C/C++ code does not access files, then the FS object and other file system APIs will not be included in the output. And, on the other hand, if your C/C++ code does use files, then file system support will be automatically included. So normally things will “just work” and you don’t need to think about this at all.

However, if your C/C++ code doesn’t use files, but you want to use them from JavaScript, then you can build with -sFORCE_FILESYSTEM, which will make the compiler include file system support even though it doesn’t see it being used.

On the other hand, if you want to not include any filesystem support code (which may be included even due to printf or iostreams, due to how musl and libc++ are structured), you can build with -sFILESYSTEM=0. Very simple stdout support will be included if necessary in such a case, enough for printf and such to work, but no filesystem code will be added, which can save a significant amount of code size.

Persistent data

Applications compiled with Emscripten usually expect synchronous I/O, so Emscripten itself provides file systems with completely synchronous interfaces.

However, due to JavaScript’s event-driven nature, most persistent storage options offer only asynchronous interfaces. Emscripten offers multiple file systems that can be mounted with FS.mount() to help deal with persistence depending on the execution context.

File systems

Note

Only the MEMFS filesystem is included by default. All others must be enabled explicitly, using -lnodefs.js (NODEFS), -lidbfs.js (IDBFS), -lworkerfs.js (WORKERFS), or -lproxyfs.js (PROXYFS).

MEMFS

This is the default file system mounted at / when the runtime is initialized. All files exist strictly in-memory, and any data written to them is lost when the page is reloaded.

NODEFS

Note

This file system is only for use when running inside node.js.

This file system lets a program in node map directories (via a mount operation) on the host filesystem to directories in Emscripten’s virtual filesystem. It uses node’s synchronous FS API to immediately persist any data written to the Emscripten file system to your local disk.

See this test for an example.

NODERAWFS

Note

This file system is only for use when running inside node.js.

This is a special backend as it replaces all normal filesystem access with direct Node.js operations, without the need to do FS.mount(). The initial working directory will be same as process.cwd() instead of VFS root directory. Because this mode directly uses Node.js to access the real local filesystem on your OS, the code will not necessarily be portable between OSes - it will be as portable as a Node.js program would be, which means that differences in how the underlying OS handles permissions and errors and so forth may be noticeable. This has mostly been tested on Linux so far.

See this section on NODEFS, where you can see a mount operation - this is not needed in NODERAWFS.

IDBFS

Note

This file system is only for use when running code inside a browser.

The IDBFS file system implements the FS.syncfs() interface, which when called will persist any operations to an IndexedDB instance.

This is provided to overcome the limitation that browsers do not offer synchronous APIs for persistent storage, and so (by default) all writes exist only temporarily in-memory.

If the mount option autoPersist: true is passed when mounting IDBFS, then whenever any changes are made to the IDBFS directory tree, they will be automatically persisted to the IndexedDB backend. This lets users avoid needing to manually call FS.syncfs to persist changes to the IDBFS mounted directory tree.

WORKERFS

Note

This file system is only for use when running code inside a worker.

This file system provides read-only access to File and Blob objects inside a worker without copying the entire data into memory and can potentially be used for huge files.

PROXYFS

This allows a module to mount another module’s file system. This is useful when separate modules need to share a file system without manually syncing file contents. For example:

// Module 2 can use the path "/fs1" to access and modify Module 1's filesystem module2.FS.mkdir("/fs1"); module2.FS.mount(module2.PROXYFS, { root: "/", fs: module1.FS }, "/fs1");

Devices

Emscripten supports registering arbitrary device drivers composed of a device id and a set of device-specific stream callbacks. Once a driver has been registered with FS.registerDevice(), a device node can be created to reference it (using FS.mkdev()).

The device node acts as an interface between the device and the file system. Any stream referencing the new node will inherit the stream callbacks registered for the device, making all of the high-level FS operations transparently interact with the device.

Note

Every device is different and unique. While common file operations like open, close, read, and write are typically supported (and inherited by file streams to provide a layer of abstraction for the equivalent libc functions to call), each device should implement whatever callbacks it needs based on its unique characteristics.

FS.makedev(ma, mi)

Converts a major and minor number into a single unique integer. This is used as an id to represent the device.

Arguments:

FS.registerDevice(dev, ops)

Registers the specified device driver with a set of callbacks.

Arguments:

Setting up standard I/O devices

Emscripten standard I/O works by going though the virtual /dev/stdin, /dev/stdout and /dev/stderr devices. You can set them up using your own I/O functions by calling FS.init().

By default:

Note

All the configuration should be done before the main run() method is executed, typically by implementing Module.preRun. See Interacting with code for more information.

FS.init(input, output, error)

Sets up standard I/O devices for stdin, stdout, and stderr.

The devices are set up using the following (optional) callbacks. If any of the callbacks throw an exception, it will be caught and handled as if the device malfunctioned.

Arguments:

File system API

Note

Functions derived from libc like FS.readdir() use all-lowercase names, whereas added functions like FS.readFile() use camelCase names.

FS.mount(type, opts, mountpoint)

Mounts the FS object specified by type to the directory specified by mountpoint. The opts object is specific to each file system type.

Arguments:

FS.unmount(mountpoint)

Unmounts the specified mountpoint.

Arguments:

FS.syncfs(populate, callback)

Responsible for iterating and synchronizing all mounted file systems in an asynchronous fashion.

Note

Currently, only the IDBFS file system implements the interfaces needed for synchronization. All other file systems are completely synchronous and don’t require synchronization.

The populate flag is used to control the intended direction of the underlying synchronization between Emscripten`s internal data, and the file system’s persistent data.

For example:

function myAppStartup(callback) { FS.mkdir('/data'); FS.mount(IDBFS, {}, '/data');

FS.syncfs(true, function (err) { // handle callback }); }

function myAppShutdown(callback) { FS.syncfs(function (err) { // handle callback }); }

A real example of this functionality can be seen in test_idbfs_sync.c.

Arguments:

FS.mkdir(path, mode)

Creates a new directory node in the file system. For example:

Note

The underlying implementation does not support user or group permissions. The caller is always treated as the owner of the folder, and only permissions relevant to the owner apply.

Arguments:

FS.mkdev(path, mode, dev)

Creates a new device node in the file system referencing the registered device driver (FS.registerDevice()) for dev. For example:

var id = FS.makedev(64, 0); FS.registerDevice(id, {}); FS.mkdev('/dummy', id);

Arguments:

FS.symlink(oldpath, newpath)

Creates a symlink node at newpath linking to oldpath. For example:

FS.writeFile('file', 'foobar'); FS.symlink('file', 'link');

Arguments:

FS.rename(oldpath, newpath)

Renames the node at oldpath to newpath. For example:

FS.writeFile('file', 'foobar'); FS.rename('file', 'newfile');

Arguments:

FS.rmdir(path)

Removes an empty directory located at path.

Example

FS.mkdir('data'); FS.rmdir('data');

Arguments:

FS.unlink(path)

Unlinks the node at path.

This removes a name from the file system. If that name was the last link to a file (and no processes have the file open) the file is deleted.

For example:

FS.writeFile('/foobar.txt', 'Hello, world'); FS.unlink('/foobar.txt');

Arguments:

FS.readlink(path)

Gets the string value stored in the symbolic link at path. For example:

#include <stdio.h> #include <emscripten.h>

int main() { MAIN_THREAD_EM_ASM( FS.writeFile('file', 'foobar'); FS.symlink('file', 'link'); console.log(FS.readlink('link')); ); return 0; }

outputs:

Arguments:

Returns:

The string value stored in the symbolic link at path.

FS.stat(path)

Gets a JavaScript object containing statistics about the node at path. For example:

#include <stdio.h> #include <emscripten.h>

int main() { MAIN_THREAD_EM_ASM( FS.writeFile('file', 'foobar'); console.log(FS.stat('file')); ); return 0; }

outputs:

{ dev: 1, ino: 13, mode: 33206, nlink: 1, uid: 0, gid: 0, rdev: 0, size: 6, atime: Mon Nov 25 2013 00:37:27 GMT-0800 (PST), mtime: Mon Nov 25 2013 00:37:27 GMT-0800 (PST), ctime: Mon Nov 25 2013 00:37:27 GMT-0800 (PST), blksize: 4096, blocks: 1 }

Arguments:

FS.lstat(path)

Identical to FS.stat(), However, if path is a symbolic link then the returned stats will be for the link itself, not the file that it links to.

Arguments:

FS.chmod(path, mode)

Change the mode flags for path to mode.

Note

The underlying implementation does not support user or group permissions. The caller is always treated as the owner of the folder, and only permissions relevant to the owner apply.

For example:

FS.writeFile('forbidden', 'can't touch this'); FS.chmod('forbidden', 0000);

Arguments:

FS.lchmod(path, mode)

Identical to FS.chmod(). However, if path is a symbolic link then the mode will be set on the link itself, not the file that it links to.

Arguments:

FS.fchmod(fd, mode)

Identical to FS.chmod(). However, a raw file descriptor is supplied as fd.

Arguments:

FS.chown(path, uid, gid)

Change the ownership of the specified file to the given user or group id.

Note

This call exists to provide a more “complete” API mapping for ported code. Values set are effectively ignored.

Arguments:

FS.lchown(path, uid, gid)

Identical to FS.chown(). However, if path is a symbolic link then the properties will be set on the link itself, not the file that it links to.

Note

This call exists to provide a more “complete” API mapping for ported code. Values set are effectively ignored.

Arguments:

FS.fchown(fd, uid, gid)

Identical to FS.chown(). However, a raw file descriptor is supplied as fd.

Note

This call exists to provide a more “complete” API mapping for ported code. Values set are effectively ignored.

Arguments:

FS.truncate(path, len)

Truncates a file to the specified length. For example:

#include <stdio.h> #include <emscripten.h>

int main() { MAIN_THREAD_EM_ASM( FS.writeFile('file', 'foobar'); FS.truncate('file', 3); console.log(FS.readFile('file', { encoding: 'utf8' })); ); return 0; }

outputs:

Arguments:

FS.ftruncate(fd, len)

Truncates the file identified by the fd to the specified length (len).

Arguments:

FS.utime(path, atime, mtime)

Change the timestamps of the file located at path. The times passed to the arguments are in milliseconds since January 1, 1970 (midnight UTC/GMT).

Note that in the current implementation the stored timestamp is a single value, the maximum of atime and mtime.

Arguments:

FS.open(path, _flags_[, _mode_])

Opens a file with the specified flags. flags can be:

Note

The underlying implementation does not support user or group permissions. The file permissions set in mode are only used if the file is created. The caller is always treated as the owner of the file, and only those permissions apply.

Arguments:

Returns:

A stream object.

FS.close(stream)

Closes the file stream.

Arguments:

FS.llseek(stream, offset, whence)

Repositions the offset of the stream offset bytes relative to the beginning, current position, or end of the file, depending on the whence parameter.

The _llseek() function repositions the offset of the open file associated with the file descriptor fd to (offset_high<<32) | offset_low bytes relative to the beginning of the file, the current position in the file, or the end of the file, depending on whether whence is SEEK_SET, SEEK_CUR, or SEEK_END, respectively. It returns the resulting file position in the argument result.

Arguments:

FS.read(stream, buffer, offset, _length_[, _position_])

Read length bytes from the stream, storing them into buffer starting at offset.

By default, reading starts from the stream’s current offset, however, a specific offset can be specified with the position argument. For example:

var stream = FS.open('abinaryfile', 'r'); var buf = new Uint8Array(4); FS.read(stream, buf, 0, 4, 0); FS.close(stream);

Arguments:

FS.write(stream, buffer, offset, _length_[, _position_])

Writes length bytes from buffer, starting at offset.

By default, writing starts from the stream’s current offset, however, a specific offset can be specified with the position argument. For example:

var data = new Uint8Array(32); var stream = FS.open('dummy', 'w+'); FS.write(stream, data, 0, data.length, 0); FS.close(stream);

Arguments:

FS.readFile(path, opts)

Reads the entire file at path and returns it as a string (encoding is utf8), or as a new Uint8Array buffer (encoding is binary).

Arguments:

Returns:

The file as a string or Uint8Array buffer, depending on the encoding.

FS.writeFile(path, data, opts)

Writes the entire contents of data to the file at path. For example:

FS.writeFile('file', 'foobar'); var contents = FS.readFile('file', { encoding: 'utf8' });

Arguments:

FS.createLazyFile(parent, name, url, canRead, canWrite)

Creates a file that will be loaded lazily on first access from a given URL or local file system path, and returns a reference to it.

Warning

Firefox and Chrome have recently disabled synchronous binary XHRs, which means this cannot work for JavaScript in regular HTML pages (but it works within Web Workers).

Example

FS.createLazyFile('/', 'foo', 'other/page.htm', true, false); FS.createLazyFile('/', 'bar', '/get_file.php?name=baz', true, true);

Arguments:

Returns:

A reference to the new file.

FS.createPreloadedFile(parent, name, url, canRead, canWrite)

Preloads a file asynchronously, and uses preload plugins to prepare its content. You should call this in preRun, run() will be delayed until all preloaded files are ready. This is how the preload-file option works in emcc when --use-preload-plugins has been specified (if you use this method by itself, you will need to build the program with that option).

Arguments:

FS.trackingDelegate[callback name]

Users can specify callbacks to receive different filesystem events. This is useful for tracking changes in the filesystem. This requires -sFS_DEBUG.

Callback name:

The name of the callback that indicates the filesystem event

Example Code

EM_ASM( FS.trackingDelegate['willMovePath'] = function(oldpath, newpath) { out('About to move "' + oldpath + '" to "' + newpath + '"'); }; FS.trackingDelegate['onMovePath'] = function(oldpath, newpath) { out('Moved "' + oldpath + '" to "' + newpath + '"'); }; FS.trackingDelegate['willDeletePath'] = function(path) { out('About to delete "' + path + '"'); }; FS.trackingDelegate['onDeletePath'] = function(path) { out('Deleted "' + path + '"'); }; FS.trackingDelegate['onOpenFile'] = function(path, flags) { out('Opened "' + path + '" with flags ' + flags); }; FS.trackingDelegate['onReadFile'] = function(path, bytesRead) { out('Read ' + bytesRead + ' bytes from "' + path + '"'); }; FS.trackingDelegate['onWriteToFile'] = function(path, bytesWritten) { out('Wrote to file "' + path + '" with ' + bytesWritten + ' bytes written'); }; FS.trackingDelegate['onSeekFile'] = function(path, position, whence) { out('Seek on "' + path + '" with position ' + position + ' and whence ' + whence); }; FS.trackingDelegate['onCloseFile'] = function(path) { out('Closed ' + path); }; FS.trackingDelegate['onMakeDirectory'] = function(path, mode) { out('Created directory ' + path + ' with mode ' + mode); }; FS.trackingDelegate['onMakeSymlink'] = function(oldpath, newpath) { out('Created symlink from ' + oldpath + ' to ' + newpath); }; );

FILE *file; file = fopen("/test.txt", "w"); fputs("hello world", file); fclose(file); rename("/test.txt", "/renamed.txt"); file = fopen("/renamed.txt", "r"); char str[256] = {}; fgets(str, 255, file); printf("File read returned '%s'\n", str); fclose(file); remove("/renamed.txt"); mkdir("/home/test", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); symlink("/renamed.txt", "/file.txt");

Example Output

Opened "/test.txt" with flags O_CREAT O_TRUNC O_WRONLY and file size 0 Wrote to file "/test.txt" with 11 bytes written Wrote to file "/test.txt" with 0 bytes written Closed /test.txt About to move "/test.txt" to "/renamed.txt" Moved "/test.txt" to "/renamed.txt" Opened "/renamed.txt" with flags O_RDONLY and file size 11 Read 0 bytes from "/renamed.txt" Read 11 bytes from "/renamed.txt" Read 0 bytes from "/renamed.txt" Read 0 bytes from "/renamed.txt" Wrote to file "/dev/tty" with 31 bytes written File read returned 'hello world' Wrote to file "/dev/tty" with 2 bytes written Closed /renamed.txt About to delete "/renamed.txt" Deleted "/renamed.txt" Created directory "/home/test" with mode 16893 Created symlink from "/renamed.txt" to "/file.txt"

File types

Emscripten’s file system supports regular files, directories, symlinks, character devices, block devices and sockets. Similarly to most Unix systems, all of these file types can be operated on using the higher-level FS operations like FS.read() and FS.write().

FS.isFile(mode)

Tests if the mode bitmask represents a file.

Arguments:

Returns:

true if the mode bitmask represents a file.

Return type:

bool

FS.isDir(mode)

Tests if the mode bitmask represents a directory.

Returns:

true if the mode bitmask represents a directory.

Return type:

bool

FS.isLink(mode)

Tests if the mode bitmask represents a symlink.

Arguments:

Returns:

true if the mode bitmask represents a symlink.

Return type:

bool

FS.isChrdev(mode)

Tests if the mode bitmask represents a character device.

Arguments:

Returns:

true if the mode bitmask represents a character device.

Return type:

bool

FS.isBlkdev(mode)

Tests if the mode bitmask represents a block device.

Arguments:

Returns:

true if the mode bitmask represents a block device.

Return type:

bool

FS.isSocket(mode)

Tests if the mode bitmask represents a socket.

Arguments:

Returns:

true if the mode bitmask represents a socket.

Return type:

bool

Paths

FS.cwd()

Gets the current working directory.

Returns:

The current working directory.

FS.chdir(path)

Sets the current working directory.

Arguments:

FS.readdir(path)

Reads the contents of the path.

Arguments:

Returns:

an array of the names of the files in the directory including '.' and '..'.

FS.lookupPath(path, opts)

Looks up the incoming path and returns an object containing both the resolved path and node.

The options (opts) allow you to specify whether the object, its parent component, a symlink, or the item the symlink points to are returned. For example:

var lookup = FS.lookupPath(path, { parent: true });

Arguments:

Returns:

an object with the format:

{ path: resolved_path, node: resolved_node }

FS.analyzePath(path, dontResolveLastLink)

Looks up the incoming path and returns an object containing information about file stats and nodes. Built on top of FS.lookupPath and provides more information about given path and its parent. If any error occurs it won’t throw but returns an error property.

Arguments:

Returns:

an object with the format:

{ isRoot: boolean, exists: boolean, error: Error, name: string, path: resolved_path, object: resolved_node, parentExists: boolean, parentPath: resolved_parent_path, parentObject: resolved_parent_node }

FS.getPath(node)

Gets the absolute path to node, accounting for mounts.

Arguments:

Returns:

The absolute path to node.