github.com/hpcng/singularity@v3.1.1+incompatible/pkg/image/squashfs.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  	"github.com/sylabs/singularity/internal/pkg/sylog"
    16  )
    17  
    18  const (
    19  	squashfsMagic    = "\x68\x73\x71\x73"
    20  	squashfsZlib     = 1
    21  	squashfsLzmaComp = 2
    22  	squashfsLzoComp  = 3
    23  	squashfsXzComp   = 4
    24  	squashfsLz4Comp  = 5
    25  )
    26  
    27  type squashfsInfo struct {
    28  	Magic       [4]byte
    29  	Inodes      uint32
    30  	MkfsTime    uint32
    31  	BlockSize   uint32
    32  	Fragments   uint32
    33  	Compression uint16
    34  }
    35  
    36  type squashfsFormat struct{}
    37  
    38  // CheckSquashfsHeader checks if byte content contains a valid squashfs header
    39  // and returns offset where squashfs partition start
    40  func CheckSquashfsHeader(b []byte) (uint64, error) {
    41  	var offset uint64
    42  
    43  	o := bytes.Index(b, []byte(launchString))
    44  	if o > 0 {
    45  		offset += uint64(o + len(launchString) + 1)
    46  	}
    47  	sinfo := &squashfsInfo{}
    48  
    49  	if uintptr(offset)+unsafe.Sizeof(sinfo) >= uintptr(len(b)) {
    50  		return offset, fmt.Errorf("can't find squashfs information header")
    51  	}
    52  
    53  	buffer := bytes.NewReader(b[offset:])
    54  
    55  	if err := binary.Read(buffer, binary.LittleEndian, sinfo); err != nil {
    56  		return offset, fmt.Errorf("can't read the top of the image")
    57  	}
    58  	if bytes.Compare(sinfo.Magic[:], []byte(squashfsMagic)) != 0 {
    59  		return offset, fmt.Errorf("not a valid squashfs image")
    60  	}
    61  
    62  	if sinfo.Compression != squashfsZlib {
    63  		compressionType := ""
    64  		switch sinfo.Compression {
    65  		case squashfsLzmaComp:
    66  			compressionType = "lzma"
    67  		case squashfsLz4Comp:
    68  			compressionType = "lz4"
    69  		case squashfsLzoComp:
    70  			compressionType = "lzo"
    71  		case squashfsXzComp:
    72  			compressionType = "xz"
    73  		}
    74  		sylog.Infof("squashfs image was compressed with %s, if it failed to run, please contact image's author", compressionType)
    75  	}
    76  	return offset, nil
    77  }
    78  
    79  func (f *squashfsFormat) initializer(img *Image, fileinfo os.FileInfo) error {
    80  	if fileinfo.IsDir() {
    81  		return fmt.Errorf("not a squashfs image")
    82  	}
    83  	b := make([]byte, bufferSize)
    84  	if n, err := img.File.Read(b); err != nil || n != bufferSize {
    85  		return fmt.Errorf("can't read first %d bytes: %s", bufferSize, err)
    86  	}
    87  	offset, err := CheckSquashfsHeader(b)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	img.Type = SQUASHFS
    92  	img.Partitions[0].Offset = offset
    93  	img.Partitions[0].Size = uint64(fileinfo.Size()) - offset
    94  	img.Partitions[0].Type = SQUASHFS
    95  	img.Partitions[0].Name = RootFs
    96  
    97  	if img.Writable {
    98  		sylog.Warningf("squashfs is not a writable filesystem")
    99  		img.Writable = false
   100  	}
   101  
   102  	return nil
   103  }
   104  
   105  func (f *squashfsFormat) openMode(writable bool) int {
   106  	return os.O_RDONLY
   107  }