File: squashfs.go

package info (click to toggle)
snapd 2.71-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 79,536 kB
  • sloc: ansic: 16,114; sh: 16,105; python: 9,941; makefile: 1,890; exp: 190; awk: 40; xml: 22
file content (138 lines) | stat: -rw-r--r-- 4,222 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
 * Copyright (C) 2018 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package syscheck

import (
	"bytes"
	"compress/gzip"
	"encoding/base64"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/snapcore/snapd/logger"
	"github.com/snapcore/snapd/osutil"
	"github.com/snapcore/snapd/osutil/squashfs"
	"github.com/snapcore/snapd/sandbox/selinux"
)

func init() {
	checks = append(checks, checkSquashfsMount)
}

// This image was created using:
//
// #!/bin/sh
//
// cd $(mktemp -d)
// cat > canary.txt<<'EOF'
// This file is used to check that snapd can read a squashfs image.
//
// The squashfs was generated with:
// EOF
// cat $0 >> canary.txt
//
// mksquashfs . /tmp/canary.squashfs -noappend -comp xz -no-xattrs -no-fragments >/dev/nul
// cat /tmp/canary.squashfs | gzip - | base64
var b64SquashfsImage = []byte(`
H4sIAGxdFVsAA8soLixmYmBgyIkVjWZgALEYGFgYBBkuMDECaQYGFQYI4INIMbBB6f9Q0MAI4R+D
0s+g9A8o/de8KiKKgYExU+meGfOB54wzmBUZuYDiVhPYbR4wTme4H8ugJcWpniK5waL4VLewwsUC
PgdVnS/pCycWn34g1rpj6bIywdLqaQdZFYQcYr7/vR1w9dTbDivRH3GahXc578hdW3Ri7mu9+KeF
PqYCrkk/5zepyFw0EjL+XxH/3ubc9E+/J0t0PxE+zv9J96pa0rt9CWyvX6aIvb3H65qo9mbikvjU
LZxrOupvcr32+2yYFzt1wTe2HdFfrOSmKXFFPf1i5ep7Wv+q+U+nBNWs/nu+UosO6PFvfl991nVG
R9XSJUxv/7/y2f2zid0+OnGi1+ey1/vatzDPvfbq+0LLwIu1Wx/u+m6/c8IN21vNCQwMX2dtWsHA
+BvodwaGpcmXftsZ8HaDg5ExMsqlgYlhCTisQDEAYiRAQxckNgMooADEjAwH4GqgEQCOK0UgBhrK
INcAFWRghMtyMiQn5iUWVeqVVJQIwOVh8QmLJ5aGF8wMsIgfBaNgFIyCUTAKRsEoGAWjYBSMglEw
bAEA+f+YuAAQAAA=
`)

var fuseBinary = "mount.fuse"

func firstCheckFuse() error {
	if squashfs.NeedsFuse() {
		if _, err := exec.LookPath(fuseBinary); err != nil {
			return fmt.Errorf(`The "fuse" filesystem is required on this system but not available. Please try to install the fuse package.`)
		}
	}
	return nil
}

func checkSquashfsMount() error {
	if err := firstCheckFuse(); err != nil {
		return err
	}

	tmpSquashfsFile, err := os.CreateTemp("", "syscheck-squashfs-")
	if err != nil {
		return err
	}
	defer os.Remove(tmpSquashfsFile.Name())

	tmpMountDir, err := os.MkdirTemp("", "syscheck-mountpoint-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpMountDir)

	// write the squashfs image
	b64dec := base64.NewDecoder(base64.StdEncoding, bytes.NewBuffer(b64SquashfsImage))
	gzReader, err := gzip.NewReader(b64dec)
	if err != nil {
		return err
	}
	if _, err := io.Copy(tmpSquashfsFile, gzReader); err != nil {
		return err
	}

	// the fstype can be squashfs or fuse.{snap,squash}fuse
	fstype, _ := squashfs.FsType()
	options := []string{"-t", fstype}
	if selinux.ProbedLevel() != selinux.Unsupported {
		if ctx := selinux.SnapMountContext(); ctx != "" {
			options = append(options, "-o", "context="+ctx)
		}
	}
	options = append(options, tmpSquashfsFile.Name(), tmpMountDir)
	cmd := exec.Command("mount", options...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("cannot mount squashfs image using %q: %v", fstype, osutil.OutputErr(output, err))
	}

	defer func() {
		if output, err := exec.Command("umount", "-l", tmpMountDir).CombinedOutput(); err != nil {
			// os.RemoveAll(tmpMountDir) will fail here if umount fails
			logger.Noticef("cannot unmount syscheck check squashfs image: %v", osutil.OutputErr(output, err))
		}
	}()

	// syscheck check the
	content, err := os.ReadFile(filepath.Join(tmpMountDir, "canary.txt"))
	if err != nil {
		return fmt.Errorf("squashfs mount returned no err but canary file cannot be read")
	}
	if !bytes.HasPrefix(content, []byte("This file is used to check that snapd can read a squashfs image.")) {
		return fmt.Errorf("unexpected squashfs canary content: %q", content)
	}

	return nil
}