Skip to content

Commit 1edf464

Browse files
committed
chore: development v0.1.1 - comprehensive testing complete [auto-commit]
1 parent 290f5d2 commit 1edf464

File tree

11 files changed

+1320
-6
lines changed

11 files changed

+1320
-6
lines changed

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/uffs-gui/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ keywords.workspace = true
2121
categories.workspace = true
2222

2323
[[bin]]
24-
name = "uffs-gui"
24+
name = "uffs_gui"
2525
path = "src/main.rs"
2626

2727
[dependencies]

crates/uffs-mft/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ readme.workspace = true
2424
keywords.workspace = true
2525
categories.workspace = true
2626

27+
[[bin]]
28+
name = "uffs_mft"
29+
path = "src/main.rs"
30+
2731
[dependencies]
2832
# Internal: Polars facade
2933
uffs-polars.workspace = true
@@ -33,9 +37,21 @@ bitflags.workspace = true
3337

3438
# Error handling
3539
thiserror.workspace = true
40+
anyhow.workspace = true
3641
rayon.workspace = true
3742
zstd = { version = "0.13.3", optional = true }
3843

44+
# CLI (for uffs_mft binary)
45+
clap.workspace = true
46+
indicatif.workspace = true
47+
48+
# Async runtime
49+
tokio.workspace = true
50+
51+
# Logging
52+
tracing.workspace = true
53+
tracing-subscriber.workspace = true
54+
3955
# Windows APIs (Windows only)
4056
[target.'cfg(windows)'.dependencies]
4157
windows.workspace = true

crates/uffs-mft/src/main.rs

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
//! # uffs_mft: MFT Command-Line Tool
2+
//!
3+
//! Low-level tool for reading and exporting NTFS Master File Table data.
4+
//!
5+
//! ## Usage
6+
//!
7+
//! ```bash
8+
//! # Read MFT from C: drive and export to Parquet
9+
//! uffs_mft read --drive C --output c_drive.parquet
10+
//!
11+
//! # Show MFT information for a drive
12+
//! uffs_mft info --drive C
13+
//!
14+
//! # List all NTFS drives
15+
//! uffs_mft drives
16+
//! ```
17+
//!
18+
//! **Note**: This tool requires Administrator privileges on Windows.
19+
20+
use std::path::PathBuf;
21+
22+
use anyhow::{Result, bail};
23+
use clap::{Parser, Subcommand};
24+
use tracing_subscriber::EnvFilter;
25+
26+
#[cfg(windows)]
27+
use anyhow::Context;
28+
#[cfg(windows)]
29+
use indicatif::{ProgressBar, ProgressStyle};
30+
#[cfg(windows)]
31+
use tracing::info;
32+
#[cfg(windows)]
33+
use uffs_mft::MftReader;
34+
35+
/// uffs_mft: Low-level NTFS MFT reading tool
36+
#[derive(Parser)]
37+
#[command(name = "uffs_mft")]
38+
#[command(author, version, about, long_about = None)]
39+
struct Cli {
40+
/// Enable verbose output
41+
#[arg(short, long, global = true)]
42+
verbose: bool,
43+
44+
#[command(subcommand)]
45+
command: Commands,
46+
}
47+
48+
#[derive(Subcommand)]
49+
enum Commands {
50+
/// Read MFT from a drive and export to Parquet
51+
Read {
52+
/// Drive letter (e.g., C, D, E)
53+
#[arg(short, long)]
54+
drive: char,
55+
56+
/// Output file path (Parquet format)
57+
#[arg(short, long)]
58+
output: PathBuf,
59+
},
60+
61+
/// Show MFT information for a drive
62+
Info {
63+
/// Drive letter (e.g., C, D, E)
64+
#[arg(short, long)]
65+
drive: char,
66+
},
67+
68+
/// List all available NTFS drives
69+
Drives,
70+
}
71+
72+
#[tokio::main]
73+
async fn main() -> Result<()> {
74+
let cli = Cli::parse();
75+
76+
// Initialize logging
77+
let filter = if cli.verbose {
78+
EnvFilter::new("debug")
79+
} else {
80+
EnvFilter::new("info")
81+
};
82+
tracing_subscriber::fmt().with_env_filter(filter).init();
83+
84+
// Check platform
85+
#[cfg(not(windows))]
86+
{
87+
bail!(
88+
"uffs_mft only works on Windows.\n\
89+
It requires direct access to the NTFS Master File Table via Windows APIs."
90+
);
91+
}
92+
93+
#[cfg(windows)]
94+
{
95+
match cli.command {
96+
Commands::Read { drive, output } => cmd_read(drive, output).await,
97+
Commands::Info { drive } => cmd_info(drive).await,
98+
Commands::Drives => cmd_drives().await,
99+
}
100+
}
101+
}
102+
103+
#[cfg(windows)]
104+
async fn cmd_read(drive: char, output: PathBuf) -> Result<()> {
105+
info!("Reading MFT from {}:", drive.to_ascii_uppercase());
106+
107+
let pb = ProgressBar::new_spinner();
108+
pb.set_style(
109+
ProgressStyle::default_spinner()
110+
.template("{spinner:.green} {msg}")
111+
.expect("valid template"),
112+
);
113+
pb.set_message("Opening volume...");
114+
115+
let reader = MftReader::open(drive)
116+
.await
117+
.with_context(|| format!("Failed to open drive {}:", drive))?;
118+
119+
pb.set_message("Reading MFT records...");
120+
let df = reader
121+
.read_all()
122+
.await
123+
.with_context(|| "Failed to read MFT")?;
124+
125+
pb.set_message("Saving to Parquet...");
126+
MftReader::save_parquet(&df, &output).with_context(|| "Failed to save Parquet")?;
127+
128+
pb.finish_with_message(format!(
129+
"✅ Exported {} records to {}",
130+
df.height(),
131+
output.display()
132+
));
133+
134+
Ok(())
135+
}
136+
137+
#[cfg(windows)]
138+
async fn cmd_info(drive: char) -> Result<()> {
139+
use uffs_mft::platform::VolumeHandle;
140+
141+
info!("MFT Info for {}:", drive.to_ascii_uppercase());
142+
143+
let handle = VolumeHandle::open(drive).with_context(|| format!("Failed to open {}:", drive))?;
144+
145+
let vol_data = handle
146+
.get_ntfs_volume_data()
147+
.with_context(|| "Failed to get volume data")?;
148+
149+
println!("Drive: {}:", drive.to_ascii_uppercase());
150+
println!(" Bytes per sector: {}", vol_data.bytes_per_sector);
151+
println!(" Bytes per cluster: {}", vol_data.bytes_per_cluster);
152+
println!(
153+
" Bytes per MFT record: {}",
154+
vol_data.bytes_per_file_record_segment
155+
);
156+
println!(" Total clusters: {}", vol_data.total_clusters);
157+
println!(" MFT start LCN: {}", vol_data.mft_start_lcn);
158+
println!(" MFT valid length: {} bytes", vol_data.mft_valid_data_length);
159+
160+
let record_count = vol_data.mft_valid_data_length / vol_data.bytes_per_file_record_segment as u64;
161+
println!(" Estimated records: {record_count}");
162+
163+
Ok(())
164+
}
165+
166+
#[cfg(windows)]
167+
async fn cmd_drives() -> Result<()> {
168+
use uffs_mft::platform::detect_ntfs_drives;
169+
170+
info!("Detecting NTFS drives...");
171+
172+
let drives = detect_ntfs_drives();
173+
174+
if drives.is_empty() {
175+
println!("No NTFS drives found.");
176+
} else {
177+
println!("NTFS drives:");
178+
for drive in drives {
179+
println!(" {}:", drive);
180+
}
181+
}
182+
183+
Ok(())
184+
}
185+

crates/uffs-tui/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ keywords.workspace = true
2424
categories.workspace = true
2525

2626
[[bin]]
27-
name = "uffs-tui"
27+
name = "uffs_tui"
2828
path = "src/main.rs"
2929

3030
[dependencies]

dist/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# UFFS Binary Distribution
2+
3+
This directory contains pre-built UFFS binaries.
4+
5+
## ⚠️ Windows Only
6+
7+
**UFFS is a Windows-only tool.** It reads the NTFS Master File Table (MFT)
8+
directly using Windows kernel APIs for ultra-fast file searching.
9+
10+
macOS and Linux are only used as cross-compilation hosts - the resulting
11+
binaries still only run on Windows.
12+
13+
## Binaries
14+
15+
| Binary | Description |
16+
|--------|-------------|
17+
| `uffs` | Main CLI tool for fast file searching |
18+
| `uffs_mft` | Low-level MFT reading tool (read, info, drives) |
19+
| `uffs_tui` | Terminal UI for interactive searching |
20+
| `uffs_gui` | Graphical UI (placeholder) |
21+
22+
## Structure
23+
24+
- `latest/` - Symlink to the current version
25+
- `v{version}/{binary}/` - Binaries for each version
26+
- `{binary}-windows-x64.exe` - Windows x64 (the only supported platform)
27+
28+
## Installation
29+
30+
### Windows
31+
32+
Copy the binary to a directory in your PATH:
33+
34+
```powershell
35+
copy uffs-windows-x64.exe C:\Users\YourName\bin\uffs.exe
36+
```
37+
38+
Or add the bin directory to your PATH:
39+
40+
```powershell
41+
# Add C:\Users\YourName\bin to your PATH if not already there
42+
$env:Path += ";$env:USERPROFILE\bin"
43+
```
44+
45+
## Cross-Compilation
46+
47+
To build Windows binaries from macOS or Linux:
48+
49+
```bash
50+
# Install prerequisites
51+
cargo install cargo-xwin
52+
rustup target add x86_64-pc-windows-msvc
53+
54+
# Build
55+
rust-script scripts/build-cross-all.rs
56+
```
57+
58+
## Why Windows Only?
59+
60+
UFFS achieves its speed by reading the NTFS Master File Table (MFT) directly
61+
from disk, bypassing the Windows file enumeration APIs. This requires:
62+
63+
- Direct volume access via `\\.\X:` paths
64+
- Windows kernel ioctls like `FSCTL_GET_NTFS_VOLUME_DATA`
65+
- Administrator privileges for raw disk access
66+
67+
These APIs are Windows-specific and cannot be replicated on other platforms.
68+

dist/latest

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v0.1.1

justfile

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,71 @@ deploy-release:
714714
@printf "\033[0;34m📦 Deploying release binary...\033[0m\n"
715715
just copy-binary release
716716

717+
# Install latest pre-built binaries from dist/ to ~/bin (platform-aware)
718+
# Install UFFS binaries to ~/bin (Windows only)
719+
# UFFS is Windows-only - it requires NTFS MFT access via Windows APIs
720+
# Binaries: uffs, uffs_mft, uffs_tui, uffs_gui
721+
use:
722+
#!/usr/bin/env bash
723+
printf "\033[0;34m📦 Installing latest UFFS binaries to ~/bin...\033[0m\n"
724+
printf "\033[1;33mℹ️ Note: UFFS is Windows-only (requires NTFS MFT access)\033[0m\n"
725+
726+
# Detect platform
727+
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
728+
ARCH=$(uname -m)
729+
730+
case "$OS-$ARCH" in
731+
mingw*|msys*|cygwin*)
732+
PLATFORM="windows-x64"
733+
;;
734+
darwin-arm64|darwin-x86_64|linux-x86_64|linux-aarch64)
735+
printf "\033[0;31m❌ UFFS only runs on Windows.\033[0m\n"
736+
printf "\033[0;34m UFFS reads the NTFS Master File Table directly using Windows APIs.\033[0m\n"
737+
printf "\033[0;34m Use 'just go' to cross-compile Windows binaries from this host.\033[0m\n"
738+
exit 1
739+
;;
740+
*)
741+
printf "\033[0;31m❌ Unsupported platform: $OS-$ARCH\033[0m\n"
742+
exit 1
743+
;;
744+
esac
745+
746+
printf "\033[0;34m → Platform: $PLATFORM\033[0m\n"
747+
748+
# Create ~/bin if needed
749+
mkdir -p ~/bin
750+
751+
# Binaries to install
752+
BINARIES="uffs uffs_mft uffs_tui uffs_gui"
753+
INSTALLED=0
754+
SKIPPED=0
755+
756+
for BINARY in $BINARIES; do
757+
SRC="dist/latest/$BINARY/$BINARY-$PLATFORM.exe"
758+
DEST="$HOME/bin/$BINARY.exe"
759+
760+
if [[ -f "$SRC" ]]; then
761+
cp "$SRC" "$DEST"
762+
chmod +x "$DEST"
763+
printf "\033[0;32m ✅ $BINARY.exe\033[0m\n"
764+
INSTALLED=$((INSTALLED + 1))
765+
else
766+
printf "\033[1;33m ⚠️ $BINARY (not found for $PLATFORM)\033[0m\n"
767+
SKIPPED=$((SKIPPED + 1))
768+
fi
769+
done
770+
771+
printf "\033[0;32m✅ Installed $INSTALLED binaries to ~/bin\033[0m\n"
772+
if [[ $SKIPPED -gt 0 ]]; then
773+
printf "\033[1;33m⚠️ Skipped $SKIPPED (run 'just go' to build all binaries)\033[0m\n"
774+
fi
775+
776+
# PATH guidance
777+
if ! echo "$PATH" | grep -q "$HOME/bin"; then
778+
printf "\033[1;33m⚠️ ~/bin not in PATH. Add to your shell profile:\033[0m\n"
779+
printf " export PATH=\"\$HOME/bin:\$PATH\"\n"
780+
fi
781+
717782
# Git commit with auto-generated message
718783
commit:
719784
@printf "\033[0;34m📝 Creating auto-generated commit...\033[0m\n"

0 commit comments

Comments
 (0)