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
}
|