Skip to content

Commit f8a77b8

Browse files
authored
Merge pull request microsoft#182042 from microsoft/sandy081/comparable-skink
Use recovery fix in main
2 parents 83e7b4d + 8669224 commit f8a77b8

File tree

1 file changed

+39
-5
lines changed

1 file changed

+39
-5
lines changed

src/vs/platform/extensionManagement/node/extensionManagementService.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { IStringDictionary } from 'vs/base/common/collections';
1010
import { toErrorMessage } from 'vs/base/common/errorMessage';
1111
import { getErrorMessage } from 'vs/base/common/errors';
1212
import { Emitter } from 'vs/base/common/event';
13-
import { randomPath } from 'vs/base/common/extpath';
1413
import { Disposable } from 'vs/base/common/lifecycle';
1514
import { ResourceSet } from 'vs/base/common/map';
1615
import { Schemas } from 'vs/base/common/network';
@@ -20,7 +19,7 @@ import { joinPath } from 'vs/base/common/resources';
2019
import * as semver from 'vs/base/common/semver/semver';
2120
import { isBoolean, isUndefined } from 'vs/base/common/types';
2221
import { URI } from 'vs/base/common/uri';
23-
import { generateUuid } from 'vs/base/common/uuid';
22+
import { generateUuid, isUUID } from 'vs/base/common/uuid';
2423
import * as pfs from 'vs/base/node/pfs';
2524
import { extract, ExtractError, IFile, zip } from 'vs/base/node/zip';
2625
import * as nls from 'vs/nls';
@@ -410,12 +409,14 @@ export class ExtensionsScanner extends Disposable {
410409
private readonly _onExtract = this._register(new Emitter<URI>());
411410
readonly onExtract = this._onExtract.event;
412411

412+
private cleanUpGeneratedFoldersPromise: Promise<void> = Promise.resolve();
413+
413414
constructor(
414415
private readonly beforeRemovingExtension: (e: ILocalExtension) => Promise<void>,
415416
@IFileService private readonly fileService: IFileService,
416417
@IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService,
417418
@IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
418-
@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService,
419+
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
419420
@ILogService private readonly logService: ILogService,
420421
) {
421422
super();
@@ -425,6 +426,8 @@ export class ExtensionsScanner extends Disposable {
425426

426427
async cleanUp(): Promise<void> {
427428
await this.removeUninstalledExtensions();
429+
this.cleanUpGeneratedFoldersPromise = this.cleanUpGeneratedFoldersPromise.then(() => this.removeGeneratedFolders());
430+
await this.cleanUpGeneratedFoldersPromise;
428431
}
429432

430433
async scanExtensions(type: ExtensionType | null, profileLocation: URI): Promise<ILocalExtension[]> {
@@ -457,8 +460,10 @@ export class ExtensionsScanner extends Disposable {
457460
}
458461

459462
async extractUserExtension(extensionKey: ExtensionKey, zipPath: string, metadata: Metadata, token: CancellationToken): Promise<ILocalExtension> {
463+
await this.cleanUpGeneratedFoldersPromise.catch(() => undefined);
464+
460465
const folderName = extensionKey.toString();
461-
const tempPath = randomPath(this.environmentService.tmpDir.fsPath);
466+
const tempPath = path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, `.${generateUuid()}`);
462467
const extensionPath = path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, folderName);
463468

464469
try {
@@ -526,7 +531,9 @@ export class ExtensionsScanner extends Disposable {
526531

527532
async removeExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise<void> {
528533
this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id, extension.location.fsPath);
529-
await this.fileService.del(extension.location, { recursive: true });
534+
const renamedLocation = this.uriIdentityService.extUri.joinPath(this.uriIdentityService.extUri.dirname(extension.location), `._${generateUuid()}`);
535+
await this.rename(extension.identifier, extension.location.fsPath, renamedLocation.fsPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */);
536+
await this.fileService.del(renamedLocation, { recursive: true });
530537
this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath);
531538
}
532539

@@ -687,6 +694,33 @@ export class ExtensionsScanner extends Disposable {
687694
await Promise.allSettled(toRemove.map(e => this.removeUninstalledExtension(e)));
688695
}
689696

697+
private async removeGeneratedFolders(): Promise<void> {
698+
this.logService.trace('ExtensionManagementService#removeGeneratedFolders');
699+
const promises: Promise<any>[] = [];
700+
let stat;
701+
try {
702+
stat = await this.fileService.resolve(this.extensionsScannerService.userExtensionsLocation);
703+
} catch (error) {
704+
if (toFileOperationResult(error) !== FileOperationResult.FILE_NOT_FOUND) {
705+
this.logService.error(error);
706+
}
707+
}
708+
for (const child of stat?.children ?? []) {
709+
if (child.isDirectory && child.name.startsWith('._') && isUUID(child.name.substring(2))) {
710+
promises.push((async () => {
711+
this.logService.trace('Deleting the generated extension folder', child.resource.toString());
712+
try {
713+
await this.fileService.del(child.resource, { recursive: true });
714+
this.logService.info('Deleted the generated extension folder', child.resource.toString());
715+
} catch (error) {
716+
this.logService.error(error);
717+
}
718+
})());
719+
}
720+
}
721+
await Promise.allSettled(promises);
722+
}
723+
690724
private joinErrors(errorOrErrors: (Error | string) | (Array<Error | string>)): Error {
691725
const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors];
692726
if (errors.length === 1) {

0 commit comments

Comments
 (0)