diff options
Diffstat (limited to 'node_modules/tmp/lib/tmp.js')
-rw-r--r-- | node_modules/tmp/lib/tmp.js | 370 |
1 files changed, 263 insertions, 107 deletions
diff --git a/node_modules/tmp/lib/tmp.js b/node_modules/tmp/lib/tmp.js index ea84faa55..bb83c7ec3 100644 --- a/node_modules/tmp/lib/tmp.js +++ b/node_modules/tmp/lib/tmp.js @@ -1,7 +1,7 @@ /*! * Tmp * - * Copyright (c) 2011-2013 KARASZI Istvan <github@spam.raszi.hu> + * Copyright (c) 2011-2015 KARASZI Istvan <github@spam.raszi.hu> * * MIT Licensed */ @@ -12,10 +12,10 @@ var fs = require('fs'), path = require('path'), - os = require('os'), - exists = fs.exists || path.exists, - tmpDir = os.tmpDir || _getTMPDir, - _c = require('constants'); + crypto = require('crypto'), + tmpDir = require('os-tmpdir'), + _c = process.binding('constants'); + /** * The working inner variables. @@ -25,8 +25,16 @@ var _TMP = tmpDir(), // the random characters to choose from - randomChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz", - randomCharsLength = randomChars.length, + RANDOM_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', + + TEMPLATE_PATTERN = /XXXXXX/, + + DEFAULT_TRIES = 3, + + CREATE_FLAGS = (_c.O_CREAT || _c.fs.O_CREAT) | (_c.O_EXCL || _c.fs.O_EXCL) | (_c.O_RDWR || _c.fs.O_RDWR), + + DIR_MODE = 448 /* 0700 */, + FILE_MODE = 384 /* 0600 */, // this will hold the objects need to be removed on exit _removeObjects = [], @@ -35,22 +43,30 @@ var _uncaughtException = false; /** - * Gets the temp directory. + * Random name generator based on crypto. + * Adapted from http://blog.tompawlak.org/how-to-generate-random-values-nodejs-javascript * + * @param {Number} howMany * @return {String} * @api private */ -function _getTMPDir() { - var tmpNames = [ 'TMPDIR', 'TMP', 'TEMP' ]; - - for (var i = 0, length = tmpNames.length; i < length; i++) { - if (_isUndefined(process.env[tmpNames[i]])) continue; +function _randomChars(howMany) { + var + value = [], + rnd = null; + + // make sure that we do not fail because we ran out of entropy + try { + rnd = crypto.randomBytes(howMany); + } catch (e) { + rnd = crypto.pseudoRandomBytes(howMany); + } - return process.env[tmpNames[i]]; + for (var i = 0; i < howMany; i++) { + value.push(RANDOM_CHARS[rnd[i] % RANDOM_CHARS.length]); } - // fallback to the default - return '/tmp'; + return value.join(''); } /** @@ -74,19 +90,51 @@ function _isUndefined(obj) { * @api private */ function _parseArguments(options, callback) { - if (!callback || typeof callback != "function") { - callback = options; + if (typeof options == 'function') { + var + tmp = options, + options = callback || {}, + callback = tmp; + } else if (typeof options == 'undefined') { options = {}; } - return [ options, callback ]; + return [options, callback]; } /** - * Gets a temporary file name. + * Generates a new temporary name. * * @param {Object} opts - * @param {Function} cb + * @returns {String} + * @api private + */ +function _generateTmpName(opts) { + if (opts.name) { + return path.join(opts.dir || _TMP, opts.name); + } + + // mkstemps like template + if (opts.template) { + return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6)); + } + + // prefix and postfix + var name = [ + opts.prefix || 'tmp-', + process.pid, + _randomChars(12), + opts.postfix || '' + ].join(''); + + return path.join(opts.dir || _TMP, name); +} + +/** + * Gets a temporary file name. + * + * @param {Object} options + * @param {Function} callback * @api private */ function _getTmpName(options, callback) { @@ -94,49 +142,23 @@ function _getTmpName(options, callback) { args = _parseArguments(options, callback), opts = args[0], cb = args[1], - template = opts.template, - templateDefined = !_isUndefined(template), - tries = opts.tries || 3; + tries = opts.tries || DEFAULT_TRIES; if (isNaN(tries) || tries < 0) return cb(new Error('Invalid tries')); - if (templateDefined && !template.match(/XXXXXX/)) + if (opts.template && !opts.template.match(TEMPLATE_PATTERN)) return cb(new Error('Invalid template provided')); - function _getName() { - - // prefix and postfix - if (!templateDefined) { - var name = [ - (_isUndefined(opts.prefix)) ? 'tmp-' : opts.prefix, - process.pid, - (Math.random() * 0x1000000000).toString(36), - opts.postfix - ].join(''); - - return path.join(opts.dir || _TMP, name); - } - - // mkstemps like template - var chars = []; - - for (var i = 0; i < 6; i++) { - chars.push(randomChars.substr(Math.floor(Math.random() * randomCharsLength), 1)); - } - - return template.replace(/XXXXXX/, chars.join('')); - } - (function _getUniqueName() { - var name = _getName(); + var name = _generateTmpName(opts); // check whether the path exists then retry if needed - exists(name, function _pathExists(pathExists) { - if (pathExists) { + fs.stat(name, function (err) { + if (!err) { if (tries-- > 0) return _getUniqueName(); - return cb(new Error('Could not get a unique tmp filename, max tries reached')); + return cb(new Error('Could not get a unique tmp filename, max tries reached ' + name)); } cb(null, name); @@ -145,6 +167,37 @@ function _getTmpName(options, callback) { } /** + * Synchronous version of _getTmpName. + * + * @param {Object} options + * @returns {String} + * @api private + */ +function _getTmpNameSync(options) { + var + args = _parseArguments(options), + opts = args[0], + tries = opts.tries || DEFAULT_TRIES; + + if (isNaN(tries) || tries < 0) + throw new Error('Invalid tries'); + + if (opts.template && !opts.template.match(TEMPLATE_PATTERN)) + throw new Error('Invalid template provided'); + + do { + var name = _generateTmpName(opts); + try { + fs.statSync(name); + } catch (e) { + return name; + } + } while (tries-- > 0); + + throw new Error('Could not get a unique tmp filename, max tries reached'); +} + +/** * Creates and opens a temporary file. * * @param {Object} options @@ -157,68 +210,80 @@ function _createTmpFile(options, callback) { opts = args[0], cb = args[1]; - opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix; + opts.postfix = (_isUndefined(opts.postfix)) ? '.tmp' : opts.postfix; // gets a temporary filename _getTmpName(opts, function _tmpNameCreated(err, name) { if (err) return cb(err); // create and open the file - fs.open(name, _c.O_CREAT | _c.O_EXCL | _c.O_RDWR, opts.mode || 0600, function _fileCreated(err, fd) { + fs.open(name, CREATE_FLAGS, opts.mode || FILE_MODE, function _fileCreated(err, fd) { if (err) return cb(err); - var removeCallback = _prepareRemoveCallback(fs.unlinkSync.bind(fs), name); - - if (!opts.keep) { - _removeObjects.unshift(removeCallback); - } - - cb(null, name, fd, removeCallback); + cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts)); }); }); } /** - * Removes files and folders in a directory recursively. + * Synchronous version of _createTmpFile. * - * @param {String} dir + * @param {Object} options + * @returns {Object} object consists of name, fd and removeCallback + * @api private */ -function _rmdirRecursiveSync(dir) { - var files = fs.readdirSync(dir); - - for (var i = 0, length = files.length; i < length; i++) { - var file = path.join(dir, files[i]); - // lstat so we don't recurse into symlinked directories. - var stat = fs.lstatSync(file); - - if (stat.isDirectory()) { - _rmdirRecursiveSync(file); - } else { - fs.unlinkSync(file); - } - } +function _createTmpFileSync(options) { + var + args = _parseArguments(options), + opts = args[0]; - fs.rmdirSync(dir); + opts.postfix = opts.postfix || '.tmp'; + + var name = _getTmpNameSync(opts); + var fd = fs.openSync(name, CREATE_FLAGS, opts.mode || FILE_MODE); + + return { + name : name, + fd : fd, + removeCallback : _prepareTmpFileRemoveCallback(name, fd, opts) + }; } /** + * Removes files and folders in a directory recursively. * - * @param {Function} removeFunction - * @param {String} path - * @returns {Function} - * @private + * @param {String} root + * @api private */ -function _prepareRemoveCallback(removeFunction, path) { - var called = false; - return function() { - if (called) { - return; +function _rmdirRecursiveSync(root) { + var dirs = [root]; + + do { + var + dir = dirs.pop(), + deferred = false, + files = fs.readdirSync(dir); + + for (var i = 0, length = files.length; i < length; i++) { + var + file = path.join(dir, files[i]), + stat = fs.lstatSync(file); // lstat so we don't recurse into symlinked directories + + if (stat.isDirectory()) { + if (!deferred) { + deferred = true; + dirs.push(dir); + } + dirs.push(file); + } else { + fs.unlinkSync(file); + } } - removeFunction(path); - - called = true; - }; + if (!deferred) { + fs.rmdirSync(dir); + } + } while (dirs.length !== 0); } /** @@ -239,23 +304,109 @@ function _createTmpDir(options, callback) { if (err) return cb(err); // create the directory - fs.mkdir(name, opts.mode || 0700, function _dirCreated(err) { + fs.mkdir(name, opts.mode || DIR_MODE, function _dirCreated(err) { if (err) return cb(err); - var removeCallback = _prepareRemoveCallback( - opts.unsafeCleanup - ? _rmdirRecursiveSync - : fs.rmdirSync.bind(fs), - name - ); + cb(null, name, _prepareTmpDirRemoveCallback(name, opts)); + }); + }); +} - if (!opts.keep) { - _removeObjects.unshift(removeCallback); +/** + * Synchronous version of _createTmpDir. + * + * @param {Object} options + * @returns {Object} object consists of name and removeCallback + * @api private + */ +function _createTmpDirSync(options) { + var + args = _parseArguments(options), + opts = args[0]; + + var name = _getTmpNameSync(opts); + fs.mkdirSync(name, opts.mode || DIR_MODE); + + return { + name : name, + removeCallback : _prepareTmpDirRemoveCallback(name, opts) + }; +} + +/** + * Prepares the callback for removal of the temporary file. + * + * @param {String} name + * @param {int} fd + * @param {Object} opts + * @api private + * @returns {Function} the callback + */ +function _prepareTmpFileRemoveCallback(name, fd, opts) { + var removeCallback = _prepareRemoveCallback(function _removeCallback(fdPath) { + try { + fs.closeSync(fdPath[0]); + } + catch (e) { + // under some node/windows related circumstances, a temporary file + // may have not be created as expected or the file was already closed + // by the user, in which case we will simply ignore the error + if (e.errno != -(_c.EBADF || _c.os.errno.EBADF) && e.errno != -(_c.ENOENT || _c.os.errno.ENOENT)) { + // reraise any unanticipated error + throw e; } + } + fs.unlinkSync(fdPath[1]); + }, [fd, name]); - cb(null, name, removeCallback); - }); - }); + if (!opts.keep) { + _removeObjects.unshift(removeCallback); + } + + return removeCallback; +} + +/** + * Prepares the callback for removal of the temporary directory. + * + * @param {String} name + * @param {Object} opts + * @returns {Function} the callback + * @api private + */ +function _prepareTmpDirRemoveCallback(name, opts) { + var removeFunction = opts.unsafeCleanup ? _rmdirRecursiveSync : fs.rmdirSync.bind(fs); + var removeCallback = _prepareRemoveCallback(removeFunction, name); + + if (!opts.keep) { + _removeObjects.unshift(removeCallback); + } + + return removeCallback; +} + +/** + * Creates a guarded function wrapping the removeFunction call. + * + * @param {Function} removeFunction + * @param {Object} arg + * @returns {Function} + * @api private + */ +function _prepareRemoveCallback(removeFunction, arg) { + var called = false; + + return function _cleanupCallback() { + if (called) return; + + var index = _removeObjects.indexOf(_cleanupCallback); + if (index >= 0) { + _removeObjects.splice(index, 1); + } + + called = true; + removeFunction(arg); + }; } /** @@ -268,9 +419,11 @@ function _garbageCollector() { return; } - for (var i = 0, length = _removeObjects.length; i < length; i++) { + // the function being called removes itself from _removeObjects, + // loop until _removeObjects is empty + while (_removeObjects.length) { try { - _removeObjects[i].call(null); + _removeObjects[0].call(null); } catch (e) { // already removed? } @@ -286,7 +439,7 @@ var version = process.versions.node.split('.').map(function (value) { }); if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) { - process.addListener('uncaughtException', function _uncaughtExceptionThrown( err ) { + process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) { _uncaughtException = true; _garbageCollector(); @@ -302,6 +455,9 @@ process.addListener('exit', function _exit(code) { // exporting all the needed methods module.exports.tmpdir = _TMP; module.exports.dir = _createTmpDir; +module.exports.dirSync = _createTmpDirSync; module.exports.file = _createTmpFile; +module.exports.fileSync = _createTmpFileSync; module.exports.tmpName = _getTmpName; +module.exports.tmpNameSync = _getTmpNameSync; module.exports.setGracefulCleanup = _setGracefulCleanup; |