diff --git a/CHANGES b/CHANGES index b49530aab..f5aa87cc2 100644 --- a/CHANGES +++ b/CHANGES @@ -2288,4 +2288,4 @@ [6.3.2] * Avatar was migrated as base64 instead of binary - +* Fix issue where filemanager came up empty for CIFS mounts diff --git a/src/mounts.js b/src/mounts.js index 45a98fe31..3dd5343f3 100644 --- a/src/mounts.js +++ b/src/mounts.js @@ -84,7 +84,7 @@ function renderMountFile(volume) { case 'sshfs': { const keyFilePath = path.join(paths.SSHFS_KEYS_DIR, `id_rsa_${mountOptions.host}`); type = 'fuse.sshfs'; - what= `${mountOptions.user}@${mountOptions.host}:${mountOptions.remoteDir}`; + what = `${mountOptions.user}@${mountOptions.host}:${mountOptions.remoteDir}`; options = `allow_other,port=${mountOptions.port},IdentityFile=${keyFilePath},StrictHostKeyChecking=no,reconnect`; // allow_other means non-root users can access it break; } diff --git a/src/paths.js b/src/paths.js index d6074fd17..f3432d1d9 100644 --- a/src/paths.js +++ b/src/paths.js @@ -21,6 +21,8 @@ exports = module.exports = { PROVIDER_FILE: '/etc/cloudron/PROVIDER', SETUP_TOKEN_FILE: '/etc/cloudron/SETUP_TOKEN', + VOLUMES_MOUNT_DIR: '/mnt/volumes', + PLATFORM_DATA_DIR: path.join(baseDir(), 'platformdata'), APPS_DATA_DIR: path.join(baseDir(), 'appsdata'), BOX_DATA_DIR: path.join(baseDir(), 'boxdata'), // box data dir is part of box backup diff --git a/src/routes/volumes.js b/src/routes/volumes.js index c5cd7985e..5664a300d 100644 --- a/src/routes/volumes.js +++ b/src/routes/volumes.js @@ -31,10 +31,11 @@ async function add(req, res, next) { assert.strictEqual(typeof req.body, 'object'); if (typeof req.body.name !== 'string') return next(new HttpError(400, 'name must be a string')); - if (typeof req.body.hostPath !== 'string') return next(new HttpError(400, 'hostPath must be a string')); if (typeof req.body.mountType !== 'string') return next(new HttpError(400, 'mountType must be a string')); if (typeof req.body.mountOptions !== 'object') return next(new HttpError(400, 'mountOptions must be a object')); + if ('hostPath' in req.body && typeof req.body.hostPath !== 'string') return next(new HttpError(400, 'hostPath must be a string')); + req.clearTimeout(); // waiting for mount can take time const [error, id] = await safe(volumes.add(req.body, auditSource.fromRequest(req))); diff --git a/src/scripts/rmmount.sh b/src/scripts/rmmount.sh index 23c9de8ed..b26c959f8 100755 --- a/src/scripts/rmmount.sh +++ b/src/scripts/rmmount.sh @@ -23,4 +23,5 @@ mount_filename=$(systemd-escape -p --suffix=mount "$where") systemctl stop "${mount_filename}" || true rm -f "/etc/systemd/system/${mount_filename}" +rmdir "${where}" || true systemctl daemon-reload diff --git a/src/volumes.js b/src/volumes.js index ed581c0d3..282fe9868 100644 --- a/src/volumes.js +++ b/src/volumes.js @@ -20,6 +20,7 @@ const assert = require('assert'), fs = require('fs'), mounts = require('./mounts.js'), path = require('path'), + paths = require('./paths.js'), safe = require('safetydance'), services = require('./services.js'), uuid = require('uuid'); @@ -73,23 +74,27 @@ async function add(volume, auditSource) { assert.strictEqual(typeof volume, 'object'); assert.strictEqual(typeof auditSource, 'object'); - const {name, hostPath, mountType, mountOptions} = volume; + const {name, mountType, mountOptions} = volume; let error = validateName(name); if (error) throw error; - error = validateHostPath(hostPath, mountType); - if (error) throw error; - error = mounts.validateMountOptions(mountType, mountOptions); if (error) throw error; - const id = uuid.v4(); + const id = uuid.v4().replace(/-/g, ''); // to make systemd mount file names more readable + + if (mountType === 'noop') { + error = validateHostPath(volume.hostPath, mountType); + if (error) throw error; + } else { + volume.hostPath = path.join(paths.VOLUMES_MOUNT_DIR, id); + } if (volume.mountType !== 'noop') await mounts.tryAddMount(volume, { timeout: 10 }); // 10 seconds try { - await database.query('INSERT INTO volumes (id, name, hostPath, mountType, mountOptionsJson) VALUES (?, ?, ?, ?, ?)', [ id, name, hostPath, mountType, JSON.stringify(mountOptions) ]); + await database.query('INSERT INTO volumes (id, name, hostPath, mountType, mountOptionsJson) VALUES (?, ?, ?, ?, ?)', [ id, name, volume.hostPath, mountType, JSON.stringify(mountOptions) ]); } catch (error) { if (error.code === 'ER_DUP_ENTRY' && error.sqlMessage.indexOf('name') !== -1) throw new BoxError(BoxError.ALREADY_EXISTS, 'name already exists'); if (error.code === 'ER_DUP_ENTRY' && error.sqlMessage.indexOf('hostPath') !== -1) throw new BoxError(BoxError.ALREADY_EXISTS, 'hostPath already exists'); @@ -97,10 +102,11 @@ async function add(volume, auditSource) { throw error; } - eventlog.add(eventlog.ACTION_VOLUME_ADD, auditSource, { id, name, hostPath }); - if (mountType === 'noop') services.rebuildService('sftp', NOOP_CALLBACK); + eventlog.add(eventlog.ACTION_VOLUME_ADD, auditSource, { id, name, hostPath: volume.hostPath }); + // in theory, we only need to do this noop volumes. but for some reason a restart is required to detect new "mounts" + services.rebuildService('sftp', NOOP_CALLBACK); - const collectdConf = ejs.render(COLLECTD_CONFIG_EJS, { volumeId: id, hostPath }); + const collectdConf = ejs.render(COLLECTD_CONFIG_EJS, { volumeId: id, hostPath: volume.hostPath }); collectd.addProfile(id, collectdConf, NOOP_CALLBACK); return id;