@@ -45,6 +45,8 @@ pub enum GroupReference {
45
45
Numeric ( u32 ) ,
46
46
/// A named reference
47
47
Name ( String ) ,
48
+ /// A file path
49
+ Path ( String ) ,
48
50
}
49
51
50
52
impl From < u32 > for GroupReference {
@@ -57,7 +59,9 @@ impl FromStr for GroupReference {
57
59
type Err = ParseIntError ;
58
60
59
61
fn from_str ( s : & str ) -> std:: result:: Result < Self , Self :: Err > {
60
- let r = if s. chars ( ) . all ( |c| matches ! ( c, '0' ..='9' ) ) {
62
+ let r = if s. starts_with ( '/' ) {
63
+ Self :: Path ( s. to_owned ( ) )
64
+ } else if s. chars ( ) . all ( |c| matches ! ( c, '0' ..='9' ) ) {
61
65
Self :: Numeric ( u32:: from_str ( s) ?)
62
66
} else {
63
67
Self :: Name ( s. to_owned ( ) )
@@ -66,21 +70,49 @@ impl FromStr for GroupReference {
66
70
}
67
71
}
68
72
73
+ /// In sysusers a uid can be defined statically or via a file path
74
+ #[ derive( Debug , PartialEq , Eq ) ]
75
+ pub enum IdSource {
76
+ /// A numeric uid
77
+ Numeric ( u32 ) ,
78
+ /// The uid is defined by the owner of this path
79
+ Path ( String ) ,
80
+ }
81
+
82
+ impl FromStr for IdSource {
83
+ type Err = ParseIntError ;
84
+
85
+ fn from_str ( s : & str ) -> std:: result:: Result < Self , Self :: Err > {
86
+ let r = if s. starts_with ( '/' ) {
87
+ Self :: Path ( s. to_owned ( ) )
88
+ } else {
89
+ Self :: Numeric ( u32:: from_str ( s) ?)
90
+ } ;
91
+ Ok ( r)
92
+ }
93
+ }
94
+
95
+ impl From < u32 > for IdSource {
96
+ fn from ( value : u32 ) -> Self {
97
+ Self :: Numeric ( value)
98
+ }
99
+ }
100
+
69
101
/// A parsed sysusers.d entry
70
102
#[ derive( Debug , PartialEq , Eq ) ]
71
103
#[ allow( missing_docs) ]
72
104
pub enum SysusersEntry {
73
105
/// Defines a user
74
106
User {
75
107
name : String ,
76
- uid : Option < u32 > ,
108
+ uid : Option < IdSource > ,
77
109
pgid : Option < GroupReference > ,
78
110
gecos : String ,
79
111
home : Option < String > ,
80
112
shell : Option < String > ,
81
113
} ,
82
114
/// Defines a group
83
- Group { name : String , id : Option < u32 > } ,
115
+ Group { name : String , id : Option < IdSource > } ,
84
116
/// Defines a range of uids
85
117
Range { start : u32 , end : u32 } ,
86
118
}
@@ -216,7 +248,8 @@ pub fn read_sysusers(rootfs: &Dir) -> Result<Vec<SysusersEntry>> {
216
248
found_groups. insert ( name. clone ( ) ) ;
217
249
// Users implicitly create a group with the same name
218
250
let pgid = pgid. as_ref ( ) . and_then ( |g| match g {
219
- GroupReference :: Numeric ( n) => Some ( * n) ,
251
+ GroupReference :: Numeric ( n) => Some ( IdSource :: Numeric ( * n) ) ,
252
+ GroupReference :: Path ( p) => Some ( IdSource :: Path ( p. clone ( ) ) ) ,
220
253
GroupReference :: Name ( _) => None ,
221
254
} ) ;
222
255
result. push ( SysusersEntry :: Group {
@@ -258,14 +291,14 @@ impl SysusersAnalysis {
258
291
pub fn analyze ( rootfs : & Dir ) -> Result < SysusersAnalysis > {
259
292
struct SysuserData {
260
293
#[ allow( dead_code) ]
261
- uid : Option < u32 > ,
294
+ uid : Option < IdSource > ,
262
295
#[ allow( dead_code) ]
263
296
pgid : Option < GroupReference > ,
264
297
}
265
298
266
299
struct SysgroupData {
267
300
#[ allow( dead_code) ]
268
- id : Option < u32 > ,
301
+ id : Option < IdSource > ,
269
302
}
270
303
271
304
let Some ( passwd) = nameservice:: passwd:: load_etc_passwd ( rootfs)
@@ -397,6 +430,14 @@ mod tests {
397
430
u vboxadd -:1 - /var/run/vboxadd -
398
431
"# } ;
399
432
433
+ /// Taken from man sysusers.d
434
+ const OTHER_SYSUSERS_EXAMPLES : & str = indoc ! { r#"
435
+ u user_name /file/owned/by/user "User Description" /home/dir /path/to/shell
436
+ g group_name /file/owned/by/group
437
+ #m user_name group_name
438
+ #r - lowest-highest
439
+ "# } ;
440
+
400
441
fn parse_all ( s : & str ) -> impl Iterator < Item = SysusersEntry > + use < ' _ > {
401
442
s. lines ( )
402
443
. filter ( |line| !( line. is_empty ( ) || line. starts_with ( '#' ) ) )
@@ -410,7 +451,7 @@ mod tests {
410
451
entries. next( ) . unwrap( ) ,
411
452
SysusersEntry :: User {
412
453
name: "root" . into( ) ,
413
- uid: Some ( 0 ) ,
454
+ uid: Some ( 0 . into ( ) ) ,
414
455
pgid: Some ( 0 . into( ) ) ,
415
456
gecos: "Super User" . into( ) ,
416
457
home: Some ( "/root" . into( ) ) ,
@@ -421,7 +462,7 @@ mod tests {
421
462
entries. next( ) . unwrap( ) ,
422
463
SysusersEntry :: User {
423
464
name: "root" . into( ) ,
424
- uid: Some ( 0 ) ,
465
+ uid: Some ( 0 . into ( ) ) ,
425
466
pgid: Some ( 0 . into( ) ) ,
426
467
gecos: "Super User" . into( ) ,
427
468
home: Some ( "/root" . into( ) ) ,
@@ -432,7 +473,7 @@ mod tests {
432
473
entries. next( ) . unwrap( ) ,
433
474
SysusersEntry :: User {
434
475
name: "bin" . into( ) ,
435
- uid: Some ( 1 ) ,
476
+ uid: Some ( 1 . into ( ) ) ,
436
477
pgid: Some ( 1 . into( ) ) ,
437
478
gecos: "bin" . into( ) ,
438
479
home: Some ( "/bin" . into( ) ) ,
@@ -444,7 +485,7 @@ mod tests {
444
485
entries. next( ) . unwrap( ) ,
445
486
SysusersEntry :: User {
446
487
name: "adm" . into( ) ,
447
- uid: Some ( 3 ) ,
488
+ uid: Some ( 3 . into ( ) ) ,
448
489
pgid: Some ( 4 . into( ) ) ,
449
490
gecos: "adm" . into( ) ,
450
491
home: Some ( "/var/adm" . into( ) ) ,
@@ -458,7 +499,7 @@ mod tests {
458
499
entries. next( ) . unwrap( ) ,
459
500
SysusersEntry :: User {
460
501
name: "qemu" . into( ) ,
461
- uid: Some ( 107 ) ,
502
+ uid: Some ( 107 . into ( ) ) ,
462
503
pgid: Some ( GroupReference :: Name ( "qemu" . into( ) ) ) ,
463
504
gecos: "qemu user" . into( ) ,
464
505
home: None ,
@@ -478,6 +519,27 @@ mod tests {
478
519
) ;
479
520
assert_eq ! ( entries. count( ) , 0 ) ;
480
521
522
+ let mut entries = parse_all ( OTHER_SYSUSERS_EXAMPLES ) ;
523
+ assert_eq ! (
524
+ entries. next( ) . unwrap( ) ,
525
+ SysusersEntry :: User {
526
+ name: "user_name" . into( ) ,
527
+ uid: Some ( IdSource :: Path ( "/file/owned/by/user" . into( ) ) ) ,
528
+ pgid: Some ( GroupReference :: Path ( "/file/owned/by/user" . into( ) ) ) ,
529
+ gecos: "User Description" . into( ) ,
530
+ home: Some ( "/home/dir" . into( ) ) ,
531
+ shell: Some ( "/path/to/shell" . into( ) )
532
+ }
533
+ ) ;
534
+ assert_eq ! (
535
+ entries. next( ) . unwrap( ) ,
536
+ SysusersEntry :: Group {
537
+ name: "group_name" . into( ) ,
538
+ id: Some ( IdSource :: Path ( "/file/owned/by/group" . into( ) ) )
539
+ }
540
+ ) ;
541
+ assert_eq ! ( entries. count( ) , 0 ) ;
542
+
481
543
Ok ( ( ) )
482
544
}
483
545
@@ -491,14 +553,14 @@ mod tests {
491
553
entries. next( ) . unwrap( ) ,
492
554
SysusersEntry :: Group {
493
555
name: "root" . into( ) ,
494
- id: Some ( 0 ) ,
556
+ id: Some ( 0 . into ( ) ) ,
495
557
}
496
558
) ;
497
559
assert_eq ! (
498
560
entries. next( ) . unwrap( ) ,
499
561
SysusersEntry :: Group {
500
562
name: "bin" . into( ) ,
501
- id: Some ( 1 ) ,
563
+ id: Some ( 1 . into ( ) ) ,
502
564
}
503
565
) ;
504
566
assert_eq ! ( entries. count( ) , 28 ) ;
0 commit comments