Update 'nix' package (#11948)

There were some incompatible changes. Most churn was from switching from
the now-deprecated fcntl:flock() function to
fcntl::Flock::lock(). The new function returns a guard object, while
with the old function, the lock was associated directly with the file
descriptor.

It's good to stay up-to-date in general, but the impetus to do this now
is that in https://github.com/neondatabase/neon/pull/11929, I want to
use some functions that were added only in the latest version of 'nix',
and it's nice to not have to build multiple versions. (Although,
different versions of 'nix' are still pulled in as indirect dependencies
from other packages)
This commit is contained in:
Heikki Linnakangas
2025-05-16 17:45:08 +03:00
committed by GitHub
parent baafcc5d41
commit 55f91cf10b
9 changed files with 66 additions and 57 deletions

25
Cargo.lock generated
View File

@@ -1112,6 +1112,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "cgroups-rs" name = "cgroups-rs"
version = "0.3.3" version = "0.3.3"
@@ -1306,7 +1312,7 @@ dependencies = [
"itertools 0.10.5", "itertools 0.10.5",
"jsonwebtoken", "jsonwebtoken",
"metrics", "metrics",
"nix 0.27.1", "nix 0.30.1",
"notify", "notify",
"num_cpus", "num_cpus",
"once_cell", "once_cell",
@@ -1429,7 +1435,7 @@ dependencies = [
"humantime-serde", "humantime-serde",
"hyper 0.14.30", "hyper 0.14.30",
"jsonwebtoken", "jsonwebtoken",
"nix 0.27.1", "nix 0.30.1",
"once_cell", "once_cell",
"pageserver_api", "pageserver_api",
"pageserver_client", "pageserver_client",
@@ -3512,9 +3518,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.169" version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]] [[package]]
name = "libloading" name = "libloading"
@@ -3821,12 +3827,13 @@ dependencies = [
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.27.1" version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [ dependencies = [
"bitflags 2.8.0", "bitflags 2.8.0",
"cfg-if", "cfg-if",
"cfg_aliases",
"libc", "libc",
"memoffset 0.9.0", "memoffset 0.9.0",
] ]
@@ -4280,7 +4287,7 @@ dependencies = [
"jsonwebtoken", "jsonwebtoken",
"md5", "md5",
"metrics", "metrics",
"nix 0.27.1", "nix 0.30.1",
"num-traits", "num-traits",
"num_cpus", "num_cpus",
"once_cell", "once_cell",
@@ -4356,7 +4363,7 @@ dependencies = [
"humantime", "humantime",
"humantime-serde", "humantime-serde",
"itertools 0.10.5", "itertools 0.10.5",
"nix 0.27.1", "nix 0.30.1",
"once_cell", "once_cell",
"postgres_backend", "postgres_backend",
"postgres_ffi", "postgres_ffi",
@@ -7899,7 +7906,7 @@ dependencies = [
"humantime", "humantime",
"jsonwebtoken", "jsonwebtoken",
"metrics", "metrics",
"nix 0.27.1", "nix 0.30.1",
"once_cell", "once_cell",
"pem", "pem",
"pin-project-lite", "pin-project-lite",

View File

@@ -127,7 +127,7 @@ md5 = "0.7.0"
measured = { version = "0.0.22", features=["lasso"] } measured = { version = "0.0.22", features=["lasso"] }
measured-process = { version = "0.0.22" } measured-process = { version = "0.0.22" }
memoffset = "0.9" memoffset = "0.9"
nix = { version = "0.27", features = ["dir", "fs", "process", "socket", "signal", "poll"] } nix = { version = "0.30.1", features = ["dir", "fs", "process", "socket", "signal", "poll"] }
# Do not update to >= 7.0.0, at least. The update will have a significant impact # Do not update to >= 7.0.0, at least. The update will have a significant impact
# on compute startup metrics (start_postgres_ms), >= 25% degradation. # on compute startup metrics (start_postgres_ms), >= 25% degradation.
notify = "6.0.0" notify = "6.0.0"

View File

@@ -14,7 +14,7 @@
use std::ffi::OsStr; use std::ffi::OsStr;
use std::io::Write; use std::io::Write;
use std::os::unix::prelude::AsRawFd; use std::os::fd::AsFd;
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
@@ -356,7 +356,7 @@ where
let file = pid_file::claim_for_current_process(&path).expect("claim pid file"); let file = pid_file::claim_for_current_process(&path).expect("claim pid file");
// Remove the FD_CLOEXEC flag on the pidfile descriptor so that the pidfile // Remove the FD_CLOEXEC flag on the pidfile descriptor so that the pidfile
// remains locked after exec. // remains locked after exec.
nix::fcntl::fcntl(file.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::empty())) nix::fcntl::fcntl(file.as_fd(), FcntlArg::F_SETFD(FdFlag::empty()))
.expect("remove FD_CLOEXEC"); .expect("remove FD_CLOEXEC");
// Don't run drop(file), it would close the file before we actually exec. // Don't run drop(file), it would close the file before we actually exec.
std::mem::forget(file); std::mem::forget(file);

View File

@@ -8,7 +8,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{BTreeSet, HashMap}; use std::collections::{BTreeSet, HashMap};
use std::fs::File; use std::fs::File;
use std::os::fd::AsRawFd;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use std::str::FromStr; use std::str::FromStr;
@@ -31,7 +30,7 @@ use control_plane::safekeeper::SafekeeperNode;
use control_plane::storage_controller::{ use control_plane::storage_controller::{
NeonStorageControllerStartArgs, NeonStorageControllerStopArgs, StorageController, NeonStorageControllerStartArgs, NeonStorageControllerStopArgs, StorageController,
}; };
use nix::fcntl::{FlockArg, flock}; use nix::fcntl::{Flock, FlockArg};
use pageserver_api::config::{ use pageserver_api::config::{
DEFAULT_HTTP_LISTEN_PORT as DEFAULT_PAGESERVER_HTTP_PORT, DEFAULT_HTTP_LISTEN_PORT as DEFAULT_PAGESERVER_HTTP_PORT,
DEFAULT_PG_LISTEN_PORT as DEFAULT_PAGESERVER_PG_PORT, DEFAULT_PG_LISTEN_PORT as DEFAULT_PAGESERVER_PG_PORT,
@@ -749,16 +748,16 @@ struct TimelineTreeEl {
/// A flock-based guard over the neon_local repository directory /// A flock-based guard over the neon_local repository directory
struct RepoLock { struct RepoLock {
_file: File, _file: Flock<File>,
} }
impl RepoLock { impl RepoLock {
fn new() -> Result<Self> { fn new() -> Result<Self> {
let repo_dir = File::open(local_env::base_path())?; let repo_dir = File::open(local_env::base_path())?;
let repo_dir_fd = repo_dir.as_raw_fd(); match Flock::lock(repo_dir, FlockArg::LockExclusive) {
flock(repo_dir_fd, FlockArg::LockExclusive)?; Ok(f) => Ok(Self { _file: f }),
Err((_, e)) => Err(e).context("flock error"),
Ok(Self { _file: repo_dir }) }
} }
} }

View File

@@ -1,7 +1,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{self, Write}; use std::io::{self, Write};
use std::os::fd::AsRawFd; use std::os::fd::AsFd;
use camino::{Utf8Path, Utf8PathBuf}; use camino::{Utf8Path, Utf8PathBuf};
@@ -210,13 +210,13 @@ pub fn overwrite(
/// Syncs the filesystem for the given file descriptor. /// Syncs the filesystem for the given file descriptor.
#[cfg_attr(target_os = "macos", allow(unused_variables))] #[cfg_attr(target_os = "macos", allow(unused_variables))]
pub fn syncfs(fd: impl AsRawFd) -> anyhow::Result<()> { pub fn syncfs(fd: impl AsFd) -> anyhow::Result<()> {
// Linux guarantees durability for syncfs. // Linux guarantees durability for syncfs.
// POSIX doesn't have syncfs, and further does not actually guarantee durability of sync(). // POSIX doesn't have syncfs, and further does not actually guarantee durability of sync().
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
use anyhow::Context; use anyhow::Context;
nix::unistd::syncfs(fd.as_raw_fd()).context("syncfs")?; nix::unistd::syncfs(fd).context("syncfs")?;
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {

View File

@@ -11,9 +11,9 @@ pub fn rename_noreplace<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
#[cfg(all(target_os = "linux", target_env = "gnu"))] #[cfg(all(target_os = "linux", target_env = "gnu"))]
{ {
nix::fcntl::renameat2( nix::fcntl::renameat2(
None, nix::fcntl::AT_FDCWD,
src, src,
None, nix::fcntl::AT_FDCWD,
dst, dst,
nix::fcntl::RenameFlags::RENAME_NOREPLACE, nix::fcntl::RenameFlags::RENAME_NOREPLACE,
) )

View File

@@ -1,6 +1,6 @@
//! A module to create and read lock files. //! A module to create and read lock files.
//! //!
//! File locking is done using [`fcntl::flock`] exclusive locks. //! File locking is done using [`nix::fcntl::Flock`] exclusive locks.
//! The only consumer of this module is currently //! The only consumer of this module is currently
//! [`pid_file`](crate::pid_file). See the module-level comment //! [`pid_file`](crate::pid_file). See the module-level comment
//! there for potential pitfalls with lock files that are used //! there for potential pitfalls with lock files that are used
@@ -9,26 +9,25 @@
use std::fs; use std::fs;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::ops::Deref; use std::ops::Deref;
use std::os::unix::prelude::AsRawFd;
use anyhow::Context; use anyhow::Context;
use camino::{Utf8Path, Utf8PathBuf}; use camino::{Utf8Path, Utf8PathBuf};
use nix::errno::Errno::EAGAIN; use nix::errno::Errno::EAGAIN;
use nix::fcntl; use nix::fcntl::{Flock, FlockArg};
use crate::crashsafe; use crate::crashsafe;
/// A handle to an open and unlocked, but not-yet-written lock file. /// A handle to an open and flocked, but not-yet-written lock file.
/// Returned by [`create_exclusive`]. /// Returned by [`create_exclusive`].
#[must_use] #[must_use]
pub struct UnwrittenLockFile { pub struct UnwrittenLockFile {
path: Utf8PathBuf, path: Utf8PathBuf,
file: fs::File, file: Flock<fs::File>,
} }
/// Returned by [`UnwrittenLockFile::write_content`]. /// Returned by [`UnwrittenLockFile::write_content`].
#[must_use] #[must_use]
pub struct LockFileGuard(fs::File); pub struct LockFileGuard(Flock<fs::File>);
impl Deref for LockFileGuard { impl Deref for LockFileGuard {
type Target = fs::File; type Target = fs::File;
@@ -67,17 +66,14 @@ pub fn create_exclusive(lock_file_path: &Utf8Path) -> anyhow::Result<UnwrittenLo
.open(lock_file_path) .open(lock_file_path)
.context("open lock file")?; .context("open lock file")?;
let res = fcntl::flock( let res = Flock::lock(lock_file, FlockArg::LockExclusiveNonblock);
lock_file.as_raw_fd(),
fcntl::FlockArg::LockExclusiveNonblock,
);
match res { match res {
Ok(()) => Ok(UnwrittenLockFile { Ok(lock_file) => Ok(UnwrittenLockFile {
path: lock_file_path.to_owned(), path: lock_file_path.to_owned(),
file: lock_file, file: lock_file,
}), }),
Err(EAGAIN) => anyhow::bail!("file is already locked"), Err((_, EAGAIN)) => anyhow::bail!("file is already locked"),
Err(e) => Err(e).context("flock error"), Err((_, e)) => Err(e).context("flock error"),
} }
} }
@@ -105,32 +101,37 @@ pub enum LockFileRead {
/// Check the [`LockFileRead`] variants for details. /// Check the [`LockFileRead`] variants for details.
pub fn read_and_hold_lock_file(path: &Utf8Path) -> anyhow::Result<LockFileRead> { pub fn read_and_hold_lock_file(path: &Utf8Path) -> anyhow::Result<LockFileRead> {
let res = fs::OpenOptions::new().read(true).open(path); let res = fs::OpenOptions::new().read(true).open(path);
let mut lock_file = match res { let lock_file = match res {
Ok(f) => f, Ok(f) => f,
Err(e) => match e.kind() { Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => return Ok(LockFileRead::NotExist), std::io::ErrorKind::NotFound => return Ok(LockFileRead::NotExist),
_ => return Err(e).context("open lock file"), _ => return Err(e).context("open lock file"),
}, },
}; };
let res = fcntl::flock( let res = Flock::lock(lock_file, FlockArg::LockExclusiveNonblock);
lock_file.as_raw_fd(),
fcntl::FlockArg::LockExclusiveNonblock,
);
// We need the content regardless of lock success / failure. // We need the content regardless of lock success / failure.
// But, read it after flock so that, if it succeeded, the content is consistent. // But, read it after flock so that, if it succeeded, the content is consistent.
let mut content = String::new();
lock_file
.read_to_string(&mut content)
.context("read lock file")?;
match res { match res {
Ok(()) => Ok(LockFileRead::NotHeldByAnyProcess( Ok(mut locked_file) => {
LockFileGuard(lock_file), let mut content = String::new();
content, locked_file
)), .read_to_string(&mut content)
Err(EAGAIN) => Ok(LockFileRead::LockedByOtherProcess { .context("read lock file")?;
not_locked_file: lock_file, Ok(LockFileRead::NotHeldByAnyProcess(
content, LockFileGuard(locked_file),
}), content,
Err(e) => Err(e).context("flock error"), ))
}
Err((mut not_locked_file, EAGAIN)) => {
let mut content = String::new();
not_locked_file
.read_to_string(&mut content)
.context("read lock file")?;
Ok(LockFileRead::LockedByOtherProcess {
not_locked_file,
content,
})
}
Err((_, e)) => Err(e).context("flock error"),
} }
} }

View File

@@ -668,7 +668,9 @@ impl From<DownloadError> for UpdateError {
impl From<std::io::Error> for UpdateError { impl From<std::io::Error> for UpdateError {
fn from(value: std::io::Error) -> Self { fn from(value: std::io::Error) -> Self {
if let Some(nix::errno::Errno::ENOSPC) = value.raw_os_error().map(nix::errno::from_i32) { if let Some(nix::errno::Errno::ENOSPC) =
value.raw_os_error().map(nix::errno::Errno::from_raw)
{
UpdateError::NoSpace UpdateError::NoSpace
} else if value } else if value
.get_ref() .get_ref()

View File

@@ -408,7 +408,7 @@ impl OpenFiles {
/// error types may be elegible for retry. /// error types may be elegible for retry.
pub(crate) fn is_fatal_io_error(e: &std::io::Error) -> bool { pub(crate) fn is_fatal_io_error(e: &std::io::Error) -> bool {
use nix::errno::Errno::*; use nix::errno::Errno::*;
match e.raw_os_error().map(nix::errno::from_i32) { match e.raw_os_error().map(nix::errno::Errno::from_raw) {
Some(EIO) => { Some(EIO) => {
// Terminate on EIO because we no longer trust the device to store // Terminate on EIO because we no longer trust the device to store
// data safely, or to uphold persistence guarantees on fsync. // data safely, or to uphold persistence guarantees on fsync.