github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/dirs/dirs.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 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 dirs 21 22 import ( 23 "fmt" 24 "os" 25 "path/filepath" 26 "strconv" 27 "strings" 28 29 "github.com/snapcore/snapd/release" 30 ) 31 32 // the various file paths 33 var ( 34 GlobalRootDir string 35 36 SnapMountDir string 37 38 DistroLibExecDir string 39 40 SnapBlobDir string 41 SnapDataDir string 42 SnapDataHomeGlob string 43 SnapDownloadCacheDir string 44 SnapAppArmorDir string 45 SnapAppArmorAdditionalDir string 46 SnapConfineAppArmorDir string 47 SnapSeccompBase string 48 SnapSeccompDir string 49 SnapMountPolicyDir string 50 SnapUdevRulesDir string 51 SnapKModModulesDir string 52 LocaleDir string 53 SnapMetaDir string 54 SnapdSocket string 55 SnapSocket string 56 SnapRunDir string 57 SnapRunNsDir string 58 SnapRunLockDir string 59 SnapBootstrapRunDir string 60 61 SnapdMaintenanceFile string 62 63 SnapdStoreSSLCertsDir string 64 65 SnapSeedDir string 66 SnapDeviceDir string 67 68 SnapAssertsDBDir string 69 SnapCookieDir string 70 SnapTrustedAccountKey string 71 SnapAssertsSpoolDir string 72 SnapSeqDir string 73 74 SnapStateFile string 75 SnapSystemKeyFile string 76 77 SnapRepairDir string 78 SnapRepairStateFile string 79 SnapRepairRunDir string 80 SnapRepairAssertsDir string 81 SnapRunRepairDir string 82 83 SnapRollbackDir string 84 85 SnapCacheDir string 86 SnapNamesFile string 87 SnapSectionsFile string 88 SnapCommandsDB string 89 SnapAuxStoreInfoDir string 90 91 SnapBinariesDir string 92 SnapServicesDir string 93 SnapUserServicesDir string 94 SnapSystemdConfDir string 95 SnapDesktopFilesDir string 96 SnapDesktopIconsDir string 97 98 SnapDBusSessionPolicyDir string 99 SnapDBusSystemPolicyDir string 100 SnapDBusSessionServicesDir string 101 SnapDBusSystemServicesDir string 102 103 SnapModeenvFile string 104 SnapBootAssetsDir string 105 SnapFDEDir string 106 SnapSaveDir string 107 SnapDeviceSaveDir string 108 109 CloudMetaDataFile string 110 CloudInstanceDataFile string 111 112 ClassicDir string 113 114 XdgRuntimeDirBase string 115 XdgRuntimeDirGlob string 116 117 CompletionHelperInCore string 118 CompletersDir string 119 120 SystemFontsDir string 121 SystemLocalFontsDir string 122 SystemFontconfigCacheDirs []string 123 124 SnapshotsDir string 125 126 ErrtrackerDbDir string 127 SysfsDir string 128 129 FeaturesDir string 130 ) 131 132 const ( 133 defaultSnapMountDir = "/snap" 134 135 // These are directories which are static inside the core snap and 136 // can never be prefixed as they will be always absolute once we 137 // are in the snap confinement environment. 138 CoreLibExecDir = "/usr/lib/snapd" 139 CoreSnapMountDir = "/snap" 140 141 // Directory with snap data inside user's home 142 UserHomeSnapDir = "snap" 143 144 // LocalInstallBlobTempPrefix is used by local install code: 145 // * in daemon to spool the snap file to <SnapBlobDir>/<LocalInstallBlobTempPrefix>* 146 // * in snapstate to auto-cleans them up using the same prefix 147 LocalInstallBlobTempPrefix = ".local-install-" 148 ) 149 150 var ( 151 // not exported because it does not honor the global rootdir 152 snappyDir = filepath.Join("var", "lib", "snapd") 153 154 callbacks = []func(string){} 155 ) 156 157 func init() { 158 // init the global directories at startup 159 root := os.Getenv("SNAPPY_GLOBAL_ROOT") 160 161 SetRootDir(root) 162 } 163 164 // StripRootDir strips the custom global root directory from the specified argument. 165 func StripRootDir(dir string) string { 166 if !filepath.IsAbs(dir) { 167 panic(fmt.Sprintf("supplied path is not absolute %q", dir)) 168 } 169 if !strings.HasPrefix(dir, GlobalRootDir) { 170 panic(fmt.Sprintf("supplied path is not related to global root %q", dir)) 171 } 172 result, err := filepath.Rel(GlobalRootDir, dir) 173 if err != nil { 174 panic(err) 175 } 176 return "/" + result 177 } 178 179 // SupportsClassicConfinement returns true if the current directory layout supports classic confinement. 180 func SupportsClassicConfinement() bool { 181 // Core systems don't support classic confinement as a policy decision. 182 if !release.OnClassic { 183 return false 184 } 185 186 // Classic systems support classic confinement if using the primary mount 187 // location for snaps, that is /snap or if using the alternate mount 188 // location, /var/lib/snapd/snap along with the /snap -> 189 // /var/lib/snapd/snap symlink in place. 190 smd := filepath.Join(GlobalRootDir, defaultSnapMountDir) 191 if SnapMountDir == smd { 192 return true 193 } 194 fi, err := os.Lstat(smd) 195 if err == nil && fi.Mode()&os.ModeSymlink != 0 { 196 if target, err := filepath.EvalSymlinks(smd); err == nil { 197 if target == SnapMountDir { 198 return true 199 } 200 } 201 } 202 203 return false 204 } 205 206 var metaSnapPath = "/meta/snap.yaml" 207 208 // isInsideBaseSnap returns true if the process is inside a base snap environment. 209 // 210 // The things that count as a base snap are: 211 // - any base snap mounted at / 212 // - any os snap mounted at / 213 func isInsideBaseSnap() (bool, error) { 214 _, err := os.Stat(metaSnapPath) 215 if err != nil && os.IsNotExist(err) { 216 return false, nil 217 } 218 return err == nil, err 219 } 220 221 // SnapdStateDir returns the path to /var/lib/snapd dir under rootdir. 222 func SnapdStateDir(rootdir string) string { 223 return filepath.Join(rootdir, snappyDir) 224 } 225 226 // SnapBlobDirUnder returns the path to the snap blob dir under rootdir. 227 func SnapBlobDirUnder(rootdir string) string { 228 return filepath.Join(rootdir, snappyDir, "snaps") 229 } 230 231 // SnapSeedDirUnder returns the path to the snap seed dir under rootdir. 232 func SnapSeedDirUnder(rootdir string) string { 233 return filepath.Join(rootdir, snappyDir, "seed") 234 } 235 236 // SnapStateFileUnder returns the path to snapd state file under rootdir. 237 func SnapStateFileUnder(rootdir string) string { 238 return filepath.Join(rootdir, snappyDir, "state.json") 239 } 240 241 // SnapModeenvFileUnder returns the path to the modeenv file under rootdir. 242 func SnapModeenvFileUnder(rootdir string) string { 243 return filepath.Join(rootdir, snappyDir, "modeenv") 244 } 245 246 // FeaturesDirUnder returns the path to the features dir under rootdir. 247 func FeaturesDirUnder(rootdir string) string { 248 return filepath.Join(rootdir, snappyDir, "features") 249 } 250 251 // SnapSystemdConfDirUnder returns the path to the systemd conf dir under 252 // rootdir. 253 func SnapSystemdConfDirUnder(rootdir string) string { 254 return filepath.Join(rootdir, "/etc/systemd/system.conf.d") 255 } 256 257 // SnapBootAssetsDirUnder returns the path to boot assets directory under a 258 // rootdir. 259 func SnapBootAssetsDirUnder(rootdir string) string { 260 return filepath.Join(rootdir, snappyDir, "boot-assets") 261 } 262 263 // SnapDeviceDirUnder returns the path to device directory under rootdir. 264 func SnapDeviceDirUnder(rootdir string) string { 265 return filepath.Join(rootdir, snappyDir, "device") 266 } 267 268 // SnapFDEDirUnder returns the path to full disk encryption state directory 269 // under rootdir. 270 func SnapFDEDirUnder(rootdir string) string { 271 return filepath.Join(SnapDeviceDirUnder(rootdir), "fde") 272 } 273 274 // SnapSaveDirUnder returns the path to device save directory under rootdir. 275 func SnapSaveDirUnder(rootdir string) string { 276 return filepath.Join(rootdir, snappyDir, "save") 277 } 278 279 // SnapFDEDirUnderSave returns the path to full disk encryption state directory 280 // inside the given save tree dir. 281 func SnapFDEDirUnderSave(savedir string) string { 282 return filepath.Join(savedir, "device/fde") 283 } 284 285 // AddRootDirCallback registers a callback for whenever the global root 286 // directory (set by SetRootDir) is changed to enable updates to variables in 287 // other packages that depend on its location. 288 func AddRootDirCallback(c func(string)) { 289 callbacks = append(callbacks, c) 290 } 291 292 // SetRootDir allows settings a new global root directory, this is useful 293 // for e.g. chroot operations 294 func SetRootDir(rootdir string) { 295 if rootdir == "" { 296 rootdir = "/" 297 } 298 GlobalRootDir = rootdir 299 300 altDirDistros := []string{ 301 "antergos", 302 "arch", 303 "archlinux", 304 "fedora", 305 "gentoo", 306 "manjaro", 307 "manjaro-arm", 308 } 309 310 isInsideBase, _ := isInsideBaseSnap() 311 if !isInsideBase && release.DistroLike(altDirDistros...) { 312 SnapMountDir = filepath.Join(rootdir, "/var/lib/snapd/snap") 313 } else { 314 SnapMountDir = filepath.Join(rootdir, defaultSnapMountDir) 315 } 316 317 SnapDataDir = filepath.Join(rootdir, "/var/snap") 318 SnapDataHomeGlob = filepath.Join(rootdir, "/home/*/", UserHomeSnapDir) 319 SnapAppArmorDir = filepath.Join(rootdir, snappyDir, "apparmor", "profiles") 320 SnapConfineAppArmorDir = filepath.Join(rootdir, snappyDir, "apparmor", "snap-confine") 321 SnapAppArmorAdditionalDir = filepath.Join(rootdir, snappyDir, "apparmor", "additional") 322 SnapDownloadCacheDir = filepath.Join(rootdir, snappyDir, "cache") 323 SnapSeccompBase = filepath.Join(rootdir, snappyDir, "seccomp") 324 SnapSeccompDir = filepath.Join(SnapSeccompBase, "bpf") 325 SnapMountPolicyDir = filepath.Join(rootdir, snappyDir, "mount") 326 SnapMetaDir = filepath.Join(rootdir, snappyDir, "meta") 327 SnapdMaintenanceFile = filepath.Join(rootdir, snappyDir, "maintenance.json") 328 SnapBlobDir = SnapBlobDirUnder(rootdir) 329 // ${snappyDir}/desktop is added to $XDG_DATA_DIRS. 330 // Subdirectories are interpreted according to the relevant 331 // freedesktop.org specifications 332 SnapDesktopFilesDir = filepath.Join(rootdir, snappyDir, "desktop", "applications") 333 SnapDesktopIconsDir = filepath.Join(rootdir, snappyDir, "desktop", "icons") 334 SnapRunDir = filepath.Join(rootdir, "/run/snapd") 335 SnapRunNsDir = filepath.Join(SnapRunDir, "/ns") 336 SnapRunLockDir = filepath.Join(SnapRunDir, "/lock") 337 338 SnapBootstrapRunDir = filepath.Join(SnapRunDir, "snap-bootstrap") 339 340 SnapdStoreSSLCertsDir = filepath.Join(rootdir, snappyDir, "ssl/store-certs") 341 342 // keep in sync with the debian/snapd.socket file: 343 SnapdSocket = filepath.Join(rootdir, "/run/snapd.socket") 344 SnapSocket = filepath.Join(rootdir, "/run/snapd-snap.socket") 345 346 SnapAssertsDBDir = filepath.Join(rootdir, snappyDir, "assertions") 347 SnapCookieDir = filepath.Join(rootdir, snappyDir, "cookie") 348 SnapAssertsSpoolDir = filepath.Join(rootdir, "run/snapd/auto-import") 349 SnapSeqDir = filepath.Join(rootdir, snappyDir, "sequence") 350 351 SnapStateFile = SnapStateFileUnder(rootdir) 352 SnapSystemKeyFile = filepath.Join(rootdir, snappyDir, "system-key") 353 354 SnapCacheDir = filepath.Join(rootdir, "/var/cache/snapd") 355 SnapNamesFile = filepath.Join(SnapCacheDir, "names") 356 SnapSectionsFile = filepath.Join(SnapCacheDir, "sections") 357 SnapCommandsDB = filepath.Join(SnapCacheDir, "commands.db") 358 SnapAuxStoreInfoDir = filepath.Join(SnapCacheDir, "aux") 359 360 SnapSeedDir = SnapSeedDirUnder(rootdir) 361 SnapDeviceDir = SnapDeviceDirUnder(rootdir) 362 363 SnapModeenvFile = SnapModeenvFileUnder(rootdir) 364 SnapBootAssetsDir = SnapBootAssetsDirUnder(rootdir) 365 SnapFDEDir = SnapFDEDirUnder(rootdir) 366 SnapSaveDir = SnapSaveDirUnder(rootdir) 367 SnapDeviceSaveDir = filepath.Join(SnapSaveDir, "device") 368 369 SnapRepairDir = filepath.Join(rootdir, snappyDir, "repair") 370 SnapRepairStateFile = filepath.Join(SnapRepairDir, "repair.json") 371 SnapRepairRunDir = filepath.Join(SnapRepairDir, "run") 372 SnapRepairAssertsDir = filepath.Join(SnapRepairDir, "assertions") 373 SnapRunRepairDir = filepath.Join(SnapRunDir, "repair") 374 375 SnapRollbackDir = filepath.Join(rootdir, snappyDir, "rollback") 376 377 SnapBinariesDir = filepath.Join(SnapMountDir, "bin") 378 SnapServicesDir = filepath.Join(rootdir, "/etc/systemd/system") 379 SnapUserServicesDir = filepath.Join(rootdir, "/etc/systemd/user") 380 SnapSystemdConfDir = SnapSystemdConfDirUnder(rootdir) 381 382 SnapDBusSystemPolicyDir = filepath.Join(rootdir, "/etc/dbus-1/system.d") 383 SnapDBusSessionPolicyDir = filepath.Join(rootdir, "/etc/dbus-1/session.d") 384 // Use 'dbus-1/services' and `dbus-1/system-services' to mirror 385 // '/usr/share/dbus-1' hierarchy. 386 SnapDBusSessionServicesDir = filepath.Join(rootdir, snappyDir, "dbus-1", "services") 387 SnapDBusSystemServicesDir = filepath.Join(rootdir, snappyDir, "dbus-1", "system-services") 388 389 CloudInstanceDataFile = filepath.Join(rootdir, "/run/cloud-init/instance-data.json") 390 391 SnapUdevRulesDir = filepath.Join(rootdir, "/etc/udev/rules.d") 392 393 SnapKModModulesDir = filepath.Join(rootdir, "/etc/modules-load.d/") 394 395 LocaleDir = filepath.Join(rootdir, "/usr/share/locale") 396 ClassicDir = filepath.Join(rootdir, "/writable/classic") 397 398 opensuseTWWithLibexec := func() bool { 399 // XXX: this is pretty naive if openSUSE ever starts going back 400 // and forth about the change 401 if !release.DistroLike("opensuse-tumbleweed") { 402 return false 403 } 404 v, err := strconv.Atoi(release.ReleaseInfo.VersionID) 405 if err != nil { 406 // nothing we can do here 407 return false 408 } 409 // first seen on snapshot "20200826" 410 if v < 20200826 { 411 return false 412 } 413 return true 414 } 415 416 if release.DistroLike("fedora") || opensuseTWWithLibexec() { 417 // RHEL, CentOS, Fedora and derivatives, some more recent 418 // snapshots of openSUSE Tumbleweed; 419 // both RHEL and CentOS list "fedora" in ID_LIKE 420 DistroLibExecDir = filepath.Join(rootdir, "/usr/libexec/snapd") 421 } else { 422 DistroLibExecDir = filepath.Join(rootdir, "/usr/lib/snapd") 423 } 424 425 XdgRuntimeDirBase = filepath.Join(rootdir, "/run/user") 426 XdgRuntimeDirGlob = filepath.Join(XdgRuntimeDirBase, "*/") 427 428 CompletionHelperInCore = filepath.Join(CoreLibExecDir, "etelpmoc.sh") 429 CompletersDir = filepath.Join(rootdir, "/usr/share/bash-completion/completions/") 430 431 // These paths agree across all supported distros 432 SystemFontsDir = filepath.Join(rootdir, "/usr/share/fonts") 433 SystemLocalFontsDir = filepath.Join(rootdir, "/usr/local/share/fonts") 434 // The cache path is true for Ubuntu, Debian, openSUSE, Arch 435 SystemFontconfigCacheDirs = []string{filepath.Join(rootdir, "/var/cache/fontconfig")} 436 if release.DistroLike("fedora") && !release.DistroLike("amzn") { 437 // Applies to Fedora and CentOS, Amazon Linux 2 is behind with 438 // updates to fontconfig and uses /var/cache/fontconfig instead, 439 // see: 440 // https://fedoraproject.org/wiki/Changes/FontconfigCacheDirChange 441 // https://bugzilla.redhat.com/show_bug.cgi?id=1416380 442 // https://bugzilla.redhat.com/show_bug.cgi?id=1377367 443 // 444 // However, snaps may still use older libfontconfig, which fails 445 // to parse the new config and defaults to 446 // /var/cache/fontconfig. In this case we need to make both 447 // locations available 448 SystemFontconfigCacheDirs = append(SystemFontconfigCacheDirs, filepath.Join(rootdir, "/usr/lib/fontconfig/cache")) 449 } 450 451 SnapshotsDir = filepath.Join(rootdir, snappyDir, "snapshots") 452 453 ErrtrackerDbDir = filepath.Join(rootdir, snappyDir, "errtracker.db") 454 SysfsDir = filepath.Join(rootdir, "/sys") 455 456 FeaturesDir = FeaturesDirUnder(rootdir) 457 458 // call the callbacks last so that the callbacks can just reference the 459 // global vars if they want, instead of using the new rootdir directly 460 for _, c := range callbacks { 461 c(rootdir) 462 } 463 } 464 465 // what inside a (non-classic) snap is /usr/lib/snapd, outside can come from different places 466 func libExecOutside(base string) string { 467 if base == "" { 468 // no explicit base; core is it 469 return filepath.Join(SnapMountDir, "core/current/usr/lib/snapd") 470 } 471 // if a base is set, libexec comes from the snapd snap if it's 472 // installed, and otherwise from the distro. 473 p := filepath.Join(SnapMountDir, "snapd/current/usr/lib/snapd") 474 if st, err := os.Stat(p); err == nil && st.IsDir() { 475 return p 476 } 477 return DistroLibExecDir 478 } 479 480 func CompleteShPath(base string) string { 481 return filepath.Join(libExecOutside(base), "complete.sh") 482 } 483 484 func IsCompleteShSymlink(compPath string) bool { 485 target, err := os.Readlink(compPath) 486 // check if the target paths ends with "/snapd/complete.sh" 487 return err == nil && filepath.Base(filepath.Dir(target)) == "snapd" && filepath.Base(target) == "complete.sh" 488 }