verdaccio/lib/local-fs.js

278 lines
6.1 KiB
JavaScript
Raw Normal View History

var fs = require('fs')
var Error = require('http-errors')
var mkdirp = require('mkdirp')
var Path = require('path')
var MyStreams = require('./streams')
function FSError(code) {
var err = Error(code)
err.code = code
return err
}
2013-06-01 00:57:28 +02:00
2014-03-31 06:32:11 +02:00
try {
var fsExt = require('fs-ext')
} catch (e) {
fsExt = {
flock: function() {
arguments[arguments.length-1]()
}
}
2014-03-31 06:32:11 +02:00
}
function tempFile(str) {
return str + '.tmp' + String(Math.random()).substr(2)
}
function renameTmp(src, dst, _cb) {
function cb(err) {
if (err) fs.unlink(src)
_cb(err)
}
if (process.platform !== 'win32') {
return fs.rename(src, dst, cb)
}
// windows can't remove opened file,
// but it seem to be able to rename it
var tmp = tempFile(dst)
fs.rename(dst, tmp, function(err) {
fs.rename(src, dst, cb)
if (!err) fs.unlink(tmp)
})
}
2013-06-18 20:14:55 +02:00
function write(dest, data, cb) {
var safe_write = function(cb) {
var tmpname = tempFile(dest)
fs.writeFile(tmpname, data, function(err) {
if (err) return cb(err)
renameTmp(tmpname, dest, cb)
})
}
safe_write(function(err) {
if (err && err.code === 'ENOENT') {
mkdirp(Path.dirname(dest), function(err) {
if (err) return cb(err)
safe_write(cb)
})
} else {
cb(err)
}
})
2013-06-01 00:57:28 +02:00
}
2013-06-20 15:07:34 +02:00
function write_stream(name) {
var stream = MyStreams.UploadTarballStream()
var _ended = 0
stream.on('end', function() {
_ended = 1
})
fs.exists(name, function(exists) {
if (exists) return stream.emit('error', FSError('EEXISTS'))
var tmpname = name + '.tmp-'+String(Math.random()).replace(/^0\./, '')
var file = fs.createWriteStream(tmpname)
var opened = false
stream.pipe(file)
stream.done = function() {
function onend() {
file.on('close', function() {
renameTmp(tmpname, name, function(err) {
if (err) {
stream.emit('error', err)
} else {
stream.emit('success')
}
})
})
file.destroySoon()
}
if (_ended) {
onend()
} else {
stream.on('end', onend)
}
}
stream.abort = function() {
if (opened) {
opened = false
file.on('close', function() {
fs.unlink(tmpname, function(){})
})
}
file.destroySoon()
}
file.on('open', function() {
opened = true
// re-emitting open because it's handled in storage.js
stream.emit('open')
})
file.on('error', function(err) {
stream.emit('error', err)
})
})
return stream
2013-06-20 15:07:34 +02:00
}
function read_stream(name, stream, callback) {
var rstream = fs.createReadStream(name)
rstream.on('error', function(err) {
stream.emit('error', err)
})
rstream.on('open', function(fd) {
fs.fstat(fd, function(err, stats) {
if (err) return stream.emit('error', err)
stream.emit('content-length', stats.size)
stream.emit('open')
rstream.pipe(stream)
})
})
var stream = MyStreams.ReadTarballStream()
stream.abort = function() {
rstream.close()
}
return stream
2013-06-20 15:07:34 +02:00
}
2013-06-01 00:57:28 +02:00
function create(name, contents, callback) {
fs.exists(name, function(exists) {
if (exists) return callback( FSError('EEXISTS') )
write(name, contents, callback)
})
2013-06-01 00:57:28 +02:00
}
function update(name, contents, callback) {
fs.exists(name, function(exists) {
if (!exists) return callback( FSError('ENOENT') )
write(name, contents, callback)
})
2013-06-01 00:57:28 +02:00
}
function read(name, callback) {
fs.readFile(name, callback)
}
// open and flock with exponential backoff
function open_flock(name, opmod, flmod, tries, backoff, cb) {
fs.open(name, opmod, function(err, fd) {
if (err) return cb(err, fd)
fsExt.flock(fd, flmod, function(err) {
if (err) {
if (!tries) {
fs.close(fd, function() {
cb(err)
})
} else {
fs.close(fd, function() {
setTimeout(function() {
open_flock(name, opmod, flmod, tries-1, backoff*2, cb)
}, backoff)
})
}
} else {
cb(null, fd)
}
})
})
2014-01-13 17:48:51 +01:00
}
// this function neither unlocks file nor closes it
// it'll have to be done manually later
2014-10-03 05:51:56 +02:00
function lock_and_read(name, _callback) {
open_flock(name, 'r', 'exnb', 4, 10, function(err, fd) {
function callback(err) {
if (err && fd) {
fs.close(fd, function(err2) {
_callback(err)
})
} else {
_callback.apply(null, arguments)
}
}
if (err) return callback(err, fd)
fs.fstat(fd, function(err, st) {
if (err) return callback(err, fd)
var buffer = Buffer(st.size)
if (st.size === 0) return onRead(null, 0, buffer)
fs.read(fd, buffer, 0, st.size, null, onRead)
function onRead(err, bytesRead, buffer) {
if (err) return callback(err, fd)
if (bytesRead != st.size) return callback(Error('st.size != bytesRead'), fd)
callback(null, fd, buffer)
}
})
})
2013-06-01 00:57:28 +02:00
}
2014-01-13 17:48:51 +01:00
module.exports.read = read
2013-06-14 10:34:29 +02:00
2014-01-13 17:48:51 +01:00
module.exports.read_json = function(name, cb) {
read(name, function(err, res) {
if (err) return cb(err)
var args = []
try {
args = [ null, JSON.parse(res.toString('utf8')) ]
} catch(err) {
args = [ err ]
}
cb.apply(null, args)
})
2013-06-14 10:34:29 +02:00
}
2014-01-13 17:48:51 +01:00
module.exports.lock_and_read = lock_and_read
2014-01-13 17:48:51 +01:00
module.exports.lock_and_read_json = function(name, cb) {
lock_and_read(name, function(err, fd, res) {
if (err) return cb(err, fd)
var args = []
try {
args = [ null, fd, JSON.parse(res.toString('utf8')) ]
} catch(err) {
args = [ err, fd ]
}
cb.apply(null, args)
})
}
2014-01-13 17:48:51 +01:00
module.exports.create = create
2013-10-18 23:17:53 +02:00
2014-01-13 17:48:51 +01:00
module.exports.create_json = function(name, value, cb) {
create(name, JSON.stringify(value, null, '\t'), cb)
2013-06-14 10:34:29 +02:00
}
2014-01-13 17:48:51 +01:00
module.exports.update = update
2013-06-14 10:34:29 +02:00
2014-01-13 17:48:51 +01:00
module.exports.update_json = function(name, value, cb) {
update(name, JSON.stringify(value, null, '\t'), cb)
2013-06-14 10:34:29 +02:00
}
2014-01-13 17:48:51 +01:00
module.exports.write = write
2013-06-14 10:34:29 +02:00
2014-01-13 17:48:51 +01:00
module.exports.write_json = function(name, value, cb) {
write(name, JSON.stringify(value, null, '\t'), cb)
2013-06-18 20:14:55 +02:00
}
2014-01-13 17:48:51 +01:00
module.exports.write_stream = write_stream
2013-06-18 20:14:55 +02:00
2014-01-13 17:48:51 +01:00
module.exports.read_stream = read_stream
2013-06-20 15:07:34 +02:00
2014-01-13 17:48:51 +01:00
module.exports.unlink = fs.unlink
2014-01-13 17:48:51 +01:00
module.exports.rmdir = fs.rmdir
2013-06-01 00:57:28 +02:00