Skip to content

Commit

Permalink
Ignore stat errors that are not ENOENT
Browse files Browse the repository at this point in the history
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
isaacs committed Oct 7, 2016
1 parent cea11fe commit 3b5e0f5
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 15 deletions.
16 changes: 9 additions & 7 deletions glob.js
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
24 changes: 16 additions & 8 deletions sync.js
Expand Up @@ -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)
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down
107 changes: 107 additions & 0 deletions test/eperm-stat.js
@@ -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.