File: helpers.go

package info (click to toggle)
snapd 2.72-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 80,412 kB
  • sloc: sh: 16,506; ansic: 16,211; python: 11,213; makefile: 1,919; exp: 190; awk: 58; xml: 22
file content (151 lines) | stat: -rw-r--r-- 4,863 bytes parent folder | download
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
139
140
141
142
143
144
145
146
147
148
149
150
151
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
 * Copyright (C) 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 builtin

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/snapcore/snapd/dirs"
	"github.com/snapcore/snapd/interfaces"
	"github.com/snapcore/snapd/interfaces/ldconfig"
	"github.com/snapcore/snapd/osutil"
	"github.com/snapcore/snapd/snap"
)

// validateSnapDir checks that dir starts with either $SNAP or ${SNAP}.
func validateSnapDir(dir string) error {
	if !strings.HasPrefix(dir, "$SNAP/") && !strings.HasPrefix(dir, "${SNAP}/") {
		return fmt.Errorf("source directory %q must start with $SNAP/ or ${SNAP}/", dir)
	}
	return nil
}

// validateLdconfigLibDirs checks that the list of directories in the
// "library-source" attribute of some slots (which is used by some interfaces
// that pass them to the ldconfig backend) is valid.
func validateLdconfigLibDirs(slot *snap.SlotInfo) error {
	// Validate directories and make sure the client driver is around
	libDirs := []string{}
	if err := slot.Attr("library-source", &libDirs); err != nil {
		return err
	}
	for _, dir := range libDirs {
		if !strings.HasPrefix(dir, "$SNAP/") && !strings.HasPrefix(dir, "${SNAP}/") {
			return fmt.Errorf(
				"%s source directory %q must start with $SNAP/ or ${SNAP}/",
				slot.Interface, dir)
		}
	}

	return nil
}

// addLdconfigLibDirs adds the list of directories with libraries defined by
// some interface slots to the ldconfig backend.
func addLdconfigLibDirs(spec *ldconfig.Specification, slot *interfaces.ConnectedSlot) error {
	libDirs := []string{}
	if err := slot.Attr("library-source", &libDirs); err != nil {
		return err
	}
	expandedDirs := make([]string, 0, len(libDirs))
	for _, dir := range libDirs {
		expandedDirs = append(expandedDirs, filepath.Clean(slot.Snap().ExpandSnapVariables(
			filepath.Join(dirs.GlobalRootDir, dir))))
	}
	return spec.AddLibDirs(expandedDirs)
}

// filePathInLibDirs returns the path of the first occurrence of fileName in the
// list of library directories of the slot.
func filePathInLibDirs(slot *interfaces.ConnectedSlot, fileName string) (string, error) {
	libDirs := []string{}
	if err := slot.Attr("library-source", &libDirs); err != nil {
		return "", err
	}
	for _, dir := range libDirs {
		path := filepath.Join(dirs.GlobalRootDir,
			slot.AppSet().Info().ExpandSnapVariables(dir), fileName)
		if osutil.FileExists(path) {
			return path, nil
		}
	}
	return "", fmt.Errorf("%q not found in the library-source directories", fileName)
}

// icdDirFilesCheck returns a list of file names found in the icd-source
// directory of the slot, after checking that the library_path in these files
// matches a file found in the directories specified by library-source.
func icdDirFilesCheck(slot *interfaces.ConnectedSlot) (checked []string, err error) {
	var icdDir string
	if err := slot.Attr("icd-source", &icdDir); err != nil {
		return nil, err
	}
	icdDir = filepath.Join(dirs.GlobalRootDir,
		slot.AppSet().Info().ExpandSnapVariables(icdDir))

	icdFiles, err := os.ReadDir(icdDir)
	if err != nil {
		return nil, err
	}

	for _, entry := range icdFiles {
		// Only regular files are considered - note that even symlinks
		// are ignored as we eventually will want to use apparmor to
		// allow access to these paths.
		if !entry.Type().IsRegular() {
			continue
		}
		// We are only interested in json files (same as libglvnd).
		if !strings.HasSuffix(entry.Name(), ".json") {
			continue
		}

		icdContent, err := os.ReadFile(filepath.Join(icdDir, entry.Name()))
		if err != nil {
			return nil, err
		}
		// We will check only library_path
		// TODO check api_version when this gets to be used by icd
		// files for vulkan or others that use this field.
		var icdJson struct {
			Icd struct {
				LibraryPath string `json:"library_path"`
			} `json:"ICD"`
		}
		err = json.Unmarshal(icdContent, &icdJson)
		if err != nil {
			return nil, fmt.Errorf("while unmarshalling %s: %w", entry.Name(), err)
		}
		// Here we are implicitly limiting library_path to be a file
		// name instead of a full path.
		_, err = filePathInLibDirs(slot, icdJson.Icd.LibraryPath)
		if err != nil {
			return nil, err
		}
		// Good enough
		checked = append(checked, filepath.Join(icdDir, entry.Name()))
	}

	return checked, nil
}