github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/build/sources/packer_sif_linux.go (about)

     1  // Copyright (c) 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 sources
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"syscall"
    15  
    16  	"github.com/sylabs/sif/pkg/sif"
    17  	"github.com/sylabs/singularity/internal/pkg/sylog"
    18  	"github.com/sylabs/singularity/pkg/build/types"
    19  	"github.com/sylabs/singularity/pkg/util/loop"
    20  )
    21  
    22  // Pack puts relevant objects in a Bundle!
    23  func (p *SIFPacker) Pack() (*types.Bundle, error) {
    24  
    25  	err := p.unpackSIF(p.b, p.srcfile)
    26  	if err != nil {
    27  		sylog.Errorf("unpackSIF Failed: %s", err)
    28  		return nil, err
    29  	}
    30  
    31  	return p.b, nil
    32  }
    33  
    34  // First pass just assumes a single system partition, later passes will handle more complex sif files
    35  // unpackSIF parses through the sif file and places each component in the sandbox
    36  func (p *SIFPacker) unpackSIF(b *types.Bundle, rootfs string) (err error) {
    37  
    38  	// load the container
    39  	fimg, err := sif.LoadContainer(rootfs, true)
    40  	if err != nil {
    41  		sylog.Errorf("error loading sif file %s: %s\n", rootfs, err)
    42  		return err
    43  	}
    44  	defer fimg.UnloadContainer()
    45  
    46  	// Get the default system partition image as rootfs part
    47  	part, _, err := fimg.GetPartPrimSys()
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	// record the fs type
    53  	mountType := ""
    54  	fstype, err := part.GetFsType()
    55  	if err != nil {
    56  		return err
    57  	}
    58  	if fstype == sif.FsSquash {
    59  		mountType = "squashfs"
    60  	} else if fstype == sif.FsExt3 {
    61  		mountType = "ext3"
    62  	} else {
    63  		return fmt.Errorf("unknown file system type: %v", fstype)
    64  	}
    65  
    66  	info := &loop.Info64{
    67  		Offset:    uint64(part.Fileoff),
    68  		SizeLimit: uint64(part.Filelen),
    69  		Flags:     loop.FlagsAutoClear,
    70  	}
    71  
    72  	//copy partition contents to bundle rootfs
    73  	err = unpackImagePartion(fimg.Fp.Name(), b.Rootfs(), mountType, info)
    74  	if err != nil {
    75  		return fmt.Errorf("While copying partition data to bundle: %v", err)
    76  	}
    77  
    78  	return nil
    79  }
    80  
    81  // unpackImagePart temporarily mounts an image parition using a loop device and then copies its contents to the destination directory
    82  func unpackImagePartion(src, dest, mountType string, info *loop.Info64) (err error) {
    83  
    84  	var number int
    85  	number = 0
    86  	loopdev := new(loop.Device)
    87  	loopdev.MaxLoopDevices = 256
    88  	loopdev.Info = info
    89  
    90  	if err := loopdev.AttachFromPath(src, os.O_RDONLY, &number); err != nil {
    91  		return err
    92  	}
    93  
    94  	tmpmnt, err := ioutil.TempDir("", "tmpmnt-")
    95  	if err != nil {
    96  		return fmt.Errorf("Failed to make tmp mount point: %v", err)
    97  	}
    98  	defer os.RemoveAll(tmpmnt)
    99  
   100  	path := fmt.Sprintf("/dev/loop%d", number)
   101  	sylog.Debugf("Mounting loop device %s to %s\n", path, tmpmnt)
   102  	err = syscall.Mount(path, tmpmnt, mountType, syscall.MS_NOSUID|syscall.MS_RDONLY|syscall.MS_NODEV, "errors=remount-ro")
   103  	if err != nil {
   104  		sylog.Errorf("Mount Failed: %s", err)
   105  		return err
   106  	}
   107  	defer syscall.Unmount(tmpmnt, 0)
   108  
   109  	// copy filesystem into dest
   110  	sylog.Debugf("Copying filesystem from %s to %s\n", tmpmnt, dest)
   111  	var stderr bytes.Buffer
   112  	cmd := exec.Command("cp", "-r", tmpmnt+`/.`, dest)
   113  	cmd.Stderr = &stderr
   114  	if err := cmd.Run(); err != nil {
   115  		return fmt.Errorf("cp Failed: %v: %v", err, stderr.String())
   116  	}
   117  
   118  	return nil
   119  }