overlay_filesystem/
overlay_filesystem.rs1use sandbox_rs::{OverlayConfig, OverlayFS};
28use std::fs;
29use tempfile::TempDir;
30
31fn main() -> Result<(), Box<dyn std::error::Error>> {
32 println!("=== Sandbox-rs: Overlay Filesystem Example ===\n");
33
34 let temp_base = TempDir::new()?;
36 let base_dir = temp_base.path().join("base");
37 let upper_dir = temp_base.path().join("upper");
38
39 println!("[1] Setting up overlay filesystem layers\n");
40
41 fs::create_dir_all(&base_dir)?;
43 fs::write(
44 base_dir.join("original.txt"),
45 "This is the original immutable file\n",
46 )?;
47 fs::write(base_dir.join("readme.txt"), "Base layer - read-only\n")?;
48
49 println!(" Created base layer at: {}", base_dir.display());
50 println!(" - original.txt");
51 println!(" - readme.txt\n");
52
53 println!("[2] Creating overlay configuration\n");
55 let overlay_config = OverlayConfig::new(&base_dir, &upper_dir);
56
57 println!(
58 " Lower layer (read-only): {}",
59 overlay_config.lower.display()
60 );
61 println!(
62 " Upper layer (read-write): {}",
63 overlay_config.upper.display()
64 );
65 println!(" Work directory: {}", overlay_config.work.display());
66 println!(" Merged view: {}\n", overlay_config.merged.display());
67
68 println!("[3] Initializing overlay filesystem\n");
70 let mut overlay = OverlayFS::new(overlay_config);
71 overlay.setup()?;
72
73 println!(" Overlay filesystem initialized");
74 println!(" Mounted: {}\n", overlay.is_mounted());
75
76 println!("[4] Simulating sandbox operations\n");
78
79 let upper_path = overlay.upper_path();
81 fs::create_dir_all(upper_path)?;
82
83 let new_file_path = upper_path.join("sandbox-output.txt");
84 fs::write(
85 &new_file_path,
86 "This file was created in the sandbox\nModified during execution\n",
87 )?;
88 println!(" Created new file: {}", new_file_path.display());
89
90 let modified_file_path = upper_path.join("modified.txt");
91 fs::write(
92 &modified_file_path,
93 "This original file was modified in the sandbox\n",
94 )?;
95 println!(
96 " Created modified file: {}\n",
97 modified_file_path.display()
98 );
99
100 println!("[5] Layer Information\n");
102
103 let lower_info = sandbox_rs::LayerInfo::from_path("lower", overlay.lower_path(), false)?;
104 println!(" Lower Layer (Read-only):");
105 println!(" Files: {}", lower_info.file_count);
106 println!(" Total size: {} bytes", lower_info.size);
107 println!(" Writable: {}\n", lower_info.writable);
108
109 let upper_info = sandbox_rs::LayerInfo::from_path("upper", overlay.upper_path(), true)?;
110 println!(" Upper Layer (Read-write changes):");
111 println!(" Files: {}", upper_info.file_count);
112 println!(" Total size: {} bytes", upper_info.size);
113 println!(" Writable: {}\n", upper_info.writable);
114
115 println!("[6] Sandbox Modifications Summary\n");
117 let changes_size = overlay.get_changes_size()?;
118 println!(" Total changes in upper layer: {} bytes", changes_size);
119 println!(" Files modified/created: {}\n", upper_info.file_count);
120
121 println!("[7] Accessing Merged View\n");
123 println!(" In a real mount, you would access both layers transparently:");
124 println!(" - {} (combined view)", overlay.merged_path().display());
125 println!(" - Files from lower layer are visible");
126 println!(" - Files from upper layer override lower layer");
127 println!(" - New files only appear in upper layer\n");
128
129 println!("[8] Cleanup and Recovery Options\n");
131 println!(" Option A: Keep upper layer for audit trail");
132 println!(
133 " - Preserve {} for reviewing changes",
134 upper_path.display()
135 );
136 println!(" - Original base layer remains untouched\n");
137
138 println!(" Option B: Discard changes (reset to base)");
139 println!(" - Delete upper layer: {}", upper_path.display());
140 println!(" - Next execution gets fresh base\n");
141
142 println!(" Option C: Commit changes to new base");
143 println!(" - Copy merged view to new base layer");
144 println!(" - Create fresh upper layer for next sandbox\n");
145
146 println!("[9] Cleaning up\n");
148 overlay.cleanup()?;
149 println!(" Overlay filesystem cleaned up\n");
150
151 println!("=== Practical Use Case ===\n");
153 println!("Scenario: Run Python data processing pipeline with multiple stages\n");
154
155 println!("Stage 1: Initial execution");
156 println!(" Base layer: /data/pipeline-v1.0 (read-only)");
157 println!(" Upper layer: /sandbox-run-1/changes");
158 println!(" Output: preprocessing complete, 1.2GB changes\n");
159
160 println!("Stage 2: Different parameters");
161 println!(" Base layer: /data/pipeline-v1.0 (same - shared!)");
162 println!(" Upper layer: /sandbox-run-2/changes (fresh)");
163 println!(" Output: processing complete, 2.1GB changes\n");
164
165 println!("Benefits:");
166 println!(" - Disk efficient: Base shared across runs");
167 println!(" - Isolation: Each run has independent changes");
168 println!(" - Auditability: See exactly what each run produced");
169 println!(" - Recoverability: Can revert to base state\n");
170
171 println!("=== Combined with Volume Mounts ===\n");
173 println!("For persistent storage across sandbox runs:");
174 println!(" - Overlay FS: For temporary isolation per execution");
175 println!(" - Volume mounts: For data that needs to survive");
176 println!(" - Example setup:");
177 println!(" - Mount /home/user/data as /data (read-write)");
178 println!(" - Overlay base /usr/lib as /lib (read-only with changes)");
179 println!(" - Any writes to /data persist beyond sandbox");
180 println!(" - Any writes to /lib are isolated per run\n");
181
182 println!("=== Example completed successfully ===");
183 Ok(())
184}