Skip to content

Commit ce7982b

Browse files
author
Benjamin Pasero
committed
introduce and use extfs.realpath
1 parent fda3ed3 commit ce7982b

5 files changed

Lines changed: 87 additions & 13 deletions

File tree

src/vs/base/node/extfs.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,13 +374,13 @@ export function writeFileAndFlush(path: string, data: string | NodeBuffer, optio
374374
/**
375375
* Copied from: https://github.com/Microsoft/vscode-node-debug/blob/master/src/node/pathUtilities.ts#L83
376376
*
377-
* Given an absolute, normalized, and existing file path 'realpath' returns the exact path that the file has on disk.
377+
* Given an absolute, normalized, and existing file path 'realcase' returns the exact path that the file has on disk.
378378
* On a case insensitive file system, the returned path might differ from the original path by character casing.
379379
* On a case sensitive file system, the returned path will always be identical to the original path.
380380
* In case of errors, null is returned. But you cannot use this function to verify that a path exists.
381-
* realpathSync does not handle '..' or '.' path segments and it does not take the locale into account.
381+
* realcaseSync does not handle '..' or '.' path segments and it does not take the locale into account.
382382
*/
383-
export function realpathSync(path: string): string {
383+
export function realcaseSync(path: string): string {
384384
const dir = paths.dirname(path);
385385
if (path === dir) { // end recursion
386386
return path;
@@ -392,15 +392,15 @@ export function realpathSync(path: string): string {
392392
const found = entries.filter(e => e.toLowerCase() === name); // use a case insensitive search
393393
if (found.length === 1) {
394394
// on a case sensitive filesystem we cannot determine here, whether the file exists or not, hence we need the 'file exists' precondition
395-
const prefix = realpathSync(dir); // recurse
395+
const prefix = realcaseSync(dir); // recurse
396396
if (prefix) {
397397
return paths.join(prefix, found[0]);
398398
}
399399
} else if (found.length > 1) {
400400
// must be a case sensitive $filesystem
401401
const ix = found.indexOf(name);
402402
if (ix >= 0) { // case sensitive
403-
const prefix = realpathSync(dir); // recurse
403+
const prefix = realcaseSync(dir); // recurse
404404
if (prefix) {
405405
return paths.join(prefix, found[ix]);
406406
}
@@ -411,4 +411,44 @@ export function realpathSync(path: string): string {
411411
}
412412

413413
return null;
414+
}
415+
416+
export function realpathSync(path: string): string {
417+
try {
418+
return fs.realpathSync(path);
419+
} catch (error) {
420+
421+
// We hit an error calling fs.realpathSync(). Since fs.realpathSync() is doing some path normalization
422+
// we now do a similar normalization and then try again if we can access the path with read
423+
// permissions at least. If that succeeds, we return that path.
424+
// fs.realpath() is resolving symlinks and that can fail in certain cases. The workaround is
425+
// to not resolve links but to simply see if the path is read accessible or not.
426+
const normalizedPath = normalizePath(path);
427+
fs.accessSync(normalizedPath, fs.constants.R_OK); // throws in case of an error
428+
429+
return normalizedPath;
430+
}
431+
}
432+
433+
export function realpath(path: string, callback: (error: Error, realpath: string) => void): void {
434+
return fs.realpath(path, (error, realpath) => {
435+
if (!error) {
436+
return callback(null, realpath);
437+
}
438+
439+
// We hit an error calling fs.realpath(). Since fs.realpath() is doing some path normalization
440+
// we now do a similar normalization and then try again if we can access the path with read
441+
// permissions at least. If that succeeds, we return that path.
442+
// fs.realpath() is resolving symlinks and that can fail in certain cases. The workaround is
443+
// to not resolve links but to simply see if the path is read accessible or not.
444+
const normalizedPath = normalizePath(path);
445+
446+
return fs.access(normalizedPath, fs.constants.R_OK, error => {
447+
return callback(error, normalizedPath);
448+
});
449+
});
450+
}
451+
452+
function normalizePath(path: string): string {
453+
return strings.rtrim(paths.normalize(path), paths.sep);
414454
}

src/vs/base/node/pfs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function rimraf(path: string): TPromise<void> {
7272
}
7373

7474
export function realpath(path: string): TPromise<string> {
75-
return nfcall(fs.realpath, path, null);
75+
return nfcall(extfs.realpath, path);
7676
}
7777

7878
export function stat(path: string): TPromise<fs.Stats> {

src/vs/base/test/node/extfs/extfs.test.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ suite('Extfs', () => {
203203
});
204204
});
205205

206-
test('realpath', (done) => {
206+
test('realcase', (done) => {
207207
const id = uuid.generateUuid();
208208
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
209209
const newDir = path.join(parentDir, 'extfs', id);
@@ -213,7 +213,7 @@ suite('Extfs', () => {
213213
// assume case insensitive file system
214214
if (process.platform === 'win32' || process.platform === 'darwin') {
215215
const upper = newDir.toUpperCase();
216-
const real = extfs.realpathSync(upper);
216+
const real = extfs.realcaseSync(upper);
217217

218218
if (real) { // can be null in case of permission errors
219219
assert.notEqual(real, upper);
@@ -224,11 +224,45 @@ suite('Extfs', () => {
224224

225225
// linux, unix, etc. -> assume case sensitive file system
226226
else {
227-
const real = extfs.realpathSync(newDir);
227+
const real = extfs.realcaseSync(newDir);
228228
assert.equal(real, newDir);
229229
}
230230

231231
extfs.del(parentDir, os.tmpdir(), () => { }, done);
232232
});
233233
});
234+
235+
test('realpath', (done) => {
236+
const id = uuid.generateUuid();
237+
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
238+
const newDir = path.join(parentDir, 'extfs', id);
239+
240+
extfs.mkdirp(newDir, 493, (error) => {
241+
242+
extfs.realpath(newDir, (error, realpath) => {
243+
assert.ok(realpath);
244+
assert.ok(!error);
245+
246+
extfs.del(parentDir, os.tmpdir(), () => { }, done);
247+
});
248+
});
249+
});
250+
251+
test('realpathSync', (done) => {
252+
const id = uuid.generateUuid();
253+
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
254+
const newDir = path.join(parentDir, 'extfs', id);
255+
256+
extfs.mkdirp(newDir, 493, (error) => {
257+
let realpath: string;
258+
try {
259+
realpath = extfs.realpathSync(newDir);
260+
} catch (error) {
261+
assert.ok(!error);
262+
}
263+
assert.ok(realpath);
264+
265+
extfs.del(parentDir, os.tmpdir(), () => { }, done);
266+
});
267+
});
234268
});

src/vs/code/node/paths.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55

66
'use strict';
77

8-
import * as fs from 'original-fs';
98
import * as path from 'path';
109
import * as arrays from 'vs/base/common/arrays';
1110
import * as strings from 'vs/base/common/strings';
1211
import * as paths from 'vs/base/common/paths';
1312
import * as platform from 'vs/base/common/platform';
1413
import * as types from 'vs/base/common/types';
1514
import { ParsedArgs } from 'vs/platform/environment/common/environment';
15+
import { realpathSync } from "vs/base/node/extfs";
1616

1717
export function validatePaths(args: ParsedArgs): ParsedArgs {
1818

@@ -43,7 +43,7 @@ function doValidatePaths(args: string[], gotoLineMode?: boolean): string[] {
4343

4444
let realPath: string;
4545
try {
46-
realPath = fs.realpathSync(pathCandidate);
46+
realPath = realpathSync(pathCandidate);
4747
} catch (error) {
4848
// in case of an error, assume the user wants to create this file
4949
// if the path is relative, we join it to the cwd

src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
1515
import { FileChangeType } from 'vs/platform/files/common/files';
1616
import { ThrottledDelayer } from 'vs/base/common/async';
1717
import strings = require('vs/base/common/strings');
18-
import { realpathSync } from 'vs/base/node/extfs';
18+
import { realcaseSync } from 'vs/base/node/extfs';
1919
import { isMacintosh } from 'vs/base/common/platform';
2020
import watcher = require('vs/workbench/services/files/node/watcher/common');
2121
import { IWatcherRequest, IWatcherService } from './watcher';
@@ -43,7 +43,7 @@ export class ChokidarWatcherService implements IWatcherService {
4343
// so we have to find the real casing of the path and do some path massaging to fix this
4444
// see https://github.com/paulmillr/chokidar/issues/418
4545
const originalBasePath = request.basePath;
46-
const realBasePath = isMacintosh ? (realpathSync(originalBasePath) || originalBasePath) : originalBasePath;
46+
const realBasePath = isMacintosh ? (realcaseSync(originalBasePath) || originalBasePath) : originalBasePath;
4747
const realBasePathLength = realBasePath.length;
4848
const realBasePathDiffers = (originalBasePath !== realBasePath);
4949

0 commit comments

Comments
 (0)