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