mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode 52dcb723a39ae75bee1bd56b3312d7fcdc87aeed (#6719)
This commit is contained in:
@@ -106,20 +106,15 @@ export class BackupFilesModel implements IBackupFilesModel {
|
||||
|
||||
export class BackupFileService implements IBackupFileService {
|
||||
|
||||
_serviceBrand: ServiceIdentifier<IBackupFileService>;
|
||||
_serviceBrand!: ServiceIdentifier<IBackupFileService>;
|
||||
|
||||
private impl: IBackupFileService;
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IFileService fileService: IFileService
|
||||
@IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService,
|
||||
@IFileService protected fileService: IFileService
|
||||
) {
|
||||
const backupWorkspaceResource = environmentService.configuration.backupWorkspaceResource;
|
||||
if (backupWorkspaceResource) {
|
||||
this.impl = new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, fileService);
|
||||
} else {
|
||||
this.impl = new InMemoryBackupFileService(this.hashPath);
|
||||
}
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
protected hashPath(resource: URI): string {
|
||||
@@ -128,9 +123,25 @@ export class BackupFileService implements IBackupFileService {
|
||||
return hash(str).toString(16);
|
||||
}
|
||||
|
||||
initialize(backupWorkspaceResource: URI): void {
|
||||
private initialize(): void {
|
||||
const backupWorkspaceResource = this.environmentService.configuration.backupWorkspaceResource;
|
||||
if (backupWorkspaceResource) {
|
||||
this.impl = new BackupFileServiceImpl(backupWorkspaceResource, this.hashPath, this.fileService);
|
||||
} else {
|
||||
this.impl = new InMemoryBackupFileService(this.hashPath);
|
||||
}
|
||||
}
|
||||
|
||||
reinitialize(): void {
|
||||
|
||||
// Re-init implementation (unless we are running in-memory)
|
||||
if (this.impl instanceof BackupFileServiceImpl) {
|
||||
this.impl.initialize(backupWorkspaceResource);
|
||||
const backupWorkspaceResource = this.environmentService.configuration.backupWorkspaceResource;
|
||||
if (backupWorkspaceResource) {
|
||||
this.impl.initialize(backupWorkspaceResource);
|
||||
} else {
|
||||
this.impl = new InMemoryBackupFileService(this.hashPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,15 +188,15 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
private static readonly PREAMBLE_META_SEPARATOR = ' '; // using a character that is know to be escaped in a URI as separator
|
||||
private static readonly PREAMBLE_MAX_LENGTH = 10000;
|
||||
|
||||
_serviceBrand: ServiceIdentifier<IBackupFileService>;
|
||||
_serviceBrand!: ServiceIdentifier<IBackupFileService>;
|
||||
|
||||
private backupWorkspacePath: URI;
|
||||
private backupWorkspacePath!: URI;
|
||||
|
||||
private isShuttingDown: boolean;
|
||||
private ioOperationQueues: ResourceQueue; // queue IO operations to ensure write order
|
||||
|
||||
private ready: Promise<IBackupFilesModel>;
|
||||
private model: IBackupFilesModel;
|
||||
private ready!: Promise<IBackupFilesModel>;
|
||||
private model!: IBackupFilesModel;
|
||||
|
||||
constructor(
|
||||
backupWorkspaceResource: URI,
|
||||
@@ -201,10 +212,10 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
initialize(backupWorkspaceResource: URI): void {
|
||||
this.backupWorkspacePath = backupWorkspaceResource;
|
||||
|
||||
this.ready = this.init();
|
||||
this.ready = this.doInitialize();
|
||||
}
|
||||
|
||||
private init(): Promise<IBackupFilesModel> {
|
||||
private doInitialize(): Promise<IBackupFilesModel> {
|
||||
this.model = new BackupFilesModel(this.fileService);
|
||||
|
||||
return this.model.resolve(this.backupWorkspacePath);
|
||||
@@ -323,7 +334,7 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
return contents.substr(0, newLineIndex);
|
||||
}
|
||||
|
||||
throw new Error(`Could not find ${JSON.stringify(matchingString)} in first ${maximumBytesToRead} bytes of ${file}`);
|
||||
throw new Error(`Backup: Could not find ${JSON.stringify(matchingString)} in first ${maximumBytesToRead} bytes of ${file}`);
|
||||
}
|
||||
|
||||
async resolveBackupContent<T extends object>(backup: URI): Promise<IResolvedBackup<T>> {
|
||||
@@ -357,9 +368,7 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
const content = await this.fileService.readFileStream(backup);
|
||||
const factory = await createTextBufferFactoryFromStream(content.value, metaPreambleFilter);
|
||||
|
||||
// Trigger read for meta data extraction from the filter above
|
||||
factory.getFirstLineText(1);
|
||||
|
||||
// Extract meta data (if any)
|
||||
let meta: T | undefined;
|
||||
const metaStartIndex = metaRaw.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR);
|
||||
if (metaStartIndex !== -1) {
|
||||
@@ -370,6 +379,15 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
}
|
||||
}
|
||||
|
||||
// We have seen reports (e.g. https://github.com/microsoft/vscode/issues/78500) where
|
||||
// if VSCode goes down while writing the backup file, the file can turn empty because
|
||||
// it always first gets truncated and then written to. In this case, we will not find
|
||||
// the meta-end marker ('\n') and as such the backup can only be invalid. We bail out
|
||||
// here if that is the case.
|
||||
if (!metaEndFound) {
|
||||
throw new Error(`Backup: Could not find meta end marker in ${backup}. The file is probably corrupt.`);
|
||||
}
|
||||
|
||||
return { value: factory, meta };
|
||||
}
|
||||
|
||||
@@ -380,7 +398,7 @@ class BackupFileServiceImpl implements IBackupFileService {
|
||||
|
||||
export class InMemoryBackupFileService implements IBackupFileService {
|
||||
|
||||
_serviceBrand: ServiceIdentifier<IBackupFileService>;
|
||||
_serviceBrand!: ServiceIdentifier<IBackupFileService>;
|
||||
|
||||
private backups: Map<string, ITextSnapshot> = new Map();
|
||||
|
||||
@@ -440,4 +458,4 @@ export class InMemoryBackupFileService implements IBackupFileService {
|
||||
toBackupResource(resource: URI): URI {
|
||||
return URI.file(join(resource.scheme, this.hashPath(resource)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,14 @@ import { BackupFileService as CommonBackupFileService } from 'vs/workbench/servi
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as crypto from 'crypto';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
|
||||
export class BackupFileService extends CommonBackupFileService {
|
||||
|
||||
protected hashPath(resource: URI): string {
|
||||
return hashPath(resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -21,5 +22,8 @@ export class BackupFileService extends CommonBackupFileService {
|
||||
*/
|
||||
export function hashPath(resource: URI): string {
|
||||
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
|
||||
|
||||
return crypto.createHash('md5').update(str).digest('hex');
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IBackupFileService, BackupFileService);
|
||||
|
||||
@@ -27,6 +27,7 @@ import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { hashPath, BackupFileService } from 'vs/workbench/services/backup/node/backupFileService';
|
||||
import { BACKUPS } from 'vs/platform/environment/common/environment';
|
||||
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
|
||||
const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice');
|
||||
const appSettingsHome = path.join(userdataDir, 'User');
|
||||
@@ -479,6 +480,28 @@ suite('BackupFileService', () => {
|
||||
await testResolveBackup(fooBarFile, contents, meta, null);
|
||||
});
|
||||
|
||||
test('should throw an error when restoring invalid backup', async () => {
|
||||
const contents = 'test\nand more stuff';
|
||||
|
||||
await service.backupResource(fooBarFile, createTextBufferFactory(contents).create(DefaultEndOfLine.LF).createSnapshot(false), 1);
|
||||
|
||||
const backup = await service.loadBackupResource(fooBarFile);
|
||||
if (!backup) {
|
||||
throw new Error('Unexpected missing backup');
|
||||
}
|
||||
|
||||
await service.fileService.writeFile(backup, VSBuffer.fromString(''));
|
||||
|
||||
let err: Error;
|
||||
try {
|
||||
await service.resolveBackupContent<IBackupTestMetaData>(backup);
|
||||
} catch (error) {
|
||||
err = error;
|
||||
}
|
||||
|
||||
assert.ok(err!);
|
||||
});
|
||||
|
||||
async function testResolveBackup(resource: URI, contents: string, meta?: IBackupTestMetaData, expectedMeta?: IBackupTestMetaData | null) {
|
||||
if (typeof expectedMeta === 'undefined') {
|
||||
expectedMeta = meta;
|
||||
|
||||
Reference in New Issue
Block a user