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