File: backend.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 (156 lines) | stat: -rw-r--r-- 5,299 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// -*- 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/>.
 *
 */

// configfiles is a backend that ensures that configuration files required by
// interfaces are present in the system. Currently it works only on classic and
// modifies the classic rootfs.
package configfiles

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/snapcore/snapd/interfaces"
	"github.com/snapcore/snapd/osutil"
	"github.com/snapcore/snapd/timings"
)

// Backend is responsible for maintaining configfiles cache.
type Backend struct{}

var _ = interfaces.SecurityBackend(&Backend{})

// Initialize does nothing for this backend.
func (b *Backend) Initialize(opts *interfaces.SecurityBackendOptions) error {
	return nil
}

// Name returns the name of the backend.
func (b *Backend) Name() interfaces.SecuritySystem {
	return "configfiles"
}

// Setup will make the configfiles backend generate the specified
// configuration files.
//
// If the method fails it should be re-tried (with a sensible strategy) by the caller.
func (b *Backend) Setup(appSet *interfaces.SnapAppSet, opts interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error {
	cfgPatterns := []string{}
	for _, iface := range repo.AllInterfaces() {
		if cfgIface, ok := iface.(interfaces.ConfigfilesUser); ok {
			cfgPatterns = append(cfgPatterns, cfgIface.PathPatterns()...)
		}
	}
	snapName := appSet.InstanceName()
	// Get the snippets that apply to this snap
	spec, err := repo.SnapSpecification(b.Name(), appSet, opts)
	if err != nil {
		return fmt.Errorf("cannot obtain configfiles specification for snap %q: %s",
			snapName, err)
	}

	return b.ensureConfigfiles(spec.(*Specification), cfgPatterns)
}

// Remove removes modules configfiles files specific to a given snap.
// This method should be called after removing a snap.
//
// If the method fails it should be re-tried (with a sensible strategy) by the caller.
func (b *Backend) Remove(snapName string) error {
	// If called for the system (snapd) snap, that is possible only in a
	// classic scenario when all other snaps in the system must have been
	// removed already to allow the removal of the snapd snap. In that
	// case, the config files will have already been removed by a Setup
	// call, so we do not need to do anything here.

	// TODO but this needs to be revisited for when we start supporting
	// configfiles plugs in snaps.
	return nil
}

// NewSpecification returns a new specification associated with this backend.
func (b *Backend) NewSpecification(*interfaces.SnapAppSet,
	interfaces.ConfinementOptions) interfaces.Specification {
	return &Specification{}
}

// SandboxFeatures returns the list of features supported by snapd for configfiles.
func (b *Backend) SandboxFeatures() []string {
	return []string{"mediated-configfiles"}
}

func (b *Backend) ensureConfigfiles(spec *Specification, cfgPatterns []string) error {
	// Setup configfiles only if the snap has plugs that require it. For the
	// moment this is only the system snap.
	if len(spec.plugs) == 0 {
		return nil
	}

	// Configuration files are created only if the files in the spec match
	// the patterns registered by interfaces.
	writtenPaths := make(map[string]bool, len(spec.pathContent))
	for path := range spec.pathContent {
		writtenPaths[path] = false
	}
	// TODO supported patterns apply currently only to a classic rootfs,
	// not to the rootfs of a snap. For the latter, the paths will be
	// relative to a directory in /var/lib/snapd/configfiles/<snap_name>/.
	// Files in there would be bind mounted so they can be seen by the
	// snap.
	for _, pattern := range cfgPatterns {
		matched := map[string]osutil.FileState{}
		for path, fileState := range spec.pathContent {
			match, err := filepath.Match(pattern, path)
			if err != nil {
				return fmt.Errorf("internal error in configfiles: %w", err)
			}
			if !match {
				continue
			}
			matched[filepath.Base(path)] = fileState
			writtenPaths[path] = true
		}
		targetDir := filepath.Dir(pattern)
		if len(matched) > 0 {
			if err := os.MkdirAll(targetDir, 0755); err != nil {
				return fmt.Errorf("cannot create directory %q: %v", targetDir, err)
			}
		}
		// Note that this still needs to run if there are no matches, to remove files
		if _, _, err := osutil.EnsureDirState(targetDir,
			filepath.Base(pattern), matched); err != nil {
			return fmt.Errorf("cannot ensure state for %s files: %w", pattern, err)
		}
	}

	notMatched := []string{}
	for path, written := range writtenPaths {
		if written {
			continue
		}
		notMatched = append(notMatched, path)
	}
	if len(notMatched) > 0 {
		return fmt.Errorf("internal error: %v not in any registered configfiles pattern",
			notMatched)
	}

	return nil
}