diff options
Diffstat (limited to 'internal/inomap')
-rw-r--r-- | internal/inomap/inomap.go | 63 | ||||
-rw-r--r-- | internal/inomap/inomap_test.go | 81 | ||||
-rw-r--r-- | internal/inomap/qino.go | 25 |
3 files changed, 169 insertions, 0 deletions
diff --git a/internal/inomap/inomap.go b/internal/inomap/inomap.go new file mode 100644 index 0000000..f8909c7 --- /dev/null +++ b/internal/inomap/inomap.go @@ -0,0 +1,63 @@ +package inomap + +import ( + "sync" + "syscall" +) + +// UINT64_MAX = 18446744073709551615 +const inumTranslateBase = 10000000000000000000 + +// InoMap ... see New() for description. +type InoMap struct { + sync.Mutex + baseDev uint64 + translate map[QIno]uint64 + translateNext uint64 +} + +// New returns a new InoMap. +// +// InoMap translates (device uint64, inode uint64) pairs to unique uint64 +// inode numbers. +// Inode numbers on the "baseDev" are passed through unchanged (as long as they +// are not higher than inumTranslateBase). +// Inode numbers on other devices are remapped to the number space above +// 10000000000000000000. The mapping is stored in a simple Go map. Entries +// can only be added and are never removed. +func New(baseDev uint64) *InoMap { + return &InoMap{ + baseDev: baseDev, + translate: make(map[QIno]uint64), + translateNext: inumTranslateBase, + } +} + +// Translate maps the passed-in (device, inode) pair to a unique inode number. +func (m *InoMap) Translate(in QIno) (out uint64) { + if in.Dev == m.baseDev && in.Ino < inumTranslateBase { + return in.Ino + } + m.Lock() + defer m.Unlock() + out = m.translate[in] + if out != 0 { + return out + } + out = m.translateNext + m.translate[in] = m.translateNext + m.translateNext++ + return out +} + +// TranslateStat translates the inode number contained in "st" if neccessary. +// Convience wrapper around Translate(). +func (m *InoMap) TranslateStat(st *syscall.Stat_t) { + in := QInoFromStat(st) + st.Ino = m.Translate(in) +} + +// Count returns the number of entries in the translation table. +func (m *InoMap) Count() int { + return len(m.translate) +} diff --git a/internal/inomap/inomap_test.go b/internal/inomap/inomap_test.go new file mode 100644 index 0000000..3c0ea7d --- /dev/null +++ b/internal/inomap/inomap_test.go @@ -0,0 +1,81 @@ +package inomap + +import ( + "sync" + "testing" +) + +func TestTranslate(t *testing.T) { + const baseDev = 12345 + m := New(baseDev) + + q := QIno{Dev: baseDev, Ino: 1} + out := m.Translate(q) + if out != 1 { + t.Errorf("expected 1, got %d", out) + } + q.Ino = inumTranslateBase + out = m.Translate(q) + if out < inumTranslateBase { + t.Errorf("got %d", out) + } + out2 := m.Translate(q) + if out2 != out { + t.Errorf("unstable mapping: %d %d", out2, out) + } +} + +func TestTranslateStress(t *testing.T) { + const baseDev = 12345 + m := New(baseDev) + var wg sync.WaitGroup + wg.Add(4) + go func() { + q := QIno{Dev: baseDev} + for i := uint64(1); i <= 10000; i++ { + q.Ino = i + out := m.Translate(q) + if out != i { + t.Fail() + } + } + wg.Done() + }() + go func() { + q := QIno{Dev: baseDev} + for i := uint64(1); i <= 10000; i++ { + q.Ino = inumTranslateBase + i + out := m.Translate(q) + if out < inumTranslateBase { + t.Fail() + } + } + wg.Done() + }() + go func() { + q := QIno{Dev: 9999999} + for i := uint64(1); i <= 10000; i++ { + q.Ino = i + out := m.Translate(q) + if out < inumTranslateBase { + t.Fail() + } + } + wg.Done() + }() + go func() { + q := QIno{Dev: 4444444} + for i := uint64(1); i <= 10000; i++ { + q.Ino = i + out := m.Translate(q) + if out < inumTranslateBase { + t.Fail() + } + } + wg.Done() + }() + wg.Wait() + if m.Count() != 30000 { + t.Fail() + } +} diff --git a/internal/inomap/qino.go b/internal/inomap/qino.go new file mode 100644 index 0000000..8f99004 --- /dev/null +++ b/internal/inomap/qino.go @@ -0,0 +1,25 @@ +package inomap + +import ( + "syscall" +) + +// QIno = Qualified Inode number. +// Uniquely identifies a backing file through the device number, +// inode number pair. +type QIno struct { + // Stat_t.{Dev,Ino} is uint64 on 32- and 64-bit Linux + Dev uint64 + Ino uint64 +} + +// QInoFromStat fills a new QIno struct with the passed Stat_t info. +func QInoFromStat(st *syscall.Stat_t) QIno { + return QIno{ + // There are some architectures that use 32-bit values here + // (darwin, freebsd-32, maybe others). Add and explicit cast to make + // this function work everywhere. + Dev: uint64(st.Dev), + Ino: uint64(st.Ino), + } +} |