syscall: implement wasip1 Fcntl

CL 494915 broke non-blocking I/O on wasip1 for files
opened with os.NewFile. This is fixed by providing an
implementation of fcntl(F_GETFL) for wasip1.

Change-Id: I78979076b95495fd4b94814552e5f5b043270cd0
Reviewed-on: https://go-review.googlesource.com/c/go/+/498195
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Achille Roussel <achille.roussel@gmail.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
diff --git a/src/internal/syscall/unix/fcntl_wasm.go b/src/internal/syscall/unix/fcntl_js.go
similarity index 91%
rename from src/internal/syscall/unix/fcntl_wasm.go
rename to src/internal/syscall/unix/fcntl_js.go
index c630273..bdfb8e0 100644
--- a/src/internal/syscall/unix/fcntl_wasm.go
+++ b/src/internal/syscall/unix/fcntl_js.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build js && wasm
+
 package unix
 
 import "syscall"
diff --git a/src/internal/syscall/unix/fcntl_wasm.go b/src/internal/syscall/unix/fcntl_wasip1.go
similarity index 69%
copy from src/internal/syscall/unix/fcntl_wasm.go
copy to src/internal/syscall/unix/fcntl_wasip1.go
index c630273..e70cd74 100644
--- a/src/internal/syscall/unix/fcntl_wasm.go
+++ b/src/internal/syscall/unix/fcntl_wasip1.go
@@ -2,10 +2,16 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:build wasip1
+
 package unix
 
 import "syscall"
 
 func Fcntl(fd int, cmd int, arg int) (int, error) {
+	if cmd == syscall.F_GETFL {
+		flags, err := fd_fdstat_get_flags(fd)
+		return int(flags), err
+	}
 	return 0, syscall.ENOSYS
 }
diff --git a/src/runtime/internal/wasitest/nonblock_test.go b/src/runtime/internal/wasitest/nonblock_test.go
index d873ef5..8fb2860 100644
--- a/src/runtime/internal/wasitest/nonblock_test.go
+++ b/src/runtime/internal/wasitest/nonblock_test.go
@@ -43,57 +43,61 @@
 		t.Skip("wasmer does not support non-blocking I/O")
 	}
 
-	args := []string{"run", "./testdata/nonblock.go"}
+	for _, mode := range []string{"os.OpenFile", "os.NewFile"} {
+		t.Run(mode, func(t *testing.T) {
+			args := []string{"run", "./testdata/nonblock.go", mode}
 
-	fifos := make([]*fifo, 8)
-	for i := range fifos {
-		path := filepath.Join(t.TempDir(), fmt.Sprintf("wasip1-nonblock-fifo-%d-%d", rand.Uint32(), i))
-		if err := syscall.Mkfifo(path, 0666); err != nil {
-			t.Fatal(err)
-		}
+			fifos := make([]*fifo, 8)
+			for i := range fifos {
+				path := filepath.Join(t.TempDir(), fmt.Sprintf("wasip1-nonblock-fifo-%d-%d", rand.Uint32(), i))
+				if err := syscall.Mkfifo(path, 0666); err != nil {
+					t.Fatal(err)
+				}
 
-		file, err := os.OpenFile(path, os.O_RDWR, 0)
-		if err != nil {
-			t.Fatal(err)
-		}
-		defer file.Close()
+				file, err := os.OpenFile(path, os.O_RDWR, 0)
+				if err != nil {
+					t.Fatal(err)
+				}
+				defer file.Close()
 
-		args = append(args, path)
-		fifos[len(fifos)-i-1] = &fifo{file, path}
-	}
+				args = append(args, path)
+				fifos[len(fifos)-i-1] = &fifo{file, path}
+			}
 
-	subProcess := exec.Command("go", args...)
+			subProcess := exec.Command("go", args...)
 
-	subProcess.Env = append(os.Environ(), "GOOS=wasip1", "GOARCH=wasm")
+			subProcess.Env = append(os.Environ(), "GOOS=wasip1", "GOARCH=wasm")
 
-	pr, pw := io.Pipe()
-	defer pw.Close()
+			pr, pw := io.Pipe()
+			defer pw.Close()
 
-	subProcess.Stderr = pw
+			subProcess.Stderr = pw
 
-	if err := subProcess.Start(); err != nil {
-		t.Fatal(err)
-	}
+			if err := subProcess.Start(); err != nil {
+				t.Fatal(err)
+			}
 
-	scanner := bufio.NewScanner(pr)
-	if !scanner.Scan() {
-		t.Fatal("expected line:", scanner.Err())
-	} else if scanner.Text() != "waiting" {
-		t.Fatal("unexpected output:", scanner.Text())
-	}
+			scanner := bufio.NewScanner(pr)
+			if !scanner.Scan() {
+				t.Fatal("expected line:", scanner.Err())
+			} else if scanner.Text() != "waiting" {
+				t.Fatal("unexpected output:", scanner.Text())
+			}
 
-	for _, fifo := range fifos {
-		if _, err := fifo.file.WriteString(fifo.path + "\n"); err != nil {
-			t.Fatal(err)
-		}
-		if !scanner.Scan() {
-			t.Fatal("expected line:", scanner.Err())
-		} else if scanner.Text() != fifo.path {
-			t.Fatal("unexpected line:", scanner.Text())
-		}
-	}
+			for _, fifo := range fifos {
+				if _, err := fifo.file.WriteString(fifo.path + "\n"); err != nil {
+					t.Fatal(err)
+				}
+				if !scanner.Scan() {
+					t.Fatal("expected line:", scanner.Err())
+				} else if scanner.Text() != fifo.path {
+					t.Fatal("unexpected line:", scanner.Text())
+				}
+			}
 
-	if err := subProcess.Wait(); err != nil {
-		t.Fatal(err)
+			if err := subProcess.Wait(); err != nil {
+				t.Fatal(err)
+			}
+		})
 	}
 }
diff --git a/src/runtime/internal/wasitest/testdata/nonblock.go b/src/runtime/internal/wasitest/testdata/nonblock.go
index 947abe7..8cbf21b 100644
--- a/src/runtime/internal/wasitest/testdata/nonblock.go
+++ b/src/runtime/internal/wasitest/testdata/nonblock.go
@@ -7,17 +7,34 @@
 import (
 	"os"
 	"sync"
+	"syscall"
 )
 
 func main() {
+	if len(os.Args) < 2 {
+		panic("usage: nonblock <MODE> [PATH...]")
+	}
+	mode := os.Args[1]
+
 	ready := make(chan struct{})
 
 	var wg sync.WaitGroup
-	for _, path := range os.Args[1:] {
+	for _, path := range os.Args[2:] {
 		f, err := os.Open(path)
 		if err != nil {
 			panic(err)
 		}
+		switch mode {
+		case "os.OpenFile":
+		case "os.NewFile":
+			fd := f.Fd()
+			if err := syscall.SetNonblock(int(fd), true); err != nil {
+				panic(err)
+			}
+			f = os.NewFile(fd, path)
+		default:
+			panic("invalid test mode")
+		}
 
 		spawnWait := make(chan struct{})