github.com/usbarmory/armory-boot@v0.0.0-20240307133412-208c66a380b9/disk/ext4.go (about)

     1  // https://github.com/usbarmory/armory-boot
     2  //
     3  // Copyright (c) WithSecure Corporation
     4  // https://foundry.withsecure.com
     5  //
     6  // Use of this source code is governed by the license
     7  // that can be found in the LICENSE file.
     8  
     9  // Package disk provides support for SD/MMC card partition access, only ext4
    10  // filesystems are currently supported.
    11  //
    12  // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as
    13  // supported by the TamaGo framework for bare metal Go on ARM SoCs, see
    14  // https://github.com/usbarmory/tamago.
    15  package disk
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"io"
    21  	"io/ioutil"
    22  	"strings"
    23  
    24  	"github.com/dsoprea/go-ext4"
    25  
    26  	"github.com/usbarmory/tamago/soc/nxp/usdhc"
    27  )
    28  
    29  // Partition represents an SD/MMC card partition, only ext4 filesystems are
    30  // currently supported.
    31  type Partition struct {
    32  	Card    *usdhc.USDHC
    33  	Offset  int64
    34  	_offset int64
    35  }
    36  
    37  func (part *Partition) getBlockGroupDescriptor(inode int) (bgd *ext4.BlockGroupDescriptor, err error) {
    38  	_, err = part.Seek(ext4.Superblock0Offset, io.SeekStart)
    39  
    40  	if err != nil {
    41  		return
    42  	}
    43  
    44  	sb, err := ext4.NewSuperblockWithReader(part)
    45  
    46  	if err != nil {
    47  		return
    48  	}
    49  
    50  	bgdl, err := ext4.NewBlockGroupDescriptorListWithReadSeeker(part, sb)
    51  
    52  	if err != nil {
    53  		return
    54  	}
    55  
    56  	return bgdl.GetWithAbsoluteInode(inode)
    57  }
    58  
    59  func (part *Partition) Read(p []byte) (n int, err error) {
    60  	buf, err := part.Card.Read(part._offset, int64(len(p)))
    61  
    62  	if err != nil {
    63  		return
    64  	}
    65  
    66  	n = copy(p, buf)
    67  	_, err = part.Seek(int64(n), io.SeekCurrent)
    68  
    69  	return
    70  }
    71  
    72  func (part *Partition) Seek(offset int64, whence int) (int64, error) {
    73  	info := part.Card.Info()
    74  	end := int64(info.Blocks) * int64(info.BlockSize)
    75  
    76  	switch whence {
    77  	case io.SeekStart:
    78  		part._offset = part.Offset + offset
    79  	case io.SeekCurrent:
    80  		part._offset += offset
    81  	case io.SeekEnd:
    82  		part._offset = end + part.Offset + offset
    83  	default:
    84  		return 0, fmt.Errorf("invalid whence %d", whence)
    85  	}
    86  
    87  	if part._offset > end {
    88  		return 0, fmt.Errorf("invalid offset %d (%d)", part._offset, offset)
    89  	}
    90  
    91  	if part._offset < part.Offset {
    92  		return 0, fmt.Errorf("invalid offset %d (%d)", part._offset, offset)
    93  	}
    94  
    95  	return part._offset, nil
    96  }
    97  
    98  func (part *Partition) ReadAll(fullPath string) (buf []byte, err error) {
    99  	fullPath = strings.TrimPrefix(fullPath, "/")
   100  	path := strings.Split(fullPath, "/")
   101  
   102  	bgd, err := part.getBlockGroupDescriptor(ext4.InodeRootDirectory)
   103  
   104  	if err != nil {
   105  		return
   106  	}
   107  
   108  	dw, err := ext4.NewDirectoryWalk(part, bgd, ext4.InodeRootDirectory)
   109  
   110  	var i int
   111  	var inodeNumber int
   112  
   113  	for {
   114  		if err != nil {
   115  			return
   116  		}
   117  
   118  		p, de, err := dw.Next()
   119  
   120  		if err == io.EOF {
   121  			break
   122  		} else if err != nil {
   123  			return nil, err
   124  		}
   125  
   126  		deInode := int(de.Data().Inode)
   127  
   128  		bgd, err = part.getBlockGroupDescriptor(deInode)
   129  
   130  		if err != nil {
   131  			return nil, err
   132  		}
   133  
   134  		if p == path[i] {
   135  			if i == len(path)-1 {
   136  				inodeNumber = deInode
   137  				break
   138  			} else {
   139  				dw, err = ext4.NewDirectoryWalk(part, bgd, deInode)
   140  
   141  				if err != nil {
   142  					return nil, err
   143  				}
   144  
   145  				i += 1
   146  			}
   147  		}
   148  	}
   149  
   150  	if inodeNumber == 0 {
   151  		return nil, errors.New("file not found")
   152  	}
   153  
   154  	inode, err := ext4.NewInodeWithReadSeeker(bgd, part, inodeNumber)
   155  
   156  	if err != nil {
   157  		return
   158  	}
   159  
   160  	en := ext4.NewExtentNavigatorWithReadSeeker(part, inode)
   161  	r := ext4.NewInodeReader(en)
   162  
   163  	return ioutil.ReadAll(r)
   164  }