mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-12 05:55:38 +00:00
Merge pull request #1984 from balena-os/lockdir
Allow directories to be used as lockfiles
This commit is contained in:
commit
175d14258b
@ -1,8 +1,8 @@
|
|||||||
import * as fs from 'fs';
|
import { promises as fs, unlinkSync, rmdirSync } from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { dirname } from 'path';
|
import { dirname } from 'path';
|
||||||
|
|
||||||
import { exec, unlinkAll } from './fs-utils';
|
import { exec } from './fs-utils';
|
||||||
|
|
||||||
// Equivalent to `drwxrwxrwt`
|
// Equivalent to `drwxrwxrwt`
|
||||||
const STICKY_WRITE_PERMISSIONS = 0o1777;
|
const STICKY_WRITE_PERMISSIONS = 0o1777;
|
||||||
@ -66,7 +66,7 @@ export async function lock(path: string, uid: number = os.userInfo().uid) {
|
|||||||
* `chmod` does not fail or throw if the directory already has the proper permissions.
|
* `chmod` does not fail or throw if the directory already has the proper permissions.
|
||||||
*/
|
*/
|
||||||
if (uid !== 0) {
|
if (uid !== 0) {
|
||||||
await fs.promises.chmod(dirname(path), STICKY_WRITE_PERMISSIONS);
|
await fs.chmod(dirname(path), STICKY_WRITE_PERMISSIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,11 +108,28 @@ export async function lock(path: string, uid: number = os.userInfo().uid) {
|
|||||||
|
|
||||||
export async function unlock(path: string): Promise<void> {
|
export async function unlock(path: string): Promise<void> {
|
||||||
// Removing the lockfile releases the lock
|
// Removing the lockfile releases the lock
|
||||||
await unlinkAll(path);
|
await fs.unlink(path).catch((e) => {
|
||||||
|
// if the error is EPERM, the file is a directory
|
||||||
|
if (e.code === 'EPERM') {
|
||||||
|
return fs.rmdir(path).catch(() => {
|
||||||
|
// if the directory is not empty or something else
|
||||||
|
// happens, ignore
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// If the file does not exist or some other error
|
||||||
|
// happens, then ignore the error
|
||||||
|
});
|
||||||
// Remove lockfile's in-memory tracking of a file
|
// Remove lockfile's in-memory tracking of a file
|
||||||
delete locksTaken[path];
|
delete locksTaken[path];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unlockSync(path: string) {
|
export function unlockSync(path: string) {
|
||||||
return fs.unlinkSync(path);
|
try {
|
||||||
|
return unlinkSync(path);
|
||||||
|
} catch (e) {
|
||||||
|
if (e.code === 'EPERM') {
|
||||||
|
return rmdirSync(path);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ describe('lib/lockfile', () => {
|
|||||||
},
|
},
|
||||||
'7654321': {
|
'7654321': {
|
||||||
two: opts.createLock
|
two: opts.createLock
|
||||||
? { 'updates.lock': mock.file({ uid: LOCKFILE_UID }) }
|
? { 'updates.lock': mock.directory({ uid: LOCKFILE_UID }) }
|
||||||
: {},
|
: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -127,9 +127,11 @@ describe('lib/lockfile', () => {
|
|||||||
await checkLockDirFiles(lockPath, { shouldExist: true });
|
await checkLockDirFiles(lockPath, { shouldExist: true });
|
||||||
|
|
||||||
await lockfile.unlock(lockPath);
|
await lockfile.unlock(lockPath);
|
||||||
|
await lockfile.unlock(lockPath2);
|
||||||
|
|
||||||
// Verify lockfile removal
|
// Verify lockfile removal
|
||||||
await checkLockDirFiles(lockPath, { shouldExist: false });
|
await checkLockDirFiles(lockPath, { shouldExist: false });
|
||||||
|
await checkLockDirFiles(lockPath2, { shouldExist: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not error on async unlock if lockfile does not exist', async () => {
|
it('should not error on async unlock if lockfile does not exist', async () => {
|
||||||
@ -149,11 +151,15 @@ describe('lib/lockfile', () => {
|
|||||||
mockDir({ createLock: true });
|
mockDir({ createLock: true });
|
||||||
|
|
||||||
lockfile.unlockSync(lockPath);
|
lockfile.unlockSync(lockPath);
|
||||||
|
lockfile.unlockSync(lockPath2);
|
||||||
|
|
||||||
// Verify lockfile does not exist
|
// Verify lockfile does not exist
|
||||||
return checkLockDirFiles(lockPath, { shouldExist: false }).catch((err) => {
|
return Promise.all([
|
||||||
|
checkLockDirFiles(lockPath, { shouldExist: false }).catch((err) => {
|
||||||
expect.fail((err as Error)?.message ?? err);
|
expect.fail((err as Error)?.message ?? err);
|
||||||
});
|
}),
|
||||||
|
checkLockDirFiles(lockPath2, { shouldExist: false }),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should try to clean up existing locks on process exit', async () => {
|
it('should try to clean up existing locks on process exit', async () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user