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 	// Use TempDir() to make sure we're on a local file system,
     40 	// so that the group ids returned by Getgroups will be allowed
     41 	// on the file. On NFS, the Getgroups groups are
     42 	// basically useless.
     43 	f := newFile("TestChown", t)
     44 	defer Remove(f.Name())
     45 	defer f.Close()
     46 	dir, err := f.Stat()
     47 	if err != nil {
     48 		t.Fatalf("stat %s: %s", f.Name(), err)
     49 	}
     50 
     51 	// Can't change uid unless root, but can try
     52 	// changing the group id. First try our current group.
     53 	gid := Getgid()
     54 	t.Log("gid:", gid)
     55 	if err = Chown(f.Name(), -1, gid); err != nil {
     56 		t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
     57 	}
     58 	sys := dir.Sys().(*syscall.Stat_t)
     59 	checkUidGid(t, f.Name(), int(sys.Uid), gid)
     60 
     61 	// Then try all the auxiliary groups.
     62 	groups, err := Getgroups()
     63 	if err != nil {
     64 		t.Fatalf("getgroups: %s", err)
     65 	}
     66 	t.Log("groups: ", groups)
     67 	for _, g := range groups {
     68 		if err = Chown(f.Name(), -1, g); err != nil {
     69 			t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
     70 		}
     71 		checkUidGid(t, f.Name(), int(sys.Uid), g)
     72 
     73 		// change back to gid to test fd.Chown
     74 		if err = f.Chown(-1, gid); err != nil {
     75 			t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
     76 		}
     77 		checkUidGid(t, f.Name(), int(sys.Uid), gid)
     78 	}
     79 }
     80 
     81 func TestFileChown(t *testing.T) {
     82 	// Use TempDir() to make sure we're on a local file system,
     83 	// so that the group ids returned by Getgroups will be allowed
     84 	// on the file. On NFS, the Getgroups groups are
     85 	// basically useless.
     86 	f := newFile("TestFileChown", t)
     87 	defer Remove(f.Name())
     88 	defer f.Close()
     89 	dir, err := f.Stat()
     90 	if err != nil {
     91 		t.Fatalf("stat %s: %s", f.Name(), err)
     92 	}
     93 
     94 	// Can't change uid unless root, but can try
     95 	// changing the group id. First try our current group.
     96 	gid := Getgid()
     97 	t.Log("gid:", gid)
     98 	if err = f.Chown(-1, gid); err != nil {
     99 		t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
    100 	}
    101 	sys := dir.Sys().(*syscall.Stat_t)
    102 	checkUidGid(t, f.Name(), int(sys.Uid), gid)
    103 
    104 	// Then try all the auxiliary groups.
    105 	groups, err := Getgroups()
    106 	if err != nil {
    107 		t.Fatalf("getgroups: %s", err)
    108 	}
    109 	t.Log("groups: ", groups)
    110 	for _, g := range groups {
    111 		if err = f.Chown(-1, g); err != nil {
    112 			t.Fatalf("fchown %s -1 %d: %s", f.Name(), g, err)
    113 		}
    114 		checkUidGid(t, f.Name(), int(sys.Uid), g)
    115 
    116 		// change back to gid to test fd.Chown
    117 		if err = f.Chown(-1, gid); err != nil {
    118 			t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
    119 		}
    120 		checkUidGid(t, f.Name(), int(sys.Uid), gid)
    121 	}
    122 }
    123 
    124 func TestLchown(t *testing.T) {
    125 	// Use TempDir() to make sure we're on a local file system,
    126 	// so that the group ids returned by Getgroups will be allowed
    127 	// on the file. On NFS, the Getgroups groups are
    128 	// basically useless.
    129 	f := newFile("TestLchown", t)
    130 	defer Remove(f.Name())
    131 	defer f.Close()
    132 	dir, err := f.Stat()
    133 	if err != nil {
    134 		t.Fatalf("stat %s: %s", f.Name(), err)
    135 	}
    136 
    137 	linkname := f.Name() + "2"
    138 	if err := Symlink(f.Name(), linkname); err != nil {
    139 		if runtime.GOOS == "android" && IsPermission(err) {
    140 			t.Skip("skipping test on Android; permission error creating symlink")
    141 		}
    142 		t.Fatalf("link %s -> %s: %v", f.Name(), linkname, err)
    143 	}
    144 	defer Remove(linkname)
    145 
    146 	// Can't change uid unless root, but can try
    147 	// changing the group id. First try our current group.
    148 	gid := Getgid()
    149 	t.Log("gid:", gid)
    150 	if err = Lchown(linkname, -1, gid); err != nil {
    151 		t.Fatalf("lchown %s -1 %d: %s", linkname, gid, err)
    152 	}
    153 	sys := dir.Sys().(*syscall.Stat_t)
    154 	checkUidGid(t, linkname, int(sys.Uid), gid)
    155 
    156 	// Then try all the auxiliary groups.
    157 	groups, err := Getgroups()
    158 	if err != nil {
    159 		t.Fatalf("getgroups: %s", err)
    160 	}
    161 	t.Log("groups: ", groups)
    162 	for _, g := range groups {
    163 		if err = Lchown(linkname, -1, g); err != nil {
    164 			t.Fatalf("lchown %s -1 %d: %s", linkname, g, err)
    165 		}
    166 		checkUidGid(t, linkname, int(sys.Uid), g)
    167 
    168 		// Check that link target's gid is unchanged.
    169 		checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
    170 	}
    171 }
    172 
    173 // Issue 16919: Readdir must return a non-empty slice or an error.
    174 func TestReaddirRemoveRace(t *testing.T) {
    175 	oldStat := *LstatP
    176 	defer func() { *LstatP = oldStat }()
    177 	*LstatP = func(name string) (FileInfo, error) {
    178 		if strings.HasSuffix(name, "some-file") {
    179 			// Act like it's been deleted.
    180 			return nil, ErrNotExist
    181 		}
    182 		return oldStat(name)
    183 	}
    184 	dir := newDir("TestReaddirRemoveRace", t)
    185 	defer RemoveAll(dir)
    186 	if err := ioutil.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
    187 		t.Fatal(err)
    188 	}
    189 	d, err := Open(dir)
    190 	if err != nil {
    191 		t.Fatal(err)
    192 	}
    193 	defer d.Close()
    194 	fis, err := d.Readdir(2) // notably, greater than zero
    195 	if len(fis) == 0 && err == nil {
    196 		// This is what used to happen (Issue 16919)
    197 		t.Fatal("Readdir = empty slice & err == nil")
    198 	}
    199 	if len(fis) != 0 || err != io.EOF {
    200 		t.Errorf("Readdir = %d entries: %v; want 0, io.EOF", len(fis), err)
    201 		for i, fi := range fis {
    202 			t.Errorf("  entry[%d]: %q, %v", i, fi.Name(), fi.Mode())
    203 		}
    204 		t.FailNow()
    205 	}
    206 }
    207