diff options
| author | Jakob Unterwurzacher | 2019-11-16 21:35:26 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2019-11-16 21:35:26 +0100 | 
| commit | e5d5ab397384001566d573a13a142ed1bc2bc2aa (patch) | |
| tree | d9b268f51a5cb33d8876a35d12b6c5c8ac9e17a5 | |
| parent | 31ff557f85cca54b935231668d382cfc0e61751a (diff) | |
openfiletable: add inummap
Generates unique inode numbers for files on different
devices.
https://github.com/rfjakob/gocryptfs/issues/435
| -rw-r--r-- | internal/openfiletable/inummap.go | 63 | ||||
| -rw-r--r-- | internal/openfiletable/inummap_test.go | 81 | 
2 files changed, 144 insertions, 0 deletions
| diff --git a/internal/openfiletable/inummap.go b/internal/openfiletable/inummap.go new file mode 100644 index 0000000..d9f6862 --- /dev/null +++ b/internal/openfiletable/inummap.go @@ -0,0 +1,63 @@ +package openfiletable + +import ( +	"sync" +	"syscall" +) + +// UINT64_MAX           = 18446744073709551615 +const inumTranslateBase = 10000000000000000000 + +// InumMap ... see NewInumMap() for description. +type InumMap struct { +	sync.Mutex +	baseDev       uint64 +	translate     map[QIno]uint64 +	translateNext uint64 +} + +// NewInumMap returns a new inumMap. +// +// inumMap 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 NewInumMap(baseDev uint64) *InumMap { +	return &InumMap{ +		baseDev:       baseDev, +		translate:     make(map[QIno]uint64), +		translateNext: inumTranslateBase, +	} +} + +// Translate maps the passed-in (device, inode) pair to a unique inode number. +func (m *InumMap) 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 *InumMap) 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 *InumMap) Count() int { +	return len(m.translate) +} diff --git a/internal/openfiletable/inummap_test.go b/internal/openfiletable/inummap_test.go new file mode 100644 index 0000000..85438bd --- /dev/null +++ b/internal/openfiletable/inummap_test.go @@ -0,0 +1,81 @@ +package openfiletable + +import ( +	"sync" +	"testing" +) + +func TestTranslate(t *testing.T) { +	const baseDev = 12345 +	m := NewInumMap(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 := NewInumMap(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() +	} +} | 
