apt_mirror_check/
lib.rs

1use crate::bad_action::BadAction;
2use crate::distribution::{check_distribution};
3use regex::Regex;
4use std::env::current_dir;
5use std::fs::File;
6use std::io::{BufRead, BufReader};
7use std::path::{Path, PathBuf};
8use walkdir::WalkDir;
9
10mod bad_action;
11mod distribution;
12mod file_attr;
13mod packages;
14mod release;
15
16pub mod log_config;
17
18pub fn check_repository(repository_dir: &Path, delete: bool) {
19    log::info!(repository_dir:?, delete; "start checking repository");
20
21    let bad_action = BadAction::new(delete);
22
23    let dists_dir = repository_dir.join("dists");
24    let pool_dir = repository_dir.join("pool");
25    if dists_dir.is_dir() && pool_dir.is_dir() {
26        // normal repo
27        if let Ok(reader) = dists_dir.read_dir() {
28            for entry in reader {
29                if let Ok(entry) = entry
30                    && let Ok(file_type) = entry.file_type()
31                    && file_type.is_dir()
32                    && !file_type.is_symlink()
33                {
34                    check_distribution(repository_dir, &entry.path(), &bad_action);
35                }
36            }
37        }
38    } else {
39        // flat repo, layout files like a distribution
40        check_distribution(repository_dir, repository_dir, &bad_action);
41    }
42}
43
44fn check_site(site_dir: &Path, delete: bool) {
45    log::info!(site_dir:?, delete; "start checking site");
46
47    // subdirectories of site may not be a repository
48    // for example https://mirrors.tsinghua.edu.cn/raspbian/raspbian/
49    //
50    // NOTE: flat repository is not supported, please use check_repository directly
51
52    WalkDir::new(site_dir)
53        .into_iter()
54        .filter_map(|entry| entry.ok())
55        .filter(|entry| entry.file_type().is_dir() && entry.file_name().to_str() == Some("dists"))
56        .filter_map(|entry| entry.path().parent().map(|p| p.to_path_buf()))
57        .for_each(|repository_dir| {
58        check_repository(&repository_dir, delete);
59    });
60}
61
62/// check mirror
63///
64/// # Arguments
65///
66/// * `base_dir`: base directory of apt-mirror where mirror/skel/var lives
67pub fn check_mirror<P>(base_dir: P, delete: bool)
68where
69    P: AsRef<Path>,
70{
71    let base_dir = base_dir.as_ref();
72    let mirror_dir = base_dir.join("mirror");
73
74    log::info!(base_dir:?, delete; "start checking mirror");
75
76    match mirror_dir.read_dir() {
77        Ok(reader) => {
78            for entry in reader {
79                if let Ok(entry) = entry
80                    && let Ok(file_type) = entry.file_type()
81                {
82                    if file_type.is_dir() {
83                        check_site(&entry.path(), delete);
84                    }
85                }
86            }
87        }
88        Err(e) => {
89            log::error!(mirror_dir:?, e:err; "mirror directory error, maybe wrong base_dir specified");
90        }
91    }
92}
93
94fn find_base_dir_in_apt_mirror_config() -> Option<PathBuf> {
95    if let Ok(file) = File::open("/etc/apt/mirror.list") {
96        let re = Regex::new(r"set\s+base_path\s+([\w/-]+)").unwrap();
97
98        let reader = BufReader::new(file);
99
100        for line_result in reader.lines() {
101            if let Ok(line) = line_result {
102                if let Some(m) = re.captures(&line) {
103                    return Some(PathBuf::from(m.get(1).unwrap().as_str()));
104                }
105            }
106        }
107    }
108
109    None
110}
111
112/// user specified -> /etc/apt/mirror.list -> current dir
113fn guess_base_dir(base_dir: Option<PathBuf>) -> PathBuf {
114    if let Some(base_dir) = base_dir {
115        log::info!(base_dir:?; "use user specified base_dir");
116        base_dir
117    } else if let Some(base_dir) = find_base_dir_in_apt_mirror_config() {
118        log::info!(base_dir:?; "use base_dir from /etc/apt/mirror.list");
119        base_dir
120    } else {
121        let base_dir = current_dir().unwrap(); // ignore rare case
122        log::info!(base_dir:?; "use current directory as base_dir");
123        base_dir
124    }
125}
126
127pub fn check_mirror_guessed(base_dir: Option<PathBuf>, delete: bool) {
128    check_mirror(guess_base_dir(base_dir), delete);
129}