Save: Flush to disk after writing to file (fixes #9589)

This commit is contained in:
Benjamin Pasero 2016-08-09 11:28:21 +02:00
parent 6dd65cc0c5
commit 974bce453e
5 changed files with 83 additions and 17 deletions

View file

@ -1442,6 +1442,8 @@ declare module "fs" {
export function futimesSync(fd: number, atime: Date, mtime: Date): void;
export function fsync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
export function fsyncSync(fd: number): void;
export function fdatasync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void;
export function fdatasyncSync(fd: number): void;
export function write(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void;
export function write(fd: number, buffer: Buffer, offset: number, length: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void;
export function write(fd: number, data: any, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void;
@ -1503,9 +1505,9 @@ declare module "fs" {
* @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer.
*/
export function readFileSync(filename: string, options?: { flag?: string; }): Buffer;
export function writeFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void;
export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
export function writeFile(filename: string|number, data: any, callback?: (err: NodeJS.ErrnoException) => void): void;
export function writeFile(filename: string|number, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
export function writeFile(filename: string|number, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;
export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void;
export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void;
export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;

View file

@ -312,4 +312,36 @@ export function mv(source: string, target: string, callback: (error: Error) => v
return callback(err);
});
}
// Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk
// We do this in cases where we want to make sure the data is really on disk and
// not in some cache.
//
// See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194
export function writeFileAndFlush(path: string, data: string | NodeBuffer, options: { encoding?: string; mode?: number; flag?: string; }, callback: (error: Error) => void): void {
if (!options) {
options = { encoding: 'utf8', mode: 0o666, flag: 'w' };
} else if (typeof options === 'string') {
options = { encoding: <string>options, mode: 0o666, flag: 'w' };
}
// Open the file with same flags and mode as fs.writeFile()
fs.open(path, options.flag, options.mode, (openError, fd) => {
if (openError) {
return callback(openError);
}
// It is valid to pass a fd handle to fs.writeFile() and this will keep the handle open!
fs.writeFile(fd, data, options.encoding, (writeError) => {
if (writeError) {
return fs.close(fd, () => callback(writeError)); // still need to close the handle on error!
}
// Flush contents (not metadata) of the file to disk
fs.fdatasync(fd, (syncError) => {
return fs.close(fd, (closeError) => callback(syncError || closeError)); // make sure to carry over the fdatasync error if any!
});
});
});
}

View file

@ -135,6 +135,12 @@ export function writeFile(path: string, data: any, encoding: string = 'utf8'): P
return nfcall(fs.writeFile, path, data, encoding);
}
export function writeFileAndFlush(path: string, data: string, encoding?: string): Promise;
export function writeFileAndFlush(path: string, data: NodeBuffer, encoding?: string): Promise;
export function writeFileAndFlush(path: string, data: any, encoding: string = 'utf8'): Promise {
return nfcall(extfs.writeFileAndFlush, path, data, encoding);
}
/**
* Read a dir and return only subfolders
*/

View file

@ -18,9 +18,9 @@ import extfs = require('vs/base/node/extfs');
suite('Extfs', () => {
test('mkdirp', function (done: () => void) {
var id = uuid.generateUuid();
var parentDir = path.join(os.tmpdir(), 'vsctests', id);
var newDir = path.join(parentDir, 'extfs', id);
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'extfs', id);
extfs.mkdirp(newDir, 493, (error) => {
assert.ok(!error);
@ -31,12 +31,12 @@ suite('Extfs', () => {
});
test('copy, move and delete', function (done: () => void) {
var id = uuid.generateUuid();
var id2 = uuid.generateUuid();
var sourceDir = require.toUrl('./fixtures');
var parentDir = path.join(os.tmpdir(), 'vsctests', 'extfs');
var targetDir = path.join(parentDir, id);
var targetDir2 = path.join(parentDir, id2);
const id = uuid.generateUuid();
const id2 = uuid.generateUuid();
const sourceDir = require.toUrl('./fixtures');
const parentDir = path.join(os.tmpdir(), 'vsctests', 'extfs');
const targetDir = path.join(parentDir, id);
const targetDir2 = path.join(parentDir, id2);
extfs.copy(sourceDir, targetDir, (error) => {
assert.ok(!error);
@ -76,9 +76,9 @@ suite('Extfs', () => {
test('readdir', function (done: () => void) {
if (strings.canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) {
var id = uuid.generateUuid();
var parentDir = path.join(os.tmpdir(), 'vsctests', id);
var newDir = path.join(parentDir, 'extfs', id, 'öäü');
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'extfs', id, 'öäü');
extfs.mkdirp(newDir, 493, (error) => {
assert.ok(!error);
@ -94,4 +94,30 @@ suite('Extfs', () => {
done();
}
});
test('writeFileAndFlush', function (done: () => void) {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'extfs', id);
const testFile = path.join(newDir, 'flushed.txt');
extfs.mkdirp(newDir, 493, (error) => {
assert.ok(!error);
assert.ok(fs.existsSync(newDir));
extfs.writeFileAndFlush(testFile, 'Hello World', null, (error) => {
assert.ok(!error);
assert.equal(fs.readFileSync(testFile), 'Hello World');
const largeString = (new Array(100 * 1024)).join('Large String\n');
extfs.writeFileAndFlush(testFile, largeString, null, (error) => {
assert.ok(!error);
assert.equal(fs.readFileSync(testFile), largeString);
done();
});
});
});
});
});

View file

@ -272,13 +272,13 @@ export class FileService implements IFileService {
// Write fast if we do UTF 8 without BOM
if (!addBom && encodingToWrite === encoding.UTF8) {
writeFilePromise = pfs.writeFile(absolutePath, value, encoding.UTF8);
writeFilePromise = pfs.writeFileAndFlush(absolutePath, value, encoding.UTF8);
}
// Otherwise use encoding lib
else {
let encoded = encoding.encode(value, encodingToWrite, { addBOM: addBom });
writeFilePromise = pfs.writeFile(absolutePath, encoded);
writeFilePromise = pfs.writeFileAndFlush(absolutePath, encoded);
}
// 4.) set contents