Skip to content

Commit 6da8c2f

Browse files
committed
sysusers: Parse path references too
These are uncommon, but valid. xref: bootc-dev#1331 Signed-off-by: Colin Walters <[email protected]>
1 parent ab827f5 commit 6da8c2f

File tree

1 file changed

+75
-13
lines changed

1 file changed

+75
-13
lines changed

sysusers/src/lib.rs

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub enum GroupReference {
4545
Numeric(u32),
4646
/// A named reference
4747
Name(String),
48+
/// A file path
49+
Path(String),
4850
}
4951

5052
impl From<u32> for GroupReference {
@@ -57,7 +59,9 @@ impl FromStr for GroupReference {
5759
type Err = ParseIntError;
5860

5961
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')) {
6165
Self::Numeric(u32::from_str(s)?)
6266
} else {
6367
Self::Name(s.to_owned())
@@ -66,21 +70,49 @@ impl FromStr for GroupReference {
6670
}
6771
}
6872

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+
69101
/// A parsed sysusers.d entry
70102
#[derive(Debug, PartialEq, Eq)]
71103
#[allow(missing_docs)]
72104
pub enum SysusersEntry {
73105
/// Defines a user
74106
User {
75107
name: String,
76-
uid: Option<u32>,
108+
uid: Option<IdSource>,
77109
pgid: Option<GroupReference>,
78110
gecos: String,
79111
home: Option<String>,
80112
shell: Option<String>,
81113
},
82114
/// Defines a group
83-
Group { name: String, id: Option<u32> },
115+
Group { name: String, id: Option<IdSource> },
84116
/// Defines a range of uids
85117
Range { start: u32, end: u32 },
86118
}
@@ -216,7 +248,8 @@ pub fn read_sysusers(rootfs: &Dir) -> Result<Vec<SysusersEntry>> {
216248
found_groups.insert(name.clone());
217249
// Users implicitly create a group with the same name
218250
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())),
220253
GroupReference::Name(_) => None,
221254
});
222255
result.push(SysusersEntry::Group {
@@ -258,14 +291,14 @@ impl SysusersAnalysis {
258291
pub fn analyze(rootfs: &Dir) -> Result<SysusersAnalysis> {
259292
struct SysuserData {
260293
#[allow(dead_code)]
261-
uid: Option<u32>,
294+
uid: Option<IdSource>,
262295
#[allow(dead_code)]
263296
pgid: Option<GroupReference>,
264297
}
265298

266299
struct SysgroupData {
267300
#[allow(dead_code)]
268-
id: Option<u32>,
301+
id: Option<IdSource>,
269302
}
270303

271304
let Some(passwd) = nameservice::passwd::load_etc_passwd(rootfs)
@@ -397,6 +430,14 @@ mod tests {
397430
u vboxadd -:1 - /var/run/vboxadd -
398431
"#};
399432

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+
400441
fn parse_all(s: &str) -> impl Iterator<Item = SysusersEntry> + use<'_> {
401442
s.lines()
402443
.filter(|line| !(line.is_empty() || line.starts_with('#')))
@@ -410,7 +451,7 @@ mod tests {
410451
entries.next().unwrap(),
411452
SysusersEntry::User {
412453
name: "root".into(),
413-
uid: Some(0),
454+
uid: Some(0.into()),
414455
pgid: Some(0.into()),
415456
gecos: "Super User".into(),
416457
home: Some("/root".into()),
@@ -421,7 +462,7 @@ mod tests {
421462
entries.next().unwrap(),
422463
SysusersEntry::User {
423464
name: "root".into(),
424-
uid: Some(0),
465+
uid: Some(0.into()),
425466
pgid: Some(0.into()),
426467
gecos: "Super User".into(),
427468
home: Some("/root".into()),
@@ -432,7 +473,7 @@ mod tests {
432473
entries.next().unwrap(),
433474
SysusersEntry::User {
434475
name: "bin".into(),
435-
uid: Some(1),
476+
uid: Some(1.into()),
436477
pgid: Some(1.into()),
437478
gecos: "bin".into(),
438479
home: Some("/bin".into()),
@@ -444,7 +485,7 @@ mod tests {
444485
entries.next().unwrap(),
445486
SysusersEntry::User {
446487
name: "adm".into(),
447-
uid: Some(3),
488+
uid: Some(3.into()),
448489
pgid: Some(4.into()),
449490
gecos: "adm".into(),
450491
home: Some("/var/adm".into()),
@@ -458,7 +499,7 @@ mod tests {
458499
entries.next().unwrap(),
459500
SysusersEntry::User {
460501
name: "qemu".into(),
461-
uid: Some(107),
502+
uid: Some(107.into()),
462503
pgid: Some(GroupReference::Name("qemu".into())),
463504
gecos: "qemu user".into(),
464505
home: None,
@@ -478,6 +519,27 @@ mod tests {
478519
);
479520
assert_eq!(entries.count(), 0);
480521

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+
481543
Ok(())
482544
}
483545

@@ -491,14 +553,14 @@ mod tests {
491553
entries.next().unwrap(),
492554
SysusersEntry::Group {
493555
name: "root".into(),
494-
id: Some(0),
556+
id: Some(0.into()),
495557
}
496558
);
497559
assert_eq!(
498560
entries.next().unwrap(),
499561
SysusersEntry::Group {
500562
name: "bin".into(),
501-
id: Some(1),
563+
id: Some(1.into()),
502564
}
503565
);
504566
assert_eq!(entries.count(), 28);

0 commit comments

Comments
 (0)