use std::{
fs, io,
path::{Path, PathBuf},
};
use super::read::validate_session_id;
pub(crate) const SESSION_ROOT_MODE: u32 = 0o700;
pub(crate) const SESSION_FILE_MODE: u32 = 0o600;
#[cfg(unix)]
fn effective_user_id() -> libc::uid_t {
unsafe { libc::geteuid() }
}
fn create_session_root(root: &Path) -> io::Result<()> {
#[cfg(unix)]
{
use std::os::unix::fs::DirBuilderExt;
let mut builder = fs::DirBuilder::new();
builder.recursive(true).mode(SESSION_ROOT_MODE);
builder.create(root)
}
#[cfg(not(unix))]
{
fs::create_dir_all(root)
}
}
pub(crate) fn prepare_session_root(root: &Path) -> anyhow::Result<()> {
match fs::symlink_metadata(root) {
Ok(_) => {}
Err(error) if error.kind() == io::ErrorKind::NotFound => {
create_session_root(root)?;
set_mode(root, SESSION_ROOT_MODE)?;
}
Err(error) => return Err(error.into()),
}
#[cfg(unix)]
{
let metadata = fs::symlink_metadata(root)?;
if is_legacy_mode_candidate(&metadata) {
return Err(legacy_permission_error(root));
}
}
validate_session_root(root)
}
#[cfg(unix)]
fn is_legacy_mode_candidate(metadata: &fs::Metadata) -> bool {
use std::os::unix::fs::MetadataExt;
!metadata.file_type().is_symlink()
&& metadata.file_type().is_dir()
&& metadata.uid() == effective_user_id()
&& metadata.mode() & 0o077 != 0
}
#[cfg(unix)]
fn legacy_permission_error(root: &Path) -> anyhow::Error {
if discover_repair_plan(root).is_ok() {
anyhow::anyhow!(
"session root permissions are not owner-private: {}. Run `magi-code sessions repair-permissions`; magi-code did not change any permissions itself.",
root.display()
)
} else {
anyhow::anyhow!(
"session root permissions are not owner-private: {}. Layout is unsafe, unreadable, or unrecognized; inspect it manually. magi-code did not change any permissions itself.",
root.display()
)
}
}
#[cfg(unix)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RepairPlan {
pub(crate) root: PathBuf,
targets: Vec<RepairTarget>,
}
#[cfg(unix)]
#[derive(Debug, Clone, PartialEq, Eq)]
struct RepairTarget {
relative: PathBuf,
dev: u64,
ino: u64,
is_dir: bool,
uid: u32,
nlink: u64,
}
#[cfg(unix)]
impl RepairPlan {
pub(crate) fn target_count(&self) -> usize {
self.targets.len()
}
pub(crate) fn permissions_are_secure(&self) -> std::io::Result<bool> {
use std::os::unix::fs::MetadataExt;
for target in &self.targets {
let metadata = fs::symlink_metadata(self.root.join(&target.relative))?;
if metadata.mode() & 0o777 != target_mode(target.is_dir) {
return Ok(false);
}
}
Ok(true)
}
}
#[cfg(unix)]
fn target_mode(is_dir: bool) -> u32 {
if is_dir {
SESSION_ROOT_MODE
} else {
SESSION_FILE_MODE
}
}
#[cfg(unix)]
pub(crate) fn discover_repair_plan(root: &Path) -> Result<RepairPlan, ()> {
use std::os::unix::fs::MetadataExt;
let metadata = fs::symlink_metadata(root).map_err(|_| ())?;
if !metadata.file_type().is_dir()
|| metadata.file_type().is_symlink()
|| metadata.uid() != effective_user_id()
{
return Err(());
}
let mut targets = Vec::new();
add_repair_target(PathBuf::new(), &metadata, true, &mut targets)?;
collect_repair_entries(root, Path::new(""), &mut targets, true)?;
targets.sort_by(|a, b| a.relative.cmp(&b.relative));
Ok(RepairPlan {
root: root.to_path_buf(),
targets,
})
}
#[cfg(unix)]
fn add_repair_target(
relative: PathBuf,
metadata: &fs::Metadata,
is_dir: bool,
targets: &mut Vec<RepairTarget>,
) -> Result<(), ()> {
use std::os::unix::fs::MetadataExt;
if metadata.file_type().is_symlink()
|| (is_dir && !metadata.file_type().is_dir())
|| (!is_dir && !metadata.file_type().is_file())
|| metadata.uid() != effective_user_id()
|| (!is_dir && metadata.nlink() != 1)
|| (!is_dir && metadata.mode() & 0o400 == 0)
{
return Err(());
}
targets.push(RepairTarget {
relative,
dev: metadata.dev(),
ino: metadata.ino(),
is_dir,
uid: metadata.uid(),
nlink: metadata.nlink(),
});
Ok(())
}
#[cfg(unix)]
fn collect_repair_entries(
root: &Path,
prefix: &Path,
targets: &mut Vec<RepairTarget>,
allow_subagents: bool,
) -> Result<(), ()> {
for (name, path) in read_legacy_entries(&root.join(prefix)).map_err(|_| ())? {
let relative = prefix.join(&name);
if name == "subagents" || name == ".history" {
let metadata = fs::symlink_metadata(&path).map_err(|_| ())?;
add_repair_target(relative.clone(), &metadata, true, targets)?;
if name == "subagents" {
if !allow_subagents {
return Err(());
}
collect_repair_entries(root, &relative, targets, false)?;
} else {
for (session_id, session_path) in read_legacy_entries(&path).map_err(|_| ())? {
validate_session_id(session_id.clone()).map_err(|_| ())?;
let session_relative = relative.join(&session_id);
let session_metadata = fs::symlink_metadata(&session_path).map_err(|_| ())?;
add_repair_target(session_relative.clone(), &session_metadata, true, targets)?;
for (generation, archive_path) in
read_legacy_entries(&session_path).map_err(|_| ())?
{
if generation
.strip_suffix(".jsonl")
.and_then(|v| v.parse::<u64>().ok())
.is_none()
{
return Err(());
}
let archive_metadata =
fs::symlink_metadata(&archive_path).map_err(|_| ())?;
add_repair_target(
session_relative.join(generation),
&archive_metadata,
false,
targets,
)?;
}
}
}
} else {
let metadata = fs::symlink_metadata(&path).map_err(|_| ())?;
validate_legacy_file(&path)?;
add_repair_target(relative, &metadata, false, targets)?;
}
}
Ok(())
}
#[cfg(unix)]
pub(crate) fn apply_repair_plan(plan: &RepairPlan) -> anyhow::Result<usize> {
let current = discover_repair_plan(&plan.root)
.map_err(|_| anyhow::anyhow!("session layout changed; refusing permission repair"))?;
if current != *plan {
anyhow::bail!("session layout changed; refusing permission repair")
}
use std::os::fd::{AsRawFd, FromRawFd};
let root_file = open_repair_root(&plan.root)?;
let mut files = Vec::new();
for target in &plan.targets {
let (fd, metadata) =
open_repair_target(root_file.as_raw_fd(), &target.relative, target.is_dir).map_err(
|error| {
anyhow::anyhow!(
"cannot safely open repair target {}: {error}",
target.relative.display()
)
},
)?;
use std::os::unix::fs::MetadataExt;
if metadata.dev() != target.dev
|| metadata.ino() != target.ino
|| metadata.uid() != target.uid
|| metadata.nlink() != target.nlink
|| (metadata.file_type().is_dir() != target.is_dir)
|| (!target.is_dir && metadata.nlink() != 1)
{
unsafe { libc::close(fd) };
anyhow::bail!("session layout changed; refusing permission repair")
}
let file = unsafe { fs::File::from_raw_fd(fd) };
files.push((file, target_mode(target.is_dir), target.relative.clone()));
}
let mut changed = 0;
for (file, mode, relative) in files {
let result = unsafe { libc::fchmod(file.as_raw_fd(), mode as libc::mode_t) };
if result != 0 {
return Err(anyhow::anyhow!(
"permission repair partially completed: changed {changed} of {} targets; failed target {}: {}",
plan.target_count(),
relative.display(),
io::Error::last_os_error()
));
}
changed += 1;
}
Ok(changed)
}
#[cfg(unix)]
fn open_repair_root(root: &Path) -> anyhow::Result<fs::File> {
use std::os::unix::fs::OpenOptionsExt;
let mut options = fs::OpenOptions::new();
options
.read(true)
.custom_flags(libc::O_DIRECTORY | libc::O_NOFOLLOW | libc::O_CLOEXEC);
Ok(options.open(root)?)
}
#[cfg(unix)]
fn open_repair_target(
root_fd: std::os::fd::RawFd,
relative: &Path,
is_dir: bool,
) -> anyhow::Result<(std::os::fd::RawFd, fs::Metadata)> {
use std::os::fd::{FromRawFd, IntoRawFd};
let mut directory = unsafe { libc::dup(root_fd) };
if directory < 0 {
return Err(io::Error::last_os_error().into());
}
let components = relative.components().collect::<Vec<_>>();
for (index, component) in components.iter().enumerate() {
let name = component
.as_os_str()
.to_str()
.ok_or_else(|| anyhow::anyhow!("invalid session entry"))?;
let c_name =
std::ffi::CString::new(name).map_err(|_| anyhow::anyhow!("invalid session entry"))?;
let last = index + 1 == components.len();
let flags = if last {
libc::O_RDONLY | if is_dir { libc::O_DIRECTORY } else { 0 }
} else {
libc::O_RDONLY | libc::O_DIRECTORY
};
let fd = unsafe {
libc::openat(
directory,
c_name.as_ptr(),
flags | libc::O_CLOEXEC | libc::O_NOFOLLOW,
)
};
unsafe { libc::close(directory) };
if fd < 0 {
return Err(io::Error::last_os_error().into());
}
directory = fd;
}
if components.is_empty() {
let duplicate = unsafe { libc::dup(directory) };
if duplicate < 0 {
unsafe { libc::close(directory) };
return Err(io::Error::last_os_error().into());
}
let metadata = unsafe { fs::File::from_raw_fd(duplicate) }.metadata()?;
return Ok((directory, metadata));
}
let file = unsafe { fs::File::from_raw_fd(directory) };
let metadata = file.metadata()?;
Ok((file.into_raw_fd(), metadata))
}
#[cfg(unix)]
fn read_legacy_entries(root: &Path) -> io::Result<Vec<(String, PathBuf)>> {
let mut entries = Vec::new();
for entry in fs::read_dir(root)? {
let entry = entry?;
let name = entry
.file_name()
.into_string()
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "non-UTF-8 session entry"))?;
entries.push((name, entry.path()));
}
entries.sort_by(|left, right| left.0.cmp(&right.0));
Ok(entries)
}
#[cfg(unix)]
fn validate_legacy_file(path: &Path) -> Result<(), ()> {
use std::os::unix::fs::MetadataExt;
let name = path.file_name().and_then(|name| name.to_str()).ok_or(())?;
let id = name
.strip_suffix(".metadata.json")
.or_else(|| name.strip_suffix(".jsonl"))
.ok_or(())?;
validate_session_id(id.to_string()).map_err(|_| ())?;
let metadata = fs::symlink_metadata(path).map_err(|_| ())?;
if metadata.file_type().is_symlink()
|| !metadata.file_type().is_file()
|| metadata.nlink() != 1
|| metadata.uid() != effective_user_id()
{
return Err(());
}
Ok(())
}
pub(crate) fn validate_session_root(root: &Path) -> anyhow::Result<()> {
let metadata = fs::symlink_metadata(root)?;
validate_root_metadata(&metadata)
}
fn validate_root_metadata(metadata: &fs::Metadata) -> anyhow::Result<()> {
if metadata.file_type().is_symlink() || !metadata.file_type().is_dir() {
anyhow::bail!("session root must be a non-symlink directory");
}
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
if metadata.uid() != effective_user_id() {
anyhow::bail!("session root owner is not current user");
}
if metadata.mode() & 0o077 != 0 {
anyhow::bail!("session root permissions are not owner-private");
}
}
Ok(())
}
pub(crate) fn primary_path(root: &Path, id: &str) -> anyhow::Result<PathBuf> {
let id = validate_session_id(id.to_string())?;
Ok(root.join(format!("{id}.jsonl")))
}
pub(crate) fn validate_existing_file(path: &Path) -> anyhow::Result<Option<fs::Metadata>> {
let path_metadata = match fs::symlink_metadata(path) {
Ok(metadata) => metadata,
Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(None),
Err(error) => return Err(error.into()),
};
validate_file_metadata(&path_metadata)?;
Ok(Some(path_metadata))
}
fn validate_file_metadata(metadata: &fs::Metadata) -> anyhow::Result<()> {
if metadata.file_type().is_symlink() || !metadata.file_type().is_file() {
anyhow::bail!("session store object is not a regular file");
}
#[cfg(unix)]
{
use std::os::unix::fs::MetadataExt;
if metadata.nlink() != 1 {
anyhow::bail!("session store object has unexpected hard links");
}
if metadata.uid() != effective_user_id() {
anyhow::bail!("session store object owner is not current user");
}
if metadata.mode() & 0o077 != 0 {
anyhow::bail!("session store object permissions are not owner-private");
}
}
Ok(())
}
#[cfg(unix)]
fn open_root(root: &Path) -> anyhow::Result<fs::File> {
use std::os::unix::fs::OpenOptionsExt;
let mut options = fs::OpenOptions::new();
options
.read(true)
.custom_flags(libc::O_DIRECTORY | libc::O_NOFOLLOW);
let file = options.open(root)?;
validate_root_metadata(&file.metadata()?)?;
Ok(file)
}
fn open_relative(root: &Path, name: &str, write: bool, create: bool) -> anyhow::Result<fs::File> {
#[cfg(unix)]
{
use std::os::fd::{AsRawFd, FromRawFd};
let root_file = open_root(root)?;
let mut flags = if write {
libc::O_WRONLY | libc::O_APPEND
} else {
libc::O_RDONLY
};
flags |= libc::O_CLOEXEC | libc::O_NOFOLLOW;
if create {
flags |= libc::O_CREAT;
}
let name = std::ffi::CString::new(name)
.map_err(|_| anyhow::anyhow!("invalid session filename"))?;
let fd = unsafe {
libc::openat(
root_file.as_raw_fd(),
name.as_ptr(),
flags,
SESSION_FILE_MODE,
)
};
if fd < 0 {
return Err(io::Error::last_os_error().into());
}
let file = unsafe { fs::File::from_raw_fd(fd) };
validate_file_metadata(&file.metadata()?)?;
Ok(file)
}
#[cfg(not(unix))]
{
validate_session_root(root)?;
let path = root.join(name);
let mut options = fs::OpenOptions::new();
options
.read(!write)
.write(write)
.create(create)
.append(write);
let file = options.open(path)?;
validate_file_metadata(&file.metadata()?)?;
Ok(file)
}
}
fn open_relative_new_or_existing(root: &Path, name: &str) -> anyhow::Result<(fs::File, bool)> {
#[cfg(unix)]
{
use std::os::fd::{AsRawFd, FromRawFd};
let root_file = open_root(root)?;
let name = std::ffi::CString::new(name)
.map_err(|_| anyhow::anyhow!("invalid session filename"))?;
let flags = libc::O_WRONLY
| libc::O_APPEND
| libc::O_CREAT
| libc::O_EXCL
| libc::O_CLOEXEC
| libc::O_NOFOLLOW;
let fd = unsafe {
libc::openat(
root_file.as_raw_fd(),
name.as_ptr(),
flags,
SESSION_FILE_MODE,
)
};
if fd >= 0 {
let file = unsafe { fs::File::from_raw_fd(fd) };
validate_file_metadata(&file.metadata()?)?;
return Ok((file, true));
}
let error = io::Error::last_os_error();
if error.kind() != io::ErrorKind::AlreadyExists {
return Err(error.into());
}
let flags = libc::O_WRONLY | libc::O_APPEND | libc::O_CLOEXEC | libc::O_NOFOLLOW;
let fd = unsafe { libc::openat(root_file.as_raw_fd(), name.as_ptr(), flags) };
if fd < 0 {
return Err(io::Error::last_os_error().into());
}
let file = unsafe { fs::File::from_raw_fd(fd) };
validate_file_metadata(&file.metadata()?)?;
Ok((file, false))
}
#[cfg(not(unix))]
{
let path = root.join(name);
let existed = path.try_exists()?;
Ok((open_relative(root, name, true, true)?, !existed))
}
}
pub(crate) fn open_primary(root: &Path, id: &str) -> anyhow::Result<(fs::File, bool)> {
let id = validate_session_id(id.to_string())?;
open_relative_new_or_existing(root, &format!("{id}.jsonl"))
}
pub(crate) fn open_existing_primary(root: &Path, id: &str) -> anyhow::Result<Option<fs::File>> {
let id = validate_session_id(id.to_string())?;
open_existing_named(root, &format!("{id}.jsonl"))
}
pub(crate) fn open_existing_named(root: &Path, name: &str) -> anyhow::Result<Option<fs::File>> {
if Path::new(name).file_name().and_then(|value| value.to_str()) != Some(name) {
anyhow::bail!("session store object has unexpected name");
}
match open_relative(root, name, false, false) {
Ok(file) => Ok(Some(file)),
Err(error)
if error
.downcast_ref::<io::Error>()
.is_some_and(|e| e.kind() == io::ErrorKind::NotFound) =>
{
Ok(None)
}
Err(error) => Err(error),
}
}
pub(crate) fn validate_path_file(
root: &Path,
id: &str,
path: &Path,
) -> anyhow::Result<fs::Metadata> {
let expected = primary_path(root, id)?;
if path != expected {
anyhow::bail!("session store object has unexpected path");
}
open_existing_primary(root, id)?
.map(|file| file.metadata())
.transpose()?
.ok_or_else(|| anyhow::anyhow!("session store object is missing"))
}
fn set_mode(path: &Path, mode: u32) -> io::Result<()> {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(path, fs::Permissions::from_mode(mode))?;
}
let _ = (path, mode);
Ok(())
}
#[cfg(test)]
pub(crate) fn secure_profiling_session_root(root: &Path) {
fs::create_dir_all(root).unwrap();
set_mode(root, SESSION_ROOT_MODE).unwrap();
for entry in fs::read_dir(root).unwrap().flatten() {
if entry
.file_type()
.map(|kind| kind.is_file())
.unwrap_or(false)
{
set_mode(&entry.path(), SESSION_FILE_MODE).unwrap();
}
}
}
#[cfg(unix)]
fn create_quarantine(
root_file: &std::fs::File,
name: &str,
) -> io::Result<(String, std::os::fd::RawFd)> {
use std::os::fd::AsRawFd;
for _ in 0..16 {
let quarantine_name = format!(
".{name}.prune-{}",
NEXT_QUARANTINE.fetch_add(1, Ordering::Relaxed)
);
let quarantine_c =
std::ffi::CString::new(quarantine_name.as_str()).expect("generated quarantine name");
let mkdir_result = unsafe {
libc::mkdirat(
root_file.as_raw_fd(),
quarantine_c.as_ptr(),
SESSION_ROOT_MODE as libc::mode_t,
)
};
if mkdir_result == 0 {
let quarantine_fd = unsafe {
libc::openat(
root_file.as_raw_fd(),
quarantine_c.as_ptr(),
libc::O_RDONLY | libc::O_DIRECTORY | libc::O_CLOEXEC | libc::O_NOFOLLOW,
)
};
if quarantine_fd >= 0 {
return Ok((quarantine_name, quarantine_fd));
}
let error = io::Error::last_os_error();
unsafe {
libc::unlinkat(
root_file.as_raw_fd(),
quarantine_c.as_ptr(),
libc::AT_REMOVEDIR,
);
}
return Err(error);
}
let error = io::Error::last_os_error();
if error.kind() != io::ErrorKind::AlreadyExists {
return Err(error);
}
}
Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"unable to allocate session removal quarantine",
))
}
#[cfg(unix)]
fn stat_relative(
directory_fd: &std::os::fd::RawFd,
name: &std::ffi::CStr,
) -> io::Result<(u64, u64, bool)> {
let mut stat = std::mem::MaybeUninit::<libc::stat>::uninit();
let result = unsafe {
libc::fstatat(
*directory_fd,
name.as_ptr(),
stat.as_mut_ptr(),
libc::AT_SYMLINK_NOFOLLOW,
)
};
if result != 0 {
return Err(io::Error::last_os_error());
}
let stat = unsafe { stat.assume_init() };
#[cfg_attr(target_os = "linux", expect(clippy::unnecessary_cast))]
let device = stat.st_dev as u64;
Ok((
device,
stat.st_ino,
stat.st_mode & libc::S_IFMT == libc::S_IFDIR,
))
}
#[cfg(target_os = "linux")]
fn rename_noreplace(
source_dir: std::os::fd::RawFd,
source: &std::ffi::CStr,
target_dir: std::os::fd::RawFd,
target: &std::ffi::CStr,
) -> io::Result<()> {
let result = unsafe {
libc::renameat2(
source_dir,
source.as_ptr(),
target_dir,
target.as_ptr(),
libc::RENAME_NOREPLACE,
)
};
if result == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
#[cfg(target_os = "macos")]
fn rename_noreplace(
source_dir: std::os::fd::RawFd,
source: &std::ffi::CStr,
target_dir: std::os::fd::RawFd,
target: &std::ffi::CStr,
) -> io::Result<()> {
let result = unsafe {
libc::renameatx_np(
source_dir,
source.as_ptr(),
target_dir,
target.as_ptr(),
libc::RENAME_EXCL,
)
};
if result == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
pub(crate) fn remove_primary(root: &Path, id: &str) -> io::Result<()> {
let id = validate_session_id(id.to_string())
.map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?;
let name = format!("{id}.jsonl");
#[cfg(unix)]
{
use std::os::fd::{AsRawFd, FromRawFd};
use std::os::unix::fs::MetadataExt;
let root_file = open_root(root).map_err(|error| io::Error::other(error.to_string()))?;
let name_c = std::ffi::CString::new(name.clone()).expect("validated session filename");
let fd = unsafe {
libc::openat(
root_file.as_raw_fd(),
name_c.as_ptr(),
libc::O_RDONLY | libc::O_CLOEXEC | libc::O_NOFOLLOW,
)
};
if fd < 0 {
return Err(io::Error::last_os_error());
}
let candidate = unsafe { fs::File::from_raw_fd(fd) };
let candidate_metadata = candidate.metadata()?;
validate_file_metadata(&candidate_metadata)
.map_err(|error| io::Error::other(error.to_string()))?;
let (quarantine_name, quarantine_fd) = create_quarantine(&root_file, &name)?;
let quarantine_c =
std::ffi::CString::new(quarantine_name.as_str()).expect("generated quarantine name");
let entry_c = std::ffi::CString::new("entry").unwrap();
let moved = unsafe {
libc::renameat(
root_file.as_raw_fd(),
name_c.as_ptr(),
quarantine_fd,
entry_c.as_ptr(),
)
};
if moved != 0 {
let error = io::Error::last_os_error();
unsafe {
libc::close(quarantine_fd);
libc::unlinkat(
root_file.as_raw_fd(),
quarantine_c.as_ptr(),
libc::AT_REMOVEDIR,
);
}
return Err(error);
}
let (moved_dev, moved_ino, moved_is_dir) = match stat_relative(&quarantine_fd, &entry_c) {
Ok(stat) => stat,
Err(error) => {
let restored =
rename_noreplace(quarantine_fd, &entry_c, root_file.as_raw_fd(), &name_c);
unsafe { libc::close(quarantine_fd) };
if restored.is_ok() {
unsafe {
libc::unlinkat(
root_file.as_raw_fd(),
quarantine_c.as_ptr(),
libc::AT_REMOVEDIR,
);
}
return Err(error);
}
return Err(io::Error::other(
"session store removal could not inspect or restore moved object",
));
}
};
let same_object =
candidate_metadata.dev() == moved_dev && candidate_metadata.ino() == moved_ino;
if same_object {
let result = unsafe {
libc::unlinkat(
quarantine_fd,
entry_c.as_ptr(),
if moved_is_dir { libc::AT_REMOVEDIR } else { 0 },
)
};
if result != 0 {
let error = io::Error::last_os_error();
unsafe { libc::close(quarantine_fd) };
return Err(error);
}
unsafe { libc::close(quarantine_fd) };
let result = unsafe {
libc::unlinkat(
root_file.as_raw_fd(),
quarantine_c.as_ptr(),
libc::AT_REMOVEDIR,
)
};
return if result == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
};
}
let restored = rename_noreplace(quarantine_fd, &entry_c, root_file.as_raw_fd(), &name_c);
unsafe { libc::close(quarantine_fd) };
if let Err(error) = restored {
if error.kind() == io::ErrorKind::AlreadyExists {
return Err(io::Error::other(
"session store object changed during removal; original path occupied",
));
}
return Err(error);
}
let result = unsafe {
libc::unlinkat(
root_file.as_raw_fd(),
quarantine_c.as_ptr(),
libc::AT_REMOVEDIR,
)
};
if result != 0 {
return Err(io::Error::last_os_error());
}
Err(io::Error::other(
"session store object changed during removal",
))
}
#[cfg(not(unix))]
{
remove_primary_portable(root, &name)
}
}
#[cfg(not(unix))]
fn remove_primary_portable(root: &Path, name: &str) -> io::Result<()> {
validate_session_root(root).map_err(|error| io::Error::other(error.to_string()))?;
let path = root.join(name);
match validate_existing_file(&path).map_err(|error| io::Error::other(error.to_string()))? {
Some(_) => fs::remove_file(path),
None => Err(io::Error::from(io::ErrorKind::NotFound)),
}
}
#[cfg(unix)]
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(unix)]
static NEXT_QUARANTINE: AtomicU64 = AtomicU64::new(0);