move-file.js 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. 'use strict'
  2. const fs = require('graceful-fs')
  3. const BB = require('bluebird')
  4. const chmod = BB.promisify(fs.chmod)
  5. const unlink = BB.promisify(fs.unlink)
  6. let move
  7. let pinflight
  8. module.exports = moveFile
  9. function moveFile (src, dest) {
  10. // This isn't quite an fs.rename -- the assumption is that
  11. // if `dest` already exists, and we get certain errors while
  12. // trying to move it, we should just not bother.
  13. //
  14. // In the case of cache corruption, users will receive an
  15. // EINTEGRITY error elsewhere, and can remove the offending
  16. // content their own way.
  17. //
  18. // Note that, as the name suggests, this strictly only supports file moves.
  19. return BB.fromNode(cb => {
  20. fs.link(src, dest, err => {
  21. if (err) {
  22. if (err.code === 'EEXIST' || err.code === 'EBUSY') {
  23. // file already exists, so whatever
  24. } else if (err.code === 'EPERM' && process.platform === 'win32') {
  25. // file handle stayed open even past graceful-fs limits
  26. } else {
  27. return cb(err)
  28. }
  29. }
  30. return cb()
  31. })
  32. }).then(() => {
  33. // content should never change for any reason, so make it read-only
  34. return BB.join(unlink(src), process.platform !== 'win32' && chmod(dest, '0444'))
  35. }).catch(() => {
  36. if (!pinflight) { pinflight = require('promise-inflight') }
  37. return pinflight('cacache-move-file:' + dest, () => {
  38. return BB.promisify(fs.stat)(dest).catch(err => {
  39. if (err.code !== 'ENOENT') {
  40. // Something else is wrong here. Bail bail bail
  41. throw err
  42. }
  43. // file doesn't already exist! let's try a rename -> copy fallback
  44. if (!move) { move = require('move-concurrently') }
  45. return move(src, dest, { BB, fs })
  46. })
  47. })
  48. })
  49. }