Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 12 additions & 38 deletions crates/uv-fs/src/locked_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,42 +234,26 @@ impl LockedFile {

#[cfg(unix)]
fn create(path: impl AsRef<Path>) -> Result<fs_err::File, LockedFileError> {
use fs_err::os::unix::fs::OpenOptionsExt;
use rustix::io::Errno;
#[expect(clippy::disallowed_types)]
use std::{fs::File, os::unix::fs::PermissionsExt};
use tempfile::NamedTempFile;
use std::{fs::Permissions, os::unix::fs::PermissionsExt};
use tempfile::Builder;

/// The permissions the lockfile should end up with
const DESIRED_MODE: u32 = 0o666;
const DESIRED_MODE: u32 = 0o444;

#[expect(clippy::disallowed_types)]
fn try_set_permissions(file: &File, path: &Path) {
if let Err(err) = file.set_permissions(std::fs::Permissions::from_mode(DESIRED_MODE)) {
warn!(
"Failed to set permissions on temporary file `{path}`: {err}",
path = path.user_display()
);
}
}

// If path already exists, return it.
if let Ok(file) = fs_err::OpenOptions::new()
.read(true)
.write(true)
.open(path.as_ref())
{
// If path already exists (and we can read it), return it.
if let Ok(file) = fs_err::OpenOptions::new().read(true).open(path.as_ref()) {
return Ok(file);
}

// Otherwise, create a temporary file with 666 permissions. We must set
// permissions _after_ creating the file, to override the `umask`.
// Otherwise, create a temporary file with DESIRED_MODE in the same directory as path.
let perms = Permissions::from_mode(DESIRED_MODE);
let file = if let Some(parent) = path.as_ref().parent() {
NamedTempFile::new_in(parent)
Builder::new().permissions(perms).tempfile_in(parent)
} else {
NamedTempFile::new()
Builder::new().permissions(perms).tempfile()
}
.map_err(LockedFileError::CreateTemporary)?;
try_set_permissions(file.as_file(), file.path());

// Try to move the file to path, but if path exists now, just open path
match file.persist_noclobber(path.as_ref()) {
Expand All @@ -278,7 +262,6 @@ impl LockedFile {
if err.error.kind() == std::io::ErrorKind::AlreadyExists {
fs_err::OpenOptions::new()
.read(true)
.write(true)
.open(path.as_ref())
.map_err(Into::into)
} else if matches!(
Expand All @@ -298,22 +281,13 @@ impl LockedFile {
// are locking two different files. Also, since `persist_noclobber` is more
// likely to not be supported on special filesystems which don't have permission
// bits, it's less likely to ever matter.

let file = fs_err::OpenOptions::new()
.read(true)
.write(true)
.mode(DESIRED_MODE)
.create(true)
.open(path.as_ref())?;

// We don't want to `try_set_permissions` in cases where another user's process
// has already created the lockfile and changed its permissions because we might
// not have permission to change the permissions which would produce a confusing
// warning.
if file
.metadata()
.is_ok_and(|metadata| metadata.permissions().mode() != DESIRED_MODE)
{
try_set_permissions(file.file(), path.as_ref());
}
Ok(file)
} else {
let temp_path = err.file.into_temp_path();
Expand Down
Loading