"encoding/binary"
"fmt"
"io"
+ "math"
"sort"
"strconv"
"strings"
}
// ParsePasswds parses a file in the format of /etc/passwd and returns all
-// entries as Passwd structs.
+// entries as slice of Passwd structs.
func ParsePasswds(r io.Reader) ([]Passwd, error) {
var res []Passwd
- 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) != 7 {
Gid: gid,
Gecos: x[4],
Dir: x[5],
- Shell: x[6],
+ // ReadString() contains the delimiter
+ Shell: strings.TrimSuffix(x[6], "\n"),
})
}
- err := s.Err()
- if err != nil {
- return nil, err
- }
-
return res, nil
}
-func SerializePasswd(p Passwd) []byte {
+func SerializePasswd(p Passwd) ([]byte, error) {
// Concatenate all (NUL-terminated) strings and store the offsets
var data bytes.Buffer
data.Write([]byte(p.Name))
offShell := uint16(data.Len())
data.Write([]byte(p.Shell))
data.WriteByte(0)
+ // Ensure the offsets can fit the length of this entry
+ if data.Len() > math.MaxUint16 {
+ return nil, fmt.Errorf("passwd too large to serialize: %v, %v",
+ data.Len(), p)
+ }
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 SerializePasswds(w io.Writer, pws []Passwd) error {
for _, p := range pws {
// TODO: warn about duplicate entries
offsets[p] = uint64(data.Len())
- data.Write(SerializePasswd(p))
+ x, err := SerializePasswd(p)
+ if err != nil {
+ return err
+ }
+ data.Write(x)
}
// Copy to prevent sorting from modifying the argument