github.com/apptainer/singularity@v3.1.1+incompatible/pkg/image/unpacker/squashfs.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 unpacker 7 8 import ( 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 ) 15 16 // Squashfs represents a squashfs unpacker 17 type Squashfs struct { 18 UnsquashfsPath string 19 } 20 21 // NewSquashfs initializes and returns a Squahfs unpacker instance 22 func NewSquashfs() *Squashfs { 23 s := &Squashfs{} 24 s.UnsquashfsPath, _ = exec.LookPath("unsquashfs") 25 return s 26 } 27 28 // HasUnsquashfs returns if unsquashfs binary has been found or not 29 func (s *Squashfs) HasUnsquashfs() bool { 30 return s.UnsquashfsPath != "" 31 } 32 33 func (s *Squashfs) extract(files []string, reader io.Reader, dest string) error { 34 if !s.HasUnsquashfs() { 35 return fmt.Errorf("could not extract squashfs data, unsquashfs not found") 36 } 37 38 // pipe over stdin by default 39 stdin := true 40 filename := "/proc/self/fd/0" 41 42 if _, ok := reader.(*os.File); !ok { 43 // unsquashfs doesn't support to send file content over 44 // a stdin pipe since it use lseek for every read it does 45 tmp, err := ioutil.TempFile("", "archive-") 46 if err != nil { 47 return fmt.Errorf("failed to create staging file: %s", err) 48 } 49 filename = tmp.Name() 50 stdin = false 51 defer os.Remove(filename) 52 53 if _, err := io.Copy(tmp, reader); err != nil { 54 return fmt.Errorf("failed to copy content in staging file: %s", err) 55 } 56 if err := tmp.Close(); err != nil { 57 return fmt.Errorf("failed to close staging file: %s", err) 58 } 59 } 60 61 args := []string{"-f", "-d", dest, filename} 62 for _, f := range files { 63 args = append(args, f) 64 } 65 cmd := exec.Command(s.UnsquashfsPath, args...) 66 if stdin { 67 cmd.Stdin = reader 68 } 69 if err := cmd.Run(); err != nil { 70 return fmt.Errorf("extract command failed: %s", err) 71 } 72 return nil 73 } 74 75 // ExtractAll extracts a squashfs filesystem read from reader to a 76 // destination directory 77 func (s *Squashfs) ExtractAll(reader io.Reader, dest string) error { 78 return s.extract([]string{}, reader, dest) 79 } 80 81 // ExtractFiles extracts provided files from a squashfs filesystem 82 // read from reader to a destination directory 83 func (s *Squashfs) ExtractFiles(files []string, reader io.Reader, dest string) error { 84 if len(files) == 0 { 85 return fmt.Errorf("no files to extract") 86 } 87 return s.extract(files, reader, dest) 88 }