diff options
Diffstat (limited to 'pkg/sentry/fs/ext')
-rw-r--r-- | pkg/sentry/fs/ext/BUILD | 11 | ||||
-rw-r--r-- | pkg/sentry/fs/ext/disklayout/block_group.go | 10 | ||||
-rw-r--r-- | pkg/sentry/fs/ext/disklayout/superblock.go | 9 | ||||
-rw-r--r-- | pkg/sentry/fs/ext/disklayout/superblock_old.go | 3 | ||||
-rw-r--r-- | pkg/sentry/fs/ext/ext.go | 26 | ||||
-rw-r--r-- | pkg/sentry/fs/ext/utils.go | 103 |
6 files changed, 157 insertions, 5 deletions
diff --git a/pkg/sentry/fs/ext/BUILD b/pkg/sentry/fs/ext/BUILD index 3c2a02eac..b386f31c8 100644 --- a/pkg/sentry/fs/ext/BUILD +++ b/pkg/sentry/fs/ext/BUILD @@ -4,8 +4,15 @@ load("//tools/go_stateify:defs.bzl", "go_library") go_library( name = "ext", - srcs = ["ext.go"], + srcs = [ + "ext.go", + "utils.go", + ], importpath = "gvisor.dev/gvisor/pkg/sentry/fs/ext", visibility = ["//pkg/sentry:internal"], - deps = ["//pkg/sentry/fs/ext/disklayout"], + deps = [ + "//pkg/abi/linux", + "//pkg/sentry/fs/ext/disklayout", + "//pkg/syserror", + ], ) diff --git a/pkg/sentry/fs/ext/disklayout/block_group.go b/pkg/sentry/fs/ext/disklayout/block_group.go index 32ea3d97d..ad6f4fef8 100644 --- a/pkg/sentry/fs/ext/disklayout/block_group.go +++ b/pkg/sentry/fs/ext/disklayout/block_group.go @@ -18,6 +18,16 @@ package disklayout // is split into a series of block groups. This provides an access layer to // information needed to access and use a block group. // +// Location: +// - The block group descriptor table is always placed in the blocks +// immediately after the block containing the superblock. +// - The 1st block group descriptor in the original table is in the +// (sb.FirstDataBlock() + 1)th block. +// - See SuperBlock docs to see where the block group descriptor table is +// replicated. +// - sb.BgDescSize() must be used as the block group descriptor entry size +// while reading the table from disk. +// // See https://www.kernel.org/doc/html/latest/filesystems/ext4/globals.html#block-group-descriptors. type BlockGroup interface { // InodeTable returns the absolute block number of the block containing the diff --git a/pkg/sentry/fs/ext/disklayout/superblock.go b/pkg/sentry/fs/ext/disklayout/superblock.go index e4b8f46fb..7a337a5e0 100644 --- a/pkg/sentry/fs/ext/disklayout/superblock.go +++ b/pkg/sentry/fs/ext/disklayout/superblock.go @@ -14,6 +14,11 @@ package disklayout +const ( + // SbOffset is the absolute offset at which the superblock is placed. + SbOffset = 1024 +) + // SuperBlock should be implemented by structs representing the ext superblock. // The superblock holds a lot of information about the enclosing filesystem. // This interface aims to provide access methods to important information held @@ -57,8 +62,6 @@ type SuperBlock interface { // // If the filesystem has 1kb data blocks then this should return 1. For all // other configurations, this typically returns 0. - // - // The first block group descriptor is in (FirstDataBlock() + 1)th block. FirstDataBlock() uint32 // BlockSize returns the size of one data block in this filesystem. @@ -128,7 +131,7 @@ type SuperBlock interface { } // SbRevision is the type for superblock revisions. -type SbRevision int +type SbRevision uint32 // Super block revisions. const ( diff --git a/pkg/sentry/fs/ext/disklayout/superblock_old.go b/pkg/sentry/fs/ext/disklayout/superblock_old.go index c74953610..aada8b550 100644 --- a/pkg/sentry/fs/ext/disklayout/superblock_old.go +++ b/pkg/sentry/fs/ext/disklayout/superblock_old.go @@ -44,6 +44,9 @@ type SuperBlockOld struct { DefResGID uint16 } +// Compiles only if SuperBlockOld implements SuperBlock. +var _ SuperBlock = (*SuperBlockOld)(nil) + // InodesCount implements SuperBlock.InodesCount. func (sb *SuperBlockOld) InodesCount() uint32 { return sb.InodesCountRaw } diff --git a/pkg/sentry/fs/ext/ext.go b/pkg/sentry/fs/ext/ext.go index 7602e2bf0..9d33be08f 100644 --- a/pkg/sentry/fs/ext/ext.go +++ b/pkg/sentry/fs/ext/ext.go @@ -19,7 +19,9 @@ import ( "io" "sync" + "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/sentry/fs/ext/disklayout" + "gvisor.dev/gvisor/pkg/syserror" ) // Filesystem implements vfs.FilesystemImpl. @@ -47,3 +49,27 @@ type Filesystem struct { // Immutable after initialization. bgs []disklayout.BlockGroup } + +// newFilesystem is the Filesystem constructor. +func newFilesystem(dev io.ReadSeeker) (*Filesystem, error) { + fs := Filesystem{dev: dev} + var err error + + fs.sb, err = readSuperBlock(dev) + if err != nil { + return nil, err + } + + if fs.sb.Magic() != linux.EXT_SUPER_MAGIC { + // mount(2) specifies that EINVAL should be returned if the superblock is + // invalid. + return nil, syserror.EINVAL + } + + fs.bgs, err = readBlockGroups(dev, fs.sb) + if err != nil { + return nil, err + } + + return &fs, nil +} diff --git a/pkg/sentry/fs/ext/utils.go b/pkg/sentry/fs/ext/utils.go new file mode 100644 index 000000000..861ef9a73 --- /dev/null +++ b/pkg/sentry/fs/ext/utils.go @@ -0,0 +1,103 @@ +// 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 ( + "encoding/binary" + "io" + + "gvisor.dev/gvisor/pkg/sentry/fs/ext/disklayout" + "gvisor.dev/gvisor/pkg/syserror" +) + +// readFromDisk performs a binary read from disk into the given struct from +// the absolute offset provided. +// +// All disk reads should use this helper so we avoid reading from stale +// previously used offsets. This function forces the offset parameter. +func readFromDisk(dev io.ReadSeeker, abOff int64, v interface{}) error { + if _, err := dev.Seek(abOff, io.SeekStart); err != nil { + return syserror.EIO + } + + if err := binary.Read(dev, binary.LittleEndian, v); err != nil { + return syserror.EIO + } + + return nil +} + +// readSuperBlock reads the SuperBlock from block group 0 in the underlying +// device. There are three versions of the superblock. This function identifies +// and returns the correct version. +func readSuperBlock(dev io.ReadSeeker) (disklayout.SuperBlock, error) { + var sb disklayout.SuperBlock = &disklayout.SuperBlockOld{} + if err := readFromDisk(dev, disklayout.SbOffset, sb); err != nil { + return nil, err + } + if sb.Revision() == disklayout.OldRev { + return sb, nil + } + + sb = &disklayout.SuperBlock32Bit{} + if err := readFromDisk(dev, disklayout.SbOffset, sb); err != nil { + return nil, err + } + if !sb.IncompatibleFeatures().Is64Bit { + return sb, nil + } + + sb = &disklayout.SuperBlock64Bit{} + if err := readFromDisk(dev, disklayout.SbOffset, sb); err != nil { + return nil, err + } + return sb, nil +} + +// blockGroupsCount returns the number of block groups in the ext fs. +func blockGroupsCount(sb disklayout.SuperBlock) uint64 { + blocksCount := sb.BlocksCount() + blocksPerGroup := uint64(sb.BlocksPerGroup()) + + // Round up the result. float64 can compromise precision so do it manually. + bgCount := blocksCount / blocksPerGroup + if blocksCount%blocksPerGroup != 0 { + bgCount++ + } + + return bgCount +} + +// readBlockGroups reads the block group descriptor table from block group 0 in +// the underlying device. +func readBlockGroups(dev io.ReadSeeker, sb disklayout.SuperBlock) ([]disklayout.BlockGroup, error) { + bgCount := blockGroupsCount(sb) + bgdSize := uint64(sb.BgDescSize()) + is64Bit := sb.IncompatibleFeatures().Is64Bit + bgds := make([]disklayout.BlockGroup, bgCount) + + for i, off := uint64(0), uint64(sb.FirstDataBlock()+1)*sb.BlockSize(); i < bgCount; i, off = i+1, off+bgdSize { + if is64Bit { + bgds[i] = &disklayout.BlockGroup64Bit{} + } else { + bgds[i] = &disklayout.BlockGroup32Bit{} + } + + if err := readFromDisk(dev, int64(off), bgds[i]); err != nil { + return nil, err + } + } + return bgds, nil +} |