github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/os/os_unix_test.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build darwin dragonfly freebsd linux netbsd openbsd solaris
     6  
     7  package os_test
     8  
     9  import (
    10  	"io"
    11  	"io/ioutil"
    12  	. "os"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  	"syscall"
    17  	"testing"
    18  )
    19  
    20  func init() {
    21  	isReadonlyError = func(err error) bool { return err == syscall.EROFS }
    22  }
    23  
    24  func checkUidGid(t *testing.T, path string, uid, gid int) {
    25  	dir, err := Lstat(path)
    26  	if err != nil {
    27  		t.Fatalf("Lstat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err)
    28  	}
    29  	sys := dir.Sys().(*syscall.Stat_t)
    30  	if int(sys.Uid) != uid {
    31  		t.Errorf("Lstat %q: uid %d want %d", path, sys.Uid, uid)
    32  	}
    33  	if int(sys.Gid) != gid {
    34  		t.Errorf("Lstat %q: gid %d want %d", path, sys.Gid, gid)
    35  	}
    36  }
    37  
    38  func TestChown(t *testing.T) {
    39  	// Chown is not supported under windows or Plan 9.
    40  	// Plan9 provides a native ChownPlan9 version instead.
    41  	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
    42  		t.Skipf("%s does not support syscall.Chown", runtime.GOOS)
    43  	}
    44  	// Use TempDir() to make sure we're on a local file system,
    45  	// so that the group ids returned by Getgroups will be allowed
    46  	// on the file. On NFS, the Getgroups groups are
    47  	// basically useless.
    48  	f := newFile("TestChown", t)
    49  	defer Remove(f.Name())
    50  	defer f.Close()
    51  	dir, err := f.Stat()
    52  	if err != nil {
    53  		t.Fatalf("stat %s: %s", f.Name(), err)
    54  	}
    55  
    56  	// Can't change uid unless root, but can try
    57  	// changing the group id. First try our current group.
    58  	gid := Getgid()
    59  	t.Log("gid:", gid)
    60  	if err = Chown(f.Name(), -1, gid); err != nil {
    61  		t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
    62  	}
    63  	sys := dir.Sys().(*syscall.Stat_t)
    64  	checkUidGid(t, f.Name(), int(sys.Uid), gid)
    65  
    66  	// Then try all the auxiliary groups.
    67  	groups, err := Getgroups()
    68  	if err != nil {
    69  		t.Fatalf("getgroups: %s", err)
    70  	}
    71  	t.Log("groups: ", groups)
    72  	for _, g := range groups {
    73  		if err = Chown(f.Name(), -1, g); err != nil {
    74  			t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
    75  		}
    76  		checkUidGid(t, f.Name(), int(sys.Uid), g)
    77  
    78  		// change back to gid to test fd.Chown
    79  		if err = f.Chown(-1, gid); err != nil {
    80  			t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
    81  		}
    82  		checkUidGid(t, f.Name(), int(sys.Uid), gid)
    83  	}
    84  }
    85  
    86  func TestFileChown(t *testing.T) {
    87  	// Fchown is not supported under windows or Plan 9.
    88  	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
    89  		t.Skipf("%s does not support syscall.Fchown", runtime.GOOS)
    90  	}
    91  	// Use TempDir() to make sure we're on a local file system,
    92  	// so that the group ids returned by Getgroups will be allowed
    93  	// on the file. On NFS, the Getgroups groups are
    94  	// basically useless.
    95  	f := newFile("TestFileChown", t)
    96  	defer Remove(f.Name())
    97  	defer f.Close()
    98  	dir, err := f.Stat()
    99  	if err != nil {
   100  		t.Fatalf("stat %s: %s", f.Name(), err)
   101  	}
   102  
   103  	// Can't change uid unless root, but can try
   104  	// changing the group id. First try our current group.
   105  	gid := Getgid()
   106  	t.Log("gid:", gid)
   107  	if err = f.Chown(-1, gid); err != nil {
   108  		t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
   109  	}
   110  	sys := dir.Sys().(*syscall.Stat_t)
   111  	checkUidGid(t, f.Name(), int(sys.Uid), gid)
   112  
   113  	// Then try all the auxiliary groups.
   114  	groups, err := Getgroups()
   115  	if err != nil {
   116  		t.Fatalf("getgroups: %s", err)
   117  	}
   118  	t.Log("groups: ", groups)
   119  	for _, g := range groups {
   120  		if err = f.Chown(-1, g); err != nil {
   121  			t.Fatalf("fchown %s -1 %d: %s", f.Name(), g, err)
   122  		}
   123  		checkUidGid(t, f.Name(), int(sys.Uid), g)
   124  
   125  		// change back to gid to test fd.Chown
   126  		if err = f.Chown(-1, gid); err != nil {
   127  			t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
   128  		}
   129  		checkUidGid(t, f.Name(), int(sys.Uid), gid)
   130  	}
   131  }
   132  
   133  func TestLchown(t *testing.T) {
   134  	// Lchown is not supported under windows or Plan 9.
   135  	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
   136  		t.Skipf("%s does not support syscall.Lchown", runtime.GOOS)
   137  	}
   138  	// Use TempDir() to make sure we're on a local file system,
   139  	// so that the group ids returned by Getgroups will be allowed
   140  	// on the file. On NFS, the Getgroups groups are
   141  	// basically useless.
   142  	f := newFile("TestLchown", t)
   143  	defer Remove(f.Name())
   144  	defer f.Close()
   145  	dir, err := f.Stat()
   146  	if err != nil {
   147  		t.Fatalf("stat %s: %s", f.Name(), err)
   148  	}
   149  
   150  	linkname := f.Name() + "2"
   151  	if err := Symlink(f.Name(), linkname); err != nil {
   152  		if runtime.GOOS == "android" && IsPermission(err) {
   153  			t.Skip("skipping test on Android; permission error creating symlink")
   154  		}
   155  		t.Fatalf("link %s -> %s: %v", f.Name(), linkname, err)
   156  	}
   157  	defer Remove(linkname)
   158  
   159  	// Can't change uid unless root, but can try
   160  	// changing the group id. First try our current group.
   161  	gid := Getgid()
   162  	t.Log("gid:", gid)
   163  	if err = Lchown(linkname, -1, gid); err != nil {
   164  		t.Fatalf("lchown %s -1 %d: %s", linkname, gid, err)
   165  	}
   166  	sys := dir.Sys().(*syscall.Stat_t)
   167  	checkUidGid(t, linkname, int(sys.Uid), gid)
   168  
   169  	// Then try all the auxiliary groups.
   170  	groups, err := Getgroups()
   171  	if err != nil {
   172  		t.Fatalf("getgroups: %s", err)
   173  	}
   174  	t.Log("groups: ", groups)
   175  	for _, g := range groups {
   176  		if err = Lchown(linkname, -1, g); err != nil {
   177  			t.Fatalf("lchown %s -1 %d: %s", linkname, g, err)
   178  		}
   179  		checkUidGid(t, linkname, int(sys.Uid), g)
   180  
   181  		// Check that link target's gid is unchanged.
   182  		checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
   183  	}
   184  }
   185  
   186  // Issue 16919: Readdir must return a non-empty slice or an error.
   187  func TestReaddirRemoveRace(t *testing.T) {
   188  	oldStat := *LstatP
   189  	defer func() { *LstatP = oldStat }()
   190  	*LstatP = func(name string) (FileInfo, error) {
   191  		if strings.HasSuffix(name, "some-file") {
   192  			// Act like it's been deleted.
   193  			return nil, ErrNotExist
   194  		}
   195  		return oldStat(name)
   196  	}
   197  	dir := newDir("TestReaddirRemoveRace", t)
   198  	defer RemoveAll(dir)
   199  	if err := ioutil.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	d, err := Open(dir)
   203  	if err != nil {
   204  		t.Fatal(err)
   205  	}
   206  	defer d.Close()
   207  	fis, err := d.Readdir(2) // notably, greater than zero
   208  	if len(fis) == 0 && err == nil {
   209  		// This is what used to happen (Issue 16919)
   210  		t.Fatal("Readdir = empty slice & err == nil")
   211  	}
   212  	if len(fis) != 0 || err != io.EOF {
   213  		t.Errorf("Readdir = %d entries: %v; want 0, io.EOF", len(fis), err)
   214  		for i, fi := range fis {
   215  			t.Errorf("  entry[%d]: %q, %v", i, fi.Name(), fi.Mode())
   216  		}
   217  		t.FailNow()
   218  	}
   219  }