Home | History | Annotate | Download | only in os
      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 }
    220