chown only a lock file swarmctl just created and refuse a hard-linked one
This commit is contained in:
parent
be744d45b9
commit
6b93839290
3 changed files with 149 additions and 34 deletions
|
|
@ -19,7 +19,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fs::{self, File, Permissions};
|
||||
use std::io::Write as _;
|
||||
use std::os::unix::fs::{OpenOptionsExt as _, PermissionsExt as _};
|
||||
use std::os::unix::fs::PermissionsExt as _;
|
||||
use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
|
|
@ -305,15 +305,19 @@ pub async fn lock_users_file(users_file: &Path, wait: Duration) -> Result<File>
|
|||
lock_name.push(".lock");
|
||||
let lock_path = dir.join(lock_name);
|
||||
|
||||
let flags = rustix::fs::OFlags::NOFOLLOW | rustix::fs::OFlags::NONBLOCK;
|
||||
let file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(false)
|
||||
.mode(0o600)
|
||||
.custom_flags(flags.bits().cast_signed())
|
||||
.open(&lock_path)
|
||||
.with_context(|| format!("opening {}", lock_path.display()))?;
|
||||
// Read-only: `flock` needs no write access, and a lock file `swarmctl`
|
||||
// (root) left behind readable but not writable must still be lockable.
|
||||
let fd = rustix::fs::open(
|
||||
&lock_path,
|
||||
rustix::fs::OFlags::RDONLY
|
||||
| rustix::fs::OFlags::CREATE
|
||||
| rustix::fs::OFlags::NOFOLLOW
|
||||
| rustix::fs::OFlags::NONBLOCK
|
||||
| rustix::fs::OFlags::CLOEXEC,
|
||||
rustix::fs::Mode::RUSR | rustix::fs::Mode::WUSR,
|
||||
)
|
||||
.with_context(|| format!("opening {}", lock_path.display()))?;
|
||||
let file = File::from(fd);
|
||||
if !file
|
||||
.metadata()
|
||||
.with_context(|| format!("stat {}", lock_path.display()))?
|
||||
|
|
@ -741,6 +745,23 @@ some_future_top_level_key: 7
|
|||
fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
|
||||
/// A lock file left behind without write access (root-owned after a crash
|
||||
/// in `swarmctl`) can still be locked: `flock` needs only a read fd.
|
||||
#[tokio::test]
|
||||
async fn a_read_only_lock_file_can_still_be_locked() {
|
||||
let dir = tempdir();
|
||||
let users_file = dir.join("users.yml");
|
||||
let lock_path = dir.join("users.yml.lock");
|
||||
fs::write(&lock_path, "").unwrap();
|
||||
fs::set_permissions(&lock_path, Permissions::from_mode(0o444)).unwrap();
|
||||
|
||||
lock_users_file(&users_file, Duration::from_secs(1))
|
||||
.await
|
||||
.expect("lock a read-only lock file");
|
||||
|
||||
fs::remove_dir_all(&dir).ok();
|
||||
}
|
||||
|
||||
/// Two read-modify-writes that each sit between load and publish long
|
||||
/// enough for the other to load: without the lock the second `publish`
|
||||
/// drops the first user.
|
||||
|
|
|
|||
Loading…
Reference in a new issue