Permalink
Browse files

fs: add O_EXCL support, exclusive open file

  • Loading branch information...
1 parent 12bcf93 commit 94a5f1dbcd29eba2e68d6270f496fce124d7a03a @bnoordhuis committed Jan 31, 2012
Showing with 59 additions and 16 deletions.
  1. +12 −0 doc/api/fs.markdown
  2. +36 −15 lib/fs.js
  3. +11 −1 test/simple/test-fs-open-flags.js
View
@@ -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).
View
@@ -37,6 +37,20 @@ 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_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 +192,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')) {
+ console.trace('O_EXCL is not supported on your platform.');
@isaacs
isaacs Jan 31, 2012

It seems like it'd be better to just go ahead and throw a ENOSYS here.

+ }
- 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
@@ -46,6 +46,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); });
});

2 comments on commit 94a5f1d

@isaacs
isaacs commented on 94a5f1d Jan 31, 2012

Apart from comment above, lgtm.

@isaacs
isaacs commented on 94a5f1d Jan 31, 2012

Oh, also, docs please :)

Please sign in to comment.