Skip to content

Commit 3e1a85c

Browse files
committed
Fix buggy inode-handling
1. Very large inodes (those in excess of `Number.MAX_SAFE_INTEGER`) were getting clipped to a certain value, resulting in the wrong file being statted. This caused files to appear as directories (and vice versa). To solve this, we now call `fs.lstatSync` with its "bigint" option. 2. Files are now identified by a combination of device ID and inode; the latter alone isn't enough to uniquely identify a filesystem resource.
1 parent 28af167 commit 3e1a85c

File tree

4 files changed

+32
-14
lines changed

4 files changed

+32
-14
lines changed

lib/filesystem.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class FileSystem {
1212

1313
constructor(){
1414
this.paths = new PathMap();
15-
this.inodes = new Map();
15+
this.ids = new Map();
1616
this.emitter = new Emitter();
1717
this.disposables = new CompositeDisposable();
1818
}
@@ -47,16 +47,16 @@ class FileSystem {
4747
else{
4848
lstat.lastError = null;
4949
const stats = lstat(path);
50-
const inode = stats ? stats.ino : null;
50+
const id = stats ? [stats.dev, stats.ino].filter(Boolean).join(".") : null;
5151

5252
// Return null for nonexistent entities if `mustExist` is truthy
5353
if(mustExist && lstat.lastError && "ENOENT" === lstat.lastError.code)
5454
return null;
5555

56-
if(inode){
56+
if(id){
5757
// Don't reregister an entity after it's been moved
58-
if(stats.nlink < 2 && this.inodes.has(inode)){
59-
const resource = this.inodes.get(inode);
58+
if(stats.nlink < 2 && this.ids.has(id)){
59+
const resource = this.ids.get(id);
6060
resource.setPath(path);
6161
return resource;
6262
}
@@ -76,16 +76,16 @@ class FileSystem {
7676
: new File(path, stats);
7777

7878
this.paths.set(path, resource);
79-
inode && this.inodes.set(inode, resource);
79+
id && this.ids.set(id, resource);
8080

8181
const disposables = new CompositeDisposable(
8282
resource.onDidDestroy(() => disposables.dispose()),
8383
resource.onDidMove(paths => this.updatePath(paths.from, paths.to)),
8484
new Disposable(() => {
8585
this.paths.delete(resource.path);
8686
this.paths.delete(resource.path.replace(/\//g, "\\"));
87-
if(inode && resource.stats.nlink < 2)
88-
this.inodes.delete(inode);
87+
if(id && resource.stats.nlink < 2)
88+
this.ids.delete(id);
8989
})
9090
);
9191

lib/resource.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,13 @@ class Resource{
110110
if(stats){
111111
this.pendingStats = false;
112112
this.stats = stats;
113-
this.type = EntityType.ALL & stats.mode;
113+
this.type = EntityType.ALL & Number(stats.mode);
114+
115+
// Identify resource uniquely, if possible
116+
if(stats.ino){
117+
const {dev, ino} = stats;
118+
this.id = dev ? `${dev}.${ino}` : String(ino);
119+
}
114120

115121
if(stats.isSymbolicLink())
116122
this.isSymlink = true;
@@ -319,6 +325,7 @@ class Resource{
319325

320326
Object.assign(Resource.prototype, {
321327
destroyed: false,
328+
id: null,
322329
isDirectory: false,
323330
isFile: false,
324331
isSymlink: false,

lib/system-task.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function exec(path, ops, meta = {}){
5454
let stats = null;
5555
if(OP_STAT & ops){
5656
op = OP_STAT;
57-
stats = fs.lstatSync(path);
57+
stats = fs.lstatSync(path, {bigint: true});
5858
emit("op:done", op, path, stats);
5959
}
6060

lib/utils.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,26 @@
33
const fs = require("fs");
44
const path = require("path");
55

6+
// Non-breaking fs functions
7+
const lstat = nerf(fs.lstatSync);
8+
const realpath = nerf(fs.realpathSync);
9+
610
module.exports = {
711
nerf,
812
normalisePath,
913
sipFile,
1014
statify,
11-
12-
// Non-breaking fs functions
13-
lstat: nerf(fs.lstatSync),
14-
realpath: nerf(fs.realpathSync),
15+
realpath,
16+
lstat(path){
17+
const stats = lstat(path, {bigint: true});
18+
stats.blocks = Number(stats.blocks);
19+
stats.mode = Number(stats.mode);
20+
stats.nlink = Number(stats.nlink);
21+
stats.uid = Number(stats.uid);
22+
stats.gid = Number(stats.gid);
23+
stats.rdev = Number(stats.rdev);
24+
return stats;
25+
},
1526
};
1627

1728

0 commit comments

Comments
 (0)