// 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 disklayout import ( "fmt" "strconv" "testing" "gvisor.dev/gvisor/pkg/sentry/kernel/time" ) // TestInodeSize tests that the inode structs are of the correct size. func TestInodeSize(t *testing.T) { assertSize(t, InodeOld{}, oldInodeSize) // This was updated from 156 bytes to 160 bytes in Oct 2015. assertSize(t, InodeNew{}, 160) } // TestTimestampSeconds tests that the seconds part of [a/c/m] timestamps in // ext4 inode structs are decoded correctly. // // These tests are derived from the table under https://www.kernel.org/doc/html/latest/filesystems/ext4/dynamic.html#inode-timestamps. func TestTimestampSeconds(t *testing.T) { type timestampTest struct { // msbSet tells if the most significant bit of InodeOld.[X]TimeRaw is set. // If this is set then the 32-bit time is negative. msbSet bool // lowerBound tells if we should take the lowest possible value of // InodeOld.[X]TimeRaw while satisfying test.msbSet condition. If set to // false it tells to take the highest possible value. lowerBound bool // extraBits is InodeNew.[X]TimeExtra. extraBits uint32 // want is the kernel time struct that is expected. want time.Time } tests := []timestampTest{ // 1901-12-13 { msbSet: true, lowerBound: true, extraBits: 0, want: time.FromUnix(int64(-0x80000000), 0), }, // 1969-12-31 { msbSet: true, lowerBound: false, extraBits: 0, want: time.FromUnix(int64(-1), 0), }, // 1970-01-01 { msbSet: false, lowerBound: true, extraBits: 0, want: time.FromUnix(int64(0), 0), }, // 2038-01-19 { msbSet: false, lowerBound: false, extraBits: 0, want: time.FromUnix(int64(0x7fffffff), 0), }, // 2038-01-19 { msbSet: true, lowerBound: true, extraBits: 1, want: time.FromUnix(int64(0x80000000), 0), }, // 2106-02-07 { msbSet: true, lowerBound: false, extraBits: 1, want: time.FromUnix(int64(0xffffffff), 0), }, // 2106-02-07 { msbSet: false, lowerBound: true, extraBits: 1, want: time.FromUnix(int64(0x100000000), 0), }, // 2174-02-25 { msbSet: false, lowerBound: false, extraBits: 1, want: time.FromUnix(int64(0x17fffffff), 0), }, // 2174-02-25 { msbSet: true, lowerBound: true, extraBits: 2, want: time.FromUnix(int64(0x180000000), 0), }, // 2242-03-16 { msbSet: true, lowerBound: false, extraBits: 2, want: time.FromUnix(int64(0x1ffffffff), 0), }, // 2242-03-16 { msbSet: false, lowerBound: true, extraBits: 2, want: time.FromUnix(int64(0x200000000), 0), }, // 2310-04-04 { msbSet: false, lowerBound: false, extraBits: 2, want: time.FromUnix(int64(0x27fffffff), 0), }, // 2310-04-04 { msbSet: true, lowerBound: true, extraBits: 3, want: time.FromUnix(int64(0x280000000), 0), }, // 2378-04-22 { msbSet: true, lowerBound: false, extraBits: 3, want: time.FromUnix(int64(0x2ffffffff), 0), }, // 2378-04-22 { msbSet: false, lowerBound: true, extraBits: 3, want: time.FromUnix(int64(0x300000000), 0), }, // 2446-05-10 { msbSet: false, lowerBound: false, extraBits: 3, want: time.FromUnix(int64(0x37fffffff), 0), }, } lowerMSB0 := int32(0) // binary: 00000000 00000000 00000000 00000000 upperMSB0 := int32(0x7fffffff) // binary: 01111111 11111111 11111111 11111111 lowerMSB1 := int32(-0x80000000) // binary: 10000000 00000000 00000000 00000000 upperMSB1 := int32(-1) // binary: 11111111 11111111 11111111 11111111 get32BitTime := func(test timestampTest) int32 { if test.msbSet { if test.lowerBound { return lowerMSB1 } return upperMSB1 } if test.lowerBound { return lowerMSB0 } return upperMSB0 } getTestName := func(test timestampTest) string { return fmt.Sprintf( "Tests time decoding with epoch bits 0b%s and 32-bit raw time: MSB set=%t, lower bound=%t", strconv.FormatInt(int64(test.extraBits), 2), test.msbSet, test.lowerBound, ) } for _, test := range tests { t.Run(getTestName(test), func(t *testing.T) { if got := fromExtraTime(get32BitTime(test), test.extraBits); got != test.want { t.Errorf("Expected: %v, Got: %v", test.want, got) } }) } }