diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-09-29 11:51:22 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-09-29 11:51:22 -0700 |
commit | 61221f3dd86360875f1a6ece4aebaa0a20bc025c (patch) | |
tree | 7ea64acb419554505a891a2b0b06537077216268 | |
parent | 51256ab4452f9b1aa3774ef2eece26faa652ab22 (diff) | |
parent | 4db117910eb13fc93d632dd4e3fb4cf127544538 (diff) |
Merge pull request #1601 from inolen/idbfs
NODEFS and IDBFS support
-rw-r--r-- | src/library.js | 25 | ||||
-rw-r--r-- | src/library_fs.js | 69 | ||||
-rw-r--r-- | src/library_idbfs.js | 216 | ||||
-rw-r--r-- | src/library_memfs.js | 23 | ||||
-rw-r--r-- | src/library_nodefs.js | 234 | ||||
-rw-r--r-- | src/library_sockfs.js | 12 | ||||
-rw-r--r-- | src/modules.js | 2 | ||||
-rw-r--r-- | tests/fs/test_idbfs_sync.c | 48 | ||||
-rw-r--r-- | tests/fs/test_nodefs_rw.c | 49 | ||||
-rw-r--r-- | tests/test_browser.py | 5 | ||||
-rw-r--r-- | tests/test_core.py | 50 | ||||
-rw-r--r-- | tests/unistd/access.c | 17 | ||||
-rw-r--r-- | tests/unistd/access.out | 40 | ||||
-rw-r--r-- | tests/unistd/io.c | 13 | ||||
-rw-r--r-- | tests/unistd/links.c | 21 | ||||
-rw-r--r-- | tests/unistd/links.out | 6 | ||||
-rw-r--r-- | tests/unistd/misc.c | 24 | ||||
-rw-r--r-- | tests/unistd/truncate.c | 23 | ||||
-rw-r--r-- | tests/unistd/unlink.c | 12 |
19 files changed, 765 insertions, 124 deletions
diff --git a/src/library.js b/src/library.js index 326369c5..6209df28 100644 --- a/src/library.js +++ b/src/library.js @@ -688,24 +688,13 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/chdir.html // NOTE: The path argument may be a string, to simplify fchdir(). if (typeof path !== 'string') path = Pointer_stringify(path); - var lookup; try { - lookup = FS.lookupPath(path, { follow: true }); + FS.chdir(path); + return 0; } catch (e) { FS.handleFSError(e); return -1; } - if (!FS.isDir(lookup.node.mode)) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - return -1; - } - var err = FS.nodePermissions(lookup.node, 'x'); - if (err) { - ___setErrNo(err); - return -1; - } - FS.currentPath = lookup.path; - return 0; }, chown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], chown: function(path, owner, group, dontResolveLastLink) { @@ -909,12 +898,14 @@ LibraryManager.library = { if (size == 0) { ___setErrNo(ERRNO_CODES.EINVAL); return 0; - } else if (size < FS.currentPath.length + 1) { + } + var cwd = FS.cwd(); + if (size < cwd.length + 1) { ___setErrNo(ERRNO_CODES.ERANGE); return 0; } else { - for (var i = 0; i < FS.currentPath.length; i++) { - {{{ makeSetValue('buf', 'i', 'FS.currentPath.charCodeAt(i)', 'i8') }}} + for (var i = 0; i < cwd.length; i++) { + {{{ makeSetValue('buf', 'i', 'cwd.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('buf', 'i', '0', 'i8') }}} return buf; @@ -4249,7 +4240,7 @@ LibraryManager.library = { llvm_va_end: function() {}, llvm_va_copy: function(ppdest, ppsrc) { - // copy the list start + // copy the list start {{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; // copy the list's current offset (will be advanced with each call to va_arg) diff --git a/src/library_fs.js b/src/library_fs.js index 84a5245b..c4b29227 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1,5 +1,5 @@ mergeInto(LibraryManager.library, { - $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', 'stdin', 'stdout', 'stderr', 'fflush'], + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr', 'fflush'], $FS__postset: 'FS.staticInit();' + '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + @@ -14,6 +14,7 @@ mergeInto(LibraryManager.library, { 'Module["FS_createDevice"] = FS.createDevice;', $FS: { root: null, + mounts: [], devices: [null], streams: [null], nextInode: 1, @@ -50,11 +51,8 @@ mergeInto(LibraryManager.library, { // // paths // - cwd: function() { - return FS.currentPath; - }, lookupPath: function(path, opts) { - path = PATH.resolve(FS.currentPath, path); + path = PATH.resolve(FS.cwd(), path); opts = opts || { recurse_count: 0 }; if (opts.recurse_count > 8) { // max recursive lookup of 8 @@ -309,7 +307,7 @@ mergeInto(LibraryManager.library, { if (!FS.isDir(node.mode)) { return ERRNO_CODES.ENOTDIR; } - if (FS.isRoot(node) || FS.getPath(node) === FS.currentPath) { + if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return ERRNO_CODES.EBUSY; } } else { @@ -422,17 +420,45 @@ mergeInto(LibraryManager.library, { // // core // + syncfs: function(populate, callback) { + if (typeof(populate) === 'function') { + callback = populate; + populate = false; + } + + var completed = 0; + var total = FS.mounts.length; + var done = function(err) { + if (err) { + return callback(err); + } + if (++completed >= total) { + callback(null); + } + }; + + // sync all mounts + for (var i = 0; i < FS.mounts.length; i++) { + var mount = FS.mounts[i]; + if (!mount.type.syncfs) { + done(null); + continue; + } + mount.type.syncfs(mount, populate, done); + } + }, mount: function(type, opts, mountpoint) { + var lookup; + if (mountpoint) { + lookup = FS.lookupPath(mountpoint, { follow: false }); + mountpoint = lookup.path; // use the absolute path + } var mount = { type: type, opts: opts, mountpoint: mountpoint, root: null }; - var lookup; - if (mountpoint) { - lookup = FS.lookupPath(mountpoint, { follow: false }); - } // create a root node for the fs var root = type.mount(mount); root.mount = mount; @@ -446,6 +472,8 @@ mergeInto(LibraryManager.library, { FS.root = mount.root; } } + // add to our cached list of mounts + FS.mounts.push(mount); return root; }, lookup: function(parent, name) { @@ -759,7 +787,6 @@ mergeInto(LibraryManager.library, { follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}}) }); node = lookup.node; - path = lookup.path; } catch (e) { // ignore } @@ -791,10 +818,13 @@ mergeInto(LibraryManager.library, { if ((flags & {{{ cDefine('O_TRUNC')}}})) { FS.truncate(node, 0); } + // we've already handled these, don't pass down to the underlying vfs + flags &= ~({{{ cDefine('O_EXCL') }}} | {{{ cDefine('O_TRUNC') }}}); + // register the stream with the filesystem var stream = FS.createStream({ - path: path, node: node, + path: FS.getPath(node), // we want the absolute path to the node flags: flags, seekable: true, position: 0, @@ -959,8 +989,21 @@ mergeInto(LibraryManager.library, { // // module-level FS code - // TODO move to pre/postamble // + cwd: function() { + return FS.currentPath; + }, + chdir: function(path) { + var lookup = FS.lookupPath(path, { follow: true }); + if (!FS.isDir(lookup.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + var err = FS.nodePermissions(lookup.node, 'x'); + if (err) { + throw new FS.ErrnoError(err); + } + FS.currentPath = lookup.path; + }, createDefaultDirectories: function() { FS.mkdir('/tmp'); }, diff --git a/src/library_idbfs.js b/src/library_idbfs.js new file mode 100644 index 00000000..9031bad8 --- /dev/null +++ b/src/library_idbfs.js @@ -0,0 +1,216 @@ +mergeInto(LibraryManager.library, { + $IDBFS__deps: ['$FS', '$MEMFS', '$PATH'], + $IDBFS: { + dbs: {}, + indexedDB: function() { + return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; + }, + DB_VERSION: 20, + DB_STORE_NAME: 'FILE_DATA', + // reuse all of the core MEMFS functionality + mount: function(mount) { + return MEMFS.mount.apply(null, arguments); + }, + // the only custom function IDBFS implements is to handle + // synchronizing the wrapped MEMFS with a backing IDB instance + syncfs: function(mount, populate, callback) { + IDBFS.getLocalSet(mount, function(err, local) { + if (err) return callback(err); + + IDBFS.getRemoteSet(mount, function(err, remote) { + if (err) return callback(err); + + var src = populate ? remote : local; + var dst = populate ? local : remote; + + IDBFS.reconcile(src, dst, callback); + }); + }); + }, + reconcile: function(src, dst, callback) { + var total = 0; + + var create = {}; + for (var key in src.files) { + if (!src.files.hasOwnProperty(key)) continue; + var e = src.files[key]; + var e2 = dst.files[key]; + if (!e2 || e.timestamp > e2.timestamp) { + create[key] = e; + total++; + } + } + + var remove = {}; + for (var key in dst.files) { + if (!dst.files.hasOwnProperty(key)) continue; + var e = dst.files[key]; + var e2 = src.files[key]; + if (!e2) { + remove[key] = e; + total++; + } + } + + if (!total) { + // early out + return callback(null); + } + + var completed = 0; + var done = function(err) { + if (err) return callback(err); + if (++completed >= total) { + return callback(null); + } + }; + + // create a single transaction to handle and IDB reads / writes we'll need to do + var db = src.type === 'remote' ? src.db : dst.db; + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite'); + transaction.onerror = function() { callback(this.error); }; + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + + for (var path in create) { + if (!create.hasOwnProperty(path)) continue; + var entry = create[path]; + + if (dst.type === 'local') { + // save file to local + try { + if (FS.isDir(entry.mode)) { + FS.mkdir(path, entry.mode); + } else if (FS.isFile(entry.mode)) { + var stream = FS.open(path, 'w+', 0666); + FS.write(stream, entry.contents, 0, entry.contents.length, 0, true /* canOwn */); + FS.close(stream); + } + done(null); + } catch (e) { + return done(e); + } + } else { + // save file to IDB + var req = store.put(entry, path); + req.onsuccess = function() { done(null); }; + req.onerror = function() { done(this.error); }; + } + } + + for (var path in remove) { + if (!remove.hasOwnProperty(path)) continue; + var entry = remove[path]; + + if (dst.type === 'local') { + // delete file from local + try { + if (FS.isDir(entry.mode)) { + // TODO recursive delete? + FS.rmdir(path); + } else if (FS.isFile(entry.mode)) { + FS.unlink(path); + } + done(null); + } catch (e) { + return done(e); + } + } else { + // delete file from IDB + var req = store.delete(path); + req.onsuccess = function() { done(null); }; + req.onerror = function() { done(this.error); }; + } + } + }, + getLocalSet: function(mount, callback) { + var files = {}; + + var isRealDir = function(p) { + return p !== '.' && p !== '..'; + }; + var toAbsolute = function(root) { + return function(p) { + return PATH.join(root, p); + } + }; + + var check = FS.readdir(mount.mountpoint) + .filter(isRealDir) + .map(toAbsolute(mount.mountpoint)); + + while (check.length) { + var path = check.pop(); + var stat, node; + + try { + var lookup = FS.lookupPath(path); + node = lookup.node; + stat = FS.stat(path); + } catch (e) { + return callback(e); + } + + if (FS.isDir(stat.mode)) { + check.push.apply(check, FS.readdir(path) + .filter(isRealDir) + .map(toAbsolute(path))); + + files[path] = { mode: stat.mode, timestamp: stat.mtime }; + } else if (FS.isFile(stat.mode)) { + files[path] = { contents: node.contents, mode: stat.mode, timestamp: stat.mtime }; + } else { + return callback(new Error('node type not supported')); + } + } + + return callback(null, { type: 'local', files: files }); + }, + getDB: function(name, callback) { + // look it up in the cache + var db = IDBFS.dbs[name]; + if (db) { + return callback(null, db); + } + var req; + try { + req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION); + } catch (e) { + return onerror(e); + } + req.onupgradeneeded = function() { + db = req.result; + db.createObjectStore(IDBFS.DB_STORE_NAME); + }; + req.onsuccess = function() { + db = req.result; + // add to the cache + IDBFS.dbs[name] = db; + callback(null, db); + }; + req.onerror = function() { + callback(this.error); + }; + }, + getRemoteSet: function(mount, callback) { + var files = {}; + + IDBFS.getDB(mount.mountpoint, function(err, db) { + if (err) return callback(err); + + var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly'); + transaction.onerror = function() { callback(this.error); }; + + var store = transaction.objectStore(IDBFS.DB_STORE_NAME); + store.openCursor().onsuccess = function(event) { + var cursor = event.target.result; + if (!cursor) { + return callback(null, { type: 'remote', db: db, files: files }); + } + + files[cursor.key] = cursor.value; + cursor.continue(); + }; + }); + } + } +}); diff --git a/src/library_memfs.js b/src/library_memfs.js index 28178d9f..4e56d996 100644 --- a/src/library_memfs.js +++ b/src/library_memfs.js @@ -5,18 +5,10 @@ mergeInto(LibraryManager.library, { CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary) CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink CONTENT_FIXED: 3, // contains some fixed-size content written into it, in a typed array - ensureFlexible: function(node) { - if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) { - var contents = node.contents; - node.contents = Array.prototype.slice.call(contents); - node.contentMode = MEMFS.CONTENT_FLEXIBLE; - } - }, - mount: function(mount) { - return MEMFS.create_node(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + return MEMFS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); }, - create_node: function(parent, name, mode, dev) { + createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { // no supported throw new FS.ErrnoError(ERRNO_CODES.EPERM); @@ -74,6 +66,13 @@ mergeInto(LibraryManager.library, { } return node; }, + ensureFlexible: function(node) { + if (node.contentMode !== MEMFS.CONTENT_FLEXIBLE) { + var contents = node.contents; + node.contents = Array.prototype.slice.call(contents); + node.contentMode = MEMFS.CONTENT_FLEXIBLE; + } + }, node_ops: { getattr: function(node) { var attr = {}; @@ -121,7 +120,7 @@ mergeInto(LibraryManager.library, { throw new FS.ErrnoError(ERRNO_CODES.ENOENT); }, mknod: function(parent, name, mode, dev) { - return MEMFS.create_node(parent, name, mode, dev); + return MEMFS.createNode(parent, name, mode, dev); }, rename: function(old_node, new_dir, new_name) { // if we're overwriting a directory at new_name, make sure it's empty. @@ -163,7 +162,7 @@ mergeInto(LibraryManager.library, { return entries; }, symlink: function(parent, newname, oldpath) { - var node = MEMFS.create_node(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); + var node = MEMFS.createNode(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); node.link = oldpath; return node; }, diff --git a/src/library_nodefs.js b/src/library_nodefs.js new file mode 100644 index 00000000..d8df1689 --- /dev/null +++ b/src/library_nodefs.js @@ -0,0 +1,234 @@ +mergeInto(LibraryManager.library, { + $NODEFS__deps: ['$FS', '$PATH'], + $NODEFS__postset: 'if (ENVIRONMENT_IS_NODE) { var fs = require("fs"); }', + $NODEFS: { + mount: function (mount) { + assert(ENVIRONMENT_IS_NODE); + return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0); + }, + createNode: function (parent, name, mode, dev) { + if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var node = FS.createNode(parent, name, mode); + node.node_ops = NODEFS.node_ops; + node.stream_ops = NODEFS.stream_ops; + return node; + }, + getMode: function (path) { + var stat; + try { + stat = fs.lstatSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return stat.mode; + }, + realPath: function (node) { + var parts = []; + while (node.parent !== node) { + parts.push(node.name); + node = node.parent; + } + parts.push(node.mount.opts.root); + parts.reverse(); + return PATH.join.apply(null, parts); + }, + node_ops: { + getattr: function(node) { + var path = NODEFS.realPath(node); + var stat; + try { + stat = fs.lstatSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return { + dev: stat.dev, + ino: stat.ino, + mode: stat.mode, + nlink: stat.nlink, + uid: stat.uid, + gid: stat.gid, + rdev: stat.rdev, + size: stat.size, + atime: stat.atime, + mtime: stat.mtime, + ctime: stat.ctime, + blksize: stat.blksize, + blocks: stat.blocks + }; + }, + setattr: function(node, attr) { + var path = NODEFS.realPath(node); + try { + if (attr.mode !== undefined) { + fs.chmodSync(path, attr.mode); + // update the common node structure mode as well + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + var date = new Date(attr.timestamp); + fs.utimesSync(path, date, date); + } + if (attr.size !== undefined) { + fs.truncateSync(path, attr.size); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + lookup: function (parent, name) { + var path = PATH.join(NODEFS.realPath(parent), name); + var mode = NODEFS.getMode(path); + return NODEFS.createNode(parent, name, mode); + }, + mknod: function (parent, name, mode, dev) { + var node = NODEFS.createNode(parent, name, mode, dev); + // create the backing node for this in the fs root as well + var path = NODEFS.realPath(node); + try { + if (FS.isDir(node.mode)) { + fs.mkdirSync(path, node.mode); + } else { + fs.writeFileSync(path, '', { mode: node.mode }); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return node; + }, + rename: function (oldNode, newDir, newName) { + var oldPath = NODEFS.realPath(oldNode); + var newPath = PATH.join(NODEFS.realPath(newDir), newName); + try { + fs.renameSync(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + unlink: function(parent, name) { + var path = PATH.join(NODEFS.realPath(parent), name); + try { + fs.unlinkSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + rmdir: function(parent, name) { + var path = PATH.join(NODEFS.realPath(parent), name); + try { + fs.rmdirSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + readdir: function(node) { + var path = NODEFS.realPath(node); + try { + return fs.readdirSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + symlink: function(parent, newName, oldPath) { + var newPath = PATH.join(NODEFS.realPath(parent), newName); + try { + fs.symlinkSync(oldPath, newPath); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + readlink: function(node) { + var path = NODEFS.realPath(node); + try { + return fs.readlinkSync(path); + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + }, + stream_ops: { + open: function (stream) { + var path = NODEFS.realPath(stream.node); + try { + if (FS.isFile(stream.node.mode)) { + stream.nfd = fs.openSync(path, stream.flags); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + close: function (stream) { + try { + if (FS.isFile(stream.node.mode)) { + fs.closeSync(stream.nfd); + } + } catch (e) { + if (!e.code) throw e; + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + }, + read: function (stream, buffer, offset, length, position) { + // FIXME this is terrible. + var nbuffer = new Buffer(length); + var res; + try { + res = fs.readSync(stream.nfd, nbuffer, 0, length, position); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + if (res > 0) { + for (var i = 0; i < res; i++) { + buffer[offset + i] = nbuffer[i]; + } + } + return res; + }, + write: function (stream, buffer, offset, length, position) { + // FIXME this is terrible. + var nbuffer = new Buffer(buffer.subarray(offset, offset + length)); + var res; + try { + res = fs.writeSync(stream.nfd, nbuffer, 0, length, position); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + return res; + }, + llseek: function (stream, offset, whence) { + var position = offset; + if (whence === 1) { // SEEK_CUR. + position += stream.position; + } else if (whence === 2) { // SEEK_END. + if (FS.isFile(stream.node.mode)) { + try { + var stat = fs.fstatSync(stream.nfd); + position += stat.size; + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES[e.code]); + } + } + } + + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + + stream.position = position; + return position; + } + } + } +});
\ No newline at end of file diff --git a/src/library_sockfs.js b/src/library_sockfs.js index b11c6495..af29d11b 100644 --- a/src/library_sockfs.js +++ b/src/library_sockfs.js @@ -5,12 +5,6 @@ mergeInto(LibraryManager.library, { mount: function(mount) { return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); }, - nextname: function() { - if (!SOCKFS.nextname.current) { - SOCKFS.nextname.current = 0; - } - return 'socket[' + (SOCKFS.nextname.current++) + ']'; - }, createSocket: function(family, type, protocol) { var streaming = type == {{{ cDefine('SOCK_STREAM') }}}; if (protocol) { @@ -95,6 +89,12 @@ mergeInto(LibraryManager.library, { sock.sock_ops.close(sock); } }, + nextname: function() { + if (!SOCKFS.nextname.current) { + SOCKFS.nextname.current = 0; + } + return 'socket[' + (SOCKFS.nextname.current++) + ']'; + }, // backend-specific stream ops websocket_sock_ops: { // diff --git a/src/modules.js b/src/modules.js index 1029b233..2757c2cb 100644 --- a/src/modules.js +++ b/src/modules.js @@ -422,7 +422,7 @@ var LibraryManager = { load: function() { if (this.library) return; - var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_memfs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries); + var libraries = ['library.js', 'library_path.js', 'library_fs.js', 'library_idbfs.js', 'library_memfs.js', 'library_nodefs.js', 'library_sockfs.js', 'library_tty.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries); for (var i = 0; i < libraries.length; i++) { eval(processMacros(preprocess(read(libraries[i])))); } diff --git a/tests/fs/test_idbfs_sync.c b/tests/fs/test_idbfs_sync.c new file mode 100644 index 00000000..ff356416 --- /dev/null +++ b/tests/fs/test_idbfs_sync.c @@ -0,0 +1,48 @@ +#include <stdio.h> +#include <emscripten.h> + +#define EM_ASM_REEXPAND(x) EM_ASM(x) + +void success() { + int result = 1; + REPORT_RESULT(); +} + +int main() { + EM_ASM( + FS.mkdir('/working'); + FS.mount(IDBFS, {}, '/working'); + ); + +#if FIRST + // store local files to backing IDB + EM_ASM_REEXPAND( + FS.writeFile('/working/waka.txt', 'az'); + FS.writeFile('/working/moar.txt', SECRET); + FS.syncfs(function (err) { + assert(!err); + + ccall('success', 'v', '', []); + }); + ); +#else + // load files from backing IDB + EM_ASM_REEXPAND( + FS.syncfs(true, function (err) { + assert(!err); + + var contents = FS.readFile('/working/waka.txt', { encoding: 'utf8' }); + assert(contents === 'az'); + + var secret = FS.readFile('/working/moar.txt', { encoding: 'utf8' }); + assert(secret === SECRET); + + ccall('success', 'v', '', []); + }); + ); +#endif + + emscripten_exit_with_live_runtime(); + + return 0; +} diff --git a/tests/fs/test_nodefs_rw.c b/tests/fs/test_nodefs_rw.c new file mode 100644 index 00000000..140da332 --- /dev/null +++ b/tests/fs/test_nodefs_rw.c @@ -0,0 +1,49 @@ +#include <assert.h> +#include <stdio.h> +#include <emscripten.h> + +int main() { + FILE *file; + int res; + char buffer[512]; + + // write something locally with node + EM_ASM( + var fs = require('fs'); + fs.writeFileSync('foobar.txt', 'yeehaw'); + ); + + // mount the current folder as a NODEFS instance + // inside of emscripten + EM_ASM( + FS.mkdir('/working'); + FS.mount(NODEFS, { root: '.' }, '/working'); + ); + + // read and validate the contents of the file + file = fopen("/working/foobar.txt", "r"); + assert(file); + res = fread(buffer, sizeof(char), 6, file); + assert(res == 6); + fclose(file); + + assert(!strcmp(buffer, "yeehaw")); + + // write out something new + file = fopen("/working/foobar.txt", "w"); + assert(file); + res = fwrite("cheez", sizeof(char), 5, file); + assert(res == 5); + fclose(file); + + // validate the changes were persisted to the underlying fs + EM_ASM( + var fs = require('fs'); + var contents = fs.readFileSync('foobar.txt', { encoding: 'utf8' }); + assert(contents === 'cheez'); + ); + + puts("success"); + + return 0; +} diff --git a/tests/test_browser.py b/tests/test_browser.py index d50488ec..799759a1 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -880,6 +880,11 @@ keydown(100);keyup(100); // trigger the end self.btest('file_db.cpp', secret, args=['--preload-file', 'moar.txt']) # even with a file there, we load over it shutil.move('test.html', 'third.html') + def test_fs_idbfs_sync(self): + secret = str(time.time()) + self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=['-DFIRST', '-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']''']) + self.btest(path_from_root('tests', 'fs', 'test_idbfs_sync.c'), '1', force_c=True, args=['-DSECRET=\'' + secret + '\'', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']''']) + def test_sdl_pumpevents(self): # key events should be detected using SDL_PumpEvents open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' diff --git a/tests/test_core.py b/tests/test_core.py index d59fae40..f41d59ab 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -7742,12 +7742,18 @@ def process(filename): finally: Settings.INCLUDE_FULL_LIBRARY = 0 + def test_fs_nodefs_rw(self): + src = open(path_from_root('tests', 'fs', 'test_nodefs_rw.c'), 'r').read() + self.do_run(src, 'success', force_c=True) + def test_unistd_access(self): if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code if not self.is_le32(): return self.skip('le32 needed for inline js') - src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read() - self.do_run(src, expected) + for fs in ['MEMFS', 'NODEFS']: + src = open(path_from_root('tests', 'unistd', 'access.c'), 'r').read() + expected = open(path_from_root('tests', 'unistd', 'access.out'), 'r').read() + Building.COMPILER_TEST_OPTS = ['-D' + fs] + self.do_run(src, expected) def test_unistd_curdir(self): if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code @@ -7783,9 +7789,11 @@ def process(filename): def test_unistd_truncate(self): if Settings.ASM_JS: Settings.ASM_JS = 2 # skip validation, asm does not support random code if not self.is_le32(): return self.skip('le32 needed for inline js') - src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').read() - expected = open(path_from_root('tests', 'unistd', 'truncate.out'), 'r').read() - self.do_run(src, expected) + for fs in ['MEMFS', 'NODEFS']: + src = open(path_from_root('tests', 'unistd', 'truncate.c'), 'r').re |