From 0928a526dd5e15a4792ad8186fc85a9164ff6493 Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 1 Mar 2013 09:10:26 -0800 Subject: [PATCH] fs: Support mode/flag options to read/append/writeFile Fix #4841 --- doc/api/fs.markdown | 44 ++++++++--- lib/fs.js | 128 +++++++++++++++++++++----------- test/simple/test-fs-append-file-sync.js | 11 ++- test/simple/test-fs-append-file.js | 9 ++- test/simple/test-fs-write-file.js | 9 ++- 5 files changed, 142 insertions(+), 59 deletions(-) diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index 100bcc1..29d3aa3 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -410,7 +410,12 @@ The callback is given the three arguments, `(err, bytesRead, buffer)`. Synchronous version of `fs.read`. Returns the number of `bytesRead`. -## fs.readFile(filename, [encoding], [callback]) +## fs.readFile(filename, [options], [callback]) + +* `filename` {String} +* `options` {Object} + * `encoding` {String | Null} default = `null` + * `flag` {String} default = `'r'` Asynchronously reads the entire contents of a file. Example: @@ -425,19 +430,28 @@ contents of the file. If no encoding is specified, then the raw buffer is returned. -## fs.readFileSync(filename, [encoding]) +## fs.readFileSync(filename, [options]) Synchronous version of `fs.readFile`. Returns the contents of the `filename`. -If `encoding` is specified then this function returns a string. Otherwise it -returns a buffer. +If the `encoding` option is specified then this function returns a +string. Otherwise it returns a buffer. + +## fs.writeFile(filename, data, [options], [callback]) -## fs.writeFile(filename, data, [encoding], [callback]) +* `filename` {String} +* `data` {String | Buffer} +* `options` {Object} + * `encoding` {String | Null} default = `'utf8'` + * `mode` {Number} default = `438` (aka `0666` in Octal) + * `flag` {String} default = `'w'` Asynchronously writes data to a file, replacing the file if it already exists. -`data` can be a string or a buffer. The `encoding` argument is ignored if -`data` is a buffer. It defaults to `'utf8'`. +`data` can be a string or a buffer. + +The `encoding` option is ignored if `data` is a buffer. It defaults +to `'utf8'`. Example: @@ -446,15 +460,21 @@ Example: console.log('It\'s saved!'); }); -## fs.writeFileSync(filename, data, [encoding]) +## fs.writeFileSync(filename, data, [options]) The synchronous version of `fs.writeFile`. -## fs.appendFile(filename, data, encoding='utf8', [callback]) +## fs.appendFile(filename, data, [options], [callback]) + +* `filename` {String} +* `data` {String | Buffer} +* `options` {Object} + * `encoding` {String | Null} default = `'utf8'` + * `mode` {Number} default = `438` (aka `0666` in Octal) + * `flag` {String} default = `'a'` Asynchronously append data to a file, creating the file if it not yet exists. -`data` can be a string or a buffer. The `encoding` argument is ignored if -`data` is a buffer. +`data` can be a string or a buffer. Example: @@ -463,7 +483,7 @@ Example: console.log('The "data to append" was appended to file!'); }); -## fs.appendFileSync(filename, data, encoding='utf8') +## fs.appendFileSync(filename, data, [options]) The synchronous version of `fs.appendFile`. diff --git a/lib/fs.js b/lib/fs.js index a1bc487..1ad0b2e 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -165,10 +165,20 @@ fs.existsSync = function(path) { } }; -fs.readFile = function(path, encoding_) { - var encoding = typeof(encoding_) === 'string' ? encoding_ : null; +fs.readFile = function(path, options, callback_) { var callback = maybeCallback(arguments[arguments.length - 1]); + if (typeof options === 'function' || !options) { + options = { encoding: null, flag: 'r' }; + } else if (typeof options === 'string') { + options = { encoding: options, flag: 'r' }; + } else if (!options) { + options = { encoding: null, flag: 'r' }; + } else if (typeof options !== 'object') { + throw new TypeError('Bad arguments'); + } + + var encoding = options.encoding; assertEncoding(encoding); // first, stat the file, so we know the size. @@ -178,7 +188,8 @@ fs.readFile = function(path, encoding_) { var pos = 0; var fd; - fs.open(path, constants.O_RDONLY, 438 /*=0666*/, function(er, fd_) { + var flag = options.flag || 'r'; + fs.open(path, flag, 438 /*=0666*/, function(er, fd_) { if (er) return callback(er); fd = fd_; @@ -243,10 +254,20 @@ fs.readFile = function(path, encoding_) { } }; -fs.readFileSync = function(path, encoding) { +fs.readFileSync = function(path, options) { + if (!options) { + options = { encoding: null, flag: 'r' }; + } else if (typeof options === 'string') { + options = { encoding: options, flag: 'r' }; + } else if (typeof options !== 'object') { + throw new TypeError('Bad arguments'); + } + + var encoding = options.encoding; assertEncoding(encoding); - var fd = fs.openSync(path, constants.O_RDONLY, 438 /*=0666*/); + var flag = options.flag || 'r'; + var fd = fs.openSync(path, flag, 438 /*=0666*/); var size; var threw = true; @@ -888,72 +909,93 @@ function writeAll(fd, buffer, offset, length, position, callback) { }); } -fs.writeFile = function(path, data, encoding_, callback) { - var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8'); - assertEncoding(encoding); +fs.writeFile = function(path, data, options, callback) { + var callback = maybeCallback(arguments[arguments.length - 1]); - callback = maybeCallback(arguments[arguments.length - 1]); - fs.open(path, 'w', 438 /*=0666*/, function(openErr, fd) { + if (typeof options === 'function' || !options) { + options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' }; + } else if (typeof options === 'string') { + options = { encoding: options, mode: 438, flag: 'w' }; + } else if (!options) { + options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' }; + } else if (typeof options !== 'object') { + throw new TypeError('Bad arguments'); + } + + assertEncoding(options.encoding); + + var flag = options.flag || 'w'; + fs.open(path, options.flag || 'w', options.mode, function(openErr, fd) { if (openErr) { if (callback) callback(openErr); } else { var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, - encoding); - writeAll(fd, buffer, 0, buffer.length, 0, callback); + options.encoding || 'utf8'); + var position = /a/.test(flag) ? null : 0; + writeAll(fd, buffer, 0, buffer.length, position, callback); } }); }; -fs.writeFileSync = function(path, data, encoding) { - assertEncoding(encoding); +fs.writeFileSync = function(path, data, options) { + if (!options) { + options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'w' }; + } else if (typeof options === 'string') { + options = { encoding: options, mode: 438, flag: 'w' }; + } else if (typeof options !== 'object') { + throw new TypeError('Bad arguments'); + } - var fd = fs.openSync(path, 'w'); + assertEncoding(options.encoding); + + var flag = options.flag || 'w'; + var fd = fs.openSync(path, flag); if (!Buffer.isBuffer(data)) { - data = new Buffer('' + data, encoding || 'utf8'); + data = new Buffer('' + data, options.encoding || 'utf8'); } var written = 0; var length = data.length; + var position = /a/.test(flag) ? null : 0; try { while (written < length) { - written += fs.writeSync(fd, data, written, length - written, written); + written += fs.writeSync(fd, data, written, length - written, position); + position += written; } } finally { fs.closeSync(fd); } }; -fs.appendFile = function(path, data, encoding_, callback) { - var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8'); - assertEncoding(encoding); +fs.appendFile = function(path, data, options, callback_) { + var callback = maybeCallback(arguments[arguments.length - 1]); - callback = maybeCallback(arguments[arguments.length - 1]); + if (typeof options === 'function' || !options) { + options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' }; + } else if (typeof options === 'string') { + options = { encoding: options, mode: 438, flag: 'a' }; + } else if (!options) { + options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' }; + } else if (typeof options !== 'object') { + throw new TypeError('Bad arguments'); + } - fs.open(path, 'a', 438 /*=0666*/, function(err, fd) { - if (err) return callback(err); - var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, encoding); - writeAll(fd, buffer, 0, buffer.length, null, callback); - }); + if (!options.flag) + options = util._extend({ flag: 'a' }, options); + fs.writeFile(path, data, options, callback); }; -fs.appendFileSync = function(path, data, encoding) { - assertEncoding(encoding); - - var fd = fs.openSync(path, 'a'); - if (!Buffer.isBuffer(data)) { - data = new Buffer('' + data, encoding || 'utf8'); +fs.appendFileSync = function(path, data, options) { + if (!options) { + options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' }; + } else if (typeof options === 'string') { + options = { encoding: options, mode: 438, flag: 'a' }; + } else if (typeof options !== 'object') { + throw new TypeError('Bad arguments'); } - var written = 0; - var position = null; - var length = data.length; + if (!options.flag) + options = util._extend({ flag: 'a' }, options); - try { - while (written < length) { - written += fs.writeSync(fd, data, written, length - written, position); - position += written; // XXX not safe with multiple concurrent writers? - } - } finally { - fs.closeSync(fd); - } + fs.writeFileSync(path, data, options); }; function errnoException(errorno, syscall) { diff --git a/test/simple/test-fs-append-file-sync.js b/test/simple/test-fs-append-file-sync.js index 4e95c67..7bc6a01 100644 --- a/test/simple/test-fs-append-file-sync.js +++ b/test/simple/test-fs-append-file-sync.js @@ -73,10 +73,17 @@ assert.equal(buf.length + currentFileData.length, fileData3.length); // test that appendFile accepts numbers. var filename4 = join(common.tmpDir, 'append-sync4.txt'); -fs.writeFileSync(filename4, currentFileData); +fs.writeFileSync(filename4, currentFileData, { mode: m }); common.error('appending to ' + filename4); -fs.appendFileSync(filename4, num); +var m = 0600; +fs.appendFileSync(filename4, num, { mode: m }); + +// windows permissions aren't unix +if (process.platform !== 'win32') { + var st = fs.statSync(filename4); + assert.equal(st.mode & 0700, m); +} var fileData4 = fs.readFileSync(filename4); diff --git a/test/simple/test-fs-append-file.js b/test/simple/test-fs-append-file.js index ad32508..450c8d3 100644 --- a/test/simple/test-fs-append-file.js +++ b/test/simple/test-fs-append-file.js @@ -101,12 +101,19 @@ fs.writeFileSync(filename4, currentFileData); common.error('appending to ' + filename4); -fs.appendFile(filename4, n, function(e) { +var m = 0600; +fs.appendFile(filename4, n, { mode: m }, function(e) { if (e) throw e; ncallbacks++; common.error('appended to file4'); + // windows permissions aren't unix + if (process.platform !== 'win32') { + var st = fs.statSync(filename4); + assert.equal(st.mode & 0700, m); + } + fs.readFile(filename4, function(e, buffer) { if (e) throw e; common.error('file4 read'); diff --git a/test/simple/test-fs-write-file.js b/test/simple/test-fs-write-file.js index 00704ed..b70ea44 100644 --- a/test/simple/test-fs-write-file.js +++ b/test/simple/test-fs-write-file.js @@ -76,9 +76,16 @@ fs.writeFile(filename2, buf, function(e) { var filename3 = join(common.tmpDir, 'test3.txt'); common.error('writing to ' + filename3); -fs.writeFile(filename3, n, function(e) { +var m = 0600; +fs.writeFile(filename3, n, { mode: m }, function(e) { if (e) throw e; + // windows permissions aren't unix + if (process.platform !== 'win32') { + var st = fs.statSync(filename3); + assert.equal(st.mode & 0700, m); + } + ncallbacks++; common.error('file3 written'); -- 2.7.4