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

Commit

Permalink
Merge pull request #9 from the-maldridge/indexed
Browse files Browse the repository at this point in the history
Add index generation for caches
  • Loading branch information
MiLk authored Nov 15, 2019
2 parents b851418 + 54f9b93 commit 21ad4a3
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
examples/*/*.cache
examples/*/*.cache*
vendor/
34 changes: 34 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
package cache

import (
"bytes"
"fmt"
"io"
"sort"
)

type ACL func(e Entry) bool
Expand Down Expand Up @@ -57,3 +60,34 @@ func (c *Cache) WriteTo(w io.Writer) (int64, error) {
}
return total, nil
}

// Index generates an index for the given cache on a particular
// column. This is required for caches beyond a libnss-cache defined
// size in order for them to be read correctly.
func (c *Cache) Index(col int) bytes.Buffer {
ordered := make([]string, len(c.entries))
mapped := make(map[string]Entry, len(c.entries))
for i := range c.entries {
key := c.entries[i].Column(col)
ordered[i] = key
mapped[key] = c.entries[i]
}

// libnss-cache depends on the indexes being ordered in order
// to accelerate the system with a binary search.
sort.Strings(ordered)

var b bytes.Buffer
var offset int64
for _, key := range ordered {
b.WriteString(key)
b.WriteByte(0)
fmt.Fprintf(&b, "%08d", offset)
for i := 0; i < 32-len(key)-1; i++ {
b.WriteByte(0)
}
b.WriteString("\n")
offset += int64(len(mapped[key].String())) + 1
}
return b
}
33 changes: 33 additions & 0 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,36 @@ func TestCache_WriteTo(t *testing.T) {
_, err := c.WriteTo(w)
assert.NotNil(t, err)
}

func TestCacheIndex(t *testing.T) {
c := NewCache()
c.Add(&PasswdEntry{
Name: "foo",
Passwd: "x",
UID: 1000,
GID: 1000,
GECOS: "Mr Foo",
Dir: "/home/foo",
Shell: "/bin/bash",
}, &PasswdEntry{
Name: "admin",
Passwd: "x",
UID: 1002,
GID: 1000,
GECOS: "Admin",
Dir: "/home/admin",
Shell: "/bin/bash",
}, &PasswdEntry{
Name: "bar",
Passwd: "x",
UID: 1001,
GID: 1000,
GECOS: "Mrs Bar",
Dir: "/home/bar",
Shell: "/bin/bash",
})

idx := c.Index(0)
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}
assert.Equal(t, expected, idx.Bytes())
}
39 changes: 39 additions & 0 deletions cache/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
type Entry interface {
fmt.Stringer
io.WriterTo

Column(int) string
}

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

// Column returns the information from the requested columns or an
// empty string if no column is known.
func (e *PasswdEntry) Column(col int) string {
switch col {
case 0:
return e.Name
case 2:
return fmt.Sprintf("%d", e.UID)
default:
return ""
}
}

func (e *PasswdEntry) String() string {
return fmt.Sprintf(e.format(), e.args()...)
}
Expand Down Expand Up @@ -89,6 +104,17 @@ func (e *ShadowEntry) args() []interface{} {
}
}

// Column returns the information from the requested columns or an
// empty string if no column is known.
func (e *ShadowEntry) Column(col int) string {
switch col {
case 0:
return e.Name
default:
return ""
}
}

func (e *ShadowEntry) String() string {
return fmt.Sprintf(e.format(), e.args()...)
}
Expand Down Expand Up @@ -124,6 +150,19 @@ func (e *GroupEntry) args() []interface{} {
}
}

// Column returns the information from the requested columns or an
// empty string if no column is known.
func (e *GroupEntry) Column(col int) string {
switch col {
case 0:
return e.Name
case 2:
return fmt.Sprintf("%d", e.GID)
default:
return ""
}
}

func (e *GroupEntry) String() string {
return fmt.Sprintf(e.format(), e.args()...)
}
Expand Down
33 changes: 33 additions & 0 deletions cache/entries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@ func TestPasswdEntry_WriteTo(t *testing.T) {
assert.Equal(t, expected, b.String())
}

func TestPasswdEntry_Column(t *testing.T) {
e := PasswdEntry{
Name: "foo",
UID: 1000,
GID: 1000,
GECOS: "Mr Foo",
Dir: "/home/foo",
Shell: "/usr/bin/bash",
}

assert.Equal(t, "foo", e.Column(0))
assert.Equal(t, "1000", e.Column(2))
assert.Equal(t, "", e.Column(1))
}

func TestShadowEntry_String(t *testing.T) {
e := ShadowEntry{
Name: "foo",
Expand All @@ -56,6 +71,14 @@ func TestShadowEntry_WriteTo(t *testing.T) {
assert.Equal(t, expected, b.String())
}

func TestShadowEntry_Column(t *testing.T) {
e := ShadowEntry{
Name: "foo",
}
assert.Equal(t, "foo", e.Column(0))
assert.Equal(t, "", e.Column(1))
}

func TestGroupEntry_String(t *testing.T) {
e := GroupEntry{
Name: "foo",
Expand All @@ -76,6 +99,16 @@ func TestGroupEntry_WriteTo(t *testing.T) {
assert.Equal(t, expected, b.String())
}

func TestGroupEntry_Column(t *testing.T) {
e := GroupEntry{
Name: "foo",
GID: 1000,
}
assert.Equal(t, "foo", e.Column(0))
assert.Equal(t, "1000", e.Column(2))
assert.Equal(t, "", e.Column(1))
}

func writerToError(i int64, e error) error {
return e
}
29 changes: 25 additions & 4 deletions nsscache.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package nsscache

import (
"fmt"
"path"
"path/filepath"

"os"

Expand Down Expand Up @@ -75,7 +75,8 @@ func defaultWriteOptions() WriteOptions {
}
}

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

for _, name := range []string{"passwd", "shadow", "group"} {
filepath := path.Join(wo.Directory, fmt.Sprintf("%s.%s", name, wo.Extension))
fpath := filepath.Join(wo.Directory, fmt.Sprintf("%s.%s", name, wo.Extension))
mode := 0644
if name == "shadow" {
mode = 0000
}
if err := WriteAtomic(filepath, (*cm)[name], os.FileMode(mode)); err != nil {
if err := WriteAtomic(fpath, (*cm)[name], os.FileMode(mode)); err != nil {
return err
}
}

idxCfg := []struct {
cache string
column int
supext string
}{
{"passwd", 0, "ixname"},
{"passwd", 2, "ixuid"},
{"group", 0, "ixname"},
{"group", 2, "ixgid"},
{"shadow", 0, "ixname"},
}

for _, idx := range idxCfg {
fpath := filepath.Join(wo.Directory, fmt.Sprintf("%s.%s.%s", idx.cache, wo.Extension, idx.supext))
idx := (*cm)[idx.cache].Index(idx.column)
if err := WriteAtomic(fpath, &idx, os.FileMode(0644)); err != nil {
return err
}
}
Expand Down
8 changes: 8 additions & 0 deletions nsscache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,16 @@ func TestCacheMap_WriteFiles(t *testing.T) {

_, err = os.Stat(path.Join(dir, "passwd.cachetest"))
assert.Nil(t, err)
_, err = os.Stat(path.Join(dir, "passwd.cachetest.ixname"))
assert.Nil(t, err)
_, err = os.Stat(path.Join(dir, "passwd.cachetest.ixuid"))
assert.Nil(t, err)
_, err = os.Stat(path.Join(dir, "group.cachetest"))
assert.Nil(t, err)
_, err = os.Stat(path.Join(dir, "group.cachetest.ixname"))
assert.Nil(t, err)
_, err = os.Stat(path.Join(dir, "group.cachetest.ixgid"))
assert.Nil(t, err)
_, err = os.Stat(path.Join(dir, "shadow.cachetest"))
assert.Nil(t, err)

Expand Down

0 comments on commit 21ad4a3

Please sign in to comment.