diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index 62c40b3d91..16f8fbb35a 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -259,17 +259,29 @@ An exception occurs if the file does not exist. * `'w'` - Open file for writing. The file is created (if it does not exist) or truncated (if it exists). +* `'wx'` - Like `'w'` but opens the file in exclusive mode. + * `'w+'` - Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists). +* `'wx+'` - Like `'w+'` but opens the file in exclusive mode. + * `'a'` - Open file for appending. The file is created if it does not exist. +* `'ax'` - Like `'a'` but opens the file in exclusive mode. + * `'a+'` - Open file for reading and appending. The file is created if it does not exist. +* `'ax+'` - Like `'a+'` but opens the file in exclusive mode. + `mode` defaults to `0666`. The callback gets two arguments `(err, fd)`. +Exclusive mode (`O_EXCL`) ensures that `path` is newly created. `fs.open()` +fails if a file by that name already exists. On POSIX systems, symlinks are +not followed. Exclusive mode may or may not work with network file systems. + ### fs.openSync(path, flags, [mode]) Synchronous open(2). diff --git a/lib/fs.js b/lib/fs.js index a44654c802..1c8f975504 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -37,6 +37,19 @@ var EventEmitter = require('events').EventEmitter; var kMinPoolSpace = 128; var kPoolSize = 40 * 1024; +var O_APPEND = constants.O_APPEND || 0; +var O_CREAT = constants.O_CREAT || 0; +var O_DIRECTORY = constants.O_DIRECTORY || 0; +var O_EXCL = constants.O_EXCL || 0; +var O_NOCTTY = constants.O_NOCTTY || 0; +var O_NOFOLLOW = constants.O_NOFOLLOW || 0; +var O_RDONLY = constants.O_RDONLY || 0; +var O_RDWR = constants.O_RDWR || 0; +var O_SYMLINK = constants.O_SYMLINK || 0; +var O_SYNC = constants.O_SYNC || 0; +var O_TRUNC = constants.O_TRUNC || 0; +var O_WRONLY = constants.O_WRONLY || 0; + fs.Stats = binding.Stats; fs.Stats.prototype._checkModeProperty = function(property) { @@ -178,28 +191,35 @@ function stringToFlags(flag) { if (typeof flag !== 'string') { return flag; } - switch (flag) { - case 'r': - return constants.O_RDONLY; - case 'r+': - return constants.O_RDWR; + // O_EXCL is mandated by POSIX, Windows supports it too. + // Let's add a check anyway, just in case. + if (!O_EXCL && ~flag.indexOf('x')) { + throw errnoException('ENOSYS', 'fs.open(O_EXCL)'); + } - case 'w': - return constants.O_CREAT | constants.O_TRUNC | constants.O_WRONLY; + switch (flag) { + case 'r' : return O_RDONLY; + case 'r+' : return O_RDWR; - case 'w+': - return constants.O_CREAT | constants.O_TRUNC | constants.O_RDWR; + case 'w' : return O_TRUNC | O_CREAT | O_WRONLY; + case 'wx' : // fall through + case 'xw' : return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL; - case 'a': - return constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY; + case 'w+' : return O_TRUNC | O_CREAT | O_RDWR; + case 'wx+': // fall through + case 'xw+': return O_TRUNC | O_CREAT | O_RDWR | O_EXCL; - case 'a+': - return constants.O_APPEND | constants.O_CREAT | constants.O_RDWR; + case 'a' : return O_APPEND | O_CREAT | O_WRONLY; + case 'ax' : // fall through + case 'xa' : return O_APPEND | O_CREAT | O_WRONLY | O_EXCL; - default: - throw new Error('Unknown file open flag: ' + flag); + case 'a+' : return O_APPEND | O_CREAT | O_RDWR; + case 'ax+': // fall through + case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL; } + + throw new Error('Unknown file open flag: ' + flag); } // exported but hidden, only used by test/simple/test-fs-open-flags.js diff --git a/test/simple/test-fs-open-flags.js b/test/simple/test-fs-open-flags.js index 4e01b11fa7..7d51cc9a4c 100644 --- a/test/simple/test-fs-open-flags.js +++ b/test/simple/test-fs-open-flags.js @@ -45,6 +45,16 @@ assert.equal(fs._stringToFlags('w+'), O_TRUNC|O_CREAT|O_RDWR); assert.equal(fs._stringToFlags('a'), O_APPEND|O_CREAT|O_WRONLY); assert.equal(fs._stringToFlags('a+'), O_APPEND|O_CREAT|O_RDWR); -'+ +a +r +w rw wa war raw r++ a++ w++'.split(' ').forEach(function(flags) { +assert.equal(fs._stringToFlags('wx'), O_TRUNC|O_CREAT|O_WRONLY|O_EXCL); +assert.equal(fs._stringToFlags('xw'), O_TRUNC|O_CREAT|O_WRONLY|O_EXCL); +assert.equal(fs._stringToFlags('wx+'), O_TRUNC|O_CREAT|O_RDWR|O_EXCL); +assert.equal(fs._stringToFlags('xw+'), O_TRUNC|O_CREAT|O_RDWR|O_EXCL); +assert.equal(fs._stringToFlags('ax'), O_APPEND|O_CREAT|O_WRONLY|O_EXCL); +assert.equal(fs._stringToFlags('xa'), O_APPEND|O_CREAT|O_WRONLY|O_EXCL); +assert.equal(fs._stringToFlags('ax+'), O_APPEND|O_CREAT|O_RDWR|O_EXCL); +assert.equal(fs._stringToFlags('xa+'), O_APPEND|O_CREAT|O_RDWR|O_EXCL); + +('+ +a +r +w rw wa war raw r++ a++ w++' + + 'x +x x+ rx rx+ wxx wax xwx xxx').split(' ').forEach(function(flags) { assert.throws(function() { fs._stringToFlags(flags); }); });