cmd/golangorg: make zip contents seekable

https://golang.org/misc/cgo/stdio/testdata/fib.go fails right now
because the http.FileServer wants to use Seek to find out the file size.
Make that work by introducing an FS wrapper that enables seeking
in an in-memory copy of the file content.

Fixes golang/go#46809.

Change-Id: I353905310dc74594e54e0181dc821a97992b8da7
Reviewed-on: https://go-review.googlesource.com/c/website/+/329249
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Russ Cox <rsc@golang.org>
diff --git a/cmd/golangorg/server.go b/cmd/golangorg/server.go
index 182bd4a..d1e48f2 100644
--- a/cmd/golangorg/server.go
+++ b/cmd/golangorg/server.go
@@ -6,12 +6,14 @@
 package main
 
 import (
+	"bytes"
 	"context"
 	"encoding/json"
 	"flag"
 	"fmt"
 	"go/format"
 	"io"
+	"io/ioutil"
 	"log"
 	"net/http"
 	"os"
@@ -125,8 +127,7 @@
 		if err != nil {
 			log.Fatal(err)
 		}
-		defer z.Close()
-		gorootFS = z
+		gorootFS = &seekableFS{z}
 	} else {
 		gorootFS = osfs.DirFS(goroot)
 	}
@@ -382,3 +383,47 @@
 	}
 	return nil, errOut
 }
+
+// A seekableFS is an FS wrapper that makes every file seekable
+// by reading it entirely into memory when it is opened and then
+// serving read operations (including seek) from the memory copy.
+type seekableFS struct {
+	fs fs.FS
+}
+
+func (s *seekableFS) Open(name string) (fs.File, error) {
+	f, err := s.fs.Open(name)
+	if err != nil {
+		return nil, err
+	}
+	info, err := f.Stat()
+	if err != nil {
+		f.Close()
+		return nil, err
+	}
+	if info.IsDir() {
+		return f, nil
+	}
+	data, err := ioutil.ReadAll(f)
+	if err != nil {
+		f.Close()
+		return nil, err
+	}
+	var sf seekableFile
+	sf.File = f
+	sf.Reset(data)
+	return &sf, nil
+}
+
+// A seekableFile is a fs.File augmented by an in-memory copy of the file data to allow use of Seek.
+type seekableFile struct {
+	bytes.Reader
+	fs.File
+}
+
+// Read calls f.Reader.Read.
+// Both f.Reader and f.File have Read methods - a conflict - so f inherits neither.
+// This method calls the one we want.
+func (f *seekableFile) Read(b []byte) (int, error) {
+	return f.Reader.Read(b)
+}