Skip to content

Commit ca93253

Browse files
committed
improved locking so that file access is done through the lock guard
1 parent 828a57e commit ca93253

File tree

3 files changed

+46
-31
lines changed

3 files changed

+46
-31
lines changed

tmc-langs-cli/src/config/credentials.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::Token;
22
use anyhow::{Context, Result};
3+
use file_util::create_file_lock;
34
use serde::{Deserialize, Serialize};
4-
use std::fs::{self, File};
5+
use std::fs;
6+
use std::ops::Deref;
57
use std::path::PathBuf;
68
use tmc_langs_framework::file_util;
79

@@ -29,15 +31,15 @@ impl Credentials {
2931
return Ok(None);
3032
}
3133

32-
file_util::lock!(&credentials_path);
33-
34-
let file = File::open(&credentials_path).with_context(|| {
34+
let mut credentials_file = file_util::open_file_lock(&credentials_path)?;
35+
let guard = credentials_file.lock().with_context(|| {
3536
format!(
36-
"Failed to read credentials file at {}",
37+
"Failed to lock credentials file at {}",
3738
credentials_path.display()
3839
)
3940
})?;
40-
match serde_json::from_reader(file) {
41+
42+
match serde_json::from_reader(guard.deref()) {
4143
Ok(token) => Ok(Some(Credentials {
4244
path: credentials_path,
4345
token,
@@ -64,21 +66,16 @@ impl Credentials {
6466
pub fn save(client_name: &str, token: Token) -> Result<()> {
6567
let credentials_path = Self::get_credentials_path(client_name)?;
6668

67-
if credentials_path.exists() {
68-
// waiting for any other process working with the file to finish
69-
file_util::lock!(&credentials_path);
70-
}
71-
7269
if let Some(p) = credentials_path.parent() {
7370
fs::create_dir_all(p)
7471
.with_context(|| format!("Failed to create directory {}", p.display()))?;
7572
}
76-
let credentials_file = File::create(&credentials_path)
73+
let mut credentials_file = create_file_lock(&credentials_path)
7774
.with_context(|| format!("Failed to create file at {}", credentials_path.display()))?;
78-
file_util::lock!(&credentials_path);
75+
let guard = credentials_file.lock()?;
7976

8077
// write token
81-
if let Err(e) = serde_json::to_writer(credentials_file, &token) {
78+
if let Err(e) = serde_json::to_writer(guard.deref(), &token) {
8279
// failed to write token, removing credentials file
8380
fs::remove_file(&credentials_path).with_context(|| {
8481
format!(

tmc-langs-cli/src/config/tmc_config.rs

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
33
use anyhow::{Context, Result};
44
use serde::{Deserialize, Serialize};
5-
use std::io::Write;
5+
use std::io::{Read, Write};
66
use std::path::{Path, PathBuf};
7-
use std::{
8-
borrow::Cow,
9-
fs::{self, File},
10-
};
7+
use std::{borrow::Cow, fs};
118
use tmc_langs_framework::file_util;
129
use toml::{value::Table, Value};
1310

@@ -66,28 +63,30 @@ impl TmcConfig {
6663

6764
pub fn save(self, client_name: &str) -> Result<()> {
6865
let path = Self::get_location(client_name)?;
69-
file_util::lock!(&path);
66+
let mut lock = file_util::create_file_lock(&path)?;
67+
let mut guard = lock.lock()?;
7068

7169
let toml = toml::to_string_pretty(&self).context("Failed to serialize HashMap")?;
72-
fs::write(&path, toml.as_bytes())
70+
guard
71+
.write_all(toml.as_bytes())
7372
.with_context(|| format!("Failed to write TOML to {}", path.display()))?;
7473
Ok(())
7574
}
7675

7776
pub fn reset(client_name: &str) -> Result<()> {
7877
let path = Self::get_location(client_name)?;
79-
file_util::lock!(&path);
80-
81-
Self::init_at(client_name, &path)?;
78+
Self::init_at(client_name, &path)?; // init locks the file
8279
Ok(())
8380
}
8481

8582
pub fn load(client_name: &str) -> Result<TmcConfig> {
8683
let path = Self::get_location(client_name)?;
87-
file_util::lock!(&path);
84+
let mut lock = file_util::open_file_lock(&path)?;
85+
let mut guard = lock.lock()?;
8886

89-
let config = match fs::read(&path) {
90-
Ok(bytes) => match toml::from_slice(&bytes) {
87+
let mut buf = vec![];
88+
let config = match guard.read_to_end(&mut buf) {
89+
Ok(_) => match toml::from_slice(&buf) {
9190
Ok(config) => Ok(config),
9291
Err(_) => {
9392
log::error!(
@@ -112,10 +111,9 @@ impl TmcConfig {
112111

113112
// initializes the default configuration file at the given path
114113
fn init_at(client_name: &str, path: &Path) -> Result<TmcConfig> {
115-
file_util::lock!(path);
116-
117-
let mut file = File::create(&path)
114+
let mut lock = file_util::create_file_lock(path)
118115
.with_context(|| format!("Failed to create new config file at {}", path.display()))?;
116+
let mut guard = lock.lock()?;
119117

120118
let default_project_dir = dirs::data_local_dir()
121119
.context("Failed to find local data directory")?
@@ -134,7 +132,8 @@ impl TmcConfig {
134132
};
135133

136134
let toml = toml::to_string_pretty(&config).context("Failed to serialize config")?;
137-
file.write_all(toml.as_bytes())
135+
guard
136+
.write_all(toml.as_bytes())
138137
.with_context(|| format!("Failed to write default config to {}", path.display()))?;
139138
Ok(config)
140139
}

tmc-langs-framework/src/file_util.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Various utility functions, primarily wrapping the standard library's IO and filesystem functions
22
33
use crate::error::FileIo;
4+
use fd_lock::FdLock;
45
use std::fs::{self, File};
56
use std::io::{Read, Write};
67
use std::path::Path;
@@ -25,6 +26,13 @@ pub fn open_file<P: AsRef<Path>>(path: P) -> Result<File, FileIo> {
2526
File::open(path).map_err(|e| FileIo::FileOpen(path.to_path_buf(), e))
2627
}
2728

29+
/// Does not work on directories on Windows.
30+
pub fn open_file_lock<P: AsRef<Path>>(path: P) -> Result<FdLock<File>, FileIo> {
31+
let file = open_file(path)?;
32+
let lock = FdLock::new(file);
33+
Ok(lock)
34+
}
35+
2836
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, FileIo> {
2937
let path = path.as_ref();
3038
let mut file = open_file(path)?;
@@ -50,6 +58,17 @@ pub fn create_file<P: AsRef<Path>>(path: P) -> Result<File, FileIo> {
5058
File::create(path).map_err(|e| FileIo::FileCreate(path.to_path_buf(), e))
5159
}
5260

61+
pub fn create_file_lock<P: AsRef<Path>>(path: P) -> Result<FdLock<File>, FileIo> {
62+
if let Ok(existing) = open_file(&path) {
63+
// wait for an existing process to be done with the file before rewriting
64+
let mut lock = FdLock::new(existing);
65+
lock.lock().unwrap();
66+
}
67+
let file = create_file(path)?;
68+
let lock = FdLock::new(file);
69+
Ok(lock)
70+
}
71+
5372
pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<(), FileIo> {
5473
let path = path.as_ref();
5574
fs::remove_file(path).map_err(|e| FileIo::FileRemove(path.to_path_buf(), e))

0 commit comments

Comments
 (0)