diff --git a/src/client/datascience/interactive-ipynb/digestStorage.ts b/src/client/datascience/interactive-ipynb/digestStorage.ts index 0db3feed5fa5..620953bff53d 100644 --- a/src/client/datascience/interactive-ipynb/digestStorage.ts +++ b/src/client/datascience/interactive-ipynb/digestStorage.ts @@ -26,10 +26,20 @@ export class DigestStorage implements IDigestStorage { public async saveDigest(uri: Uri, signature: string) { const fileLocation = await this.getFileLocation(uri); // Since the signature is a hex digest, the character 'z' is being used to delimit the start and end of a single digest - await this.fs.appendFile(fileLocation, `z${signature}z\n`); - if (!this.loggedFileLocations.has(fileLocation)) { - traceInfo(`Wrote trust for ${uri.toString()} to ${fileLocation}`); - this.loggedFileLocations.add(fileLocation); + try { + await this.saveDigestInner(uri, fileLocation, signature); + } catch (err) { + // The nbsignatures dir is only initialized on extension activation. + // If the user deletes it to reset trust, the next attempt to trust + // an untrusted notebook in the same session will fail because the parent + // directory does not exist. + if (isFileNotFoundError(err)) { + // Gracefully recover from such errors by reinitializing directory and retrying + await this.initDir(); + await this.saveDigestInner(uri, fileLocation, signature); + } else { + traceError(err); + } } } @@ -46,6 +56,14 @@ export class DigestStorage implements IDigestStorage { } } + private async saveDigestInner(uri: Uri, fileLocation: string, signature: string) { + await this.fs.appendFile(fileLocation, `z${signature}z\n`); + if (!this.loggedFileLocations.has(fileLocation)) { + traceInfo(`Wrote trust for ${uri.toString()} to ${fileLocation}`); + this.loggedFileLocations.add(fileLocation); + } + } + private async getFileLocation(uri: Uri): Promise { const normalizedName = os.platform() === 'win32' ? uri.fsPath.toLowerCase() : uri.fsPath; const hashedName = createHash('sha256').update(normalizedName).digest('hex');