wininskit 0.1.2

Thin checked wrappers over the Win32 an installer needs: elevation, ACLs, services, the Restart Manager, the registry and shortcuts.
//! Times the install-folder permission grant against the number of files
//! already under it, and checks that new files still inherit it.
//!
//! The obvious call, `SetNamedSecurityInfoW`, walks every existing child and
//! rewrites its inherited entry, so it costs time proportional to what is
//! already installed. A fresh install never shows it; a re-install over eighty
//! thousand files does. This measures what the grant actually costs now and
//! proves the inheritance it relies on still happens.
//!
//! Run it elevated: `cargo run -p wininskit --example acl-timing`

use std::{fs, path::Path, process::Command, time::Instant};

fn main() {
    if !wininskit::is_elevated() {
        println!("run this elevated - the grant needs it");
        return;
    }

    let root = std::env::temp_dir().join("wininskit-acl-timing");
    println!("{:>9}  {:>9}  {:>9}", "files", "first", "again");

    for count in [0usize, 1_000, 10_000, 40_000] {
        let _ = fs::remove_dir_all(&root);
        fill(&root, count);

        let started = Instant::now();
        let first = wininskit::grant_users_full_access(&root);
        let first_seconds = started.elapsed().as_secs_f64();

        // A re-install grants again over a tree that already carries it. This
        // is the case that used to cost the most and should now cost nothing.
        let started = Instant::now();
        let again = wininskit::grant_users_full_access(&root);
        let again_seconds = started.elapsed().as_secs_f64();

        match (first, again) {
            (Ok(()), Ok(())) => {
                println!("{count:>9}  {first_seconds:>9.3}  {again_seconds:>9.3}")
            }
            (first, again) => println!("{count:>9}  {first:?} {again:?}"),
        }
    }

    // The grant is written without walking the tree, so everything rests on the
    // filesystem applying inheritance when a file is created. Checked rather
    // than assumed.
    let child = root.join("created-after-the-grant.txt");
    fs::write(&child, b"x").expect("write child");
    println!("\ninherited entries on a file created after the grant:");
    match Command::new("icacls").arg(&child).output() {
        Ok(output) => {
            let text = String::from_utf8_lossy(&output.stdout);
            for line in text.lines().filter(|l| l.contains("(I)")) {
                println!("  {}", line.trim());
            }
            println!(
                "  -> BUILTIN\\Users present: {}",
                text.contains("BUILTIN\\Users")
            );
        }
        Err(error) => println!("  could not run icacls: {error}"),
    }

    let _ = fs::remove_dir_all(&root);
}

/// Spreads the files over subdirectories, so this measures a tree rather than
/// one enormous directory.
fn fill(root: &Path, count: usize) {
    fs::create_dir_all(root).expect("create root");
    const PER_DIRECTORY: usize = 200;
    for index in 0..count {
        let directory = root.join(format!("d{:04}", index / PER_DIRECTORY));
        if index.is_multiple_of(PER_DIRECTORY) {
            fs::create_dir_all(&directory).expect("create directory");
        }
        fs::write(directory.join(format!("f{index:06}.bin")), b"x").expect("write file");
    }
}