Skip to content

Commit

Permalink
environment - canonicalize value of homedir()
Browse files Browse the repository at this point in the history
In some environments, it is common for a user's home directory to be a
symbolic link (symlink) to a different filesystem location. For
example, for user 'jane' the value of the HOME environment variable is
'/home/jane' but '/home/jane' is actually a symbolic link to
'/some/place/else/homes/jane'. This setup is very common on high
performance computing (HPC) systems.

When such a user opens a folder in VS Code, the 'Open Folder' dialog
defaults to the user' HOME, i.e. '/home/jane'. If the user opens a
folder located somwhere in their home, this directory is opened by VS
Code unresolved, i.e. '/home/jane/project'.

When the users opens a file by Ctrl-clicking a file link in the
integrated terminal, the resolved file path is opened, i.e.
'/some/place/else/homes/jane/project/file'. This is suboptimal because
if 'file' was already opened in the workspace, opening it from the
integrated terminal opens a second tab instead of focusing on the
already opened tab, as VS Code does not recognize it is the same file.
This is made worse by the fact that some language extensions that
provide 'Outline' functionality do not work at all in this newly opened
tab from the resolved file path.

To fix this, canonicalize the value of a user's home directory early in
the environment initialization, by calling 'realpathSync' on the path
returned by Node's 'homedir()' in the constructor of
NativeEnvironmentService. This results in the resolved value of a user's
home directory being the default location for the 'Open Folder'
dialog. Since now the already opened 'file' and 'file' opened from the
integrated terminal have the same path, VS Code does recognize they are
indeed the same file, and focuses on the already opened tab instead of
opening a second tab.

Test this by temporarily changing the value of HOME (USERPROFILE on
Windows) to point to a symlink and verifying that the userHome property
of a newly created NativeEnvironmentService is returned as resolved.
Note that for some reason, in order for 'fs.rmSync' to correctly clean
up the symlink after the test, it has to be called with 'recursive:
true'.

Fixes: microsoft#118973
Fixes: microsoft#129023
  • Loading branch information
phil-blain committed Apr 9, 2022
1 parent e358a02 commit 60a9f31
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/vs/platform/environment/node/environmentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { homedir, tmpdir } from 'os';
import { realpathSync } from 'vs/base/node/extpath';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { AbstractNativeEnvironmentService } from 'vs/platform/environment/common/environmentService';
import { getUserDataPath } from 'vs/platform/environment/node/userDataPath';
Expand All @@ -13,7 +14,7 @@ export class NativeEnvironmentService extends AbstractNativeEnvironmentService {

constructor(args: NativeParsedArgs, productService: IProductService) {
super(args, {
homeDir: homedir(),
homeDir: realpathSync(homedir()),
tmpDir: tmpdir(),
userDataDir: getUserDataPath(args)
}, productService);
Expand Down
31 changes: 31 additions & 0 deletions src/vs/platform/environment/test/node/environmentService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
*--------------------------------------------------------------------------------------------*/

import * as assert from 'assert';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { URI } from 'vs/base/common/uri';
import { parseExtensionHostPort } from 'vs/platform/environment/common/environmentService';
import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv';
import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
Expand Down Expand Up @@ -67,4 +71,31 @@ suite('EnvironmentService', () => {
const service2 = new NativeEnvironmentService(args, { _serviceBrand: undefined, ...product });
assert.notStrictEqual(service1.userDataPath, service2.userDataPath);
});

// https://github.com/microsoft/vscode/issues/118973
// https://github.com/microsoft/vscode/issues/129023
test('symlinked HOME is canonicalized', () => {
function setHome(home: string) {
if (process.platform === 'win32') {
process.env.USERPROFILE = home;
} else {
process.env.HOME = home;
}
}

const oldHome = os.homedir();
const pwd = process.cwd();
const symlinkedHome = path.join(pwd, 'symlinked');
const canonicalHome = path.join(pwd, 'canonical');
fs.mkdirSync(canonicalHome);
fs.symlinkSync(canonicalHome, symlinkedHome);
setHome(symlinkedHome);

const service1 = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), { _serviceBrand: undefined, ...product });
assert.deepStrictEqual(service1.userHome, URI.file(canonicalHome));

fs.rmSync(symlinkedHome, { recursive: true });
fs.rmdirSync(canonicalHome);
setHome(oldHome);
});
});

0 comments on commit 60a9f31

Please sign in to comment.