Use 'r+' with truncation when saving existing files on Windows (#42899)

* Use 'r+' with truncation when saving existing files on Windows

Opening a file with 'w' flag removes its alternate data streams. To
prevent this, extend the logic used for hidden files to all existing
files.

Fixes: https://github.com/Microsoft/vscode/issues/6363

* adopt truncate for saving as admin
This commit is contained in:
Nikolai Vavilov 2018-08-20 19:55:12 +03:00 committed by Benjamin Pasero
parent ff8009d5d3
commit aaa778b5e2
2 changed files with 21 additions and 21 deletions

View file

@ -87,18 +87,17 @@ export async function main(argv: string[]): Promise<any> {
// Write source to target // Write source to target
const data = fs.readFileSync(source); const data = fs.readFileSync(source);
try { if (isWindows) {
writeFileAndFlushSync(target, data); // On Windows we use a different strategy of saving the file
} catch (error) { // by first truncating the file and then writing with r+ mode.
// On Windows and if the file exists with an EPERM error, we try a different strategy of saving the file // This helps to save hidden files on Windows
// by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows // (see https://github.com/Microsoft/vscode/issues/931) and
// (see https://github.com/Microsoft/vscode/issues/931) // prevent removing alternate data streams
if (isWindows && error.code === 'EPERM') { // (see https://github.com/Microsoft/vscode/issues/6363)
fs.truncateSync(target, 0); fs.truncateSync(target, 0);
writeFileAndFlushSync(target, data, { flag: 'r+' }); writeFileAndFlushSync(target, data, { flag: 'r+' });
} else { } else {
throw error; writeFileAndFlushSync(target, data);
}
} }
// Restore previous mode as needed // Restore previous mode as needed

View file

@ -607,22 +607,23 @@ export class FileService extends Disposable implements IFileService {
return addBomPromise.then(addBom => { return addBomPromise.then(addBom => {
// 4.) set contents and resolve // 4.) set contents and resolve
return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite).then(void 0, error => { if (!exists || !isWindows) {
if (!exists || error.code !== 'EPERM' || !isWindows) { return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite);
return TPromise.wrapError(error);
} }
// On Windows and if the file exists with an EPERM error, we try a different strategy of saving the file // On Windows and if the file exists, we use a different strategy of saving the file
// by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows // by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows
// (see https://github.com/Microsoft/vscode/issues/931) // (see https://github.com/Microsoft/vscode/issues/931) and prevent removing alternate data streams
// (see https://github.com/Microsoft/vscode/issues/6363)
else {
// 5.) truncate // 4.) truncate
return pfs.truncate(absolutePath, 0).then(() => { return pfs.truncate(absolutePath, 0).then(() => {
// 6.) set contents (this time with r+ mode) and resolve again // 5.) set contents (with r+ mode) and resolve
return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' }); return this.doSetContentsAndResolve(resource, absolutePath, value, addBom, encodingToWrite, { flag: 'r+' });
}); });
}); }
}); });
}); });
}).then(null, error => { }).then(null, error => {