Skip to content

Commit

Permalink
fix #197: cleanupCallback must be sync or async based on initial API …
Browse files Browse the repository at this point in the history
…call
  • Loading branch information
silkentrance committed Jan 24, 2020
1 parent 6e79e62 commit be6fce9
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 45 deletions.
Empty file added CHANGELOG.md
Empty file.
111 changes: 66 additions & 45 deletions lib/tmp.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ function file(options, callback) {
/* istanbul ignore else */
if (err) return cb(err);

// FIXME overall handling of opts.discardDescriptor is off
if (opts.discardDescriptor) {
// FIXME must not unlink
return fs.close(fd, function _discardCallback(err) {
/* istanbul ignore else */
if (err) {
Expand All @@ -270,12 +272,11 @@ function file(options, callback) {
}
cb(null, name, undefined, _prepareTmpFileRemoveCallback(name, -1, opts));
});
} else {
// FIXME detachDescriptor passes the descriptor whereas discardDescriptor closes it
const discardOrDetachDescriptor = opts.discardDescriptor || opts.detachDescriptor;
cb(null, name, fd, _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts, false));
}
/* istanbul ignore else */
if (opts.detachDescriptor) {
return cb(null, name, fd, _prepareTmpFileRemoveCallback(name, -1, opts));
}
cb(null, name, fd, _prepareTmpFileRemoveCallback(name, fd, opts));
});
});
}
Expand Down Expand Up @@ -304,7 +305,7 @@ function fileSync(options) {
return {
name: name,
fd: fd,
removeCallback: _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts)
removeCallback: _prepareTmpFileRemoveCallback(name, discardOrDetachDescriptor ? -1 : fd, opts, true)
};
}

Expand All @@ -330,7 +331,7 @@ function dir(options, callback) {
/* istanbul ignore else */
if (err) return cb(err);

cb(null, name, _prepareTmpDirRemoveCallback(name, opts));
cb(null, name, _prepareTmpDirRemoveCallback(name, opts, false));
});
});
}
Expand All @@ -352,7 +353,7 @@ function dirSync(options) {

return {
name: name,
removeCallback: _prepareTmpDirRemoveCallback(name, opts)
removeCallback: _prepareTmpDirRemoveCallback(name, opts, true)
};
}

Expand All @@ -370,7 +371,7 @@ function _removeFileAsync(fdPath, next) {
return next(err);
}
next();
}
};

if (0 <= fdPath[0])
fs.close(fdPath[0], function (err) {
Expand Down Expand Up @@ -405,19 +406,23 @@ function _removeFileSync(fdPath) {
/**
* Prepares the callback for removal of the temporary file.
*
* Returns either a sync callback or a async callback depending on whether
* fileSync or file was called, which is expressed by the sync parameter.
*
* @param {string} name the path of the file
* @param {number} fd file descriptor
* @param {Object} opts
* @returns {fileCallback}
* @param {boolean} sync
* @returns {fileCallback | fileCallbackSync}
* @private
*/
function _prepareTmpFileRemoveCallback(name, fd, opts) {
const removeCallbackSync = _prepareRemoveCallback(_removeFileSync, [fd, name]);
const removeCallback = _prepareRemoveCallback(_removeFileAsync, [fd, name], removeCallbackSync);
function _prepareTmpFileRemoveCallback(name, fd, opts, sync) {
const removeCallbackSync = _prepareRemoveCallback(_removeFileSync, [fd, name], sync);
const removeCallback = _prepareRemoveCallback(_removeFileAsync, [fd, name], sync, removeCallbackSync);

if (!opts.keep) _removeObjects.unshift(removeCallbackSync);

return removeCallback;
return sync ? removeCallbackSync : removeCallback;
}

/**
Expand Down Expand Up @@ -445,67 +450,62 @@ function _rimrafRemoveDirSyncWrapper(dirPath, next) {
}
}

const FN_RMDIR_SYNC = fs.rmdirSync.bind(fs);

/**
* Prepares the callback for removal of the temporary directory.
*
* Returns either a sync callback or a async callback depending on whether
* tmpFileSync or tmpFile was called, which is expressed by the sync parameter.
*
* @param {string} name
* @param {Object} opts
* @param {boolean} sync
* @returns {Function} the callback
* @private
*/
function _prepareTmpDirRemoveCallback(name, opts) {
function _prepareTmpDirRemoveCallback(name, opts, sync) {
const removeFunction = opts.unsafeCleanup ? _rimrafRemoveDirWrapper : fs.rmdir.bind(fs);
const removeFunctionSync = opts.unsafeCleanup ? _rimrafRemoveDirSyncWrapper : FN_RMDIR_SYNC;
const removeCallbackSync = _prepareRemoveCallback(removeFunctionSync, name);
const removeCallback = _prepareRemoveCallback(removeFunction, name, removeCallbackSync);
const removeFunctionSync = opts.unsafeCleanup ? _rimrafRemoveDirSyncWrapper : fs.rmdirSync.bind(fs);
const removeCallbackSync = _prepareRemoveCallback(removeFunctionSync, name, sync);
const removeCallback = _prepareRemoveCallback(removeFunction, name, sync, removeCallbackSync);
if (!opts.keep) _removeObjects.unshift(removeCallbackSync);

return removeCallback;
return sync ? removeCallbackSync : removeCallback;
}

/**
* Creates a guarded function wrapping the removeFunction call.
*
* The cleanup callback is save to be called multiple times.
* Subsequent invocations will be ignored.
*
* @param {Function} removeFunction
* @param {Object} arg
* @returns {Function}
* @param {string} fileOrDirName
* @param {boolean} sync
* @param {cleanupCallbackSync?} cleanupCallbackSync
* @returns {cleanupCallback | cleanupCallbackSync}
* @private
*/
function _prepareRemoveCallback(removeFunction, arg, cleanupCallbackSync) {
function _prepareRemoveCallback(removeFunction, fileOrDirName, sync, cleanupCallbackSync) {
var called = false;

// if sync is true, the next parameter will be ignored
return function _cleanupCallback(next) {
next = next || function () {};

/* istanbul ignore else */
if (!called) {
// remove cleanupCallback from cache
const toRemove = cleanupCallbackSync || _cleanupCallback;
const index = _removeObjects.indexOf(toRemove);
/* istanbul ignore else */
if (index >= 0) _removeObjects.splice(index, 1);

called = true;
// sync?
if (removeFunction.length === 1) {
try {
removeFunction(arg);
return next(null);
}
catch (err) {
// if no next is provided and since we are
// in silent cleanup mode on process exit,
// we will ignore the error
return next(err);
}
if (sync) {
return removeFunction(fileOrDirName);
} else {
// must no call rmdirSync/rmSync this way
if (removeFunction == FN_RMDIR_SYNC) {
return removeFunction(arg);
} else {
return removeFunction(arg, next);
}
return removeFunction(fileOrDirName, next || function() {});
}
} else return next(new Error('cleanup callback has already been called'));
}
};
}

Expand Down Expand Up @@ -726,18 +726,39 @@ _safely_install_sigint_listener();
* @param {cleanupCallback} fn the cleanup callback function
*/

/**
* @callback fileCallbackSync
* @param {?Error} err the error object if anything goes wrong
* @param {string} name the temporary file name
* @param {number} fd the file descriptor
* @param {cleanupCallbackSync} fn the cleanup callback function
*/

/**
* @callback dirCallback
* @param {?Error} err the error object if anything goes wrong
* @param {string} name the temporary file name
* @param {cleanupCallback} fn the cleanup callback function
*/

/**
* @callback dirCallbackSync
* @param {?Error} err the error object if anything goes wrong
* @param {string} name the temporary file name
* @param {cleanupCallbackSync} fn the cleanup callback function
*/

/**
* Removes the temporary created file or directory.
*
* @callback cleanupCallback
* @param {simpleCallback} [next] function to call after entry was removed
* @param {simpleCallback} [next] function to call whenever the tmp object needs to be removed
*/

/**
* Removes the temporary created file or directory.
*
* @callback cleanupCallbackSync
*/

/**
Expand Down

0 comments on commit be6fce9

Please sign in to comment.