Skip to content

Commit

Permalink
Fix fs.stat(), fs.lstat(), and fs.fstat() for Node 7.7+
Browse files Browse the repository at this point in the history
Node 7.7 changed the behavior of the `binding.{stat,lstat,fstat}` functions (see nodejs/node#11522). This updates the binding functions used in mock-fs to match the new Node binding behavior, while still maintaining compatibility with old Node versions. The new behavior is detected when the second argument to `binding.{stat,lstat,fstat}` is a `Float64Array`, which would be an invalid argument for previous versions of the binding.
  • Loading branch information
not-an-aardvark committed Mar 2, 2017
1 parent 61926f8 commit 10f7934
Showing 1 changed file with 61 additions and 7 deletions.
68 changes: 61 additions & 7 deletions lib/binding.js
Expand Up @@ -20,7 +20,7 @@ var getPathParts = require('./filesystem').getPathParts;
* @return {*} Return (if callback is not provided).
*/
function maybeCallback(callback, thisArg, func) {
if (callback) {
if (callback && (typeof callback === 'function' || typeof callback.oncomplete === 'function')) {
var err = null;
var val;
try {
Expand Down Expand Up @@ -266,11 +266,36 @@ Binding.prototype.realpath = function(filepath, encoding, callback) {
});
};

/**
* 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)} callback Callback (optional).
* @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) {
Expand All @@ -283,22 +308,41 @@ Binding.prototype.stat = function(filepath, callback) {
if (!item) {
throw new FSError('ENOENT', filepath);
}
return new Stats(item.getStats());
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)} callback Callback (optional).
* @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();
return new Stats(item.getStats());
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);
}
});
};

Expand Down Expand Up @@ -933,7 +977,8 @@ Binding.prototype.readlink = function(pathname, encoding, callback) {
/**
* Stat an item.
* @param {string} filepath Path.
* @param {function(Error, Stats)} callback Callback (optional).
* @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) {
Expand All @@ -942,7 +987,16 @@ Binding.prototype.lstat = function(filepath, callback) {
if (!item) {
throw new FSError('ENOENT', filepath);
}
return new Stats(item.getStats());
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());
}
});
};

Expand Down

0 comments on commit 10f7934

Please sign in to comment.