135 lines
5.2 KiB
JavaScript
135 lines
5.2 KiB
JavaScript
'use strict';
|
|
|
|
exports = module.exports = {
|
|
upload: upload,
|
|
download: download,
|
|
|
|
copy: copy,
|
|
|
|
listDir: listDir,
|
|
|
|
remove: remove,
|
|
removeDir: removeDir,
|
|
|
|
testConfig: testConfig,
|
|
removePrivateFields: removePrivateFields,
|
|
injectPrivateFields: injectPrivateFields
|
|
};
|
|
|
|
var assert = require('assert'),
|
|
BoxError = require('../boxerror.js'),
|
|
debug = require('debug')('box:storage/sshfs'),
|
|
filesystem = require('./filesystem.js'),
|
|
mkdirp = require('mkdirp'),
|
|
path = require('path'),
|
|
safe = require('safetydance'),
|
|
shell = require('../shell.js');
|
|
|
|
// we mostly reuse the filesystem.js implementation but the config params are different
|
|
function translateConfig(apiConfig) {
|
|
assert.strictEqual(typeof apiConfig, 'object');
|
|
|
|
// skip chown
|
|
apiConfig._doNotChown = true;
|
|
|
|
// do not preserver file attributes
|
|
apiConfig._doNotPreserveAttributes = true;
|
|
|
|
// resolve the actual path
|
|
apiConfig.backupFolder = path.join(apiConfig.mountPoint, apiConfig.prefix);
|
|
|
|
return apiConfig;
|
|
}
|
|
|
|
// storage api
|
|
function upload(apiConfig, backupFilePath, sourceStream, callback) {
|
|
assert.strictEqual(typeof apiConfig, 'object');
|
|
assert.strictEqual(typeof backupFilePath, 'string');
|
|
assert.strictEqual(typeof sourceStream, 'object');
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
filesystem.upload(translateConfig(apiConfig), backupFilePath, sourceStream, callback);
|
|
}
|
|
|
|
function download(apiConfig, sourceFilePath, callback) {
|
|
assert.strictEqual(typeof apiConfig, 'object');
|
|
assert.strictEqual(typeof sourceFilePath, 'string');
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
filesystem.download(translateConfig(apiConfig), sourceFilePath, callback);
|
|
}
|
|
|
|
function listDir(apiConfig, dir, batchSize, iteratorCallback, callback) {
|
|
assert.strictEqual(typeof apiConfig, 'object');
|
|
assert.strictEqual(typeof dir, 'string');
|
|
assert.strictEqual(typeof batchSize, 'number');
|
|
assert.strictEqual(typeof iteratorCallback, 'function');
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
filesystem.listDir(translateConfig(apiConfig), dir, batchSize, iteratorCallback, callback);
|
|
}
|
|
|
|
function copy(apiConfig, oldFilePath, newFilePath) {
|
|
assert.strictEqual(typeof apiConfig, 'object');
|
|
assert.strictEqual(typeof oldFilePath, 'string');
|
|
assert.strictEqual(typeof newFilePath, 'string');
|
|
|
|
return filesystem.copy(translateConfig(apiConfig), oldFilePath, newFilePath);
|
|
}
|
|
|
|
function remove(apiConfig, filename, callback) {
|
|
assert.strictEqual(typeof apiConfig, 'object');
|
|
assert.strictEqual(typeof filename, 'string');
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
filename.remove(translateConfig(apiConfig), filename, callback);
|
|
}
|
|
|
|
function removeDir(apiConfig, pathPrefix) {
|
|
assert.strictEqual(typeof apiConfig, 'object');
|
|
assert.strictEqual(typeof pathPrefix, 'string');
|
|
|
|
return filesystem.removeDir(translateConfig(apiConfig), pathPrefix);
|
|
}
|
|
|
|
function testConfig(apiConfig, callback) {
|
|
assert.strictEqual(typeof apiConfig, 'object');
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
if (!apiConfig.mountPoint || typeof apiConfig.mountPoint !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'mountPoint must be non-empty string', { field: 'mountPoint' }));
|
|
|
|
if ('noHardlinks' in apiConfig && typeof apiConfig.noHardlinks !== 'boolean') return callback(new BoxError(BoxError.BAD_FIELD, 'noHardlinks must be boolean', { field: 'noHardLinks' }));
|
|
if (typeof apiConfig.prefix !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'prefix must be a string', { field: 'prefix' }));
|
|
|
|
// TODO check fstab entry
|
|
// TODO check mountpoint
|
|
|
|
const backupPath = path.join(apiConfig.mountPoint, apiConfig.prefix);
|
|
|
|
const stat = safe.fs.statSync(backupPath);
|
|
if (!stat) return callback(new BoxError(BoxError.BAD_FIELD, 'Directory does not exist or cannot be accessed: ' + safe.error.message), { field: 'prefix' });
|
|
if (!stat.isDirectory()) return callback(new BoxError(BoxError.BAD_FIELD, 'Backup location is not a directory', { field: 'prefix' }));
|
|
|
|
if (!safe.fs.mkdirSync(path.join(backupPath, 'snapshot')) && safe.error.code !== 'EEXIST') {
|
|
if (safe.error && safe.error.code === 'EACCES') return callback(new BoxError(BoxError.BAD_FIELD, `Access denied. Run "chown yellowtent:yellowtent ${backupPath}" on the server`, { field: 'prefix' }));
|
|
return callback(new BoxError(BoxError.BAD_FIELD, safe.error.message, { field: 'prefix' }));
|
|
}
|
|
|
|
if (!safe.fs.writeFileSync(path.join(backupPath, 'cloudron-testfile'), 'testcontent')) {
|
|
return callback(new BoxError(BoxError.BAD_FIELD, `Unable to create test file as 'yellowtent' user in ${backupPath}: ${safe.error.message}. Check dir/mount permissions`, { field: 'prefix' }));
|
|
}
|
|
|
|
if (!safe.fs.unlinkSync(path.join(backupPath, 'cloudron-testfile'))) {
|
|
return callback(new BoxError(BoxError.BAD_FIELD, `Unable to remove test file as 'yellowtent' user in ${backupPath}: ${safe.error.message}. Check dir/mount permissions`, { field: 'prefix' }));
|
|
}
|
|
|
|
callback(null);
|
|
}
|
|
|
|
function removePrivateFields(apiConfig) {
|
|
return apiConfig;
|
|
}
|
|
|
|
function injectPrivateFields(/* newConfig, currentConfig */) {
|
|
}
|