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