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
7package os_test
8
9import (
10	. "os"
11	"runtime"
12	"syscall"
13	"testing"
14)
15
16func init() {
17	isReadonlyError = func(err error) bool { return err == syscall.EROFS }
18}
19
20func checkUidGid(t *testing.T, path string, uid, gid int) {
21	dir, err := Lstat(path)
22	if err != nil {
23		t.Fatalf("Lstat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err)
24	}
25	sys := dir.Sys().(*syscall.Stat_t)
26	if int(sys.Uid) != uid {
27		t.Errorf("Lstat %q: uid %d want %d", path, sys.Uid, uid)
28	}
29	if int(sys.Gid) != gid {
30		t.Errorf("Lstat %q: gid %d want %d", path, sys.Gid, gid)
31	}
32}
33
34func TestChown(t *testing.T) {
35	// Chown is not supported under windows or Plan 9.
36	// Plan9 provides a native ChownPlan9 version instead.
37	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
38		t.Skipf("%s does not support syscall.Chown", runtime.GOOS)
39	}
40	// Use TempDir() to make sure we're on a local file system,
41	// so that the group ids returned by Getgroups will be allowed
42	// on the file.  On NFS, the Getgroups groups are
43	// basically useless.
44	f := newFile("TestChown", t)
45	defer Remove(f.Name())
46	defer f.Close()
47	dir, err := f.Stat()
48	if err != nil {
49		t.Fatalf("stat %s: %s", f.Name(), err)
50	}
51
52	// Can't change uid unless root, but can try
53	// changing the group id.  First try our current group.
54	gid := Getgid()
55	t.Log("gid:", gid)
56	if err = Chown(f.Name(), -1, gid); err != nil {
57		t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err)
58	}
59	sys := dir.Sys().(*syscall.Stat_t)
60	checkUidGid(t, f.Name(), int(sys.Uid), gid)
61
62	// Then try all the auxiliary groups.
63	groups, err := Getgroups()
64	if err != nil {
65		t.Fatalf("getgroups: %s", err)
66	}
67	t.Log("groups: ", groups)
68	for _, g := range groups {
69		if err = Chown(f.Name(), -1, g); err != nil {
70			t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
71		}
72		checkUidGid(t, f.Name(), int(sys.Uid), g)
73
74		// change back to gid to test fd.Chown
75		if err = f.Chown(-1, gid); err != nil {
76			t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
77		}
78		checkUidGid(t, f.Name(), int(sys.Uid), gid)
79	}
80}
81
82func TestFileChown(t *testing.T) {
83	// Fchown is not supported under windows or Plan 9.
84	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
85		t.Skipf("%s does not support syscall.Fchown", runtime.GOOS)
86	}
87	// Use TempDir() to make sure we're on a local file system,
88	// so that the group ids returned by Getgroups will be allowed
89	// on the file.  On NFS, the Getgroups groups are
90	// basically useless.
91	f := newFile("TestFileChown", t)
92	defer Remove(f.Name())
93	defer f.Close()
94	dir, err := f.Stat()
95	if err != nil {
96		t.Fatalf("stat %s: %s", f.Name(), err)
97	}
98
99	// Can't change uid unless root, but can try
100	// changing the group id.  First try our current group.
101	gid := Getgid()
102	t.Log("gid:", gid)
103	if err = f.Chown(-1, gid); err != nil {
104		t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
105	}
106	sys := dir.Sys().(*syscall.Stat_t)
107	checkUidGid(t, f.Name(), int(sys.Uid), gid)
108
109	// Then try all the auxiliary groups.
110	groups, err := Getgroups()
111	if err != nil {
112		t.Fatalf("getgroups: %s", err)
113	}
114	t.Log("groups: ", groups)
115	for _, g := range groups {
116		if err = f.Chown(-1, g); err != nil {
117			t.Fatalf("fchown %s -1 %d: %s", f.Name(), g, err)
118		}
119		checkUidGid(t, f.Name(), int(sys.Uid), g)
120
121		// change back to gid to test fd.Chown
122		if err = f.Chown(-1, gid); err != nil {
123			t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
124		}
125		checkUidGid(t, f.Name(), int(sys.Uid), gid)
126	}
127}
128
129func TestLchown(t *testing.T) {
130	// Lchown is not supported under windows or Plan 9.
131	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
132		t.Skipf("%s does not support syscall.Lchown", runtime.GOOS)
133	}
134	// Use TempDir() to make sure we're on a local file system,
135	// so that the group ids returned by Getgroups will be allowed
136	// on the file.  On NFS, the Getgroups groups are
137	// basically useless.
138	f := newFile("TestLchown", t)
139	defer Remove(f.Name())
140	defer f.Close()
141	dir, err := f.Stat()
142	if err != nil {
143		t.Fatalf("stat %s: %s", f.Name(), err)
144	}
145
146	linkname := f.Name() + "2"
147	if err := Symlink(f.Name(), linkname); err != nil {
148		t.Fatalf("link %s -> %s: %v", f.Name(), linkname, err)
149	}
150	defer Remove(linkname)
151
152	// Can't change uid unless root, but can try
153	// changing the group id.  First try our current group.
154	gid := Getgid()
155	t.Log("gid:", gid)
156	if err = Lchown(linkname, -1, gid); err != nil {
157		t.Fatalf("lchown %s -1 %d: %s", linkname, gid, err)
158	}
159	sys := dir.Sys().(*syscall.Stat_t)
160	checkUidGid(t, linkname, int(sys.Uid), gid)
161
162	// Then try all the auxiliary groups.
163	groups, err := Getgroups()
164	if err != nil {
165		t.Fatalf("getgroups: %s", err)
166	}
167	t.Log("groups: ", groups)
168	for _, g := range groups {
169		if err = Lchown(linkname, -1, g); err != nil {
170			t.Fatalf("lchown %s -1 %d: %s", linkname, g, err)
171		}
172		checkUidGid(t, linkname, int(sys.Uid), g)
173
174		// Check that link target's gid is unchanged.
175		checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
176	}
177}
178