Skip to content

Commit a9e120f

Browse files
authored
refactor: replace flock with std flock (#15935)
### What does this PR try to resolve? Replace flock with std flock. ### How to test and review this PR? Given we've supported Oracle since 2022-12 and don't want to break people, this is blocked on std supporting flock emulation via fcntl rust-lang/rust#146269
2 parents 3ceb2cb + 8ec1a52 commit a9e120f

File tree

1 file changed

+31
-190
lines changed

1 file changed

+31
-190
lines changed

src/cargo/util/flock.rs

Lines changed: 31 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//! The [`FileLock`] type represents a locked file, and provides access to the
88
//! file.
99
10+
use std::fs::TryLockError;
1011
use std::fs::{File, OpenOptions};
1112
use std::io;
1213
use std::io::{Read, Seek, SeekFrom, Write};
@@ -17,7 +18,6 @@ use crate::util::errors::CargoResult;
1718
use crate::util::style;
1819
use anyhow::Context as _;
1920
use cargo_util::paths;
20-
use sys::*;
2121

2222
/// A locked file.
2323
///
@@ -103,7 +103,7 @@ impl Write for FileLock {
103103
impl Drop for FileLock {
104104
fn drop(&mut self) {
105105
if let Some(f) = self.f.take() {
106-
if let Err(e) = unlock(&f) {
106+
if let Err(e) = f.unlock() {
107107
tracing::warn!("failed to release lock: {e:?}");
108108
}
109109
}
@@ -216,9 +216,7 @@ impl Filesystem {
216216
let mut opts = OpenOptions::new();
217217
opts.read(true).write(true).create(true);
218218
let (path, f) = self.open(path.as_ref(), &opts, true)?;
219-
acquire(gctx, msg, &path, &|| try_lock_exclusive(&f), &|| {
220-
lock_exclusive(&f)
221-
})?;
219+
acquire(gctx, msg, &path, &|| f.try_lock(), &|| f.lock())?;
222220
Ok(FileLock { f: Some(f), path })
223221
}
224222

@@ -233,7 +231,7 @@ impl Filesystem {
233231
let mut opts = OpenOptions::new();
234232
opts.read(true).write(true).create(true);
235233
let (path, f) = self.open(path.as_ref(), &opts, true)?;
236-
if try_acquire(&path, &|| try_lock_exclusive(&f))? {
234+
if try_acquire(&path, &|| f.try_lock())? {
237235
Ok(Some(FileLock { f: Some(f), path }))
238236
} else {
239237
Ok(None)
@@ -259,8 +257,8 @@ impl Filesystem {
259257
P: AsRef<Path>,
260258
{
261259
let (path, f) = self.open(path.as_ref(), &OpenOptions::new().read(true), false)?;
262-
acquire(gctx, msg, &path, &|| try_lock_shared(&f), &|| {
263-
lock_shared(&f)
260+
acquire(gctx, msg, &path, &|| f.try_lock_shared(), &|| {
261+
f.lock_shared()
264262
})?;
265263
Ok(FileLock { f: Some(f), path })
266264
}
@@ -279,8 +277,8 @@ impl Filesystem {
279277
let mut opts = OpenOptions::new();
280278
opts.read(true).write(true).create(true);
281279
let (path, f) = self.open(path.as_ref(), &opts, true)?;
282-
acquire(gctx, msg, &path, &|| try_lock_shared(&f), &|| {
283-
lock_shared(&f)
280+
acquire(gctx, msg, &path, &|| f.try_lock_shared(), &|| {
281+
f.lock_shared()
284282
})?;
285283
Ok(FileLock { f: Some(f), path })
286284
}
@@ -296,7 +294,7 @@ impl Filesystem {
296294
let mut opts = OpenOptions::new();
297295
opts.read(true).write(true).create(true);
298296
let (path, f) = self.open(path.as_ref(), &opts, true)?;
299-
if try_acquire(&path, &|| try_lock_shared(&f))? {
297+
if try_acquire(&path, &|| f.try_lock_shared())? {
300298
Ok(Some(FileLock { f: Some(f), path }))
301299
} else {
302300
Ok(None)
@@ -335,7 +333,7 @@ impl PartialEq<Filesystem> for Path {
335333
}
336334
}
337335

338-
fn try_acquire(path: &Path, lock_try: &dyn Fn() -> io::Result<()>) -> CargoResult<bool> {
336+
fn try_acquire(path: &Path, lock_try: &dyn Fn() -> Result<(), TryLockError>) -> CargoResult<bool> {
339337
// File locking on Unix is currently implemented via `flock`, which is known
340338
// to be broken on NFS. We could in theory just ignore errors that happen on
341339
// NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
@@ -352,22 +350,21 @@ fn try_acquire(path: &Path, lock_try: &dyn Fn() -> io::Result<()>) -> CargoResul
352350
}
353351

354352
match lock_try() {
355-
Ok(()) => return Ok(true),
353+
Ok(()) => Ok(true),
356354

357355
// In addition to ignoring NFS which is commonly not working we also
358356
// just ignore locking on filesystems that look like they don't
359357
// implement file locking.
360-
Err(e) if error_unsupported(&e) => return Ok(true),
358+
Err(TryLockError::Error(e)) if error_unsupported(&e) => Ok(true),
361359

362-
Err(e) => {
363-
if !error_contended(&e) {
364-
let e = anyhow::Error::from(e);
365-
let cx = format!("failed to lock file: {}", path.display());
366-
return Err(e.context(cx));
367-
}
360+
Err(TryLockError::Error(e)) => {
361+
let e = anyhow::Error::from(e);
362+
let cx = format!("failed to lock file: {}", path.display());
363+
Err(e.context(cx))
368364
}
365+
366+
Err(TryLockError::WouldBlock) => Ok(false),
369367
}
370-
Ok(false)
371368
}
372369

373370
/// Acquires a lock on a file in a "nice" manner.
@@ -389,7 +386,7 @@ fn acquire(
389386
gctx: &GlobalContext,
390387
msg: &str,
391388
path: &Path,
392-
lock_try: &dyn Fn() -> io::Result<()>,
389+
lock_try: &dyn Fn() -> Result<(), TryLockError>,
393390
lock_block: &dyn Fn() -> io::Result<()>,
394391
) -> CargoResult<()> {
395392
if cfg!(debug_assertions) {
@@ -431,176 +428,20 @@ fn is_on_nfs_mount(_path: &Path) -> bool {
431428
}
432429

433430
#[cfg(unix)]
434-
mod sys {
435-
use std::fs::File;
436-
use std::io::{Error, Result};
437-
use std::os::unix::io::AsRawFd;
438-
439-
#[cfg(not(target_os = "solaris"))]
440-
const LOCK_SH: i32 = libc::LOCK_SH;
441-
#[cfg(target_os = "solaris")]
442-
const LOCK_SH: i32 = 1;
443-
#[cfg(not(target_os = "solaris"))]
444-
const LOCK_EX: i32 = libc::LOCK_EX;
445-
#[cfg(target_os = "solaris")]
446-
const LOCK_EX: i32 = 2;
447-
#[cfg(not(target_os = "solaris"))]
448-
const LOCK_NB: i32 = libc::LOCK_NB;
449-
#[cfg(target_os = "solaris")]
450-
const LOCK_NB: i32 = 4;
451-
#[cfg(not(target_os = "solaris"))]
452-
const LOCK_UN: i32 = libc::LOCK_UN;
453-
#[cfg(target_os = "solaris")]
454-
const LOCK_UN: i32 = 8;
455-
456-
pub(super) fn lock_shared(file: &File) -> Result<()> {
457-
flock(file, LOCK_SH)
458-
}
459-
460-
pub(super) fn lock_exclusive(file: &File) -> Result<()> {
461-
flock(file, LOCK_EX)
462-
}
463-
464-
pub(super) fn try_lock_shared(file: &File) -> Result<()> {
465-
flock(file, LOCK_SH | LOCK_NB)
466-
}
467-
468-
pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
469-
flock(file, LOCK_EX | LOCK_NB)
470-
}
471-
472-
pub(super) fn unlock(file: &File) -> Result<()> {
473-
flock(file, LOCK_UN)
474-
}
475-
476-
pub(super) fn error_contended(err: &Error) -> bool {
477-
err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK)
478-
}
479-
480-
pub(super) fn error_unsupported(err: &Error) -> bool {
481-
match err.raw_os_error() {
482-
// Unfortunately, depending on the target, these may or may not be the same.
483-
// For targets in which they are the same, the duplicate pattern causes a warning.
484-
#[allow(unreachable_patterns)]
485-
Some(libc::ENOTSUP | libc::EOPNOTSUPP) => true,
486-
Some(libc::ENOSYS) => true,
487-
_ => false,
488-
}
489-
}
490-
491-
#[cfg(not(target_os = "solaris"))]
492-
fn flock(file: &File, flag: libc::c_int) -> Result<()> {
493-
let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
494-
if ret < 0 {
495-
Err(Error::last_os_error())
496-
} else {
497-
Ok(())
498-
}
499-
}
500-
501-
#[cfg(target_os = "solaris")]
502-
fn flock(file: &File, flag: libc::c_int) -> Result<()> {
503-
// Solaris lacks flock(), so try to emulate using fcntl()
504-
let mut flock = libc::flock {
505-
l_type: 0,
506-
l_whence: 0,
507-
l_start: 0,
508-
l_len: 0,
509-
l_sysid: 0,
510-
l_pid: 0,
511-
l_pad: [0, 0, 0, 0],
512-
};
513-
flock.l_type = if flag & LOCK_UN != 0 {
514-
libc::F_UNLCK
515-
} else if flag & LOCK_EX != 0 {
516-
libc::F_WRLCK
517-
} else if flag & LOCK_SH != 0 {
518-
libc::F_RDLCK
519-
} else {
520-
panic!("unexpected flock() operation")
521-
};
522-
523-
let mut cmd = libc::F_SETLKW;
524-
if (flag & LOCK_NB) != 0 {
525-
cmd = libc::F_SETLK;
526-
}
527-
528-
let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &flock) };
529-
530-
if ret < 0 {
531-
Err(Error::last_os_error())
532-
} else {
533-
Ok(())
534-
}
431+
fn error_unsupported(err: &std::io::Error) -> bool {
432+
match err.raw_os_error() {
433+
// Unfortunately, depending on the target, these may or may not be the same.
434+
// For targets in which they are the same, the duplicate pattern causes a warning.
435+
#[allow(unreachable_patterns)]
436+
Some(libc::ENOTSUP | libc::EOPNOTSUPP) => true,
437+
Some(libc::ENOSYS) => true,
438+
_ => false,
535439
}
536440
}
537441

538442
#[cfg(windows)]
539-
mod sys {
540-
use std::fs::File;
541-
use std::io::{Error, Result};
542-
use std::mem;
543-
use std::os::windows::io::AsRawHandle;
544-
545-
use windows_sys::Win32::Foundation::HANDLE;
546-
use windows_sys::Win32::Foundation::{ERROR_INVALID_FUNCTION, ERROR_LOCK_VIOLATION};
547-
use windows_sys::Win32::Storage::FileSystem::{
548-
LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile,
549-
};
550-
551-
pub(super) fn lock_shared(file: &File) -> Result<()> {
552-
lock_file(file, 0)
553-
}
554-
555-
pub(super) fn lock_exclusive(file: &File) -> Result<()> {
556-
lock_file(file, LOCKFILE_EXCLUSIVE_LOCK)
557-
}
558-
559-
pub(super) fn try_lock_shared(file: &File) -> Result<()> {
560-
lock_file(file, LOCKFILE_FAIL_IMMEDIATELY)
561-
}
562-
563-
pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
564-
lock_file(file, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY)
565-
}
566-
567-
pub(super) fn error_contended(err: &Error) -> bool {
568-
err.raw_os_error()
569-
.map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32)
570-
}
571-
572-
pub(super) fn error_unsupported(err: &Error) -> bool {
573-
err.raw_os_error()
574-
.map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32)
575-
}
576-
577-
pub(super) fn unlock(file: &File) -> Result<()> {
578-
unsafe {
579-
let ret = UnlockFile(file.as_raw_handle() as HANDLE, 0, 0, !0, !0);
580-
if ret == 0 {
581-
Err(Error::last_os_error())
582-
} else {
583-
Ok(())
584-
}
585-
}
586-
}
587-
588-
fn lock_file(file: &File, flags: u32) -> Result<()> {
589-
unsafe {
590-
let mut overlapped = mem::zeroed();
591-
let ret = LockFileEx(
592-
file.as_raw_handle() as HANDLE,
593-
flags,
594-
0,
595-
!0,
596-
!0,
597-
&mut overlapped,
598-
);
599-
if ret == 0 {
600-
Err(Error::last_os_error())
601-
} else {
602-
Ok(())
603-
}
604-
}
605-
}
443+
fn error_unsupported(err: &std::io::Error) -> bool {
444+
use windows_sys::Win32::Foundation::ERROR_INVALID_FUNCTION;
445+
err.raw_os_error()
446+
.map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32)
606447
}

0 commit comments

Comments
 (0)