| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 | 'use strict';var util = require('util');var fs = require('graceful-fs');var assign = require('object.assign');var date = require('value-or-function').date;var Writable = require('readable-stream').Writable;var constants = require('./constants');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 isValidUnixId(id) {  if (typeof id !== 'number') {    return false;  }  if (id < 0) {    return false;  }  return true;}function getFlags(options) {  var flags = !options.append ? 'w' : 'a';  if (!options.overwrite) {    flags += 'x';  }  return flags;}function isFatalOverwriteError(err, flags) {  if (!err) {    return false;  }  if (err.code === 'EEXIST' && flags[1] === 'x') {    // Handle scenario for file overwrite failures.    return false;  }  // Otherwise, this is a fatal error  return true;}function isFatalUnlinkError(err) {  if (!err || err.code === 'ENOENT') {    return false;  }  return true;}function getModeDiff(fsMode, vinylMode) {  var modeDiff = 0;  if (typeof vinylMode === 'number') {    modeDiff = (vinylMode ^ fsMode) & constants.MASK_MODE;  }  return modeDiff;}function getTimesDiff(fsStat, vinylStat) {  var mtime = date(vinylStat.mtime) || 0;  if (!mtime) {    return;  }  var atime = date(vinylStat.atime) || 0;  if (+mtime === +fsStat.mtime &&      +atime === +fsStat.atime) {    return;  }  if (!atime) {    atime = date(fsStat.atime) || undefined;  }  var timesDiff = {    mtime: vinylStat.mtime,    atime: atime,  };  return timesDiff;}function getOwnerDiff(fsStat, vinylStat) {  if (!isValidUnixId(vinylStat.uid) &&      !isValidUnixId(vinylStat.gid)) {    return;  }  if ((!isValidUnixId(fsStat.uid) && !isValidUnixId(vinylStat.uid)) ||      (!isValidUnixId(fsStat.gid) && !isValidUnixId(vinylStat.gid))) {    return;  }  var uid = fsStat.uid; // Default to current uid.  if (isValidUnixId(vinylStat.uid)) {    uid = vinylStat.uid;  }  var gid = fsStat.gid; // Default to current gid.  if (isValidUnixId(vinylStat.gid)) {    gid = vinylStat.gid;  }  if (uid === fsStat.uid &&      gid === fsStat.gid) {    return;  }  var ownerDiff = {    uid: uid,    gid: gid,  };  return ownerDiff;}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 reflectStat(path, file, callback) {  // Set file.stat to the reflect current state on disk  fs.stat(path, onStat);  function onStat(statErr, stat) {    if (statErr) {      return callback(statErr);    }    file.stat = stat;    callback();  }}function reflectLinkStat(path, file, callback) {  // Set file.stat to the reflect current state on disk  fs.lstat(path, onLstat);  function onLstat(lstatErr, stat) {    if (lstatErr) {      return callback(lstatErr);    }    file.stat = stat;    callback();  }}function updateMetadata(fd, file, callback) {  fs.fstat(fd, onStat);  function onStat(statErr, stat) {    if (statErr) {      return callback(statErr);    }    // 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);    // Check if uid/gid need to be updated    var ownerDiff = getOwnerDiff(stat, file.stat);    // Set file.stat to the reflect current state on disk    assign(file.stat, stat);    // Nothing to do    if (!modeDiff && !timesDiff && !ownerDiff) {      return callback();    }    // Check access, `futimes`, `fchmod` & `fchown` only work if we own    // the file, or if we are effectively root (`fchown` only when root).    if (!isOwner(stat)) {      return callback();    }    if (modeDiff) {      return mode();    }    if (timesDiff) {      return times();    }    owner();    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);        }        if (ownerDiff) {          return owner(fchmodErr);        }        callback(fchmodErr);      }    }    function times(propagatedErr) {      fs.futimes(fd, timesDiff.atime, timesDiff.mtime, onFutimes);      function onFutimes(futimesErr) {        if (!futimesErr) {          file.stat.atime = timesDiff.atime;          file.stat.mtime = timesDiff.mtime;        }        if (ownerDiff) {          return owner(propagatedErr || futimesErr);        }        callback(propagatedErr || futimesErr);      }    }    function owner(propagatedErr) {      fs.fchown(fd, ownerDiff.uid, ownerDiff.gid, onFchown);      function onFchown(fchownErr) {        if (!fchownErr) {          file.stat.uid = ownerDiff.uid;          file.stat.gid = ownerDiff.gid;        }        callback(propagatedErr || fchownErr);      }    }  }}function symlink(srcPath, destPath, opts, callback) {  // Because fs.symlink does not allow atomic overwrite option with flags, we  // delete and recreate if the link already exists and overwrite is true.  if (opts.flags === 'w') {    // TODO What happens when we call unlink with windows junctions?    fs.unlink(destPath, onUnlink);  } else {    fs.symlink(srcPath, destPath, opts.type, onSymlink);  }  function onUnlink(unlinkErr) {    if (isFatalUnlinkError(unlinkErr)) {      return callback(unlinkErr);    }    fs.symlink(srcPath, destPath, opts.type, onSymlink);  }  function onSymlink(symlinkErr) {    if (isFatalOverwriteError(symlinkErr, opts.flags)) {      return callback(symlinkErr);    }    callback();  }}/*  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(filepath, data, options, callback) {  if (typeof options === 'function') {    callback = options;    options = {};  }  if (!Buffer.isBuffer(data)) {    return callback(new TypeError('Data must be a Buffer'));  }  if (!options) {    options = {};  }  // Default the same as node  var mode = options.mode || constants.DEFAULT_FILE_MODE;  var flags = options.flags || 'w';  var position = APPEND_MODE_REGEXP.test(flags) ? null : 0;  fs.open(filepath, flags, mode, onOpen);  function onOpen(openErr, fd) {    if (openErr) {      return onComplete(openErr);    }    fs.write(fd, data, 0, data.length, position, onComplete);    function onComplete(writeErr) {      callback(writeErr, fd);    }  }}function createWriteStream(path, options, flush) {  return new WriteStream(path, options, flush);}// Taken from node core and altered to receive a flush function and simplified// To be used for cleanup (like updating times/mode/etc)function WriteStream(path, options, flush) {  // Not exposed so we can avoid the case where someone doesn't use `new`  if (typeof options === 'function') {    flush = options;    options = null;  }  options = options || {};  Writable.call(this, options);  this.flush = flush;  this.path = path;  this.mode = options.mode || constants.DEFAULT_FILE_MODE;  this.flags = options.flags || 'w';  // Used by node's `fs.WriteStream`  this.fd = null;  this.start = null;  this.open();  // Dispose on finish.  this.once('finish', this.close);}util.inherits(WriteStream, Writable);WriteStream.prototype.open = function() {  var self = this;  fs.open(this.path, this.flags, this.mode, onOpen);  function onOpen(openErr, fd) {    if (openErr) {      self.destroy();      self.emit('error', openErr);      return;    }    self.fd = fd;    self.emit('open', fd);  }};// Use our `end` method since it is patched for flushWriteStream.prototype.destroySoon = WriteStream.prototype.end;WriteStream.prototype._destroy = function(err, cb) {  this.close(function(err2) {    cb(err || err2);  });};WriteStream.prototype.close = function(cb) {  var that = this;  if (cb) {    this.once('close', cb);  }  if (this.closed || typeof this.fd !== 'number') {    if (typeof this.fd !== 'number') {      this.once('open', closeOnOpen);      return;    }    return process.nextTick(function() {      that.emit('close');    });  }  this.closed = true;  fs.close(this.fd, function(er) {    if (er) {      that.emit('error', er);    } else {      that.emit('close');    }  });  this.fd = null;};WriteStream.prototype._final = function(callback) {  if (typeof this.flush !== 'function') {    return callback();  }  this.flush(this.fd, callback);};function closeOnOpen() {  this.close();}WriteStream.prototype._write = function(data, encoding, callback) {  var self = this;  // This is from node core but I have no idea how to get code coverage on it  if (!Buffer.isBuffer(data)) {    return this.emit('error', new Error('Invalid data'));  }  if (typeof this.fd !== 'number') {    return this.once('open', onOpen);  }  fs.write(this.fd, data, 0, data.length, null, onWrite);  function onOpen() {    self._write(data, encoding, callback);  }  function onWrite(writeErr) {    if (writeErr) {      self.destroy();      callback(writeErr);      return;    }    callback();  }};module.exports = {  closeFd: closeFd,  isValidUnixId: isValidUnixId,  getFlags: getFlags,  isFatalOverwriteError: isFatalOverwriteError,  isFatalUnlinkError: isFatalUnlinkError,  getModeDiff: getModeDiff,  getTimesDiff: getTimesDiff,  getOwnerDiff: getOwnerDiff,  isOwner: isOwner,  reflectStat: reflectStat,  reflectLinkStat: reflectLinkStat,  updateMetadata: updateMetadata,  symlink: symlink,  writeFile: writeFile,  createWriteStream: createWriteStream,};
 |