github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/ext/disklayout/extent.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package disklayout
    16  
    17  import (
    18  	"github.com/SagerNet/gvisor/pkg/marshal"
    19  )
    20  
    21  // Extents were introduced in ext4 and provide huge performance gains in terms
    22  // data locality and reduced metadata block usage. Extents are organized in
    23  // extent trees. The root node is contained in inode.BlocksRaw.
    24  //
    25  // Terminology:
    26  //   - Physical Block:
    27  //       Filesystem data block which is addressed normally wrt the entire
    28  //       filesystem (addressed with 48 bits).
    29  //
    30  //   - File Block:
    31  //       Data block containing *only* file data and addressed wrt to the file
    32  //       with only 32 bits. The (i)th file block contains file data from
    33  //       byte (i * sb.BlockSize()) to ((i+1) * sb.BlockSize()).
    34  
    35  const (
    36  	// ExtentHeaderSize is the size of the header of an extent tree node.
    37  	ExtentHeaderSize = 12
    38  
    39  	// ExtentEntrySize is the size of an entry in an extent tree node.
    40  	// This size is the same for both leaf and internal nodes.
    41  	ExtentEntrySize = 12
    42  
    43  	// ExtentMagic is the magic number which must be present in the header.
    44  	ExtentMagic = 0xf30a
    45  )
    46  
    47  // ExtentEntryPair couples an in-memory ExtendNode with the ExtentEntry that
    48  // points to it. We want to cache these structs in memory to avoid repeated
    49  // disk reads.
    50  //
    51  // Note: This struct itself does not represent an on-disk struct.
    52  type ExtentEntryPair struct {
    53  	// Entry points to the child node on disk.
    54  	Entry ExtentEntry
    55  	// Node points to child node in memory. Is nil if the current node is a leaf.
    56  	Node *ExtentNode
    57  }
    58  
    59  // ExtentNode represents an extent tree node. For internal nodes, all Entries
    60  // will be ExtendIdxs. For leaf nodes, they will all be Extents.
    61  //
    62  // Note: This struct itself does not represent an on-disk struct.
    63  type ExtentNode struct {
    64  	Header  ExtentHeader
    65  	Entries []ExtentEntryPair
    66  }
    67  
    68  // ExtentEntry represents an extent tree node entry. The entry can either be
    69  // an ExtentIdx or Extent itself. This exists to simplify navigation logic.
    70  type ExtentEntry interface {
    71  	marshal.Marshallable
    72  
    73  	// FileBlock returns the first file block number covered by this entry.
    74  	FileBlock() uint32
    75  
    76  	// PhysicalBlock returns the child physical block that this entry points to.
    77  	PhysicalBlock() uint64
    78  }
    79  
    80  // ExtentHeader emulates the ext4_extent_header struct in ext4. Each extent
    81  // tree node begins with this and is followed by `NumEntries` number of:
    82  //   - Extent         if `Depth` == 0
    83  //   - ExtentIdx      otherwise
    84  //
    85  // +marshal
    86  type ExtentHeader struct {
    87  	// Magic in the extent magic number, must be 0xf30a.
    88  	Magic uint16
    89  
    90  	// NumEntries indicates the number of valid entries following the header.
    91  	NumEntries uint16
    92  
    93  	// MaxEntries that could follow the header. Used while adding entries.
    94  	MaxEntries uint16
    95  
    96  	// Height represents the distance of this node from the farthest leaf. Please
    97  	// note that Linux incorrectly calls this `Depth` (which means the distance
    98  	// of the node from the root).
    99  	Height uint16
   100  	_      uint32
   101  }
   102  
   103  // ExtentIdx emulates the ext4_extent_idx struct in ext4. Only present in
   104  // internal nodes. Sorted in ascending order based on FirstFileBlock since
   105  // Linux does a binary search on this. This points to a block containing the
   106  // child node.
   107  //
   108  // +marshal
   109  type ExtentIdx struct {
   110  	FirstFileBlock uint32
   111  	ChildBlockLo   uint32
   112  	ChildBlockHi   uint16
   113  	_              uint16
   114  }
   115  
   116  // Compiles only if ExtentIdx implements ExtentEntry.
   117  var _ ExtentEntry = (*ExtentIdx)(nil)
   118  
   119  // FileBlock implements ExtentEntry.FileBlock.
   120  func (ei *ExtentIdx) FileBlock() uint32 {
   121  	return ei.FirstFileBlock
   122  }
   123  
   124  // PhysicalBlock implements ExtentEntry.PhysicalBlock. It returns the
   125  // physical block number of the child block.
   126  func (ei *ExtentIdx) PhysicalBlock() uint64 {
   127  	return (uint64(ei.ChildBlockHi) << 32) | uint64(ei.ChildBlockLo)
   128  }
   129  
   130  // Extent represents the ext4_extent struct in ext4. Only present in leaf
   131  // nodes. Sorted in ascending order based on FirstFileBlock since Linux does a
   132  // binary search on this. This points to an array of data blocks containing the
   133  // file data. It covers `Length` data blocks starting from `StartBlock`.
   134  //
   135  // +marshal
   136  type Extent struct {
   137  	FirstFileBlock uint32
   138  	Length         uint16
   139  	StartBlockHi   uint16
   140  	StartBlockLo   uint32
   141  }
   142  
   143  // Compiles only if Extent implements ExtentEntry.
   144  var _ ExtentEntry = (*Extent)(nil)
   145  
   146  // FileBlock implements ExtentEntry.FileBlock.
   147  func (e *Extent) FileBlock() uint32 {
   148  	return e.FirstFileBlock
   149  }
   150  
   151  // PhysicalBlock implements ExtentEntry.PhysicalBlock. It returns the
   152  // physical block number of the first data block this extent covers.
   153  func (e *Extent) PhysicalBlock() uint64 {
   154  	return (uint64(e.StartBlockHi) << 32) | uint64(e.StartBlockLo)
   155  }