aboutsummaryrefslogtreecommitdiff
path: root/src/library_fs.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/library_fs.js')
-rw-r--r--src/library_fs.js221
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();