fix(security): [#1981] 修复原子写入fsync、优化错误提示及平台兼容
This commit is contained in:
Generated
+1
-1
@@ -261,7 +261,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "l-s"
|
||||
version = "0.5.1"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
|
||||
+4
-4
@@ -206,16 +206,16 @@ fn write_atomic(path: &Path, contents: &str) -> Result<()> {
|
||||
let tmp_path = parent.join(format!(".l-s-tmp-{}-{nanos}", std::process::id()));
|
||||
|
||||
let result = (|| -> Result<()> {
|
||||
let mut file = OpenOptions::new()
|
||||
let mut temp = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(&tmp_path)
|
||||
.with_context(|| format!("无法创建临时文件: {}", tmp_path.display()))?;
|
||||
file.write_all(contents.as_bytes())
|
||||
temp.write_all(contents.as_bytes())
|
||||
.with_context(|| format!("无法写入临时文件: {}", tmp_path.display()))?;
|
||||
file.sync_all()
|
||||
temp.sync_all()
|
||||
.with_context(|| format!("无法同步临时文件: {}", tmp_path.display()))?;
|
||||
drop(file);
|
||||
drop(temp);
|
||||
|
||||
fs::rename(&tmp_path, path).with_context(|| {
|
||||
format!(
|
||||
|
||||
+29
-10
@@ -16,10 +16,12 @@ use crate::constants::{DEFAULT_BUFFER_SIZE, HEAD_115_BYTES, HEAD_BAIDU_BYTES};
|
||||
use crate::head_hash::{calc_head_115, calc_head_baidu, HeadChunk};
|
||||
use crate::utils::{basename, friendly_size, hex_upper};
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[cfg(all(not(unix), not(windows)))]
|
||||
use std::fs;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::fs::{MetadataExt, OpenOptionsExt};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FileMeta {
|
||||
@@ -166,18 +168,13 @@ where
|
||||
F1: FnMut(u64),
|
||||
F2: FnMut(),
|
||||
{
|
||||
let info = file
|
||||
.metadata()
|
||||
.with_context(|| format!("无法读取文件信息: {}", path.display()))?;
|
||||
if !info.is_file() {
|
||||
return Err(anyhow!("{} 不是文件", path.display()));
|
||||
}
|
||||
|
||||
let mut buffer = vec![0u8; DEFAULT_BUFFER_SIZE];
|
||||
let mut hasher = Xxh3::new();
|
||||
|
||||
loop {
|
||||
let read_len = file.read(&mut buffer)?;
|
||||
let read_len = file
|
||||
.read(&mut buffer)
|
||||
.with_context(|| format!("无法读取文件: {}", path.display()))?;
|
||||
if read_len == 0 {
|
||||
break;
|
||||
}
|
||||
@@ -210,7 +207,29 @@ fn open_file_nofollow(path: &Path) -> Result<File> {
|
||||
.with_context(|| format!("无法打开文件: {}", path.display()))
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
#[cfg(windows)]
|
||||
fn open_file_nofollow(path: &Path) -> Result<File> {
|
||||
const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;
|
||||
const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
|
||||
|
||||
let mut options = OpenOptions::new();
|
||||
options.read(true);
|
||||
options.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT);
|
||||
let file = options
|
||||
.open(path)
|
||||
.with_context(|| format!("无法打开文件: {}", path.display()))?;
|
||||
|
||||
let info = file
|
||||
.metadata()
|
||||
.with_context(|| format!("无法读取文件信息: {}", path.display()))?;
|
||||
if info.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
||||
return Err(anyhow!("不支持扫描符号链接或重解析点: {}", path.display()));
|
||||
}
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
#[cfg(all(not(unix), not(windows)))]
|
||||
fn open_file_nofollow(path: &Path) -> Result<File> {
|
||||
let info = fs::symlink_metadata(path)
|
||||
.with_context(|| format!("无法读取文件信息: {}", path.display()))?;
|
||||
|
||||
+16
-4
@@ -454,7 +454,7 @@ mod unix_walk {
|
||||
.metadata()
|
||||
.with_context(|| format!("无法读取文件信息: {}", path.display()))?;
|
||||
if !info.is_file() {
|
||||
return Err(anyhow!("{} 不是文件", path.display()));
|
||||
return Err(anyhow!("{} 打开后不是普通文件", path.display()));
|
||||
}
|
||||
if !stat_matches(&info, &entry.stat) {
|
||||
return Err(anyhow!("扫描期间文件被替换: {}", path.display()));
|
||||
@@ -545,7 +545,11 @@ mod unix_walk {
|
||||
}
|
||||
EntryKind::Other => {
|
||||
if !should_skip_file(&name) {
|
||||
return Err(anyhow!("{} 不是文件", full_path.display()));
|
||||
return Err(anyhow!(
|
||||
"不支持的特殊文件: {} (mode {:o})",
|
||||
full_path.display(),
|
||||
entry.stat.st_mode
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -638,7 +642,11 @@ mod unix_walk {
|
||||
}
|
||||
EntryKind::Other => {
|
||||
if !should_skip_file(&name) {
|
||||
return Err(anyhow!("{} 不是文件", full_path.display()));
|
||||
return Err(anyhow!(
|
||||
"不支持的特殊文件: {} (mode {:o})",
|
||||
full_path.display(),
|
||||
entry.stat.st_mode
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -685,7 +693,11 @@ mod unix_walk {
|
||||
}
|
||||
EntryKind::Other => {
|
||||
if !should_skip_file(&name) {
|
||||
return Err(anyhow!("{} 不是文件", full_path.display()));
|
||||
return Err(anyhow!(
|
||||
"不支持的特殊文件: {} (mode {:o})",
|
||||
full_path.display(),
|
||||
entry.stat.st_mode
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user