diff options
-rw-r--r-- | src/library.js | 1743 | ||||
-rw-r--r-- | src/settings.js | 2 | ||||
-rwxr-xr-x | tests/runner.py | 4 | ||||
-rw-r--r-- | tests/stat/test_chmod.c | 24 | ||||
-rw-r--r-- | tests/stat/test_mknod.c | 5 | ||||
-rw-r--r-- | tests/stat/test_stat.c | 3 | ||||
-rw-r--r-- | tests/stdio/test_rename.c | 4 |
7 files changed, 214 insertions, 1571 deletions
diff --git a/src/library.js b/src/library.js index 60a600ee..201a63e0 100644 --- a/src/library.js +++ b/src/library.js @@ -28,714 +28,7 @@ LibraryManager.library = { stderr: 'allocate(1, "i32*", ALLOC_STATIC)', _impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)', - $FSCOM__deps: ['$FS'], - $FSCOM: { - // Makes sure a file's contents are loaded. Returns whether the file has - // been loaded successfully. No-op for files that have been loaded already. - forceLoadFile: function(obj) { - if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; - var success = true; - if (typeof XMLHttpRequest !== 'undefined') { - throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); - } else if (Module['read']) { - // Command-line. - try { - // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as - // read() will try to parse UTF8. - obj.contents = intArrayFromString(Module['read'](obj.url), true); - } catch (e) { - success = false; - } - } else { - throw new Error('Cannot load without read() or XMLHttpRequest.'); - } - if (!success) ___setErrNo(ERRNO_CODES.EIO); - return success; - }, - // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous - // XHR, which is not possible in browsers except in a web worker! Use preloading, - // either --preload-file in emcc or FS.createPreloadedFile - createLazyFile: function(parent, name, url, canRead, canWrite) { - if (typeof XMLHttpRequest !== 'undefined') { - if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; - // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. - var LazyUint8Array = function() { - this.lengthKnown = false; - this.chunks = []; // Loaded chunks. Index is the chunk number - } - LazyUint8Array.prototype.get = function(idx) { - if (idx > this.length-1 || idx < 0) { - return undefined; - } - var chunkOffset = idx % this.chunkSize; - var chunkNum = Math.floor(idx / this.chunkSize); - return this.getter(chunkNum)[chunkOffset]; - } - LazyUint8Array.prototype.setDataGetter = function(getter) { - this.getter = getter; - } - LazyUint8Array.prototype.cacheLength = function() { - // Find length - var xhr = new XMLHttpRequest(); - xhr.open('HEAD', url, false); - xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); - var datalength = Number(xhr.getResponseHeader("Content-length")); - var header; - var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; -#if SMALL_XHR_CHUNKS - var chunkSize = 1024; // Chunk size in bytes -#else - var chunkSize = 1024*1024; // Chunk size in bytes -#endif - - if (!hasByteServing) chunkSize = datalength; - - // Function to get a range from the remote URL. - var doXHR = (function(from, to) { - if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); - if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); - - // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, false); - if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); - - // Some hints to the browser that we want binary data. - if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; - if (xhr.overrideMimeType) { - xhr.overrideMimeType('text/plain; charset=x-user-defined'); - } - - xhr.send(null); - if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); - if (xhr.response !== undefined) { - return new Uint8Array(xhr.response || []); - } else { - return intArrayFromString(xhr.responseText || '', true); - } - }); - var lazyArray = this; - lazyArray.setDataGetter(function(chunkNum) { - var start = chunkNum * chunkSize; - var end = (chunkNum+1) * chunkSize - 1; // including this byte - end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block - if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { - lazyArray.chunks[chunkNum] = doXHR(start, end); - } - if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); - return lazyArray.chunks[chunkNum]; - }); - - this._length = datalength; - this._chunkSize = chunkSize; - this.lengthKnown = true; - } - - var lazyArray = new LazyUint8Array(); - Object.defineProperty(lazyArray, "length", { - get: function() { - if(!this.lengthKnown) { - this.cacheLength(); - } - return this._length; - } - }); - Object.defineProperty(lazyArray, "chunkSize", { - get: function() { - if(!this.lengthKnown) { - this.cacheLength(); - } - return this._chunkSize; - } - }); - - var properties = { isDevice: false, contents: lazyArray }; - } else { - var properties = { isDevice: false, url: url }; - } - - var node = FS.createFile(parent, name, properties, canRead, canWrite); -#if USE_OLD_FS == 0 - // This is a total hack, but I want to get this lazy file code out of the - // core of MEMFS. If we want to keep this lazy file concept I feel it should - // be its own thin LAZYFS proxying calls to MEMFS. - if (properties.contents) { - node.contents = properties.contents; - } else if (properties.url) { - node.contents = null; - node.url = properties.url; - } - // override each stream op with one that tries to force load the lazy file first - var stream_ops = {}; - var keys = Object.keys(node.stream_ops); - keys.forEach(function (key) { - var fn = node.stream_ops[key]; - stream_ops[key] = function () { - if (!FS.forceLoadFile(node)) { - throw new FS.ErrnoError(ERRNO_CODES.EIO); - } - return fn.apply(null, arguments); - }; - }); - // use a custom read function - stream_ops.read = function (stream, buffer, offset, length, position) { - var contents = stream.node.contents; - var size = Math.min(contents.length - position, length); - if (contents.slice) { // normal array - for (var i = 0; i < size; i++) { - buffer[offset + i] = contents[position + i]; - } - } else { - for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR - buffer[offset + i] = contents.get(position + i); - } - } - return size; - }; - node.stream_ops = stream_ops; -#endif - return node; - }, - // Preloads a file asynchronously. You can call this before run, for example in - // preRun. run will be delayed until this file arrives and is set up. - // If you call it after run(), you may want to pause the main loop until it - // completes, if so, you can use the onload parameter to be notified when - // that happens. - // In addition to normally creating the file, we also asynchronously preload - // the browser-friendly versions of it: For an image, we preload an Image - // element and for an audio, and Audio. These are necessary for SDL_Image - // and _Mixer to find the files in preloadedImages/Audios. - // You can also call this with a typed array instead of a url. It will then - // do preloading for the Image/Audio part, as if the typed array were the - // result of an XHR that you did manually. - createPreloadedFile__deps: ['$PATH'], - createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile) { - Browser.init(); - var fullname = PATH.join(parent, name).substr(1); - function processData(byteArray) { - function finish(byteArray) { - if (!dontCreateFile) { - FS.createDataFile(parent, name, byteArray, canRead, canWrite); - } - if (onload) onload(); - removeRunDependency('cp ' + fullname); - } - var handled = false; - Module['preloadPlugins'].forEach(function(plugin) { - if (handled) return; - if (plugin['canHandle'](fullname)) { - plugin['handle'](byteArray, fullname, finish, function() { - if (onerror) onerror(); - removeRunDependency('cp ' + fullname); - }); - handled = true; - } - }); - if (!handled) finish(byteArray); - } - addRunDependency('cp ' + fullname); - if (typeof url == 'string') { - Browser.asyncLoad(url, function(byteArray) { - processData(byteArray); - }, onerror); - } else { - processData(url); - } - } - }, - -#if USE_OLD_FS - $FS__deps: ['$FSCOM', '$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], - $FS__postset: 'FS.staticInit();' + - '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + - '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + - '__ATEXIT__.push({ func: function() { FS.quit() } });' + - // export some names through closure - 'Module["FS_createFolder"] = FS.createFolder;' + - 'Module["FS_createPath"] = FS.createPath;' + - 'Module["FS_createDataFile"] = FS.createDataFile;' + - 'Module["FS_createPreloadedFile"] = FS.createPreloadedFile;' + - 'Module["FS_createLazyFile"] = FS.createLazyFile;' + - 'Module["FS_createLink"] = FS.createLink;' + - 'Module["FS_createDevice"] = FS.createDevice;', - $FS: { - // The path to the current folder. - currentPath: '/', - // The inode to assign to the next created object. - nextInode: 2, - // Currently opened file or directory streams. Padded with null so the zero - // index is unused, as the indices are used as pointers. This is not split - // into separate fileStreams and folderStreams lists because the pointers - // must be interchangeable, e.g. when used in fdopen(). - // streams is kept as a dense array. It may contain |null| to fill in - // holes, however. - streams: [null], -#if ASSERTIONS - checkStreams: function() { - for (var i in FS.streams) if (FS.streams.hasOwnProperty(i)) assert(i >= 0 && i < FS.streams.length); // no keys not in dense span - for (var i = 0; i < FS.streams.length; i++) assert(typeof FS.streams[i] == 'object'); // no non-null holes in dense span - }, -#endif - // Whether we are currently ignoring permissions. Useful when preparing the - // filesystem and creating files inside read-only folders. - // This is set to false when the runtime is initialized, allowing you - // to modify the filesystem freely before run() is called. - ignorePermissions: true, - createStream: function(stream, fd) { - if (typeof stream === 'undefined') { - stream = null; - } - if (!fd) { - if (stream && stream.socket) { - for (var i = 1; i < 64; i++) { - if (!FS.streams[i]) { - fd = i; - break; - } - } - assert(fd, 'ran out of low fds for sockets'); - } else { - fd = Math.max(FS.streams.length, 64); - for (var i = FS.streams.length; i < fd; i++) { - FS.streams[i] = null; // Keep dense - } - } - } - // Close WebSocket first if we are about to replace the fd (i.e. dup2) - if (FS.streams[fd] && FS.streams[fd].socket && FS.streams[fd].socket.close) { - FS.streams[fd].socket.close(); - } - if (stream) { - stream.fd = fd; - } - FS.streams[fd] = stream; - return stream; - }, - closeStream: function(stream) { - FS.streams[stream.fd] = null; - }, - getStream: function (fd) { - return FS.streams[fd]; - }, - joinPath: function(parts, forceRelative) { - var ret = parts[0]; - for (var i = 1; i < parts.length; i++) { - if (ret[ret.length-1] != '/') ret += '/'; - ret += parts[i]; - } - if (forceRelative && ret[0] == '/') ret = ret.substr(1); - return ret; - }, - // Converts any path to an absolute path. Resolves embedded "." and ".." - // parts. - absolutePath: function(relative, base) { - if (typeof relative !== 'string') return null; - if (base === undefined) base = FS.currentPath; - if (relative && relative[0] == '/') base = ''; - var full = base + '/' + relative; - var parts = full.split('/').reverse(); - var absolute = ['']; - while (parts.length) { - var part = parts.pop(); - if (part == '' || part == '.') { - // Nothing. - } else if (part == '..') { - if (absolute.length > 1) absolute.pop(); - } else { - absolute.push(part); - } - } - return absolute.length == 1 ? '/' : absolute.join('/'); - }, - // Analyzes a relative or absolute path returning a description, containing: - // isRoot: Whether the path points to the root. - // exists: Whether the object at the path exists. - // error: If !exists, this will contain the errno code of the cause. - // name: The base name of the object (null if !parentExists). - // path: The absolute path to the object, with all links resolved. - // object: The filesystem record of the object referenced by the path. - // parentExists: Whether the parent of the object exist and is a folder. - // parentPath: The absolute path to the parent folder. - // parentObject: The filesystem record of the parent folder. - analyzePath: function(path, dontResolveLastLink, linksVisited) { - var ret = { - isRoot: false, - exists: false, - error: 0, - name: null, - path: null, - object: null, - parentExists: false, - parentPath: null, - parentObject: null - }; -#if FS_LOG - var inputPath = path; - function log() { - Module['print']('FS.analyzePath("' + inputPath + '", ' + - dontResolveLastLink + ', ' + - linksVisited + ') => {' + - 'isRoot: ' + ret.isRoot + ', ' + - 'exists: ' + ret.exists + ', ' + - 'error: ' + ret.error + ', ' + - 'name: "' + ret.name + '", ' + - 'path: "' + ret.path + '", ' + - 'object: ' + ret.object + ', ' + - 'parentExists: ' + ret.parentExists + ', ' + - 'parentPath: "' + ret.parentPath + '", ' + - 'parentObject: ' + ret.parentObject + '}'); - } -#endif - path = FS.absolutePath(path); - if (path == '/') { - ret.isRoot = true; - ret.exists = ret.parentExists = true; - ret.name = '/'; - ret.path = ret.parentPath = '/'; - ret.object = ret.parentObject = FS.root; - } else if (path !== null) { - linksVisited = linksVisited || 0; - path = path.slice(1).split('/'); - var current = FS.root; - var traversed = ['']; - while (path.length) { - if (path.length == 1 && current.isFolder) { - ret.parentExists = true; - ret.parentPath = traversed.length == 1 ? '/' : traversed.join('/'); - ret.parentObject = current; - ret.name = path[0]; - } - var target = path.shift(); - if (!current.isFolder) { - ret.error = ERRNO_CODES.ENOTDIR; - break; - } else if (!current.read) { - ret.error = ERRNO_CODES.EACCES; - break; - } else if (!current.contents.hasOwnProperty(target)) { - ret.error = ERRNO_CODES.ENOENT; - break; - } - current = current.contents[target]; - if (current.link && !(dontResolveLastLink && path.length == 0)) { - if (linksVisited > 40) { // Usual Linux SYMLOOP_MAX. - ret.error = ERRNO_CODES.ELOOP; - break; - } - var link = FS.absolutePath(current.link, traversed.join('/')); - ret = FS.analyzePath([link].concat(path).join('/'), - dontResolveLastLink, linksVisited + 1); -#if FS_LOG - log(); -#endif - return ret; - } - traversed.push(target); - if (path.length == 0) { - ret.exists = true; - ret.path = traversed.join('/'); - ret.object = current; - } - } - } -#if FS_LOG - log(); -#endif - return ret; - }, - // Finds the file system object at a given path. If dontResolveLastLink is - // set to true and the object is a symbolic link, it will be returned as is - // instead of being resolved. Links embedded in the path are still resolved. - findObject: function(path, dontResolveLastLink) { - var ret = FS.analyzePath(path, dontResolveLastLink); - if (ret.exists) { - return ret.object; - } else { - ___setErrNo(ret.error); - return null; - } - }, - // Creates a file system record: file, link, device or folder. - createObject: function(parent, name, properties, canRead, canWrite) { -#if FS_LOG - Module['print']('FS.createObject("' + parent + '", ' + - '"' + name + '", ' + - JSON.stringify(properties) + ', ' + - canRead + ', ' + - canWrite + ')'); -#endif - if (!parent) parent = '/'; - if (typeof parent === 'string') parent = FS.findObject(parent); - - if (!parent) { - ___setErrNo(ERRNO_CODES.EACCES); - throw new Error('Parent path must exist.'); - } - if (!parent.isFolder) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - throw new Error('Parent must be a folder.'); - } - if (!parent.write && !FS.ignorePermissions) { - ___setErrNo(ERRNO_CODES.EACCES); - throw new Error('Parent folder must be writeable.'); - } - if (!name || name == '.' || name == '..') { - ___setErrNo(ERRNO_CODES.ENOENT); - throw new Error('Name must not be empty.'); - } - if (parent.contents.hasOwnProperty(name)) { - ___setErrNo(ERRNO_CODES.EEXIST); - throw new Error("Can't overwrite object."); - } - - parent.contents[name] = { - read: canRead === undefined ? true : canRead, - write: canWrite === undefined ? false : canWrite, - timestamp: Date.now(), - inodeNumber: FS.nextInode++ - }; - for (var key in properties) { - if (properties.hasOwnProperty(key)) { - parent.contents[name][key] = properties[key]; - } - } - - return parent.contents[name]; - }, - // Creates a folder. - createFolder: function(parent, name, canRead, canWrite) { - var properties = {isFolder: true, isDevice: false, contents: {}}; - return FS.createObject(parent, name, properties, canRead, canWrite); - }, - // Creates a folder and all its missing parents. - createPath: function(parent, path, canRead, canWrite) { - var current = FS.findObject(parent); - if (current === null) throw new Error('Invalid parent.'); - path = path.split('/').reverse(); - while (path.length) { - var part = path.pop(); - if (!part) continue; - if (!current.contents.hasOwnProperty(part)) { - FS.createFolder(current, part, canRead, canWrite); - } - current = current.contents[part]; - } - return current; - }, - - // Creates a file record, given specific properties. - createFile: function(parent, name, properties, canRead, canWrite) { - properties.isFolder = false; - return FS.createObject(parent, name, properties, canRead, canWrite); - }, - // Creates a file record from existing data. - createDataFile: function(parent, name, data, canRead, canWrite) { - if (typeof data === 'string') { - var dataArray = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) dataArray[i] = data.charCodeAt(i); - data = dataArray; - } - var properties = { - isDevice: false, - contents: data.subarray ? data.subarray(0) : data // as an optimization, create a new array wrapper (not buffer) here, to help JS engines understand this object - }; - return FS.createFile(parent, name, properties, canRead, canWrite); - }, - createLazyFile: function() { - return FSCOM.createLazyFile.apply(this, arguments); - }, - createPreloadedFile: function() { - FSCOM.createPreloadedFile.apply(this, arguments); - }, - // Creates a link to a specific local path. - createLink: function(parent, name, target, canRead, canWrite) { - var properties = {isDevice: false, link: target}; - return FS.createFile(parent, name, properties, canRead, canWrite); - }, - // Creates a character device with input and output callbacks: - // input: Takes no parameters, returns a byte value or null if no data is - // currently available. - // output: Takes a byte value; doesn't return anything. Can also be passed - // null to perform a flush of any cached data. - createDevice: function(parent, name, input, output) { - if (!(input || output)) { - throw new Error('A device must have at least one callback defined.'); - } - var ops = {isDevice: true, input: input, output: output}; - return FS.createFile(parent, name, ops, Boolean(input), Boolean(output)); - }, - forceLoadFile: function() { - return FSCOM.forceLoadFile.apply(this, arguments); - }, - staticInit: function () { - // The main file system tree. All the contents are inside this. - FS.root = { - read: true, - write: true, - isFolder: true, - isDevice: false, - timestamp: Date.now(), - inodeNumber: 1, - contents: {} - }; - // Create the temporary folder, if not already created - try { - FS.createFolder('/', 'tmp', true, true); - } catch(e) {} - FS.createFolder('/', 'dev', true, true); - }, - // Initializes the filesystems with stdin/stdout/stderr devices, given - // optional handlers. - init: function(input, output, error) { - // Make sure we initialize only once. - assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); - FS.init.initialized = true; - - // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here - input = input || Module['stdin']; - output = output || Module['stdout']; - error = error || Module['stderr']; - - // Default handlers. - var stdinOverridden = true, stdoutOverridden = true, stderrOverridden = true; - if (!input) { - stdinOverridden = false; - input = function() { - if (!input.cache || !input.cache.length) { - var result; - if (typeof window != 'undefined' && - typeof window.prompt == 'function') { - // Browser. - result = window.prompt('Input: '); - if (result === null) result = String.fromCharCode(0); // cancel ==> EOF - } else if (typeof readline == 'function') { - // Command line. - result = readline(); - } - if (!result) result = ''; - input.cache = intArrayFromString(result + '\n', true); - } - return input.cache.shift(); - }; - } - var utf8 = new Runtime.UTF8Processor(); - function createSimpleOutput() { - var fn = function (val) { - if (val === null || val === {{{ charCode('\n') }}}) { - fn.printer(fn.buffer.join('')); - fn.buffer = []; - } else { - fn.buffer.push(utf8.processCChar(val)); - } - }; - return fn; - } - if (!output) { - stdoutOverridden = false; - output = createSimpleOutput(); - } - if (!output.printer) output.printer = Module['print']; - if (!output.buffer) output.buffer = []; - if (!error) { - stderrOverridden = false; - error = createSimpleOutput(); - } - if (!error.printer) error.printer = Module['printErr']; - if (!error.buffer) error.buffer = []; - - // Create the I/O devices. - var stdin = FS.createDevice('/dev', 'stdin', input); - stdin.isTerminal = !stdinOverridden; - var stdout = FS.createDevice('/dev', 'stdout', null, output); - stdout.isTerminal = !stdoutOverridden; - var stderr = FS.createDevice('/dev', 'stderr', null, error); - stderr.isTerminal = !stderrOverridden; - FS.createDevice('/dev', 'tty', input, output); - FS.createDevice('/dev', 'null', function(){}, function(){}); - - // Create default streams. - FS.streams[1] = { - path: '/dev/stdin', - object: stdin, - position: 0, - isRead: true, - isWrite: false, - isAppend: false, - error: false, - eof: false, - ungotten: [] - }; - FS.streams[2] = { - path: '/dev/stdout', - object: stdout, - position: 0, - isRead: false, - isWrite: true, - isAppend: false, - error: false, - eof: false, - ungotten: [] - }; - FS.streams[3] = { - path: '/dev/stderr', - object: stderr, - position: 0, - isRead: false, - isWrite: true, - isAppend: false, - error: false, - eof: false, - ungotten: [] - }; - // TODO: put these low in memory like we used to assert on: assert(Math.max(_stdin, _stdout, _stderr) < 15000); // make sure these are low, we flatten arrays with these - {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 1, 'void*') }}}; - {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 2, 'void*') }}}; - {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 3, 'void*') }}}; - - // Other system paths - FS.createPath('/', 'dev/shm/tmp', true, true); // temp files - - // Newlib initialization - for (var i = FS.streams.length; i < Math.max(_stdin, _stdout, _stderr) + {{{ QUANTUM_SIZE }}}; i++) { - FS.streams[i] = null; // Make sure to keep FS.streams dense - } - FS.streams[_stdin] = FS.streams[1]; - FS.streams[_stdout] = FS.streams[2]; - FS.streams[_stderr] = FS.streams[3]; -#if ASSERTIONS - FS.checkStreams(); - // see previous TODO on stdin etc.: assert(FS.streams.length < 1024); // at this early stage, we should not have a large set of file descriptors - just a few -#endif - allocate([ allocate( - {{{ Runtime.QUANTUM_SIZE === 4 ? '[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0]' : '[0, _stdin, _stdout, _stderr]' }}}, - 'void*', ALLOC_NORMAL) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}}); - }, - - quit: function() { - if (!FS.init.initialized) return; - // Flush any partially-printed lines in stdout and stderr. Careful, they may have been closed - if (FS.streams[2] && FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output({{{ charCode('\n') }}}); - if (FS.streams[3] && FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output({{{ charCode('\n') }}}); - }, - - // Standardizes a path. Useful for making comparisons of pathnames work in a consistent manner. - // For example, ./file and file are really the same, so this function will remove ./ - standardizePath: function(path) { - if (path.substr(0, 2) == './') path = path.substr(2); - return path; - }, - - deleteFile: function(path) { - path = FS.analyzePath(path); - if (!path.parentExists || !path.exists) { - throw 'Invalid path ' + path; - } - delete path.parentObject.contents[path.name]; - } - }, -#else - $FS__deps: ['$FSCOM', '$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', 'stdin', 'stdout', 'stderr', 'fflush'], + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', '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 } });' + @@ -1399,14 +692,216 @@ LibraryManager.library = { var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); return VFS.symlink(target, path); }, - forceLoadFile: function() { - return FSCOM.forceLoadFile.apply(this, arguments); + // Makes sure a file's contents are loaded. Returns whether the file has + // been loaded successfully. No-op for files that have been loaded already. + forceLoadFile: function(obj) { + if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; + var success = true; + if (typeof XMLHttpRequest !== 'undefined') { + throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + } else if (Module['read']) { + // Command-line. + try { + // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as + // read() will try to parse UTF8. + obj.contents = intArrayFromString(Module['read'](obj.url), true); + } catch (e) { + success = false; + } + } else { + throw new Error('Cannot load without read() or XMLHttpRequest.'); + } + if (!success) ___setErrNo(ERRNO_CODES.EIO); + return success; }, - createLazyFile: function () { - return FSCOM.createLazyFile.apply(this, arguments); + // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous + // XHR, which is not possible in browsers except in a web worker! Use preloading, + // either --preload-file in emcc or FS.createPreloadedFile + createLazyFile: function(parent, name, url, canRead, canWrite) { + if (typeof XMLHttpRequest !== 'undefined') { + if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; + // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. + var LazyUint8Array = function() { + this.lengthKnown = false; + this.chunks = []; // Loaded chunks. Index is the chunk number + } + LazyUint8Array.prototype.get = function(idx) { + if (idx > this.length-1 || idx < 0) { + return undefined; + } + var chunkOffset = idx % this.chunkSize; + var chunkNum = Math.floor(idx / this.chunkSize); + return this.getter(chunkNum)[chunkOffset]; + } + LazyUint8Array.prototype.setDataGetter = function(getter) { + this.getter = getter; + } + LazyUint8Array.prototype.cacheLength = function() { + // Find length + var xhr = new XMLHttpRequest(); + xhr.open('HEAD', url, false); + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + var datalength = Number(xhr.getResponseHeader("Content-length")); + var header; + var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; +#if SMALL_XHR_CHUNKS + var chunkSize = 1024; // Chunk size in bytes +#else + var chunkSize = 1024*1024; // Chunk size in bytes +#endif + + if (!hasByteServing) chunkSize = datalength; + + // Function to get a range from the remote URL. + var doXHR = (function(from, to) { + if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); + if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); + + // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, false); + if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); + + // Some hints to the browser that we want binary data. + if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; + if (xhr.overrideMimeType) { + xhr.overrideMimeType('text/plain; charset=x-user-defined'); + } + + xhr.send(null); + if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); + if (xhr.response !== undefined) { + return new Uint8Array(xhr.response || []); + } else { + return intArrayFromString(xhr.responseText || '', true); + } + }); + var lazyArray = this; + lazyArray.setDataGetter(function(chunkNum) { + var start = chunkNum * chunkSize; + var end = (chunkNum+1) * chunkSize - 1; // including this byte + end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { + lazyArray.chunks[chunkNum] = doXHR(start, end); + } + if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); + return lazyArray.chunks[chunkNum]; + }); + + this._length = datalength; + this._chunkSize = chunkSize; + this.lengthKnown = true; + } + + var lazyArray = new LazyUint8Array(); + Object.defineProperty(lazyArray, "length", { + get: function() { + if(!this.lengthKnown) { + this.cacheLength(); + } + return this._length; + } + }); + Object.defineProperty(lazyArray, "chunkSize", { + get: function() { + if(!this.lengthKnown) { + this.cacheLength(); + } + return this._chunkSize; + } + }); + + var properties = { isDevice: false, contents: lazyArray }; + } else { + var properties = { isDevice: false, url: url }; + } + + var node = FS.createFile(parent, name, properties, canRead, canWrite); + // This is a total hack, but I want to get this lazy file code out of the + // core of MEMFS. If we want to keep this lazy file concept I feel it should + // be its own thin LAZYFS proxying calls to MEMFS. + if (properties.contents) { + node.contents = properties.contents; + } else if (properties.url) { + node.contents = null; + node.url = properties.url; + } + // override each stream op with one that tries to force load the lazy file first + var stream_ops = {}; + var keys = Object.keys(node.stream_ops); + keys.forEach(function (key) { + var fn = node.stream_ops[key]; + stream_ops[key] = function () { + if (!FS.forceLoadFile(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + return fn.apply(null, arguments); + }; + }); + // use a custom read function + stream_ops.read = function (stream, buffer, offset, length, position) { + var contents = stream.node.contents; + var size = Math.min(contents.length - position, length); + if (contents.slice) { // normal array + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } else { + for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR + buffer[offset + i] = contents.get(position + i); + } + } + return size; + }; + node.stream_ops = stream_ops; + return node; }, - createPreloadedFile: function () { - FSCOM.createPreloadedFile.apply(this, arguments); + // Preloads a file asynchronously. You can call this before run, for example in + // preRun. run will be delayed until this file arrives and is set up. + // If you call it after run(), you may want to pause the main loop until it + // completes, if so, you can use the onload parameter to be notified when + // that happens. + // In addition to normally creating the file, we also asynchronously preload + // the browser-friendly versions of it: For an image, we preload an Image + // element and for an audio, and Audio. These are necessary for SDL_Image + // and _Mixer to find the files in preloadedImages/Audios. + // You can also call this with a typed array instead of a url. It will then + // do preloading for the Image/Audio part, as if the typed array were the + // result of an XHR that you did manually. + createPreloadedFile__deps: ['$PATH'], + createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile) { + Browser.init(); + var fullname = PATH.join(parent, name).substr(1); + function processData(byteArray) { + function finish(byteArray) { + if (!dontCreateFile) { + FS.createDataFile(parent, name, byteArray, canRead, canWrite); + } + if (onload) onload(); + removeRunDependency('cp ' + fullname); + } + var handled = false; + Module['preloadPlugins'].forEach(function(plugin) { + if (handled) return; + if (plugin['canHandle'](fullname)) { + plugin['handle'](byteArray, fullname, finish, function() { + if (onerror) onerror(); + removeRunDependency('cp ' + fullname); + }); + handled = true; + } + }); + if (!handled) finish(byteArray); + } + addRunDependency('cp ' + fullname); + if (typeof url == 'string') { + Browser.asyncLoad(url, function(byteArray) { + processData(byteArray); + }, onerror); + } else { + processData(url); + } } }, @@ -2257,46 +1752,6 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/007908799/xsh/opendir.html // NOTE: Calculating absolute path redundantly since we need to associate it // with the opened stream. -#if USE_OLD_FS - var path = FS.absolutePath(Pointer_stringify(dirname)); - if (path === null) { - ___setErrNo(ERRNO_CODES.ENOENT); - return 0; - } - var target = FS.findObject(path); - if (target === null) return 0; - if (!target.isFolder) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - return 0; - } else if (!target.read) { - ___setErrNo(ERRNO_CODES.EACCES); - return 0; - } - var contents = []; - for (var key in target.contents) contents.push(key); - var stream = FS.createStream({ - path: path, - object: target, - // An index into contents. Special values: -2 is ".", -1 is "..". - position: -2, - isRead: true, - isWrite: false, - isAppend: false, - error: false, - eof: false, - ungotten: [], - // Folder-specific properties: - // Remember the contents at the time of opening in an array, so we can - // seek between them relying on a single order. - contents: contents, - // Each stream has its own area for readdir() returns. - currentEntry: _malloc(___dirent_struct_layout.__size__) - }); -#if ASSERTIONS - FS.checkStreams(); -#endif - return stream.fd; -#else var path = Pointer_stringify(dirname); if (!path) { ___setErrNo(ERRNO_CODES.ENOENT); @@ -2317,35 +1772,19 @@ LibraryManager.library = { var err = _open(dirname, {{{ cDefine('O_RDONLY') }}}, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); // open returns 0 on failure, not -1 return err === -1 ? 0 : err; -#endif }, closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'close'], closedir: function(dirp) { // int closedir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html -#if USE_OLD_FS - var stream = FS.getStream(dirp); - if (!stream || !stream.object.isFolder) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - _free(stream.currentEntry); - FS.closeStream(stream); - return 0; -#else return _close(dirp); -#endif }, telldir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], telldir: function(dirp) { // long int telldir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/telldir.html var stream = FS.getStream(dirp); -#if USE_OLD_FS - if (!stream || !stream.object.isFolder) { -#else if (!stream || !FS.isDir(stream.node.mode)) { -#endif ___setErrNo(ERRNO_CODES.EBADF); return -1; } @@ -2355,85 +1794,22 @@ LibraryManager.library = { seekdir: function(dirp, loc) { // void seekdir(DIR *dirp, long int loc); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/seekdir.html -#if USE_OLD_FS - var stream = FS.getStream(dirp); - if (!stream || !stream.object.isFolder) { - ___setErrNo(ERRNO_CODES.EBADF); - return; - } - var entries = 0; - for (var key in stream.contents) entries++; - if (loc >= entries) { - ___setErrNo(ERRNO_CODES.EINVAL); - } else { - stream.position = loc; - } -#else _lseek(dirp, loc, {{{ cDefine('SEEK_SET') }}}); -#endif }, rewinddir__deps: ['seekdir'], rewinddir: function(dirp) { // void rewinddir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/rewinddir.html -#if USE_OLD_FS - _seekdir(dirp, -2); -#else _seekdir(dirp, 0); -#endif }, readdir_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], readdir_r: function(dirp, entry, result) { // int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html var stream = FS.getStream(dirp); -#if USE_OLD_FS - if (!stream || !stream.object.isFolder) { -#else if (!stream) { -#endif return ___setErrNo(ERRNO_CODES.EBADF); } -#if USE_OLD_FS - var loc = stream.position; - var entries = 0; - for (var key in stream.contents) entries++; - if (loc < -2 || loc >= entries) { - {{{ makeSetValue('result', '0', '0', 'i8*') }}} - } else { - var name, inode, type; - if (loc === -2) { - name = '.'; - inode = 1; // Really undefined. - type = 4; //DT_DIR - } else if (loc === -1) { - name = '..'; - inode = 1; // Really undefined. - type = 4; //DT_DIR - } else { - var object; - name = stream.contents[loc]; - object = stream.object.contents[name]; - inode = object.inodeNumber; - type = object.isDevice ? 2 // DT_CHR, character device. - : object.isFolder ? 4 // DT_DIR, directory. - : object.link !== undefined ? 10 // DT_LNK, symbolic link. - : 8; // DT_REG, regular file. - } - stream.position++; - var offsets = ___dirent_struct_layout; - {{{ makeSetValue('entry', 'offsets.d_ino', 'inode', 'i32') }}} - {{{ makeSetValue('entry', 'offsets.d_off', 'stream.position', 'i32') }}} - {{{ makeSetValue('entry', 'offsets.d_reclen', 'name.length + 1', 'i32') }}} - for (var i = 0; i < name.length; i++) { - {{{ makeSetValue('entry + offsets.d_name', 'i', 'name.charCodeAt(i)', 'i8') }}} - } - {{{ makeSetValue('entry + offsets.d_name', 'i', '0', 'i8') }}} - {{{ makeSetValue('entry', 'offsets.d_type', 'type', 'i8') }}} - {{{ makeSetValue('result', '0', 'entry', 'i8*') }}} - } - return 0; -#else var entries; try { entries = VFS.readdir(stream); @@ -2470,30 +1846,16 @@ LibraryManager.library = { {{{ makeSetValue('result', '0', 'entry', 'i8*') }}} stream.position++; return 0; -#endif }, readdir__deps: ['readdir_r', '__setErrNo', '$ERRNO_CODES'], readdir: function(dirp) { // struct dirent *readdir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html var stream = FS.getStream(dirp); -#if USE_OLD_FS - if (!stream || !stream.object.isFolder) { -#else if (!stream) { -#endif ___setErrNo(ERRNO_CODES.EBADF); return 0; } -#if USE_OLD_FS - if (!_readdir.result) _readdir.result = _malloc(4); - _readdir_r(dirp, stream.currentEntry, _readdir.result); - if ({{{ makeGetValue(0, '_readdir.result', 'i8*') }}} === 0) { - return 0; - } else { - return stream.currentEntry; - } -#else // TODO Is it supposed to be safe to execute multiple readdirs? if (!_readdir.entry) _readdir.entry = _malloc(___dirent_struct_layout.__size__); if (!_readdir.result) _readdir.result = _malloc(4); @@ -2503,7 +1865,6 @@ LibraryManager.library = { return 0; } return {{{ makeGetValue(0, '_readdir.result', 'i8*') }}}; -#endif }, __01readdir64_: 'readdir', // TODO: Check if we need to link any other aliases. @@ -2529,12 +1890,6 @@ LibraryManager.library = { time = Date.now(); } path = Pointer_stringify(path); -#if USE_OLD_FS - var file = FS.findObject(path); - if (file === null) return -1; - file.timestamp = time; - return 0; -#else try { VFS.utime(path, time, time); return 0; @@ -2542,7 +1897,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif; }, utimes: function() { throw 'utimes not implemented' }, @@ -2636,68 +1990,6 @@ LibraryManager.library = { // NOTE: dontResolveLastLink is a shortcut for lstat(). It should never be // used in client code. path = typeof path !== 'string' ? Pointer_stringify(path) : path; -#if USE_OLD_FS - var obj = FS.findObject(path, dontResolveLastLink); - if (obj === null || !FS.forceLoadFile(obj)) return -1; - - var offsets = ___stat_struct_layout; - - // Constants. - {{{ makeSetValue('buf', 'offsets.st_nlink', '1', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.st_uid', '0', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.st_gid', '0', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.st_blksize', '4096', 'i32') }}} - - // Variables. - {{{ makeSetValue('buf', 'offsets.st_ino', 'obj.inodeNumber', 'i32') }}} - var time = Math.floor(obj.timestamp / 1000); - if (offsets.st_atime === undefined) { - offsets.st_atime = offsets.st_atim.tv_sec; - offsets.st_mtime = offsets.st_mtim.tv_sec; - offsets.st_ctime = offsets.st_ctim.tv_sec; - var nanosec = (obj.timestamp % 1000) * 1000; - {{{ makeSetValue('buf', 'offsets.st_atim.tv_nsec', 'nanosec', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.st_mtim.tv_nsec', 'nanosec', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.st_ctim.tv_nsec', 'nanosec', 'i32') }}} - } - {{{ makeSetValue('buf', 'offsets.st_atime', 'time', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.st_mtime', 'time', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.st_ctime', 'time', 'i32') }}} - var mode = 0; - var size = 0; - var blocks = 0; - var dev = 0; - var rdev = 0; - if (obj.isDevice) { - // Device numbers reuse inode numbers. - dev = rdev = obj.inodeNumber; - size = blocks = 0; - mode = 0x2000; // S_IFCHR. - } else { - dev = 1; - rdev = 0; - // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), - // but this is not required by the standard. - if (obj.isFolder) { - size = 4096; - blocks = 1; - mode = 0x4000; // S_IFDIR. - } else { - var data = obj.contents || obj.link; - size = data.length; - blocks = Math.ceil(data.length / 4096); - mode = obj.link === undefined ? 0x8000 : 0xA000; // S_IFREG, S_IFLNK. - } - } - {{{ makeSetValue('buf', 'offsets.st_dev', 'dev', 'i32') }}}; - {{{ makeSetValue('buf', 'offsets.st_rdev', 'rdev', 'i32') }}}; - {{{ makeSetValue('buf', 'offsets.st_size', 'size', 'i32') }}} - {{{ makeSetValue('buf', 'offsets.st_blocks', 'blocks', 'i32') }}} - if (obj.read) mode |= 0x16D; // S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH. - if (obj.write) mode |= 0x92; // S_IWUSR | S_IWGRP | S_IWOTH. - {{{ makeSetValue('buf', 'offsets.st_mode', 'mode', 'i32') }}} - return 0; -#else try { var stat = dontResolveLastLink ? VFS.lstat(path) : VFS.stat(path); {{{ makeSetValue('buf', '___stat_struct_layout.st_dev', 'stat.dev', 'i32') }}}; @@ -2718,7 +2010,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, lstat__deps: ['stat'], lstat: function(path, buf) { @@ -2742,31 +2033,6 @@ LibraryManager.library = { // int mknod(const char *path, mode_t mode, dev_t dev); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mknod.html path = Pointer_stringify(path); -#if USE_OLD_FS - var fmt = (mode & {{{ cDefine('S_IFMT') }}}); - if (fmt !== {{{ cDefine('S_IFREG') }}} && fmt !== {{{ cDefine('S_IFCHR') }}} && - fmt !== {{{ cDefine('S_IFBLK') }}} && fmt !== {{{ cDefine('S_IFIFO') }}} && - fmt !== {{{ cDefine('S_IFSOCK') }}}) { - // not valid formats for mknod - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - if (fmt === {{{ cDefine('S_IFCHR') }}} || fmt === {{{ cDefine('S_IFBLK') }}} || - fmt === {{{ cDefine('S_IFIFO') }}} || fmt === {{{ cDefine('S_IFSOCK') }}}) { - // not supported currently - ___setErrNo(ERRNO_CODES.EPERM); - return -1; - } - path = FS.analyzePath(path); - var properties = { contents: [], isFolder: false }; // S_IFDIR. - try { - FS.createObject(path.parentObject, path.name, properties, - mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. - return 0; - } catch (e) { - return -1; - } -#else // we don't want this in the JS API as the JS API // uses mknod to create all nodes. var err = FS.mayMknod(mode); @@ -2781,24 +2047,12 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, mkdir__deps: ['mknod'], mkdir: function(path, mode) { // int mkdir(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkdir.html path = Pointer_stringify(path); -#if USE_OLD_FS - path = FS.analyzePath(path); - var properties = { contents: [], isFolder: true }; - try { - FS.createObject(path.parentObject, path.name, properties, - mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. - return 0; - } catch (e) { - return -1; - } -#else try { VFS.mkdir(path, mode, 0); return 0; @@ -2806,7 +2060,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, mkfifo__deps: ['__setErrNo', '$ERRNO_CODES'], mkfifo: function(path, mode) { @@ -2826,14 +2079,6 @@ LibraryManager.library = { // NOTE: dontResolveLastLink is a shortcut for lchmod(). It should never be // used in client code. path = typeof path !== 'string' ? Pointer_stringify(path) : path; -#if USE_OLD_FS - var obj = FS.findObject(path, dontResolveLastLink); - if (obj === null) return -1; - obj.read = mode & 0x100; // S_IRUSR. - obj.write = mode & 0x80; // S_IWUSR. - obj.timestamp = Date.now(); - return 0; -#else try { VFS.chmod(path, mode); return 0; @@ -2841,20 +2086,11 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, fchmod__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chmod'], fchmod: function(fildes, mode) { // int fchmod(int fildes, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fchmod.html -#if USE_OLD_FS - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - return _chmod(stream.path, mode); -#else try { VFS.fchmod(fildes, mode); return 0; @@ -2862,14 +2098,10 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, lchmod__deps: ['chmod'], lchmod: function (path, mode) { path = Pointer_stringify(path); -#if USE_OLD_FS - return _chmod(path, mode, true); -#else try { VFS.lchmod(path, mode); return 0; @@ -2877,7 +2109,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, umask__deps: ['$FS'], @@ -2962,108 +2193,6 @@ LibraryManager.library = { // NOTE: This implementation tries to mimic glibc rather than strictly // following the POSIX standard. var mode = {{{ makeGetValue('varargs', 0, 'i32') }}}; - -#if USE_OLD_FS - // Simplify flags. - var accessMode = oflag & {{{ cDefine('O_ACCMODE') }}}; - var isWrite = accessMode != {{{ cDefine('O_RDONLY') }}}; - var isRead = accessMode != {{{ cDefine('O_WRONLY') }}}; - var isCreate = Boolean(oflag & {{{ cDefine('O_CREAT') }}}); - var isExistCheck = Boolean(oflag & {{{ cDefine('O_EXCL') }}}); - var isTruncate = Boolean(oflag & {{{ cDefine('O_TRUNC') }}}); - var isAppend = Boolean(oflag & {{{ cDefine('O_APPEND') }}}); - - // Verify path. - var origPath = path; - path = FS.analyzePath(Pointer_stringify(path)); - if (!path.parentExists) { - ___setErrNo(path.error); - return -1; - } - var target = path.object || null; - var finalPath; - - // Verify the file exists, create if needed and allowed. - if (target) { - if (isCreate && isExistCheck) { - ___setErrNo(ERRNO_CODES.EEXIST); - return -1; - } - if ((isWrite || isTruncate) && target.isFolder) { - ___setErrNo(ERRNO_CODES.EISDIR); - return -1; - } - if (isRead && !target.read || isWrite && !target.write) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } - if (isTruncate && !target.isDevice) { - target.contents = []; - } else { - if (!FS.forceLoadFile(target)) { - ___setErrNo(ERRNO_CODES.EIO); - return -1; - } - } - finalPath = path.path; - } else { - if (!isCreate) { - ___setErrNo(ERRNO_CODES.ENOENT); - return -1; - } - if (!path.parentObject.write) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } - target = FS.createDataFile(path.parentObject, path.name, [], - mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. - finalPath = path.parentPath + '/' + path.name; - } - // Actually create an open stream. - var stream; - if (target.isFolder) { - var entryBuffer = 0; - if (___dirent_struct_layout) { - entryBuffer = _malloc(___dirent_struct_layout.__size__); - } - var contents = []; - for (var key in target.contents) contents.push(key); - stream = FS.createStream({ - path: finalPath, - object: target, - // An index into contents. Special values: -2 is ".", -1 is "..". - position: -2, - isRead: true, - isWrite: false, - isAppend: false, - error: false, - eof: false, - ungotten: [], - // Folder-specific properties: - // Remember the contents at the time of opening in an array, so we can - // seek between them relying on a single order. - contents: contents, - // Each stream has its own area for readdir() returns. - currentEntry: entryBuffer - }); - } else { - stream = FS.createStream({ - path: finalPath, - object: target, - position: 0, - isRead: isRead, - isWrite: isWrite, - isAppend: isAppend, - error: false, - eof: false, - ungotten: [] - }); - } -#if ASSERTIONS - FS.checkStreams(); -#endif - return stream.fd; -#else path = Pointer_stringify(path); try { var stream = VFS.open(path, oflag, mode); @@ -3072,7 +2201,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, creat__deps: ['open'], creat: function(path, mode) { @@ -3106,48 +2234,21 @@ LibraryManager.library = { return -1; } var newStream; -#if USE_OLD_FS - newStream = {}; - for (var member in stream) { - newStream[member] = stream[member]; - } - arg = dup2 ? arg : Math.max(arg, FS.streams.length); // dup2 wants exactly arg; fcntl wants a free descriptor >= arg - FS.createStream(newStream, arg); -#else try { newStream = VFS.open(stream.path, stream.flags, 0, arg); } catch (e) { ___setErrNo(e.errno); return -1; } -#endiif -#if ASSERTIONS - FS.checkStreams(); -#endif return newStream.fd; case {{{ cDefine('F_GETFD') }}}: case {{{ cDefine('F_SETFD') }}}: return 0; // FD_CLOEXEC makes no sense for a single process. case {{{ cDefine('F_GETFL') }}}: -#if USE_OLD_FS - var flags = 0; - if (stream.isRead && stream.isWrite) flags = {{{ cDefine('O_RDWR') }}}; - else if (!stream.isRead && stream.isWrite) flags = {{{ cDefine('O_WRONLY') }}}; - else if (stream.isRead && !stream.isWrite) flags = {{{ cDefine('O_RDONLY') }}}; - if (stream.isAppend) flags |= {{{ cDefine('O_APPEND') }}}; - // Synchronization and blocking flags are irrelevant to us. - return flags; -#else return stream.flags; -#endif case {{{ cDefine('F_SETFL') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; -#if USE_OLD_FS - stream.isAppend = Boolean(arg | {{{ cDefine('O_APPEND') }}}); - // Synchronization and blocking flags are irrelevant to us. -#else stream.flags |= arg; -#endif return 0; case {{{ cDefine('F_GETLK') }}}: case {{{ cDefine('F_GETLK64') }}}: @@ -3186,20 +2287,10 @@ LibraryManager.library = { // int posix_fallocate(int fd, off_t offset, off_t len); // http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fallocate.html var stream = FS.getStream(fd); -#if USE_OLD_FS - if (!stream || !stream.isWrite || stream.link || stream.isFolder || stream.isDevice) { -#else if (!stream) { -#endif ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS - var contents = stream.object.contents; - var limit = offset + len; - while (limit > contents.length) contents.push(0); - return 0; -#else try { VFS.allocate(stream, offset, len); return 0; @@ -3207,7 +2298,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, // ========================================================================== @@ -3262,17 +2352,6 @@ LibraryManager.library = { // int access(const char *path, int amode); // http://pubs.opengroup.org/onlinepubs/000095399/functions/access.html path = Pointer_stringify(path); -#if USE_OLD_FS - var target = FS.findObject(path); - if (target === null) return -1; - if ((amode & 2 && !target.write) || // W_OK. - ((amode & 1 || amode & 4) && !target.read)) { // X_OK, R_OK. - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else { - return 0; - } -#else if (amode & ~{{{ cDefine('S_IRWXO') }}}) { // need a valid mode ___setErrNo(ERRNO_CODES.EINVAL); @@ -3295,7 +2374,6 @@ LibraryManager.library = { return -1; } return 0; -#endif }, chdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], chdir: function(path) { @@ -3303,19 +2381,6 @@ 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); -#if USE_OLD_FS - path = FS.analyzePath(path); - if (!path.exists) { - ___setErrNo(path.error); - return -1; - } else if (!path.object.isFolder) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - return -1; - } else { - FS.currentPath = path.path; - return 0; - } -#else var lookup; try { lookup = FS.lookupPath(path, { follow: true }); @@ -3334,7 +2399,6 @@ LibraryManager.library = { } FS.currentPath = lookup.path; return 0; -#endif }, chown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], chown: function(path, owner, group, dontResolveLastLink) { @@ -3345,12 +2409,6 @@ LibraryManager.library = { // NOTE: dontResolveLastLink is a shortcut for lchown(). It should never be // used in client code. if (typeof path !== 'string') path = Pointer_stringify(path); -#if USE_OLD_FS - var target = FS.findObject(path, dontResolveLastLink); - if (target === null) return -1; - target.timestamp = Date.now(); - return 0; -#else try { VFS.chown(path, owner, group); return 0; @@ -3358,7 +2416,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, chroot__deps: ['__setErrNo', '$ERRNO_CODES'], chroot: function(path) { @@ -3376,13 +2433,6 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS - if (stream.currentEntry) { - _free(stream.currentEntry); - } - FS.closeStream(stream); - return 0; -#else try { VFS.close(stream); return 0; @@ -3390,7 +2440,6 @@ LibraryManager.library = { ___setErrNo(e.errno);; return -1; } -#endif }, dup__deps: ['fcntl'], dup: function(fildes) { @@ -3410,9 +2459,6 @@ LibraryManager.library = { return fildes; } else { _close(fildes2); -#if USE_OLD_FS - return _fcntl(fildes, 0, allocate([fildes2, 0, 0, 0], 'i32', ALLOC_STACK), true); // F_DUPFD. -#else try { var stream2 = VFS.open(stream.path, stream.flags, 0, fildes2, fildes2); return stream2.fd; @@ -3420,21 +2466,12 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif } }, fchown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chown'], fchown: function(fildes, owner, group) { // int fchown(int fildes, uid_t owner, gid_t group); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fchown.html -#if USE_OLD_FS - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - return _chown(stream.path, owner, group); -#else try { VFS.fchown(fildes, owner, group); return 0; @@ -3442,7 +2479,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, fchdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chdir'], fchdir: function(fildes) { @@ -3539,30 +2575,6 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/truncate.html // NOTE: The path argument may be a string, to simplify ftruncate(). if (typeof path !== 'string') path = Pointer_stringify(path); -#if USE_OLD_FS - if (length < 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else { - var target = FS.findObject(path); - if (target === null) return -1; - if (target.isFolder) { - ___setErrNo(ERRNO_CODES.EISDIR); - return -1; - } else if (target.isDevice) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else if (!target.write) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else { - var contents = target.contents; - if (length < contents.length) contents.length = length; - else while (length > contents.length) contents.push(0); - target.timestamp = Date.now(); - return 0; - } -#else try { VFS.truncate(path, length); return 0; @@ -3570,24 +2582,11 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, ftruncate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'truncate'], ftruncate: function(fildes, length) { // int ftruncate(int fildes, off_t length); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftruncate.html -#if USE_OLD_FS - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - if (!stream.isWrite) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - return _truncate(stream.path, length); -#else try { VFS.ftruncate(fildes, length); return 0; @@ -3595,7 +2594,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, getcwd__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], getcwd: function(buf, size) { @@ -3630,12 +2628,8 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return 0; } -#if USE_OLD_FS - if (!stream.object.isTerminal) { -#else // HACK - implement tcgetattr if (!stream.tty) { -#endif ___setErrNo(ERRNO_CODES.ENOTTY); return 0; } @@ -3674,37 +2668,16 @@ LibraryManager.library = { // off_t lseek(int fildes, off_t offset, int whence); // http://pubs.opengroup.org/onlinepubs/000095399/functions/lseek.html var stream = FS.getStream(fildes); -#if USE_OLD_FS - if (!stream || stream.object.isDevice) { -#else if (!stream) { -#endif ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS - var position = offset; - if (whence === 1) { // SEEK_CUR. - position += stream.position; - } else if (whence === 2) { // SEEK_END. - position += stream.object.contents.length; - } - if (position < 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else { - stream.ungotten = []; - stream.position = position; - return position; - } -#else try { return VFS.llseek(stream, offset, whence); } catch (e) { ___setErrNo(e.errno); return -1; } -#endif }, pipe__deps: ['__setErrNo', '$ERRNO_CODES'], pipe: function(fildes) { @@ -3724,45 +2697,6 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS - if (stream.object.isDevice) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } else if (!stream.isRead) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else if (stream.object.isFolder) { - ___setErrNo(ERRNO_CODES.EISDIR); - return -1; - } else if (nbyte < 0 || offset < 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else if (offset >= stream.object.contents.length) { - return 0; - } else { - var bytesRead = 0; - var contents = stream.object.contents; - var size = Math.min(contents.length - offset, nbyte); - assert(size >= 0); - -#if USE_TYPED_ARRAYS == 2 - if (contents.subarray) { // typed array - HEAPU8.set(contents.subarray(offset, offset+size), buf); - } else -#endif - if (contents.slice) { // normal array - for (var i = 0; i < size; i++) { - {{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}} - } - } else { - for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR - {{{ makeSetValue('buf', 'i', 'contents.get(offset + i)', 'i8') }}} - } - } - bytesRead += size; - return bytesRead; - } -#else try { var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; return VFS.read(stream, slab, buf, nbyte, offset); @@ -3770,7 +2704,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'recv', 'pread'], read: function(fildes, buf, nbyte) { @@ -3781,50 +2714,11 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS + if (stream && ('socket' in stream)) { return _recv(fildes, buf, nbyte, 0); - } else if (!stream.isRead) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else if (nbyte < 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else { - var bytesRead; - if (stream.object.isDevice) { - if (stream.object.input) { - bytesRead = 0; - for (var i = 0; i < nbyte; i++) { - try { - var result = stream.object.input(); - } catch (e) { - ___setErrNo(ERRNO_CODES.EIO); - return -1; - } - if (result === undefined && bytesRead === 0) { - ___setErrNo(ERRNO_CODES.EAGAIN); - return -1; - } - if (result === null || result === undefined) break; - bytesRead++; - {{{ makeSetValue('buf', 'i', 'result', 'i8') }}} - } - return bytesRead; - } else { - ___setErrNo(ERRNO_CODES.ENXIO); - return -1; - } - } else { - bytesRead = _pread(fildes, buf, nbyte, stream.position); - assert(bytesRead >= -1); - if (bytesRead != -1) { - stream.position += bytesRead; - } - return bytesRead; - } } -#else + try { var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; return VFS.read(stream, slab, buf, nbyte); @@ -3832,7 +2726,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, sync: function() { // void sync(void); @@ -3844,29 +2737,6 @@ LibraryManager.library = { // int rmdir(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rmdir.html path = Pointer_stringify(path); -#if USE_OLD_FS - path = FS.analyzePath(path, true); - if (!path.parentExists || !path.exists) { - ___setErrNo(path.error); - return -1; - } else if (!path.parentObject.write) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else if (!path.object.isFolder) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - return -1; - } else if (path.isRoot || path.path == FS.currentPath) { - ___setErrNo(ERRNO_CODES.EBUSY); - return -1; - } else { - for (var i in path.object.contents) { - ___setErrNo(ERRNO_CODES.ENOTEMPTY); - return -1; - } - delete path.parentObject.contents[path.name]; - return 0; - } -#else try { VFS.rmdir(path); return 0; @@ -3874,29 +2744,12 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, unlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], unlink: function(path) { // int unlink(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/unlink.html path = Pointer_stringify(path); -#if USE_OLD_FS - path = FS.analyzePath(path, true); - if (!path.parentExists || !path.exists) { - ___setErrNo(path.error); - return -1; - } else if (path.object.isFolder) { - ___setErrNo(ERRNO_CODES.EPERM); - return -1; - } else if (!path.parentObject.write) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else { - delete path.parentObject.contents[path.name]; - return 0; - } -#else try { VFS.unlink(path); return 0; @@ -3904,7 +2757,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, ttyname__deps: ['ttyname_r'], ttyname: function(fildes) { @@ -3935,20 +2787,6 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/symlink.html path1 = Pointer_stringify(path1); path2 = Pointer_stringify(path2); -#if USE_OLD_FS - var path = FS.analyzePath(path2, true); - if (!path.parentExists) { - ___setErrNo(path.error); - return -1; - } else if (path.exists) { - ___setErrNo(ERRNO_CODES.EEXIST); - return -1; - } else { - FS.createLink(path.parentPath, path.name, - path1, true, true); - return 0; - } -#else try { VFS.symlink(path1, path2); return 0; @@ -3956,28 +2794,12 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, readlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], readlink: function(path, buf, bufsize) { // ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize); // http://pubs.opengroup.org/onlinepubs/000095399/functions/readlink.html path = Pointer_stringify(path); -#if USE_OLD_FS - var target = FS.findObject(path, true); - if (target === null) return -1; - if (target.link !== undefined) { - var length = Math.min(bufsize - 1, target.link.length); - for (var i = 0; i < length; i++) { - {{{ makeSetValue('buf', 'i', 'target.link.charCodeAt(i)', 'i8') }}} - } - if (bufsize - 1 > length) {{{ makeSetValue('buf', 'i', '0', 'i8') }}} - return i; - } else { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } -#else var str; try { str = VFS.readlink(path); @@ -3988,7 +2810,6 @@ LibraryManager.library = { str = str.slice(0, Math.max(0, bufsize - 1)); writeStringToMemory(str, buf, true); return str.length; -#endif }, pwrite__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], pwrite: function(fildes, buf, nbyte, offset) { @@ -3999,29 +2820,6 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS - if (stream.object.isDevice) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } else if (!stream.isWrite) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else if (stream.object.isFolder) { - ___setErrNo(ERRNO_CODES.EISDIR); - return -1; - } else if (nbyte < 0 || offset < 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else { - var contents = stream.object.contents; - while (contents.length < offset) contents.push(0); - for (var i = 0; i < nbyte; i++) { - contents[offset + i] = {{{ makeGetValue('buf', 'i', 'i8', undefined, 1) }}}; - } - stream.object.timestamp = Date.now(); - return i; - } -#else try { var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; return VFS.write(stream, slab, buf, nbyte, offset); @@ -4029,7 +2827,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, write__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'send', 'pwrite'], write: function(fildes, buf, nbyte) { @@ -4040,39 +2837,11 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS + if (stream && ('socket' in stream)) { return _send(fildes, buf, nbyte, 0); - } else if (!stream.isWrite) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else if (nbyte < 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else { - if (stream.object.isDevice) { - if (stream.object.output) { - for (var i = 0; i < nbyte; i++) { - try { - stream.object.output({{{ makeGetValue('buf', 'i', 'i8') }}}); - } catch (e) { - ___setErrNo(ERRNO_CODES.EIO); - return -1; - } - } - stream.object.timestamp = Date.now(); - return i; - } else { - ___setErrNo(ERRNO_CODES.ENXIO); - return -1; - } - } else { - var bytesWritten = _pwrite(fildes, buf, nbyte, stream.position); - if (bytesWritten != -1) stream.position += bytesWritten; - return bytesWritten; - } } -#else + try { var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; return VFS.write(stream, slab, buf, nbyte); @@ -4080,7 +2849,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, alarm: function(seconds) { // unsigned alarm(unsigned seconds); @@ -5228,29 +3996,7 @@ LibraryManager.library = { fflush: function(stream) { // int fflush(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fflush.html -#if USE_OLD_FS - var flush = function(filedes) { - // Right now we write all data directly, except for output devices. - var streamObj = FS.getStream(filedes); - if (streamObj && streamObj.object.output) { - if (!streamObj.object.isTerminal) { // don't flush terminals, it would cause a \n to also appear - streamObj.object.output(null); - } - } - }; - try { - if (stream === 0) { - for (var i = 0; i < FS.streams.length; i++) if (FS.streams[i]) flush(i); - } else { - flush(stream); - } - return 0; - } catch (e) { - ___setErrNo(ERRNO_CODES.EIO); - return -1; - } -#else -#endif + // we don't currently perform any user-space buffering of data }, fgetc__deps: ['$FS', 'fread'], fgetc__postset: '_fgetc.ret = allocate([0], "i8", ALLOC_STATIC);', @@ -5288,11 +4034,7 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS - if (stream.object.isDevice) { -#else if (FS.isChrdev(stream.node.mode)) { -#endif ___setErrNo(ERRNO_CODES.ESPIPE); return -1; } @@ -5484,11 +4226,7 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS - if (stream.object.isDevice) { -#else if (FS.isChrdev(stream.node.mode)) { -#endif ___setErrNo(ERRNO_CODES.EPIPE); return -1; } @@ -5507,11 +4245,7 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EBADF); return -1; } -#if USE_OLD_FS - if (stream.object.isDevice) { -#else if (FS.isChrdev(stream.node.mode)) { -#endif ___setErrNo(ERRNO_CODES.ESPIPE); return -1; } else { @@ -5578,30 +4312,6 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/000095399/functions/rename.html old_path = Pointer_stringify(old_path); new_path = Pointer_stringify(new_path); -#if USE_OLD_FS - var oldObj = FS.analyzePath(old_path); - var newObj = FS.analyzePath(new_path); - if (newObj.path == oldObj.path) { - return 0; - } else if (!oldObj.exists) { - ___setErrNo(oldObj.error); - return -1; - } else if (oldObj.isRoot || oldObj.path == FS.currentPath) { - ___setErrNo(ERRNO_CODES.EBUSY); - return -1; - } else if (newObj.parentPath && - newObj.parentPath.indexOf(oldObj.path) == 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else if (newObj.exists && newObj.object.isFolder) { - ___setErrNo(ERRNO_CODES.EISDIR); - return -1; - } else { - delete oldObj.parentObject.contents[oldObj.name]; - newObj.parentObject.contents[newObj.name] = oldObj.object; - return 0; - } -#else try { VFS.rename(old_path, new_path); return 0; @@ -5609,7 +4319,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif }, rewind__deps: ['$FS', 'fseek'], rewind: function(stream) { @@ -5867,29 +4576,6 @@ LibraryManager.library = { } else { var info = FS.getStream(stream); if (!info) return -1; -#if USE_OLD_FS - var contents = info.object.contents; - // Only make a new copy when MAP_PRIVATE is specified. - if (flags & MAP_PRIVATE == 0) { - // We can't emulate MAP_SHARED when the file is not backed by HEAP. - assert(contents.buffer === HEAPU8.buffer); - ptr = contents.byteOffset; - allocated = false; - } else { - // Try to avoid unnecessary slices. - if (offset > 0 || offset + num < contents.length) { - if (contents.subarray) { - contents = contents.subarray(offset, offset+num); - } else { - contents = Array.prototype.slice.call(contents, offset, offset+num); - } - } - ptr = _malloc(num); - if (!ptr) return -1; - HEAPU8.set(contents, ptr); - allocated = true; - } -#else try { var res = VFS.mmap(info, HEAPU8, start, num, offset, prot, flags); ptr = res.ptr; @@ -5898,7 +4584,6 @@ LibraryManager.library = { ___setErrNo(e.errno); return -1; } -#endif } _mmap.mappings[ptr] = { malloc: ptr, num: num, allocated: allocated }; diff --git a/src/settings.js b/src/settings.js index b35f9b24..19108f3b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -249,8 +249,6 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r // a new project and want to see a list of file system operations happening // so that you can create a virtual file system with all of the required files. -var USE_OLD_FS = 1; // Switch to toggle the new / old FS code. Currently only used for testing purposes. - var USE_BSS = 1; // https://en.wikipedia.org/wiki/.bss // When enabled, 0-initialized globals are sorted to the end of the globals list, // enabling us to not explicitly store the initialization value for each 0 byte. diff --git a/tests/runner.py b/tests/runner.py index b57b209a..76841026 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -3591,7 +3591,6 @@ Exiting setjmp function, level: 0, prev_jmp: -1 self.do_run(src, 'z:1*', force_c=True) def test_rename(self): - Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)] src = open(path_from_root('tests', 'stdio', 'test_rename.c'), 'r').read() self.do_run(src, 'success', force_c=True) @@ -7220,17 +7219,14 @@ def process(filename): self.do_run(src, 'success', force_c=True) def test_stat(self): - Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)] src = open(path_from_root('tests', 'stat', 'test_stat.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_stat_chmod(self): - Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)] src = open(path_from_root('tests', 'stat', 'test_chmod.c'), 'r').read() self.do_run(src, 'success', force_c=True) def test_stat_mknod(self): - Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)] src = open(path_from_root('tests', 'stat', 'test_mknod.c'), 'r').read() self.do_run(src, 'success', force_c=True) diff --git a/tests/stat/test_chmod.c b/tests/stat/test_chmod.c index 94e6c12b..cb84e028 100644 --- a/tests/stat/test_chmod.c +++ b/tests/stat/test_chmod.c @@ -55,11 +55,7 @@ void test() { memset(&s, 0, sizeof s); stat("file", &s); -#if USE_OLD_FS - assert(s.st_mode == (0222 | S_IFREG)); -#else assert(s.st_mode == (0200 | S_IFREG)); -#endif assert(s.st_ctime != lastctime); // @@ -73,11 +69,7 @@ void test() { memset(&s, 0, sizeof s); stat("file", &s); -#if USE_OLD_FS - assert(s.st_mode == (0000 | S_IFREG)); -#else assert(s.st_mode == (0100 | S_IFREG)); -#endif assert(s.st_ctime != lastctime); // @@ -94,11 +86,7 @@ void test() { assert(!err); memset(&s, 0, sizeof s); stat("folder", &s); -#if USE_OLD_FS - assert(s.st_mode == (0222 | S_IFDIR)); -#else assert(s.st_mode == (0300 | S_IFDIR)); -#endif assert(s.st_ctime != lastctime); // @@ -109,11 +97,7 @@ void test() { // make sure the file it references changed stat("file-link", &s); -#if USE_OLD_FS - assert(s.st_mode == (0555 | S_IFREG)); -#else assert(s.st_mode == (0400 | S_IFREG)); -#endif // but the link didn't lstat("file-link", &s); @@ -127,19 +111,11 @@ void test() { // make sure the file it references didn't change stat("file-link", &s); -#if USE_OLD_FS - assert(s.st_mode == (0555 | S_IFREG)); -#else assert(s.st_mode == (0400 | S_IFREG)); -#endif // but the link did lstat("file-link", &s); -#if USE_OLD_FS - assert(s.st_mode == (0555 | S_IFLNK)); -#else assert(s.st_mode == (0500 | S_IFLNK)); -#endif puts("success"); } diff --git a/tests/stat/test_mknod.c b/tests/stat/test_mknod.c index 4cff57d9..be1d0c39 100644 --- a/tests/stat/test_mknod.c +++ b/tests/stat/test_mknod.c @@ -52,15 +52,10 @@ void test() { // mknod a character device err = mknod("mknod-device", S_IFCHR | 0777, 123); -#if USE_OLD_FS - assert(err); - assert(errno == EPERM); -#else assert(!err); memset(&s, 0, sizeof s); stat("mknod-device", &s); assert(S_ISCHR(s.st_mode)); -#endif #endif diff --git a/tests/stat/test_stat.c b/tests/stat/test_stat.c index 14e88370..6e220eb9 100644 --- a/tests/stat/test_stat.c +++ b/tests/stat/test_stat.c @@ -103,13 +103,10 @@ void test() { assert(s.st_ino); assert(S_ISCHR(s.st_mode)); assert(s.st_nlink); -#if !USE_OLD_FS - // old FS doesn't store proper device ids #ifndef __APPLE__ // mac uses makedev(3, 2) for /dev/null assert(s.st_rdev == makedev(1, 3)); #endif -#endif assert(!s.st_size); assert(s.st_atime); assert(s.st_mtime); diff --git a/tests/stdio/test_rename.c b/tests/stdio/test_rename.c index ddbcd339..20ffb513 100644 --- a/tests/stdio/test_rename.c +++ b/tests/stdio/test_rename.c @@ -56,7 +56,6 @@ void test() { assert(err == -1); assert(errno == EISDIR); -#if !USE_OLD_FS // can't overwrite a file with a folder err = rename("dir", "file"); assert(err == -1); @@ -71,19 +70,16 @@ void test() { err = rename("dir", "dir-readonly/dir"); assert(err == -1); assert(errno == EACCES); -#endif // source should not be ancestor of target err = rename("dir", "dir/somename"); assert(err == -1); assert(errno == EINVAL); -#if !USE_OLD_FS // target should not be an ancestor of source err = rename("dir/subdir", "dir"); assert(err == -1); assert(errno == ENOTEMPTY); -#endif // do some valid renaming err = rename("dir/file", "dir/file1"); |