// 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" ) // 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 )