move-file.js 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. 'use strict'
  2. const fs = require('fs/promises')
  3. const { moveFile: move } = require('@npmcli/fs')
  4. const pinflight = require('promise-inflight')
  5. module.exports = moveFile
  6. async function moveFile (src, dest) {
  7. const isWindows = process.platform === 'win32'
  8. // This isn't quite an fs.rename -- the assumption is that
  9. // if `dest` already exists, and we get certain errors while
  10. // trying to move it, we should just not bother.
  11. //
  12. // In the case of cache corruption, users will receive an
  13. // EINTEGRITY error elsewhere, and can remove the offending
  14. // content their own way.
  15. //
  16. // Note that, as the name suggests, this strictly only supports file moves.
  17. try {
  18. await fs.link(src, dest)
  19. } catch (err) {
  20. if (isWindows && err.code === 'EPERM') {
  21. // XXX This is a really weird way to handle this situation, as it
  22. // results in the src file being deleted even though the dest
  23. // might not exist. Since we pretty much always write files to
  24. // deterministic locations based on content hash, this is likely
  25. // ok (or at worst, just ends in a future cache miss). But it would
  26. // be worth investigating at some time in the future if this is
  27. // really what we want to do here.
  28. } else if (err.code === 'EEXIST' || err.code === 'EBUSY') {
  29. // file already exists, so whatever
  30. } else {
  31. throw err
  32. }
  33. }
  34. try {
  35. await Promise.all([
  36. fs.unlink(src),
  37. !isWindows && fs.chmod(dest, '0444'),
  38. ])
  39. } catch (e) {
  40. return pinflight('cacache-move-file:' + dest, async () => {
  41. await fs.stat(dest).catch((err) => {
  42. if (err.code !== 'ENOENT') {
  43. // Something else is wrong here. Bail bail bail
  44. throw err
  45. }
  46. })
  47. // file doesn't already exist! let's try a rename -> copy fallback
  48. // only delete if it successfully copies
  49. return move(src, dest)
  50. })
  51. }
  52. }