summaryrefslogtreecommitdiff
path: root/src/lib.rs
blob: d6222082af4412d19576f039d6070409eff475bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
use std::fs::read;
use std::path::{Path, PathBuf};

/// Name of tag file.
pub const CACHEDIR_TAG: &str = "CACHEDIR.TAG";

/// Prefix of contents of tag file.
pub const TAG: &str = "Signature: 8a477f597d28d172789f06886806bc55";

/// Is a given file a cache directory tag?
pub fn is_cachedir_tag(filename: &Path) -> Result<bool, CacheDirError> {
    // Is the filename OK?
    if let Some(basename) = filename.file_name() {
        if basename != CACHEDIR_TAG {
            return Ok(false);
        }
    }

    // Does the file exist? It's OK if it doesn't, but we don't want
    // to try to read it if it doesn't.
    if !filename.exists() {
        return Ok(false);
    }

    // Does the file start with the right prefix?
    let data = read(filename).map_err(|e| CacheDirError::Read(filename.into(), e))?;
    if data.len() >= TAG.len() {
        Ok(&data[..TAG.len()] == TAG.as_bytes())
    } else {
        Ok(false)
    }
}

#[derive(Debug, thiserror::Error)]
pub enum CacheDirError {
    #[error("failed to read file {0}")]
    Read(PathBuf, #[source] std::io::Error),

    #[error("failed to write file {0}")]
    Write(PathBuf, #[source] std::io::Error),

    #[error("failed to delete file {0}")]
    Remove(PathBuf, #[source] std::io::Error),

    #[error(transparent)]
    WalkDir(#[from] walkdir::Error),
}