web 3d图形渲染器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

69 lines
2.3 KiB

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