diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 80b9f0107be..b71cbd95d71 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -14,6 +14,8 @@ import { isRootOrDriveLetter } from 'vs/base/common/extpath'; import { generateUuid } from 'vs/base/common/uuid'; import { normalizeNFC } from 'vs/base/common/normalization'; +//#region Constants + // See https://github.com/microsoft/vscode/issues/30180 const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB @@ -25,6 +27,10 @@ const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE; export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE; +//#endregion + +//#region rimraf + export enum RimRafMode { /** @@ -142,6 +148,10 @@ export function rimrafSync(path: string): void { } } +//#endregion + +//#region readdir with NFC support (macos) + export async function readdir(path: string): Promise { return handleDirectoryChildren(await promisify(fs.readdir)(path)); } @@ -174,119 +184,148 @@ function handleDirectoryChildren(children: string[]): string[] { return children; } -export function exists(path: string): Promise { - return promisify(fs.exists)(path); -} +export async function readDirsInDir(dirPath: string): Promise { + const children = await readdir(dirPath); + const directories: string[] = []; -export function chmod(path: string, mode: number): Promise { - return promisify(fs.chmod)(path, mode); -} - -export function stat(path: string): Promise { - return promisify(fs.stat)(path); -} - -export interface IStatAndLink { - - // The stats of the file. If the file is a symbolic - // link, the stats will be of that target file and - // not the link itself. - // If the file is a symbolic link pointing to a non - // existing file, the stat will be of the link and - // the `dangling` flag will indicate this. - stat: fs.Stats; - - // Will be provided if the resource is a symbolic link - // on disk. Use the `dangling` flag to find out if it - // points to a resource that does not exist on disk. - symbolicLink?: { dangling: boolean }; -} - -export async function statLink(path: string): Promise { - - // First stat the link - let lstats: fs.Stats | undefined; - try { - lstats = await lstat(path); - - // Return early if the stat is not a symbolic link at all - if (!lstats.isSymbolicLink()) { - return { stat: lstats }; + for (const child of children) { + if (await SymlinkSupport.dirExists(join(dirPath, child))) { + directories.push(child); } - } catch (error) { - /* ignore - use stat() instead */ } - // If the stat is a symbolic link or failed to stat, use fs.stat() - // which for symbolic links will stat the target they point to - try { - const stats = await stat(path); + return directories; +} - return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined }; - } catch (error) { +//#endregion - // If the link points to a non-existing file we still want - // to return it as result while setting dangling: true flag - if (error.code === 'ENOENT' && lstats) { - return { stat: lstats, symbolicLink: { dangling: true } }; - } +export function whenDeleted(path: string): Promise { - // Windows: workaround a node.js bug where reparse points - // are not supported (https://github.com/nodejs/node/issues/36790) - if (isWindows && error.code === 'EACCES' && lstats) { - try { - const stats = await stat(await readlink(path)); + // Complete when wait marker file is deleted + return new Promise(resolve => { + let running = false; + const interval = setInterval(() => { + if (!running) { + running = true; + fs.access(path, err => { + running = false; - return { stat: stats, symbolicLink: lstats.isSymbolicLink() ? { dangling: false } : undefined }; - } catch (error) { - - // If the link points to a non-existing file we still want - // to return it as result while setting dangling: true flag - if (error.code === 'ENOENT') { - return { stat: lstats, symbolicLink: { dangling: true } }; - } - - throw error; + if (err) { + clearInterval(interval); + resolve(undefined); + } + }); } + }, 1000); + }); +} + +//#region Methods with symbolic links support + +export namespace SymlinkSupport { + + export interface IStats { + + // The stats of the file. If the file is a symbolic + // link, the stats will be of that target file and + // not the link itself. + // If the file is a symbolic link pointing to a non + // existing file, the stat will be of the link and + // the `dangling` flag will indicate this. + stat: fs.Stats; + + // Will be provided if the resource is a symbolic link + // on disk. Use the `dangling` flag to find out if it + // points to a resource that does not exist on disk. + symbolicLink?: { dangling: boolean }; + } + + /** + * Resolves the `fs.Stats` of the provided path. If the path is a + * symbolic link, the `fs.Stats` will be from the target it points + * to. If the target does not exist, `dangling: true` will be returned + * as `symbolicLink` value. + */ + export async function stat(path: string): Promise { + + // First stat the link + let lstats: fs.Stats | undefined; + try { + lstats = await lstat(path); + + // Return early if the stat is not a symbolic link at all + if (!lstats.isSymbolicLink()) { + return { stat: lstats }; + } + } catch (error) { + /* ignore - use stat() instead */ } - throw error; + // If the stat is a symbolic link or failed to stat, use fs.stat() + // which for symbolic links will stat the target they point to + try { + const stats = await fs.promises.stat(path); + + return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined }; + } catch (error) { + + // If the link points to a non-existing file we still want + // to return it as result while setting dangling: true flag + if (error.code === 'ENOENT' && lstats) { + return { stat: lstats, symbolicLink: { dangling: true } }; + } + + // Windows: workaround a node.js bug where reparse points + // are not supported (https://github.com/nodejs/node/issues/36790) + if (isWindows && error.code === 'EACCES' && lstats) { + try { + const stats = await fs.promises.stat(await readlink(path)); + + return { stat: stats, symbolicLink: lstats.isSymbolicLink() ? { dangling: false } : undefined }; + } catch (error) { + + // If the link points to a non-existing file we still want + // to return it as result while setting dangling: true flag + if (error.code === 'ENOENT') { + return { stat: lstats, symbolicLink: { dangling: true } }; + } + + throw error; + } + } + + throw error; + } + } + + export async function fileExists(path: string): Promise { + try { + const { stat, symbolicLink } = await SymlinkSupport.stat(path); + + return stat.isFile() && symbolicLink?.dangling !== true; + } catch (error) { + // Ignore, path might not exist + } + + return false; + } + + export async function dirExists(path: string): Promise { + try { + const { stat, symbolicLink } = await SymlinkSupport.stat(path); + + return stat.isDirectory() && symbolicLink?.dangling !== true; + } catch (error) { + // Ignore, path might not exist + } + + return false; } } -export function lstat(path: string): Promise { - return promisify(fs.lstat)(path); -} +//#endregion -export function rename(oldPath: string, newPath: string): Promise { - return promisify(fs.rename)(oldPath, newPath); -} - -export function renameIgnoreError(oldPath: string, newPath: string): Promise { - return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve())); -} - -export function readlink(path: string): Promise { - return promisify(fs.readlink)(path); -} - -export function unlink(path: string): Promise { - return promisify(fs.unlink)(path); -} - -export function symlink(target: string, path: string, type?: string): Promise { - return promisify(fs.symlink)(target, path, type); -} - -export function truncate(path: string, len: number): Promise { - return promisify(fs.truncate)(path, len); -} - -export function readFile(path: string): Promise; -export function readFile(path: string, encoding: string): Promise; -export function readFile(path: string, encoding?: string): Promise { - return promisify(fs.readFile)(path, encoding); -} +//#region Write File // According to node.js docs (https://nodejs.org/docs/v6.5.0/api/fs.html#fs_fs_writefile_file_data_options_callback) // it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return. @@ -422,70 +461,16 @@ function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptio }; } -export async function readDirsInDir(dirPath: string): Promise { - const children = await readdir(dirPath); - const directories: string[] = []; +//#endregion - for (const child of children) { - if (await dirExists(join(dirPath, child))) { - directories.push(child); - } - } - - return directories; -} - -export async function dirExists(path: string): Promise { - try { - const { stat, symbolicLink } = await statLink(path); - - return stat.isDirectory() && symbolicLink?.dangling !== true; - } catch (error) { - // Ignore, path might not exist - } - - return false; -} - -export async function fileExists(path: string): Promise { - try { - const { stat, symbolicLink } = await statLink(path); - - return stat.isFile() && symbolicLink?.dangling !== true; - } catch (error) { - // Ignore, path might not exist - } - - return false; -} - -export function whenDeleted(path: string): Promise { - - // Complete when wait marker file is deleted - return new Promise(resolve => { - let running = false; - const interval = setInterval(() => { - if (!running) { - running = true; - fs.exists(path, exists => { - running = false; - - if (!exists) { - clearInterval(interval); - resolve(undefined); - } - }); - } - }, 1000); - }); -} +//#region Move / Copy export async function move(source: string, target: string): Promise { if (source === target) { return; // simulate node.js behaviour here and do a no-op if paths match } - // We have been updating `mtime` for move operations since the + // We have been updating `mtime` for move operations for files since the // beginning for reasons that are no longer quite clear, but changing // this could be risky as well. As such, trying to reason about it: // It is very common as developer to have file watchers enabled that watch @@ -554,7 +539,7 @@ export async function copy(source: string, target: string, handledSourcesIn?: { handledSources[source] = true; } - const { stat, symbolicLink } = await statLink(source); + const { stat, symbolicLink } = await SymlinkSupport.stat(source); if (symbolicLink?.dangling) { return; // skip over dangling symbolic links (https://github.com/microsoft/vscode/issues/111621) } @@ -605,3 +590,61 @@ async function doCopyFile(source: string, target: string, mode: number): Promise reader.pipe(writer); }); } + +//#endregion + +//#region Async FS Methods + +export async function exists(path: string): Promise { + try { + await fs.promises.access(path); + + return true; + } catch { + return false; + } +} + +export function chmod(path: string, mode: number): Promise { + return promisify(fs.chmod)(path, mode); +} + +export function stat(path: string): Promise { + return promisify(fs.stat)(path); +} + +export function lstat(path: string): Promise { + return promisify(fs.lstat)(path); +} + +export function rename(oldPath: string, newPath: string): Promise { + return promisify(fs.rename)(oldPath, newPath); +} + +export function renameIgnoreError(oldPath: string, newPath: string): Promise { + return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve())); +} + +export function readlink(path: string): Promise { + return promisify(fs.readlink)(path); +} + +export function unlink(path: string): Promise { + return promisify(fs.unlink)(path); +} + +export function symlink(target: string, path: string, type?: string): Promise { + return promisify(fs.symlink)(target, path, type); +} + +export function truncate(path: string, len: number): Promise { + return promisify(fs.truncate)(path, len); +} + +export function readFile(path: string): Promise; +export function readFile(path: string, encoding: string): Promise; +export function readFile(path: string, encoding?: string): Promise { + return promisify(fs.readFile)(path, encoding); +} + +//#endregion diff --git a/src/vs/base/node/powershell.ts b/src/vs/base/node/powershell.ts index 29a7f594452..f99e1162de9 100644 --- a/src/vs/base/node/powershell.ts +++ b/src/vs/base/node/powershell.ts @@ -38,7 +38,7 @@ class PossiblePowerShellExe implements IPossiblePowerShellExe { public async exists(): Promise { if (this.knownToExist === undefined) { - this.knownToExist = await pfs.fileExists(this.exePath); + this.knownToExist = await pfs.SymlinkSupport.fileExists(this.exePath); } return this.knownToExist; } @@ -100,7 +100,7 @@ async function findPSCoreWindowsInstallation( const powerShellInstallBaseDir = path.join(programFilesPath, 'PowerShell'); // Ensure the base directory exists - if (!await pfs.dirExists(powerShellInstallBaseDir)) { + if (!await pfs.SymlinkSupport.dirExists(powerShellInstallBaseDir)) { return null; } @@ -142,7 +142,7 @@ async function findPSCoreWindowsInstallation( // Now look for the file const exePath = path.join(powerShellInstallBaseDir, item, 'pwsh.exe'); - if (!await pfs.fileExists(exePath)) { + if (!await pfs.SymlinkSupport.fileExists(exePath)) { continue; } @@ -169,7 +169,7 @@ async function findPSCoreMsix({ findPreview }: { findPreview?: boolean } = {}): // Find the base directory for MSIX application exe shortcuts const msixAppDir = path.join(env.LOCALAPPDATA, 'Microsoft', 'WindowsApps'); - if (!await pfs.dirExists(msixAppDir)) { + if (!await pfs.SymlinkSupport.dirExists(msixAppDir)) { return null; } diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts index 770a60615ca..31d8e6057ad 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -8,7 +8,7 @@ import * as fs from 'fs'; import { tmpdir } from 'os'; import { join, sep } from 'vs/base/common/path'; import { generateUuid } from 'vs/base/common/uuid'; -import { copy, exists, move, readdir, readDirsInDir, readdirWithFileTypes, readFile, renameIgnoreError, rimraf, RimRafMode, rimrafSync, statLink, writeFile, writeFileSync } from 'vs/base/node/pfs'; +import { copy, exists, move, readdir, readDirsInDir, readdirWithFileTypes, readFile, renameIgnoreError, rimraf, RimRafMode, rimrafSync, SymlinkSupport, writeFile, writeFileSync } from 'vs/base/node/pfs'; import { timeout } from 'vs/base/common/async'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { canNormalize } from 'vs/base/common/normalization'; @@ -240,10 +240,10 @@ flakySuite('PFS', function () { fs.symlinkSync(directory, symbolicLink, 'junction'); - let statAndIsLink = await statLink(directory); + let statAndIsLink = await SymlinkSupport.stat(directory); assert.ok(!statAndIsLink?.symbolicLink); - statAndIsLink = await statLink(symbolicLink); + statAndIsLink = await SymlinkSupport.stat(symbolicLink); assert.ok(statAndIsLink?.symbolicLink); assert.ok(!statAndIsLink?.symbolicLink?.dangling); }); @@ -261,7 +261,7 @@ flakySuite('PFS', function () { await rimraf(directory); - const statAndIsLink = await statLink(symbolicLink); + const statAndIsLink = await SymlinkSupport.stat(symbolicLink); assert.ok(statAndIsLink?.symbolicLink); assert.ok(statAndIsLink?.symbolicLink?.dangling); }); diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 42f5fb335cd..1f3b67af11a 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; -import * as crypto from 'crypto'; -import * as path from 'vs/base/common/path'; -import * as platform from 'vs/base/common/platform'; +import { createHash } from 'crypto'; +import { join } from 'vs/base/common/path'; +import { isLinux } from 'vs/base/common/platform'; import { writeFileSync, writeFile, readFile, readdir, exists, rimraf, rename, RimRafMode } from 'vs/base/node/pfs'; import { IBackupMainService, IWorkspaceBackupInfo, isWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup'; import { IBackupWorkspacesFormat, IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup'; @@ -35,7 +35,7 @@ export class BackupMainService implements IBackupMainService { // - ignore path casing on Windows/macOS // - respect path casing on Linux private readonly backupUriComparer = extUriBiasedIgnorePathCase; - private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !platform.isLinux) }; + private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !isLinux) }; constructor( @IEnvironmentMainService environmentService: IEnvironmentMainService, @@ -204,7 +204,7 @@ export class BackupMainService implements IBackupMainService { } private getBackupPath(oldFolderHash: string): string { - return path.join(this.backupHome, oldFolderHash); + return join(this.backupHome, oldFolderHash); } private async validateWorkspaces(rootWorkspaces: IWorkspaceBackupInfo[]): Promise { @@ -406,7 +406,7 @@ export class BackupMainService implements IBackupMainService { for (const backupSchema of backupSchemas) { try { - const backupSchemaChildren = await readdir(path.join(backupPath, backupSchema)); + const backupSchemaChildren = await readdir(join(backupPath, backupSchema)); if (backupSchemaChildren.length > 0) { return true; } @@ -454,11 +454,11 @@ export class BackupMainService implements IBackupMainService { if (folderUri.scheme === Schemas.file) { // for backward compatibility, use the fspath as key - key = platform.isLinux ? folderUri.fsPath : folderUri.fsPath.toLowerCase(); + key = isLinux ? folderUri.fsPath : folderUri.fsPath.toLowerCase(); } else { key = folderUri.toString().toLowerCase(); } - return crypto.createHash('md5').update(key).digest('hex'); + return createHash('md5').update(key).digest('hex'); } } diff --git a/src/vs/platform/files/node/diskFileSystemProvider.ts b/src/vs/platform/files/node/diskFileSystemProvider.ts index 75811f569e9..baed33b3335 100644 --- a/src/vs/platform/files/node/diskFileSystemProvider.ts +++ b/src/vs/platform/files/node/diskFileSystemProvider.ts @@ -10,7 +10,7 @@ import { FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, File import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { isLinux, isWindows } from 'vs/base/common/platform'; -import { statLink, unlink, move, copy, readFile, truncate, rimraf, RimRafMode, exists, readdirWithFileTypes } from 'vs/base/node/pfs'; +import { SymlinkSupport, unlink, move, copy, readFile, truncate, rimraf, RimRafMode, exists, readdirWithFileTypes } from 'vs/base/node/pfs'; import { normalize, basename, dirname } from 'vs/base/common/path'; import { joinPath } from 'vs/base/common/resources'; import { isEqual } from 'vs/base/common/extpath'; @@ -80,7 +80,7 @@ export class DiskFileSystemProvider extends Disposable implements async stat(resource: URI): Promise { try { - const { stat, symbolicLink } = await statLink(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly + const { stat, symbolicLink } = await SymlinkSupport.stat(this.toFilePath(resource)); // cannot use fs.stat() here to support links properly return { type: this.toType(stat, symbolicLink), diff --git a/src/vs/platform/files/node/watcher/nodejs/watcherService.ts b/src/vs/platform/files/node/watcher/nodejs/watcherService.ts index 16c5428714b..2a68be01737 100644 --- a/src/vs/platform/files/node/watcher/nodejs/watcherService.ts +++ b/src/vs/platform/files/node/watcher/nodejs/watcherService.ts @@ -5,7 +5,7 @@ import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/platform/files/node/watcher/watcher'; import { Disposable } from 'vs/base/common/lifecycle'; -import { statLink } from 'vs/base/node/pfs'; +import { SymlinkSupport } from 'vs/base/node/pfs'; import { realpath } from 'vs/base/node/extpath'; import { watchFolder, watchFile, CHANGE_BUFFER_DELAY } from 'vs/base/node/watcher'; import { FileChangeType } from 'vs/platform/files/common/files'; @@ -35,7 +35,7 @@ export class FileWatcher extends Disposable { private async startWatching(): Promise { try { - const { stat, symbolicLink } = await statLink(this.path); + const { stat, symbolicLink } = await SymlinkSupport.stat(this.path); if (this.isDisposed) { return; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 50c4460ad4a..97b12c744be 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -15,7 +15,7 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { AddFirstParameterToFunctions } from 'vs/base/common/types'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService'; -import { dirExists } from 'vs/base/node/pfs'; +import { SymlinkSupport } from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -261,7 +261,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain const paths = await this.dialogMainService.pickFileFolder(options); if (paths) { this.sendPickerTelemetry(paths, options.telemetryEventName || 'openFileFolder', options.telemetryExtraData); - this.doOpenPicked(await Promise.all(paths.map(async path => (await dirExists(path)) ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) })), options, windowId); + this.doOpenPicked(await Promise.all(paths.map(async path => (await SymlinkSupport.dirExists(path)) ? { folderUri: URI.file(path) } : { fileUri: URI.file(path) })), options, windowId); } } diff --git a/src/vs/workbench/api/node/extHostOutputService.ts b/src/vs/workbench/api/node/extHostOutputService.ts index 358507bdf22..2204c403d32 100644 --- a/src/vs/workbench/api/node/extHostOutputService.ts +++ b/src/vs/workbench/api/node/extHostOutputService.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/path'; import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender'; import { toLocalISOString } from 'vs/base/common/date'; -import { dirExists } from 'vs/base/node/pfs'; +import { SymlinkSupport } from 'vs/base/node/pfs'; import { promises } from 'fs'; import { AbstractExtHostOutputChannel, ExtHostPushOutputChannel, ExtHostOutputService, LazyOutputChannel } from 'vs/workbench/api/common/extHostOutput'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; @@ -86,7 +86,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService { private async _doCreateOutChannel(name: string): Promise { try { const outputDirPath = join(this._logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); - const exists = await dirExists(outputDirPath); + const exists = await SymlinkSupport.dirExists(outputDirPath); if (!exists) { await promises.mkdir(outputDirPath, { recursive: true }); } diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index ff36c4e6331..9be699a7ea9 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -5,7 +5,7 @@ import * as os from 'os'; import * as platform from 'vs/base/common/platform'; -import { readFile, fileExists, stat, lstat } from 'vs/base/node/pfs'; +import { readFile, SymlinkSupport, stat, lstat } from 'vs/base/node/pfs'; import { LinuxDistro, IShellDefinition } from 'vs/workbench/contrib/terminal/common/terminal'; import { coalesce } from 'vs/base/common/arrays'; import { normalize, basename } from 'vs/base/common/path'; @@ -14,7 +14,7 @@ import { enumeratePowerShellInstallations } from 'vs/base/node/powershell'; let detectedDistro = LinuxDistro.Unknown; if (platform.isLinux) { const file = '/etc/os-release'; - fileExists(file).then(async exists => { + SymlinkSupport.fileExists(file).then(async exists => { if (!exists) { return; } diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index c19a1f5ba58..bef6b6e9235 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -144,7 +144,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { return { values: undefined, default: `${basename}.nls.json` }; }); } else { - localizedMessages = pfs.fileExists(basename + '.nls' + extension).then(exists => { + localizedMessages = pfs.SymlinkSupport.fileExists(basename + '.nls' + extension).then(exists => { if (!exists) { return undefined; } @@ -220,7 +220,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler { return new Promise<{ localized: string; original: string | null; }>((c, e) => { function loop(basename: string, locale: string): void { let toCheck = `${basename}.nls.${locale}.json`; - pfs.fileExists(toCheck).then(exists => { + pfs.SymlinkSupport.fileExists(toCheck).then(exists => { if (exists) { c({ localized: toCheck, original: `${basename}.nls.json` }); } @@ -597,7 +597,7 @@ export class ExtensionScanner { const isBuiltin = input.isBuiltin; const isUnderDevelopment = input.isUnderDevelopment; - return pfs.fileExists(path.join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => { + return pfs.SymlinkSupport.fileExists(path.join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => { if (exists) { const nlsConfig = ExtensionScannerInput.createNLSConfig(input); return this.scanExtension(input.ourVersion, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig).then((extensionDescription) => {