github.com/c12o16h1/go/src@v0.0.0-20200114212001-5a151c0f00ed/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 aix darwin dragonfly freebsd js,wasm 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 "time" 19 ) 20 21 func init() { 22 isReadonlyError = func(err error) bool { return err == syscall.EROFS } 23 } 24 25 // For TestRawConnReadWrite. 26 type syscallDescriptor = int 27 28 func checkUidGid(t *testing.T, path string, uid, gid int) { 29 dir, err := Lstat(path) 30 if err != nil { 31 t.Fatalf("Lstat %q (looking for uid/gid %d/%d): %s", path, uid, gid, err) 32 } 33 sys := dir.Sys().(*syscall.Stat_t) 34 if int(sys.Uid) != uid { 35 t.Errorf("Lstat %q: uid %d want %d", path, sys.Uid, uid) 36 } 37 if int(sys.Gid) != gid { 38 t.Errorf("Lstat %q: gid %d want %d", path, sys.Gid, gid) 39 } 40 } 41 42 func TestChown(t *testing.T) { 43 // Use TempDir() to make sure we're on a local file system, 44 // so that the group ids returned by Getgroups will be allowed 45 // on the file. On NFS, the Getgroups groups are 46 // basically useless. 47 f := newFile("TestChown", t) 48 defer Remove(f.Name()) 49 defer f.Close() 50 dir, err := f.Stat() 51 if err != nil { 52 t.Fatalf("stat %s: %s", f.Name(), err) 53 } 54 55 // Can't change uid unless root, but can try 56 // changing the group id. First try our current group. 57 gid := Getgid() 58 t.Log("gid:", gid) 59 if err = Chown(f.Name(), -1, gid); err != nil { 60 t.Fatalf("chown %s -1 %d: %s", f.Name(), gid, err) 61 } 62 sys := dir.Sys().(*syscall.Stat_t) 63 checkUidGid(t, f.Name(), int(sys.Uid), gid) 64 65 // Then try all the auxiliary groups. 66 groups, err := Getgroups() 67 if err != nil { 68 t.Fatalf("getgroups: %s", err) 69 } 70 t.Log("groups: ", groups) 71 for _, g := range groups { 72 if err = Chown(f.Name(), -1, g); err != nil { 73 t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err) 74 } 75 checkUidGid(t, f.Name(), int(sys.Uid), g) 76 77 // change back to gid to test fd.Chown 78 if err = f.Chown(-1, gid); err != nil { 79 t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err) 80 } 81 checkUidGid(t, f.Name(), int(sys.Uid), gid) 82 } 83 } 84 85 func TestFileChown(t *testing.T) { 86 // Use TempDir() to make sure we're on a local file system, 87 // so that the group ids returned by Getgroups will be allowed 88 // on the file. On NFS, the Getgroups groups are 89 // basically useless. 90 f := newFile("TestFileChown", t) 91 defer Remove(f.Name()) 92 defer f.Close() 93 dir, err := f.Stat() 94 if err != nil { 95 t.Fatalf("stat %s: %s", f.Name(), err) 96 } 97 98 // Can't change uid unless root, but can try 99 // changing the group id. First try our current group. 100 gid := Getgid() 101 t.Log("gid:", gid) 102 if err = f.Chown(-1, gid); err != nil { 103 t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err) 104 } 105 sys := dir.Sys().(*syscall.Stat_t) 106 checkUidGid(t, f.Name(), int(sys.Uid), gid) 107 108 // Then try all the auxiliary groups. 109 groups, err := Getgroups() 110 if err != nil { 111 t.Fatalf("getgroups: %s", err) 112 } 113 t.Log("groups: ", groups) 114 for _, g := range groups { 115 if err = f.Chown(-1, g); err != nil { 116 t.Fatalf("fchown %s -1 %d: %s", f.Name(), g, err) 117 } 118 checkUidGid(t, f.Name(), int(sys.Uid), g) 119 120 // change back to gid to test fd.Chown 121 if err = f.Chown(-1, gid); err != nil { 122 t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err) 123 } 124 checkUidGid(t, f.Name(), int(sys.Uid), gid) 125 } 126 } 127 128 func TestLchown(t *testing.T) { 129 // Use TempDir() to make sure we're on a local file system, 130 // so that the group ids returned by Getgroups will be allowed 131 // on the file. On NFS, the Getgroups groups are 132 // basically useless. 133 f := newFile("TestLchown", t) 134 defer Remove(f.Name()) 135 defer f.Close() 136 dir, err := f.Stat() 137 if err != nil { 138 t.Fatalf("stat %s: %s", f.Name(), err) 139 } 140 141 linkname := f.Name() + "2" 142 if err := Symlink(f.Name(), linkname); err != nil { 143 if runtime.GOOS == "android" && IsPermission(err) { 144 t.Skip("skipping test on Android; permission error creating symlink") 145 } 146 t.Fatalf("link %s -> %s: %v", f.Name(), linkname, err) 147 } 148 defer Remove(linkname) 149 150 // Can't change uid unless root, but can try 151 // changing the group id. First try our current group. 152 gid := Getgid() 153 t.Log("gid:", gid) 154 if err = Lchown(linkname, -1, gid); err != nil { 155 if err, ok := err.(*PathError); ok && err.Err == syscall.ENOSYS { 156 t.Skip("lchown is unavailable") 157 } 158 t.Fatalf("lchown %s -1 %d: %s", linkname, gid, err) 159 } 160 sys := dir.Sys().(*syscall.Stat_t) 161 checkUidGid(t, linkname, int(sys.Uid), gid) 162 163 // Then try all the auxiliary groups. 164 groups, err := Getgroups() 165 if err != nil { 166 t.Fatalf("getgroups: %s", err) 167 } 168 t.Log("groups: ", groups) 169 for _, g := range groups { 170 if err = Lchown(linkname, -1, g); err != nil { 171 t.Fatalf("lchown %s -1 %d: %s", linkname, g, err) 172 } 173 checkUidGid(t, linkname, int(sys.Uid), g) 174 175 // Check that link target's gid is unchanged. 176 checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid)) 177 } 178 } 179 180 // Issue 16919: Readdir must return a non-empty slice or an error. 181 func TestReaddirRemoveRace(t *testing.T) { 182 oldStat := *LstatP 183 defer func() { *LstatP = oldStat }() 184 *LstatP = func(name string) (FileInfo, error) { 185 if strings.HasSuffix(name, "some-file") { 186 // Act like it's been deleted. 187 return nil, ErrNotExist 188 } 189 return oldStat(name) 190 } 191 dir := newDir("TestReaddirRemoveRace", t) 192 defer RemoveAll(dir) 193 if err := ioutil.WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil { 194 t.Fatal(err) 195 } 196 d, err := Open(dir) 197 if err != nil { 198 t.Fatal(err) 199 } 200 defer d.Close() 201 fis, err := d.Readdir(2) // notably, greater than zero 202 if len(fis) == 0 && err == nil { 203 // This is what used to happen (Issue 16919) 204 t.Fatal("Readdir = empty slice & err == nil") 205 } 206 if len(fis) != 0 || err != io.EOF { 207 t.Errorf("Readdir = %d entries: %v; want 0, io.EOF", len(fis), err) 208 for i, fi := range fis { 209 t.Errorf(" entry[%d]: %q, %v", i, fi.Name(), fi.Mode()) 210 } 211 t.FailNow() 212 } 213 } 214 215 // Issue 23120: respect umask when doing Mkdir with the sticky bit 216 func TestMkdirStickyUmask(t *testing.T) { 217 const umask = 0077 218 dir := newDir("TestMkdirStickyUmask", t) 219 defer RemoveAll(dir) 220 oldUmask := syscall.Umask(umask) 221 defer syscall.Umask(oldUmask) 222 p := filepath.Join(dir, "dir1") 223 if err := Mkdir(p, ModeSticky|0755); err != nil { 224 t.Fatal(err) 225 } 226 fi, err := Stat(p) 227 if err != nil { 228 t.Fatal(err) 229 } 230 if mode := fi.Mode(); (mode&umask) != 0 || (mode&^ModePerm) != (ModeDir|ModeSticky) { 231 t.Errorf("unexpected mode %s", mode) 232 } 233 } 234 235 // See also issues: 22939, 24331 236 func newFileTest(t *testing.T, blocking bool) { 237 if runtime.GOOS == "js" { 238 t.Skipf("syscall.Pipe is not available on %s.", runtime.GOOS) 239 } 240 241 p := make([]int, 2) 242 if err := syscall.Pipe(p); err != nil { 243 t.Fatalf("pipe: %v", err) 244 } 245 defer syscall.Close(p[1]) 246 247 // Set the read-side to non-blocking. 248 if !blocking { 249 if err := syscall.SetNonblock(p[0], true); err != nil { 250 syscall.Close(p[0]) 251 t.Fatalf("SetNonblock: %v", err) 252 } 253 } 254 // Convert it to a file. 255 file := NewFile(uintptr(p[0]), "notapipe") 256 if file == nil { 257 syscall.Close(p[0]) 258 t.Fatalf("failed to convert fd to file!") 259 } 260 defer file.Close() 261 262 timeToWrite := 100 * time.Millisecond 263 timeToDeadline := 1 * time.Millisecond 264 if !blocking { 265 // Use a longer time to avoid flakes. 266 // We won't be waiting this long anyhow. 267 timeToWrite = 1 * time.Second 268 } 269 270 // Try to read with deadline (but don't block forever). 271 b := make([]byte, 1) 272 timer := time.AfterFunc(timeToWrite, func() { syscall.Write(p[1], []byte("a")) }) 273 defer timer.Stop() 274 file.SetReadDeadline(time.Now().Add(timeToDeadline)) 275 _, err := file.Read(b) 276 if !blocking { 277 // We want it to fail with a timeout. 278 if !IsTimeout(err) { 279 t.Fatalf("No timeout reading from file: %v", err) 280 } 281 } else { 282 // We want it to succeed after 100ms 283 if err != nil { 284 t.Fatalf("Error reading from file: %v", err) 285 } 286 } 287 } 288 289 func TestNewFileBlock(t *testing.T) { 290 t.Parallel() 291 newFileTest(t, true) 292 } 293 294 func TestNewFileNonBlock(t *testing.T) { 295 t.Parallel() 296 newFileTest(t, false) 297 } 298 299 func TestSplitPath(t *testing.T) { 300 t.Parallel() 301 for _, tt := range []struct{ path, wantDir, wantBase string }{ 302 {"a", ".", "a"}, 303 {"a/", ".", "a"}, 304 {"a//", ".", "a"}, 305 {"a/b", "a", "b"}, 306 {"a/b/", "a", "b"}, 307 {"a/b/c", "a/b", "c"}, 308 {"/a", "/", "a"}, 309 {"/a/", "/", "a"}, 310 {"/a/b", "/a", "b"}, 311 {"/a/b/", "/a", "b"}, 312 {"/a/b/c", "/a/b", "c"}, 313 {"//a", "/", "a"}, 314 {"//a/", "/", "a"}, 315 {"///a", "/", "a"}, 316 {"///a/", "/", "a"}, 317 } { 318 if dir, base := SplitPath(tt.path); dir != tt.wantDir || base != tt.wantBase { 319 t.Errorf("splitPath(%q) = %q, %q, want %q, %q", tt.path, dir, base, tt.wantDir, tt.wantBase) 320 } 321 } 322 }