github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/osutil/syncdir_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 osutil_test 21 22 import ( 23 "io/ioutil" 24 "os" 25 "path" 26 "path/filepath" 27 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/osutil" 31 "github.com/snapcore/snapd/testutil" 32 ) 33 34 type EnsureDirStateSuite struct { 35 dir string 36 glob string 37 } 38 39 var _ = Suite(&EnsureDirStateSuite{glob: "*.snap"}) 40 41 func (s *EnsureDirStateSuite) SetUpTest(c *C) { 42 s.dir = c.MkDir() 43 } 44 45 func (s *EnsureDirStateSuite) TestVerifiesExpectedFiles(c *C) { 46 name := filepath.Join(s.dir, "expected.snap") 47 err := ioutil.WriteFile(name, []byte("expected"), 0600) 48 c.Assert(err, IsNil) 49 changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{ 50 "expected.snap": &osutil.MemoryFileState{Content: []byte("expected"), Mode: 0600}, 51 }) 52 c.Assert(err, IsNil) 53 // Report says that nothing has changed 54 c.Assert(changed, HasLen, 0) 55 c.Assert(removed, HasLen, 0) 56 // The content is correct 57 c.Assert(path.Join(s.dir, "expected.snap"), testutil.FileEquals, "expected") 58 // The permissions are correct 59 stat, err := os.Stat(name) 60 c.Assert(err, IsNil) 61 c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) 62 } 63 64 func (s *EnsureDirStateSuite) TestTwoPatterns(c *C) { 65 name1 := filepath.Join(s.dir, "expected.snap") 66 err := ioutil.WriteFile(name1, []byte("expected-1"), 0600) 67 c.Assert(err, IsNil) 68 69 name2 := filepath.Join(s.dir, "expected.snap-update-ns") 70 err = ioutil.WriteFile(name2, []byte("expected-2"), 0600) 71 c.Assert(err, IsNil) 72 73 changed, removed, err := osutil.EnsureDirStateGlobs(s.dir, []string{"*.snap", "*.snap-update-ns"}, map[string]osutil.FileState{ 74 "expected.snap": &osutil.MemoryFileState{Content: []byte("expected-1"), Mode: 0600}, 75 "expected.snap-update-ns": &osutil.MemoryFileState{Content: []byte("expected-2"), Mode: 0600}, 76 }) 77 c.Assert(err, IsNil) 78 // Report says that nothing has changed 79 c.Assert(changed, HasLen, 0) 80 c.Assert(removed, HasLen, 0) 81 // The content is correct 82 c.Assert(name1, testutil.FileEquals, "expected-1") 83 c.Assert(name2, testutil.FileEquals, "expected-2") 84 // The permissions are correct 85 stat, err := os.Stat(name1) 86 c.Assert(err, IsNil) 87 c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) 88 stat, err = os.Stat(name2) 89 c.Assert(err, IsNil) 90 c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) 91 } 92 93 func (s *EnsureDirStateSuite) TestMultipleMatches(c *C) { 94 name := filepath.Join(s.dir, "foo") 95 err := ioutil.WriteFile(name, []byte("content"), 0600) 96 c.Assert(err, IsNil) 97 // When a file is matched by multiple globs it removed correctly. 98 changed, removed, err := osutil.EnsureDirStateGlobs(s.dir, []string{"foo", "f*"}, nil) 99 c.Assert(err, IsNil) 100 c.Assert(changed, HasLen, 0) 101 c.Assert(removed, DeepEquals, []string{"foo"}) 102 } 103 104 func (s *EnsureDirStateSuite) TestCreatesMissingFiles(c *C) { 105 name := filepath.Join(s.dir, "missing.snap") 106 changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{ 107 "missing.snap": &osutil.MemoryFileState{Content: []byte(`content`), Mode: 0600}, 108 }) 109 c.Assert(err, IsNil) 110 // Created file is reported 111 c.Assert(changed, DeepEquals, []string{"missing.snap"}) 112 c.Assert(removed, HasLen, 0) 113 // The content is correct 114 c.Assert(name, testutil.FileEquals, "content") 115 // The permissions are correct 116 stat, err := os.Stat(name) 117 c.Assert(err, IsNil) 118 c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) 119 } 120 121 func (s *EnsureDirStateSuite) TestRemovesUnexpectedFiless(c *C) { 122 name := filepath.Join(s.dir, "evil.snap") 123 err := ioutil.WriteFile(name, []byte(`evil text`), 0600) 124 c.Assert(err, IsNil) 125 changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{}) 126 c.Assert(err, IsNil) 127 // Removed file is reported 128 c.Assert(changed, HasLen, 0) 129 c.Assert(removed, DeepEquals, []string{"evil.snap"}) 130 // The file is removed 131 _, err = os.Stat(name) 132 c.Assert(os.IsNotExist(err), Equals, true) 133 } 134 135 func (s *EnsureDirStateSuite) TestIgnoresUnrelatedFiles(c *C) { 136 name := filepath.Join(s.dir, "unrelated") 137 err := ioutil.WriteFile(name, []byte(`text`), 0600) 138 c.Assert(err, IsNil) 139 changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{}) 140 c.Assert(err, IsNil) 141 // Report says that nothing has changed 142 c.Assert(changed, HasLen, 0) 143 c.Assert(removed, HasLen, 0) 144 // The file is still there 145 _, err = os.Stat(name) 146 c.Assert(err, IsNil) 147 } 148 149 func (s *EnsureDirStateSuite) TestCorrectsFilesWithDifferentSize(c *C) { 150 name := filepath.Join(s.dir, "differing.snap") 151 err := ioutil.WriteFile(name, []byte(``), 0600) 152 c.Assert(err, IsNil) 153 changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{ 154 "differing.snap": &osutil.MemoryFileState{Content: []byte(`Hello World`), Mode: 0600}, 155 }) 156 c.Assert(err, IsNil) 157 // changed file is reported 158 c.Assert(changed, DeepEquals, []string{"differing.snap"}) 159 c.Assert(removed, HasLen, 0) 160 // The content is changed 161 c.Assert(name, testutil.FileEquals, "Hello World") 162 // The permissions are what we expect 163 stat, err := os.Stat(name) 164 c.Assert(err, IsNil) 165 c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) 166 } 167 168 func (s *EnsureDirStateSuite) TestCorrectsFilesWithSameSize(c *C) { 169 name := filepath.Join(s.dir, "differing.snap") 170 err := ioutil.WriteFile(name, []byte("evil"), 0600) 171 c.Assert(err, IsNil) 172 changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{ 173 "differing.snap": &osutil.MemoryFileState{Content: []byte("good"), Mode: 0600}, 174 }) 175 c.Assert(err, IsNil) 176 // changed file is reported 177 c.Assert(changed, DeepEquals, []string{"differing.snap"}) 178 c.Assert(removed, HasLen, 0) 179 // The content is changed 180 c.Assert(name, testutil.FileEquals, "good") 181 // The permissions are what we expect 182 stat, err := os.Stat(name) 183 c.Assert(err, IsNil) 184 c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) 185 } 186 187 func (s *EnsureDirStateSuite) TestFixesFilesWithBadPermissions(c *C) { 188 name := filepath.Join(s.dir, "sensitive.snap") 189 // NOTE: the existing file is currently wide-open for everyone" 190 err := ioutil.WriteFile(name, []byte("password"), 0666) 191 c.Assert(err, IsNil) 192 changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{ 193 // NOTE: we want the file to be private 194 "sensitive.snap": &osutil.MemoryFileState{Content: []byte("password"), Mode: 0600}, 195 }) 196 c.Assert(err, IsNil) 197 // changed file is reported 198 c.Assert(changed, DeepEquals, []string{"sensitive.snap"}) 199 c.Assert(removed, HasLen, 0) 200 // The content is still the same 201 c.Assert(name, testutil.FileEquals, "password") 202 // The permissions are changed 203 stat, err := os.Stat(name) 204 c.Assert(err, IsNil) 205 c.Assert(stat.Mode().Perm(), Equals, os.FileMode(0600)) 206 } 207 208 func (s *EnsureDirStateSuite) TestReportsAbnormalFileLocation(c *C) { 209 _, _, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{"subdir/file.snap": &osutil.MemoryFileState{}}) 210 c.Assert(err, ErrorMatches, `internal error: EnsureDirState got filename "subdir/file.snap" which has a path component`) 211 } 212 213 func (s *EnsureDirStateSuite) TestReportsAbnormalFileName(c *C) { 214 _, _, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{"without-namespace": &osutil.MemoryFileState{}}) 215 c.Assert(err, ErrorMatches, `internal error: EnsureDirState got filename "without-namespace" which doesn't match the glob pattern "\*\.snap"`) 216 } 217 218 func (s *EnsureDirStateSuite) TestReportsAbnormalPatterns(c *C) { 219 _, _, err := osutil.EnsureDirState(s.dir, "[", nil) 220 c.Assert(err, ErrorMatches, `internal error: EnsureDirState got invalid pattern "\[": syntax error in pattern`) 221 } 222 223 func (s *EnsureDirStateSuite) TestRemovesAllManagedFilesOnError(c *C) { 224 // Create a "prior.snap" file 225 prior := filepath.Join(s.dir, "prior.snap") 226 err := ioutil.WriteFile(prior, []byte("data"), 0600) 227 c.Assert(err, IsNil) 228 // Create a "clash.snap" directory to simulate failure 229 clash := filepath.Join(s.dir, "clash.snap") 230 err = os.Mkdir(clash, 0000) 231 c.Assert(err, IsNil) 232 // Try to ensure directory state 233 changed, removed, err := osutil.EnsureDirState(s.dir, s.glob, map[string]osutil.FileState{ 234 "prior.snap": &osutil.MemoryFileState{Content: []byte("data"), Mode: 0600}, 235 "clash.snap": &osutil.MemoryFileState{Content: []byte("data"), Mode: 0600}, 236 }) 237 c.Assert(changed, HasLen, 0) 238 c.Assert(removed, DeepEquals, []string{"clash.snap", "prior.snap"}) 239 c.Assert(err, ErrorMatches, "open .*/clash.snap: permission denied") 240 // The clashing file is removed 241 _, err = os.Stat(clash) 242 c.Assert(os.IsNotExist(err), Equals, true) 243 }