github.com/rigado/snapd@v2.42.5-go-mod+incompatible/osutil/io_test.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 osutil_test 21 22 import ( 23 "errors" 24 "io/ioutil" 25 "math/rand" 26 "os" 27 "path/filepath" 28 29 "github.com/snapcore/snapd/osutil" 30 "github.com/snapcore/snapd/osutil/sys" 31 "github.com/snapcore/snapd/strutil" 32 "github.com/snapcore/snapd/testutil" 33 34 . "gopkg.in/check.v1" 35 ) 36 37 type AtomicWriteTestSuite struct{} 38 39 var _ = Suite(&AtomicWriteTestSuite{}) 40 41 func (ts *AtomicWriteTestSuite) TestAtomicWriteFile(c *C) { 42 tmpdir := c.MkDir() 43 44 p := filepath.Join(tmpdir, "foo") 45 err := osutil.AtomicWriteFile(p, []byte("canary"), 0644, 0) 46 c.Assert(err, IsNil) 47 48 c.Check(p, testutil.FileEquals, "canary") 49 50 // no files left behind! 51 d, err := ioutil.ReadDir(tmpdir) 52 c.Assert(err, IsNil) 53 c.Assert(len(d), Equals, 1) 54 } 55 56 func (ts *AtomicWriteTestSuite) TestAtomicWriteFilePermissions(c *C) { 57 tmpdir := c.MkDir() 58 59 p := filepath.Join(tmpdir, "foo") 60 err := osutil.AtomicWriteFile(p, []byte(""), 0600, 0) 61 c.Assert(err, IsNil) 62 63 st, err := os.Stat(p) 64 c.Assert(err, IsNil) 65 c.Assert(st.Mode()&os.ModePerm, Equals, os.FileMode(0600)) 66 } 67 68 func (ts *AtomicWriteTestSuite) TestAtomicWriteFileOverwrite(c *C) { 69 tmpdir := c.MkDir() 70 p := filepath.Join(tmpdir, "foo") 71 c.Assert(ioutil.WriteFile(p, []byte("hello"), 0644), IsNil) 72 c.Assert(osutil.AtomicWriteFile(p, []byte("hi"), 0600, 0), IsNil) 73 74 c.Assert(p, testutil.FileEquals, "hi") 75 } 76 77 func (ts *AtomicWriteTestSuite) TestAtomicWriteFileSymlinkNoFollow(c *C) { 78 tmpdir := c.MkDir() 79 rodir := filepath.Join(tmpdir, "ro") 80 p := filepath.Join(rodir, "foo") 81 s := filepath.Join(tmpdir, "foo") 82 c.Assert(os.MkdirAll(rodir, 0755), IsNil) 83 c.Assert(os.Symlink(s, p), IsNil) 84 c.Assert(os.Chmod(rodir, 0500), IsNil) 85 defer os.Chmod(rodir, 0700) 86 87 err := osutil.AtomicWriteFile(p, []byte("hi"), 0600, 0) 88 c.Assert(err, NotNil) 89 } 90 91 func (ts *AtomicWriteTestSuite) TestAtomicWriteFileAbsoluteSymlinks(c *C) { 92 tmpdir := c.MkDir() 93 rodir := filepath.Join(tmpdir, "ro") 94 p := filepath.Join(rodir, "foo") 95 s := filepath.Join(tmpdir, "foo") 96 c.Assert(os.MkdirAll(rodir, 0755), IsNil) 97 c.Assert(os.Symlink(s, p), IsNil) 98 c.Assert(os.Chmod(rodir, 0500), IsNil) 99 defer os.Chmod(rodir, 0700) 100 101 err := osutil.AtomicWriteFile(p, []byte("hi"), 0600, osutil.AtomicWriteFollow) 102 c.Assert(err, IsNil) 103 104 c.Assert(p, testutil.FileEquals, "hi") 105 } 106 107 func (ts *AtomicWriteTestSuite) TestAtomicWriteFileOverwriteAbsoluteSymlink(c *C) { 108 tmpdir := c.MkDir() 109 rodir := filepath.Join(tmpdir, "ro") 110 p := filepath.Join(rodir, "foo") 111 s := filepath.Join(tmpdir, "foo") 112 c.Assert(os.MkdirAll(rodir, 0755), IsNil) 113 c.Assert(os.Symlink(s, p), IsNil) 114 c.Assert(os.Chmod(rodir, 0500), IsNil) 115 defer os.Chmod(rodir, 0700) 116 117 c.Assert(ioutil.WriteFile(s, []byte("hello"), 0644), IsNil) 118 c.Assert(osutil.AtomicWriteFile(p, []byte("hi"), 0600, osutil.AtomicWriteFollow), IsNil) 119 120 c.Assert(p, testutil.FileEquals, "hi") 121 } 122 123 func (ts *AtomicWriteTestSuite) TestAtomicWriteFileRelativeSymlinks(c *C) { 124 tmpdir := c.MkDir() 125 rodir := filepath.Join(tmpdir, "ro") 126 p := filepath.Join(rodir, "foo") 127 c.Assert(os.MkdirAll(rodir, 0755), IsNil) 128 c.Assert(os.Symlink("../foo", p), IsNil) 129 c.Assert(os.Chmod(rodir, 0500), IsNil) 130 defer os.Chmod(rodir, 0700) 131 132 err := osutil.AtomicWriteFile(p, []byte("hi"), 0600, osutil.AtomicWriteFollow) 133 c.Assert(err, IsNil) 134 135 c.Assert(p, testutil.FileEquals, "hi") 136 } 137 138 func (ts *AtomicWriteTestSuite) TestAtomicWriteFileOverwriteRelativeSymlink(c *C) { 139 tmpdir := c.MkDir() 140 rodir := filepath.Join(tmpdir, "ro") 141 p := filepath.Join(rodir, "foo") 142 s := filepath.Join(tmpdir, "foo") 143 c.Assert(os.MkdirAll(rodir, 0755), IsNil) 144 c.Assert(os.Symlink("../foo", p), IsNil) 145 c.Assert(os.Chmod(rodir, 0500), IsNil) 146 defer os.Chmod(rodir, 0700) 147 148 c.Assert(ioutil.WriteFile(s, []byte("hello"), 0644), IsNil) 149 c.Assert(osutil.AtomicWriteFile(p, []byte("hi"), 0600, osutil.AtomicWriteFollow), IsNil) 150 151 c.Assert(p, testutil.FileEquals, "hi") 152 } 153 154 func (ts *AtomicWriteTestSuite) TestAtomicWriteFileNoOverwriteTmpExisting(c *C) { 155 tmpdir := c.MkDir() 156 // ensure we always get the same result 157 rand.Seed(1) 158 expectedRandomness := strutil.MakeRandomString(12) + "~" 159 // ensure we always get the same result 160 rand.Seed(1) 161 162 p := filepath.Join(tmpdir, "foo") 163 err := ioutil.WriteFile(p+"."+expectedRandomness, []byte(""), 0644) 164 c.Assert(err, IsNil) 165 166 err = osutil.AtomicWriteFile(p, []byte(""), 0600, 0) 167 c.Assert(err, ErrorMatches, "open .*: file exists") 168 } 169 170 func (ts *AtomicWriteTestSuite) TestAtomicFileChownError(c *C) { 171 eUid := sys.UserID(42) 172 eGid := sys.GroupID(74) 173 eErr := errors.New("this didn't work") 174 defer osutil.MockChown(func(fd *os.File, uid sys.UserID, gid sys.GroupID) error { 175 c.Check(uid, Equals, eUid) 176 c.Check(gid, Equals, eGid) 177 return eErr 178 })() 179 180 d := c.MkDir() 181 p := filepath.Join(d, "foo") 182 183 aw, err := osutil.NewAtomicFile(p, 0644, 0, eUid, eGid) 184 c.Assert(err, IsNil) 185 defer aw.Cancel() 186 187 _, err = aw.Write([]byte("hello")) 188 c.Assert(err, IsNil) 189 190 c.Check(aw.Commit(), Equals, eErr) 191 } 192 193 func (ts *AtomicWriteTestSuite) TestAtomicFileCancelError(c *C) { 194 d := c.MkDir() 195 p := filepath.Join(d, "foo") 196 aw, err := osutil.NewAtomicFile(p, 0644, 0, osutil.NoChown, osutil.NoChown) 197 c.Assert(err, IsNil) 198 199 c.Assert(aw.File.Close(), IsNil) 200 // Depending on golang version the error is one of the two. 201 c.Check(aw.Cancel(), ErrorMatches, "invalid argument|file already closed") 202 } 203 204 func (ts *AtomicWriteTestSuite) TestAtomicFileCancelBadError(c *C) { 205 d := c.MkDir() 206 p := filepath.Join(d, "foo") 207 aw, err := osutil.NewAtomicFile(p, 0644, 0, osutil.NoChown, osutil.NoChown) 208 c.Assert(err, IsNil) 209 defer aw.Close() 210 211 osutil.SetAtomicFileRenamed(aw, true) 212 213 c.Check(aw.Cancel(), Equals, osutil.ErrCannotCancel) 214 } 215 216 func (ts *AtomicWriteTestSuite) TestAtomicFileCancelNoClose(c *C) { 217 d := c.MkDir() 218 p := filepath.Join(d, "foo") 219 aw, err := osutil.NewAtomicFile(p, 0644, 0, osutil.NoChown, osutil.NoChown) 220 c.Assert(err, IsNil) 221 c.Assert(aw.Close(), IsNil) 222 223 c.Check(aw.Cancel(), IsNil) 224 } 225 226 func (ts *AtomicWriteTestSuite) TestAtomicFileCancel(c *C) { 227 d := c.MkDir() 228 p := filepath.Join(d, "foo") 229 230 aw, err := osutil.NewAtomicFile(p, 0644, 0, osutil.NoChown, osutil.NoChown) 231 c.Assert(err, IsNil) 232 fn := aw.File.Name() 233 c.Check(osutil.FileExists(fn), Equals, true) 234 c.Check(aw.Cancel(), IsNil) 235 c.Check(osutil.FileExists(fn), Equals, false) 236 } 237 238 // SafeIoAtomicWriteTestSuite runs all AtomicWrite with safe 239 // io enabled 240 type SafeIoAtomicWriteTestSuite struct { 241 AtomicWriteTestSuite 242 243 restoreUnsafeIO func() 244 } 245 246 var _ = Suite(&SafeIoAtomicWriteTestSuite{}) 247 248 func (s *SafeIoAtomicWriteTestSuite) SetUpSuite(c *C) { 249 s.restoreUnsafeIO = osutil.SetUnsafeIO(false) 250 } 251 252 func (s *SafeIoAtomicWriteTestSuite) TearDownSuite(c *C) { 253 s.restoreUnsafeIO() 254 }