// Copyright (C) 2022-2023 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later package btrfsitem import ( "fmt" "git.lukeshu.com/btrfs-progs-ng/lib/binstruct" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsprim" "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol" ) // FileExtent items map from regions in a file to regions in the // logical address space. // // Compare with: // // - Extents, which map from regions in the logical address space to // regions in a file. // - Metadata, which are like Extents but without .Info. // // Key: // // key.objectid = inode // key.offset = offset within file type FileExtent struct { // complex EXTENT_DATA=108 Generation btrfsprim.Generation `bin:"off=0x0, siz=0x8"` // transaction ID that created this extent RAMBytes int64 `bin:"off=0x8, siz=0x8"` // upper bound of what compressed data will decompress to // 32 bits describing the data encoding Compression CompressionType `bin:"off=0x10, siz=0x1"` Encryption uint8 `bin:"off=0x11, siz=0x1"` OtherEncoding uint16 `bin:"off=0x12, siz=0x2"` // reserved for later use Type FileExtentType `bin:"off=0x14, siz=0x1"` // inline data or real extent binstruct.End `bin:"off=0x15"` // only one of these, depending on .Type BodyInline []byte `bin:"-"` // .Type == FILE_EXTENT_INLINE BodyExtent FileExtentExtent `bin:"-"` // .Type == FILE_EXTENT_REG or FILE_EXTENT_PREALLOC } type FileExtentExtent struct { // Position and size of extent within the device DiskByteNr btrfsvol.LogicalAddr `bin:"off=0x0, siz=0x8"` DiskNumBytes btrfsvol.AddrDelta `bin:"off=0x8, siz=0x8"` // Position of data within the extent Offset btrfsvol.AddrDelta `bin:"off=0x10, siz=0x8"` // Decompressed/unencrypted size NumBytes int64 `bin:"off=0x18, siz=0x8"` binstruct.End `bin:"off=0x20"` } func (o *FileExtent) Free() { bytePool.Put(o.BodyInline) *o = FileExtent{} fileExtentPool.Put(o) } func (o FileExtent) Clone() FileExtent { o.BodyInline = cloneBytes(o.BodyInline) return o } func (o *FileExtent) UnmarshalBinary(dat []byte) (int, error) { n, err := binstruct.UnmarshalWithoutInterface(dat, o) if err != nil { return n, err } switch o.Type { case FILE_EXTENT_INLINE: o.BodyInline = cloneBytes(dat[n:]) n += len(o.BodyInline) case FILE_EXTENT_REG, FILE_EXTENT_PREALLOC: _n, err := binstruct.Unmarshal(dat[n:], &o.BodyExtent) n += _n if err != nil { return n, err } default: return n, fmt.Errorf("unknown file extent type %v", o.Type) } return n, nil } func (o FileExtent) MarshalBinary() ([]byte, error) { dat, err := binstruct.MarshalWithoutInterface(o) if err != nil { return dat, err } switch o.Type { case FILE_EXTENT_INLINE: dat = append(dat, o.BodyInline...) case FILE_EXTENT_REG, FILE_EXTENT_PREALLOC: bs, err := binstruct.Marshal(o.BodyExtent) dat = append(dat, bs...) if err != nil { return dat, err } default: return dat, fmt.Errorf("unknown file extent type %v", o.Type) } return dat, nil } type FileExtentType uint8 const ( FILE_EXTENT_INLINE FileExtentType = iota FILE_EXTENT_REG FILE_EXTENT_PREALLOC ) var fileExtentTypeNames = []string{ "inline", "regular", "prealloc", } func (o FileExtent) Size() (int64, error) { switch o.Type { case FILE_EXTENT_INLINE: return int64(len(o.BodyInline)), nil case FILE_EXTENT_REG, FILE_EXTENT_PREALLOC: return o.BodyExtent.NumBytes, nil default: return 0, fmt.Errorf("unknown file extent type %v", o.Type) } } func (fet FileExtentType) String() string { name := "unknown" if int(fet) < len(fileExtentTypeNames) { name = fileExtentTypeNames[fet] } return fmt.Sprintf("%d (%s)", fet, name) } type CompressionType uint8 const ( COMPRESS_NONE CompressionType = iota COMPRESS_ZLIB COMPRESS_LZO COMPRESS_ZSTD ) var compressionTypeNames = []string{ "none", "zlib", "lzo", "zstd", } func (ct CompressionType) String() string { name := "unknown" if int(ct) < len(compressionTypeNames) { name = compressionTypeNames[ct] } return fmt.Sprintf("%d (%s)", ct, name) }