summaryrefslogtreecommitdiff
path: root/internal/inomap
diff options
context:
space:
mode:
Diffstat (limited to 'internal/inomap')
-rw-r--r--internal/inomap/inomap.go63
-rw-r--r--internal/inomap/inomap_test.go81
-rw-r--r--internal/inomap/qino.go25
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),
+ }
+}