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