github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/interfaces/mount/spec.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 21 22 import ( 23 "fmt" 24 "path" 25 "sort" 26 27 "github.com/snapcore/snapd/dirs" 28 "github.com/snapcore/snapd/interfaces" 29 "github.com/snapcore/snapd/logger" 30 "github.com/snapcore/snapd/osutil" 31 "github.com/snapcore/snapd/snap" 32 ) 33 34 // Specification assists in collecting mount entries associated with an interface. 35 // 36 // Unlike the Backend itself (which is stateless and non-persistent) this type 37 // holds internal state that is used by the mount backend during the interface 38 // setup process. 39 type Specification struct { 40 // The mount profile is internally re-sorted by snap-update-ns based on 41 // the source of given mount entry and MountEntry.Dir. See 42 // cmd/snap-update-ns/sorting.go for details. 43 44 layout []osutil.MountEntry 45 general []osutil.MountEntry 46 user []osutil.MountEntry 47 overname []osutil.MountEntry 48 } 49 50 // AddMountEntry adds a new mount entry. 51 func (spec *Specification) AddMountEntry(e osutil.MountEntry) error { 52 spec.general = append(spec.general, e) 53 return nil 54 } 55 56 //AddUserMountEntry adds a new user mount entry. 57 func (spec *Specification) AddUserMountEntry(e osutil.MountEntry) error { 58 spec.user = append(spec.user, e) 59 return nil 60 } 61 62 // AddOvernameMountEntry adds a new overname mount entry. 63 func (spec *Specification) AddOvernameMountEntry(e osutil.MountEntry) error { 64 spec.overname = append(spec.overname, e) 65 return nil 66 } 67 68 func mountEntryFromLayout(layout *snap.Layout) osutil.MountEntry { 69 var entry osutil.MountEntry 70 71 mountPoint := layout.Snap.ExpandSnapVariables(layout.Path) 72 entry.Dir = mountPoint 73 74 // XXX: what about ro mounts? 75 if layout.Bind != "" { 76 mountSource := layout.Snap.ExpandSnapVariables(layout.Bind) 77 entry.Options = []string{"rbind", "rw"} 78 entry.Name = mountSource 79 } 80 if layout.BindFile != "" { 81 mountSource := layout.Snap.ExpandSnapVariables(layout.BindFile) 82 entry.Options = []string{"bind", "rw", osutil.XSnapdKindFile()} 83 entry.Name = mountSource 84 } 85 86 if layout.Type == "tmpfs" { 87 entry.Type = "tmpfs" 88 entry.Name = "tmpfs" 89 } 90 91 if layout.Symlink != "" { 92 oldname := layout.Snap.ExpandSnapVariables(layout.Symlink) 93 entry.Options = []string{osutil.XSnapdKindSymlink(), osutil.XSnapdSymlink(oldname)} 94 } 95 96 var uid uint32 97 // Only root is allowed here until we support custom users. Root is default. 98 switch layout.User { 99 case "root", "": 100 uid = 0 101 } 102 if uid != 0 { 103 entry.Options = append(entry.Options, osutil.XSnapdUser(uid)) 104 } 105 106 var gid uint32 107 // Only root is allowed here until we support custom groups. Root is default. 108 // This is validated in spec.go. 109 switch layout.Group { 110 case "root", "": 111 gid = 0 112 } 113 if gid != 0 { 114 entry.Options = append(entry.Options, osutil.XSnapdGroup(gid)) 115 } 116 117 if layout.Mode != 0755 { 118 entry.Options = append(entry.Options, osutil.XSnapdMode(uint32(layout.Mode))) 119 } 120 121 // Indicate that this is a layout mount entry. 122 entry.Options = append(entry.Options, osutil.XSnapdOriginLayout()) 123 return entry 124 } 125 126 // AddLayout adds mount entries based on the layout of the snap. 127 func (spec *Specification) AddLayout(si *snap.Info) { 128 // TODO: handle layouts in base snaps as well as in this snap. 129 130 // walk the layout elements in deterministic order, by mount point name 131 paths := make([]string, 0, len(si.Layout)) 132 for path := range si.Layout { 133 paths = append(paths, path) 134 } 135 sort.Strings(paths) 136 137 for _, path := range paths { 138 entry := mountEntryFromLayout(si.Layout[path]) 139 spec.layout = append(spec.layout, entry) 140 } 141 } 142 143 // MountEntries returns a copy of the added mount entries. 144 func (spec *Specification) MountEntries() []osutil.MountEntry { 145 result := make([]osutil.MountEntry, 0, len(spec.overname)+len(spec.layout)+len(spec.general)) 146 // overname is the mappings that were added to support parallel 147 // installation of snaps and must come first, as they establish the base 148 // namespace for any further operations 149 result = append(result, spec.overname...) 150 result = append(result, spec.layout...) 151 result = append(result, spec.general...) 152 unclashMountEntries(result) 153 return result 154 } 155 156 // UserMountEntries returns a copy of the added user mount entries. 157 func (spec *Specification) UserMountEntries() []osutil.MountEntry { 158 result := make([]osutil.MountEntry, len(spec.user)) 159 copy(result, spec.user) 160 unclashMountEntries(result) 161 return result 162 } 163 164 // unclashMountEntries renames mount points if they clash with other entries. 165 // 166 // Subsequent entries get suffixed with -2, -3, etc. 167 // The initial entry is unaltered (and does not become -1). 168 func unclashMountEntries(entries []osutil.MountEntry) { 169 count := make(map[string]int, len(entries)) 170 for i := range entries { 171 path := entries[i].Dir 172 count[path]++ 173 if c := count[path]; c > 1 { 174 newDir := fmt.Sprintf("%s-%d", entries[i].Dir, c) 175 logger.Noticef("renaming mount entry for directory %q to %q to avoid a clash", entries[i].Dir, newDir) 176 entries[i].Dir = newDir 177 } 178 } 179 } 180 181 // Implementation of methods required by interfaces.Specification 182 183 // AddConnectedPlug records mount-specific side-effects of having a connected plug. 184 func (spec *Specification) AddConnectedPlug(iface interfaces.Interface, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 185 type definer interface { 186 MountConnectedPlug(spec *Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error 187 } 188 if iface, ok := iface.(definer); ok { 189 return iface.MountConnectedPlug(spec, plug, slot) 190 } 191 return nil 192 } 193 194 // AddConnectedSlot records mount-specific side-effects of having a connected slot. 195 func (spec *Specification) AddConnectedSlot(iface interfaces.Interface, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 196 type definer interface { 197 MountConnectedSlot(spec *Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error 198 } 199 if iface, ok := iface.(definer); ok { 200 return iface.MountConnectedSlot(spec, plug, slot) 201 } 202 return nil 203 } 204 205 // AddPermanentPlug records mount-specific side-effects of having a plug. 206 func (spec *Specification) AddPermanentPlug(iface interfaces.Interface, plug *snap.PlugInfo) error { 207 type definer interface { 208 MountPermanentPlug(spec *Specification, plug *snap.PlugInfo) error 209 } 210 if iface, ok := iface.(definer); ok { 211 return iface.MountPermanentPlug(spec, plug) 212 } 213 return nil 214 } 215 216 // AddPermanentSlot records mount-specific side-effects of having a slot. 217 func (spec *Specification) AddPermanentSlot(iface interfaces.Interface, slot *snap.SlotInfo) error { 218 type definer interface { 219 MountPermanentSlot(spec *Specification, slot *snap.SlotInfo) error 220 } 221 if iface, ok := iface.(definer); ok { 222 return iface.MountPermanentSlot(spec, slot) 223 } 224 return nil 225 } 226 227 // AddOvername records mappings of snap directories. 228 // 229 // When the snap is installed with an instance key, set up its mount namespace 230 // such that it appears as a non-instance key snap. This ensures compatibility 231 // with code making assumptions about $SNAP{,_DATA,_COMMON} locations. That is, 232 // given a snap foo_bar, the mappings added are: 233 // 234 // - /snap/foo_bar -> /snap/foo 235 // - /var/snap/foo_bar -> /var/snap/foo 236 func (spec *Specification) AddOvername(info *snap.Info) { 237 if info.InstanceKey == "" { 238 return 239 } 240 241 // /snap/foo_bar -> /snap/foo 242 spec.AddOvernameMountEntry(osutil.MountEntry{ 243 Name: path.Join(dirs.CoreSnapMountDir, info.InstanceName()), 244 Dir: path.Join(dirs.CoreSnapMountDir, info.SnapName()), 245 Options: []string{"rbind", osutil.XSnapdOriginOvername()}, 246 }) 247 // /var/snap/foo_bar -> /var/snap/foo 248 spec.AddOvernameMountEntry(osutil.MountEntry{ 249 Name: path.Join(dirs.SnapDataDir, info.InstanceName()), 250 Dir: path.Join(dirs.SnapDataDir, info.SnapName()), 251 Options: []string{"rbind", osutil.XSnapdOriginOvername()}, 252 }) 253 }