README

Build Status Coverage Status GoDoc

Overview

This package allows you to browse an ext4 filesystem directly. It does not use FUSE or touch the kernel, so no privileges are required.

This package also exposes the data in the journal (if one is available).

Example

Recursively walk all of the files in the filesystem:

inodeNumber := InodeRootDirectory

filepath := path.Join(assetsPath, "hierarchy_32.ext4")

f, err := os.Open(filepath)
log.PanicIf(err)

defer f.Close()

_, err = f.Seek(Superblock0Offset, io.SeekStart)
log.PanicIf(err)

sb, err := NewSuperblockWithReader(f)
log.PanicIf(err)

bgdl, err := NewBlockGroupDescriptorListWithReadSeeker(f, sb)
log.PanicIf(err)

bgd, err := bgdl.GetWithAbsoluteInode(inodeNumber)
log.PanicIf(err)

dw, err := NewDirectoryWalk(f, bgd, inodeNumber)
log.PanicIf(err)

allEntries := make([]string, 0)

for {
	fullPath, de, err := dw.Next()
	if err == io.EOF {
		break
	} else if err != nil {
		log.Panic(err)
	}

	description := fmt.Sprintf("%s: %s", fullPath, de.String())
	allEntries = append(allEntries, description)
}

sort.Strings(allEntries)

for _, entryDescription := range allEntries {
	fmt.Println(entryDescription)
}

// Output:
//
// directory1/fortune1: DirectoryEntry<NAME=[fortune1] INODE=(15) TYPE=[regular]-(1)>
// directory1/fortune2: DirectoryEntry<NAME=[fortune2] INODE=(14) TYPE=[regular]-(1)>
// directory1/fortune5: DirectoryEntry<NAME=[fortune5] INODE=(20) TYPE=[regular]-(1)>
// directory1/fortune6: DirectoryEntry<NAME=[fortune6] INODE=(21) TYPE=[regular]-(1)>
// directory1/subdirectory1/fortune3: DirectoryEntry<NAME=[fortune3] INODE=(17) TYPE=[regular]-(1)>
// directory1/subdirectory1/fortune4: DirectoryEntry<NAME=[fortune4] INODE=(18) TYPE=[regular]-(1)>
// directory1/subdirectory1: DirectoryEntry<NAME=[subdirectory1] INODE=(16) TYPE=[directory]-(2)>
// directory1/subdirectory2/fortune7: DirectoryEntry<NAME=[fortune7] INODE=(22) TYPE=[regular]-(1)>
// directory1/subdirectory2/fortune8: DirectoryEntry<NAME=[fortune8] INODE=(23) TYPE=[regular]-(1)>
// directory1/subdirectory2: DirectoryEntry<NAME=[subdirectory2] INODE=(19) TYPE=[directory]-(2)>
// directory1: DirectoryEntry<NAME=[directory1] INODE=(13) TYPE=[directory]-(2)>
// directory2/fortune10: DirectoryEntry<NAME=[fortune10] INODE=(26) TYPE=[regular]-(1)>
// directory2/fortune9: DirectoryEntry<NAME=[fortune9] INODE=(25) TYPE=[regular]-(1)>
// directory2: DirectoryEntry<NAME=[directory2] INODE=(24) TYPE=[directory]-(2)>
// lost+found: DirectoryEntry<NAME=[lost+found] INODE=(11) TYPE=[directory]-(2)>
// thejungle.txt: DirectoryEntry<NAME=[thejungle.txt] INODE=(12) TYPE=[regular]-(1)>

This example and others are documented here.

Notes

  • Modern filesystems are supported, including both 32-bit and 64-bit addressing. Obscure filesystem options may not be compatible. See the compatibility assertions in NewSuperblockWithReader.
    • 64-bit addressing should be fine, as the high addressing should likely be zero when 64-bit addressing is turned-off (which is primarily what our unit-tests test with). However, the available documentation is limited on the subject. It's specifically not clear which of the various high/low addresses are affected by the 64-bit mode.

To Do

  • Finish implementing checksum calculation and validation. Currently all checksums are readable but with no additional functionality.

Documentation

Index

Examples

Constants

View Source
const (
	BgdFlagInodeTableAndBitmapNotInitialized = uint16(0x1)
	BgdFlagBitmapNotInitialized              = uint16(0x2)
	BgdFlagInodeTableZeroed                  = uint16(0x4)
)
View Source
const (
	Ext4FilenameMaxLen     = 255
	Ext4DirectoryEntrySize = Ext4FilenameMaxLen + 8
)
View Source
const (
	FileTypeUnknown         = uint8(0x0)
	FileTypeRegular         = uint8(0x1)
	FileTypeDirectory       = uint8(0x2)
	FileTypeCharacterDevice = uint8(0x3)
	FileTypeBlockDevice     = uint8(0x4)
	FileTypeFifo            = uint8(0x5)
	FileTypeSocket          = uint8(0x6)
	FileTypeSymbolicLink    = uint8(0x7)
)

File types.

View Source
const (
	ExtentMagic            = uint16(0xf30A)
	ExtentHeaderSize       = 12
	ExtentIndexAndLeafSize = 12
)
View Source
const (
	InodeDefectiveBlocks          = 1
	InodeRootDirectory            = 2
	InodeUserQuota                = 3
	InodeGroupQuota               = 4
	InodeBootLoader               = 5
	InodeUndeleteDirectory        = 6
	InodeReservedGroupDescriptors = 7
	InodeJournal                  = 8
	InodeExclude                  = 9
	InodeReplica                  = 10
)

Reserved inodes.

View Source
const (
	Ext4NdirBlocks = 12
	Ext4IndBlock   = Ext4NdirBlocks
	Ext4DindBlock  = (Ext4IndBlock + 1)
	Ext4TindBlock  = (Ext4DindBlock + 1)
	Ext4NBlocks    = (Ext4TindBlock + 1)
)
View Source
const (
	InodeFlagSecrm           = 0x1
	InodeFlagUnrm            = 0x2
	InodeFlagCompr           = 0x4
	InodeFlagSync            = 0x8
	InodeFlagImmutable       = 0x10
	InodeFlagAppend          = 0x20
	InodeFlagNodump          = 0x40
	InodeFlagNoatime         = 0x80
	InodeFlagDirty           = 0x100
	InodeFlagComprblk        = 0x200
	InodeFlagNocompr         = 0x400
	InodeFlagEncrypt         = 0x800
	InodeFlagIndex           = 0x1000
	InodeFlagImagic          = 0x2000
	InodeFlagJournalData     = 0x4000
	InodeFlagNotail          = 0x8000
	InodeFlagDirsync         = 0x10000
	InodeFlagTopdir          = 0x20000
	InodeFlagHugeFile        = 0x40000
	InodeFlagExtents         = 0x80000
	InodeFlagEaInode         = 0x200000
	InodeFlagEofblocks       = 0x400000
	InodeFlagSnapfile        = 0x01000000
	InodeFlagSnapfileDeleted = 0x04000000
	InodeFlagSnapfileShrunk  = 0x08000000
	InodeFlagInlineData      = 0x10000000
	InodeFlagProjinherit     = 0x20000000
)
View Source
const (
	Ext4Magic = 0xef53

	SuperblockSize = 1024

	// The first superblock is after the bootloader code.
	Superblock0Offset = int64(1024)
)
View Source
const (
	SbStateCleanlyUnmounted      = 0x0001
	SbStateErrorsDetected        = 0x0002
	SbStateOrphansBeingRecovered = 0x0004
)
View Source
const (
	SbErrorsContinue        = 1
	SbErrorsRemountReadonly = 2
	SbErrorsPanic           = 3
)
View Source
const (
	SbOsLinux   = 0
	SbOsHurd    = 1
	SbOsMasix   = 2
	SbOsFreebsd = 3
	SbOsLites   = 4
)
View Source
const (
	SbRevlevelGoodOldRev = 0
	SbRevlevelDynamicRev = 1
)
View Source
const (
	SbDefHashVersionLegacy          = 0x0
	SbDefHashVersionHalfMd4         = 0x1
	SbDefHashVersionTea             = 0x2
	SbDefHashVersionLegacyUnsigned  = 0x3
	SbDefHashVersionHalfMd4Unsigned = 0x4
	SbDefHashVersionTeaUnsigned     = 0x5
)
View Source
const (
	SbMountOptionDebug         = uint32(0x001)
	SbMountOptionBsdGroups     = uint32(0x002)
	SbMountOptionXattrUser     = uint32(0x004)
	SbMountOptionAcl           = uint32(0x008)
	SbMountOptionUid16         = uint32(0x010)
	SbMountOptionJmodeData     = uint32(0x020)
	SbMountOptionJmodeOrdered  = uint32(0x040)
	SbMountOptionJmodeWback    = uint32(0x060)
	SbMountOptionNoBarrier     = uint32(0x100)
	SbMountOptionBlockValidity = uint32(0x200)
	SbMountOptionDiscard       = uint32(0x400)
	SbMountOptionNoDelAlloc    = uint32(0x800)
)
View Source
const (
	SbFlagSignedDirectoryHash   = uint32(0x1)
	SbFlagUnsignedDirectoryHash = uint32(0x2)
	SbFlagTestDevelopmentCode   = uint32(0x4)
)
View Source
const (
	SbEncryptAlgoInvalid   = uint8(0)
	SbEncryptAlgoAes256Xt  = uint8(1)
	SbEncryptAlgoAes256Gcm = uint8(2)
	SbEncryptAlgoAes256Cbc = uint8(3)
)
View Source
const (
	// COMPAT_DIR_PREALLOC
	SbFeatureCompatDirPrealloc = uint32(0x0001)

	// COMPAT_IMAGIC_INODES
	SbFeatureCompatImagicInodes = uint32(0x0002)

	// COMPAT_HAS_JOURNAL
	SbFeatureCompatHasJournal = uint32(0x0004)

	// COMPAT_EXT_ATTR
	SbFeatureCompatExtAttr = uint32(0x0008)

	// COMPAT_RESIZE_INODE
	SbFeatureCompatResizeInode = uint32(0x0010)

	// COMPAT_DIR_INDEX
	SbFeatureCompatDirIndex = uint32(0x0020)

	// COMPAT_LAZY_BG
	SbFeatureCompatLazyBg = uint32(0x40)

	// COMPAT_EXCLUDE_INODE
	SbFeatureCompatExcludeInode = uint32(0x80)

	// COMPAT_EXCLUDE_BITMAP
	SbFeatureCompatExcludeBitmap = uint32(0x100)

	// COMPAT_SPARSE_SUPER2
	SbFeatureCompatSparseSuperblockV2 = uint32(0x200)
)
View Source
const (
	// RO_COMPAT_SPARSE_SUPER
	SbFeatureRoCompatSparseSuper = uint32(0x1)

	// RO_COMPAT_LARGE_FILE
	SbFeatureRoCompatLargeFile = uint32(0x2)

	// RO_COMPAT_BTREE_DIR
	SbFeatureRoCompatBtreeDir = uint32(0x4)

	// RO_COMPAT_HUGE_FILE
	SbFeatureRoCompatHugeFile = uint32(0x8)

	// RO_COMPAT_GDT_CSUM
	SbFeatureRoCompatGdtCsum = uint32(0x10)

	// RO_COMPAT_DIR_NLINK
	SbFeatureRoCompatDirNlink = uint32(0x20)

	// RO_COMPAT_EXTRA_ISIZE
	SbFeatureRoCompatExtraIsize = uint32(0x40)

	// RO_COMPAT_HAS_SNAPSHOT
	SbFeatureRoCompatHasSnapshot = uint32(0x80)

	// RO_COMPAT_QUOTA
	SbFeatureRoCompatQuota = uint32(0x100)

	// RO_COMPAT_BIGALLOC
	SbFeatureRoCompatBigAlloc = uint32(0x200)

	// RO_COMPAT_METADATA_CSUM
	SbFeatureRoCompatMetadataCsum = uint32(0x400)

	// RO_COMPAT_REPLICA
	SbFeatureRoCompatReplica = uint32(0x800)

	// RO_COMPAT_READONLY
	SbFeatureRoCompatReadonly = uint32(0x1000)

	// RO_COMPAT_PROJECT
	SbFeatureRoCompatProject = uint32(0x2000)
)
View Source
const (
	// INCOMPAT_COMPRESSION
	SbFeatureIncompatCompression = uint32(0x0001)

	// INCOMPAT_FILETYPE
	SbFeatureIncompatFiletype = uint32(0x0002)

	// INCOMPAT_RECOVER
	SbFeatureIncompatRecover = uint32(0x0004) /* Needs recovery */

	// INCOMPAT_JOURNAL_DEV
	SbFeatureIncompatJournalDev = uint32(0x0008) /* Journal device */

	// INCOMPAT_META_BG
	SbFeatureIncompatMetaBg = uint32(0x0010)

	// INCOMPAT_EXTENTS
	SbFeatureIncompatExtents = uint32(0x0040) /* extents support */

	// INCOMPAT_64BIT
	SbFeatureIncompat64bit = uint32(0x0080)

	// INCOMPAT_MMP
	SbFeatureIncompatMmp = uint32(0x0100)

	// INCOMPAT_FLEX_BG
	SbFeatureIncompatFlexBg = uint32(0x0200)

	// INCOMPAT_EA_INODE
	SbFeatureIncompatLargeExtendedAttributeValues = uint32(0x400)

	// INCOMPAT_DIRDATA
	SbFeatureIncompatDirData = uint32(0x1000)

	// INCOMPAT_CSUM_SEED
	SbFeatureIncompatCsumSeed = uint32(0x2000)

	// INCOMPAT_LARGEDIR
	SbFeatureIncompatLargeDir = uint32(0x4000)

	// INCOMPAT_INLINE_DATA
	SbFeatureIncompatInlineData = uint32(0x8000)

	// INCOMPAT_ENCRYPT
	SbFeatureIncompatEncrypt = uint32(0x10000)
)
View Source
const (
	TestDirectoryInodeNumber = 2
	TestFileInodeNumber      = 12
)
View Source
const (
	BlockGroupDescriptorSize = 64
)
View Source
const (
	Ext4ExtentChecksumTailSize = 4
)

Variables

View Source
var (
	// SbFeatureCompatNames is an ordered list of names.
	SbFeatureCompatNames = []string{
		"DirIndex",
		"DirPrealloc",
		"ExcludeBitmap",
		"ExcludeInode",
		"ExtAttr",
		"HasJournal",
		"ImagicInodes",
		"LazyBg",
		"ResizeInode",
		"SparseSuper2",
	}

	SbFeatureCompatLookup = map[string]uint32{
		"DirPrealloc":   SbFeatureCompatDirPrealloc,
		"ImagicInodes":  SbFeatureCompatImagicInodes,
		"HasJournal":    SbFeatureCompatHasJournal,
		"ExtAttr":       SbFeatureCompatExtAttr,
		"ResizeInode":   SbFeatureCompatResizeInode,
		"DirIndex":      SbFeatureCompatDirIndex,
		"LazyBg":        SbFeatureCompatLazyBg,
		"ExcludeInode":  SbFeatureCompatExcludeInode,
		"ExcludeBitmap": SbFeatureCompatExcludeBitmap,
		"SparseSuper2":  SbFeatureCompatSparseSuperblockV2,
	}
)
View Source
var (
	// SbFeatureRoCompatNames is an ordered list of names.
	SbFeatureRoCompatNames = []string{
		"BigAlloc",
		"BtreeDir",
		"DirNlink",
		"ExtraIsize",
		"GdtCsum",
		"HasSnapshot",
		"HugeFile",
		"LargeFile",
		"MetadataCsum",
		"Project",
		"Quota",
		"Readonly",
		"Replica",
		"SparseSuper",
	}

	SbFeatureRoCompatLookup = map[string]uint32{
		"SparseSuper":  SbFeatureRoCompatSparseSuper,
		"LargeFile":    SbFeatureRoCompatLargeFile,
		"BtreeDir":     SbFeatureRoCompatBtreeDir,
		"HugeFile":     SbFeatureRoCompatHugeFile,
		"GdtCsum":      SbFeatureRoCompatGdtCsum,
		"DirNlink":     SbFeatureRoCompatDirNlink,
		"ExtraIsize":   SbFeatureRoCompatExtraIsize,
		"HasSnapshot":  SbFeatureRoCompatHasSnapshot,
		"Quota":        SbFeatureRoCompatQuota,
		"BigAlloc":     SbFeatureRoCompatBigAlloc,
		"MetadataCsum": SbFeatureRoCompatMetadataCsum,
		"Replica":      SbFeatureRoCompatReplica,
		"Readonly":     SbFeatureRoCompatReadonly,
		"Project":      SbFeatureRoCompatProject,
	}
)
View Source
var (
	// SbFeatureIncompatNames is an ordered list of names.
	SbFeatureIncompatNames = []string{
		"64bit",
		"Compression",
		"CsumSeed",
		"DirData",
		"Encrypt",
		"Extents",
		"Filetype",
		"FlexBg",
		"InlineData",
		"JournalDev",
		"LargeDir",
		"LargeExtendedAttributeValues",
		"MetaBg",
		"Mmp",
		"Recover",
	}

	SbFeatureIncompatLookup = map[string]uint32{
		"Compression":                  SbFeatureIncompatCompression,
		"Filetype":                     SbFeatureIncompatFiletype,
		"Recover":                      SbFeatureIncompatRecover,
		"JournalDev":                   SbFeatureIncompatJournalDev,
		"MetaBg":                       SbFeatureIncompatMetaBg,
		"Extents":                      SbFeatureIncompatExtents,
		"64bit":                        SbFeatureIncompat64bit,
		"Mmp":                          SbFeatureIncompatMmp,
		"FlexBg":                       SbFeatureIncompatFlexBg,
		"LargeExtendedAttributeValues": SbFeatureIncompatLargeExtendedAttributeValues,
		"DirData":                      SbFeatureIncompatDirData,
		"CsumSeed":                     SbFeatureIncompatCsumSeed,
		"LargeDir":                     SbFeatureIncompatLargeDir,
		"InlineData":                   SbFeatureIncompatInlineData,
		"Encrypt":                      SbFeatureIncompatEncrypt,
	}
)
View Source
var (
	ErrNotExt4 = errors.New("not ext4")
)
View Source
var (
	FileTypeLookup = map[uint8]string{
		FileTypeUnknown:         "unknown",
		FileTypeRegular:         "regular",
		FileTypeDirectory:       "directory",
		FileTypeCharacterDevice: "character device",
		FileTypeBlockDevice:     "block device",
		FileTypeFifo:            "fifo",
		FileTypeSocket:          "socket",
		FileTypeSymbolicLink:    "symbolic link",
	}
)
View Source
var (
	InodeFlagLookup = map[string]int{
		"Secrm":           InodeFlagSecrm,
		"Unrm":            InodeFlagUnrm,
		"Compr":           InodeFlagCompr,
		"Sync":            InodeFlagSync,
		"Immutable":       InodeFlagImmutable,
		"Append":          InodeFlagAppend,
		"Nodump":          InodeFlagNodump,
		"Noatime":         InodeFlagNoatime,
		"Dirty":           InodeFlagDirty,
		"Comprblk":        InodeFlagComprblk,
		"Nocompr":         InodeFlagNocompr,
		"Encrypt":         InodeFlagEncrypt,
		"Index":           InodeFlagIndex,
		"Imagic":          InodeFlagImagic,
		"JournalData":     InodeFlagJournalData,
		"Notail":          InodeFlagNotail,
		"Dirsync":         InodeFlagDirsync,
		"Topdir":          InodeFlagTopdir,
		"HugeFile":        InodeFlagHugeFile,
		"Extents":         InodeFlagExtents,
		"EaInode":         InodeFlagEaInode,
		"Eofblocks":       InodeFlagEofblocks,
		"Snapfile":        InodeFlagSnapfile,
		"SnapfileDeleted": InodeFlagSnapfileDeleted,
		"SnapfileShrunk":  InodeFlagSnapfileShrunk,
		"InlineData":      InodeFlagInlineData,
		"Projinherit":     InodeFlagProjinherit,
	}
)

Functions

This section is empty.

Types

type BlockGroupDescriptor

type BlockGroupDescriptor struct {
	// contains filtered or unexported fields
}

func NewBlockGroupDescriptorWithReader

func NewBlockGroupDescriptorWithReader(r io.Reader, sb *Superblock) (bgd *BlockGroupDescriptor, err error)

func (*BlockGroupDescriptor) Data

func (*BlockGroupDescriptor) Dump

func (bgd *BlockGroupDescriptor) Dump()

func (*BlockGroupDescriptor) InodeBitmapBlock

func (bgd *BlockGroupDescriptor) InodeBitmapBlock() uint64

func (*BlockGroupDescriptor) InodeTableBlock

func (bgd *BlockGroupDescriptor) InodeTableBlock() uint64

InodeTableBlock returns the absolute block number of the inode-table.

func (*BlockGroupDescriptor) IsBitmapNotInitialized

func (bgd *BlockGroupDescriptor) IsBitmapNotInitialized() bool

func (*BlockGroupDescriptor) IsInodeTableAndBitmapNotInitialized

func (bgd *BlockGroupDescriptor) IsInodeTableAndBitmapNotInitialized() bool

func (*BlockGroupDescriptor) IsInodeTableZeroed

func (bgd *BlockGroupDescriptor) IsInodeTableZeroed() bool

func (*BlockGroupDescriptor) Superblock

func (bgd *BlockGroupDescriptor) Superblock() *Superblock

type BlockGroupDescriptorData

type BlockGroupDescriptorData struct {
	BgBlockBitmapLo     uint32 /* Blocks bitmap block */
	BgInodeBitmapLo     uint32 /* Inodes bitmap block */
	BgInodeTableLo      uint32 /* Inodes table block */
	BgFreeBlocksCountLo uint16 /* Free blocks count */
	BgFreeInodesCountLo uint16 /* Free inodes count */
	BgUsedDirsCountLo   uint16 /* Directories count */
	BgFlags             uint16 /* EXT4_BG_flags (INODE_UNINIT, etc) */
	BgExcludeBitmapLo   uint32 /* Lower 32-bits of location of snapshot exclusion bitmap. */
	BgBlockBitmapCsumLo uint16 /* Lower 16-bits of the block bitmap checksum. */
	BgInodeBitmapCsumLo uint16 /* Lower 16-bits of the inode bitmap checksum. */
	BgItableUnusedLo    uint16 /* Unused inodes count */
	BgChecksum          uint16 /* crc16(sb_uuid+group+desc) */
	BgBlockBitmapHi     uint32 /* Blocks bitmap block MSB */
	BgInodeBitmapHi     uint32 /* Inodes bitmap block MSB */
	BgInodeTableHi      uint32 /* Inodes table block MSB */
	BgFreeBlocksCountHi uint16 /* Free blocks count MSB */
	BgFreeInodesCountHi uint16 /* Free inodes count MSB */
	BgUsedDirsCountHi   uint16 /* Directories count MSB */
	BgItableUnusedHi    uint16 /* Unused inodes count MSB */
	BgExcludeBitmapHi   uint32 /* Upper 32-bits of location of snapshot exclusion bitmap. */
	BgBlockBitmapCsumHi uint16 /* Upper 16-bits of the block bitmap checksum. */
	BgInodeBitmapCsumHi uint16 /* Upper 16-bits of the inode bitmap checksum. */
	BgReserved2         uint32 /* Padding to 64 bytes. */
}

type BlockGroupDescriptorList

type BlockGroupDescriptorList struct {
	// contains filtered or unexported fields
}

func NewBlockGroupDescriptorListWithReadSeeker

func NewBlockGroupDescriptorListWithReadSeeker(rs io.ReadSeeker, sb *Superblock) (bgdl *BlockGroupDescriptorList, err error)

NewBlockGroupDescriptorListWithReadSeeker returns a `BlockGroupDescriptorsList`, which has all block-group-descriptors in a big slice. Filesystems with the flex_bg capability flag (most) will group all of the BGD data together right at the top.

func (*BlockGroupDescriptorList) GetWithAbsoluteInode

func (bgdl *BlockGroupDescriptorList) GetWithAbsoluteInode(n int) (bgd *BlockGroupDescriptor, err error)

type DirectoryBrowser

type DirectoryBrowser struct {
	// contains filtered or unexported fields
}

DirectoryBrowser provides high-level directory navigation.

func NewDirectoryBrowser

func NewDirectoryBrowser(rs io.ReadSeeker, inode *Inode) *DirectoryBrowser

func (*DirectoryBrowser) Next

func (db *DirectoryBrowser) Next() (de *DirectoryEntry, err error)

Next parses the next directory entry from the underlying inode data reader. Returns `io.EOF` when done. This will also return the "." and ".." entries.

type DirectoryEntry

type DirectoryEntry struct {
	// contains filtered or unexported fields
}

DirectoryEntry wraps the raw directory entry and provides higher-level functionality.

func (*DirectoryEntry) Data

func (de *DirectoryEntry) Data() *Ext4DirEntry2

func (*DirectoryEntry) IsBlockDevice

func (de *DirectoryEntry) IsBlockDevice() bool

func (*DirectoryEntry) IsCharacterDevice

func (de *DirectoryEntry) IsCharacterDevice() bool

func (*DirectoryEntry) IsDirectory

func (de *DirectoryEntry) IsDirectory() bool

func (*DirectoryEntry) IsFifo

func (de *DirectoryEntry) IsFifo() bool

func (*DirectoryEntry) IsRegular

func (de *DirectoryEntry) IsRegular() bool

func (*DirectoryEntry) IsSocket

func (de *DirectoryEntry) IsSocket() bool
func (de *DirectoryEntry) IsSymbolicLink() bool

func (*DirectoryEntry) IsUnknownType

func (de *DirectoryEntry) IsUnknownType() bool

func (*DirectoryEntry) Name

func (de *DirectoryEntry) Name() string

func (*DirectoryEntry) String

func (de *DirectoryEntry) String() string

func (*DirectoryEntry) TypeName

func (de *DirectoryEntry) TypeName() string

type DirectoryWalk

type DirectoryWalk struct {
	// contains filtered or unexported fields
}

DirectoryWalk provides full directory-structure recursion.

func NewDirectoryWalk

func NewDirectoryWalk(rs io.ReadSeeker, bgd *BlockGroupDescriptor, rootInodeNumber int) (dw *DirectoryWalk, err error)

func (*DirectoryWalk) Next

func (dw *DirectoryWalk) Next() (fullPath string, de *DirectoryEntry, err error)

Next steps through the entire tree starting at the given root inode, one entry at a time. We guarantee that all adjacent entries will be processed adjacently. This will not return the "." and ".." entries.

Example
inodeNumber := InodeRootDirectory

filepath := path.Join(assetsPath, "hierarchy_32.ext4")

f, err := os.Open(filepath)
log.PanicIf(err)

defer f.Close()

_, err = f.Seek(Superblock0Offset, io.SeekStart)
log.PanicIf(err)

sb, err := NewSuperblockWithReader(f)
log.PanicIf(err)

bgdl, err := NewBlockGroupDescriptorListWithReadSeeker(f, sb)
log.PanicIf(err)

bgd, err := bgdl.GetWithAbsoluteInode(inodeNumber)
log.PanicIf(err)

dw, err := NewDirectoryWalk(f, bgd, inodeNumber)
log.PanicIf(err)

allEntries := make([]string, 0)

for {
	fullPath, de, err := dw.Next()
	if err == io.EOF {
		break
	} else if err != nil {
		log.Panic(err)
	}

	description := fmt.Sprintf("%s: %s", fullPath, de.String())
	allEntries = append(allEntries, description)
}

sort.Strings(allEntries)

for _, entryDescription := range allEntries {
	fmt.Println(entryDescription)
}
Output:


directory1/fortune1: DirectoryEntry<NAME=[fortune1] INODE=(15) TYPE=[regular]-(1)>
directory1/fortune2: DirectoryEntry<NAME=[fortune2] INODE=(14) TYPE=[regular]-(1)>
directory1/fortune5: DirectoryEntry<NAME=[fortune5] INODE=(20) TYPE=[regular]-(1)>
directory1/fortune6: DirectoryEntry<NAME=[fortune6] INODE=(21) TYPE=[regular]-(1)>
directory1/subdirectory1/fortune3: DirectoryEntry<NAME=[fortune3] INODE=(17) TYPE=[regular]-(1)>
directory1/subdirectory1/fortune4: DirectoryEntry<NAME=[fortune4] INODE=(18) TYPE=[regular]-(1)>
directory1/subdirectory1: DirectoryEntry<NAME=[subdirectory1] INODE=(16) TYPE=[directory]-(2)>
directory1/subdirectory2/fortune7: DirectoryEntry<NAME=[fortune7] INODE=(22) TYPE=[regular]-(1)>
directory1/subdirectory2/fortune8: DirectoryEntry<NAME=[fortune8] INODE=(23) TYPE=[regular]-(1)>
directory1/subdirectory2: DirectoryEntry<NAME=[subdirectory2] INODE=(19) TYPE=[directory]-(2)>
directory1: DirectoryEntry<NAME=[directory1] INODE=(13) TYPE=[directory]-(2)>
directory2/fortune10: DirectoryEntry<NAME=[fortune10] INODE=(26) TYPE=[regular]-(1)>
directory2/fortune9: DirectoryEntry<NAME=[fortune9] INODE=(25) TYPE=[regular]-(1)>
directory2: DirectoryEntry<NAME=[directory2] INODE=(24) TYPE=[directory]-(2)>
lost+found: DirectoryEntry<NAME=[lost+found] INODE=(11) TYPE=[directory]-(2)>
thejungle.txt: DirectoryEntry<NAME=[thejungle.txt] INODE=(12) TYPE=[regular]-(1)>

type Ext4DirEntry2

type Ext4DirEntry2 struct {
	Inode    uint32 // Number of the inode that this directory entry points to.
	RecLen   uint16 // Length of this directory entry.
	NameLen  uint8  // Length of the file name.
	FileType uint8  // File type code, see ftype table below.
	Name     []byte // File name. Has a maximum size of Ext4FilenameMaxLen but actual length derived from `RecLen`.
}

Ext4DirEntry2 is one of potentially many sequential entries stored in a directory inode.

type ExtentHeaderNode

type ExtentHeaderNode struct {
	EhMagic      uint16 /* probably will support different formats */
	EhEntryCount uint16 /* number of valid entries */
	EhMax        uint16 /* capacity of store in entries */
	EhDepth      uint16 /* has tree real underlying blocks? */
	EhGeneration uint32 /* generation of the tree */
}

func (*ExtentHeaderNode) String

func (eh *ExtentHeaderNode) String() string

type ExtentIndexNode

type ExtentIndexNode struct {
	EiLogicalBlock        uint32 /* index covers logical blocks from 'block' */
	EiLeafPhysicalBlockLo uint32 /* pointer to the physical block of the next level. leaf or next index could be there */
	EiLeafPhysicalBlockHi uint16 /* high 16 bits of physical block */
	EiUnused              uint16
}

func (*ExtentIndexNode) LeafPhysicalBlock

func (ein *ExtentIndexNode) LeafPhysicalBlock() uint64

func (*ExtentIndexNode) String

func (ein *ExtentIndexNode) String() string

type ExtentLeafNode

type ExtentLeafNode struct {
	EeFirstLogicalBlock    uint32 /* first logical block extent covers */
	EeLogicalBlockCount    uint16 /* number of blocks covered by extent */
	EeStartPhysicalBlockHi uint16 /* high 16 bits of physical block */
	EeStartPhysicalBlockLo uint32 /* low 32 bits of physical block */
}

func (*ExtentLeafNode) StartPhysicalBlock

func (eln *ExtentLeafNode) StartPhysicalBlock() uint64

func (*ExtentLeafNode) String

func (eln *ExtentLeafNode) String() string

type ExtentNavigator

type ExtentNavigator struct {
	// contains filtered or unexported fields
}

func NewExtentNavigatorWithReadSeeker

func NewExtentNavigatorWithReadSeeker(rs io.ReadSeeker, inode *Inode) *ExtentNavigator

func (*ExtentNavigator) Read

func (en *ExtentNavigator) Read(offset uint64) (data []byte, err error)

Read returns the inode data from the given offset to the end of the logical block that it's found in.

"logical", meaning that (0) refers to the first block of this inode's data.

Example
f, inode, err := GetTestInode(TestFileInodeNumber)
log.PanicIf(err)

defer f.Close()

en := NewExtentNavigatorWithReadSeeker(f, inode)

inodeSize := inode.Size()
actualBytes := make([]byte, inodeSize)

for offset := uint64(0); offset < inodeSize; {
	data, err := en.Read(offset)
	log.PanicIf(err)

	copy(actualBytes[offset:], data)
	offset += uint64(len(data))
}

// Take the first part, strip some nonprintable characters from the front,
// and normalize the newlines so that this example tests properly.
firstPart := string(actualBytes[3:100])
firstPart = strings.Replace(firstPart, "\r\n", "\n", -1)

fmt.Println(firstPart)
Output:


The Project Gutenberg EBook of The Jungle, by Upton Sinclair

This eBook is for the use of anyo

type ExtentTail

type ExtentTail struct {
	EbChecksum uint32
}

type Inode

type Inode struct {
	// contains filtered or unexported fields
}

func GetInode

func GetInode(filesystemPath string, inodeNumber int) (f *os.File, inode *Inode, err error)

func GetTestInode

func GetTestInode(inodeNumber int) (f *os.File, inode *Inode, err error)

GetTestInode returns a test inode struct and `os.File` for the file. It's the responsibility of the caller to close it.

func NewInodeWithReadSeeker

func NewInodeWithReadSeeker(bgd *BlockGroupDescriptor, rs io.ReadSeeker, absoluteInodeNumber int) (inode *Inode, err error)

func (*Inode) AccessTime

func (inode *Inode) AccessTime() time.Time

func (*Inode) BlockGroupDescriptor

func (inode *Inode) BlockGroupDescriptor() (bgd *BlockGroupDescriptor)

func (*Inode) Data

func (inode *Inode) Data() *InodeData

func (*Inode) DeletionTime

func (inode *Inode) DeletionTime() time.Time

func (*Inode) Dump

func (inode *Inode) Dump()

func (*Inode) DumpFlags

func (inode *Inode) DumpFlags(includeFalses bool)

func (*Inode) FileCreationTime

func (inode *Inode) FileCreationTime() time.Time

func (*Inode) Flag

func (inode *Inode) Flag(flag int) bool

func (*Inode) InodeChangeTime

func (inode *Inode) InodeChangeTime() time.Time

func (*Inode) ModificationTime

func (inode *Inode) ModificationTime() time.Time

func (*Inode) Size

func (inode *Inode) Size() uint64

func (*Inode) String

func (inode *Inode) String() string

type InodeData

type InodeData struct {
	IMode       uint16 /* File mode */
	IUid        uint16 /* Low 16 bits of Owner Uid */
	ISizeLo     uint32 /* Size in bytes */
	IAtime      uint32 /* Access time */
	ICtime      uint32 /* Inode Change time */
	IMtime      uint32 /* Modification time */
	IDtime      uint32 /* Deletion Time */
	IGid        uint16 /* Low 16 bits of Group Id */
	ILinksCount uint16 /* Links count */
	IBlocksLo   uint32 /* Blocks count */
	IFlags      uint32 /* File flags */

	// union {
	//     struct {
	//         __le32  l_i_version;
	//     } linux1;
	//     struct {
	//         __u32  h_i_translator;
	//     } hurd1;
	//     struct {
	//         __u32  m_i_reserved1;
	//     } masix1;
	// } osd1;             /* OS dependent 1 */
	Osd1 [4]byte

	/*
		IBlock is a general buffer for our data, which can have various
		interpretations. `Ext4NBlocks` comes from the kernel where it is a count in
		terms of uint32's, which is then cast as a struct. However, it works better
		for us as an array of bytes.
	*/
	IBlock [Ext4NBlocks * 4]byte

	IGeneration uint32 /* File version (for NFS) */
	IFileAclLo  uint32 /* File ACL */
	ISizeHigh   uint32
	IObsoFaddr  uint32 /* Obsoleted fragment address */

	// union {
	//     struct {
	//         __le16  l_i_blocks_high; /* were l_i_reserved1 */
	//         __le16  l_i_file_acl_high;
	//         __le16  l_i_uid_high;   /* these 2 fields */
	//         __le16  l_i_gid_high;   /* were reserved2[0] */
	//         __le16  l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */
	//         __le16  l_i_reserved;
	//     } linux2;
	//     struct {
	//         __le16  h_i_reserved1;   Obsoleted fragment number/size which are removed in ext4
	//         __u16   h_i_mode_high;
	//         __u16   h_i_uid_high;
	//         __u16   h_i_gid_high;
	//         __u32   h_i_author;
	//     } hurd2;
	//     struct {
	//         __le16  h_i_reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */
	//         __le16  m_i_file_acl_high;
	//         __u32   m_i_reserved2[2];
	//     } masix2;
	// } osd2;             /* OS dependent 2 */
	Osd2 [12]byte

	IExtraIsize  uint16
	IChecksumHi  uint16 /* crc32c(uuid+inum+inode) BE */
	ICtimeExtra  uint32 /* extra Change time      (nsec << 2 | epoch) */
	IMtimeExtra  uint32 /* extra Modification time(nsec << 2 | epoch) */
	IAtimeExtra  uint32 /* extra Access time      (nsec << 2 | epoch) */
	ICrtime      uint32 /* File Creation time */
	ICrtimeExtra uint32 /* extra FileCreationtime (nsec << 2 | epoch) */
	IVersionHi   uint32 /* high 32 bits for 64-bit version */
	IProjid      uint32 /* Project ID */
}

type InodeReader

type InodeReader struct {
	// contains filtered or unexported fields
}

InodeReader fulfills the `io.Reader` interface to read arbitrary amounts of data.

func NewInodeReader

func NewInodeReader(en *ExtentNavigator) *InodeReader

func (*InodeReader) Offset

func (ir *InodeReader) Offset() uint64

func (*InodeReader) Read

func (ir *InodeReader) Read(p []byte) (n int, err error)

Read fills the given slice with data and returns an `io.EOF` error with (0) bytes when done. (`n`) may be less then `len(p)`.

func (*InodeReader) Skip

func (ir *InodeReader) Skip(n uint64) (skipped uint64, err error)

Skip simulates a read but just discards the data.

type Superblock

type Superblock struct {
	// contains filtered or unexported fields
}

func NewSuperblockWithReader

func NewSuperblockWithReader(rs io.ReadSeeker) (sb *Superblock, err error)
Example
filepath := path.Join(assetsPath, "tiny.ext4")

f, err := os.Open(filepath)
log.PanicIf(err)

defer f.Close()

// Skip over the boot-code at the front of the filesystem.
_, err = f.Seek(Superblock0Offset, io.SeekStart)
log.PanicIf(err)

sb, err := NewSuperblockWithReader(f)
log.PanicIf(err)

fmt.Println(sb.VolumeName())
Output:

tinyimage

func (*Superblock) BlockCount

func (sb *Superblock) BlockCount() uint64

func (*Superblock) BlockGroupCount

func (sb *Superblock) BlockGroupCount() (blockGroups uint64)

func (*Superblock) BlockGroupInodeNumberWithAbsoluteInodeNumber

func (sb *Superblock) BlockGroupInodeNumberWithAbsoluteInodeNumber(absoluteInodeNumber int) int

func (*Superblock) BlockGroupNumberWithAbsoluteInodeNumber

func (sb *Superblock) BlockGroupNumberWithAbsoluteInodeNumber(absoluteInodeNumber int) int

func (*Superblock) BlockSize

func (sb *Superblock) BlockSize() uint32

func (*Superblock) Data

func (sb *Superblock) Data() *SuperblockData

func (*Superblock) Dump

func (sb *Superblock) Dump()

func (*Superblock) DumpFeatures

func (sb *Superblock) DumpFeatures(includeFalses bool)

func (*Superblock) HasCompatibleFeature

func (sb *Superblock) HasCompatibleFeature(mask uint32) bool

func (*Superblock) HasExtended

func (sb *Superblock) HasExtended() bool

func (*Superblock) HasIncompatibleFeature

func (sb *Superblock) HasIncompatibleFeature(mask uint32) bool

func (*Superblock) HasReadonlyCompatibleFeature

func (sb *Superblock) HasReadonlyCompatibleFeature(mask uint32) bool

func (*Superblock) Is64Bit

func (sb *Superblock) Is64Bit() bool

func (*Superblock) LastCheckTime

func (sb *Superblock) LastCheckTime() time.Time

func (*Superblock) MountTime

func (sb *Superblock) MountTime() time.Time

func (*Superblock) ReadPhysicalBlock

func (sb *Superblock) ReadPhysicalBlock(absoluteBlockNumber uint64, length uint64) (data []byte, err error)
Example
filepath := path.Join(assetsPath, "tiny.ext4")

f, err := os.Open(filepath)
log.PanicIf(err)

defer f.Close()

_, err = f.Seek(Superblock0Offset, io.SeekStart)
log.PanicIf(err)

sb, err := NewSuperblockWithReader(f)
log.PanicIf(err)

pBlock := uint64(sb.Data().SFirstDataBlock)
data, err := sb.ReadPhysicalBlock(pBlock, uint64(SuperblockSize))
log.PanicIf(err)

data = data
Output:

func (*Superblock) VolumeName

func (sb *Superblock) VolumeName() string

func (*Superblock) WriteTime

func (sb *Superblock) WriteTime() time.Time

type SuperblockData

type SuperblockData struct {

	// 0x00
	SInodesCount       uint32
	SBlocksCountLo     uint32
	SRBlocksCountLo    uint32
	SFreeBlocksCountLo uint32

	// 0x10
	SFreeInodesCount uint32
	SFirstDataBlock  uint32
	SLogBlockSize    uint32
	SLogClusterSize  uint32

	// 0x20
	SBlocksPerGroup   uint32
	SClustersPerGroup uint32
	SInodesPerGroup   uint32
	SMtime            uint32

	// 0x30
	SWtime         uint32
	SMntCount      uint16
	SMaxMntCount   uint16
	SMagic         uint16
	SState         uint16
	SErrors        uint16
	SMinorRevLevel uint16

	// 0x40
	SLastcheck     uint32
	SCheckinterval uint32
	SCreatorOs     uint32
	SRevLevel      uint32

	// 0x50
	SDefResuid uint16
	SDefResgid uint16

	/*
	 * These fields are for EXT4_DYNAMIC_REV superblocks only.
	 *
	 * Note: the difference between the compatible feature set and
	 * the incompatible feature set is that if there is a bit set
	 * in the incompatible feature set that the kernel doesn't
	 * know about, it should refuse to mount the filesystem.
	 *
	 * e2fsck's requirements are more strict; if it doesn't know
	 * about a feature in either the compatible or incompatible
	 * feature set, it must abort and not try to meddle with
	 * things it doesn't understand...
	 */
	SFirstIno      uint32 /* First non-reserved inode */
	SInodeSize     uint16 /* size of inode structure */
	SBlockGroupNr  uint16 /* block group # of this superblock */
	SFeatureCompat uint32 /* compatible feature set */

	// 0x60
	SFeatureIncompat uint32 /* incompatible feature set */
	SFeatureRoCompat uint32 /* readonly-compatible feature set */

	// 0x68
	SUuid [16]uint8 /* 128-bit uuid for volume */

	// 0x78
	SVolumeName [16]byte /* volume name */

	// 0x88
	SLastMounted [64]byte /* directory where last mounted */

	// 0xC8
	SAlgorithmUsageBitmap uint32 /* For compression */

	/*
	 * Performance hints.  Directory preallocation should only
	 * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
	 */
	SPreallocBlocks    uint8  /* Nr of blocks to try to preallocate*/
	SPreallocDirBlocks uint8  /* Nr to preallocate for dirs */
	SReservedGdtBlocks uint16 /* Per group desc for online growth */

	// 0xD0
	/*
	 * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
	 */
	SJournalUuid [16]uint8 /* uuid of journal superblock */

	// 0xE0
	SJournalInum    uint32    /* inode number of journal file */
	SJournalDev     uint32    /* device number of journal file */
	SLastOrphan     uint32    /* start of list of inodes to delete */
	SHashSeed       [4]uint32 /* HTREE hash seed */
	SDefHashVersion uint8     /* Default hash version to use */
	SJnlBackupType  uint8
	SDescSize       uint16 /* Size of group descriptors, in bytes, if the 64bit incompat feature flag is set. */

	// 0x100
	SDefaultMountOpts uint32
	SFirstMetaBg      uint32     /* First metablock block group */
	SMkfsTime         uint32     /* When the filesystem was created */
	SJnlBlocks        [17]uint32 /* Backup of the journal inode */

	// 0x150
	SBlocksCountHi     uint32 /* Blocks count */
	SRBlocksCountHi    uint32 /* Reserved blocks count */
	SFreeBlocksCountHi uint32 /* Free blocks count */
	SMinExtraIsize     uint16 /* All inodes have at least # bytes */
	SWantExtraIsize    uint16 /* New inodes should reserve # bytes */

	SFlags            uint32 /* Miscellaneous flags */
	SRaidStride       uint16 /* RAID stride */
	SMmpInterval      uint16 /* # seconds to wait in MMP checking */
	SMmpBlock         uint64 /* Block for multi-mount protection */
	SRaidStripeWidth  uint32 /* blocks on all data disks (N*stride)*/
	SLogGroupsPerFlex uint8  /* FLEX_BG group size */
	SChecksumType     uint8  /* metadata checksum algorithm used */
	SEncryptionLevel  uint8  /* versioning level for encryption */
	SReservedPad      uint8  /* Padding to next 32bits */
	SKbytesWritten    uint64 /* nr of lifetime kilobytes written */

	SSnapshotInum         uint32 /* Inode number of active snapshot */
	SSnapshotId           uint32 /* sequential ID of active snapshot */
	SSnapshotRBlocksCount uint64 /* reserved blocks for active snapshot's future use */
	SSnapshotList         uint32 /* inode number of the head of the on-disk snapshot list */

	SErrorCount      uint32    /* number of fs errors */
	SFirstErrorTime  uint32    /* first time an error happened */
	SFirstErrorIno   uint32    /* inode involved in first error */
	SFirstErrorBlock uint64    /* block involved of first error */
	SFirstErrorFunc  [32]uint8 /* function where the error happened */
	SFirstErrorLine  uint32    /* line number where error happened */
	SLastErrorTime   uint32    /* most recent time of an error */
	SLastErrorIno    uint32    /* inode involved in last error */
	SLastErrorLine   uint32    /* line number where error happened */
	SLastErrorBlock  uint64    /* block involved of last error */
	SLastErrorFunc   [32]uint8 /* function where the error happened */

	SMountOpts        [64]uint8
	SUsrQuotaInum     uint32    /* inode for tracking user quota */
	SGrpQuotaInum     uint32    /* inode for tracking group quota */
	SOverheadClusters uint32    /* overhead blocks/clusters in fs */
	SBackupBgs        [2]uint32 /* groups with sparse_super2 SBs */
	SEncryptAlgos     [4]uint8  /* Encryption algorithms in use  */
	SEncryptPwSalt    [16]uint8 /* Salt used for string2key algorithm */
	SLpfIno           uint32    /* Location of the lost+found inode */
	SPrjQuotaInum     uint32    /* inode for tracking project quota */
	SChecksumSeed     uint32    /* crc32c(uuid) if csum_seed set */
	SWtimeHi          uint8
	SMtimeHi          uint8
	SMkfsTimeHi       uint8
	SLastcheckHi      uint8
	SFirstErrorTimeHi uint8
	SLastErrorTimeHi  uint8
	SPad              [2]uint8
	SReserved         [96]uint32 /* Padding to the end of the block */
	SChecksum         int32      /* crc32c(superblock) */
}

Directories

Path Synopsis