github.com/apptainer/singularity@v3.1.1+incompatible/pkg/image/ext3.go (about)

     1  // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package image
     7  
     8  import (
     9  	"bytes"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"os"
    13  	"unsafe"
    14  )
    15  
    16  const (
    17  	extMagicOffset      = 1080
    18  	extMagic            = "\x53\xEF"
    19  	compatHasJournal    = 0x4
    20  	incompatFileType    = 0x2
    21  	incompatRecover     = 0x4
    22  	incompatMetabg      = 0x10
    23  	rocompatSparseSuper = 0x1
    24  	rocompatLargeFile   = 0x2
    25  	rocompatBtreeDir    = 0x4
    26  )
    27  
    28  const notValidExt3ImageMessage = "file is not a valid ext3 image"
    29  
    30  type extFSInfo struct {
    31  	Magic    [2]byte
    32  	State    uint16
    33  	Dummy    [8]uint32
    34  	Compat   uint32
    35  	Incompat uint32
    36  	Rocompat uint32
    37  }
    38  
    39  type ext3Format struct{}
    40  
    41  // CheckExt3Header checks if byte content contains a valid ext3 header
    42  // and returns offset where ext3 partition begin
    43  func CheckExt3Header(b []byte) (uint64, error) {
    44  	var offset uint64 = extMagicOffset
    45  
    46  	o := bytes.Index(b, []byte(launchString))
    47  	if o > 0 {
    48  		offset += uint64(o + len(launchString) + 1)
    49  	}
    50  	einfo := &extFSInfo{}
    51  
    52  	if uintptr(offset)+unsafe.Sizeof(einfo) >= uintptr(len(b)) {
    53  		return offset, fmt.Errorf("can't find ext3 information header")
    54  	}
    55  	buffer := bytes.NewReader(b[offset:])
    56  
    57  	if err := binary.Read(buffer, binary.LittleEndian, einfo); err != nil {
    58  		return offset, fmt.Errorf("can't read the top of the image")
    59  	}
    60  	if bytes.Compare(einfo.Magic[:], []byte(extMagic)) != 0 {
    61  		return offset, fmt.Errorf(notValidExt3ImageMessage)
    62  	}
    63  	if einfo.Compat&compatHasJournal == 0 {
    64  		return offset, fmt.Errorf(notValidExt3ImageMessage)
    65  	}
    66  	if einfo.Incompat&^(incompatFileType|incompatRecover|incompatMetabg) != 0 {
    67  		return offset, fmt.Errorf(notValidExt3ImageMessage)
    68  	}
    69  	if einfo.Rocompat&^(rocompatSparseSuper|rocompatLargeFile|rocompatBtreeDir) != 0 {
    70  		return offset, fmt.Errorf(notValidExt3ImageMessage)
    71  	}
    72  	offset -= extMagicOffset
    73  	return offset, nil
    74  }
    75  
    76  func (f *ext3Format) initializer(img *Image, fileinfo os.FileInfo) error {
    77  	if fileinfo.IsDir() {
    78  		return fmt.Errorf("not an ext3 image")
    79  	}
    80  	b := make([]byte, bufferSize)
    81  	if n, err := img.File.Read(b); err != nil || n != bufferSize {
    82  		return fmt.Errorf("can't read first %d bytes: %s", bufferSize, err)
    83  	}
    84  	offset, err := CheckExt3Header(b)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	img.Type = EXT3
    89  	img.Partitions[0].Offset = offset
    90  	img.Partitions[0].Size = uint64(fileinfo.Size()) - offset
    91  	img.Partitions[0].Type = EXT3
    92  	img.Partitions[0].Name = RootFs
    93  	return nil
    94  }
    95  
    96  func (f *ext3Format) openMode(writable bool) int {
    97  	if writable {
    98  		return os.O_RDWR
    99  	}
   100  	return os.O_RDONLY
   101  }