diff options
Diffstat (limited to 'node_modules/vinyl-fs/lib/fileOperations.js')
-rw-r--r-- | node_modules/vinyl-fs/lib/fileOperations.js | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/node_modules/vinyl-fs/lib/fileOperations.js b/node_modules/vinyl-fs/lib/fileOperations.js new file mode 100644 index 000000000..e323fb70e --- /dev/null +++ b/node_modules/vinyl-fs/lib/fileOperations.js @@ -0,0 +1,206 @@ +'use strict'; + +var fs = require('graceful-fs'); +var assign = require('object-assign'); +var isEqual = require('lodash.isequal'); +var isValidDate = require('vali-date'); + +// TODO shared module +// TODO include sticky/setuid/setgid, i.e. 7777? +var MASK_MODE = parseInt('0777', 8); +var DEFAULT_FILE_MODE = parseInt('0666', 8); +var APPEND_MODE_REGEXP = /a/; + +function closeFd(propagatedErr, fd, callback) { + if (typeof fd !== 'number') { + return callback(propagatedErr); + } + + fs.close(fd, onClosed); + + function onClosed(closeErr) { + if (propagatedErr || closeErr) { + return callback(propagatedErr || closeErr); + } + + callback(); + } +} + +function getModeDiff(fsMode, vinylMode) { + var modeDiff = 0; + + if (typeof vinylMode === 'number') { + modeDiff = (vinylMode ^ fsMode) & MASK_MODE; + } + + return modeDiff; +} + +function getTimesDiff(fsStat, vinylStat) { + + if (!isValidDate(vinylStat.mtime)) { + return; + } + + if (isEqual(vinylStat.mtime, fsStat.mtime) && + isEqual(vinylStat.atime, fsStat.atime)) { + return; + } + + var atime; + if (isValidDate(vinylStat.atime)) { + atime = vinylStat.atime; + } else { + atime = fsStat.atime; + } + + if (!isValidDate(atime)) { + atime = undefined; + } + + var timesDiff = { + mtime: vinylStat.mtime, + atime: atime, + }; + + return timesDiff; +} + +function isOwner(fsStat) { + var hasGetuid = (typeof process.getuid === 'function'); + var hasGeteuid = (typeof process.geteuid === 'function'); + + // If we don't have either, assume we don't have permissions. + // This should only happen on Windows. + // Windows basically noops fchmod and errors on futimes called on directories. + if (!hasGeteuid && !hasGetuid) { + return false; + } + + var uid; + if (hasGeteuid) { + uid = process.geteuid(); + } else { + uid = process.getuid(); + } + + if (fsStat.uid !== uid && uid !== 0) { + return false; + } + + return true; +} + +function updateMetadata(fd, file, callback) { + + fs.fstat(fd, onStat); + + function onStat(err, stat) { + if (err) { + return callback(err, fd); + } + + // Check if mode needs to be updated + var modeDiff = getModeDiff(stat.mode, file.stat.mode); + + // Check if atime/mtime need to be updated + var timesDiff = getTimesDiff(stat, file.stat); + + // Set file.stat to the reflect current state on disk + assign(file.stat, stat); + + // Nothing to do + if (!modeDiff && !timesDiff) { + return callback(null, fd); + } + + // Check access, `futimes` and `fchmod` only work if we own the file, + // or if we are effectively root. + if (!isOwner(stat)) { + return callback(null, fd); + } + + if (modeDiff) { + return mode(); + } + times(); + + function mode() { + var mode = stat.mode ^ modeDiff; + + fs.fchmod(fd, mode, onFchmod); + + function onFchmod(fchmodErr) { + if (!fchmodErr) { + file.stat.mode = mode; + } + if (timesDiff) { + return times(fchmodErr); + } + callback(fchmodErr, fd); + } + } + + function times(fchmodErr) { + fs.futimes(fd, timesDiff.atime, timesDiff.mtime, onFutimes); + + function onFutimes(futimesErr) { + if (!futimesErr) { + file.stat.atime = timesDiff.atime; + file.stat.mtime = timesDiff.mtime; + } + callback(fchmodErr || futimesErr, fd); + } + } + } +} + +/* + Custom writeFile implementation because we need access to the + file descriptor after the write is complete. + Most of the implementation taken from node core. + */ +function writeFile(path, data, options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + + if (!Buffer.isBuffer(data)) { + callback(new TypeError('Data must be a Buffer')); + return; + } + + if (!options) { + options = {}; + } + + // Default the same as node + var mode = options.mode || DEFAULT_FILE_MODE; + var flag = options.flag || 'w'; + var position = APPEND_MODE_REGEXP.test(flag) ? null : 0; + + fs.open(path, flag, mode, onOpen); + + function onOpen(err, fd) { + if (err) { + return onComplete(err); + } + + fs.write(fd, data, 0, data.length, position, onComplete); + + function onComplete(err) { + callback(err, fd); + } + } +} + +module.exports = { + closeFd: closeFd, + getModeDiff: getModeDiff, + getTimesDiff: getTimesDiff, + isOwner: isOwner, + updateMetadata: updateMetadata, + writeFile: writeFile, +}; |