Source file
src/os/os_unix_test.go
Documentation: os
1
2
3
4
5
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
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
49
50
51
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
61
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
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 if testenv.SyscallIsNotSupported(err) {
79 t.Logf("chown %s -1 %d: %s (error ignored)", f.Name(), g, err)
80
81 checkUidGid(t, f.Name(), int(sys.Uid), gid)
82 continue
83 }
84 t.Fatalf("chown %s -1 %d: %s", f.Name(), g, err)
85 }
86 checkUidGid(t, f.Name(), int(sys.Uid), g)
87
88
89 if err = f.Chown(-1, gid); err != nil {
90 t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
91 }
92 checkUidGid(t, f.Name(), int(sys.Uid), gid)
93 }
94 }
95
96 func TestFileChown(t *testing.T) {
97 if runtime.GOOS == "wasip1" {
98 t.Skip("file ownership not supported on " + runtime.GOOS)
99 }
100 t.Parallel()
101
102
103
104
105
106 f := newFile("TestFileChown", t)
107 defer Remove(f.Name())
108 defer f.Close()
109 dir, err := f.Stat()
110 if err != nil {
111 t.Fatalf("stat %s: %s", f.Name(), err)
112 }
113
114
115
116 gid := Getgid()
117 t.Log("gid:", gid)
118 if err = f.Chown(-1, gid); err != nil {
119 t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
120 }
121 sys := dir.Sys().(*syscall.Stat_t)
122 checkUidGid(t, f.Name(), int(sys.Uid), gid)
123
124
125 groups, err := Getgroups()
126 if err != nil {
127 t.Fatalf("getgroups: %s", err)
128 }
129 t.Log("groups: ", groups)
130 for _, g := range groups {
131 if err = f.Chown(-1, g); err != nil {
132 if testenv.SyscallIsNotSupported(err) {
133 t.Logf("chown %s -1 %d: %s (error ignored)", f.Name(), g, err)
134
135 checkUidGid(t, f.Name(), int(sys.Uid), gid)
136 continue
137 }
138 t.Fatalf("fchown %s -1 %d: %s", f.Name(), g, err)
139 }
140 checkUidGid(t, f.Name(), int(sys.Uid), g)
141
142
143 if err = f.Chown(-1, gid); err != nil {
144 t.Fatalf("fchown %s -1 %d: %s", f.Name(), gid, err)
145 }
146 checkUidGid(t, f.Name(), int(sys.Uid), gid)
147 }
148 }
149
150 func TestLchown(t *testing.T) {
151 testenv.MustHaveSymlink(t)
152 t.Parallel()
153
154
155
156
157
158 f := newFile("TestLchown", t)
159 defer Remove(f.Name())
160 defer f.Close()
161 dir, err := f.Stat()
162 if err != nil {
163 t.Fatalf("stat %s: %s", f.Name(), err)
164 }
165
166 linkname := f.Name() + "2"
167 if err := Symlink(f.Name(), linkname); err != nil {
168 if runtime.GOOS == "android" && IsPermission(err) {
169 t.Skip("skipping test on Android; permission error creating symlink")
170 }
171 t.Fatalf("link %s -> %s: %v", f.Name(), linkname, err)
172 }
173 defer Remove(linkname)
174
175
176
177 gid := Getgid()
178 t.Log("gid:", gid)
179 if err = Lchown(linkname, -1, gid); err != nil {
180 if err, ok := err.(*PathError); ok && err.Err == syscall.ENOSYS {
181 t.Skip("lchown is unavailable")
182 }
183 t.Fatalf("lchown %s -1 %d: %s", linkname, gid, err)
184 }
185 sys := dir.Sys().(*syscall.Stat_t)
186 checkUidGid(t, linkname, int(sys.Uid), gid)
187
188
189 groups, err := Getgroups()
190 if err != nil {
191 t.Fatalf("getgroups: %s", err)
192 }
193 t.Log("groups: ", groups)
194 for _, g := range groups {
195 if err = Lchown(linkname, -1, g); err != nil {
196 if testenv.SyscallIsNotSupported(err) {
197 t.Logf("lchown %s -1 %d: %s (error ignored)", f.Name(), g, err)
198
199 checkUidGid(t, f.Name(), int(sys.Uid), gid)
200 continue
201 }
202 t.Fatalf("lchown %s -1 %d: %s", linkname, g, err)
203 }
204 checkUidGid(t, linkname, int(sys.Uid), g)
205
206
207 checkUidGid(t, f.Name(), int(sys.Uid), int(sys.Gid))
208
209 if err = Lchown(linkname, -1, gid); err != nil {
210 t.Fatalf("lchown %s -1 %d: %s", f.Name(), gid, err)
211 }
212 }
213 }
214
215
216 func TestReaddirRemoveRace(t *testing.T) {
217 oldStat := *LstatP
218 defer func() { *LstatP = oldStat }()
219 *LstatP = func(name string) (FileInfo, error) {
220 if strings.HasSuffix(name, "some-file") {
221
222 return nil, ErrNotExist
223 }
224 return oldStat(name)
225 }
226 dir := newDir("TestReaddirRemoveRace", t)
227 defer RemoveAll(dir)
228 if err := WriteFile(filepath.Join(dir, "some-file"), []byte("hello"), 0644); err != nil {
229 t.Fatal(err)
230 }
231 d, err := Open(dir)
232 if err != nil {
233 t.Fatal(err)
234 }
235 defer d.Close()
236 fis, err := d.Readdir(2)
237 if len(fis) == 0 && err == nil {
238
239 t.Fatal("Readdir = empty slice & err == nil")
240 }
241 if len(fis) != 0 || err != io.EOF {
242 t.Errorf("Readdir = %d entries: %v; want 0, io.EOF", len(fis), err)
243 for i, fi := range fis {
244 t.Errorf(" entry[%d]: %q, %v", i, fi.Name(), fi.Mode())
245 }
246 t.FailNow()
247 }
248 }
249
250
251 func TestMkdirStickyUmask(t *testing.T) {
252 if runtime.GOOS == "wasip1" {
253 t.Skip("file permissions not supported on " + runtime.GOOS)
254 }
255 t.Parallel()
256
257 const umask = 0077
258 dir := newDir("TestMkdirStickyUmask", t)
259 defer RemoveAll(dir)
260
261 oldUmask := syscall.Umask(umask)
262 defer syscall.Umask(oldUmask)
263
264
265
266
267
268 control := filepath.Join(dir, "control")
269 if err := Mkdir(control, 0755); err != nil {
270 t.Fatal(err)
271 }
272 cfi, err := Stat(control)
273 if err != nil {
274 t.Fatal(err)
275 }
276
277 p := filepath.Join(dir, "dir1")
278 if err := Mkdir(p, ModeSticky|0755); err != nil {
279 t.Fatal(err)
280 }
281 fi, err := Stat(p)
282 if err != nil {
283 t.Fatal(err)
284 }
285
286 got := fi.Mode()
287 want := cfi.Mode() | ModeSticky
288 if got != want {
289 t.Errorf("Mkdir(_, ModeSticky|0755) created dir with mode %v; want %v", got, want)
290 }
291 }
292
293
294 func newFileTest(t *testing.T, blocking bool) {
295 if runtime.GOOS == "js" || runtime.GOOS == "wasip1" {
296 t.Skipf("syscall.Pipe is not available on %s.", runtime.GOOS)
297 }
298
299 p := make([]int, 2)
300 if err := syscall.Pipe(p); err != nil {
301 t.Fatalf("pipe: %v", err)
302 }
303 defer syscall.Close(p[1])
304
305
306 if !blocking {
307 if err := syscall.SetNonblock(p[0], true); err != nil {
308 syscall.Close(p[0])
309 t.Fatalf("SetNonblock: %v", err)
310 }
311 }
312
313 file := NewFile(uintptr(p[0]), "notapipe")
314 if file == nil {
315 syscall.Close(p[0])
316 t.Fatalf("failed to convert fd to file!")
317 }
318 defer file.Close()
319
320 timeToWrite := 100 * time.Millisecond
321 timeToDeadline := 1 * time.Millisecond
322 if !blocking {
323
324
325 timeToWrite = 1 * time.Second
326 }
327
328
329 b := make([]byte, 1)
330 timer := time.AfterFunc(timeToWrite, func() { syscall.Write(p[1], []byte("a")) })
331 defer timer.Stop()
332 file.SetReadDeadline(time.Now().Add(timeToDeadline))
333 _, err := file.Read(b)
334 if !blocking {
335
336 if !isDeadlineExceeded(err) {
337 t.Fatalf("No timeout reading from file: %v", err)
338 }
339 } else {
340
341 if err != nil {
342 t.Fatalf("Error reading from file: %v", err)
343 }
344 }
345 }
346
347 func TestNewFileBlock(t *testing.T) {
348 t.Parallel()
349 newFileTest(t, true)
350 }
351
352 func TestNewFileNonBlock(t *testing.T) {
353 t.Parallel()
354 newFileTest(t, false)
355 }
356
357 func TestNewFileInvalid(t *testing.T) {
358 t.Parallel()
359 const negOne = ^uintptr(0)
360 if f := NewFile(negOne, "invalid"); f != nil {
361 t.Errorf("NewFile(-1) got %v want nil", f)
362 }
363 }
364
365 func TestSplitPath(t *testing.T) {
366 t.Parallel()
367 for _, tt := range []struct{ path, wantDir, wantBase string }{
368 {"a", ".", "a"},
369 {"a/", ".", "a"},
370 {"a//", ".", "a"},
371 {"a/b", "a", "b"},
372 {"a/b/", "a", "b"},
373 {"a/b/c", "a/b", "c"},
374 {"/a", "/", "a"},
375 {"/a/", "/", "a"},
376 {"/a/b", "/a", "b"},
377 {"/a/b/", "/a", "b"},
378 {"/a/b/c", "/a/b", "c"},
379 {"//a", "/", "a"},
380 {"//a/", "/", "a"},
381 {"///a", "/", "a"},
382 {"///a/", "/", "a"},
383 } {
384 if dir, base := SplitPath(tt.path); dir != tt.wantDir || base != tt.wantBase {
385 t.Errorf("splitPath(%q) = %q, %q, want %q, %q", tt.path, dir, base, tt.wantDir, tt.wantBase)
386 }
387 }
388 }
389
390
391
392
393
394 func TestIssue60181(t *testing.T) {
395 defer chtmpdir(t)()
396
397 want := "hello gopher"
398
399 a, err := CreateTemp("", "a")
400 if err != nil {
401 t.Fatal(err)
402 }
403 a.WriteString(want[:5])
404 a.Close()
405
406 b, err := CreateTemp("", "b")
407 if err != nil {
408 t.Fatal(err)
409 }
410 b.WriteString(want[5:])
411 b.Close()
412
413 afd, err := syscall.Open(a.Name(), syscall.O_RDWR|syscall.O_APPEND, 0)
414 if err != nil {
415 t.Fatal(err)
416 }
417
418 bfd, err := syscall.Open(b.Name(), syscall.O_RDONLY, 0)
419 if err != nil {
420 t.Fatal(err)
421 }
422
423 aa := NewFile(uintptr(afd), a.Name())
424 defer aa.Close()
425 bb := NewFile(uintptr(bfd), b.Name())
426 defer bb.Close()
427
428
429
430
431 _, err = io.Copy(aa, bb)
432 if err != nil {
433 t.Fatal(err)
434 }
435
436 buf, err := ReadFile(aa.Name())
437 if err != nil {
438 t.Fatal(err)
439 }
440
441 if got := string(buf); got != want {
442 t.Errorf("files not concatenated: got %q, want %q", got, want)
443 }
444 }
445
View as plain text