@@ -2,12 +2,14 @@ mod etc_tree;
2
2
3
3
use im:: HashMap ;
4
4
use itertools:: Itertools ;
5
+ use regex;
5
6
use serde:: { Deserialize , Serialize } ;
6
7
use std:: fs:: { DirBuilder , Permissions } ;
7
8
use std:: os:: unix:: fs as unixfs;
8
9
use std:: os:: unix:: prelude:: PermissionsExt ;
9
10
use std:: path;
10
11
use std:: path:: { Path , PathBuf } ;
12
+ use std:: sync:: OnceLock ;
11
13
use std:: { fs, io} ;
12
14
13
15
use self :: etc_tree:: FileStatus ;
@@ -22,6 +24,12 @@ pub use etc_tree::FileTree;
22
24
23
25
type EtcActivationResult = ActivationResult < FileTree > ;
24
26
27
+ static UID_GID_REGEX : OnceLock < regex:: Regex > = OnceLock :: new ( ) ;
28
+
29
+ fn get_uid_gid_regex ( ) -> & ' static regex:: Regex {
30
+ UID_GID_REGEX . get_or_init ( || regex:: Regex :: new ( r"^\+[0-9]+$" ) . expect ( "could not compile regex" ) )
31
+ }
32
+
25
33
#[ derive( Debug , Clone , Serialize , Deserialize ) ]
26
34
#[ serde( rename_all = "camelCase" ) ]
27
35
struct EtcFile {
@@ -352,9 +360,7 @@ fn create_etc_entry(
352
360
match copy_file (
353
361
& entry. source . store_path . join ( & entry. target ) ,
354
362
& target_path,
355
- & entry. mode ,
356
- entry. uid ,
357
- entry. gid ,
363
+ entry,
358
364
old_state,
359
365
) {
360
366
Ok ( _) => Ok ( new_state. register_managed_entry ( & target_path) ) ,
@@ -414,8 +420,54 @@ fn create_dir_recursively(dir: &Path, state: FileTree) -> EtcActivationResult {
414
420
new_state
415
421
}
416
422
417
- fn copy_file ( source : & Path , target : & Path , mode : & str ,
418
- uid : u32 , gid : u32 , old_state : & FileTree ) -> anyhow:: Result < ( ) > {
423
+ fn find_uid ( entry : & EtcFile ) -> anyhow:: Result < u32 > {
424
+ if !get_uid_gid_regex ( ) . is_match ( & entry. user ) {
425
+ nix:: unistd:: User :: from_name ( & entry. user )
426
+ . map ( |maybe_user| {
427
+ maybe_user. map_or_else (
428
+ || {
429
+ log:: warn!(
430
+ "Specified user {} not found, defaulting to root" ,
431
+ & entry. user
432
+ ) ;
433
+ 0
434
+ } ,
435
+ |user| user. uid . as_raw ( ) ,
436
+ )
437
+ } )
438
+ . map_err ( |err| anyhow:: anyhow!( err) . context ( "Failed to determine user" ) )
439
+ } else {
440
+ Ok ( entry. uid )
441
+ }
442
+ }
443
+
444
+ fn find_gid ( entry : & EtcFile ) -> anyhow:: Result < u32 > {
445
+ if !get_uid_gid_regex ( ) . is_match ( & entry. group ) {
446
+ nix:: unistd:: Group :: from_name ( & entry. group )
447
+ . map ( |maybe_group| {
448
+ maybe_group. map_or_else (
449
+ || {
450
+ log:: warn!(
451
+ "Specified group {} not found, defaulting to root" ,
452
+ & entry. group
453
+ ) ;
454
+ 0
455
+ } ,
456
+ |group| group. gid . as_raw ( ) ,
457
+ )
458
+ } )
459
+ . map_err ( |err| anyhow:: anyhow!( err) . context ( "Failed to determine group" ) )
460
+ } else {
461
+ Ok ( entry. gid )
462
+ }
463
+ }
464
+
465
+ fn copy_file (
466
+ source : & Path ,
467
+ target : & Path ,
468
+ entry : & EtcFile ,
469
+ old_state : & FileTree ,
470
+ ) -> anyhow:: Result < ( ) > {
419
471
let exists = target. try_exists ( ) ?;
420
472
if !exists || old_state. is_managed ( target) {
421
473
log:: debug!(
@@ -424,9 +476,9 @@ fn copy_file(source: &Path, target: &Path, mode: &str,
424
476
target. display( )
425
477
) ;
426
478
fs:: copy ( source, target) ?;
427
- let mode_int = u32:: from_str_radix ( mode, 8 ) ?;
479
+ let mode_int = u32:: from_str_radix ( & entry . mode , 8 ) ?;
428
480
fs:: set_permissions ( target, Permissions :: from_mode ( mode_int) ) ?;
429
- unixfs:: chown ( target, Some ( uid ) , Some ( gid ) ) ?;
481
+ unixfs:: chown ( target, Some ( find_uid ( entry ) ? ) , Some ( find_gid ( entry ) ? ) ) ?;
430
482
Ok ( ( ) )
431
483
} else {
432
484
anyhow:: bail!( "File {} already exists, ignoring." , target. display( ) ) ;
0 commit comments