gitee.com/mysnapcore/mysnapd@v0.1.0/interfaces/mount/backend.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 // Package mount implements mounts that get mapped into the snap 21 // 22 // Snappy creates fstab like configuration files that describe what 23 // directories from the system or from other snaps should get mapped 24 // into the snap. 25 // 26 // Each fstab like file looks like a regular fstab entry: 27 // /src/dir /dst/dir none bind 0 0 28 // /src/dir /dst/dir none bind,rw 0 0 29 // but only bind mounts are supported 30 package mount 31 32 import ( 33 "bytes" 34 "fmt" 35 "os" 36 37 "gitee.com/mysnapcore/mysnapd/dirs" 38 "gitee.com/mysnapcore/mysnapd/interfaces" 39 "gitee.com/mysnapcore/mysnapd/osutil" 40 "gitee.com/mysnapcore/mysnapd/sandbox/cgroup" 41 "gitee.com/mysnapcore/mysnapd/snap" 42 "gitee.com/mysnapcore/mysnapd/timings" 43 ) 44 45 // Backend is responsible for maintaining mount files for snap-confine 46 type Backend struct{} 47 48 // Initialize does nothing. 49 func (b *Backend) Initialize(*interfaces.SecurityBackendOptions) error { 50 return nil 51 } 52 53 // Name returns the name of the backend. 54 func (b *Backend) Name() interfaces.SecuritySystem { 55 return interfaces.SecurityMount 56 } 57 58 // Setup creates mount mount profile files specific to a given snap. 59 func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error { 60 // Record all changes to the mount system for this snap. 61 snapName := snapInfo.InstanceName() 62 spec, err := repo.SnapSpecification(b.Name(), snapName) 63 if err != nil { 64 return fmt.Errorf("cannot obtain mount security snippets for snap %q: %s", snapName, err) 65 } 66 spec.(*Specification).AddOvername(snapInfo) 67 spec.(*Specification).AddLayout(snapInfo) 68 spec.(*Specification).AddExtraLayouts(confinement.ExtraLayouts) 69 content := deriveContent(spec.(*Specification), snapInfo) 70 // synchronize the content with the filesystem 71 glob := fmt.Sprintf("snap.%s.*fstab", snapName) 72 dir := dirs.SnapMountPolicyDir 73 if err := os.MkdirAll(dir, 0755); err != nil { 74 return fmt.Errorf("cannot create directory for mount configuration files %q: %s", dir, err) 75 } 76 if _, _, err := osutil.EnsureDirState(dir, glob, content); err != nil { 77 return fmt.Errorf("cannot synchronize mount configuration files for snap %q: %s", snapName, err) 78 } 79 if err := UpdateSnapNamespace(snapName); err != nil { 80 return fmt.Errorf("cannot update mount namespace of snap %q: %s", snapName, err) 81 } 82 return nil 83 } 84 85 // Remove removes mount configuration files of a given snap. 86 // 87 // This method should be called after removing a snap. 88 func (b *Backend) Remove(snapName string) error { 89 glob := fmt.Sprintf("snap.%s.*fstab", snapName) 90 _, _, err := osutil.EnsureDirState(dirs.SnapMountPolicyDir, glob, nil) 91 if err != nil { 92 return fmt.Errorf("cannot synchronize mount configuration files for snap %q: %s", snapName, err) 93 } 94 return DiscardSnapNamespace(snapName) 95 } 96 97 // addMountProfile adds a mount profile with the given name, based on the given entries. 98 // 99 // If there are no entries no profile is generated. 100 func addMountProfile(content map[string]osutil.FileState, fname string, entries []osutil.MountEntry) { 101 if len(entries) == 0 { 102 return 103 } 104 var buffer bytes.Buffer 105 for _, entry := range entries { 106 fmt.Fprintf(&buffer, "%s\n", entry) 107 } 108 content[fname] = &osutil.MemoryFileState{Content: buffer.Bytes(), Mode: 0644} 109 } 110 111 // deriveContent computes .fstab tables based on requests made to the specification. 112 func deriveContent(spec *Specification, snapInfo *snap.Info) map[string]osutil.FileState { 113 content := make(map[string]osutil.FileState, 2) 114 snapName := snapInfo.InstanceName() 115 // Add the per-snap fstab file. 116 // This file is read by snap-update-ns in the global pass. 117 addMountProfile(content, fmt.Sprintf("snap.%s.fstab", snapName), spec.MountEntries()) 118 // Add the per-snap user-fstab file. 119 // This file will be read by snap-update-ns in the per-user pass. 120 addMountProfile(content, fmt.Sprintf("snap.%s.user-fstab", snapName), spec.UserMountEntries()) 121 return content 122 } 123 124 // NewSpecification returns a new mount specification. 125 func (b *Backend) NewSpecification() interfaces.Specification { 126 return &Specification{} 127 } 128 129 // SandboxFeatures returns the list of features supported by snapd for composing mount namespaces. 130 func (b *Backend) SandboxFeatures() []string { 131 commonFeatures := []string{ 132 "layouts", /* Mount profiles take layout data into account */ 133 "mount-namespace", /* Snapd creates a mount namespace for each snap */ 134 "per-snap-persistency", /* Per-snap profiles are persisted across invocations */ 135 "per-snap-profiles", /* Per-snap profiles allow changing mount namespace of a given snap */ 136 "per-snap-updates", /* Changes to per-snap mount profiles are applied instantly */ 137 "per-snap-user-profiles", /* Per-snap profiles allow changing mount namespace of a given snap for a given user */ 138 "stale-base-invalidation", /* Mount namespaces that go stale because base snap changes are automatically invalidated */ 139 } 140 cgroupv1Features := []string{ 141 "freezer-cgroup-v1", /* Snapd creates a freezer cgroup (v1) for each snap */ 142 } 143 144 if cgroup.IsUnified() { 145 // TODO: update we get feature parity on cgroup v2 146 return commonFeatures 147 } 148 149 features := append(commonFeatures, cgroupv1Features...) 150 return features 151 }