feat(fs/mount): keep open file on mount to avoid external umount (#3998)
This commit is contained in:
parent
6aa8e0d4ce
commit
c728eeaffa
@ -24,6 +24,7 @@
|
||||
"@marsaud/smb2": "^0.13.0",
|
||||
"@sindresorhus/df": "^2.1.0",
|
||||
"@xen-orchestra/async-map": "^0.0.0",
|
||||
"decorator-synchronized": "^0.3.0",
|
||||
"execa": "^1.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"get-stream": "^4.0.0",
|
||||
|
@ -1,5 +1,6 @@
|
||||
import execa from 'execa'
|
||||
import fs from 'fs-extra'
|
||||
import { ignoreErrors } from 'promise-toolbox'
|
||||
import { join } from 'path'
|
||||
import { tmpdir } from 'os'
|
||||
|
||||
@ -21,11 +22,12 @@ export default class MountHandler extends LocalHandler {
|
||||
super(remote, opts)
|
||||
|
||||
this._execa = useSudo ? sudoExeca : execa
|
||||
this._keeper = undefined
|
||||
this._params = {
|
||||
...params,
|
||||
options: [params.options, remote.options].filter(
|
||||
_ => _ !== undefined
|
||||
).join(','),
|
||||
options: [params.options, remote.options]
|
||||
.filter(_ => _ !== undefined)
|
||||
.join(','),
|
||||
}
|
||||
this._realPath = join(
|
||||
mountsDir,
|
||||
@ -37,19 +39,20 @@ export default class MountHandler extends LocalHandler {
|
||||
}
|
||||
|
||||
async _forget() {
|
||||
await this._execa('umount', ['--force', this._getRealPath()], {
|
||||
const keeper = this._keeper
|
||||
if (keeper === undefined) {
|
||||
return
|
||||
}
|
||||
this._keeper = undefined
|
||||
await fs.close(keeper)
|
||||
|
||||
await ignoreErrors.call(
|
||||
this._execa('umount', [this._getRealPath()], {
|
||||
env: {
|
||||
LANG: 'C',
|
||||
},
|
||||
}).catch(error => {
|
||||
if (
|
||||
error == null ||
|
||||
typeof error.stderr !== 'string' ||
|
||||
!error.stderr.includes('not mounted')
|
||||
) {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
_getRealPath() {
|
||||
@ -57,18 +60,32 @@ export default class MountHandler extends LocalHandler {
|
||||
}
|
||||
|
||||
async _sync() {
|
||||
await fs.ensureDir(this._getRealPath())
|
||||
// in case of multiple `sync`s, ensure we properly close previous keeper
|
||||
{
|
||||
const keeper = this._keeper
|
||||
if (keeper !== undefined) {
|
||||
this._keeper = undefined
|
||||
ignoreErrors.call(fs.close(keeper))
|
||||
}
|
||||
}
|
||||
|
||||
const realPath = this._getRealPath()
|
||||
|
||||
await fs.ensureDir(realPath)
|
||||
|
||||
try {
|
||||
const { type, device, options, env } = this._params
|
||||
return this._execa(
|
||||
await this._execa(
|
||||
'mount',
|
||||
['-t', type, device, this._getRealPath(), '-o', options],
|
||||
['-t', type, device, realPath, '-o', options],
|
||||
{
|
||||
env: {
|
||||
LANG: 'C',
|
||||
...env,
|
||||
},
|
||||
}
|
||||
).catch(error => {
|
||||
)
|
||||
} catch (error) {
|
||||
let stderr
|
||||
if (
|
||||
error == null ||
|
||||
@ -77,6 +94,14 @@ export default class MountHandler extends LocalHandler {
|
||||
) {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// keep an open file on the mount to prevent it from being unmounted if used
|
||||
// by another handler/process
|
||||
const keeperPath = `${realPath}/.keeper_${Math.random()
|
||||
.toString(36)
|
||||
.slice(2)}`
|
||||
this._keeper = await fs.open(keeperPath, 'w')
|
||||
ignoreErrors.call(fs.unlink(keeperPath))
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import getStream from 'get-stream'
|
||||
|
||||
import asyncMap from '@xen-orchestra/async-map'
|
||||
import path from 'path'
|
||||
import synchronized from 'decorator-synchronized'
|
||||
import { fromCallback, fromEvent, ignoreErrors, timeout } from 'promise-toolbox'
|
||||
import { parse } from 'xo-remote-parser'
|
||||
import { randomBytes } from 'crypto'
|
||||
@ -216,6 +217,7 @@ export default class RemoteHandlerAbstract {
|
||||
// FIXME: Some handlers are implemented based on system-wide mecanisms (such
|
||||
// as mount), forgetting them might breaking other processes using the same
|
||||
// remote.
|
||||
@synchronized()
|
||||
async forget(): Promise<void> {
|
||||
await this._forget()
|
||||
}
|
||||
@ -354,6 +356,7 @@ export default class RemoteHandlerAbstract {
|
||||
// metadata
|
||||
//
|
||||
// This method MUST ALWAYS be called before using the handler.
|
||||
@synchronized()
|
||||
async sync(): Promise<void> {
|
||||
await this._sync()
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
### Released packages
|
||||
|
||||
- @xen-orchestra/fs v0.7.0
|
||||
- xen-api v0.24.3
|
||||
- xoa-updater v0.15.2
|
||||
- xo-server v5.36.0
|
||||
|
Loading…
Reference in New Issue
Block a user