Skip to content

Commit effe13b

Browse files
committed
[rust] Use file lock to protect concurrent accesses to cache
1 parent 744e7d6 commit effe13b

File tree

4 files changed

+184
-23
lines changed

4 files changed

+184
-23
lines changed

rust/Cargo.Bazel.lock

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"checksum": "778a62d50d430548a64ececb679a81afba2ffa2ac1553d2e95807253aa723ac7",
2+
"checksum": "ba73e39704c230ab36d29779338a27060fadfde0a7e9b957428668f5fdb9c271",
33
"crates": {
44
"addr2line 0.21.0": {
55
"name": "addr2line",
@@ -4539,6 +4539,100 @@
45394539
],
45404540
"license_file": "LICENSE-APACHE"
45414541
},
4542+
"fs2 0.4.3": {
4543+
"name": "fs2",
4544+
"version": "0.4.3",
4545+
"package_url": "https://github.com/danburkert/fs2-rs",
4546+
"repository": {
4547+
"Http": {
4548+
"url": "https://static.crates.io/crates/fs2/0.4.3/download",
4549+
"sha256": "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
4550+
}
4551+
},
4552+
"targets": [
4553+
{
4554+
"Library": {
4555+
"crate_name": "fs2",
4556+
"crate_root": "src/lib.rs",
4557+
"srcs": {
4558+
"allow_empty": true,
4559+
"include": [
4560+
"**/*.rs"
4561+
]
4562+
}
4563+
}
4564+
}
4565+
],
4566+
"library_target_name": "fs2",
4567+
"common_attrs": {
4568+
"compile_data_glob": [
4569+
"**"
4570+
],
4571+
"deps": {
4572+
"common": [],
4573+
"selects": {
4574+
"cfg(unix)": [
4575+
{
4576+
"id": "libc 0.2.168",
4577+
"target": "libc"
4578+
}
4579+
],
4580+
"cfg(windows)": [
4581+
{
4582+
"id": "winapi 0.3.9",
4583+
"target": "winapi"
4584+
}
4585+
]
4586+
}
4587+
},
4588+
"edition": "2015",
4589+
"version": "0.4.3"
4590+
},
4591+
"license": "MIT/Apache-2.0",
4592+
"license_ids": [
4593+
"Apache-2.0",
4594+
"MIT"
4595+
],
4596+
"license_file": "LICENSE-APACHE"
4597+
},
4598+
"fs_extra 1.3.0": {
4599+
"name": "fs_extra",
4600+
"version": "1.3.0",
4601+
"package_url": "https://github.com/webdesus/fs_extra",
4602+
"repository": {
4603+
"Http": {
4604+
"url": "https://static.crates.io/crates/fs_extra/1.3.0/download",
4605+
"sha256": "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
4606+
}
4607+
},
4608+
"targets": [
4609+
{
4610+
"Library": {
4611+
"crate_name": "fs_extra",
4612+
"crate_root": "src/lib.rs",
4613+
"srcs": {
4614+
"allow_empty": true,
4615+
"include": [
4616+
"**/*.rs"
4617+
]
4618+
}
4619+
}
4620+
}
4621+
],
4622+
"library_target_name": "fs_extra",
4623+
"common_attrs": {
4624+
"compile_data_glob": [
4625+
"**"
4626+
],
4627+
"edition": "2018",
4628+
"version": "1.3.0"
4629+
},
4630+
"license": "MIT",
4631+
"license_ids": [
4632+
"MIT"
4633+
],
4634+
"license_file": "LICENSE"
4635+
},
45424636
"futures 0.3.30": {
45434637
"name": "futures",
45444638
"version": "0.3.30",
@@ -13320,6 +13414,14 @@
1332013414
"id": "flate2 1.0.35",
1332113415
"target": "flate2"
1332213416
},
13417+
{
13418+
"id": "fs2 0.4.3",
13419+
"target": "fs2"
13420+
},
13421+
{
13422+
"id": "fs_extra 1.3.0",
13423+
"target": "fs_extra"
13424+
},
1332313425
{
1332413426
"id": "infer 0.16.0",
1332513427
"target": "infer"
@@ -18553,7 +18655,12 @@
1855318655
],
1855418656
"crate_features": {
1855518657
"common": [
18556-
"winbase"
18658+
"fileapi",
18659+
"handleapi",
18660+
"processthreadsapi",
18661+
"std",
18662+
"winbase",
18663+
"winerror"
1855718664
],
1855818665
"selects": {}
1855918666
},
@@ -22185,6 +22292,8 @@
2218522292
"env_logger 0.11.5",
2218622293
"exitcode 1.1.2",
2218722294
"flate2 1.0.35",
22295+
"fs2 0.4.3",
22296+
"fs_extra 1.3.0",
2218822297
"infer 0.16.0",
2218922298
"log 0.4.22",
2219022299
"regex 1.11.1",

rust/Cargo.lock

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ debpkg = "0.6.0"
3636
anyhow = { version = "1.0.94", default-features = false, features = ["backtrace", "std"] }
3737
apple-flat-package = "0.20.0"
3838
which = "7.0.0"
39+
fs2 = "0.4.3"
40+
fs_extra = "1.3.0"
3941

4042
[dev-dependencies]
4143
assert_cmd = "2.0.16"

rust/src/files.rs

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ use apple_flat_package::PkgReader;
2727
use bzip2::read::BzDecoder;
2828
use directories::BaseDirs;
2929
use flate2::read::GzDecoder;
30+
use fs2::FileExt;
31+
use fs_extra::dir::{move_dir, CopyOptions};
3032
use regex::Regex;
3133
use std::fs;
3234
use std::fs::File;
@@ -53,6 +55,7 @@ const MSI: &str = "msi";
5355
const XZ: &str = "xz";
5456
const SEVEN_ZIP_HEADER: &[u8; 6] = b"7z\xBC\xAF\x27\x1C";
5557
const UNCOMPRESS_MACOS_ERR_MSG: &str = "{} files are only supported in macOS";
58+
const LOCK_FILE: &str = "sm.lock";
5659

5760
#[derive(Hash, Eq, PartialEq, Debug)]
5861
pub struct BrowserPath {
@@ -69,6 +72,35 @@ impl BrowserPath {
6972
}
7073
}
7174

75+
pub struct Lock {
76+
file: File,
77+
path: PathBuf,
78+
}
79+
80+
impl Lock {
81+
fn acquire(log: &Logger, target: &Path, single_file: Option<String>) -> Result<Self, Error> {
82+
let lock_folder = if single_file.is_some() {
83+
create_parent_path_if_not_exists(target)?;
84+
target.parent().unwrap()
85+
} else {
86+
create_path_if_not_exists(target)?;
87+
target
88+
};
89+
let path = lock_folder.join(LOCK_FILE);
90+
let file = File::create(&path)?;
91+
92+
log.trace(format!("Using lock file at {}", path.display()));
93+
file.lock_exclusive().unwrap_or_default();
94+
95+
Ok(Self { file, path })
96+
}
97+
98+
fn release(&mut self) {
99+
self.file.unlock().unwrap_or_default();
100+
fs::remove_file(&self.path).unwrap_or_default();
101+
}
102+
}
103+
72104
pub fn create_parent_path_if_not_exists(path: &Path) -> Result<(), Error> {
73105
if let Some(p) = path.parent() {
74106
create_path_if_not_exists(p)?;
@@ -120,6 +152,9 @@ pub fn uncompress(
120152
extension
121153
));
122154

155+
// Acquire file lock to prevent race conditions accessing the cache folder by concurrent SM processes
156+
let mut lock = Lock::acquire(log, target, single_file.clone())?;
157+
123158
if extension.eq_ignore_ascii_case(ZIP) {
124159
unzip(compressed_file, target, log, single_file)?
125160
} else if extension.eq_ignore_ascii_case(GZ) {
@@ -143,7 +178,7 @@ pub fn uncompress(
143178
} else if extension.eq_ignore_ascii_case(EXE) {
144179
uncompress_sfx(compressed_file, target, log)?
145180
} else if extension.eq_ignore_ascii_case(DEB) {
146-
uncompress_deb(compressed_file, target, log, os, volume.unwrap_or_default())?
181+
uncompress_deb(compressed_file, target, log, volume.unwrap_or_default())?
147182
} else if extension.eq_ignore_ascii_case(MSI) {
148183
install_msi(compressed_file, log, os)?
149184
} else if extension.eq_ignore_ascii_case(XML) || extension.eq_ignore_ascii_case(HTML) {
@@ -158,6 +193,8 @@ pub fn uncompress(
158193
extension
159194
)));
160195
}
196+
197+
lock.release();
161198
Ok(())
162199
}
163200

@@ -176,15 +213,23 @@ pub fn uncompress_sfx(compressed_file: &str, target: &Path, log: &Logger) -> Res
176213
sevenz_rust::decompress(file_reader, zip_parent).unwrap();
177214

178215
let zip_parent_str = path_to_string(zip_parent);
179-
let target_str = path_to_string(target);
180216
let core_str = format!(r"{}\core", zip_parent_str);
217+
move_folder_content(&core_str, &target, &log)?;
218+
219+
Ok(())
220+
}
221+
222+
pub fn move_folder_content(source: &str, target: &Path, log: &Logger) -> Result<(), Error> {
181223
log.trace(format!(
182-
"Moving extracted files and folders from {} to {}",
183-
core_str, target_str
224+
"Moving files and folders from {} to {}",
225+
source,
226+
target.display()
184227
));
185228
create_parent_path_if_not_exists(target)?;
186-
fs::rename(&core_str, &target_str)?;
187-
229+
let mut options = CopyOptions::new();
230+
options.content_only = true;
231+
options.skip_exist = true;
232+
move_dir(source, target, &options)?;
188233
Ok(())
189234
}
190235

@@ -261,7 +306,6 @@ pub fn uncompress_deb(
261306
compressed_file: &str,
262307
target: &Path,
263308
log: &Logger,
264-
os: &str,
265309
label: &str,
266310
) -> Result<(), Error> {
267311
let zip_parent = Path::new(compressed_file).parent().unwrap();
@@ -276,20 +320,8 @@ pub fn uncompress_deb(
276320
deb_pkg.data()?.unpack(zip_parent)?;
277321

278322
let zip_parent_str = path_to_string(zip_parent);
279-
let target_str = path_to_string(target);
280323
let opt_edge_str = format!("{}/opt/microsoft/{}", zip_parent_str, label);
281-
let opt_edge_mv = format!("mv {} {}", opt_edge_str, target_str);
282-
let command = Command::new_single(opt_edge_mv.clone());
283-
log.trace(format!(
284-
"Moving extracted files and folders from {} to {}",
285-
opt_edge_str, target_str
286-
));
287-
create_parent_path_if_not_exists(target)?;
288-
run_shell_command_by_os(os, command)?;
289-
let target_path = Path::new(target);
290-
if target_path.parent().unwrap().read_dir()?.next().is_none() {
291-
fs::rename(&opt_edge_str, &target_str)?;
292-
}
324+
move_folder_content(&opt_edge_str, &target, &log)?;
293325

294326
Ok(())
295327
}

0 commit comments

Comments
 (0)