diff options
author | Ayush Ranjan <ayushranjan@google.com> | 2019-07-23 15:50:35 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2019-07-23 15:51:50 -0700 |
commit | bd7708956f0c9e00e88eff9b35b22eea75e71f5f (patch) | |
tree | 08d110a027a067f202d672d524d09546c68dd6fc /pkg/sentry/fs/ext/extent_test.go | |
parent | 04cbb13ce9b151cf906f42e3f18ce3a875f01f63 (diff) |
ext: Added extent tree building logic.
PiperOrigin-RevId: 259628657
Diffstat (limited to 'pkg/sentry/fs/ext/extent_test.go')
-rw-r--r-- | pkg/sentry/fs/ext/extent_test.go | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/pkg/sentry/fs/ext/extent_test.go b/pkg/sentry/fs/ext/extent_test.go new file mode 100644 index 000000000..c8c52f836 --- /dev/null +++ b/pkg/sentry/fs/ext/extent_test.go @@ -0,0 +1,184 @@ +// Copyright 2019 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ext + +import ( + "bytes" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "gvisor.dev/gvisor/pkg/binary" + "gvisor.dev/gvisor/pkg/sentry/fs/ext/disklayout" +) + +// TestExtentTree tests the extent tree building logic. +// +// Test tree: +// 0.{Head}[Idx][Idx] +// / \ +// / \ +// 1.{Head}[Ext][Ext] 2.{Head}[Idx] +// / | \ +// [Phy] [Phy, Phy] 3.{Head}[Ext] +// | +// [Phy, Phy, Phy] +// +// Legend: +// - Head = ExtentHeader +// - Idx = ExtentIdx +// - Ext = Extent +// - Phy = Physical Block +// +// Please note that ext4 might not construct extent trees looking like this. +// This is purely for testing the tree traversal logic. +func TestExtentTree(t *testing.T) { + blkSize := uint64(64) // No block has more than 1 header + 4 entries. + mockDisk := make([]byte, blkSize*10) + mockInode := &inode{diskInode: &disklayout.InodeNew{}} + + node3 := &disklayout.ExtentNode{ + Header: disklayout.ExtentHeader{ + Magic: disklayout.ExtentMagic, + NumEntries: 1, + MaxEntries: 4, + Height: 0, + }, + Entries: []disklayout.ExtentEntryPair{ + { + Entry: &disklayout.Extent{ + FirstFileBlock: 3, + Length: 3, + StartBlockLo: 6, + }, + Node: nil, + }, + }, + } + + node2 := &disklayout.ExtentNode{ + Header: disklayout.ExtentHeader{ + Magic: disklayout.ExtentMagic, + NumEntries: 1, + MaxEntries: 4, + Height: 1, + }, + Entries: []disklayout.ExtentEntryPair{ + { + Entry: &disklayout.ExtentIdx{ + FirstFileBlock: 3, + ChildBlockLo: 2, + }, + Node: node3, + }, + }, + } + + node1 := &disklayout.ExtentNode{ + Header: disklayout.ExtentHeader{ + Magic: disklayout.ExtentMagic, + NumEntries: 2, + MaxEntries: 4, + Height: 0, + }, + Entries: []disklayout.ExtentEntryPair{ + { + Entry: &disklayout.Extent{ + FirstFileBlock: 0, + Length: 1, + StartBlockLo: 3, + }, + Node: nil, + }, + { + Entry: &disklayout.Extent{ + FirstFileBlock: 1, + Length: 2, + StartBlockLo: 4, + }, + Node: nil, + }, + }, + } + + node0 := &disklayout.ExtentNode{ + Header: disklayout.ExtentHeader{ + Magic: disklayout.ExtentMagic, + NumEntries: 2, + MaxEntries: 4, + Height: 2, + }, + Entries: []disklayout.ExtentEntryPair{ + { + Entry: &disklayout.ExtentIdx{ + FirstFileBlock: 0, + ChildBlockLo: 0, + }, + Node: node1, + }, + { + Entry: &disklayout.ExtentIdx{ + FirstFileBlock: 3, + ChildBlockLo: 1, + }, + Node: node2, + }, + }, + } + + writeTree(mockInode, mockDisk, node0, blkSize) + + r := bytes.NewReader(mockDisk) + if err := mockInode.buildExtTree(r, blkSize); err != nil { + t.Fatalf("inode.buildExtTree failed: %v", err) + } + + if diff := cmp.Diff(&mockInode.root, node0, cmpopts.IgnoreUnexported(disklayout.ExtentNode{})); diff != "" { + t.Errorf("extent tree mismatch (-want +got):\n%s", diff) + } +} + +// writeTree writes the tree represented by `root` to the inode and disk passed. +func writeTree(in *inode, disk []byte, root *disklayout.ExtentNode, blkSize uint64) { + rootData := binary.Marshal(nil, binary.LittleEndian, root.Header) + for _, ep := range root.Entries { + rootData = binary.Marshal(rootData, binary.LittleEndian, ep.Entry) + } + + copy(in.diskInode.Data(), rootData) + + if root.Header.Height > 0 { + for _, ep := range root.Entries { + writeTreeToDisk(disk, ep, blkSize) + } + } +} + +// writeTreeToDisk is the recursive step for writeTree which writes the tree +// on the disk only. +func writeTreeToDisk(disk []byte, curNode disklayout.ExtentEntryPair, blkSize uint64) { + nodeData := binary.Marshal(nil, binary.LittleEndian, curNode.Node.Header) + for _, ep := range curNode.Node.Entries { + nodeData = binary.Marshal(nodeData, binary.LittleEndian, ep.Entry) + } + + copy(disk[curNode.Entry.PhysicalBlock()*blkSize:], nodeData) + + if curNode.Node.Header.Height > 0 { + for _, ep := range curNode.Node.Entries { + writeTreeToDisk(disk, ep, blkSize) + } + } +} |