// Parse /etc/group files and serialize them
-// Copyright (C) 2019 Simon Ruderich
+// Copyright (C) 2019-2021 Simon Ruderich
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
"encoding/binary"
"fmt"
"io"
+ "math"
"sort"
"strconv"
"strings"
Name string
Passwd string
Gid uint64
- Members string
+ Members string // "," separated
}
func toKey(g Group) GroupKey {
}
// ParseGroups parses a file in the format of /etc/group and returns all
-// entries as Group structs.
+// entries as slice of Group structs.
func ParseGroups(r io.Reader) ([]Group, error) {
var res []Group
- s := bufio.NewScanner(r)
- for s.Scan() {
- t := s.Text()
+ s := bufio.NewReader(r)
+ for {
+ t, err := s.ReadString('\n')
+ if err != nil {
+ if err == io.EOF {
+ if t != "" {
+ return nil, fmt.Errorf(
+ "no newline in last line: %q",
+ t)
+ }
+ break
+ }
+ return nil, err
+ }
x := strings.Split(t, ":")
if len(x) != 4 {
return nil, errors.Wrapf(err, "invalid gid in line %q", t)
}
+ // ReadString() contains the delimiter
+ x[3] = strings.TrimSuffix(x[3], "\n")
+
var members []string
// No members must result in empty slice, not slice with the
// empty string
Members: members,
})
}
- err := s.Err()
- if err != nil {
- return nil, err
- }
return res, nil
}
-func SerializeGroup(g Group) []byte {
+func SerializeGroup(g Group) ([]byte, error) {
le := binary.LittleEndian
// Concatenate all (NUL-terminated) strings and store the offsets
}
// And the group members concatenated as above
data.Write(mems.Bytes())
+ // Ensure the offsets can fit the length of this entry
+ if data.Len() > math.MaxUint16 {
+ return nil, fmt.Errorf("group too large to serialize: %v, %v",
+ data.Len(), g)
+ }
size := uint16(data.Len())
var res bytes.Buffer // serialized result
// struct are 8 byte aligned
alignBufferTo(&res, 8)
- return res.Bytes()
+ return res.Bytes(), nil
}
func SerializeGroups(w io.Writer, grs []Group) error {
for _, g := range grs {
// TODO: warn about duplicate entries
offsets[toKey(g)] = uint64(data.Len())
- data.Write(SerializeGroup(g))
+ x, err := SerializeGroup(g)
+ if err != nil {
+ return err
+ }
+ data.Write(x)
}
// Copy to prevent sorting from modifying the argument
tmp := make([]byte, 8)
// Create index "sorted" in input order, used when iterating over all
- // passwd entries (getgrent_r); keeping the original order makes
+ // group entries (getgrent_r); keeping the original order makes
// debugging easier
var indexOrig bytes.Buffer
for _, g := range grs {