From 219d81f19bd902e280fe6f5f40aded185db7ba0a Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Thu, 14 Apr 2022 18:29:21 -0400 Subject: [PATCH] separate flock implementations into separate modules --- compiler/rustc_data_structures/src/flock.rs | 225 +----------------- .../rustc_data_structures/src/flock/linux.rs | 40 ++++ .../rustc_data_structures/src/flock/unix.rs | 51 ++++ .../src/flock/unsupported.rs | 16 ++ .../src/flock/windows.rs | 77 ++++++ 5 files changed, 194 insertions(+), 215 deletions(-) create mode 100644 compiler/rustc_data_structures/src/flock/linux.rs create mode 100644 compiler/rustc_data_structures/src/flock/unix.rs create mode 100644 compiler/rustc_data_structures/src/flock/unsupported.rs create mode 100644 compiler/rustc_data_structures/src/flock/windows.rs diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index 293ef4caac4..e395d8dbbbf 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -7,225 +7,20 @@ #![allow(non_camel_case_types)] #![allow(nonstandard_style)] -use std::fs::{File, OpenOptions}; -use std::io; -use std::path::Path; - cfg_if! { - // We use `flock` rather than `fcntl` on Linux, because WSL1 does not support - // `fcntl`-style advisory locks properly (rust-lang/rust#72157). - // - // For other Unix targets we still use `fcntl` because it's more portable than - // `flock`. if #[cfg(target_os = "linux")] { - use std::os::unix::prelude::*; - - #[derive(Debug)] - pub struct Lock { - _file: File, - } - - impl Lock { - pub fn new(p: &Path, - wait: bool, - create: bool, - exclusive: bool) - -> io::Result { - let file = OpenOptions::new() - .read(true) - .write(true) - .create(create) - .mode(libc::S_IRWXU as u32) - .open(p)?; - - let mut operation = if exclusive { - libc::LOCK_EX - } else { - libc::LOCK_SH - }; - if !wait { - operation |= libc::LOCK_NB - } - - let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; - if ret == -1 { - Err(io::Error::last_os_error()) - } else { - Ok(Lock { _file: file }) - } - } - - pub fn error_unsupported(err: &io::Error) -> bool { - matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS)) - } - } - - // Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. Lock acquired by - // `flock` is associated with the file descriptor and closing the file release it - // automatically. + mod linux; + use linux as imp; } else if #[cfg(unix)] { - use std::mem; - use std::os::unix::prelude::*; - - #[derive(Debug)] - pub struct Lock { - file: File, - } - - impl Lock { - pub fn new(p: &Path, - wait: bool, - create: bool, - exclusive: bool) - -> io::Result { - let file = OpenOptions::new() - .read(true) - .write(true) - .create(create) - .mode(libc::S_IRWXU as u32) - .open(p)?; - - let lock_type = if exclusive { - libc::F_WRLCK - } else { - libc::F_RDLCK - }; - - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = lock_type as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - flock.l_start = 0; - flock.l_len = 0; - - let cmd = if wait { libc::F_SETLKW } else { libc::F_SETLK }; - let ret = unsafe { - libc::fcntl(file.as_raw_fd(), cmd, &flock) - }; - if ret == -1 { - Err(io::Error::last_os_error()) - } else { - Ok(Lock { file }) - } - } - - pub fn error_unsupported(err: &io::Error) -> bool { - matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS)) - } - } - - impl Drop for Lock { - fn drop(&mut self) { - let mut flock: libc::flock = unsafe { mem::zeroed() }; - flock.l_type = libc::F_UNLCK as libc::c_short; - flock.l_whence = libc::SEEK_SET as libc::c_short; - flock.l_start = 0; - flock.l_len = 0; - - unsafe { - libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock); - } - } - } + mod unix; + use unix as imp; } else if #[cfg(windows)] { - use std::mem; - use std::os::windows::prelude::*; - - use winapi::shared::winerror::ERROR_INVALID_FUNCTION; - use winapi::um::minwinbase::{OVERLAPPED, LOCKFILE_FAIL_IMMEDIATELY, LOCKFILE_EXCLUSIVE_LOCK}; - use winapi::um::fileapi::LockFileEx; - use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE}; - - #[derive(Debug)] - pub struct Lock { - _file: File, - } - - impl Lock { - pub fn new(p: &Path, - wait: bool, - create: bool, - exclusive: bool) - -> io::Result { - assert!(p.parent().unwrap().exists(), - "Parent directory of lock-file must exist: {}", - p.display()); - - let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; - - let mut open_options = OpenOptions::new(); - open_options.read(true) - .share_mode(share_mode); - - if create { - open_options.create(true) - .write(true); - } - - debug!("attempting to open lock file `{}`", p.display()); - let file = match open_options.open(p) { - Ok(file) => { - debug!("lock file opened successfully"); - file - } - Err(err) => { - debug!("error opening lock file: {}", err); - return Err(err) - } - }; - - let ret = unsafe { - let mut overlapped: OVERLAPPED = mem::zeroed(); - - let mut dwFlags = 0; - if !wait { - dwFlags |= LOCKFILE_FAIL_IMMEDIATELY; - } - - if exclusive { - dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; - } - - debug!("attempting to acquire lock on lock file `{}`", - p.display()); - LockFileEx(file.as_raw_handle(), - dwFlags, - 0, - 0xFFFF_FFFF, - 0xFFFF_FFFF, - &mut overlapped) - }; - if ret == 0 { - let err = io::Error::last_os_error(); - debug!("failed acquiring file lock: {}", err); - Err(err) - } else { - debug!("successfully acquired lock"); - Ok(Lock { _file: file }) - } - } - - pub fn error_unsupported(err: &io::Error) -> bool { - err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32) - } - } - - // Note that we don't need a Drop impl on the Windows: The file is unlocked - // automatically when it's closed. + mod windows; + use windows as imp; } else { - #[derive(Debug)] - pub struct Lock(()); - - impl Lock { - pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool) - -> io::Result - { - let msg = "file locks not supported on this platform"; - Err(io::Error::new(io::ErrorKind::Other, msg)) - } - - pub fn error_unsupported(_err: &io::Error) -> bool { - true - } - } + mod unsupported; + use unsupported as imp; } } + +pub use imp::Lock; diff --git a/compiler/rustc_data_structures/src/flock/linux.rs b/compiler/rustc_data_structures/src/flock/linux.rs new file mode 100644 index 00000000000..bb3ecfbc370 --- /dev/null +++ b/compiler/rustc_data_structures/src/flock/linux.rs @@ -0,0 +1,40 @@ +//! We use `flock` rather than `fcntl` on Linux, because WSL1 does not support +//! `fcntl`-style advisory locks properly (rust-lang/rust#72157). For other Unix +//! targets we still use `fcntl` because it's more portable than `flock`. + +use std::fs::{File, OpenOptions}; +use std::io; +use std::os::unix::prelude::*; +use std::path::Path; + +#[derive(Debug)] +pub struct Lock { + _file: File, +} + +impl Lock { + pub fn new(p: &Path, wait: bool, create: bool, exclusive: bool) -> io::Result { + let file = OpenOptions::new() + .read(true) + .write(true) + .create(create) + .mode(libc::S_IRWXU as u32) + .open(p)?; + + let mut operation = if exclusive { libc::LOCK_EX } else { libc::LOCK_SH }; + if !wait { + operation |= libc::LOCK_NB + } + + let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; + if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(Lock { _file: file }) } + } + + pub fn error_unsupported(err: &io::Error) -> bool { + matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS)) + } +} + +// Note that we don't need a Drop impl to execute `flock(fd, LOCK_UN)`. A lock acquired by +// `flock` is associated with the file descriptor and closing the file releases it +// automatically. diff --git a/compiler/rustc_data_structures/src/flock/unix.rs b/compiler/rustc_data_structures/src/flock/unix.rs new file mode 100644 index 00000000000..4e5297d582e --- /dev/null +++ b/compiler/rustc_data_structures/src/flock/unix.rs @@ -0,0 +1,51 @@ +use std::fs::{File, OpenOptions}; +use std::io; +use std::mem; +use std::os::unix::prelude::*; +use std::path::Path; + +#[derive(Debug)] +pub struct Lock { + file: File, +} + +impl Lock { + pub fn new(p: &Path, wait: bool, create: bool, exclusive: bool) -> io::Result { + let file = OpenOptions::new() + .read(true) + .write(true) + .create(create) + .mode(libc::S_IRWXU as u32) + .open(p)?; + + let lock_type = if exclusive { libc::F_WRLCK } else { libc::F_RDLCK }; + + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = lock_type as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + flock.l_start = 0; + flock.l_len = 0; + + let cmd = if wait { libc::F_SETLKW } else { libc::F_SETLK }; + let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &flock) }; + if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(Lock { file }) } + } + + pub fn error_unsupported(err: &io::Error) -> bool { + matches!(err.raw_os_error(), Some(libc::ENOTSUP) | Some(libc::ENOSYS)) + } +} + +impl Drop for Lock { + fn drop(&mut self) { + let mut flock: libc::flock = unsafe { mem::zeroed() }; + flock.l_type = libc::F_UNLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + flock.l_start = 0; + flock.l_len = 0; + + unsafe { + libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock); + } + } +} diff --git a/compiler/rustc_data_structures/src/flock/unsupported.rs b/compiler/rustc_data_structures/src/flock/unsupported.rs new file mode 100644 index 00000000000..9245fca373d --- /dev/null +++ b/compiler/rustc_data_structures/src/flock/unsupported.rs @@ -0,0 +1,16 @@ +use std::io; +use std::path::Path; + +#[derive(Debug)] +pub struct Lock(()); + +impl Lock { + pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool) -> io::Result { + let msg = "file locks not supported on this platform"; + Err(io::Error::new(io::ErrorKind::Other, msg)) + } + + pub fn error_unsupported(_err: &io::Error) -> bool { + true + } +} diff --git a/compiler/rustc_data_structures/src/flock/windows.rs b/compiler/rustc_data_structures/src/flock/windows.rs new file mode 100644 index 00000000000..43e6caaa18d --- /dev/null +++ b/compiler/rustc_data_structures/src/flock/windows.rs @@ -0,0 +1,77 @@ +use std::fs::{File, OpenOptions}; +use std::io; +use std::mem; +use std::os::windows::prelude::*; +use std::path::Path; + +use winapi::shared::winerror::ERROR_INVALID_FUNCTION; +use winapi::um::fileapi::LockFileEx; +use winapi::um::minwinbase::{LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, OVERLAPPED}; +use winapi::um::winnt::{FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE}; + +#[derive(Debug)] +pub struct Lock { + _file: File, +} + +impl Lock { + pub fn new(p: &Path, wait: bool, create: bool, exclusive: bool) -> io::Result { + assert!( + p.parent().unwrap().exists(), + "Parent directory of lock-file must exist: {}", + p.display() + ); + + let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; + + let mut open_options = OpenOptions::new(); + open_options.read(true).share_mode(share_mode); + + if create { + open_options.create(true).write(true); + } + + debug!("attempting to open lock file `{}`", p.display()); + let file = match open_options.open(p) { + Ok(file) => { + debug!("lock file opened successfully"); + file + } + Err(err) => { + debug!("error opening lock file: {}", err); + return Err(err); + } + }; + + let ret = unsafe { + let mut overlapped: OVERLAPPED = mem::zeroed(); + + let mut dwFlags = 0; + if !wait { + dwFlags |= LOCKFILE_FAIL_IMMEDIATELY; + } + + if exclusive { + dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; + } + + debug!("attempting to acquire lock on lock file `{}`", p.display()); + LockFileEx(file.as_raw_handle(), dwFlags, 0, 0xFFFF_FFFF, 0xFFFF_FFFF, &mut overlapped) + }; + if ret == 0 { + let err = io::Error::last_os_error(); + debug!("failed acquiring file lock: {}", err); + Err(err) + } else { + debug!("successfully acquired lock"); + Ok(Lock { _file: file }) + } + } + + pub fn error_unsupported(err: &io::Error) -> bool { + err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32) + } +} + +// Note that we don't need a Drop impl on Windows: The file is unlocked +// automatically when it's closed.