s0_udp 0.1.0

sync0 基于 compio 的高速 UDP 传输、线协议、自适应 Zstd 与 Dragonfly P2P 引擎
use std::{
  borrow::Cow,
  ffi::{OsStr, OsString},
  fmt::Write,
  fs,
  io::ErrorKind,
  path::{Path, PathBuf},
  str,
};

use gxhash::gxhash64;

use super::FsError;

/// 协议路径最大字节数
pub const MAX_PATH: usize = 32_767;
/// 单个文件名最大字节数
const NAME_MAX: usize = 255;
pub(crate) const TMP_EXT: &str = ".s0tmp";
pub(crate) const PART_EXT: &str = ".s0part";

/// 将相对路径转为 `/` 分隔的借用/拥有 UTF-8 字符串(Unix 下零堆分配借用)
pub(crate) fn to_slash_cow(rel: &Path) -> Cow<'_, str> {
  let s = rel.to_string_lossy();
  if s.contains('\\') {
    Cow::Owned(s.replace('\\', "/"))
  } else {
    s
  }
}

/// 将相对路径转为 `/` 分隔的 UTF-8 字符串(Unix 下零额外分配)
pub(crate) fn to_slash(rel: &Path) -> String {
  to_slash_cow(rel).into_owned()
}

fn rel_path_str(b: &[u8]) -> Result<&str, FsError> {
  if b.is_empty() || b.len() > MAX_PATH {
    return Err(FsError::BadPath("长度非法".to_owned()));
  }
  let s = str::from_utf8(b).map_err(|_| FsError::BadPath("非 UTF-8".to_owned()))?;
  if s
    .split('/')
    .any(|c| matches!(c, "" | "." | "..") || c.contains(['\0', '\\', ':']))
  {
    return Err(FsError::BadPath("空段、绝对路径、.. 或非法字符".to_owned()));
  }
  Ok(s)
}

/// 校验协议相对路径:UTF-8、非空、非绝对、无空段 / `.` / `..` / `\` / `:` / NUL
pub fn rel_path(b: &[u8]) -> Result<&Path, FsError> {
  rel_path_str(b).map(Path::new)
}

/// 在根目录 `root` 下安全解析相对路径 `rel`
pub fn safe_join(root: &Path, rel: &[u8]) -> Result<PathBuf, FsError> {
  let s = rel_path_str(rel)?;
  let mut cur = PathBuf::with_capacity(root.as_os_str().len() + 1 + s.len());
  cur.push(root);
  let mut it = s.split('/').peekable();
  let mut check_fs = true;
  while let Some(seg) = it.next() {
    cur.push(seg);
    if check_fs {
      match fs::symlink_metadata(&cur) {
        Ok(m) => {
          if m.file_type().is_symlink() {
            return Err(FsError::BadPath("路径含符号链接".to_owned()));
          }
          if it.peek().is_some() && !m.is_dir() {
            return Err(FsError::BadPath("父路径非目录".to_owned()));
          }
        }
        Err(e) if e.kind() == ErrorKind::NotFound => {
          check_fs = false;
        }
        Err(e) => return Err(e.into()),
      }
    }
  }
  Ok(cur)
}

/// 目标文件的暂存路径:与目标同目录,保证 rename 原子
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Staging {
  pub tmp: PathBuf,
  pub part: PathBuf,
}

impl Staging {
  pub fn of(path: &Path) -> Result<Self, FsError> {
    let name = path
      .file_name()
      .ok_or_else(|| FsError::BadPath("无文件名".to_owned()))?;
    let dir = path.parent().unwrap_or(Path::new(""));
    Ok(Self {
      tmp: staged(dir, name, TMP_EXT),
      part: staged(dir, name, PART_EXT),
    })
  }

  /// 删除残留暂存文件(不存在视为成功)
  pub fn clean(&self) -> Result<(), FsError> {
    remove(&self.tmp)?;
    remove(&self.part)
  }
}

fn staged(dir: &Path, name: &OsStr, ext: &str) -> PathBuf {
  let mut s = OsString::with_capacity(1 + name.len().min(NAME_MAX) + ext.len());
  s.push(".");
  if 1 + name.len() + PART_EXT.len() > NAME_MAX {
    let _ = write!(s, "s0-{:016x}", gxhash64(name.as_encoded_bytes(), 0));
  } else {
    s.push(name);
  }
  s.push(ext);
  if dir.as_os_str().is_empty() {
    PathBuf::from(s)
  } else {
    dir.join(s)
  }
}

/// 删除文件,不存在视为成功
pub fn remove(p: &Path) -> Result<(), FsError> {
  match fs::remove_file(p) {
    Err(e) if e.kind() != ErrorKind::NotFound => Err(e.into()),
    _ => Ok(()),
  }
}