diff options
Diffstat (limited to 'src/library_fs.js')
-rw-r--r-- | src/library_fs.js | 221 |
1 files changed, 161 insertions, 60 deletions
diff --git a/src/library_fs.js b/src/library_fs.js index e6b060f6..1428f041 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -16,7 +16,7 @@ mergeInto(LibraryManager.library, { root: null, mounts: [], devices: [null], - streams: [null], + streams: [], nextInode: 1, nameTable: null, currentPath: '/', @@ -40,7 +40,17 @@ mergeInto(LibraryManager.library, { // lookupPath: function(path, opts) { path = PATH.resolve(FS.cwd(), path); - opts = opts || { recurse_count: 0 }; + opts = opts || {}; + + var defaults = { + follow_mount: true, + recurse_count: 0 + }; + for (var key in defaults) { + if (opts[key] === undefined) { + opts[key] = defaults[key]; + } + } if (opts.recurse_count > 8) { // max recursive lookup of 8 throw new FS.ErrnoError(ERRNO_CODES.ELOOP); @@ -67,10 +77,11 @@ mergeInto(LibraryManager.library, { // jump to the mount's root node if this is a mountpoint if (FS.isMountpoint(current)) { - current = current.mount.root; + if (!islast || (islast && opts.follow_mount)) { + current = current.mounted.root; + } } - // follow symlinks // by default, lookupPath will not follow a symlink if it is the final path component. // setting opts.follow = true will override this behavior. if (!islast || opts.follow) { @@ -163,28 +174,26 @@ mergeInto(LibraryManager.library, { createNode: function(parent, name, mode, rdev) { if (!FS.FSNode) { FS.FSNode = function(parent, name, mode, rdev) { + if (!parent) { + parent = this; // root node sets parent to itself + } + this.parent = parent; + this.mount = parent.mount; + this.mounted = null; this.id = FS.nextInode++; this.name = name; this.mode = mode; this.node_ops = {}; this.stream_ops = {}; this.rdev = rdev; - this.parent = null; - this.mount = null; - if (!parent) { - parent = this; // root node sets parent to itself - } - this.parent = parent; - this.mount = parent.mount; - FS.hashAddNode(this); }; + FS.FSNode.prototype = {}; + // compatibility var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; var writeMode = {{{ cDefine('S_IWUGO') }}}; - FS.FSNode.prototype = {}; - // NOTE we must use Object.defineProperties instead of individual calls to // Object.defineProperty in order to make closure compiler happy Object.defineProperties(FS.FSNode.prototype, { @@ -204,7 +213,12 @@ mergeInto(LibraryManager.library, { }, }); } - return new FS.FSNode(parent, name, mode, rdev); + + var node = new FS.FSNode(parent, name, mode, rdev); + + FS.hashAddNode(node); + + return node; }, destroyNode: function(node) { FS.hashRemoveNode(node); @@ -213,7 +227,7 @@ mergeInto(LibraryManager.library, { return node === node.parent; }, isMountpoint: function(node) { - return node.mounted; + return !!node.mounted; }, isFile: function(mode) { return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFREG') }}}; @@ -344,7 +358,7 @@ mergeInto(LibraryManager.library, { // MAX_OPEN_FDS: 4096, nextfd: function(fd_start, fd_end) { - fd_start = fd_start || 1; + fd_start = fd_start || 0; fd_end = fd_end || FS.MAX_OPEN_FDS; for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { @@ -400,6 +414,22 @@ mergeInto(LibraryManager.library, { }, // + // file pointers + // + // instead of maintaining a separate mapping from FILE* to file descriptors, + // we employ a simple trick: the pointer to a stream is its fd plus 1. This + // means that all valid streams have a valid non-zero pointer while allowing + // the fs for stdin to be the standard value of zero. + // + // + getStreamFromPtr: function(ptr) { + return FS.streams[ptr - 1]; + }, + getPtrForStream: function(stream) { + return stream ? stream.fd + 1 : 0; + }, + + // // devices // // each character device consists of a device id + stream operations. @@ -441,61 +471,131 @@ mergeInto(LibraryManager.library, { // // core // + getMounts: function(mount) { + var mounts = []; + var check = [mount]; + + while (check.length) { + var m = check.pop(); + + mounts.push(m); + + check.push.apply(check, m.mounts); + } + + return mounts; + }, syncfs: function(populate, callback) { if (typeof(populate) === 'function') { callback = populate; populate = false; } + var mounts = FS.getMounts(FS.root.mount); var completed = 0; - var total = FS.mounts.length; + function done(err) { if (err) { - return callback(err); + if (!done.errored) { + done.errored = true; + return callback(err); + } + return; } - if (++completed >= total) { + if (++completed >= mounts.length) { callback(null); } }; // sync all mounts - for (var i = 0; i < FS.mounts.length; i++) { - var mount = FS.mounts[i]; + mounts.forEach(function (mount) { if (!mount.type.syncfs) { - done(null); - continue; + return done(null); } mount.type.syncfs(mount, populate, done); - } + }); }, mount: function(type, opts, mountpoint) { - var lookup; - if (mountpoint) { - lookup = FS.lookupPath(mountpoint, { follow: false }); + var root = mountpoint === '/'; + var pseudo = !mountpoint; + var node; + + if (root && FS.root) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } else if (!root && !pseudo) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + mountpoint = lookup.path; // use the absolute path + node = lookup.node; + + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + + if (!FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } } + var mount = { type: type, opts: opts, mountpoint: mountpoint, - root: null + mounts: [] }; + // create a root node for the fs - var root = type.mount(mount); - root.mount = mount; - mount.root = root; - // assign the mount info to the mountpoint's node - if (lookup) { - lookup.node.mount = mount; - lookup.node.mounted = true; - // compatibility update FS.root if we mount to / - if (mountpoint === '/') { - FS.root = mount.root; + var mountRoot = type.mount(mount); + mountRoot.mount = mount; + mount.root = mountRoot; + + if (root) { + FS.root = mountRoot; + } else if (node) { + // set as a mountpoint + node.mounted = mount; + + // add the new mount to the current mount's children + if (node.mount) { + node.mount.mounts.push(mount); } } - // add to our cached list of mounts - FS.mounts.push(mount); - return root; + + return mountRoot; + }, + unmount: function (mountpoint) { + var lookup = FS.lookupPath(mountpoint, { follow_mount: false }); + + if (!FS.isMountpoint(lookup.node)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + + // destroy the nodes for this mount, and all its child mounts + var node = lookup.node; + var mount = node.mounted; + var mounts = FS.getMounts(mount); + + Object.keys(FS.nameTable).forEach(function (hash) { + var current = FS.nameTable[hash]; + + while (current) { + var next = current.name_next; + + if (mounts.indexOf(current.mount) !== -1) { + FS.destroyNode(current); + } + + current = next; + } + }); + + // no longer a mountpoint + node.mounted = null; + + // remove this mount from the child mounts + var idx = node.mount.mounts.indexOf(mount); + assert(idx !== -1); + node.mount.mounts.splice(idx, 1); }, lookup: function(parent, name) { return parent.node_ops.lookup(parent, name); @@ -516,13 +616,13 @@ mergeInto(LibraryManager.library, { }, // helpers to create specific types of nodes create: function(path, mode) { - mode = mode !== undefined ? mode : 0666; + mode = mode !== undefined ? mode : 438 /* 0666 */; mode &= {{{ cDefine('S_IALLUGO') }}}; mode |= {{{ cDefine('S_IFREG') }}}; return FS.mknod(path, mode, 0); }, mkdir: function(path, mode) { - mode = mode !== undefined ? mode : 0777; + mode = mode !== undefined ? mode : 511 /* 0777 */; mode &= {{{ cDefine('S_IRWXUGO') }}} | {{{ cDefine('S_ISVTX') }}}; mode |= {{{ cDefine('S_IFDIR') }}}; return FS.mknod(path, mode, 0); @@ -530,7 +630,7 @@ mergeInto(LibraryManager.library, { mkdev: function(path, mode, dev) { if (typeof(dev) === 'undefined') { dev = mode; - mode = 0666; + mode = 438 /* 0666 */; } mode |= {{{ cDefine('S_IFCHR') }}}; return FS.mknod(path, mode, dev); @@ -677,7 +777,7 @@ mergeInto(LibraryManager.library, { FS.destroyNode(node); }, readlink: function(path) { - var lookup = FS.lookupPath(path, { follow: false }); + var lookup = FS.lookupPath(path); var link = lookup.node; if (!link.node_ops.readlink) { throw new FS.ErrnoError(ERRNO_CODES.EINVAL); @@ -795,7 +895,7 @@ mergeInto(LibraryManager.library, { }, open: function(path, flags, mode, fd_start, fd_end) { flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; - mode = typeof mode === 'undefined' ? 0666 : mode; + mode = typeof mode === 'undefined' ? 438 /* 0666 */ : mode; if ((flags & {{{ cDefine('O_CREAT') }}})) { mode = (mode & {{{ cDefine('S_IALLUGO') }}}) | {{{ cDefine('S_IFREG') }}}; } else { @@ -975,6 +1075,9 @@ mergeInto(LibraryManager.library, { opts = opts || {}; opts.flags = opts.flags || 'r'; opts.encoding = opts.encoding || 'binary'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } var ret; var stream = FS.open(path, opts.flags); var stat = FS.stat(path); @@ -989,8 +1092,6 @@ mergeInto(LibraryManager.library, { } } else if (opts.encoding === 'binary') { ret = buf; - } else { - throw new Error('Invalid encoding type "' + opts.encoding + '"'); } FS.close(stream); return ret; @@ -999,15 +1100,16 @@ mergeInto(LibraryManager.library, { opts = opts || {}; opts.flags = opts.flags || 'w'; opts.encoding = opts.encoding || 'utf8'; + if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { + throw new Error('Invalid encoding type "' + opts.encoding + '"'); + } var stream = FS.open(path, opts.flags, opts.mode); if (opts.encoding === 'utf8') { var utf8 = new Runtime.UTF8Processor(); var buf = new Uint8Array(utf8.processJSString(data)); - FS.write(stream, buf, 0, buf.length, 0); + FS.write(stream, buf, 0, buf.length, 0, opts.canOwn); } else if (opts.encoding === 'binary') { - FS.write(stream, data, 0, data.length, 0); - } else { - throw new Error('Invalid encoding type "' + opts.encoding + '"'); + FS.write(stream, data, 0, data.length, 0, opts.canOwn); } FS.close(stream); }, @@ -1080,16 +1182,16 @@ mergeInto(LibraryManager.library, { // open default streams for the stdin, stdout and stderr devices var stdin = FS.open('/dev/stdin', 'r'); - {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}}; - assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')'); + {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'FS.getPtrForStream(stdin)', 'void*') }}}; + assert(stdin.fd === 0, 'invalid handle for stdin (' + stdin.fd + ')'); var stdout = FS.open('/dev/stdout', 'w'); - {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}}; - assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')'); + {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'FS.getPtrForStream(stdout)', 'void*') }}}; + assert(stdout.fd === 1, 'invalid handle for stdout (' + stdout.fd + ')'); var stderr = FS.open('/dev/stderr', 'w'); - {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}}; - assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')'); + {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'FS.getPtrForStream(stderr)', 'void*') }}}; + assert(stderr.fd === 2, 'invalid handle for stderr (' + stderr.fd + ')'); }, ensureErrnoError: function() { if (FS.ErrnoError) return; @@ -1119,7 +1221,6 @@ mergeInto(LibraryManager.library, { FS.nameTable = new Array(4096); - FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); FS.mount(MEMFS, {}, '/'); FS.createDefaultDirectories(); |