2015-01-28 18:41:53 +00:00
|
|
|
os = require('os')
|
2015-01-28 12:33:50 +00:00
|
|
|
fs = require('fs')
|
2015-01-28 19:30:08 +00:00
|
|
|
path = require('path')
|
2015-01-28 19:13:43 +00:00
|
|
|
_ = require('lodash-contrib')
|
2015-01-28 18:53:34 +00:00
|
|
|
async = require('async')
|
2015-01-28 18:41:53 +00:00
|
|
|
childProcess = require('child_process')
|
2015-01-28 12:33:50 +00:00
|
|
|
progressStream = require('progress-stream')
|
|
|
|
|
2015-01-28 18:41:53 +00:00
|
|
|
IS_WINDOWS = os.platform() is 'win32'
|
2015-01-28 19:26:56 +00:00
|
|
|
DISK_IO_FLAGS = 'rs+'
|
2015-01-28 14:16:01 +00:00
|
|
|
|
2015-01-28 18:41:53 +00:00
|
|
|
exports.rescanDrives = (callback) ->
|
|
|
|
return callback() if not IS_WINDOWS
|
|
|
|
diskpartRescanScriptPath = path.join(__dirname, 'scripts', 'diskpart_rescan')
|
2015-01-28 19:40:00 +00:00
|
|
|
childProcess.exec("diskpart /s \"#{diskpartRescanScriptPath}\"", {}, _.unary(callback))
|
2015-01-28 14:16:01 +00:00
|
|
|
|
2015-01-28 18:51:11 +00:00
|
|
|
exports.eraseMBR = (devicePath, callback) ->
|
|
|
|
return callback() if not IS_WINDOWS
|
|
|
|
|
|
|
|
bufferSize = 512
|
|
|
|
|
|
|
|
async.waterfall([
|
|
|
|
|
|
|
|
(callback) ->
|
2015-01-28 19:26:56 +00:00
|
|
|
fs.open(devicePath, DISK_IO_FLAGS, null, callback)
|
2015-01-28 18:51:11 +00:00
|
|
|
|
|
|
|
(fd, callback) ->
|
|
|
|
buffer = new Buffer(bufferSize)
|
|
|
|
buffer.fill(0)
|
|
|
|
fs.write fd, buffer, 0, bufferSize, 0, (error, bytesWritten) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
return callback(null, bytesWritten, fd)
|
|
|
|
|
|
|
|
(bytesWritten, fd, callback) ->
|
|
|
|
if bytesWritten isnt bufferSize
|
|
|
|
error = "Bytes written: #{bytesWritten}, expected #{bufferSize}"
|
|
|
|
return callback(error)
|
|
|
|
|
|
|
|
fs.close(fd, callback)
|
|
|
|
|
|
|
|
], callback)
|
|
|
|
|
2015-01-28 12:33:50 +00:00
|
|
|
exports.writeImage = (devicePath, imagePath, options = {}, callback = _.noop) ->
|
|
|
|
|
|
|
|
if not fs.existsSync(imagePath)
|
|
|
|
return callback(new Error("Invalid OS image: #{imagePath}"))
|
|
|
|
|
2015-01-28 18:41:53 +00:00
|
|
|
if not IS_WINDOWS and not fs.existsSync(devicePath)
|
2015-01-28 12:33:50 +00:00
|
|
|
return callback(new Error("Invalid device: #{devicePath}"))
|
|
|
|
|
|
|
|
imageFileSize = fs.statSync(imagePath).size
|
|
|
|
|
2015-01-28 19:26:56 +00:00
|
|
|
if imageFileSize is 0
|
|
|
|
return callback(new Error("Invalid OS image: #{imagePath}. The image is 0 bytes."))
|
|
|
|
|
2015-01-28 12:33:50 +00:00
|
|
|
progress = progressStream
|
|
|
|
length: imageFileSize
|
|
|
|
time: 500
|
|
|
|
|
|
|
|
if options.progress
|
|
|
|
progress.on('progress', options.onProgress)
|
|
|
|
|
2015-01-28 19:13:43 +00:00
|
|
|
async.waterfall [
|
2015-01-28 18:41:53 +00:00
|
|
|
|
2015-01-28 18:51:11 +00:00
|
|
|
(callback) ->
|
|
|
|
exports.eraseMBR(devicePath, callback)
|
|
|
|
|
2015-01-28 18:41:53 +00:00
|
|
|
(callback) ->
|
|
|
|
exports.rescanDrives(callback)
|
|
|
|
|
|
|
|
(callback) ->
|
2015-01-28 19:26:56 +00:00
|
|
|
deviceFileStream = fs.createWriteStream(devicePath, flags: DISK_IO_FLAGS)
|
|
|
|
deviceFileStream.on('error', callback)
|
|
|
|
|
|
|
|
imageFileStream = fs.createReadStream(imagePath)
|
2015-01-28 19:13:43 +00:00
|
|
|
imageFileStream
|
2015-01-28 18:41:53 +00:00
|
|
|
.pipe(progress)
|
2015-01-28 19:13:43 +00:00
|
|
|
.pipe(deviceFileStream)
|
2015-01-28 18:41:53 +00:00
|
|
|
|
|
|
|
# TODO: We should make use of nodewindows.elevate()
|
|
|
|
# if we get an EPERM error.
|
2015-01-28 19:13:43 +00:00
|
|
|
.on('error', _.unary(callback))
|
2015-01-28 18:41:53 +00:00
|
|
|
|
2015-01-28 19:13:43 +00:00
|
|
|
.on('close', _.unary(callback))
|
2015-01-28 18:41:53 +00:00
|
|
|
|
|
|
|
(callback) ->
|
|
|
|
exports.rescanDrives(callback)
|
|
|
|
|
2015-01-28 19:13:43 +00:00
|
|
|
], (error) ->
|
|
|
|
return callback() if not error?
|
2015-01-28 12:33:50 +00:00
|
|
|
|
2015-01-28 19:13:43 +00:00
|
|
|
if error.code is 'EBUSY'
|
|
|
|
error.message = "Try umounting #{error.path} first."
|
2015-01-28 12:33:50 +00:00
|
|
|
|
2015-01-28 19:51:36 +00:00
|
|
|
if error.code is 'ENOENT'
|
|
|
|
error.message = "Invalid device #{error.path}"
|
|
|
|
|
|
|
|
# Prevents outer handler to take
|
|
|
|
# it as an usual ENOENT error
|
|
|
|
delete error.code
|
|
|
|
|
2015-01-28 19:13:43 +00:00
|
|
|
return callback(error)
|