| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 | module.exports = Writervar fs = require('graceful-fs')var inherits = require('inherits')var rimraf = require('rimraf')var mkdir = require('mkdirp')var path = require('path')var umask = process.platform === 'win32' ? 0 : process.umask()var getType = require('./get-type.js')var Abstract = require('./abstract.js')// Must do this *before* loading the child classesinherits(Writer, Abstract)Writer.dirmode = parseInt('0777', 8) & (~umask)Writer.filemode = parseInt('0666', 8) & (~umask)var DirWriter = require('./dir-writer.js')var LinkWriter = require('./link-writer.js')var FileWriter = require('./file-writer.js')var ProxyWriter = require('./proxy-writer.js')// props is the desired state.  current is optionally the current stat,// provided here so that subclasses can avoid statting the target// more than necessary.function Writer (props, current) {  var self = this  if (typeof props === 'string') {    props = { path: props }  }  // polymorphism.  // call fstream.Writer(dir) to get a DirWriter object, etc.  var type = getType(props)  var ClassType = Writer  switch (type) {    case 'Directory':      ClassType = DirWriter      break    case 'File':      ClassType = FileWriter      break    case 'Link':    case 'SymbolicLink':      ClassType = LinkWriter      break    case null:    default:      // Don't know yet what type to create, so we wrap in a proxy.      ClassType = ProxyWriter      break  }  if (!(self instanceof ClassType)) return new ClassType(props)  // now get down to business.  Abstract.call(self)  if (!props.path) self.error('Must provide a path', null, true)  // props is what we want to set.  // set some convenience properties as well.  self.type = props.type  self.props = props  self.depth = props.depth || 0  self.clobber = props.clobber === false ? props.clobber : true  self.parent = props.parent || null  self.root = props.root || (props.parent && props.parent.root) || self  self._path = self.path = path.resolve(props.path)  if (process.platform === 'win32') {    self.path = self._path = self.path.replace(/\?/g, '_')    if (self._path.length >= 260) {      self._swallowErrors = true      self._path = '\\\\?\\' + self.path.replace(/\//g, '\\')    }  }  self.basename = path.basename(props.path)  self.dirname = path.dirname(props.path)  self.linkpath = props.linkpath || null  props.parent = props.root = null  // console.error("\n\n\n%s setting size to", props.path, props.size)  self.size = props.size  if (typeof props.mode === 'string') {    props.mode = parseInt(props.mode, 8)  }  self.readable = false  self.writable = true  // buffer until ready, or while handling another entry  self._buffer = []  self.ready = false  self.filter = typeof props.filter === 'function' ? props.filter : null  // start the ball rolling.  // this checks what's there already, and then calls  // self._create() to call the impl-specific creation stuff.  self._stat(current)}// Calling this means that it's something we can't create.// Just assert that it's already there, otherwise raise a warning.Writer.prototype._create = function () {  var self = this  fs[self.props.follow ? 'stat' : 'lstat'](self._path, function (er) {    if (er) {      return self.warn('Cannot create ' + self._path + '\n' +        'Unsupported type: ' + self.type, 'ENOTSUP')    }    self._finish()  })}Writer.prototype._stat = function (current) {  var self = this  var props = self.props  var stat = props.follow ? 'stat' : 'lstat'  var who = self._proxy || self  if (current) statCb(null, current)  else fs[stat](self._path, statCb)  function statCb (er, current) {    if (self.filter && !self.filter.call(who, who, current)) {      self._aborted = true      self.emit('end')      self.emit('close')      return    }    // if it's not there, great.  We'll just create it.    // if it is there, then we'll need to change whatever differs    if (er || !current) {      return create(self)    }    self._old = current    var currentType = getType(current)    // if it's a type change, then we need to clobber or error.    // if it's not a type change, then let the impl take care of it.    if (currentType !== self.type || self.type === 'File' && current.nlink > 1) {      return rimraf(self._path, function (er) {        if (er) return self.error(er)        self._old = null        create(self)      })    }    // otherwise, just handle in the app-specific way    // this creates a fs.WriteStream, or mkdir's, or whatever    create(self)  }}function create (self) {  // console.error("W create", self._path, Writer.dirmode)  // XXX Need to clobber non-dirs that are in the way,  // unless { clobber: false } in the props.  mkdir(path.dirname(self._path), Writer.dirmode, function (er, made) {    // console.error("W created", path.dirname(self._path), er)    if (er) return self.error(er)    // later on, we have to set the mode and owner for these    self._madeDir = made    return self._create()  })}function endChmod (self, want, current, path, cb) {  var wantMode = want.mode  var chmod = want.follow || self.type !== 'SymbolicLink'    ? 'chmod' : 'lchmod'  if (!fs[chmod]) return cb()  if (typeof wantMode !== 'number') return cb()  var curMode = current.mode & parseInt('0777', 8)  wantMode = wantMode & parseInt('0777', 8)  if (wantMode === curMode) return cb()  fs[chmod](path, wantMode, cb)}function endChown (self, want, current, path, cb) {  // Don't even try it unless root.  Too easy to EPERM.  if (process.platform === 'win32') return cb()  if (!process.getuid || process.getuid() !== 0) return cb()  if (typeof want.uid !== 'number' &&    typeof want.gid !== 'number') return cb()  if (current.uid === want.uid &&    current.gid === want.gid) return cb()  var chown = (self.props.follow || self.type !== 'SymbolicLink')    ? 'chown' : 'lchown'  if (!fs[chown]) return cb()  if (typeof want.uid !== 'number') want.uid = current.uid  if (typeof want.gid !== 'number') want.gid = current.gid  fs[chown](path, want.uid, want.gid, cb)}function endUtimes (self, want, current, path, cb) {  if (!fs.utimes || process.platform === 'win32') return cb()  var utimes = (want.follow || self.type !== 'SymbolicLink')    ? 'utimes' : 'lutimes'  if (utimes === 'lutimes' && !fs[utimes]) {    utimes = 'utimes'  }  if (!fs[utimes]) return cb()  var curA = current.atime  var curM = current.mtime  var meA = want.atime  var meM = want.mtime  if (meA === undefined) meA = curA  if (meM === undefined) meM = curM  if (!isDate(meA)) meA = new Date(meA)  if (!isDate(meM)) meA = new Date(meM)  if (meA.getTime() === curA.getTime() &&    meM.getTime() === curM.getTime()) return cb()  fs[utimes](path, meA, meM, cb)}// XXX This function is beastly.  Break it up!Writer.prototype._finish = function () {  var self = this  if (self._finishing) return  self._finishing = true  // console.error(" W Finish", self._path, self.size)  // set up all the things.  // At this point, we're already done writing whatever we've gotta write,  // adding files to the dir, etc.  var todo = 0  var errState = null  var done = false  if (self._old) {    // the times will almost *certainly* have changed.    // adds the utimes syscall, but remove another stat.    self._old.atime = new Date(0)    self._old.mtime = new Date(0)    // console.error(" W Finish Stale Stat", self._path, self.size)    setProps(self._old)  } else {    var stat = self.props.follow ? 'stat' : 'lstat'    // console.error(" W Finish Stating", self._path, self.size)    fs[stat](self._path, function (er, current) {      // console.error(" W Finish Stated", self._path, self.size, current)      if (er) {        // if we're in the process of writing out a        // directory, it's very possible that the thing we're linking to        // doesn't exist yet (especially if it was intended as a symlink),        // so swallow ENOENT errors here and just soldier on.        if (er.code === 'ENOENT' &&          (self.type === 'Link' || self.type === 'SymbolicLink') &&          process.platform === 'win32') {          self.ready = true          self.emit('ready')          self.emit('end')          self.emit('close')          self.end = self._finish = function () {}          return        } else return self.error(er)      }      setProps(self._old = current)    })  }  return  function setProps (current) {    todo += 3    endChmod(self, self.props, current, self._path, next('chmod'))    endChown(self, self.props, current, self._path, next('chown'))    endUtimes(self, self.props, current, self._path, next('utimes'))  }  function next (what) {    return function (er) {      // console.error("   W Finish", what, todo)      if (errState) return      if (er) {        er.fstream_finish_call = what        return self.error(errState = er)      }      if (--todo > 0) return      if (done) return      done = true      // we may still need to set the mode/etc. on some parent dirs      // that were created previously.  delay end/close until then.      if (!self._madeDir) return end()      else endMadeDir(self, self._path, end)      function end (er) {        if (er) {          er.fstream_finish_call = 'setupMadeDir'          return self.error(er)        }        // all the props have been set, so we're completely done.        self.emit('end')        self.emit('close')      }    }  }}function endMadeDir (self, p, cb) {  var made = self._madeDir  // everything *between* made and path.dirname(self._path)  // needs to be set up.  Note that this may just be one dir.  var d = path.dirname(p)  endMadeDir_(self, d, function (er) {    if (er) return cb(er)    if (d === made) {      return cb()    }    endMadeDir(self, d, cb)  })}function endMadeDir_ (self, p, cb) {  var dirProps = {}  Object.keys(self.props).forEach(function (k) {    dirProps[k] = self.props[k]    // only make non-readable dirs if explicitly requested.    if (k === 'mode' && self.type !== 'Directory') {      dirProps[k] = dirProps[k] | parseInt('0111', 8)    }  })  var todo = 3  var errState = null  fs.stat(p, function (er, current) {    if (er) return cb(errState = er)    endChmod(self, dirProps, current, p, next)    endChown(self, dirProps, current, p, next)    endUtimes(self, dirProps, current, p, next)  })  function next (er) {    if (errState) return    if (er) return cb(errState = er)    if (--todo === 0) return cb()  }}Writer.prototype.pipe = function () {  this.error("Can't pipe from writable stream")}Writer.prototype.add = function () {  this.error("Can't add to non-Directory type")}Writer.prototype.write = function () {  return true}function objectToString (d) {  return Object.prototype.toString.call(d)}function isDate (d) {  return typeof d === 'object' && objectToString(d) === '[object Date]'}
 |