Skip to content
This repository was archived by the owner on Oct 2, 2023. It is now read-only.

Commit 21ad4a3

Browse files
authored
Merge pull request #9 from the-maldridge/indexed
Add index generation for caches
2 parents b851418 + 54f9b93 commit 21ad4a3

File tree

7 files changed

+173
-5
lines changed

7 files changed

+173
-5
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
examples/*/*.cache
1+
examples/*/*.cache*
22
vendor/

cache/cache.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
package cache
33

44
import (
5+
"bytes"
6+
"fmt"
57
"io"
8+
"sort"
69
)
710

811
type ACL func(e Entry) bool
@@ -57,3 +60,34 @@ func (c *Cache) WriteTo(w io.Writer) (int64, error) {
5760
}
5861
return total, nil
5962
}
63+
64+
// Index generates an index for the given cache on a particular
65+
// column. This is required for caches beyond a libnss-cache defined
66+
// size in order for them to be read correctly.
67+
func (c *Cache) Index(col int) bytes.Buffer {
68+
ordered := make([]string, len(c.entries))
69+
mapped := make(map[string]Entry, len(c.entries))
70+
for i := range c.entries {
71+
key := c.entries[i].Column(col)
72+
ordered[i] = key
73+
mapped[key] = c.entries[i]
74+
}
75+
76+
// libnss-cache depends on the indexes being ordered in order
77+
// to accelerate the system with a binary search.
78+
sort.Strings(ordered)
79+
80+
var b bytes.Buffer
81+
var offset int64
82+
for _, key := range ordered {
83+
b.WriteString(key)
84+
b.WriteByte(0)
85+
fmt.Fprintf(&b, "%08d", offset)
86+
for i := 0; i < 32-len(key)-1; i++ {
87+
b.WriteByte(0)
88+
}
89+
b.WriteString("\n")
90+
offset += int64(len(mapped[key].String())) + 1
91+
}
92+
return b
93+
}

cache/cache_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,36 @@ func TestCache_WriteTo(t *testing.T) {
101101
_, err := c.WriteTo(w)
102102
assert.NotNil(t, err)
103103
}
104+
105+
func TestCacheIndex(t *testing.T) {
106+
c := NewCache()
107+
c.Add(&PasswdEntry{
108+
Name: "foo",
109+
Passwd: "x",
110+
UID: 1000,
111+
GID: 1000,
112+
GECOS: "Mr Foo",
113+
Dir: "/home/foo",
114+
Shell: "/bin/bash",
115+
}, &PasswdEntry{
116+
Name: "admin",
117+
Passwd: "x",
118+
UID: 1002,
119+
GID: 1000,
120+
GECOS: "Admin",
121+
Dir: "/home/admin",
122+
Shell: "/bin/bash",
123+
}, &PasswdEntry{
124+
Name: "bar",
125+
Passwd: "x",
126+
UID: 1001,
127+
GID: 1000,
128+
GECOS: "Mrs Bar",
129+
Dir: "/home/bar",
130+
Shell: "/bin/bash",
131+
})
132+
133+
idx := c.Index(0)
134+
expected := []byte{97, 100, 109, 105, 110, 0, 48, 48, 48, 48, 48, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 98, 97, 114, 0, 48, 48, 48, 48, 48, 48, 52, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 102, 111, 111, 0, 48, 48, 48, 48, 48, 48, 57, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10}
135+
assert.Equal(t, expected, idx.Bytes())
136+
}

cache/entries.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
type Entry interface {
1010
fmt.Stringer
1111
io.WriterTo
12+
13+
Column(int) string
1214
}
1315

1416
// PasswdEntry describes an entry of the /etc/passwd file
@@ -44,6 +46,19 @@ func (e *PasswdEntry) args() []interface{} {
4446
}
4547
}
4648

49+
// Column returns the information from the requested columns or an
50+
// empty string if no column is known.
51+
func (e *PasswdEntry) Column(col int) string {
52+
switch col {
53+
case 0:
54+
return e.Name
55+
case 2:
56+
return fmt.Sprintf("%d", e.UID)
57+
default:
58+
return ""
59+
}
60+
}
61+
4762
func (e *PasswdEntry) String() string {
4863
return fmt.Sprintf(e.format(), e.args()...)
4964
}
@@ -89,6 +104,17 @@ func (e *ShadowEntry) args() []interface{} {
89104
}
90105
}
91106

107+
// Column returns the information from the requested columns or an
108+
// empty string if no column is known.
109+
func (e *ShadowEntry) Column(col int) string {
110+
switch col {
111+
case 0:
112+
return e.Name
113+
default:
114+
return ""
115+
}
116+
}
117+
92118
func (e *ShadowEntry) String() string {
93119
return fmt.Sprintf(e.format(), e.args()...)
94120
}
@@ -124,6 +150,19 @@ func (e *GroupEntry) args() []interface{} {
124150
}
125151
}
126152

153+
// Column returns the information from the requested columns or an
154+
// empty string if no column is known.
155+
func (e *GroupEntry) Column(col int) string {
156+
switch col {
157+
case 0:
158+
return e.Name
159+
case 2:
160+
return fmt.Sprintf("%d", e.GID)
161+
default:
162+
return ""
163+
}
164+
}
165+
127166
func (e *GroupEntry) String() string {
128167
return fmt.Sprintf(e.format(), e.args()...)
129168
}

cache/entries_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ func TestPasswdEntry_WriteTo(t *testing.T) {
3636
assert.Equal(t, expected, b.String())
3737
}
3838

39+
func TestPasswdEntry_Column(t *testing.T) {
40+
e := PasswdEntry{
41+
Name: "foo",
42+
UID: 1000,
43+
GID: 1000,
44+
GECOS: "Mr Foo",
45+
Dir: "/home/foo",
46+
Shell: "/usr/bin/bash",
47+
}
48+
49+
assert.Equal(t, "foo", e.Column(0))
50+
assert.Equal(t, "1000", e.Column(2))
51+
assert.Equal(t, "", e.Column(1))
52+
}
53+
3954
func TestShadowEntry_String(t *testing.T) {
4055
e := ShadowEntry{
4156
Name: "foo",
@@ -56,6 +71,14 @@ func TestShadowEntry_WriteTo(t *testing.T) {
5671
assert.Equal(t, expected, b.String())
5772
}
5873

74+
func TestShadowEntry_Column(t *testing.T) {
75+
e := ShadowEntry{
76+
Name: "foo",
77+
}
78+
assert.Equal(t, "foo", e.Column(0))
79+
assert.Equal(t, "", e.Column(1))
80+
}
81+
5982
func TestGroupEntry_String(t *testing.T) {
6083
e := GroupEntry{
6184
Name: "foo",
@@ -76,6 +99,16 @@ func TestGroupEntry_WriteTo(t *testing.T) {
7699
assert.Equal(t, expected, b.String())
77100
}
78101

102+
func TestGroupEntry_Column(t *testing.T) {
103+
e := GroupEntry{
104+
Name: "foo",
105+
GID: 1000,
106+
}
107+
assert.Equal(t, "foo", e.Column(0))
108+
assert.Equal(t, "1000", e.Column(2))
109+
assert.Equal(t, "", e.Column(1))
110+
}
111+
79112
func writerToError(i int64, e error) error {
80113
return e
81114
}

nsscache.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package nsscache
33

44
import (
55
"fmt"
6-
"path"
6+
"path/filepath"
77

88
"os"
99

@@ -75,7 +75,8 @@ func defaultWriteOptions() WriteOptions {
7575
}
7676
}
7777

78-
// WriteFiles write the content of the cache structs into files that libnss-cache can read
78+
// WriteFiles write the content of the cache structs into files that
79+
// libnss-cache can read.
7980
func (cm *CacheMap) WriteFiles(options *WriteOptions) error {
8081
wo := defaultWriteOptions()
8182
if options != nil {
@@ -88,12 +89,32 @@ func (cm *CacheMap) WriteFiles(options *WriteOptions) error {
8889
}
8990

9091
for _, name := range []string{"passwd", "shadow", "group"} {
91-
filepath := path.Join(wo.Directory, fmt.Sprintf("%s.%s", name, wo.Extension))
92+
fpath := filepath.Join(wo.Directory, fmt.Sprintf("%s.%s", name, wo.Extension))
9293
mode := 0644
9394
if name == "shadow" {
9495
mode = 0000
9596
}
96-
if err := WriteAtomic(filepath, (*cm)[name], os.FileMode(mode)); err != nil {
97+
if err := WriteAtomic(fpath, (*cm)[name], os.FileMode(mode)); err != nil {
98+
return err
99+
}
100+
}
101+
102+
idxCfg := []struct {
103+
cache string
104+
column int
105+
supext string
106+
}{
107+
{"passwd", 0, "ixname"},
108+
{"passwd", 2, "ixuid"},
109+
{"group", 0, "ixname"},
110+
{"group", 2, "ixgid"},
111+
{"shadow", 0, "ixname"},
112+
}
113+
114+
for _, idx := range idxCfg {
115+
fpath := filepath.Join(wo.Directory, fmt.Sprintf("%s.%s.%s", idx.cache, wo.Extension, idx.supext))
116+
idx := (*cm)[idx.cache].Index(idx.column)
117+
if err := WriteAtomic(fpath, &idx, os.FileMode(0644)); err != nil {
97118
return err
98119
}
99120
}

nsscache_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,16 @@ func TestCacheMap_WriteFiles(t *testing.T) {
164164

165165
_, err = os.Stat(path.Join(dir, "passwd.cachetest"))
166166
assert.Nil(t, err)
167+
_, err = os.Stat(path.Join(dir, "passwd.cachetest.ixname"))
168+
assert.Nil(t, err)
169+
_, err = os.Stat(path.Join(dir, "passwd.cachetest.ixuid"))
170+
assert.Nil(t, err)
167171
_, err = os.Stat(path.Join(dir, "group.cachetest"))
168172
assert.Nil(t, err)
173+
_, err = os.Stat(path.Join(dir, "group.cachetest.ixname"))
174+
assert.Nil(t, err)
175+
_, err = os.Stat(path.Join(dir, "group.cachetest.ixgid"))
176+
assert.Nil(t, err)
169177
_, err = os.Stat(path.Join(dir, "shadow.cachetest"))
170178
assert.Nil(t, err)
171179

0 commit comments

Comments
 (0)