Merge sshfs.js into filesystem.js
This commit is contained in:
@@ -19,6 +19,10 @@ exports = module.exports = {
|
||||
injectPrivateFields: injectPrivateFields
|
||||
};
|
||||
|
||||
const PROVIDER_FILESYSTEM = 'filesystem';
|
||||
const PROVIDER_SSHFS = 'sshfs';
|
||||
const PROVIDER_CIFS = 'cifs';
|
||||
|
||||
var assert = require('assert'),
|
||||
BoxError = require('../boxerror.js'),
|
||||
DataLayout = require('../datalayout.js'),
|
||||
@@ -37,6 +41,9 @@ var assert = require('assert'),
|
||||
function getBackupPath(apiConfig) {
|
||||
assert.strictEqual(typeof apiConfig, 'object');
|
||||
|
||||
if (apiConfig.provider === PROVIDER_SSHFS) return path.join(apiConfig.mountPoint, apiConfig.prefix);
|
||||
if (apiConfig.provider === PROVIDER_CIFS) return path.join(apiConfig.mountPoint, apiConfig.prefix);
|
||||
|
||||
return apiConfig.backupFolder;
|
||||
}
|
||||
|
||||
@@ -46,6 +53,8 @@ function checkPreconditions(apiConfig, dataLayout, callback) {
|
||||
assert(dataLayout instanceof DataLayout, 'dataLayout must be a DataLayout');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
// TODO check filesystem is mounted for sshfs and cifs so we don't write into the actual folder on disk
|
||||
|
||||
let used = 0;
|
||||
for (let localPath of dataLayout.localPaths()) {
|
||||
debug(`checkPreconditions: getting disk usage of ${localPath}`);
|
||||
@@ -56,7 +65,7 @@ function checkPreconditions(apiConfig, dataLayout, callback) {
|
||||
|
||||
debug(`checkPreconditions: ${used} bytes`);
|
||||
|
||||
df.file(apiConfig.backupFolder).then(function (diskUsage) {
|
||||
df.file(getBackupPath(apiConfig)).then(function (diskUsage) {
|
||||
const needed = used + (1024 * 1024 * 1024); // check if there is atleast 1GB left afterwards
|
||||
if (diskUsage.available <= needed) return callback(new BoxError(BoxError.FS_ERROR, `Not enough disk space for backup. Needed: ${prettyBytes(needed)} Available: ${prettyBytes(diskUsage.available)}`));
|
||||
|
||||
@@ -93,11 +102,9 @@ function upload(apiConfig, backupFilePath, sourceStream, callback) {
|
||||
// in test, upload() may or may not be called via sudo script
|
||||
const BACKUP_UID = parseInt(process.env.SUDO_UID, 10) || process.getuid();
|
||||
|
||||
// see sshfs backend
|
||||
if (!apiConfig._doNotChown) {
|
||||
if (!safe.fs.chownSync(backupFilePath, BACKUP_UID, BACKUP_UID)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to chown:' + safe.error.message));
|
||||
if (!safe.fs.chownSync(path.dirname(backupFilePath), BACKUP_UID, BACKUP_UID)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to chown:' + safe.error.message));
|
||||
}
|
||||
// sshfs and cifs handle ownership through the mount args
|
||||
if (apiConfig.provider === PROVIDER_FILESYSTEM && !safe.fs.chownSync(backupFilePath, BACKUP_UID, BACKUP_UID)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to chown:' + safe.error.message));
|
||||
if (apiConfig.provider === PROVIDER_FILESYSTEM && !safe.fs.chownSync(path.dirname(backupFilePath), BACKUP_UID, BACKUP_UID)) return callback(new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to chown:' + safe.error.message));
|
||||
|
||||
debug('upload %s: done.', backupFilePath);
|
||||
|
||||
@@ -161,8 +168,8 @@ function copy(apiConfig, oldFilePath, newFilePath) {
|
||||
|
||||
events.emit('progress', `Copying ${oldFilePath} to ${newFilePath}`);
|
||||
|
||||
// see sshfs backend
|
||||
var cpOptions = apiConfig._doNotPreserveAttributes ? '-dR' : '-a';
|
||||
// sshfs and cifs do not allow preserving attributes
|
||||
var cpOptions = apiConfig.provider === PROVIDER_FILESYSTEM ? '-a' : '-dR';
|
||||
|
||||
// this will hardlink backups saving space
|
||||
cpOptions += apiConfig.noHardlinks ? '' : 'l';
|
||||
@@ -215,31 +222,42 @@ function testConfig(apiConfig, callback) {
|
||||
assert.strictEqual(typeof apiConfig, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
if (typeof apiConfig.backupFolder !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'backupFolder must be string', { field: 'backupFolder' }));
|
||||
if (apiConfig.provider === PROVIDER_FILESYSTEM) {
|
||||
if (!apiConfig.backupFolder || typeof apiConfig.backupFolder !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'backupFolder must be non-empty string', { field: 'backupFolder' }));
|
||||
if ('externalDisk' in apiConfig && typeof apiConfig.externalDisk !== 'boolean') return callback(new BoxError(BoxError.BAD_FIELD, 'externalDisk must be boolean', { field: 'externalDisk' }));
|
||||
}
|
||||
|
||||
if (!apiConfig.backupFolder) return callback(new BoxError(BoxError.BAD_FIELD, 'backupFolder is required', { field: 'backupFolder' }));
|
||||
if (apiConfig.provider === PROVIDER_SSHFS || apiConfig.provider === PROVIDER_CIFS) {
|
||||
if (!apiConfig.mountPoint || typeof apiConfig.mountPoint !== 'string') return callback(new BoxError(BoxError.BAD_FIELD, 'mountPoint must be non-empty string', { field: 'mountPoint' }));
|
||||
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
|
||||
}
|
||||
|
||||
// common checks
|
||||
const backupPath = getBackupPath(apiConfig);
|
||||
const field = apiConfig.provider === PROVIDER_FILESYSTEM ? 'backupFolder' : '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 });
|
||||
if (!stat.isDirectory()) return callback(new BoxError(BoxError.BAD_FIELD, 'Backup location is not a directory', { field }));
|
||||
|
||||
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 }));
|
||||
return callback(new BoxError(BoxError.BAD_FIELD, safe.error.message, { field }));
|
||||
}
|
||||
|
||||
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 }));
|
||||
}
|
||||
|
||||
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 }));
|
||||
}
|
||||
|
||||
if ('noHardlinks' in apiConfig && typeof apiConfig.noHardlinks !== 'boolean') return callback(new BoxError(BoxError.BAD_FIELD, 'noHardlinks must be boolean', { field: 'noHardLinks' }));
|
||||
|
||||
if ('externalDisk' in apiConfig && typeof apiConfig.externalDisk !== 'boolean') return callback(new BoxError(BoxError.BAD_FIELD, 'externalDisk must be boolean', { field: 'externalDisk' }));
|
||||
|
||||
const stat = safe.fs.statSync(apiConfig.backupFolder);
|
||||
if (!stat) return callback(new BoxError(BoxError.BAD_FIELD, 'Directory does not exist or cannot be accessed: ' + safe.error.message), { field: 'backupFolder' });
|
||||
if (!stat.isDirectory()) return callback(new BoxError(BoxError.BAD_FIELD, 'Backup location is not a directory', { field: 'backupFolder' }));
|
||||
|
||||
if (!safe.fs.mkdirSync(path.join(apiConfig.backupFolder, '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 ${apiConfig.backupFolder}" on the server`, { field: 'backupFolder' }));
|
||||
return callback(new BoxError(BoxError.BAD_FIELD, safe.error.message, { field: 'backupFolder' }));
|
||||
}
|
||||
|
||||
if (!safe.fs.writeFileSync(path.join(apiConfig.backupFolder, 'cloudron-testfile'), 'testcontent')) {
|
||||
return callback(new BoxError(BoxError.BAD_FIELD, `Unable to create test file as 'yellowtent' user in ${apiConfig.backupFolder}: ${safe.error.message}. Check dir/mount permissions`, { field: 'backupFolder' }));
|
||||
}
|
||||
|
||||
if (!safe.fs.unlinkSync(path.join(apiConfig.backupFolder, 'cloudron-testfile'))) {
|
||||
return callback(new BoxError(BoxError.BAD_FIELD, `Unable to remove test file as 'yellowtent' user in ${apiConfig.backupFolder}: ${safe.error.message}. Check dir/mount permissions`, { field: 'backupFolder' }));
|
||||
}
|
||||
|
||||
callback(null);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user