OXYGENHUB

Coverage / crates/flow/src/portable.rs 72.0%

18 of 25 lines run, 6 of 18 functions · run #226 on main · run missed

1
//! What differs between the systems a runner runs on (oxygenflow#83): file
2
//! modes, file identities and links are Unix's. On Windows, where a runner
3
//! offers the WASM tier only, modes are not kept (files read as 0644 and
4
//! directories as 0755), each file is its own, and sizes are lengths.
5
6
use std::fs::{Metadata, OpenOptions};
7
use std::io;
8
use std::path::Path;
9
10
/// Set `p`'s permission bits; nothing on Windows.
1124×
pub fn set_mode(p: &Path, mode: u32) -> io::Result<()> {
12
    #[cfg(unix)]
13
    {
14
        use std::os::unix::fs::PermissionsExt;
1524×
        std::fs::set_permissions(p, std::fs::Permissions::from_mode(mode))
16
    }
17
    #[cfg(not(unix))]
18
    {
19
        let _ = (p, mode);
20
        Ok(())
21
    }
2224×
}
23
24
/// A file's permission bits (`0o7777` of its mode).
2549×
pub fn mode(md: &Metadata) -> u32 {
26
    #[cfg(unix)]
27
    {
28
        use std::os::unix::fs::MetadataExt;
2949×
        md.mode() & 0o7777
30
    }
31
    #[cfg(not(unix))]
32
    {
33
        let base = if md.is_dir() { 0o755 } else { 0o644 };
34
        if md.permissions().readonly() {
35
            base & !0o222
36
        } else {
37
            base
38
        }
39
    }
4049×
}
41
42
/// Open a new file readable and writable by its owner only.
4332×
pub fn private(o: &mut OpenOptions) -> &mut OpenOptions {
44
    #[cfg(unix)]
45
    {
46
        use std::os::unix::fs::OpenOptionsExt;
4732×
        o.mode(0o600)
48
    }
49
    #[cfg(not(unix))]
50
    {
51
        o
52
    }
5332×
}
54
55
/// Make `p` and its parents, the last only its owner's.
5617×
pub fn create_private_dir(p: &Path) -> io::Result<()> {
57
    #[cfg(unix)]
58
    {
5917×
        std::os::unix::fs::DirBuilderExt::mode(std::fs::DirBuilder::new().recursive(true), 0o700).create(p)
60
    }
61
    #[cfg(not(unix))]
62
    {
63
        std::fs::create_dir_all(p)
64
    }
6517×
}
66
67
/// The disk a file takes: its blocks on Unix, its length elsewhere.
68101×
pub fn disk_bytes(md: &Metadata) -> u64 {
69
    #[cfg(unix)]
70
    {
71
        use std::os::unix::fs::MetadataExt;
72101×
        md.blocks() * 512
73
    }
74
    #[cfg(not(unix))]
75
    {
76
        md.len()
77
    }
78101×
}
79
80
/// For a file with more than one name, what names it once (device, inode);
81
/// `None` for a file with one name, a directory, or anywhere but Unix.
8291×
pub fn shared_identity(md: &Metadata) -> Option<(u64, u64)> {
83
    #[cfg(unix)]
84
    {
85
        use std::os::unix::fs::MetadataExt;
8691×
        (md.nlink() > 1 && !md.is_dir()).then(|| (md.dev(), md.ino()))
87
    }
88
    #[cfg(not(unix))]
89
    {
90
        let _ = md;
91
        None
92
    }
9391×
}
94
95
/// Make `link` a symbolic link to `target` (a file link on Windows, which
96
/// needs Developer Mode or the right to make them).
970×
pub fn symlink(target: &Path, link: &Path) -> io::Result<()> {
98
    #[cfg(unix)]
99
    {
1000×
        std::os::unix::fs::symlink(target, link)
101
    }
102
    #[cfg(windows)]
103
    {
104
        std::os::windows::fs::symlink_file(target, link)
105
    }
1060×
}
107
108
/// Rename `from` to `to`, which must not exist. On Windows through
109
/// `MoveFileExW`, which renames a program that is running, trying again
110
/// while another program (a virus scanner) holds the file.
1110×
pub fn rename(from: &Path, to: &Path) -> io::Result<()> {
112
    #[cfg(windows)]
113
    {
114
        use std::os::windows::ffi::OsStrExt;
115
        let wide = |p: &Path| p.as_os_str().encode_wide().chain(Some(0)).collect::<Vec<u16>>();
116
        let (f, t) = (wide(from), wide(to));
117
        // A virus scanner holds a new program for a while after it is
118
        // written (a sharing violation, 32, or access denied, 5): try again
119
        // for half a minute.
120
        let mut tries = 0;
121
        loop {
122
            // SAFETY: both are NUL-terminated wide paths.
123
            if unsafe { windows_sys::Win32::Storage::FileSystem::MoveFileExW(f.as_ptr(), t.as_ptr(), 0) } != 0 {
124
                return Ok(());
125
            }
126
            let e = io::Error::last_os_error();
127
            tries += 1;
128
            if tries >= 30 || !matches!(e.raw_os_error(), Some(32) | Some(5)) {
129
                return Err(e);
130
            }
131
            std::thread::sleep(std::time::Duration::from_secs(1));
132
        }
133
    }
134
    #[cfg(not(windows))]
135
    {
1360×
        std::fs::rename(from, to)
137
    }
1380×
}