Add a shared cleanup lock guard for local log directories (#55358)

## What changed

Export `LocalLogsDir` from `codex-file-system` to create a caller-selected diagnostic directory and retain a shared directory lock on Unix. Clones share the lock so callers can keep it held until all log connections close.

New directories use mode `0700` on Unix; existing permissions are preserved. Initialization checks that the locked directory still matches the selected path and returns an error if it changed.

GitOrigin-RevId: fc870d7ee0997d67ba1a23f72ba4ac3cf89fd31a
This commit is contained in:
Dmitriy Kovalenko
2026-10-19 19:33:42 +00:00
committed by copyberry
parent 9b865ab31e
commit 18d09e1eda
2 changed files with 64 additions and 0 deletions
+2
View File
@@ -1,6 +1,7 @@
mod environment_accessor;
mod exec_permission_profile_serde;
mod find_up;
mod local_logs;
mod nfs;
use bytes::Bytes;
@@ -30,6 +31,7 @@ pub use find_up::find_nearest_ancestor_with_markers;
pub use find_up::find_nearest_native_ancestor_with_markers;
use futures::Stream;
use futures::stream::BoxStream;
pub use local_logs::LocalLogsDir;
pub use nfs::is_nfs;
use serde::Deserialize;
use serde::Serialize;
+62
View File
@@ -0,0 +1,62 @@
//! Creates the configured diagnostic directory and retains a shared directory lock on Unix.
//! The caller selects the path. This module does not search for storage.
use std::io;
use std::path::Path;
use std::path::PathBuf;
/// A diagnostic directory with a Unix cleanup lock retained by all clones.
#[derive(Clone, Debug)]
#[must_use = "keep the guard until all log connections close"]
pub struct LocalLogsDir {
path: PathBuf,
#[cfg(unix)]
_cleanup_lock: std::sync::Arc<std::fs::File>,
}
impl LocalLogsDir {
/// Create the selected directory. Keep the guard until all log connections close.
/// On Unix, newly created directories start with mode 0700. Existing permissions
/// are preserved, including on user-created directories or after the user changes
/// the mode. We do not enforce or restore private permissions on later calls.
pub fn new(path: &Path) -> io::Result<Self> {
let mut builder = std::fs::DirBuilder::new();
builder.recursive(true);
#[cfg(unix)]
{
use std::os::unix::fs::DirBuilderExt;
// Set private permissions only at creation; preserve user-chosen modes afterward.
builder.mode(0o700);
}
builder.create(path)?;
// the main reason to use the diagnostic is to redirect the logs to the tmp/ or /dev/shm (tmpfs)
// so on unix system we have to ensure that the directory is not going to be cleaned via shared lock
#[cfg(unix)]
let directory = {
use std::os::unix::fs::MetadataExt;
let directory = std::fs::File::open(path)?;
directory.try_lock_shared()?;
let locked = directory.metadata()?;
let current = std::fs::metadata(path)?;
if !locked.is_dir() || (current.dev(), current.ino()) != (locked.dev(), locked.ino()) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"log directory changed during initialization",
));
}
std::sync::Arc::new(directory)
};
Ok(Self {
path: path.to_path_buf(),
#[cfg(unix)]
_cleanup_lock: directory,
})
}
pub fn path(&self) -> &Path {
&self.path
}
}