275 lines
8.3 KiB
Go
275 lines
8.3 KiB
Go
// 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 (
|
||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||
"gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||
)
|
||
|
||
// Special inodes. See https://www.kernel.org/doc/html/latest/filesystems/ext4/overview.html#special-inodes.
|
||
const (
|
||
// RootDirInode is the inode number of the root directory inode.
|
||
RootDirInode = 2
|
||
)
|
||
|
||
// The Inode interface must be implemented by structs representing ext inodes.
|
||
// The inode stores all the metadata pertaining to the file (except for the
|
||
// file name which is held by the directory entry). It does NOT expose all
|
||
// fields and should be extended if need be.
|
||
//
|
||
// Some file systems (e.g. FAT) use the directory entry to store all this
|
||
// information. Ext file systems do not so that they can support hard links.
|
||
// However, ext4 cheats a little bit and duplicates the file type in the
|
||
// directory entry for performance gains.
|
||
//
|
||
// See https://www.kernel.org/doc/html/latest/filesystems/ext4/dynamic.html#index-nodes.
|
||
type Inode interface {
|
||
// Mode returns the linux file mode which is majorly used to extract
|
||
// information like:
|
||
// - File permissions (read/write/execute by user/group/others).
|
||
// - Sticky, set UID and GID bits.
|
||
// - File type.
|
||
//
|
||
// Masks to extract this information are provided in pkg/abi/linux/file.go.
|
||
Mode() linux.FileMode
|
||
|
||
// UID returns the owner UID.
|
||
UID() auth.KUID
|
||
|
||
// GID returns the owner GID.
|
||
GID() auth.KGID
|
||
|
||
// Size returns the size of the file in bytes.
|
||
Size() uint64
|
||
|
||
// InodeSize returns the size of this inode struct in bytes.
|
||
// In ext2 and ext3, the inode struct and inode disk record size was fixed at
|
||
// 128 bytes. Ext4 makes it possible for the inode struct to be bigger.
|
||
// However, accessing any field beyond the 128 bytes marker must be verified
|
||
// using this method.
|
||
InodeSize() uint16
|
||
|
||
// AccessTime returns the last access time. Shows when the file was last read.
|
||
//
|
||
// If InExtendedAttr is set, then this should NOT be used because the
|
||
// underlying field is used to store the extended attribute value checksum.
|
||
AccessTime() time.Time
|
||
|
||
// ChangeTime returns the last change time. Shows when the file meta data
|
||
// (like permissions) was last changed.
|
||
//
|
||
// If InExtendedAttr is set, then this should NOT be used because the
|
||
// underlying field is used to store the lower 32 bits of the attribute
|
||
// value’s reference count.
|
||
ChangeTime() time.Time
|
||
|
||
// ModificationTime returns the last modification time. Shows when the file
|
||
// content was last modified.
|
||
//
|
||
// If InExtendedAttr is set, then this should NOT be used because
|
||
// the underlying field contains the number of the inode that owns the
|
||
// extended attribute.
|
||
ModificationTime() time.Time
|
||
|
||
// DeletionTime returns the deletion time. Inodes are marked as deleted by
|
||
// writing to the underlying field. FS tools can restore files until they are
|
||
// actually overwritten.
|
||
DeletionTime() time.Time
|
||
|
||
// LinksCount returns the number of hard links to this inode.
|
||
//
|
||
// Normally there is an upper limit on the number of hard links:
|
||
// - ext2/ext3 = 32,000
|
||
// - ext4 = 65,000
|
||
//
|
||
// This implies that an ext4 directory cannot have more than 64,998
|
||
// subdirectories because each subdirectory will have a hard link to the
|
||
// directory via the `..` entry. The directory has hard link via the `.` entry
|
||
// of its own. And finally the inode is initiated with 1 hard link (itself).
|
||
//
|
||
// The underlying value is reset to 1 if all the following hold:
|
||
// - Inode is a directory.
|
||
// - SbDirNlink is enabled.
|
||
// - Number of hard links is incremented past 64,999.
|
||
// Hard link value of 1 for a directory would indicate that the number of hard
|
||
// links is unknown because a directory can have minimum 2 hard links (itself
|
||
// and `.` entry).
|
||
LinksCount() uint16
|
||
|
||
// Flags returns InodeFlags which represents the inode flags.
|
||
Flags() InodeFlags
|
||
|
||
// Data returns the underlying inode.i_block array as a slice so it's
|
||
// modifiable. This field is special and is used to store various kinds of
|
||
// things depending on the filesystem version and inode type. The underlying
|
||
// field name in Linux is a little misleading.
|
||
// - In ext2/ext3, it contains the block map.
|
||
// - In ext4, it contains the extent tree root node.
|
||
// - For inline files, it contains the file contents.
|
||
// - For symlinks, it contains the link path (if it fits here).
|
||
//
|
||
// See https://www.kernel.org/doc/html/latest/filesystems/ext4/dynamic.html#the-contents-of-inode-i-block.
|
||
Data() []byte
|
||
}
|
||
|
||
// Inode flags. This is not comprehensive and flags which were not used in
|
||
// the Linux kernel have been excluded.
|
||
const (
|
||
// InSync indicates that all writes to the file must be synchronous.
|
||
InSync = 0x8
|
||
|
||
// InImmutable indicates that this file is immutable.
|
||
InImmutable = 0x10
|
||
|
||
// InAppend indicates that this file can only be appended to.
|
||
InAppend = 0x20
|
||
|
||
// InNoDump indicates that teh dump(1) utility should not dump this file.
|
||
InNoDump = 0x40
|
||
|
||
// InNoAccessTime indicates that the access time of this inode must not be
|
||
// updated.
|
||
InNoAccessTime = 0x80
|
||
|
||
// InIndex indicates that this directory has hashed indexes.
|
||
InIndex = 0x1000
|
||
|
||
// InJournalData indicates that file data must always be written through a
|
||
// journal device.
|
||
InJournalData = 0x4000
|
||
|
||
// InDirSync indicates that all the directory entiry data must be written
|
||
// synchronously.
|
||
InDirSync = 0x10000
|
||
|
||
// InTopDir indicates that this inode is at the top of the directory hierarchy.
|
||
InTopDir = 0x20000
|
||
|
||
// InHugeFile indicates that this is a huge file.
|
||
InHugeFile = 0x40000
|
||
|
||
// InExtents indicates that this inode uses extents.
|
||
InExtents = 0x80000
|
||
|
||
// InExtendedAttr indicates that this inode stores a large extended attribute
|
||
// value in its data blocks.
|
||
InExtendedAttr = 0x200000
|
||
|
||
// InInline indicates that this inode has inline data.
|
||
InInline = 0x10000000
|
||
|
||
// InReserved indicates that this inode is reserved for the ext4 library.
|
||
InReserved = 0x80000000
|
||
)
|
||
|
||
// InodeFlags represents all possible combinations of inode flags. It aims to
|
||
// cover the bit masks and provide a more user-friendly interface.
|
||
type InodeFlags struct {
|
||
Sync bool
|
||
Immutable bool
|
||
Append bool
|
||
NoDump bool
|
||
NoAccessTime bool
|
||
Index bool
|
||
JournalData bool
|
||
DirSync bool
|
||
TopDir bool
|
||
HugeFile bool
|
||
Extents bool
|
||
ExtendedAttr bool
|
||
Inline bool
|
||
Reserved bool
|
||
}
|
||
|
||
// ToInt converts inode flags back to its 32-bit rep.
|
||
func (f InodeFlags) ToInt() uint32 {
|
||
var res uint32
|
||
|
||
if f.Sync {
|
||
res |= InSync
|
||
}
|
||
if f.Immutable {
|
||
res |= InImmutable
|
||
}
|
||
if f.Append {
|
||
res |= InAppend
|
||
}
|
||
if f.NoDump {
|
||
res |= InNoDump
|
||
}
|
||
if f.NoAccessTime {
|
||
res |= InNoAccessTime
|
||
}
|
||
if f.Index {
|
||
res |= InIndex
|
||
}
|
||
if f.JournalData {
|
||
res |= InJournalData
|
||
}
|
||
if f.DirSync {
|
||
res |= InDirSync
|
||
}
|
||
if f.TopDir {
|
||
res |= InTopDir
|
||
}
|
||
if f.HugeFile {
|
||
res |= InHugeFile
|
||
}
|
||
if f.Extents {
|
||
res |= InExtents
|
||
}
|
||
if f.ExtendedAttr {
|
||
res |= InExtendedAttr
|
||
}
|
||
if f.Inline {
|
||
res |= InInline
|
||
}
|
||
if f.Reserved {
|
||
res |= InReserved
|
||
}
|
||
|
||
return res
|
||
}
|
||
|
||
// InodeFlagsFromInt converts the integer representation of inode flags to
|
||
// a InodeFlags struct.
|
||
func InodeFlagsFromInt(f uint32) InodeFlags {
|
||
return InodeFlags{
|
||
Sync: f&InSync > 0,
|
||
Immutable: f&InImmutable > 0,
|
||
Append: f&InAppend > 0,
|
||
NoDump: f&InNoDump > 0,
|
||
NoAccessTime: f&InNoAccessTime > 0,
|
||
Index: f&InIndex > 0,
|
||
JournalData: f&InJournalData > 0,
|
||
DirSync: f&InDirSync > 0,
|
||
TopDir: f&InTopDir > 0,
|
||
HugeFile: f&InHugeFile > 0,
|
||
Extents: f&InExtents > 0,
|
||
ExtendedAttr: f&InExtendedAttr > 0,
|
||
Inline: f&InInline > 0,
|
||
Reserved: f&InReserved > 0,
|
||
}
|
||
}
|
||
|
||
// These masks define how users can view/modify inode flags. The rest of the
|
||
// flags are for internal kernel usage only.
|
||
const (
|
||
InUserReadFlagMask = 0x4BDFFF
|
||
InUserWriteFlagMask = 0x4B80FF
|
||
)
|