mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
fix(infra): tolerate concurrent tmp dir repair
This commit is contained in:
@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Telegram: use durable message edits for streaming previews instead of native draft state, so generated replies no longer flicker through draft-to-message transitions that look like duplicates. (#75073) Thanks @obviyus.
|
||||
- Telegram: echo preflighted DM voice-note transcripts back to the originating chat, including Telegram DM topic thread metadata, instead of only echoing later media-understanding transcripts. Fixes #75084. Thanks @M-Lietz.
|
||||
- Web search: describe `web_search` as using the configured provider instead of hard-coding Brave when DuckDuckGo or another provider is active. Fixes #75088. Thanks @sun-rongyang.
|
||||
- Infra/tmp: tolerate concurrent temp-dir permission repairs by rechecking directories that another process already tightened, so parallel ACP subprocess startup no longer throws `Unsafe fallback OpenClaw temp dir`. Fixes #66867. Thanks @Kane808-AI and @jarvisz8.
|
||||
|
||||
## 2026.4.29
|
||||
|
||||
|
||||
@@ -394,6 +394,82 @@ describe("resolvePreferredOpenClawTmpDir", () => {
|
||||
expect(warn).toHaveBeenCalledWith(expect.stringContaining("tightened permissions on temp dir"));
|
||||
});
|
||||
|
||||
it("uses /tmp/openclaw when another process tightened permissions before repair", () => {
|
||||
const chmodSync = vi.fn();
|
||||
const warn = vi.fn();
|
||||
const tmpdir = vi.fn(() => "/var/fallback");
|
||||
const states = [0o40777, 0o40700, 0o40700];
|
||||
const lstatSync = vi.fn<NonNullable<TmpDirOptions["lstatSync"]>>((target: string) => {
|
||||
if (target === POSIX_OPENCLAW_TMP_DIR) {
|
||||
return makeDirStat({ mode: states.shift() ?? 0o40700 });
|
||||
}
|
||||
return secureDirStat();
|
||||
});
|
||||
|
||||
const resolved = resolvePreferredOpenClawTmpDir({
|
||||
accessSync: vi.fn(),
|
||||
lstatSync,
|
||||
chmodSync,
|
||||
mkdirSync: vi.fn(),
|
||||
getuid: vi.fn(() => 501),
|
||||
tmpdir,
|
||||
warn,
|
||||
});
|
||||
|
||||
expect(resolved).toBe(POSIX_OPENCLAW_TMP_DIR);
|
||||
expect(chmodSync).not.toHaveBeenCalled();
|
||||
expect(warn).not.toHaveBeenCalled();
|
||||
expect(tmpdir).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses fallback when another process tightened fallback permissions before repair", () => {
|
||||
const fallbackPath = fallbackTmp();
|
||||
const chmodSync = vi.fn();
|
||||
const warn = vi.fn();
|
||||
const states = [0o40777, 0o40700, 0o40700];
|
||||
|
||||
const resolved = resolveWithReadOnlyTmpFallback({
|
||||
fallbackPath,
|
||||
fallbackLstatSync: vi.fn(() => makeDirStat({ mode: states.shift() ?? 0o40700 })),
|
||||
chmodSync,
|
||||
warn,
|
||||
});
|
||||
|
||||
expect(resolved).toBe(fallbackPath);
|
||||
expect(chmodSync).not.toHaveBeenCalled();
|
||||
expect(warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses /tmp/openclaw when chmod loses a concurrent repair race", () => {
|
||||
const chmodSync = vi.fn((target: string, mode: number) => {
|
||||
if (target === POSIX_OPENCLAW_TMP_DIR && mode === 0o700) {
|
||||
throw nodeErrorWithCode("EPERM");
|
||||
}
|
||||
});
|
||||
const warn = vi.fn();
|
||||
const states = [0o40777, 0o40777, 0o40700];
|
||||
const lstatSync = vi.fn<NonNullable<TmpDirOptions["lstatSync"]>>((target: string) => {
|
||||
if (target === POSIX_OPENCLAW_TMP_DIR) {
|
||||
return makeDirStat({ mode: states.shift() ?? 0o40700 });
|
||||
}
|
||||
return secureDirStat();
|
||||
});
|
||||
|
||||
const resolved = resolvePreferredOpenClawTmpDir({
|
||||
accessSync: vi.fn(),
|
||||
lstatSync,
|
||||
chmodSync,
|
||||
mkdirSync: vi.fn(),
|
||||
getuid: vi.fn(() => 501),
|
||||
tmpdir: vi.fn(() => "/var/fallback"),
|
||||
warn,
|
||||
});
|
||||
|
||||
expect(resolved).toBe(POSIX_OPENCLAW_TMP_DIR);
|
||||
expect(chmodSync).toHaveBeenCalledWith(POSIX_OPENCLAW_TMP_DIR, 0o700);
|
||||
expect(warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("throws when the fallback directory cannot be created", () => {
|
||||
expect(() =>
|
||||
resolvePreferredOpenClawTmpDir({
|
||||
|
||||
@@ -106,10 +106,24 @@ export function resolvePreferredOpenClawTmpDir(
|
||||
if (uid !== undefined && typeof st.uid === "number" && st.uid !== uid) {
|
||||
return false;
|
||||
}
|
||||
if (typeof st.mode !== "number" || (st.mode & 0o022) === 0) {
|
||||
if (typeof st.mode !== "number") {
|
||||
return false;
|
||||
}
|
||||
chmodSync(candidatePath, 0o700);
|
||||
if ((st.mode & 0o022) === 0) {
|
||||
return resolveDirState(candidatePath) === "available";
|
||||
}
|
||||
try {
|
||||
chmodSync(candidatePath, 0o700);
|
||||
} catch (chmodErr) {
|
||||
if (
|
||||
isNodeErrorWithCode(chmodErr, "EPERM") ||
|
||||
isNodeErrorWithCode(chmodErr, "EACCES") ||
|
||||
isNodeErrorWithCode(chmodErr, "ENOENT")
|
||||
) {
|
||||
return resolveDirState(candidatePath) === "available";
|
||||
}
|
||||
throw chmodErr;
|
||||
}
|
||||
warn(`[openclaw] tightened permissions on temp dir: ${candidatePath}`);
|
||||
return resolveDirState(candidatePath) === "available";
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user