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