@@ -14,18 +14,9 @@ const STASH_DIR: &str = "/var/devbox/overlay/stash";
1414/// List files changed in the overlay upper layer.
1515/// Returns a list of (status, path) tuples.
1616pub async fn diff ( runtime : & dyn Runtime , sandbox_name : & str ) -> Result < Vec < OverlayChange > > {
17- // List all files in the upper directory
18- let sudo = runtime. sudo_prefix ( ) ;
19- let result = runtime
20- . exec_cmd (
21- sandbox_name,
22- & [
23- "bash" , "-lc" ,
24- & format ! ( "{sudo}find {UPPER} -not -path {UPPER} -printf '%y %P\\ n'" ) ,
25- ] ,
26- false ,
27- )
28- . await ?;
17+ // List all files in the upper directory (needs root for overlay dirs)
18+ let cmd = format ! ( "find {UPPER} -not -path {UPPER} -printf '%y %P\\ n'" ) ;
19+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
2920
3021 if result. exit_code != 0 {
3122 bail ! ( "Failed to scan overlay changes: {}" , result. stderr. trim( ) ) ;
@@ -160,9 +151,7 @@ pub async fn commit(
160151 return Ok ( filtered. len ( ) ) ;
161152 }
162153
163- // Sync: rsync from upper to lower for each changed file
164- // We need to handle additions, modifications, and deletions
165- let sudo = runtime. sudo_prefix ( ) ;
154+ // Sync: copy from upper to lower for each changed file
166155 let mut committed = 0 ;
167156
168157 for change in & filtered {
@@ -172,10 +161,8 @@ pub async fn commit(
172161 match change. status {
173162 ChangeStatus :: Added | ChangeStatus :: Modified => {
174163 if change. is_dir {
175- let cmd = format ! ( "{sudo}mkdir -p {lower_path}" ) ;
176- let result = runtime
177- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
178- . await ?;
164+ let cmd = format ! ( "mkdir -p {lower_path}" ) ;
165+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
179166 if result. exit_code != 0 {
180167 eprintln ! (
181168 "Warning: failed to create dir {}: {}" ,
@@ -194,16 +181,12 @@ pub async fn commit(
194181 . unwrap_or_default( )
195182 ) ;
196183 if !parent. is_empty ( ) && parent != LOWER {
197- let cmd = format ! ( "{sudo}mkdir -p {parent}" ) ;
198- let _ = runtime
199- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
200- . await ;
184+ let cmd = format ! ( "mkdir -p {parent}" ) ;
185+ let _ = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ;
201186 }
202187
203- let cmd = format ! ( "{sudo}cp -a {upper_path} {lower_path}" ) ;
204- let result = runtime
205- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
206- . await ?;
188+ let cmd = format ! ( "cp -a {upper_path} {lower_path}" ) ;
189+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
207190 if result. exit_code != 0 {
208191 eprintln ! (
209192 "Warning: failed to commit {}: {}" ,
@@ -215,10 +198,8 @@ pub async fn commit(
215198 }
216199 }
217200 ChangeStatus :: Deleted => {
218- let cmd = format ! ( "{sudo}rm -rf {lower_path}" ) ;
219- let result = runtime
220- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
221- . await ?;
201+ let cmd = format ! ( "rm -rf {lower_path}" ) ;
202+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
222203 if result. exit_code != 0 {
223204 eprintln ! (
224205 "Warning: failed to delete {}: {}" ,
@@ -250,15 +231,12 @@ pub async fn discard(
250231 sandbox_name : & str ,
251232 paths : Option < & [ String ] > ,
252233) -> Result < usize > {
253- let sudo = runtime. sudo_prefix ( ) ;
254234 if let Some ( filter_paths) = paths {
255235 let mut discarded = 0 ;
256236 for path in filter_paths {
257237 let upper_path = format ! ( "{UPPER}/{}" , path. trim_start_matches( '/' ) ) ;
258- let cmd = format ! ( "{sudo}rm -rf {upper_path}" ) ;
259- let result = runtime
260- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
261- . await ?;
238+ let cmd = format ! ( "rm -rf {upper_path}" ) ;
239+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
262240 if result. exit_code == 0 {
263241 println ! ( " Discarded: {path}" ) ;
264242 discarded += 1 ;
@@ -270,17 +248,8 @@ pub async fn discard(
270248 Ok ( discarded)
271249 } else {
272250 // Clear entire upper layer
273- let result = runtime
274- . exec_cmd (
275- sandbox_name,
276- & [
277- "bash" ,
278- "-lc" ,
279- & format ! ( "{sudo}rm -rf {UPPER}/* {UPPER}/.[!.]* 2>/dev/null; true" ) ,
280- ] ,
281- false ,
282- )
283- . await ?;
251+ let cmd = format ! ( "rm -rf {UPPER}/* {UPPER}/.[!.]* 2>/dev/null; true" ) ;
252+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
284253
285254 if result. exit_code != 0 {
286255 bail ! ( "Failed to clear overlay: {}" , result. stderr. trim( ) ) ;
@@ -298,23 +267,17 @@ pub async fn stash(runtime: &dyn Runtime, sandbox_name: &str) -> Result<()> {
298267 bail ! ( "A stash already exists. Pop or discard it first (`devbox layer stash-pop`)." ) ;
299268 }
300269
301- let sudo = runtime. sudo_prefix ( ) ;
302-
303270 // Move upper to stash
304- let cmd = format ! ( "{sudo}mv {UPPER} {STASH_DIR}" ) ;
305- let result = runtime
306- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
307- . await ?;
271+ let cmd = format ! ( "mv {UPPER} {STASH_DIR}" ) ;
272+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
308273
309274 if result. exit_code != 0 {
310275 bail ! ( "Failed to stash overlay: {}" , result. stderr. trim( ) ) ;
311276 }
312277
313278 // Recreate empty upper directory
314- let cmd = format ! ( "{sudo}mkdir -p {UPPER}" ) ;
315- let result = runtime
316- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
317- . await ?;
279+ let cmd = format ! ( "mkdir -p {UPPER}" ) ;
280+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
318281
319282 if result. exit_code != 0 {
320283 bail ! (
@@ -333,25 +296,19 @@ pub async fn stash_pop(runtime: &dyn Runtime, sandbox_name: &str) -> Result<()>
333296 bail ! ( "No stash found. Nothing to pop." ) ;
334297 }
335298
336- let sudo = runtime. sudo_prefix ( ) ;
337-
338299 // Merge stash back into upper (copy hidden and regular files)
339300 let merge_cmd = format ! (
340- "{sudo} cp -a {STASH_DIR}/* {UPPER}/ 2>/dev/null; {sudo} cp -a {STASH_DIR}/.[!.]* {UPPER}/ 2>/dev/null; true"
301+ "cp -a {STASH_DIR}/* {UPPER}/ 2>/dev/null; cp -a {STASH_DIR}/.[!.]* {UPPER}/ 2>/dev/null; true"
341302 ) ;
342- let result = runtime
343- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & merge_cmd] , false )
344- . await ?;
303+ let result = runtime. run_as_root ( sandbox_name, & merge_cmd, false ) . await ?;
345304
346305 if result. exit_code != 0 {
347306 bail ! ( "Failed to restore stash: {}" , result. stderr. trim( ) ) ;
348307 }
349308
350309 // Remove the stash directory
351- let cmd = format ! ( "{sudo}rm -rf {STASH_DIR}" ) ;
352- let result = runtime
353- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
354- . await ?;
310+ let cmd = format ! ( "rm -rf {STASH_DIR}" ) ;
311+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
355312
356313 if result. exit_code != 0 {
357314 bail ! ( "Failed to clean up stash: {}" , result. stderr. trim( ) ) ;
@@ -378,16 +335,9 @@ pub async fn has_stash(runtime: &dyn Runtime, sandbox_name: &str) -> Result<bool
378335/// Newer kernels don't allow `mount -o remount` on OverlayFS, so we
379336/// unmount and remount with the same options instead.
380337pub async fn refresh ( runtime : & dyn Runtime , sandbox_name : & str ) -> Result < ( ) > {
381- let sudo = runtime. sudo_prefix ( ) ;
382-
383338 // Try simple remount first (works on older kernels)
384- let result = runtime
385- . exec_cmd (
386- sandbox_name,
387- & [ "bash" , "-lc" , & format ! ( "{sudo}mount -o remount {WORKSPACE}" ) ] ,
388- false ,
389- )
390- . await ?;
339+ let cmd = format ! ( "mount -o remount {WORKSPACE}" ) ;
340+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
391341
392342 if result. exit_code == 0 {
393343 println ! ( "Overlay refreshed — host changes are now visible." ) ;
@@ -397,12 +347,10 @@ pub async fn refresh(runtime: &dyn Runtime, sandbox_name: &str) -> Result<()> {
397347 // Remount not supported — unmount and remount manually.
398348 // The upper layer is on disk, so nothing is lost.
399349 let remount_cmd = format ! (
400- "{sudo} umount {WORKSPACE} && {sudo} mount -t overlay overlay \
350+ "umount {WORKSPACE} && mount -t overlay overlay \
401351 -o lowerdir={LOWER},upperdir={UPPER},workdir={WORK} {WORKSPACE}"
402352 ) ;
403- let result = runtime
404- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & remount_cmd] , false )
405- . await ?;
353+ let result = runtime. run_as_root ( sandbox_name, & remount_cmd, false ) . await ?;
406354
407355 if result. exit_code != 0 {
408356 bail ! ( "Failed to refresh overlay: {}" , result. stderr. trim( ) ) ;
@@ -468,14 +416,11 @@ pub async fn lower_layer_changes(runtime: &dyn Runtime, sandbox_name: &str) -> R
468416 // Compare the lower layer mtime against a timestamp file we create on mount.
469417 // If no timestamp exists, we can't detect changes — just check for stale handles.
470418 // Simpler approach: find files in lower newer than the overlay work dir (created at mount time).
471- let sudo = runtime. sudo_prefix ( ) ;
472419 let cmd = format ! (
473- "{sudo} find {} -newer {} -not -path {} -type f -printf '%P\\ n' 2>/dev/null | head -50" ,
420+ "find {} -newer {} -not -path {} -type f -printf '%P\\ n' 2>/dev/null | head -50" ,
474421 LOWER , WORK , LOWER
475422 ) ;
476- let result = runtime
477- . exec_cmd ( sandbox_name, & [ "bash" , "-lc" , & cmd] , false )
478- . await ?;
423+ let result = runtime. run_as_root ( sandbox_name, & cmd, false ) . await ?;
479424
480425 if result. exit_code != 0 {
481426 return Ok ( vec ! [ ] ) ;
0 commit comments