diff --git a/src/backups.js b/src/backups.js index f9bd53a03..05e0201a7 100644 --- a/src/backups.js +++ b/src/backups.js @@ -28,8 +28,8 @@ exports = module.exports = { _getBackupFilePath: getBackupFilePath, _createTarPackStream: createTarPackStream, _tarExtract: tarExtract, - _createEmptyDirs: createEmptyDirs, - _saveEmptyDirs: saveEmptyDirs + _restoreFsMetadata: restoreFsMetadata, + _saveFsMetadata: saveFsMetadata }; var addons = require('./addons.js'), @@ -265,15 +265,23 @@ function sync(backupConfig, backupId, dataDir, callback) { }); } -function saveEmptyDirs(appDataDir, callback) { +function saveFsMetadata(appDataDir, callback) { assert.strictEqual(typeof appDataDir, 'string'); assert.strictEqual(typeof callback, 'function'); - var emptyDirs = safe.child_process.execSync('find . -type d -empty', { cwd: `${appDataDir}` }); - + var emptyDirs = safe.child_process.execSync('find . -type d -empty', { cwd: `${appDataDir}`, encoding: 'utf8' }); if (emptyDirs === null) return callback(safe.error); - if (!safe.fs.writeFileSync(`${appDataDir}/emptydirs.txt`, emptyDirs)) return callback(safe.error); + var execFiles = safe.child_process.execSync('find . -type f -executable', { cwd: `${appDataDir}`, encoding: 'utf8' }); + if (execFiles === null) return callback(safe.error); + + var metadata = { + emptyDirs: emptyDirs.trim().split('\n'), + execFiles: execFiles.trim().split('\n') + }; + + if (!safe.fs.writeFileSync(`${appDataDir}/fsmetadata.json`, JSON.stringify(metadata, null, 4))) return callback(safe.error); + callback(); } @@ -300,7 +308,7 @@ function upload(backupId, format, dataDir, callback) { }, callback); } else { async.series([ - saveEmptyDirs.bind(null, dataDir), + saveFsMetadata.bind(null, dataDir), sync.bind(null, backupConfig, backupId, dataDir) ], callback); } @@ -355,21 +363,27 @@ function tarExtract(inStream, destination, key, callback) { } } -function createEmptyDirs(appDataDir, callback) { +function restoreFsMetadata(appDataDir, callback) { assert.strictEqual(typeof appDataDir, 'string'); assert.strictEqual(typeof callback, 'function'); log('Recreating empty directories'); - var emptyDirs = safe.fs.readFileSync(path.join(appDataDir, 'emptydirs.txt'), 'utf8'); - if (emptyDirs === null) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, 'emptydirs.txt was not found:' + safe.error.message)); + var metadata = safe.JSON.parse(safe.fs.readFileSync(path.join(appDataDir, 'fsmetadata.json'), 'utf8')); + if (metadata === null) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, 'Error loading fsmetadata.txt:' + safe.error.message)); - async.eachSeries(emptyDirs.trim().split('\n'), function createPath(emptyDir, iteratorDone) { + async.eachSeries(metadata.emptyDirs, function createPath(emptyDir, iteratorDone) { mkdirp(path.join(appDataDir, emptyDir), iteratorDone); }, function (error) { - if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, `unable to crate path: ${error.message}`)); + if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, `unable to create path: ${error.message}`)); - callback(); + async.eachSeries(metadata.execFiles, function createPath(execFile, iteratorDone) { + fs.chmod(path.join(appDataDir, execFile), parseInt('0755', 8), iteratorDone); + }, function (error) { + if (error) return callback(new BackupsError(BackupsError.EXTERNAL_ERROR, `unable to chmod: ${error.message}`)); + + callback(); + }); }); } @@ -398,7 +412,7 @@ function download(backupId, format, dataDir, callback) { events.on('done', function (error) { if (error) return callback(error); - createEmptyDirs(dataDir, callback); + restoreFsMetadata(dataDir, callback); }); } }); diff --git a/src/routes/test/backups-test.js b/src/routes/test/backups-test.js index 67d1de8a3..ef89a47b7 100644 --- a/src/routes/test/backups-test.js +++ b/src/routes/test/backups-test.js @@ -10,12 +10,10 @@ var appdb = require('../../appdb.js'), config = require('../../config.js'), database = require('../../database.js'), expect = require('expect.js'), - http = require('http'), nock = require('nock'), superagent = require('superagent'), server = require('../../server.js'), - settings = require('../../settings.js'), - url = require('url'); + settings = require('../../settings.js'); var SERVER_URL = 'http://localhost:' + config.get('port'); diff --git a/src/test/backups-test.js b/src/test/backups-test.js index ee9a53994..16e1f47b8 100644 --- a/src/test/backups-test.js +++ b/src/test/backups-test.js @@ -244,7 +244,7 @@ describe('backups', function () { }); }); - describe('empty dirs', function () { + describe('fs meta data', function () { var tmpdir; before(function () { tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'backups-test')); @@ -253,28 +253,34 @@ describe('backups', function () { rimraf.sync(tmpdir); }); - it('saves empty dirs file', function (done) { + it('saves special files', function (done) { createTree(tmpdir, { 'data': { 'subdir': { 'emptydir': { } } }, 'dir2': { 'file': 'stuff' } }); + fs.chmodSync(path.join(tmpdir, 'dir2/file'), parseInt('0755', 8)); - backups._saveEmptyDirs(tmpdir, function (error) { + backups._saveFsMetadata(tmpdir, function (error) { expect(error).to.not.be.ok(); - var emptyDirs = fs.readFileSync(path.join(tmpdir, 'emptydirs.txt'), 'utf8').trim().split('\n'); + var emptyDirs = JSON.parse(fs.readFileSync(path.join(tmpdir, 'fsmetadata.json'), 'utf8')).emptyDirs; expect(emptyDirs).to.eql(['./data/subdir/emptydir']); + var execFiles = JSON.parse(fs.readFileSync(path.join(tmpdir, 'fsmetadata.json'), 'utf8')).execFiles; + expect(execFiles).to.eql(['./dir2/file']); + done(); }); }); - it('creates empty dirs file', function (done) { + it('restores special files', function (done) { rimraf.sync(path.join(tmpdir, 'data')); expect(fs.existsSync(path.join(tmpdir, 'data/subdir/emptydir'))).to.be(false); // just make sure rimraf worked - backups._createEmptyDirs(tmpdir, function (error) { + backups._restoreFsMetadata(tmpdir, function (error) { expect(error).to.not.be.ok(); expect(fs.existsSync(path.join(tmpdir, 'data/subdir/emptydir'))).to.be(true); + var mode = fs.statSync(path.join(tmpdir, 'dir2/file')).mode; + expect(mode & ~fs.constants.S_IFREG).to.be(parseInt('0755', 8)); done(); });