diff --git a/setup/start/sudoers b/setup/start/sudoers index caf87c046..1434b192e 100644 --- a/setup/start/sudoers +++ b/setup/start/sudoers @@ -34,6 +34,9 @@ yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/retire.sh Defaults!/home/yellowtent/box/src/scripts/rmbackup.sh env_keep="HOME BOX_ENV" yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/rmbackup.sh +Defaults!/home/yellowtent/box/src/scripts/cpbackup.sh env_keep="HOME BOX_ENV" +yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/cpbackup.sh + Defaults!/home/yellowtent/box/src/scripts/update.sh env_keep="HOME BOX_ENV" yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/update.sh diff --git a/src/backups.js b/src/backups.js index 5d0de949b..04422cf39 100644 --- a/src/backups.js +++ b/src/backups.js @@ -154,7 +154,7 @@ function copyLastBackup(app, manifest, prefix, callback) { assert.strictEqual(typeof callback, 'function'); var timestamp = (new Date()).toISOString().replace(/[T.]/g, '-').replace(/[:Z]/g,''); - var newBackupId = util.format('%s/app_%s_%s_v%s.tar.gz', prefix, app.id, timestamp, manifest.version); + var newBackupId = util.format('%s/app_%s_%s_v%s', prefix, app.id, timestamp, manifest.version); settings.getBackupConfig(function (error, backupConfig) { if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); diff --git a/src/scripts/cpbackup.sh b/src/scripts/cpbackup.sh new file mode 100755 index 000000000..3abac653a --- /dev/null +++ b/src/scripts/cpbackup.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# This is part of the storage/filesystem backend! + +set -eu -o pipefail + +if [[ ${EUID} -ne 0 ]]; then + echo "This script should be run as root." > /dev/stderr + exit 1 +fi + +if [[ $# == 1 && "$1" == "--check" ]]; then + echo "OK" + exit 0 +fi + +if [[ $# -lt 2 ]]; then + echo "Usage: cpbackup.sh " + exit 1 +fi + +# ensure destination path +readonly DEST_PATH=$(dirname "${2}") +mkdir -p "${DEST_PATH}" + +# copy the file +cp "${1}" "${2}" diff --git a/src/storage/filesystem.js b/src/storage/filesystem.js index 0c46fdda7..8ca4c79ef 100644 --- a/src/storage/filesystem.js +++ b/src/storage/filesystem.js @@ -33,7 +33,8 @@ var assert = require('assert'), archiver = require('archiver'); var FALLBACK_BACKUP_FOLDER = '/var/backups'; -var RMBACKUP_CMD = path.join(__dirname, '../scripts/rmbackup.sh'); +var COPY_BACKUP_CMD = path.join(__dirname, '../scripts/cpbackup.sh'); +var REMOVE_BACKUP_CMD = path.join(__dirname, '../scripts/rmbackup.sh'); function backup(apiConfig, backupId, sourceDirectories, callback) { assert.strictEqual(typeof apiConfig, 'object'); @@ -132,7 +133,7 @@ function getDownloadStream(apiConfig, backupId, callback) { assert.strictEqual(typeof backupId, 'string'); assert.strictEqual(typeof callback, 'function'); - var backupFilePath = path.join(apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER, backupId); + var backupFilePath = path.join(apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER, backupId + '.tar.gz'); debug('[%s] getDownloadStream: %s %s', backupId, backupId, backupFilePath); @@ -170,8 +171,6 @@ function getAppRestoreConfig(apiConfig, backupId, callback) { assert.strictEqual(typeof backupId, 'string'); assert.strictEqual(typeof callback, 'function'); - callback = once(callback); - var sourceFilePath = path.join(apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER, backupId + '.json'); debug('[%s] getAppRestoreConfig: %s', backupId, sourceFilePath); @@ -195,21 +194,19 @@ function copyBackup(apiConfig, oldBackupId, newBackupId, callback) { callback = once(callback); - var oldBackupFilePath = path.join(apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER, oldBackupId + '.tar.gz'); - var newBackupFilePath = path.join(apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER, newBackupId + '.tar.gz'); + var oldBackupFilePath = path.join(apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER, oldBackupId); + var newBackupFilePath = path.join(apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER, newBackupId); - // FIXME this most likely has a permissions issue as this process runs as yellowtent not root - mkdirp(path.dirname(newBackupFilePath), function (error) { - if (error) return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + async.series([ + shell.sudo.bind(null, 'copyBackup', [ COPY_BACKUP_CMD, oldBackupFilePath + '.tar.gz', newBackupFilePath + '.tar.gz' ]), + shell.sudo.bind(null, 'copyBackup', [ COPY_BACKUP_CMD, oldBackupFilePath + '.json', newBackupFilePath + '.json' ]) + ], function (error) { + if (error) { + console.error('Unable to copy backup %s -> %s.', oldBackupFilePath, newBackupFilePath, safe.error); + return callback(new BackupsError(BackupsError.INTERNAL_ERROR, error)); + } - var readStream = fs.createReadStream(oldBackupFilePath); - var writeStream = fs.createWriteStream(newBackupFilePath); - - readStream.on('error', callback); - writeStream.on('error', callback); - writeStream.on('close', callback); - - readStream.pipe(writeStream); + callback(); }); } @@ -222,7 +219,7 @@ function removeBackup(apiConfig, backupId, appBackupIds, callback) { async.each([backupId].concat(appBackupIds), function (id, callback) { var filePath = path.join(apiConfig.backupFolder || FALLBACK_BACKUP_FOLDER, id + '.tar.gz'); - shell.sudo('deleteBackup', [ RMBACKUP_CMD, filePath ], function (error) { + shell.sudo('deleteBackup', [ REMOVE_BACKUP_CMD, filePath ], function (error) { if (error) console.error('Unable to remove %s. Not fatal.', filePath, safe.error); callback(); }); diff --git a/src/test/checkInstall b/src/test/checkInstall index e3c745dcb..97c2a6923 100755 --- a/src/test/checkInstall +++ b/src/test/checkInstall @@ -15,6 +15,7 @@ scripts=("${SOURCE_DIR}/src/scripts/rmappdir.sh" \ "${SOURCE_DIR}/src/scripts/backupbox.sh" \ "${SOURCE_DIR}/src/scripts/backupapp.sh" \ "${SOURCE_DIR}/src/scripts/rmbackup.sh" \ + "${SOURCE_DIR}/src/scripts/cpbackup.sh" \ "${SOURCE_DIR}/src/scripts/restoreapp.sh" \ "${SOURCE_DIR}/src/scripts/reboot.sh" \ "${SOURCE_DIR}/src/scripts/update.sh" \