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  }