2016-09-15 11:29:45 +02:00
'use strict' ;
exports = module . exports = {
2017-09-17 18:50:29 -07:00
upload : upload ,
download : download ,
2017-09-23 14:27:35 -07:00
downloadDir : downloadDir ,
2017-09-17 18:50:29 -07:00
copy : copy ,
2017-09-23 11:09:36 -07:00
remove : remove ,
2017-09-27 17:34:49 -07:00
removeDir : removeDir ,
2016-09-16 11:21:08 +02:00
2017-01-04 16:22:58 -08:00
backupDone : backupDone ,
2016-10-11 11:36:25 +02:00
testConfig : testConfig
2016-09-15 11:29:45 +02:00
} ;
2017-04-20 15:15:49 +02:00
var assert = require ( 'assert' ) ,
2016-10-10 13:21:45 +02:00
BackupsError = require ( '../backups.js' ) . BackupsError ,
2017-04-11 11:00:55 +02:00
debug = require ( 'debug' ) ( 'box:storage/filesystem' ) ,
2017-10-04 11:00:30 -07:00
EventEmitter = require ( 'events' ) ,
2016-09-16 11:55:59 +02:00
fs = require ( 'fs' ) ,
2017-04-11 11:00:55 +02:00
mkdirp = require ( 'mkdirp' ) ,
2017-04-18 15:32:59 +02:00
path = require ( 'path' ) ,
2017-04-20 16:59:17 -07:00
safe = require ( 'safetydance' ) ,
2017-09-20 09:57:16 -07:00
shell = require ( '../shell.js' ) ;
2016-09-16 11:55:59 +02:00
2017-04-18 15:32:59 +02:00
// storage api
2017-09-20 09:57:16 -07:00
function upload ( apiConfig , backupFilePath , sourceStream , callback ) {
2016-09-15 11:29:45 +02:00
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-09-19 20:40:38 -07:00
assert . strictEqual ( typeof backupFilePath , 'string' ) ;
2017-09-20 09:57:16 -07:00
assert . strictEqual ( typeof sourceStream , 'object' ) ;
2016-09-15 11:29:45 +02:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-04-20 16:59:17 -07:00
mkdirp ( path . dirname ( backupFilePath ) , function ( error ) {
2017-04-20 19:00:12 -07:00
if ( error ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2016-09-16 11:21:08 +02:00
2017-09-22 14:40:37 -07:00
safe . fs . unlinkSync ( backupFilePath ) ; // remove any hardlink
2017-04-20 16:59:17 -07:00
var fileStream = fs . createWriteStream ( backupFilePath ) ;
2016-09-16 11:21:08 +02:00
2017-09-27 19:31:07 -07:00
// this pattern is required to ensure that the file got created before 'finish'
fileStream . on ( 'open' , function ( ) {
sourceStream . pipe ( fileStream ) ;
} ) ;
2017-04-20 15:35:52 +02:00
fileStream . on ( 'error' , function ( error ) {
2017-09-20 09:57:16 -07:00
debug ( '[%s] upload: out stream error.' , backupFilePath , error ) ;
2017-04-20 19:00:12 -07:00
callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2017-04-20 15:35:52 +02:00
} ) ;
2017-09-26 16:42:54 -07:00
fileStream . on ( 'finish' , function ( ) {
2017-09-27 14:44:48 -07:00
// in test, upload() may or may not be called via sudo script
const BACKUP _UID = parseInt ( process . env . SUDO _UID , 10 ) || process . getuid ( ) ;
if ( ! safe . fs . chownSync ( backupFilePath , BACKUP _UID , BACKUP _UID ) ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , 'Unable to chown:' + safe . error . message ) ) ;
if ( ! safe . fs . chownSync ( path . dirname ( backupFilePath ) , BACKUP _UID , BACKUP _UID ) ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , 'Unable to chown:' + safe . error . message ) ) ;
2017-04-20 16:59:17 -07:00
2017-09-27 14:44:48 -07:00
debug ( 'upload %s: done.' , backupFilePath ) ;
2017-04-20 16:59:17 -07:00
2017-09-27 14:44:48 -07:00
callback ( null ) ;
2017-04-11 11:00:55 +02:00
} ) ;
} ) ;
2016-09-16 11:21:08 +02:00
}
2017-09-20 09:57:16 -07:00
function download ( apiConfig , sourceFilePath , callback ) {
2016-09-15 11:29:45 +02:00
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-09-19 20:40:38 -07:00
assert . strictEqual ( typeof sourceFilePath , 'string' ) ;
2016-09-15 11:29:45 +02:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-20 09:57:16 -07:00
debug ( 'download: %s' , sourceFilePath ) ;
2017-04-11 11:00:55 +02:00
2017-09-28 14:26:39 -07:00
if ( ! safe . fs . existsSync ( sourceFilePath ) ) return callback ( new BackupsError ( BackupsError . NOT _FOUND , 'File not found' ) ) ;
2017-04-21 15:28:25 -07:00
var fileStream = fs . createReadStream ( sourceFilePath ) ;
2017-09-28 14:26:39 -07:00
callback ( null , fileStream ) ;
2017-04-17 14:46:19 +02:00
}
2017-09-23 14:27:35 -07:00
function downloadDir ( apiConfig , backupFilePath , destDir , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
assert . strictEqual ( typeof backupFilePath , 'string' ) ;
assert . strictEqual ( typeof destDir , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debug ( 'downloadDir: %s -> %s' , backupFilePath , destDir ) ;
2017-09-26 15:47:26 -07:00
shell . exec ( 'downloadDir' , '/bin/cp' , [ '-r' , backupFilePath + '/.' , destDir ] , { } , function ( error ) {
2017-09-23 14:27:35 -07:00
if ( error ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2017-10-02 20:08:00 -07:00
callback ( null ) ;
2017-09-23 14:27:35 -07:00
} ) ;
}
2017-10-04 11:00:30 -07:00
function copy ( apiConfig , oldFilePath , newFilePath ) {
2017-04-17 14:46:19 +02:00
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-09-19 20:40:38 -07:00
assert . strictEqual ( typeof oldFilePath , 'string' ) ;
assert . strictEqual ( typeof newFilePath , 'string' ) ;
2017-04-17 14:46:19 +02:00
2017-09-18 14:25:46 -07:00
debug ( 'copy: %s -> %s' , oldFilePath , newFilePath ) ;
2017-04-20 15:15:49 +02:00
2017-10-04 11:00:30 -07:00
var events = new EventEmitter ( ) ;
2017-04-20 16:59:17 -07:00
mkdirp ( path . dirname ( newFilePath ) , function ( error ) {
2017-10-04 11:00:30 -07:00
if ( error ) return events . emit ( 'done' , new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2017-04-17 14:46:19 +02:00
2017-09-18 12:42:42 -07:00
// this will hardlink backups saving space
shell . exec ( 'copy' , '/bin/cp' , [ '-al' , oldFilePath , newFilePath ] , { } , function ( error ) {
2017-10-04 11:00:30 -07:00
if ( error ) return events . emit ( 'done' , new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2017-04-20 16:59:17 -07:00
2017-10-04 11:00:30 -07:00
events . emit ( 'done' , null ) ;
2017-04-20 16:59:17 -07:00
} ) ;
2017-04-20 15:41:25 +02:00
} ) ;
2017-10-04 11:00:30 -07:00
return events ;
2016-09-15 11:29:45 +02:00
}
2017-09-27 17:34:49 -07:00
function remove ( apiConfig , filename , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
assert . strictEqual ( typeof filename , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-28 20:55:39 -07:00
var stat = safe . fs . statSync ( filename ) ;
if ( ! stat ) return callback ( ) ;
2017-09-27 17:34:49 -07:00
2017-09-28 20:55:39 -07:00
if ( stat . isFile ( ) ) {
2017-09-30 17:28:35 -07:00
if ( ! safe . fs . unlinkSync ( filename ) ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , safe . error . message ) ) ;
2017-09-28 20:55:39 -07:00
} else if ( stat . isDirectory ( ) ) {
2017-09-30 17:28:35 -07:00
if ( ! safe . fs . rmdirSync ( filename ) ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , safe . error . message ) ) ;
2017-09-28 20:55:39 -07:00
}
2017-09-27 17:34:49 -07:00
2017-10-02 20:08:00 -07:00
callback ( null ) ;
2017-09-27 17:34:49 -07:00
}
function removeDir ( apiConfig , pathPrefix , callback ) {
2017-04-17 15:45:58 +02:00
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-09-23 11:09:36 -07:00
assert . strictEqual ( typeof pathPrefix , 'string' ) ;
2017-04-17 15:45:58 +02:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-27 17:34:49 -07:00
shell . exec ( 'removeDir' , '/bin/rm' , [ '-rf' , pathPrefix ] , { } , function ( error ) {
2017-09-23 11:09:36 -07:00
if ( error ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2017-04-23 11:34:46 -07:00
2017-10-02 20:08:00 -07:00
callback ( null ) ;
2017-09-23 11:09:36 -07:00
} ) ;
2017-04-17 15:45:58 +02:00
}
2016-10-11 11:36:25 +02:00
function testConfig ( apiConfig , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-19 12:43:13 -07:00
if ( typeof apiConfig . backupFolder !== 'string' ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'backupFolder must be string' ) ) ;
2016-10-11 11:36:25 +02:00
2017-09-19 12:43:13 -07:00
if ( ! apiConfig . backupFolder ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'backupFolder is required' ) ) ;
2017-04-21 15:37:57 +02:00
fs . stat ( apiConfig . backupFolder , function ( error , result ) {
if ( error ) {
debug ( 'testConfig: %s' , apiConfig . backupFolder , error ) ;
return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'Directory does not exist or cannot be accessed' ) ) ;
}
if ( ! result . isDirectory ( ) ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'Backup location is not a directory' ) ) ;
callback ( null ) ;
} ) ;
2016-10-11 11:36:25 +02:00
}
2017-01-04 16:22:58 -08:00
2017-09-26 12:28:33 -07:00
function backupDone ( apiConfig , backupId , appBackupIds , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-04-21 10:31:43 +02:00
assert . strictEqual ( typeof backupId , 'string' ) ;
assert ( Array . isArray ( appBackupIds ) ) ;
2017-01-04 16:22:58 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
callback ( ) ;
}