UNPKG

mock-fs-require-fix

Version:

Fork of the tschaub/mock-fs project.

1,046 lines (939 loc) 31.5 kB
'use strict'; var path = require('path'); var File = require('./file'); var FileDescriptor = require('./descriptor'); var Directory = require('./directory'); var SymbolicLink = require('./symlink'); var FSError = require('./error'); var constants = require('constants'); var getPathParts = require('./filesystem').getPathParts; /** * Call the provided function and either return the result or call the callback * with it (depending on if a callback is provided). * @param {function()} callback Optional callback. * @param {Object} thisArg This argument for the following function. * @param {function()} func Function to call. * @return {*} Return (if callback is not provided). */ function maybeCallback(callback, thisArg, func) { if (callback && (typeof callback === 'function' || typeof callback.oncomplete === 'function')) { var err = null; var val; try { val = func.call(thisArg); } catch (e) { err = e; } // Unpack callback from FSReqWrap if (callback.oncomplete) { callback = callback.oncomplete.bind(callback); } process.nextTick(function() { if (val === undefined) { callback(err); } else { callback(err, val); } }); } else { return func.call(thisArg); } } function notImplemented() { throw new Error('Method not implemented'); } /** * Create a new stats object. * @param {Object} config Stats properties. * @constructor */ function Stats(config) { for (var key in config) { this[key] = config[key]; } } /** * Check if mode indicates property. * @param {number} property Property to check. * @return {boolean} Property matches mode. */ Stats.prototype._checkModeProperty = function(property) { return ((this.mode & constants.S_IFMT) === property); }; /** * @return {Boolean} Is a directory. */ Stats.prototype.isDirectory = function() { return this._checkModeProperty(constants.S_IFDIR); }; /** * @return {Boolean} Is a regular file. */ Stats.prototype.isFile = function() { return this._checkModeProperty(constants.S_IFREG); }; /** * @return {Boolean} Is a block device. */ Stats.prototype.isBlockDevice = function() { return this._checkModeProperty(constants.S_IFBLK); }; /** * @return {Boolean} Is a character device. */ Stats.prototype.isCharacterDevice = function() { return this._checkModeProperty(constants.S_IFCHR); }; /** * @return {Boolean} Is a symbolic link. */ Stats.prototype.isSymbolicLink = function() { return this._checkModeProperty(constants.S_IFLNK); }; /** * @return {Boolean} Is a named pipe. */ Stats.prototype.isFIFO = function() { return this._checkModeProperty(constants.S_IFIFO); }; /** * @return {Boolean} Is a socket. */ Stats.prototype.isSocket = function() { return this._checkModeProperty(constants.S_IFSOCK); }; /** * Create a new binding with the given file system. * @param {FileSystem} system Mock file system. * @constructor */ function Binding(system) { /** * Mock file system. * @type {FileSystem} */ this._system = system; /** * Stats constructor. * @type {function} */ this.Stats = Stats; /** * Lookup of open files. * @type {Object.<number, FileDescriptor>} */ this._openFiles = {}; /** * Counter for file descriptors. * @type {number} */ this._counter = 0; } /** * Get the file system underlying this binding. * @return {FileSystem} The underlying file system. */ Binding.prototype.getSystem = function() { return this._system; }; /** * Reset the file system underlying this binding. * @param {FileSystem} system The new file system. */ Binding.prototype.setSystem = function(system) { this._system = system; }; /** * Get a file descriptor. * @param {number} fd File descriptor identifier. * @return {FileDescriptor} File descriptor. */ Binding.prototype._getDescriptorById = function(fd) { if (!this._openFiles.hasOwnProperty(fd)) { throw new FSError('EBADF'); } return this._openFiles[fd]; }; /** * Keep track of a file descriptor as open. * @param {FileDescriptor} descriptor The file descriptor. * @return {number} Identifier for file descriptor. */ Binding.prototype._trackDescriptor = function(descriptor) { var fd = ++this._counter; this._openFiles[fd] = descriptor; return fd; }; /** * Stop tracking a file descriptor as open. * @param {number} fd Identifier for file descriptor. */ Binding.prototype._untrackDescriptorById = function(fd) { if (!this._openFiles.hasOwnProperty(fd)) { throw new FSError('EBADF'); } delete this._openFiles[fd]; }; /** * Resolve the canonicalized absolute pathname. * @param {string|Buffer} filepath The file path. * @param {string} encoding The encoding for the return. * @return {string|Buffer} The real path. */ Binding.prototype.realpath = function(filepath, encoding, callback) { return maybeCallback(callback, this, function() { var realPath; if (Buffer.isBuffer(filepath)) { filepath = filepath.toString(); } var resolved = path.resolve(filepath); var parts = getPathParts(resolved); var item = this._system.getRoot(); var itemPath = '/'; var name, i, ii; for (i = 0, ii = parts.length; i < ii; ++i) { name = parts[i]; while (item instanceof SymbolicLink) { itemPath = path.resolve(path.dirname(itemPath), item.getPath()); item = this._system.getItem(itemPath); } if (!item) { throw new FSError('ENOENT', filepath); } if (item instanceof Directory) { itemPath = path.resolve(itemPath, name); item = item.getItem(name); } else { throw new FSError('ENOTDIR', filepath); } } if (item) { while (item instanceof SymbolicLink) { itemPath = path.resolve(path.dirname(itemPath), item.getPath()); item = this._system.getItem(itemPath); } realPath = itemPath; } else { throw new FSError('ENOENT', filepath); } if (encoding === 'buffer') { realPath = new Buffer(realPath); } return realPath; }); }; /** * Fill a Float64Array with stat information * This is based on the internal FillStatsArray function in Node. * https://github.com/nodejs/node/blob/4e05952a8a75af6df625415db612d3a9a1322682/src/node_file.cc#L533 * @param {object} stats An object with file stats * @param {Float64Array} statValues A Float64Array where stat values should be inserted * @returns {void} */ function fillStatsArray(stats, statValues) { statValues[0] = stats.dev; statValues[1] = stats.mode; statValues[2] = stats.nlink; statValues[3] = stats.uid; statValues[4] = stats.gid; statValues[5] = stats.rdev; statValues[6] = stats.blksize; statValues[7] = stats.ino; statValues[8] = stats.size; statValues[9] = stats.blocks; statValues[10] = +stats.atime; statValues[11] = +stats.mtime; statValues[12] = +stats.ctime; statValues[13] = +stats.birthtime; } /** * Stat an item. * @param {string} filepath Path. * @param {function(Error, Stats)|Float64Array} callback Callback (optional). In Node 7.7.0+ this will be a Float64Array * that should be filled with stat values. * @return {Stats|undefined} Stats or undefined (if sync). */ Binding.prototype.stat = function(filepath, callback) { return maybeCallback(callback, this, function() { var item = this._system.getItem(filepath); if (item instanceof SymbolicLink) { item = this._system.getItem( path.resolve(path.dirname(filepath), item.getPath())); } if (!item) { throw new FSError('ENOENT', filepath); } var stats = item.getStats(); // In Node 7.7.0+, binding.stat accepts a Float64Array as the second argument, // which should be filled with stat values. // In prior versions of Node, binding.stat simply returns a Stats instance. if (callback instanceof Float64Array) { fillStatsArray(stats, callback); } else { return new Stats(stats); } }); }; /** * Stat an item. * @param {number} fd File descriptor. * @param {function(Error, Stats)|Float64Array} callback Callback (optional). In Node 7.7.0+ this will be a Float64Array * that should be filled with stat values. * @return {Stats|undefined} Stats or undefined (if sync). */ Binding.prototype.fstat = function(fd, callback) { return maybeCallback(callback, this, function() { var descriptor = this._getDescriptorById(fd); var item = descriptor.getItem(); var stats = item.getStats(); // In Node 7.7.0+, binding.stat accepts a Float64Array as the second argument, // which should be filled with stat values. // In prior versions of Node, binding.stat simply returns a Stats instance. if (callback instanceof Float64Array) { fillStatsArray(stats, callback); } else { return new Stats(stats); } }); }; /** * Close a file descriptor. * @param {number} fd File descriptor. * @param {function(Error)} callback Callback (optional). */ Binding.prototype.close = function(fd, callback) { maybeCallback(callback, this, function() { this._untrackDescriptorById(fd); }); }; /** * Open and possibly create a file. * @param {string} pathname File path. * @param {number} flags Flags. * @param {number} mode Mode. * @param {function(Error, string)} callback Callback (optional). * @return {string} File descriptor (if sync). */ Binding.prototype.open = function(pathname, flags, mode, callback) { return maybeCallback(callback, this, function() { var descriptor = new FileDescriptor(flags); var item = this._system.getItem(pathname); if (item instanceof SymbolicLink) { item = this._system.getItem( path.resolve(path.dirname(pathname), item.getPath())); } if (descriptor.isExclusive() && item) { throw new FSError('EEXIST', pathname); } if (descriptor.isCreate() && !item) { var parent = this._system.getItem(path.dirname(pathname)); if (!parent) { throw new FSError('ENOENT', pathname); } if (!(parent instanceof Directory)) { throw new FSError('ENOTDIR', pathname); } item = new File(); if (mode) { item.setMode(mode); } parent.addItem(path.basename(pathname), item); } if (descriptor.isRead()) { if (!item) { throw new FSError('ENOENT', pathname); } if (!item.canRead()) { throw new FSError('EACCES', pathname); } } if (descriptor.isWrite() && !item.canWrite()) { throw new FSError('EACCES', pathname); } if (descriptor.isTruncate()) { item.setContent(''); } if (descriptor.isTruncate() || descriptor.isAppend()) { descriptor.setPosition(item.getContent().length); } descriptor.setItem(item); return this._trackDescriptor(descriptor); }); }; /** * Read from a file descriptor. * @param {string} fd File descriptor. * @param {Buffer} buffer Buffer that the contents will be written to. * @param {number} offset Offset in the buffer to start writing to. * @param {number} length Number of bytes to read. * @param {?number} position Where to begin reading in the file. If null, * data will be read from the current file position. * @param {function(Error, number, Buffer)} callback Callback (optional) called * with any error, number of bytes read, and the buffer. * @return {number} Number of bytes read (if sync). */ Binding.prototype.read = function(fd, buffer, offset, length, position, callback) { return maybeCallback(callback, this, function() { var descriptor = this._getDescriptorById(fd); if (!descriptor.isRead()) { throw new FSError('EBADF'); } var file = descriptor.getItem(); if (!(file instanceof File)) { // deleted or not a regular file throw new FSError('EBADF'); } if (typeof position !== 'number' || position < 0) { position = descriptor.getPosition(); } var content = file.getContent(); var start = Math.min(position, content.length); var end = Math.min(position + length, content.length); var read = (start < end) ? content.copy(buffer, offset, start, end) : 0; descriptor.setPosition(position + read); return read; }); }; /** * Write to a file descriptor given a buffer. * @param {string} fd File descriptor. * @param {Array<Buffer>} buffers Array of buffers with contents to write. * @param {?number} position Where to begin writing in the file. If null, * data will be written to the current file position. * @param {function(Error, number, Buffer)} callback Callback (optional) called * with any error, number of bytes written, and the buffer. * @return {number} Number of bytes written (if sync). */ Binding.prototype.writeBuffers = function(fd, buffers, position, callback) { return maybeCallback(callback, this, function() { var descriptor = this._getDescriptorById(fd); if (!descriptor.isWrite()) { throw new FSError('EBADF'); } var file = descriptor.getItem(); if (!(file instanceof File)) { // not a regular file throw new FSError('EBADF'); } if (typeof position !== 'number' || position < 0) { position = descriptor.getPosition(); } var content = file.getContent(); var newContent = Buffer.concat(buffers); var newLength = position + newContent.length; if (content.length < newLength) { var tempContent = new Buffer(newLength); content.copy(tempContent); content = tempContent; } var written = newContent.copy(content, position); file.setContent(content); descriptor.setPosition(newLength); return written; }); }; /** * Write to a file descriptor given a buffer. * @param {string} fd File descriptor. * @param {Buffer} buffer Buffer with contents to write. * @param {number} offset Offset in the buffer to start writing from. * @param {number} length Number of bytes to write. * @param {?number} position Where to begin writing in the file. If null, * data will be written to the current file position. * @param {function(Error, number, Buffer)} callback Callback (optional) called * with any error, number of bytes written, and the buffer. * @return {number} Number of bytes written (if sync). */ Binding.prototype.writeBuffer = function(fd, buffer, offset, length, position, callback) { return maybeCallback(callback, this, function() { var descriptor = this._getDescriptorById(fd); if (!descriptor.isWrite()) { throw new FSError('EBADF'); } var file = descriptor.getItem(); if (!(file instanceof File)) { // not a regular file throw new FSError('EBADF'); } if (typeof position !== 'number' || position < 0) { position = descriptor.getPosition(); } var content = file.getContent(); var newLength = position + length; if (content.length < newLength) { var newContent = new Buffer(newLength); content.copy(newContent); content = newContent; } var sourceEnd = Math.min(offset + length, buffer.length); var written = buffer.copy(content, position, offset, sourceEnd); file.setContent(content); descriptor.setPosition(newLength); return written; }); }; /** * Alias for writeBuffer (used in Node <= 0.10). * @param {string} fd File descriptor. * @param {Buffer} buffer Buffer with contents to write. * @param {number} offset Offset in the buffer to start writing from. * @param {number} length Number of bytes to write. * @param {?number} position Where to begin writing in the file. If null, * data will be written to the current file position. * @param {function(Error, number, Buffer)} callback Callback (optional) called * with any error, number of bytes written, and the buffer. * @return {number} Number of bytes written (if sync). */ Binding.prototype.write = Binding.prototype.writeBuffer; /** * Write to a file descriptor given a string. * @param {string} fd File descriptor. * @param {string} string String with contents to write. * @param {number} position Where to begin writing in the file. If null, * data will be written to the current file position. * @param {string} encoding String encoding. * @param {function(Error, number, string)} callback Callback (optional) called * with any error, number of bytes written, and the string. * @return {number} Number of bytes written (if sync). */ Binding.prototype.writeString = function(fd, string, position, encoding, callback) { var buffer = new Buffer(string, encoding); var wrapper; if (callback) { if (callback.oncomplete) { callback = callback.oncomplete.bind(callback); } wrapper = function(err, written, returned) { callback(err, written, returned && string); }; } return this.writeBuffer(fd, buffer, 0, string.length, position, wrapper); }; /** * Rename a file. * @param {string} oldPath Old pathname. * @param {string} newPath New pathname. * @param {function(Error)} callback Callback (optional). * @return {undefined} */ Binding.prototype.rename = function(oldPath, newPath, callback) { return maybeCallback(callback, this, function() { var oldItem = this._system.getItem(oldPath); if (!oldItem) { throw new FSError('ENOENT', oldPath); } var oldParent = this._system.getItem(path.dirname(oldPath)); var oldName = path.basename(oldPath); var newItem = this._system.getItem(newPath); var newParent = this._system.getItem(path.dirname(newPath)); var newName = path.basename(newPath); if (newItem) { // make sure they are the same type if (oldItem instanceof File) { if (newItem instanceof Directory) { throw new FSError('EISDIR', newPath); } } else if (oldItem instanceof Directory) { if (!(newItem instanceof Directory)) { throw new FSError('ENOTDIR', newPath); } if (newItem.list().length > 0) { throw new FSError('ENOTEMPTY', newPath); } } newParent.removeItem(newName); } else { if (!newParent) { throw new FSError('ENOENT', newPath); } if (!(newParent instanceof Directory)) { throw new FSError('ENOTDIR', newPath); } } oldParent.removeItem(oldName); newParent.addItem(newName, oldItem); }); }; /** * Read a directory. * @param {string} dirpath Path to directory. * @param {string} encoding The encoding ('utf-8' or 'buffer'). * @param {function(Error, (Array.<string>|Array.<Buffer>)} callback Callback * (optional) called with any error or array of items in the directory. * @return {Array.<string>|Array.<Buffer>} Array of items in directory (if sync). */ Binding.prototype.readdir = function(dirpath, encoding, callback) { if (encoding && typeof encoding !== 'string') { callback = encoding; encoding = 'utf-8'; } return maybeCallback(callback, this, function() { var dpath = dirpath; var dir = this._system.getItem(dirpath); while (dir instanceof SymbolicLink) { dpath = path.resolve(path.dirname(dpath), dir.getPath()); dir = this._system.getItem(dpath); } if (!dir) { throw new FSError('ENOENT', dirpath); } if (!(dir instanceof Directory)) { throw new FSError('ENOTDIR', dirpath); } var list = dir.list(); if (encoding === 'buffer') { list = list.map(function(item) { return new Buffer(item); }); } return list; }); }; /** * Create a directory. * @param {string} pathname Path to new directory. * @param {number} mode Permissions. * @param {function(Error)} callback Optional callback. */ Binding.prototype.mkdir = function(pathname, mode, callback) { maybeCallback(callback, this, function() { var item = this._system.getItem(pathname); if (item) { throw new FSError('EEXIST', pathname); } var parent = this._system.getItem(path.dirname(pathname)); if (!parent) { throw new FSError('ENOENT', pathname); } this.access(path.dirname(pathname), parseInt('0002', 8)); var dir = new Directory(); if (mode) { dir.setMode(mode); } parent.addItem(path.basename(pathname), dir); }); }; /** * Remove a directory. * @param {string} pathname Path to directory. * @param {function(Error)} callback Optional callback. */ Binding.prototype.rmdir = function(pathname, callback) { maybeCallback(callback, this, function() { var item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } if (!(item instanceof Directory)) { throw new FSError('ENOTDIR', pathname); } if (item.list().length > 0) { throw new FSError('ENOTEMPTY', pathname); } this.access(path.dirname(pathname), parseInt('0002', 8)); var parent = this._system.getItem(path.dirname(pathname)); parent.removeItem(path.basename(pathname)); }); }; /** * Truncate a file. * @param {number} fd File descriptor. * @param {number} len Number of bytes. * @param {function(Error)} callback Optional callback. */ Binding.prototype.ftruncate = function(fd, len, callback) { maybeCallback(callback, this, function() { var descriptor = this._getDescriptorById(fd); if (!descriptor.isWrite()) { throw new FSError('EINVAL'); } var file = descriptor.getItem(); if (!(file instanceof File)) { throw new FSError('EINVAL'); } var content = file.getContent(); var newContent = new Buffer(len); content.copy(newContent); file.setContent(newContent); }); }; /** * Legacy support. * @param {number} fd File descriptor. * @param {number} len Number of bytes. * @param {function(Error)} callback Optional callback. */ Binding.prototype.truncate = Binding.prototype.ftruncate; /** * Change user and group owner. * @param {string} pathname Path. * @param {number} uid User id. * @param {number} gid Group id. * @param {function(Error)} callback Optional callback. */ Binding.prototype.chown = function(pathname, uid, gid, callback) { maybeCallback(callback, this, function() { var item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } item.setUid(uid); item.setGid(gid); }); }; /** * Change user and group owner. * @param {number} fd File descriptor. * @param {number} uid User id. * @param {number} gid Group id. * @param {function(Error)} callback Optional callback. */ Binding.prototype.fchown = function(fd, uid, gid, callback) { maybeCallback(callback, this, function() { var descriptor = this._getDescriptorById(fd); var item = descriptor.getItem(); item.setUid(uid); item.setGid(gid); }); }; /** * Change permissions. * @param {string} pathname Path. * @param {number} mode Mode. * @param {function(Error)} callback Optional callback. */ Binding.prototype.chmod = function(pathname, mode, callback) { maybeCallback(callback, this, function() { var item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } item.setMode(mode); }); }; /** * Change permissions. * @param {number} fd File descriptor. * @param {number} mode Mode. * @param {function(Error)} callback Optional callback. */ Binding.prototype.fchmod = function(fd, mode, callback) { maybeCallback(callback, this, function() { var descriptor = this._getDescriptorById(fd); var item = descriptor.getItem(); item.setMode(mode); }); }; /** * Delete a named item. * @param {string} pathname Path to item. * @param {function(Error)} callback Optional callback. */ Binding.prototype.unlink = function(pathname, callback) { maybeCallback(callback, this, function() { var item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } if (item instanceof Directory) { throw new FSError('EPERM', pathname); } var parent = this._system.getItem(path.dirname(pathname)); parent.removeItem(path.basename(pathname)); }); }; /** * Update timestamps. * @param {string} pathname Path to item. * @param {number} atime Access time (in seconds). * @param {number} mtime Modification time (in seconds). * @param {function(Error)} callback Optional callback. */ Binding.prototype.utimes = function(pathname, atime, mtime, callback) { maybeCallback(callback, this, function() { var item = this._system.getItem(pathname); if (!item) { throw new FSError('ENOENT', pathname); } item.setATime(new Date(atime * 1000)); item.setMTime(new Date(mtime * 1000)); }); }; /** * Update timestamps. * @param {number} fd File descriptor. * @param {number} atime Access time (in seconds). * @param {number} mtime Modification time (in seconds). * @param {function(Error)} callback Optional callback. */ Binding.prototype.futimes = function(fd, atime, mtime, callback) { maybeCallback(callback, this, function() { var descriptor = this._getDescriptorById(fd); var item = descriptor.getItem(); item.setATime(new Date(atime * 1000)); item.setMTime(new Date(mtime * 1000)); }); }; /** * Synchronize in-core state with storage device. * @param {number} fd File descriptor. * @param {function(Error)} callback Optional callback. */ Binding.prototype.fsync = function(fd, callback) { maybeCallback(callback, this, function() { this._getDescriptorById(fd); }); }; /** * Synchronize in-core metadata state with storage device. * @param {number} fd File descriptor. * @param {function(Error)} callback Optional callback. */ Binding.prototype.fdatasync = function(fd, callback) { maybeCallback(callback, this, function() { this._getDescriptorById(fd); }); }; /** * Create a hard link. * @param {string} srcPath The existing file. * @param {string} destPath The new link to create. * @param {function(Error)} callback Optional callback. */ Binding.prototype.link = function(srcPath, destPath, callback) { maybeCallback(callback, this, function() { var item = this._system.getItem(srcPath); if (!item) { throw new FSError('ENOENT', srcPath); } if (item instanceof Directory) { throw new FSError('EPERM', srcPath); } if (this._system.getItem(destPath)) { throw new FSError('EEXIST', destPath); } var parent = this._system.getItem(path.dirname(destPath)); if (!parent) { throw new FSError('ENOENT', destPath); } if (!(parent instanceof Directory)) { throw new FSError('ENOTDIR', destPath); } parent.addItem(path.basename(destPath), item); }); }; /** * Create a symbolic link. * @param {string} srcPath Path from link to the source file. * @param {string} destPath Path for the generated link. * @param {string} type Ignored (used for Windows only). * @param {function(Error)} callback Optional callback. */ Binding.prototype.symlink = function(srcPath, destPath, type, callback) { maybeCallback(callback, this, function() { if (this._system.getItem(destPath)) { throw new FSError('EEXIST', destPath); } var parent = this._system.getItem(path.dirname(destPath)); if (!parent) { throw new FSError('ENOENT', destPath); } if (!(parent instanceof Directory)) { throw new FSError('ENOTDIR', destPath); } var link = new SymbolicLink(); link.setPath(srcPath); parent.addItem(path.basename(destPath), link); }); }; /** * Read the contents of a symbolic link. * @param {string} pathname Path to symbolic link. * @param {string} encoding The encoding ('utf-8' or 'buffer'). * @param {function(Error, (string|Buffer))} callback Optional callback. * @return {string|Buffer} Symbolic link contents (path to source). */ Binding.prototype.readlink = function(pathname, encoding, callback) { if (encoding && typeof encoding !== 'string') { callback = encoding; encoding = 'utf-8'; } return maybeCallback(callback, this, function() { var link = this._system.getItem(pathname); if (!(link instanceof SymbolicLink)) { throw new FSError('EINVAL', pathname); } var linkPath = link.getPath(); if (encoding === 'buffer') { linkPath = new Buffer(linkPath); } return linkPath; }); }; /** * Stat an item. * @param {string} filepath Path. * @param {function(Error, Stats)|Float64Array} callback Callback (optional). In Node 7.7.0+ this will be a Float64Array * that should be filled with stat values. * @return {Stats|undefined} Stats or undefined (if sync). */ Binding.prototype.lstat = function(filepath, callback) { return maybeCallback(callback, this, function() { var item = this._system.getItem(filepath); if (!item) { throw new FSError('ENOENT', filepath); } var stats = item.getStats(); // In Node 7.7.0+, binding.stat accepts a Float64Array as the second argument, // which should be filled with stat values. // In prior versions of Node, binding.stat simply returns a Stats instance. if (callback instanceof Float64Array) { fillStatsArray(stats, callback); } else { return new Stats(item.getStats()); } }); }; /** * Tests user permissions. * @param {string} filepath Path. * @param {number} mode Mode. * @param {function(Error)} callback Callback (optional). */ Binding.prototype.access = function(filepath, mode, callback) { maybeCallback(callback, this, function() { var item = this._system.getItem(filepath); if (!item) { throw new FSError('ENOENT', filepath); } if (mode && process.getuid && process.getgid) { var itemMode = item.getMode(); if (item.getUid() === process.getuid()) { if ((itemMode & (mode * 64)) !== mode * 64) { throw new FSError('EACCES', filepath); } } else if (item.getGid() === process.getgid()) { if ((itemMode & (mode * 8)) !== mode * 8) { throw new FSError('EACCES', filepath); } } else { if ((itemMode & mode) !== mode) { throw new FSError('EACCES', filepath); } } } }); }; /** * Not yet implemented. * @type {function()} */ Binding.prototype.StatWatcher = notImplemented; /** * Export the binding constructor. * @type {function()} */ exports = module.exports = Binding;