fn scan_trash>()

in src/client/trash_cleaner/src/main.rs [384:497]


fn scan_trash<P: AsRef<Path>>(trash_root: &P) -> nix::Result<()> {
    let trash_root = trash_root.as_ref();
    let mut dir = Dir::open(
        trash_root,
        OFlag::O_DIRECTORY | OFlag::O_RDONLY,
        nix::sys::stat::Mode::empty(),
    )
    .map_err(|err| {
        error!("open trash root {:?} failed, {}", trash_root, err);
        err
    })?;

    let dir_fd = dir.as_raw_fd();
    let dir_stat = nix::sys::stat::fstat(dir_fd)?;
    if dir_stat.st_uid != 0 || dir_stat.st_gid != 0 {
        error!(
            "trash root {:?} is not owned by root user, {}:{}",
            trash_root, dir_stat.st_uid, dir_stat.st_gid
        );
        abort();
    }

    for entry in dir.iter() {
        let entry = entry.map_err(|err| {
            error!("scan trash root {:?} failed, {}", trash_root, err);
            err
        })?;

        let entry_name = match entry.file_name().to_str() {
            Ok(name) => name,
            Err(err) => {
                error!(
                    "trash directory name {:?} is not utf8, {}",
                    entry.file_name(),
                    err
                );
                continue;
            }
        };
        if entry_name == "." || entry_name == ".." {
            continue;
        }

        let entry_stat = match nix::sys::stat::fstatat(
            Some(dir_fd),
            entry.file_name(),
            nix::fcntl::AtFlags::AT_SYMLINK_NOFOLLOW,
        ) {
            Ok(stat) => stat,
            Err(err) => {
                error!(
                    "stat trash direstory {:?} failed, {}",
                    entry.file_name(),
                    err
                );
                continue;
            }
        };
        if !nix::sys::stat::SFlag::from_bits_truncate(entry_stat.st_mode)
            .contains(nix::sys::stat::SFlag::S_IFDIR)
        {
            error!("trash directory {:?} is not directory", entry.file_name());
            continue;
        }
        if entry_stat.st_uid == 0 || entry_stat.st_gid == 0 {
            error!("trash directory {:?} owned by root user", entry.file_name());
            continue;
        }

        // create user context
        let _user_ctx = UserContext::new(
            Uid::from_raw(entry_stat.st_uid),
            Gid::from_raw(entry_stat.st_gid),
        );

        // open trash
        let trash = match Trash::open(entry_stat.st_uid, entry_name, trash_root.join(entry_name)) {
            Ok(trash) => trash,
            Err(errno) => {
                error!(
                    "open trash directory {:?} failed, errno {}",
                    entry_name, errno
                );
                continue;
            }
        };

        // clean trash
        match trash.clean(false) {
            Ok(n) => {
                if n > 0 {
                    info!(
                        "clean trash directory {} success, cleaned {} item",
                        entry_name, n
                    );
                } else {
                    trace!(
                        "clean trash directory {} success, cleaned {} item",
                        entry_name,
                        n
                    );
                }
            }
            Err(errno) => {
                error!(
                    "clean trash directory {} failed, errno {}",
                    entry_name, errno
                );
            }
        }
    }

    Ok(())
}