diff --git a/src/mounts.js b/src/mounts.js index ab05ea73d..081270d57 100644 --- a/src/mounts.js +++ b/src/mounts.js @@ -16,6 +16,7 @@ exports = module.exports = { MOUNT_TYPE_EXT4: 'ext4', MOUNT_TYPE_XFS: 'xfs', MOUNT_TYPE_DISK: 'disk', + MOUNT_TYPE_LOOPBACK: 'loopback' }; const assert = require('assert'), @@ -65,6 +66,7 @@ function validateMountOptions(type, options) { case exports.MOUNT_TYPE_EXT4: case exports.MOUNT_TYPE_XFS: case exports.MOUNT_TYPE_DISK: + case exports.MOUNT_TYPE_LOOPBACK: if (typeof options.diskPath !== 'string') return new BoxError(BoxError.BAD_FIELD, 'diskPath is not a string'); return null; default: @@ -81,6 +83,7 @@ function isManagedProvider(provider) { case exports.MOUNT_TYPE_EXT4: case exports.MOUNT_TYPE_XFS: case exports.MOUNT_TYPE_DISK: + case exports.MOUNT_TYPE_LOOPBACK: return true; default: return false; @@ -96,7 +99,7 @@ async function renderMountFile(mount) { const { name, hostPath, mountType, mountOptions } = mount; - let options, what, type; + let options, what, type, dependsOn; switch (mountType) { case exports.MOUNT_TYPE_CIFS: { const out = await shell.spawn('systemd-escape', [ '-p', hostPath ], { encoding: 'utf8' }); // this ensures uniqueness of creds file @@ -106,12 +109,14 @@ async function renderMountFile(mount) { type = 'cifs'; what = `//${mountOptions.host}` + path.join('/', mountOptions.remoteDir); options = `credentials=${credentialsFilePath},rw,${mountOptions.seal ? 'seal,' : ''}iocharset=utf8,file_mode=0666,dir_mode=0777,uid=yellowtent,gid=yellowtent`; + dependsOn = 'network-online.target'; break; } case exports.MOUNT_TYPE_NFS: type = 'nfs'; what = `${mountOptions.host}:${mountOptions.remoteDir}`; options = 'noauto'; // noauto means it is not a blocker for local-fs.target. _netdev is implicit. rw,hard,tcp,rsize=8192,wsize=8192,timeo=14 + dependsOn = 'network-online.target'; break; case exports.MOUNT_TYPE_EXT4: type = 'ext4'; @@ -135,14 +140,21 @@ async function renderMountFile(mount) { type = 'fuse.sshfs'; 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 + dependsOn = 'network-online.target'; break; } + case exports.MOUNT_TYPE_LOOPBACK: + type = 'ext4'; + what = mountOptions.diskPath; + options = 'loop'; + dependsOn = mountOptions.dependsOn; + break; case exports.MOUNT_TYPE_FILESYSTEM: case exports.MOUNT_TYPE_MOUNTPOINT: return; } - return ejs.render(SYSTEMD_MOUNT_EJS, { name, what, where: hostPath, options, type }); + return ejs.render(SYSTEMD_MOUNT_EJS, { name, what, where: hostPath, options, type, dependsOn }); } async function removeMount(mount) { diff --git a/src/systemd-mount.ejs b/src/systemd-mount.ejs index 1798414e1..08432456d 100644 --- a/src/systemd-mount.ejs +++ b/src/systemd-mount.ejs @@ -1,9 +1,12 @@ [Unit] Description=<%= name %> <% if ( type === 'cifs' || type === 'nfs' || type === 'fuse.sshfs' ) { %> -Requires=network-online.target -After=network-online.target +Requires=<%= dependsOn %> +After=<%= dependsOn %> Before=docker.service +<% } else if ( type === 'loopback' ) { %> +Requires=<%= dependsOn %> +After=<%= dependsOn %> <% } %> [Mount]