From a28208717231e09cb75f45277d275ac402055ac7 Mon Sep 17 00:00:00 2001 From: ghadi-rahme <117726944+ghadi-rahme@users.noreply.github.com> Date: Thu, 22 Feb 2024 12:08:42 +0200 Subject: [PATCH] many: apply new home directory rules to data copy (#13145) * Apply new home directory rules to data copy * Cleanups and adding caching of home directories * optimizing home directory caching * Adding tests for new home directory behavior * improvements and enhancements to Get/SetSnapHomeDirs and tests * snapdata_test and copydata_test fixes * Adding tests/main/user-data-handling-homedirs/task.yaml It also performs the verification for the root user as well as the test user present in /home * Adding Mock functions for snapDataDirs and snapCommonDataDirs and creating unit tests * Adding data copy between revisions test to snapd-homedirs spread test This commit also deletes the test-snapd-sh snap files and the user-data-handling-homedirs spread test * Change return behavior of GetSnapHomeDirs to be more in line with other functions and TestDataHomeDirs rename and fixes * Adding unit tests and renaming GetSnapHomeDirs * Adding unit tests and doing some cleanups * go.sum cleanups * cleanups, globbing expressions and root directory improvements * more s.tempdir cleanups in unit tests, removing debug remnants * unit test fixups, dirs.go fixups, typo fixups * SetSnapHomeDirs optimizations, fixups and unit test improvements * make globs only accessible via DataHomeGlobs * fixups and conflict resolution * comment fixup * dirs.go fixups and improvements * tests/main/snapd-homedirs: revision improvements and spread fix * tests/main/snapd-homedirs: adding details section to fix static-checks * tests/main/snapd-homedirs-vendored: fixing AppArmor tunable check * tests/main/snapd-homedirs: reverting rev read to using snap list This fixes issues with FHS compliant distros such as arch linux where the /snap directory is located in /var/lib/snapd/snap * fixing comments, small dirs.go and homedirs.go improvements * dirs/dirs.go: SeSnapHomeDirs comment fixup --- dirs/dirs.go | 93 +++++++++- dirs/dirs_test.go | 72 ++++++++ .../configstate/configcore/export_test.go | 1 + overlord/configstate/configcore/homedirs.go | 6 +- .../configstate/configcore/homedirs_test.go | 14 +- overlord/configstate/configmgr.go | 12 ++ overlord/configstate/configstate_test.go | 16 ++ overlord/snapstate/backend/copydata_test.go | 161 +++++++++++++----- overlord/snapstate/backend/export_test.go | 8 +- overlord/snapstate/backend/snapdata.go | 23 ++- overlord/snapstate/backend/snapdata_test.go | 87 +++++++++- snap/helpers.go | 12 +- snap/info.go | 42 +++-- snap/info_test.go | 26 ++- tests/main/snapd-homedirs-vendored/task.yaml | 2 +- tests/main/snapd-homedirs/task.yaml | 51 +++++- .../main/snapd-homedirs/test-snapd-sh/bin/sh | 2 - .../test-snapd-sh/meta/snap.yaml | 7 - wrappers/services_test.go | 126 +++++++------- 19 files changed, 583 insertions(+), 178 deletions(-) delete mode 100755 tests/main/snapd-homedirs/test-snapd-sh/bin/sh delete mode 100644 tests/main/snapd-homedirs/test-snapd-sh/meta/snap.yaml diff --git a/dirs/dirs.go b/dirs/dirs.go index d662937840f..d2a351483a8 100644 --- a/dirs/dirs.go +++ b/dirs/dirs.go @@ -25,13 +25,15 @@ import ( "path/filepath" "strconv" "strings" + "sync" "github.com/snapcore/snapd/release" + "github.com/snapcore/snapd/strutil" ) // the various file paths var ( - GlobalRootDir string + GlobalRootDir string = "/" RunDir string @@ -39,11 +41,11 @@ var ( DistroLibExecDir string - HiddenSnapDataHomeGlob string + hiddenSnapDataHomeGlob []string SnapBlobDir string SnapDataDir string - SnapDataHomeGlob string + snapDataHomeGlob []string SnapDownloadCacheDir string SnapAppArmorDir string SnapSeccompBase string @@ -140,6 +142,13 @@ var ( FeaturesDir string ) +// User defined home directory variables +// Not exported, use SnapHomeDirs() and SetSnapHomeDirs() instead +var ( + snapHomeDirsMu sync.Mutex + snapHomeDirs []string +) + const ( defaultSnapMountDir = "/snap" @@ -190,6 +199,67 @@ func init() { SetRootDir(root) } +// SnapHomeDirs returns a slice of the currently configured home directories. +func SnapHomeDirs() []string { + snapHomeDirsMu.Lock() + defer snapHomeDirsMu.Unlock() + dirs := make([]string, len(snapHomeDirs)) + copy(dirs, snapHomeDirs) + // Should never be true since SetSnapHomeDirs is run on init and on SetRootDir calls. + // Useful for unit tests. + if len(dirs) == 0 { + return []string{filepath.Join(GlobalRootDir, "/home")} + } + return dirs +} + +// SetSnapHomeDirs sets SnapHomeDirs to the user defined values and appends /home if needed. +// homedirs must be a comma separated list of paths to home directories. +// If homedirs is empty, SnapHomeDirs will be a slice of length 1 containing "/home". +// Also generates the data directory globbing expressions for each user. +// Expected to be run by configstate.Init, returns a slice of home directories. +func SetSnapHomeDirs(homedirs string) []string { + snapHomeDirsMu.Lock() + defer snapHomeDirsMu.Unlock() + + //clear old values + snapHomeDirs = nil + snapDataHomeGlob = nil + hiddenSnapDataHomeGlob = nil + + // Do not set the root directory as home unless explicitly specified with "." + if homedirs != "" { + snapHomeDirs = strings.Split(homedirs, ",") + for i := range snapHomeDirs { + // clean the path + snapHomeDirs[i] = filepath.Clean(snapHomeDirs[i]) + globalRootDir := GlobalRootDir + // Avoid false positives with HasPrefix + if globalRootDir != "/" && !strings.HasSuffix(globalRootDir, "/") { + globalRootDir += "/" + } + if !strings.HasPrefix(snapHomeDirs[i], globalRootDir) { + snapHomeDirs[i] = filepath.Join(GlobalRootDir, snapHomeDirs[i]) + } + // Generate data directory globbing expressions for each user. + snapDataHomeGlob = append(snapDataHomeGlob, filepath.Join(snapHomeDirs[i], "*", UserHomeSnapDir)) + hiddenSnapDataHomeGlob = append(hiddenSnapDataHomeGlob, filepath.Join(snapHomeDirs[i], "*", HiddenSnapDataHomeDir)) + } + } + + // Make sure /home is part of the list. + hasHome := strutil.ListContains(snapHomeDirs, filepath.Join(GlobalRootDir, "/home")) + + // if not add it and create the glob expressions. + if !hasHome { + snapHomeDirs = append(snapHomeDirs, filepath.Join(GlobalRootDir, "/home")) + snapDataHomeGlob = append(snapDataHomeGlob, filepath.Join(GlobalRootDir, "/home", "*", UserHomeSnapDir)) + hiddenSnapDataHomeGlob = append(hiddenSnapDataHomeGlob, filepath.Join(GlobalRootDir, "/home", "*", HiddenSnapDataHomeDir)) + } + + return snapHomeDirs +} + // StripRootDir strips the custom global root directory from the specified argument. func StripRootDir(dir string) string { if !filepath.IsAbs(dir) { @@ -205,6 +275,17 @@ func StripRootDir(dir string) string { return "/" + result } +// DataHomeGlobs returns a slice of globbing expressions for the snap directories in use. +func DataHomeGlobs(opts *SnapDirOptions) []string { + snapHomeDirsMu.Lock() + defer snapHomeDirsMu.Unlock() + if opts != nil && opts.HiddenSnapDataDir { + return hiddenSnapDataHomeGlob + } + + return snapDataHomeGlob +} + // SupportsClassicConfinement returns true if the current directory layout supports classic confinement. func SupportsClassicConfinement() bool { // Core systems don't support classic confinement as a policy decision. @@ -366,8 +447,6 @@ func SetRootDir(rootdir string) { } SnapDataDir = filepath.Join(rootdir, "/var/snap") - SnapDataHomeGlob = filepath.Join(rootdir, "/home/*/", UserHomeSnapDir) - HiddenSnapDataHomeGlob = filepath.Join(rootdir, "/home/*/", HiddenSnapDataHomeDir) SnapAppArmorDir = filepath.Join(rootdir, snappyDir, "apparmor", "profiles") SnapDownloadCacheDir = filepath.Join(rootdir, snappyDir, "cache") SnapSeccompBase = filepath.Join(rootdir, snappyDir, "seccomp") @@ -516,11 +595,15 @@ func SetRootDir(rootdir string) { FeaturesDir = FeaturesDirUnder(rootdir) + // If the root directory changes we also need to reset snapHomeDirs. + SetSnapHomeDirs("/home") + // call the callbacks last so that the callbacks can just reference the // global vars if they want, instead of using the new rootdir directly for _, c := range callbacks { c(rootdir) } + } // what inside a (non-classic) snap is /usr/lib/snapd, outside can come from different places diff --git a/dirs/dirs_test.go b/dirs/dirs_test.go index b0cdf397f7c..39abbd017d6 100644 --- a/dirs/dirs_test.go +++ b/dirs/dirs_test.go @@ -37,6 +37,78 @@ var _ = Suite(&DirsTestSuite{}) type DirsTestSuite struct{} +func (s *DirsTestSuite) TestSnapHomeDirs(c *C) { + defer dirs.SetRootDir("") + hidden := dirs.SnapDirOptions{HiddenSnapDataDir: true} + // Expected ouptut should be ROOTDIR/home + c.Check(dirs.SnapHomeDirs(), DeepEquals, []string{filepath.Join(dirs.GlobalRootDir, "/home")}) + c.Check(dirs.DataHomeGlobs(nil), DeepEquals, []string{filepath.Join(dirs.GlobalRootDir, "/home", "*", "snap")}) + c.Check(dirs.DataHomeGlobs(&hidden), DeepEquals, []string{filepath.Join(dirs.GlobalRootDir, "/home", "*", ".snap", "data")}) + + // Expected output should remove all trailing '/' and add /home if it is not present. + getSnapHomeFromSet := dirs.SetSnapHomeDirs("/home/homeDir1,/home/homeDirs/homeDir1///,/home/homeDir2/,/home/homeTest/users/") + snapHomeDirs := []string{"/home/homeDir1", "/home/homeDirs/homeDir1", "/home/homeDir2", "/home/homeTest/users", "/home"} + dataGlob := []string{"/home/homeDir1/*/snap", "/home/homeDirs/homeDir1/*/snap", "/home/homeDir2/*/snap", "/home/homeTest/users/*/snap", + "/home/*/snap"} + hiddenDataGlob := []string{"/home/homeDir1/*/.snap/data", "/home/homeDirs/homeDir1/*/.snap/data", "/home/homeDir2/*/.snap/data", + "/home/homeTest/users/*/.snap/data", "/home/*/.snap/data"} + getSnapHomeDirs := dirs.SnapHomeDirs() + c.Check(getSnapHomeDirs, DeepEquals, snapHomeDirs) + c.Check(dirs.DataHomeGlobs(nil), DeepEquals, dataGlob) + c.Check(dirs.DataHomeGlobs(&hidden), DeepEquals, hiddenDataGlob) + // Useful for detecting if any output changes were made to dirs.SnapHomeDirs() and not reflected in dirs.SetSnapHomeDirs() + c.Check(getSnapHomeFromSet, DeepEquals, snapHomeDirs) + + // Expected output, should remove all trailing '/' and not add /home since it is already present. + getSnapHomeFromSet = dirs.SetSnapHomeDirs("/home/homeDir1,/home/homeDirs/homeDir1///,/home/,/home/homeDir2/,/home/homeTest/users/") + snapHomeDirs = []string{"/home/homeDir1", "/home/homeDirs/homeDir1", "/home", "/home/homeDir2", "/home/homeTest/users"} + dataGlob = []string{"/home/homeDir1/*/snap", "/home/homeDirs/homeDir1/*/snap", "/home/*/snap", "/home/homeDir2/*/snap", + "/home/homeTest/users/*/snap"} + hiddenDataGlob = []string{"/home/homeDir1/*/.snap/data", "/home/homeDirs/homeDir1/*/.snap/data", "/home/*/.snap/data", + "/home/homeDir2/*/.snap/data", "/home/homeTest/users/*/.snap/data"} + getSnapHomeDirs = dirs.SnapHomeDirs() + c.Check(getSnapHomeDirs, DeepEquals, snapHomeDirs) + c.Check(getSnapHomeFromSet, DeepEquals, snapHomeDirs) + c.Check(dirs.DataHomeGlobs(nil), DeepEquals, dataGlob) + c.Check(dirs.DataHomeGlobs(&hidden), DeepEquals, hiddenDataGlob) + + // Expected output should only return /ROOTDIR/home as a home directory + dirs.SetRootDir("/new/root") + getSnapHomeFromSet = dirs.SetSnapHomeDirs("") + snapHomeDirs = []string{"/new/root/home"} + dataGlob = []string{"/new/root/home/*/snap"} + hiddenDataGlob = []string{"/new/root/home/*/.snap/data"} + getSnapHomeDirs = dirs.SnapHomeDirs() + c.Check(getSnapHomeDirs, DeepEquals, snapHomeDirs) + c.Check(getSnapHomeFromSet, DeepEquals, snapHomeDirs) + c.Check(dirs.DataHomeGlobs(nil), DeepEquals, dataGlob) + c.Check(dirs.DataHomeGlobs(&hidden), DeepEquals, hiddenDataGlob) + + // Expected output should append /ROOTDIR to each entry except if already present + getSnapHomeFromSet = dirs.SetSnapHomeDirs("/home/homeDir1,/new/root/home/homeDirs/homeDir1///,/home/homeDir2/,/new/root/home/homeTest/users/") + snapHomeDirs = []string{"/new/root/home/homeDir1", "/new/root/home/homeDirs/homeDir1", "/new/root/home/homeDir2", "/new/root/home/homeTest/users", "/new/root/home"} + dataGlob = []string{"/new/root/home/homeDir1/*/snap", "/new/root/home/homeDirs/homeDir1/*/snap", "/new/root/home/homeDir2/*/snap", + "/new/root/home/homeTest/users/*/snap", "/new/root/home/*/snap"} + hiddenDataGlob = []string{"/new/root/home/homeDir1/*/.snap/data", "/new/root/home/homeDirs/homeDir1/*/.snap/data", "/new/root/home/homeDir2/*/.snap/data", + "/new/root/home/homeTest/users/*/.snap/data", "/new/root/home/*/.snap/data"} + getSnapHomeDirs = dirs.SnapHomeDirs() + c.Check(getSnapHomeDirs, DeepEquals, snapHomeDirs) + c.Check(getSnapHomeFromSet, DeepEquals, snapHomeDirs) + c.Check(dirs.DataHomeGlobs(nil), DeepEquals, dataGlob) + c.Check(dirs.DataHomeGlobs(&hidden), DeepEquals, hiddenDataGlob) + + // setting a Root directory should reset the home directory + dirs.SetRootDir("/new") + c.Check(dirs.SnapHomeDirs(), DeepEquals, []string{"/new/home"}) + c.Check(dirs.DataHomeGlobs(nil), DeepEquals, []string{"/new/home/*/snap"}) + c.Check(dirs.DataHomeGlobs(&hidden), DeepEquals, []string{"/new/home/*/.snap/data"}) + dirs.SetRootDir("/") + c.Check(dirs.SnapHomeDirs(), DeepEquals, []string{"/home"}) + c.Check(dirs.DataHomeGlobs(nil), DeepEquals, []string{"/home/*/snap"}) + c.Check(dirs.DataHomeGlobs(&hidden), DeepEquals, []string{"/home/*/.snap/data"}) + +} + func (s *DirsTestSuite) TestStripRootDir(c *C) { dirs.SetRootDir("/") // strip does nothing if the default (empty) root directory is used diff --git a/overlord/configstate/configcore/export_test.go b/overlord/configstate/configcore/export_test.go index 8fcbf320605..6641dad9044 100644 --- a/overlord/configstate/configcore/export_test.go +++ b/overlord/configstate/configcore/export_test.go @@ -33,6 +33,7 @@ var ( UpdateKeyValueStream = updateKeyValueStream AddFSOnlyHandler = addFSOnlyHandler FilesystemOnlyApply = filesystemOnlyApply + UpdateHomedirsConfig = updateHomedirsConfig ) type PlainCoreConfig = plainCoreConfig diff --git a/overlord/configstate/configcore/homedirs.go b/overlord/configstate/configcore/homedirs.go index 454d67da489..bef64c3285c 100644 --- a/overlord/configstate/configcore/homedirs.go +++ b/overlord/configstate/configcore/homedirs.go @@ -117,10 +117,8 @@ func updateHomedirsConfig(config string, opts *fsOnlyContext) error { return err } - var homedirs []string - if len(config) > 0 { - homedirs = strings.Split(config, ",") - } + // Update value in dirs + homedirs := dirs.SetSnapHomeDirs(config) return configureHomedirsInAppArmorAndReload(homedirs, opts) } diff --git a/overlord/configstate/configcore/homedirs_test.go b/overlord/configstate/configcore/homedirs_test.go index e1b1fa2a77a..8e6f87631af 100644 --- a/overlord/configstate/configcore/homedirs_test.go +++ b/overlord/configstate/configcore/homedirs_test.go @@ -142,7 +142,8 @@ func (s *homedirsSuite) TestConfigureApparmorTunableFailure(c *C) { }, }) c.Check(err, ErrorMatches, "tunable error") - c.Check(homedirs, DeepEquals, []string{"/home/existingDir/one", "/home/existingDir/two"}) + c.Check(homedirs, DeepEquals, []string{filepath.Join(dirs.GlobalRootDir, "/home/existingDir/one"), + filepath.Join(dirs.GlobalRootDir, "/home/existingDir/two"), filepath.Join(dirs.GlobalRootDir, "/home")}) } func (s *homedirsSuite) TestConfigureApparmorReloadFailure(c *C) { @@ -243,7 +244,8 @@ func (s *homedirsSuite) TestConfigureHomedirsHappy(c *C) { c.Check(string(contents), Equals, "homedirs=/home/existingDir\n") // Check that the AppArmor tunables have been written... - c.Check(tunableHomedirs, DeepEquals, []string{"/home/existingDir"}) + c.Check(tunableHomedirs, DeepEquals, []string{filepath.Join(dirs.GlobalRootDir, "/home/existingDir"), + filepath.Join(dirs.GlobalRootDir, "/home")}) // ...and that profiles have been reloaded c.Check(reloadProfilesCallCount, Equals, 1) // and finally that snap-confine snippets was called @@ -309,3 +311,11 @@ func (s *homedirsSuite) TestConfigureHomedirsNotOnCore(c *C) { c.Check(reloadProfilesCallCount, Equals, 0) c.Check(setupSnapConfineSnippetsCalls, Equals, 0) } + +func (s *homedirsSuite) TestupdateHomedirsConfig(c *C) { + config := "/home/homeDir1,/home/homeDirs/homeDir1///,/home/homeDir2/,/home/homeTest/users/" + expectedHomeDirs := []string{filepath.Join(dirs.GlobalRootDir, "/home/homeDir1"), filepath.Join(dirs.GlobalRootDir, "/home/homeDirs/homeDir1"), + filepath.Join(dirs.GlobalRootDir, "/home/homeDir2"), filepath.Join(dirs.GlobalRootDir, "/home/homeTest/users"), filepath.Join(dirs.GlobalRootDir, "/home")} + configcore.UpdateHomedirsConfig(config, nil) + c.Check(dirs.SnapHomeDirs(), DeepEquals, expectedHomeDirs) +} diff --git a/overlord/configstate/configmgr.go b/overlord/configstate/configmgr.go index bd2d81b7369..c53254b4d96 100644 --- a/overlord/configstate/configmgr.go +++ b/overlord/configstate/configmgr.go @@ -23,6 +23,8 @@ package configstate import ( "regexp" + "github.com/snapcore/snapd/dirs" + "github.com/snapcore/snapd/overlord/configstate/config" "github.com/snapcore/snapd/overlord/configstate/configcore" "github.com/snapcore/snapd/overlord/hookstate" "github.com/snapcore/snapd/overlord/snapstate" @@ -43,6 +45,16 @@ func MockConfigcoreRun(f func(sysconfig.Device, configcore.RunTransaction) error func Init(st *state.State, hookManager *hookstate.HookManager) error { delayedCrossMgrInit() + // Retrieve home directories + st.Lock() + defer st.Unlock() + tr := config.NewTransaction(st) + var homedirs string + if err := tr.GetMaybe("core", "homedirs", &homedirs); err != nil { + return err + } + dirs.SetSnapHomeDirs(homedirs) + // Default configuration is handled via the "default-configure" hook hookManager.Register(regexp.MustCompile("^default-configure$"), newDefaultConfigureHandler) diff --git a/overlord/configstate/configstate_test.go b/overlord/configstate/configstate_test.go index d6bbca5c474..2dfb0201f45 100644 --- a/overlord/configstate/configstate_test.go +++ b/overlord/configstate/configstate_test.go @@ -308,6 +308,22 @@ func (s *configcoreHijackSuite) SetUpTest(c *C) { } +func (s *configcoreHijackSuite) TestConfigMngrInitHomeDirs(c *C) { + s.o = overlord.Mock() + s.state = s.o.State() + hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner()) + c.Assert(err, IsNil) + s.state.Lock() + t := config.NewTransaction(s.state) + c.Assert(t.Set("core", "homedirs", "/home,/home/department,/users,/users/seniors"), IsNil) + t.Commit() + s.state.Unlock() + err = configstate.Init(s.state, hookMgr) + c.Assert(err, IsNil) + snapHomeDirs := []string{"/home", "/home/department", "/users", "/users/seniors"} + c.Check(dirs.SnapHomeDirs(), DeepEquals, snapHomeDirs) +} + type witnessManager struct { state *state.State committed bool diff --git a/overlord/snapstate/backend/copydata_test.go b/overlord/snapstate/backend/copydata_test.go index d237f9bc281..c142aad5e1c 100644 --- a/overlord/snapstate/backend/copydata_test.go +++ b/overlord/snapstate/backend/copydata_test.go @@ -28,6 +28,7 @@ import ( "path/filepath" "regexp" "strconv" + "strings" . "gopkg.in/check.v1" @@ -83,7 +84,8 @@ func (s *copydataSuite) TestCopyData(c *C) { } func (s *copydataSuite) testCopyData(c *C, snapDir string, opts *dirs.SnapDirOptions) { - homedir := filepath.Join(s.tempdir, "home", "user1", snapDir) + dirs.SetSnapHomeDirs("/home") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user1", snapDir) homeData := filepath.Join(homedir, "hello/10") err := os.MkdirAll(homeData, 0755) c.Assert(err, IsNil) @@ -128,9 +130,90 @@ func (s *copydataSuite) testCopyData(c *C, snapDir string, opts *dirs.SnapDirOpt c.Assert(newCanaryDataFile, testutil.FileEquals, canaryData) } +// same as TestCopyData but with multiple home directories +func (s *copydataSuite) TestCopyDataMulti(c *C) { + for _, t := range []struct { + snapDir string + opts *dirs.SnapDirOptions + }{ + {snapDir: dirs.UserHomeSnapDir, opts: nil}, + {snapDir: dirs.UserHomeSnapDir, opts: &dirs.SnapDirOptions{}}, + {snapDir: dirs.HiddenSnapDataHomeDir, opts: &dirs.SnapDirOptions{HiddenSnapDataDir: true}}} { + s.testCopyDataMulti(c, t.snapDir, t.opts) + c.Assert(os.RemoveAll(s.tempdir), IsNil) + s.tempdir = c.MkDir() + dirs.SetRootDir(s.tempdir) + } +} + +func (s *copydataSuite) testCopyDataMulti(c *C, snapDir string, opts *dirs.SnapDirOptions) { + homeDirs := []string{filepath.Join(dirs.GlobalRootDir, "home"), + filepath.Join(dirs.GlobalRootDir, "home", "company"), + filepath.Join(dirs.GlobalRootDir, "home", "department"), + filepath.Join(dirs.GlobalRootDir, "office")} + dirs.SetSnapHomeDirs(strings.Join(homeDirs, ",")) + + snapHomeDirs := []string{} + snapHomeDataDirs := []string{} + snapHomeCommonDirs := []string{} + + for _, v := range homeDirs { + snapHomeDir := filepath.Join(v, "user1", snapDir) + snapHomeData := filepath.Join(snapHomeDir, "hello/10") + err := os.MkdirAll(snapHomeData, 0755) + c.Assert(err, IsNil) + homeCommonData := filepath.Join(snapHomeDir, "hello/common") + err = os.MkdirAll(homeCommonData, 0755) + c.Assert(err, IsNil) + snapHomeDirs = append(snapHomeDirs, snapHomeDir) + snapHomeDataDirs = append(snapHomeDataDirs, snapHomeData) + snapHomeCommonDirs = append(snapHomeCommonDirs, homeCommonData) + } + + canaryData := []byte("ni ni ni") + + v1 := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) + // just creates data dirs in this case + err := s.be.CopySnapData(v1, nil, opts, progress.Null) + c.Assert(err, IsNil) + + canaryDataFile := filepath.Join(v1.DataDir(), "canary.txt") + err = ioutil.WriteFile(canaryDataFile, canaryData, 0644) + c.Assert(err, IsNil) + canaryDataFile = filepath.Join(v1.CommonDataDir(), "canary.common") + err = ioutil.WriteFile(canaryDataFile, canaryData, 0644) + c.Assert(err, IsNil) + + for i := range snapHomeDataDirs { + err = ioutil.WriteFile(filepath.Join(snapHomeDataDirs[i], "canary.home"), canaryData, 0644) + c.Assert(err, IsNil) + err = ioutil.WriteFile(filepath.Join(snapHomeCommonDirs[i], "canary.common_home"), canaryData, 0644) + c.Assert(err, IsNil) + } + + v2 := snaptest.MockSnap(c, helloYaml2, &snap.SideInfo{Revision: snap.R(20)}) + err = s.be.CopySnapData(v2, v1, opts, progress.Null) + c.Assert(err, IsNil) + + newCanaryDataFile := filepath.Join(dirs.SnapDataDir, "hello/20", "canary.txt") + c.Assert(newCanaryDataFile, testutil.FileEquals, canaryData) + + // ensure common data file is still there (even though it didn't get copied) + newCanaryDataFile = filepath.Join(dirs.SnapDataDir, "hello", "common", "canary.common") + c.Assert(newCanaryDataFile, testutil.FileEquals, canaryData) + + for _, v := range snapHomeDirs { + newCanaryDataFile = filepath.Join(v, "hello/20", "canary.home") + c.Assert(newCanaryDataFile, testutil.FileEquals, canaryData) + + // ensure home common data file is still there (even though it didn't get copied) + newCanaryDataFile = filepath.Join(v, "hello", "common", "canary.common_home") + c.Assert(newCanaryDataFile, testutil.FileEquals, canaryData) + } + +} + func (s *copydataSuite) TestCopyDataBails(c *C) { - oldSnapDataHomeGlob := dirs.SnapDataHomeGlob - defer func() { dirs.SnapDataHomeGlob = oldSnapDataHomeGlob }() v1 := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) c.Assert(s.be.CopySnapData(v1, nil, nil, progress.Null), IsNil) @@ -144,10 +227,6 @@ func (s *copydataSuite) TestCopyDataBails(c *C) { // ensure that even with no home dir there is no error and the // system data gets copied func (s *copydataSuite) TestCopyDataNoUserHomes(c *C) { - // this home dir path does not exist - oldSnapDataHomeGlob := dirs.SnapDataHomeGlob - defer func() { dirs.SnapDataHomeGlob = oldSnapDataHomeGlob }() - dirs.SnapDataHomeGlob = filepath.Join(s.tempdir, "no-such-home", "*", "snap") v1 := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) err := s.be.CopySnapData(v1, nil, nil, progress.Null) @@ -199,7 +278,8 @@ func (s copydataSuite) populateHomeData(c *C, user string, revision snap.Revisio } func (s copydataSuite) populateHomeDataWithSnapDir(c *C, user string, snapDir string, revision snap.Revision) (homedir string) { - homedir = filepath.Join(s.tempdir, "home", user, snapDir) + dirs.SetSnapHomeDirs("/home") + homedir = filepath.Join(dirs.GlobalRootDir, "home", user, snapDir) homeData := filepath.Join(homedir, "hello", revision.String()) err := os.MkdirAll(homeData, 0755) c.Assert(err, IsNil) @@ -256,10 +336,6 @@ func (s *copydataSuite) testCopyDataUndo(c *C, snapDir string, opts *dirs.SnapDi } func (s *copydataSuite) TestCopyDataDoUndoNoUserHomes(c *C) { - // this home dir path does not exist - oldSnapDataHomeGlob := dirs.SnapDataHomeGlob - defer func() { dirs.SnapDataHomeGlob = oldSnapDataHomeGlob }() - dirs.SnapDataHomeGlob = filepath.Join(s.tempdir, "no-such-home", "*", "snap") v1 := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) s.populateData(c, snap.R(10)) @@ -688,7 +764,7 @@ func (s *copydataSuite) TestHideSnapData(c *C) { info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) // mock user home - homedir := filepath.Join(s.tempdir, "home", "user") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homedir @@ -750,14 +826,14 @@ func (s *copydataSuite) TestHideSnapDataSkipNoData(c *C) { info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) // mock user home - homedir := filepath.Join(s.tempdir, "home", "user") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homedir // create user without snap dir (to be skipped) usrNoSnapDir := &user.User{ - HomeDir: filepath.Join(s.tempdir, "home", "other-user"), + HomeDir: filepath.Join(dirs.GlobalRootDir, "home", "other-user"), Name: "other-user", Uid: "1001", Gid: "1001", @@ -781,7 +857,7 @@ func (s *copydataSuite) TestHideSnapDataSkipNoData(c *C) { // only the user with snap data was migrated newSnapDir := filepath.Join(homedir, dirs.HiddenSnapDataHomeDir) - matches, err := filepath.Glob(dirs.HiddenSnapDataHomeGlob) + matches, err := filepath.Glob(dirs.DataHomeGlobs(&dirs.SnapDirOptions{HiddenSnapDataDir: true})[0]) c.Assert(err, IsNil) c.Assert(matches, HasLen, 1) c.Assert(matches[0], Equals, newSnapDir) @@ -791,7 +867,7 @@ func (s *copydataSuite) TestHideSnapDataOverwrite(c *C) { info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) // mock user home - homedir := filepath.Join(s.tempdir, "home", "user") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homedir @@ -834,7 +910,7 @@ func (s *copydataSuite) TestUndoHideSnapData(c *C) { info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) // mock user home dir - homedir := filepath.Join(s.tempdir, "home", "user") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homedir @@ -902,7 +978,7 @@ func (s *copydataSuite) TestUndoHideSnapData(c *C) { func (s *copydataSuite) TestUndoHideDoesntRemoveIfDirHasFiles(c *C) { info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) - homedir := filepath.Join(s.tempdir, "home", "user") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homedir @@ -953,7 +1029,8 @@ func (s *copydataSuite) TestUndoHideDoesntRemoveIfDirHasFiles(c *C) { } func (s *copydataSuite) TestCleanupAfterCopyAndMigration(c *C) { - homedir := filepath.Join(s.tempdir, "home", "user") + dirs.SetSnapHomeDirs("/home") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homedir @@ -987,20 +1064,20 @@ func (s *copydataSuite) TestCleanupAfterCopyAndMigration(c *C) { } func (s *copydataSuite) TestRemoveIfEmpty(c *C) { - file := filepath.Join(s.tempdir, "random") + file := filepath.Join(dirs.GlobalRootDir, "random") c.Assert(os.WriteFile(file, []byte("stuff"), 0664), IsNil) // dir contains a file, shouldn't do anything - c.Assert(backend.RemoveIfEmpty(s.tempdir), IsNil) - files, err := ioutil.ReadDir(s.tempdir) + c.Assert(backend.RemoveIfEmpty(dirs.GlobalRootDir), IsNil) + files, err := ioutil.ReadDir(dirs.GlobalRootDir) c.Assert(err, IsNil) c.Check(files, HasLen, 1) - c.Check(filepath.Join(s.tempdir, files[0].Name()), testutil.FileEquals, "stuff") + c.Check(filepath.Join(dirs.GlobalRootDir, files[0].Name()), testutil.FileEquals, "stuff") c.Assert(os.Remove(file), IsNil) // dir is empty, should be removed - c.Assert(backend.RemoveIfEmpty(s.tempdir), IsNil) + c.Assert(backend.RemoveIfEmpty(dirs.GlobalRootDir), IsNil) c.Assert(osutil.FileExists(file), Equals, false) } @@ -1024,7 +1101,7 @@ func (s *copydataSuite) TestUndoHideKeepGoingPreserveFirstErr(c *C) { // mock two users so that the undo is done twice var usrs []*user.User for _, usrName := range []string{"usr1", "usr2"} { - homedir := filepath.Join(s.tempdir, "home", usrName) + homedir := filepath.Join(dirs.GlobalRootDir, "home", usrName) usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homedir @@ -1052,7 +1129,7 @@ func (s *copydataSuite) TestUndoHideKeepGoingPreserveFirstErr(c *C) { } func (s *copydataSuite) TestInitSnapUserHome(c *C) { - homeDir := filepath.Join(s.tempdir, "user") + homeDir := filepath.Join(dirs.GlobalRootDir, "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homeDir @@ -1103,7 +1180,7 @@ func (s *copydataSuite) TestInitSnapUserHome(c *C) { } func (s *copydataSuite) TestInitExposedHomeIgnoreXDGDirs(c *C) { - homeDir := filepath.Join(s.tempdir, "user") + homeDir := filepath.Join(dirs.GlobalRootDir, "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homeDir @@ -1156,11 +1233,11 @@ func (s *copydataSuite) TestInitExposedHomeIgnoreXDGDirs(c *C) { func (s *copydataSuite) TestInitSnapFailOnFirstErr(c *C) { usr1, err := user.Current() c.Assert(err, IsNil) - usr1.HomeDir = filepath.Join(s.tempdir, "user1") + usr1.HomeDir = filepath.Join(dirs.GlobalRootDir, "user1") usr2, err := user.Current() c.Assert(err, IsNil) - usr2.HomeDir = filepath.Join(s.tempdir, "user2") + usr2.HomeDir = filepath.Join(dirs.GlobalRootDir, "user2") restore := backend.MockAllUsers(func(_ *dirs.SnapDirOptions) ([]*user.User, error) { return []*user.User{usr1, usr2}, nil @@ -1192,11 +1269,11 @@ func (s *copydataSuite) TestInitSnapFailOnFirstErr(c *C) { func (s *copydataSuite) TestInitSnapUndoOnErr(c *C) { usr1, err := user.Current() c.Assert(err, IsNil) - usr1.HomeDir = filepath.Join(s.tempdir, "user1") + usr1.HomeDir = filepath.Join(dirs.GlobalRootDir, "user1") usr2, err := user.Current() c.Assert(err, IsNil) - usr2.HomeDir = filepath.Join(s.tempdir, "user2") + usr2.HomeDir = filepath.Join(dirs.GlobalRootDir, "user2") restore := backend.MockAllUsers(func(_ *dirs.SnapDirOptions) ([]*user.User, error) { return []*user.User{usr1, usr2}, nil @@ -1243,7 +1320,7 @@ func (s *copydataSuite) TestInitSnapUndoOnErr(c *C) { func (s *copydataSuite) TestInitSnapNothingToCopy(c *C) { usr, err := user.Current() c.Assert(err, IsNil) - usr.HomeDir = filepath.Join(s.tempdir, "user") + usr.HomeDir = filepath.Join(dirs.GlobalRootDir, "user") restore := backend.MockAllUsers(func(_ *dirs.SnapDirOptions) ([]*user.User, error) { return []*user.User{usr}, nil @@ -1271,7 +1348,7 @@ func (s *copydataSuite) TestInitSnapNothingToCopy(c *C) { func (s *copydataSuite) TestInitAlreadyExistsFile(c *C) { usr, err := user.Current() c.Assert(err, IsNil) - usr.HomeDir = filepath.Join(s.tempdir, "user") + usr.HomeDir = filepath.Join(dirs.GlobalRootDir, "user") restore := backend.MockAllUsers(func(_ *dirs.SnapDirOptions) ([]*user.User, error) { return []*user.User{usr}, nil @@ -1302,7 +1379,7 @@ func (s *copydataSuite) TestInitAlreadyExistsFile(c *C) { func (s *copydataSuite) TestInitAlreadyExistsDir(c *C) { usr, err := user.Current() c.Assert(err, IsNil) - usr.HomeDir = filepath.Join(s.tempdir, "user") + usr.HomeDir = filepath.Join(dirs.GlobalRootDir, "user") restore := backend.MockAllUsers(func(_ *dirs.SnapDirOptions) ([]*user.User, error) { return []*user.User{usr}, nil @@ -1337,7 +1414,7 @@ func (s *copydataSuite) TestInitAlreadyExistsDir(c *C) { func (s *copydataSuite) TestRemoveExposedHome(c *C) { usr, err := user.Current() c.Assert(err, IsNil) - usr.HomeDir = filepath.Join(s.tempdir, "user") + usr.HomeDir = filepath.Join(dirs.GlobalRootDir, "user") restore := backend.MockAllUsers(func(_ *dirs.SnapDirOptions) ([]*user.User, error) { return []*user.User{usr}, nil @@ -1382,7 +1459,7 @@ func (s *copydataSuite) TestRemoveExposedKeepGoingOnFail(c *C) { var undoInfo backend.UndoInfo var usrs []*user.User for _, usrName := range []string{"usr1", "usr2"} { - homedir := filepath.Join(s.tempdir, usrName) + homedir := filepath.Join(dirs.GlobalRootDir, usrName) usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homedir @@ -1404,13 +1481,13 @@ func (s *copydataSuite) TestRemoveExposedKeepGoingOnFail(c *C) { err := s.be.UndoInitExposedSnapHome(snapName, &undoInfo) // the first error is returned - c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot remove %q: first error`, filepath.Join(s.tempdir, "usr1", "Snap"))) + c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot remove %q: first error`, filepath.Join(dirs.GlobalRootDir, "usr1", "Snap"))) // second error is logged - c.Assert(buf, Matches, fmt.Sprintf(`.*cannot remove %q: other error\n`, filepath.Join(s.tempdir, "usr2", "Snap"))) + c.Assert(buf, Matches, fmt.Sprintf(`.*cannot remove %q: other error\n`, filepath.Join(dirs.GlobalRootDir, "usr2", "Snap"))) } func (s *copydataSuite) TestInitXDGDirsAlreadyExist(c *C) { - homeDir := filepath.Join(s.tempdir, "user") + homeDir := filepath.Join(dirs.GlobalRootDir, "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homeDir @@ -1452,7 +1529,7 @@ func (s *copydataSuite) TestInitXDGDirsAlreadyExist(c *C) { } func (s *copydataSuite) TestInitXDGDirsCreateNew(c *C) { - homeDir := filepath.Join(s.tempdir, "user") + homeDir := filepath.Join(dirs.GlobalRootDir, "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homeDir @@ -1483,7 +1560,7 @@ func (s *copydataSuite) TestInitXDGDirsCreateNew(c *C) { } func (s *copydataSuite) TestInitXDGDirsFailAlreadyExists(c *C) { - homeDir := filepath.Join(s.tempdir, "user") + homeDir := filepath.Join(dirs.GlobalRootDir, "user") usr, err := user.Current() c.Assert(err, IsNil) usr.HomeDir = homeDir diff --git a/overlord/snapstate/backend/export_test.go b/overlord/snapstate/backend/export_test.go index e3e096f3999..718244fcad0 100644 --- a/overlord/snapstate/backend/export_test.go +++ b/overlord/snapstate/backend/export_test.go @@ -28,9 +28,11 @@ import ( ) var ( - AddMountUnit = addMountUnit - RemoveMountUnit = removeMountUnit - RemoveIfEmpty = removeIfEmpty + AddMountUnit = addMountUnit + RemoveMountUnit = removeMountUnit + RemoveIfEmpty = removeIfEmpty + SnapDataDirs = snapDataDirs + SnapCommonDataDirs = snapCommonDataDirs ) func MockWrappersAddSnapdSnapServices(f func(s *snap.Info, opts *wrappers.AddSnapdSnapServicesOptions, inter wrappers.Interacter) error) (restore func()) { diff --git a/overlord/snapstate/backend/snapdata.go b/overlord/snapstate/backend/snapdata.go index a875a572fe0..042186f1a4d 100644 --- a/overlord/snapstate/backend/snapdata.go +++ b/overlord/snapstate/backend/snapdata.go @@ -116,10 +116,16 @@ func removeDirs(dirs []string) error { // snapDataDirs returns the list of data directories for the given snap version func snapDataDirs(snap *snap.Info, opts *dirs.SnapDirOptions) ([]string, error) { // collect the directories, homes first - found, err := filepath.Glob(snap.DataHomeDir(opts)) - if err != nil { - return nil, err + var found []string + + for _, entry := range snap.DataHomeDirs(opts) { + entryPaths, err := filepath.Glob(entry) + if err != nil { + return nil, err + } + found = append(found, entryPaths...) } + // then the /root user (including GlobalRootDir for tests) found = append(found, snap.UserDataDir(filepath.Join(dirs.GlobalRootDir, "/root/"), opts)) // then system data @@ -131,9 +137,14 @@ func snapDataDirs(snap *snap.Info, opts *dirs.SnapDirOptions) ([]string, error) // snapCommonDataDirs returns the list of data directories common between versions of the given snap func snapCommonDataDirs(snap *snap.Info, opts *dirs.SnapDirOptions) ([]string, error) { // collect the directories, homes first - found, err := filepath.Glob(snap.CommonDataHomeDir(opts)) - if err != nil { - return nil, err + var found []string + + for _, entry := range snap.CommonDataHomeDirs(opts) { + entryPaths, err := filepath.Glob(entry) + if err != nil { + return nil, err + } + found = append(found, entryPaths...) } // then the root user's common data dir diff --git a/overlord/snapstate/backend/snapdata_test.go b/overlord/snapstate/backend/snapdata_test.go index 40110490c3a..32ff8a93b65 100644 --- a/overlord/snapstate/backend/snapdata_test.go +++ b/overlord/snapstate/backend/snapdata_test.go @@ -22,6 +22,7 @@ package backend_test import ( "os" "path/filepath" + "strings" . "gopkg.in/check.v1" @@ -49,7 +50,8 @@ func (s *snapdataSuite) TearDownTest(c *C) { } func (s *snapdataSuite) TestRemoveSnapData(c *C) { - homedir := filepath.Join(s.tempdir, "home", "user1", "snap") + dirs.SetSnapHomeDirs("/home") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user1", "snap") homeData := filepath.Join(homedir, "hello/10") err := os.MkdirAll(homeData, 0755) c.Assert(err, IsNil) @@ -67,8 +69,85 @@ func (s *snapdataSuite) TestRemoveSnapData(c *C) { c.Assert(osutil.FileExists(filepath.Dir(varData)), Equals, true) } +// same as TestRemoveSnapData but with multiple homedirs +func (s *snapdataSuite) TestRemoveSnapDataMulti(c *C) { + homeDirs := []string{filepath.Join(dirs.GlobalRootDir, "home"), + filepath.Join(dirs.GlobalRootDir, "home", "company"), + filepath.Join(dirs.GlobalRootDir, "home", "department"), + filepath.Join(dirs.GlobalRootDir, "office")} + + dirs.SetSnapHomeDirs(strings.Join(homeDirs, ",")) + snapHomeDataDirs := []string{} + + for _, v := range homeDirs { + snapHomeDir := filepath.Join(v, "user1", "snap") + snapHomeData := filepath.Join(snapHomeDir, "hello/10") + err := os.MkdirAll(snapHomeData, 0755) + c.Assert(err, IsNil) + snapHomeDataDirs = append(snapHomeDataDirs, snapHomeData) + } + + varData := filepath.Join(dirs.SnapDataDir, "hello/10") + err := os.MkdirAll(varData, 0755) + c.Assert(err, IsNil) + info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) + err = s.be.RemoveSnapData(info, nil) + + for _, v := range snapHomeDataDirs { + c.Assert(err, IsNil) + c.Assert(osutil.FileExists(v), Equals, false) + c.Assert(osutil.FileExists(filepath.Dir(v)), Equals, true) + + } + + c.Assert(osutil.FileExists(varData), Equals, false) + c.Assert(osutil.FileExists(filepath.Dir(varData)), Equals, true) +} + +func (s *snapdataSuite) TestSnapDataDirs(c *C) { + homeDir1 := filepath.Join("home", "users") + homeDir2 := filepath.Join("remote", "users") + homeDirs := homeDir1 + "," + homeDir2 + dirs.SetSnapHomeDirs(homeDirs) + dataHomeDirs := []string{filepath.Join(dirs.GlobalRootDir, homeDir1, "user1", "snap", "hello", "10"), + filepath.Join(dirs.GlobalRootDir, homeDir1, "user2", "snap", "hello", "10"), + filepath.Join(dirs.GlobalRootDir, homeDir2, "user3", "snap", "hello", "10"), + filepath.Join(dirs.GlobalRootDir, homeDir2, "user4", "snap", "hello", "10"), + filepath.Join(dirs.GlobalRootDir, "root", "snap", "hello", "10"), + filepath.Join(dirs.GlobalRootDir, "var", "snap", "hello", "10")} + for _, path := range dataHomeDirs { + err := os.MkdirAll(path, 0755) + c.Assert(err, IsNil) + } + + info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) + snapDataDirs, err := backend.SnapDataDirs(info, nil) + c.Assert(err, IsNil) + c.Check(snapDataDirs, DeepEquals, dataHomeDirs) +} + +func (s *snapdataSuite) TestSnapCommonDataDirs(c *C) { + homeDir1 := filepath.Join(dirs.GlobalRootDir, "home", "users") + homeDir2 := filepath.Join(dirs.GlobalRootDir, "remote", "users") + homeDirs := homeDir1 + "," + homeDir2 + dirs.SetSnapHomeDirs(homeDirs) + dataHomeDirs := []string{filepath.Join(homeDir1, "user1", "snap", "hello", "common"), filepath.Join(homeDir1, "user2", "snap", "hello", "common"), + filepath.Join(homeDir2, "user3", "snap", "hello", "common"), filepath.Join(homeDir2, "user4", "snap", "hello", "common"), + filepath.Join(dirs.GlobalRootDir, "root", "snap", "hello", "common"), filepath.Join(dirs.GlobalRootDir, "var", "snap", "hello", "common")} + for _, path := range dataHomeDirs { + err := os.MkdirAll(path, 0755) + c.Assert(err, IsNil) + } + + info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) + snapCommonDataDirs, err := backend.SnapCommonDataDirs(info, nil) + c.Assert(err, IsNil) + c.Check(snapCommonDataDirs, DeepEquals, dataHomeDirs) +} + func (s *snapdataSuite) TestRemoveSnapCommonData(c *C) { - homedir := filepath.Join(s.tempdir, "home", "user1", "snap") + dirs.SetSnapHomeDirs("/home") + homedir := filepath.Join(dirs.GlobalRootDir, "home", "user1", "snap") homeCommonData := filepath.Join(homedir, "hello/common") err := os.MkdirAll(homeCommonData, 0755) c.Assert(err, IsNil) @@ -76,7 +155,7 @@ func (s *snapdataSuite) TestRemoveSnapCommonData(c *C) { err = os.MkdirAll(varCommonData, 0755) c.Assert(err, IsNil) - rootCommonDir := filepath.Join(s.tempdir, "root", "snap", "hello", "common") + rootCommonDir := filepath.Join(dirs.GlobalRootDir, "root", "snap", "hello", "common") c.Assert(os.MkdirAll(rootCommonDir, 0700), IsNil) info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) @@ -99,7 +178,7 @@ func (s *snapdataSuite) TestRemoveSnapCommonSave(c *C) { err = os.MkdirAll(varCommonData, 0755) c.Assert(err, IsNil) - rootCommonDir := filepath.Join(s.tempdir, "root", "snap", "hello", "common") + rootCommonDir := filepath.Join(dirs.GlobalRootDir, "root", "snap", "hello", "common") c.Assert(os.MkdirAll(rootCommonDir, 0700), IsNil) info := snaptest.MockSnap(c, helloYaml1, &snap.SideInfo{Revision: snap.R(10)}) diff --git a/snap/helpers.go b/snap/helpers.go index cacb3074e7a..ce6b1649a51 100644 --- a/snap/helpers.go +++ b/snap/helpers.go @@ -40,10 +40,14 @@ func IsSnapd(snapID string) bool { // AllUsers returns a list of users, including the root user and all users that // can be found under /home with a snap directory. func AllUsers(opts *dirs.SnapDirOptions) ([]*user.User, error) { - ds, err := filepath.Glob(DataHomeGlob(opts)) - if err != nil { - // can't happen? - return nil, err + var ds []string + + for _, entry := range dirs.DataHomeGlobs(opts) { + entryPaths, err := filepath.Glob(entry) + if err != nil { + return nil, err + } + ds = append(ds, entryPaths...) } users := make([]*user.User, 1, len(ds)+1) diff --git a/snap/info.go b/snap/info.go index f01eeeccdcc..0d01d7fef83 100644 --- a/snap/info.go +++ b/snap/info.go @@ -111,13 +111,13 @@ type PlaceInfo interface { // UserXdgRuntimeDir returns the per user XDG_RUNTIME_DIR directory UserXdgRuntimeDir(userID sys.UserID) string - // DataHomeDir returns a glob that matches all per user data directories + // DataHomeDirs returns a slice of globs that match all per user data directories // of a snap. - DataHomeDir(opts *dirs.SnapDirOptions) string + DataHomeDirs(opts *dirs.SnapDirOptions) []string - // CommonDataHomeDir returns a glob that matches all per user data + // CommonDataHomeDirs returns a slice of globs that match all per user data // directories common across revisions of the snap. - CommonDataHomeDir(opts *dirs.SnapDirOptions) string + CommonDataHomeDirs(opts *dirs.SnapDirOptions) []string // XdgRuntimeDirs returns a glob that matches all XDG_RUNTIME_DIR // directories for all users of the snap. @@ -693,28 +693,24 @@ func (s *Info) CommonDataSaveDir() string { return CommonDataSaveDir(s.InstanceName()) } -// DataHomeGlob returns the globbing expression for the snap directories in use -func DataHomeGlob(opts *dirs.SnapDirOptions) string { - if opts == nil { - opts = &dirs.SnapDirOptions{} - } - - if opts.HiddenSnapDataDir { - return dirs.HiddenSnapDataHomeGlob +// DataHomeDirs returns the per user data directories of the snap across multiple +// home directories. +func (s *Info) DataHomeDirs(opts *dirs.SnapDirOptions) []string { + var dataHomeGlob []string + for _, glob := range dirs.DataHomeGlobs(opts) { + dataHomeGlob = append(dataHomeGlob, filepath.Join(glob, s.InstanceName(), s.Revision.String())) } - - return dirs.SnapDataHomeGlob -} - -// DataHomeDir returns the per user data directory of the snap. -func (s *Info) DataHomeDir(opts *dirs.SnapDirOptions) string { - return filepath.Join(DataHomeGlob(opts), s.InstanceName(), s.Revision.String()) + return dataHomeGlob } -// CommonDataHomeDir returns the per user data directory common across revisions -// of the snap. -func (s *Info) CommonDataHomeDir(opts *dirs.SnapDirOptions) string { - return filepath.Join(DataHomeGlob(opts), s.InstanceName(), "common") +// CommonDataHomeDirs returns the per user data directories common across revisions +// of the snap in all defined home directories. +func (s *Info) CommonDataHomeDirs(opts *dirs.SnapDirOptions) []string { + var comDataHomeGlob []string + for _, glob := range dirs.DataHomeGlobs(opts) { + comDataHomeGlob = append(comDataHomeGlob, filepath.Join(glob, s.InstanceName(), "common")) + } + return comDataHomeGlob } // UserXdgRuntimeDir returns the XDG_RUNTIME_DIR directory of the snap for a diff --git a/snap/info_test.go b/snap/info_test.go index 54642870718..b70e452e033 100644 --- a/snap/info_test.go +++ b/snap/info_test.go @@ -1167,8 +1167,6 @@ func (s *infoSuite) testDirAndFileMethods(c *C, info snap.PlaceInfo) { c.Check(info.CommonDataSaveDir(), Equals, "/var/lib/snapd/save/snap/name") c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name") // XXX: Those are actually a globs, not directories - c.Check(info.DataHomeDir(nil), Equals, "/home/*/snap/name/1") - c.Check(info.CommonDataHomeDir(nil), Equals, "/home/*/snap/name/common") c.Check(info.XdgRuntimeDirs(), Equals, "/run/user/*/snap.name") c.Check(info.BinaryNameGlobs(), DeepEquals, []string{"name", "name.*"}) } @@ -1197,8 +1195,6 @@ func (s *infoSuite) testInstanceDirAndFileMethods(c *C, info snap.PlaceInfo) { c.Check(info.CommonDataSaveDir(), Equals, "/var/lib/snapd/save/snap/name_instance") c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name_instance") // XXX: Those are actually a globs, not directories - c.Check(info.DataHomeDir(nil), Equals, "/home/*/snap/name_instance/1") - c.Check(info.CommonDataHomeDir(nil), Equals, "/home/*/snap/name_instance/common") c.Check(info.XdgRuntimeDirs(), Equals, "/run/user/*/snap.name_instance") c.Check(info.BinaryNameGlobs(), DeepEquals, []string{"name_instance", "name_instance.*"}) } @@ -1227,6 +1223,28 @@ func (s *infoSuite) TestComponentPlaceInfoMethodsParallelInstall(c *C) { c.Check(cpi.MountDescription(), Equals, "Mount unit for name_instance, revision 1") } +func (s *infoSuite) TestDataHomeDirs(c *C) { + dirs.SetSnapHomeDirs("/home,/home/group1,/home/group2,/home/group3") + info := &snap.Info{SuggestedName: "name"} + info.SideInfo = snap.SideInfo{Revision: snap.R(1)} + + homeDirs := []string{filepath.Join(dirs.GlobalRootDir, "/home/*/snap/name/1"), filepath.Join(dirs.GlobalRootDir, "/home/group1/*/snap/name/1"), + filepath.Join(dirs.GlobalRootDir, "/home/group2/*/snap/name/1"), filepath.Join(dirs.GlobalRootDir, "/home/group3/*/snap/name/1")} + commonHomeDirs := []string{filepath.Join(dirs.GlobalRootDir, "/home/*/snap/name/common"), filepath.Join(dirs.GlobalRootDir, "/home/group1/*/snap/name/common"), + filepath.Join(dirs.GlobalRootDir, "/home/group2/*/snap/name/common"), filepath.Join(dirs.GlobalRootDir, "/home/group3/*/snap/name/common")} + c.Check(info.DataHomeDirs(nil), DeepEquals, homeDirs) + c.Check(info.CommonDataHomeDirs(nil), DeepEquals, commonHomeDirs) + + // Same test but with a hidden snap directory + opts := &dirs.SnapDirOptions{HiddenSnapDataDir: true} + hiddenHomeDirs := []string{filepath.Join(dirs.GlobalRootDir, "/home/*/.snap/data/name/1"), filepath.Join(dirs.GlobalRootDir, "/home/group1/*/.snap/data/name/1"), + filepath.Join(dirs.GlobalRootDir, "/home/group2/*/.snap/data/name/1"), filepath.Join(dirs.GlobalRootDir, "/home/group3/*/.snap/data/name/1")} + hiddenCommonHomeDirs := []string{filepath.Join(dirs.GlobalRootDir, "/home/*/.snap/data/name/common"), filepath.Join(dirs.GlobalRootDir, "/home/group1/*/.snap/data/name/common"), + filepath.Join(dirs.GlobalRootDir, "/home/group2/*/.snap/data/name/common"), filepath.Join(dirs.GlobalRootDir, "/home/group3/*/.snap/data/name/common")} + c.Check(info.DataHomeDirs(opts), DeepEquals, hiddenHomeDirs) + c.Check(info.CommonDataHomeDirs(opts), DeepEquals, hiddenCommonHomeDirs) +} + func BenchmarkTestParsePlaceInfoFromSnapFileName(b *testing.B) { for n := 0; n < b.N; n++ { for _, sn := range []string{ diff --git a/tests/main/snapd-homedirs-vendored/task.yaml b/tests/main/snapd-homedirs-vendored/task.yaml index 6afefde3af7..918aca507c8 100644 --- a/tests/main/snapd-homedirs-vendored/task.yaml +++ b/tests/main/snapd-homedirs-vendored/task.yaml @@ -81,7 +81,7 @@ execute: | MATCH "^homedirs=/remote/users$" < /var/lib/snapd/system-params echo "And that the AppArmor tunable file is proper" - MATCH "^@{HOMEDIRS}\\+=\"/remote/users\"$" < /etc/apparmor.d/tunables/home.d/snapd + MATCH "^@{HOMEDIRS}\\+=\"/remote/users\" \"/home\"$" < /etc/apparmor.d/tunables/home.d/snapd echo "Invoke the test app again (should now work)" sudo -u "$USERNAME" -i test-snapd-sh.cmd echo "Hello world" | MATCH "Hello world" diff --git a/tests/main/snapd-homedirs/task.yaml b/tests/main/snapd-homedirs/task.yaml index 79d12c92f09..1b9f4440f77 100644 --- a/tests/main/snapd-homedirs/task.yaml +++ b/tests/main/snapd-homedirs/task.yaml @@ -1,4 +1,9 @@ -summary: Test support for non-standard home directory paths +summary: Test support for non-standard home directory paths and data copy between revisions + +details: | + Verifies that the snap data in non-standard home directories gets properly copied between + revisions when the snap package is refreshed to a newer revision. Also makes sure that the + snap data in non-standard home directories is deleted when the snap package is removed. systems: - -ubuntu-core-* # Home dirs cannot be changed @@ -13,9 +18,7 @@ prepare: | # Create a new user in a non-standard location mkdir -p /remote/users useradd -b /remote/users -m -U "$USERNAME" - - # Install our test snap - "$TESTSTOOLS"/snaps-state install-local test-snapd-sh + snap install test-snapd-sh restore: | userdel -f --remove "$USERNAME" @@ -31,7 +34,7 @@ debug: | execute: | echo "Invoke the test app without setting up homedir support" - if sudo -u "$USERNAME" -i test-snapd-sh.cmd echo "Hello world" 2> stderr.log; then + if sudo -u "$USERNAME" -i test-snapd-sh.sh -c "echo Hello world" 2> stderr.log; then echo "The command succeeded; this is unexpected where AppArmor is fully working" test "$(snap debug confinement)" = partial else @@ -46,13 +49,45 @@ execute: | MATCH "^homedirs=/remote/users$" < /var/lib/snapd/system-params echo "And that the AppArmor tunable file is proper" - MATCH "^@{HOMEDIRS}\\+=\"/remote/users\"$" < /etc/apparmor.d/tunables/home.d/snapd + MATCH "^@{HOMEDIRS}\\+=\"/remote/users\" \"/home\"$" < /etc/apparmor.d/tunables/home.d/snapd echo "Invoke the test app again (should now work)" - sudo -u "$USERNAME" -i test-snapd-sh.cmd echo "Hello world" | MATCH "Hello world" + sudo -u "$USERNAME" -i test-snapd-sh.sh -c "echo Hello world" | MATCH "Hello world" echo "Ensure that the namespace is reused" # Invoke the same command once more, but this time with debugging enabled, # to verify that the existing namespace is not discarded - sudo SNAPD_DEBUG=1 -u "$USERNAME" -i test-snapd-sh.cmd echo "Hello world" 2>&1 \ + sudo SNAPD_DEBUG=1 -u "$USERNAME" -i test-snapd-sh.sh -c "echo Hello world" 2>&1 \ | MATCH "preserved mount is not stale, reusing" + + # Get the revison of the snap + rev=$(snap list test-snapd-sh|tail -n1|tr -s ' '|cut -f3 -d' ') + + homes=("/root/" "/home/test/" "/remote/users/$USERNAME/") + echo "That has some user data" + for h in "${homes[@]}"; do + test -d "$h" + d="${h}snap/test-snapd-sh/$rev" + mkdir -p "$d" + touch "$d/mock-data" + chown --recursive --reference="$h" "${h}snap/" + done + + echo "When the snap is refreshed" + snap refresh --channel=edge test-snapd-sh + new_rev=$(snap list test-snapd-sh|tail -n1|tr -s ' '|cut -f3 -d' ') + + echo "Then the user data gets copied" + for h in "${homes[@]}"; do + test -e "${h}snap/test-snapd-sh/$new_rev/mock-data" + test -e "${h}snap/test-snapd-sh/$rev/mock-data" + done + + echo "When the snap is removed" + snap remove --purge test-snapd-sh + + echo "Then all user data and root data is gone" + for h in "${homes[@]}"; do + test ! -e "${h}snap/test-snapd-sh/$new_rev/mock-data" + test ! -e "${h}snap/test-snapd-sh/$rev/mock-data" + done diff --git a/tests/main/snapd-homedirs/test-snapd-sh/bin/sh b/tests/main/snapd-homedirs/test-snapd-sh/bin/sh deleted file mode 100755 index 9d8ad4d3cbc..00000000000 --- a/tests/main/snapd-homedirs/test-snapd-sh/bin/sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exec "$@" diff --git a/tests/main/snapd-homedirs/test-snapd-sh/meta/snap.yaml b/tests/main/snapd-homedirs/test-snapd-sh/meta/snap.yaml deleted file mode 100644 index 03a353175f1..00000000000 --- a/tests/main/snapd-homedirs/test-snapd-sh/meta/snap.yaml +++ /dev/null @@ -1,7 +0,0 @@ -name: test-snapd-sh -summary: A no-strings-attached, no-fuss shell for writing tests -version: 1.0 - -apps: - cmd: - command: bin/sh diff --git a/wrappers/services_test.go b/wrappers/services_test.go index ed7b51c8313..316427fffac 100644 --- a/wrappers/services_test.go +++ b/wrappers/services_test.go @@ -115,7 +115,7 @@ func (s *servicesTestSuite) addSnapServices(snapInfo *snap.Info, preseeding bool func (s *servicesTestSuite) TestAddSnapServicesAndRemove(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") err := s.addSnapServices(info, false) c.Assert(err, IsNil) @@ -187,7 +187,7 @@ func (s *servicesTestSuite) TestEnsureSnapServicesAdds(c *C) { } info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") m := map[*snap.Info]*wrappers.SnapServiceOptions{ info: nil, @@ -232,7 +232,7 @@ WantedBy=multi-user.target func (s *servicesTestSuite) TestEnsureSnapServicesWithQuotas(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") // set up arbitrary quotas for the group to test they get written correctly to the slice resourceLimits := quota.NewResourcesBuilder(). @@ -339,7 +339,7 @@ func (s *servicesTestSuite) TestEnsureSnapServicesWithZeroCpuCountQuotas(c *C) { // Kind of a special case, if the cpu count is zero it needs to automatically scale // at the moment of writing the service file to the current number of cpu cores info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") // set up arbitrary quotas for the group to test they get written correctly to the slice resourceLimits := quota.NewResourcesBuilder(). @@ -437,7 +437,7 @@ func (s *servicesTestSuite) TestEnsureSnapServicesWithZeroCpuCountAndCpuSetQuota // we provide only 1 allowed CPU, which means that the percentage that will be written is 50% // and not 200% or how many cores that runs this test! info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") // set up arbitrary quotas for the group to test they get written correctly to the slice resourceLimits := quota.NewResourcesBuilder(). @@ -530,7 +530,7 @@ TasksAccounting=true func (s *servicesTestSuite) TestEnsureSnapServicesWithJournalNamespaceOnly(c *C) { // Ensure that the journald.conf file is correctly written info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") // set up arbitrary quotas for the group to test they get written correctly to the slice resourceLimits := quota.NewResourcesBuilder(). @@ -644,7 +644,7 @@ TasksAccounting=true func (s *servicesTestSuite) TestEnsureSnapServicesWithJournalQuotas(c *C) { // Ensure that the journald.conf file is correctly written info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") // set up arbitrary quotas for the group to test they get written correctly to the slice resourceLimits := quota.NewResourcesBuilder(). @@ -763,7 +763,7 @@ TasksAccounting=true func (s *servicesTestSuite) TestEnsureSnapServicesWithJournalQuotaRateAsZero(c *C) { // Ensure that the journald.conf file is correctly written info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") // set up arbitrary quotas for the group to test they get written correctly to the slice resourceLimits := quota.NewResourcesBuilder(). @@ -895,8 +895,8 @@ func (s *servicesTestSuite) TestEnsureSnapServicesWithSnapServices(c *C) { // Furthermore we should see the service unit file for hello-snap.svc2 refer to the // sub-group slice, and not the slice for the primary group. info := snaptest.MockSnap(c, testSnapServicesYaml, &snap.SideInfo{Revision: snap.R(12)}) - svc1File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") - svc2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service") + svc1File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svc2File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc2.service") // setup quotas for the parent, including a journal quota to verify service sub-groups // correctly inherit the journal namespace, even without having one directly specified. @@ -1092,7 +1092,7 @@ func (s *servicesTestSuite) TestEnsureSnapServicesWithIncludeServices(c *C) { // option to EnsureSnapServices. Thus what we will observe happen is that only the service // file for svc2 will be written. info := snaptest.MockSnap(c, testSnapServicesYaml, &snap.SideInfo{Revision: snap.R(12)}) - svc2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service") + svc2File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc2.service") // set up arbitrary quotas for the group to test they get written correctly to the slice grp, err := quota.NewGroup("my-root", quota.NewResourcesBuilder(). @@ -1304,7 +1304,7 @@ func expChangeObserver(c *C, exp []changesObservation) (restore func(), obs wrap func (s *servicesTestSuite) TestEnsureSnapServicesRewritesQuotaSlices(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") memLimit1 := quantity.SizeGiB memLimit2 := quantity.SizeGiB * 2 @@ -1330,7 +1330,7 @@ MemoryLimit=%[2]s # threads, etc for a slice TasksAccounting=true ` - sliceFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.foogroup.slice") + sliceFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.foogroup.slice") dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") svcContent := fmt.Sprintf(`[Unit] @@ -1406,7 +1406,7 @@ WantedBy=multi-user.target func (s *servicesTestSuite) TestEnsureSnapServicesDoesNotRewriteQuotaSlicesOnNoop(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") memLimit := quantity.SizeGiB taskLimit := 32 // arbitrarily chosen @@ -1432,7 +1432,7 @@ MemoryLimit=%[2]s TasksAccounting=true TasksMax=%[3]d ` - sliceFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.foogroup.slice") + sliceFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.foogroup.slice") dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") svcContent := fmt.Sprintf(`[Unit] @@ -1499,7 +1499,7 @@ func (s *servicesTestSuite) TestRemoveQuotaGroup(c *C) { grp, err := quota.NewGroup("foogroup", resourceLimits) c.Assert(err, IsNil) - sliceFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.foogroup.slice") + sliceFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.foogroup.slice") c.Assert(sliceFile, testutil.FileAbsent) // removing the group when the slice file doesn't exist is not an error @@ -1563,8 +1563,8 @@ apps: post-stop-command: bin/missya daemon: forking `, &snap.SideInfo{Revision: snap.R(12)}) - svcFile1 := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") - svcFile2 := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-other-snap.svc1.service") + svcFile1 := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile2 := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-other-snap.svc1.service") var err error resourceLimits := quota.NewResourcesBuilder(). @@ -1581,8 +1581,8 @@ apps: subgrp, err := grp.NewSubGroup("subgroup", resourceLimits) c.Assert(err, IsNil) - sliceFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.foogroup.slice") - subSliceFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.foogroup-subgroup.slice") + sliceFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.foogroup.slice") + subSliceFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.foogroup-subgroup.slice") m := map[*snap.Info]*wrappers.SnapServiceOptions{ info1: {QuotaGroup: grp}, @@ -1705,7 +1705,7 @@ WantedBy=multi-user.target func (s *servicesTestSuite) TestEnsureSnapServicesWithSubGroupQuotaGroupsGeneratesParentGroups(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile1 := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile1 := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") var err error resourceLimits := quota.NewResourcesBuilder(). @@ -1721,8 +1721,8 @@ func (s *servicesTestSuite) TestEnsureSnapServicesWithSubGroupQuotaGroupsGenerat subgrp, err := grp.NewSubGroup("subgroup", resourceLimits) c.Assert(err, IsNil) - sliceFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.foogroup.slice") - subSliceFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.foogroup-subgroup.slice") + sliceFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.foogroup.slice") + subSliceFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.foogroup-subgroup.slice") m := map[*snap.Info]*wrappers.SnapServiceOptions{ info: {QuotaGroup: subgrp}, @@ -1796,7 +1796,7 @@ TasksAccounting=true func (s *servicesTestSuite) TestEnsureSnapServiceEnsureError(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFileDir := filepath.Join(s.tempdir, "/etc/systemd/system") + svcFileDir := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system") m := map[*snap.Info]*wrappers.SnapServiceOptions{ info: nil, @@ -1828,7 +1828,7 @@ func (s *servicesTestSuite) TestEnsureSnapServicesPreseedingHappy(c *C) { } info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") m := map[*snap.Info]*wrappers.SnapServiceOptions{ info: nil, @@ -1902,8 +1902,8 @@ apps: post-stop-command: bin/missya daemon: forking `, &snap.SideInfo{Revision: snap.R(12)}) - svcFile1 := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") - svcFile2 := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-other-snap.svc1.service") + svcFile1 := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile2 := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-other-snap.svc1.service") // some options per-snap m := map[*snap.Info]*wrappers.SnapServiceOptions{ @@ -1977,8 +1977,8 @@ func (s *servicesTestSuite) TestEnsureSnapServicesCallback(c *C) { post-stop-command: bin/missya daemon: forking `, &snap.SideInfo{Revision: snap.R(12)}) - svc1File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") - svc2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service") + svc1File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svc2File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc2.service") dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") template := `[Unit] @@ -2070,8 +2070,8 @@ func (s *servicesTestSuite) TestEnsureSnapServicesAddsNewSvc(c *C) { post-stop-command: bin/missya daemon: forking `, &snap.SideInfo{Revision: snap.R(12)}) - svc1File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") - svc2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service") + svc1File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svc2File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc2.service") dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") template := `[Unit] @@ -2143,7 +2143,7 @@ func (s *servicesTestSuite) TestEnsureSnapServicesNoChangeNoop(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) // pretend we already have a unit file setup - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") template := `[Unit] @@ -2211,7 +2211,7 @@ func (s *servicesTestSuite) TestEnsureSnapServicesChanges(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) // pretend we already have a unit file with no VitalityRank options set - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") template := `[Unit] @@ -2274,7 +2274,7 @@ WantedBy=multi-user.target func (s *servicesTestSuite) TestEnsureSnapServicesRollsback(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") // pretend we already have a unit file with no VitalityRank options set dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") @@ -2358,7 +2358,7 @@ WantedBy=multi-user.target func (s *servicesTestSuite) TestEnsureSnapServicesRemovesNewAddOnRollback(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") // pretend we already have a unit file with no VitalityRank options set dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") @@ -2435,8 +2435,8 @@ func (s *servicesTestSuite) TestEnsureSnapServicesOnlyRemovesNewAddOnRollback(c // we won't delete existing files, but we will delete new files, so mock an // existing file to check that it doesn't get deleted - svc1File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") - svc2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service") + svc1File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svc2File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc2.service") // pretend we already have a unit file with no VitalityRank options set dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") @@ -2657,7 +2657,7 @@ plugs: daemon: simple `+t.plugSnippet, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") err := s.addSnapServices(info, false) c.Assert(err, IsNil, comment) @@ -2720,7 +2720,7 @@ func (s *servicesTestSuite) TestAddSnapServicesAndRemoveUserDaemons(c *C) { daemon: simple daemon-scope: user `, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/user/snap.hello-snap.svc1.service") err := s.addSnapServices(info, false) c.Assert(err, IsNil) @@ -3333,7 +3333,7 @@ func (s *servicesTestSuite) TestStartServicesStopsServicesIncludingActivation(c func (s *servicesTestSuite) TestStartServices(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") flags := &wrappers.StartServicesFlags{Enable: true} err := wrappers.StartServices(info.Services(), nil, flags, &progress.Null, s.perfTimings) @@ -3348,7 +3348,7 @@ func (s *servicesTestSuite) TestStartServices(c *C) { func (s *servicesTestSuite) TestStartServicesNoEnable(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") flags := &wrappers.StartServicesFlags{Enable: false} err := wrappers.StartServices(info.Services(), nil, flags, &progress.Null, s.perfTimings) @@ -3365,7 +3365,7 @@ func (s *servicesTestSuite) TestStartServicesUserDaemons(c *C) { daemon: simple daemon-scope: user `, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/user/snap.hello-snap.svc1.service") flags := &wrappers.StartServicesFlags{Enable: true} err := wrappers.StartServices(info.Services(), nil, flags, &progress.Null, s.perfTimings) @@ -3379,7 +3379,7 @@ func (s *servicesTestSuite) TestStartServicesUserDaemons(c *C) { func (s *servicesTestSuite) TestStartServicesEnabledConditional(c *C) { info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) - svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") + svcFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.service") flags := &wrappers.StartServicesFlags{} c.Check(wrappers.StartServices(info.Services(), nil, flags, progress.Null, s.perfTimings), IsNil) @@ -3649,9 +3649,9 @@ func (s *servicesTestSuite) TestAddSnapSocketFiles(c *C) { `, &snap.SideInfo{Revision: snap.R(12)}) - sock1File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock1.socket") - sock2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock2.socket") - sock3File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock3.socket") + sock1File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.sock1.socket") + sock2File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.sock2.socket") + sock3File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc1.sock3.socket") err := s.addSnapServices(info, false) c.Assert(err, IsNil) @@ -3663,7 +3663,7 @@ FileDescriptorName=sock1 ListenStream=%s SocketMode=0666 -`, filepath.Join(s.tempdir, "/var/snap/hello-snap/common/sock1.socket")) +`, filepath.Join(dirs.GlobalRootDir, "/var/snap/hello-snap/common/sock1.socket")) c.Check(sock1File, testutil.FileContains, expected) expected = fmt.Sprintf( @@ -3672,7 +3672,7 @@ Service=snap.hello-snap.svc1.service FileDescriptorName=sock2 ListenStream=%s -`, filepath.Join(s.tempdir, "/var/snap/hello-snap/12/sock2.socket")) +`, filepath.Join(dirs.GlobalRootDir, "/var/snap/hello-snap/12/sock2.socket")) c.Check(sock2File, testutil.FileContains, expected) expected = fmt.Sprintf( @@ -3681,7 +3681,7 @@ Service=snap.hello-snap.svc1.service FileDescriptorName=sock3 ListenStream=%s -`, filepath.Join(s.tempdir, "/run/user/0/snap.hello-snap/sock3.socket")) +`, filepath.Join(dirs.GlobalRootDir, "/run/user/0/snap.hello-snap/sock3.socket")) c.Check(sock3File, testutil.FileContains, expected) } @@ -3701,9 +3701,9 @@ func (s *servicesTestSuite) TestAddSnapUserSocketFiles(c *C) { listen-stream: $XDG_RUNTIME_DIR/sock3.socket `, &snap.SideInfo{Revision: snap.R(12)}) - sock1File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock1.socket") - sock2File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock2.socket") - sock3File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock3.socket") + sock1File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/user/snap.hello-snap.svc1.sock1.socket") + sock2File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/user/snap.hello-snap.svc1.sock2.socket") + sock3File := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/user/snap.hello-snap.svc1.sock3.socket") err := s.addSnapServices(info, false) c.Assert(err, IsNil) @@ -4040,19 +4040,19 @@ func (s *servicesTestSuite) TestServiceAfterBefore(c *C) { kind string matches []string }{{ - file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service"), + file: filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc2.service"), kind: "After", matches: []string{info.Apps["svc1"].ServiceName()}, }, { - file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"), + file: filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc3.service"), kind: "After", matches: []string{info.Apps["svc2"].ServiceName()}, }, { - file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"), + file: filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc3.service"), kind: "Before", matches: []string{info.Apps["svc4"].ServiceName()}, }, { - file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc4.service"), + file: filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc4.service"), kind: "After", matches: []string{ info.Apps["svc1"].ServiceName(), @@ -4100,13 +4100,13 @@ func (s *servicesTestSuite) TestServiceWatchdog(c *C) { err := s.addSnapServices(info, false) c.Assert(err, IsNil) - content, err := ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service")) + content, err := ioutil.ReadFile(filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc2.service")) c.Assert(err, IsNil) c.Check(strings.Contains(string(content), "\nWatchdogSec=12\n"), Equals, true) noWatchdog := []string{ - filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"), - filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc4.service"), + filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc3.service"), + filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc4.service"), } for _, svcPath := range noWatchdog { content, err := ioutil.ReadFile(svcPath) @@ -4125,7 +4125,7 @@ apps: daemon: simple ` info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)}) - survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.survivor.service") + survivorFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.survive-snap.survivor.service") err := s.addSnapServices(info, false) c.Assert(err, IsNil) @@ -4164,7 +4164,7 @@ func (s *servicesTestSuite) TestStopServiceSigs(c *C) { r := wrappers.MockKillWait(1 * time.Millisecond) defer r() - survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.srv.service") + survivorFile := filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.survive-snap.srv.service") for _, t := range []struct { mode string expectedSig string @@ -4530,11 +4530,11 @@ func (s *servicesTestSuite) TestServiceRestartDelay(c *C) { err := s.addSnapServices(info, false) c.Assert(err, IsNil) - content, err := ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service")) + content, err := ioutil.ReadFile(filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc2.service")) c.Assert(err, IsNil) c.Check(strings.Contains(string(content), "\nRestartSec=12\n"), Equals, true) - content, err = ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service")) + content, err = ioutil.ReadFile(filepath.Join(dirs.GlobalRootDir, "/etc/systemd/system/snap.hello-snap.svc3.service")) c.Assert(err, IsNil) c.Check(strings.Contains(string(content), "RestartSec="), Equals, false) }