From 196667c0c2f7f99816f9cd9f0f80bad0c5e73435 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sat, 1 Mar 2025 15:49:18 +0000 Subject: [PATCH] Add user/group ID mapping from passwd and group files Introduce functionality to parse `/etc/passwd` and `/etc/group` files to map symbolic user/group names to numeric IDs. This ensures consistency in file definitions by replacing symbolic references with their corresponding numeric IDs in the global configuration. An environment override for logger colors was also added. Signed-off-by: Arthur --- distrobuilder/main_incus.go | 26 ++++++++++++++++- distrobuilder/passwd.go | 56 +++++++++++++++++++++++++++++++++++++ shared/logger.go | 2 ++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 distrobuilder/passwd.go diff --git a/distrobuilder/main_incus.go b/distrobuilder/main_incus.go index c9c80b45..2b3b9d38 100644 --- a/distrobuilder/main_incus.go +++ b/distrobuilder/main_incus.go @@ -265,11 +265,35 @@ func (c *cmdIncus) run(cmd *cobra.Command, args []string, overlayDir string) err imageTargets |= shared.ImageTargetContainer } - for _, file := range c.global.definition.Files { + // Maps symbolic user/group names to their numeric IDs using information from passwd and group files. + userMap, groupMap, err := parsePasswdAndGroupFiles(overlayDir) + if err != nil { + c.global.logger.WithField("overlay", overlayDir).Warn("Could not parse passwd/group file: %w", err) + } + + for i, file := range c.global.definition.Files { if !shared.ApplyFilter(&file, c.global.definition.Image.Release, c.global.definition.Image.ArchitectureMapped, c.global.definition.Image.Variant, c.global.definition.Targets.Type, imageTargets) { continue } + if file.UID != "" && !isNumeric(file.UID) { + uid, exists := userMap[file.UID] + if exists { + c.global.definition.Files[i].UID = uid + } else { + c.global.logger.WithField("generator", file.Generator).Warnf("Could not find UID for user %q", file.UID) + } + } + + if file.UID != "" && !isNumeric(file.GID) { + gid, exists := groupMap[file.GID] + if exists { + c.global.definition.Files[i].GID = gid + } else { + c.global.logger.WithField("generator", file.Generator).Warnf("Could not find GID for group %q", file.GID) + } + } + generator, err := generators.Load(file.Generator, c.global.logger, c.global.flagCacheDir, overlayDir, file, *c.global.definition) if err != nil { return fmt.Errorf("Failed to load generator %q: %w", file.Generator, err) diff --git a/distrobuilder/passwd.go b/distrobuilder/passwd.go new file mode 100644 index 00000000..e2f1aed0 --- /dev/null +++ b/distrobuilder/passwd.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" +) + +// parsePasswdAndGroupFiles reads passwd and group files from the given root directory +// and returns mappings of names to IDs for both users and groups. +func parsePasswdAndGroupFiles(rootDir string) (map[string]string, map[string]string, error) { + userMap, err := parsePasswdFile(filepath.Join(rootDir, "/etc/passwd")) + if err != nil { + return nil, nil, fmt.Errorf("parsing passwd file: %w", err) + } + + groupMap, err := parsePasswdFile(filepath.Join(rootDir, "/etc/group")) + if err != nil { + return nil, nil, fmt.Errorf("parsing group file: %w", err) + } + + return userMap, groupMap, nil +} + +// parsePasswdFile reads a passwd-format file and returns a map of names to IDs. +func parsePasswdFile(path string) (map[string]string, error) { + idMap := make(map[string]string) + + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("reading file %s: %w", path, err) + } + + for _, line := range strings.Split(string(data), "\n") { + if line == "" { + continue + } + + fields := strings.Split(line, ":") + if len(fields) < 3 { + continue + } + + idMap[fields[0]] = fields[2] + } + + return idMap, nil +} + +// isNumeric is a helper function to check if a string is numeric. +func isNumeric(s string) bool { + _, err := strconv.Atoi(s) + return err == nil +} diff --git a/shared/logger.go b/shared/logger.go index ca04b9b5..5bcc96c7 100644 --- a/shared/logger.go +++ b/shared/logger.go @@ -17,6 +17,8 @@ func GetLogger(debug bool) (*logrus.Logger, error) { PadLevelText: true, } + formatter.EnvironmentOverrideColors = true + logger.Formatter = &formatter if debug {