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 }