Permalink
Browse files

Ignore stat errors that are not ENOENT

Fix #245

This works around cases in Windows systems where we can see that a file
exists on the directory listing, but the stat/lstat call fails for some
reason other than ENOENT or ENOTDIR.
  • Loading branch information...
1 parent cea11fe commit 3b5e0f5ea4a416182f66648cccf8b6dd4fb35413 @isaacs committed Sep 29, 2016
Showing with 132 additions and 15 deletions.
  1. +9 −7 glob.js
  2. +16 −8 sync.js
  3. +107 −0 test/eperm-stat.js
View
@@ -508,15 +508,15 @@ Glob.prototype._readdirInGlobStar = function (abs, cb) {
fs.lstat(abs, lstatcb)
function lstatcb_ (er, lstat) {
- if (er)
+ if (er && er.code === 'ENOENT')
return cb()
- var isSym = lstat.isSymbolicLink()
+ var isSym = lstat && lstat.isSymbolicLink()
self.symlinks[abs] = isSym
// If it's not a symlink or a dir, then it's definitely a regular file.
// don't bother doing a readdir in that case.
- if (!isSym && !lstat.isDirectory()) {
+ if (!isSym && lstat && !lstat.isDirectory()) {
self.cache[abs] = 'FILE'
cb()
} else
@@ -769,21 +769,23 @@ Glob.prototype._stat = function (f, cb) {
}
Glob.prototype._stat2 = function (f, abs, er, stat, cb) {
- if (er) {
+ if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) {
this.statCache[abs] = false
return cb()
}
var needDir = f.slice(-1) === '/'
this.statCache[abs] = stat
- if (abs.slice(-1) === '/' && !stat.isDirectory())
+ if (abs.slice(-1) === '/' && stat && !stat.isDirectory())
return cb(null, false, stat)
- var c = stat.isDirectory() ? 'DIR' : 'FILE'
+ var c = true
+ if (stat)
+ c = stat.isDirectory() ? 'DIR' : 'FILE'
this.cache[abs] = this.cache[abs] || c
- if (needDir && c !== 'DIR')
+ if (needDir && c === 'FILE')
return cb()
return cb(null, c, stat)
View
@@ -250,16 +250,18 @@ GlobSync.prototype._readdirInGlobStar = function (abs) {
try {
lstat = fs.lstatSync(abs)
} catch (er) {
- // lstat failed, doesn't exist
- return null
+ if (er.code === 'ENOENT') {
+ // lstat failed, doesn't exist
+ return null
+ }
}
- var isSym = lstat.isSymbolicLink()
+ var isSym = lstat && lstat.isSymbolicLink()
this.symlinks[abs] = isSym
// If it's not a symlink or a dir, then it's definitely a regular file.
// don't bother doing a readdir in that case.
- if (!isSym && !lstat.isDirectory())
+ if (!isSym && lstat && !lstat.isDirectory())
this.cache[abs] = 'FILE'
else
entries = this._readdir(abs, false)
@@ -444,10 +446,13 @@ GlobSync.prototype._stat = function (f) {
try {
lstat = fs.lstatSync(abs)
} catch (er) {
- return false
+ if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) {
+ this.statCache[abs] = false
+ return false
+ }
}
- if (lstat.isSymbolicLink()) {
+ if (lstat && lstat.isSymbolicLink()) {
try {
stat = fs.statSync(abs)
} catch (er) {
@@ -460,10 +465,13 @@ GlobSync.prototype._stat = function (f) {
this.statCache[abs] = stat
- var c = stat.isDirectory() ? 'DIR' : 'FILE'
+ var c = true
+ if (stat)
+ c = stat.isDirectory() ? 'DIR' : 'FILE'
+
this.cache[abs] = this.cache[abs] || c
- if (needDir && c !== 'DIR')
+ if (needDir && c === 'FILE')
return false
return c
View
@@ -0,0 +1,107 @@
+require("./global-leakage.js")
+var dir = __dirname + '/fixtures'
+
+var fs = require('fs')
+var expect = [
+ 'a/abcdef',
+ 'a/abcdef/g',
+ 'a/abcdef/g/h',
+ 'a/abcfed',
+ 'a/abcfed/g',
+ 'a/abcfed/g/h'
+]
+
+var lstat = fs.lstat
+var lstatSync = fs.lstatSync
+var badPaths = /\ba[\\\/]?$|\babcdef\b/
+
+fs.lstat = function (path, cb) {
+ // synthetically generate a non-ENOENT error
+ if (badPaths.test(path)) {
+ var er = new Error('synthetic')
+ er.code = 'EPERM'
+ return process.nextTick(cb.bind(null, er))
+ }
+
+ return lstat.call(fs, path, cb)
+}
+
+fs.lstatSync = function (path) {
+ // synthetically generate a non-ENOENT error
+ if (badPaths.test(path)) {
+ var er = new Error('synthetic')
+ er.code = 'EPERM'
+ throw er
+ }
+
+ return lstatSync.call(fs, path)
+}
+
+var glob = require('../')
+var t = require('tap')
+
+t.test('stat errors other than ENOENT are ok', function (t) {
+ t.plan(2)
+ t.test('async', function (t) {
+ glob('a/*abc*/**', { stat: true, cwd: dir }, function (er, matches) {
+ if (er)
+ throw er
+ t.same(matches, expect)
+ t.end()
+ })
+ })
+
+ t.test('sync', function (t) {
+ var matches = glob.sync('a/*abc*/**', { stat: true, cwd: dir })
+ t.same(matches, expect)
+ t.end()
+ })
+})
+
+t.test('globstar with error in root', function (t) {
+ var expect = [
+ 'a',
+ 'a/abcdef',
+ 'a/abcdef/g',
+ 'a/abcdef/g/h',
+ 'a/abcfed',
+ 'a/abcfed/g',
+ 'a/abcfed/g/h',
+ 'a/b',
+ 'a/b/c',
+ 'a/b/c/d',
+ 'a/bc',
+ 'a/bc/e',
+ 'a/bc/e/f',
+ 'a/c',
+ 'a/c/d',
+ 'a/c/d/c',
+ 'a/c/d/c/b',
+ 'a/cb',
+ 'a/cb/e',
+ 'a/cb/e/f',
+ 'a/symlink',
+ 'a/symlink/a',
+ 'a/symlink/a/b',
+ 'a/symlink/a/b/c',
+ 'a/x',
+ 'a/z'
+ ]
+
+ var pattern = 'a/**'
+ t.plan(2)
+ t.test('async', function (t) {
+ glob(pattern, { cwd: dir }, function (er, matches) {
+ if (er)
+ throw er
+ t.same(matches, expect)
+ t.end()
+ })
+ })
+
+ t.test('sync', function (t) {
+ var matches = glob.sync(pattern, { cwd: dir })
+ t.same(matches, expect)
+ t.end()
+ })
+})

0 comments on commit 3b5e0f5

Please sign in to comment.