"errors"
        "fmt"
        "internal/godebug"
+       "io"
        "io/fs"
        "log"
        "os"
        return openFile(path, os.O_RDONLY, 0)
 }
 
-// OpenFile opens the file at or overlaid on the given path with the flag and perm.
-func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
-       Trace("OpenFile", path)
-       return openFile(path, flag, perm)
-}
-
 func openFile(path string, flag int, perm os.FileMode) (*os.File, error) {
        cpath := canonicalize(path)
        if node, ok := overlay[cpath]; ok {
        return os.OpenFile(cpath, flag, perm)
 }
 
+// ReadFile reads the file at or overlaid on the given path.
+func ReadFile(path string) ([]byte, error) {
+       f, err := Open(path)
+       if err != nil {
+               return nil, err
+       }
+       defer f.Close()
+
+       return io.ReadAll(f)
+}
+
 // IsDirWithGoFiles reports whether dir is a directory containing Go files
 // either on disk or in the overlay.
 func IsDirWithGoFiles(dir string) (bool, error) {
 
 // operate in workspace mode. It should not be called by other commands,
 // for example 'go mod tidy', that don't operate in workspace mode.
 func InitWorkfile() {
+       // Initialize fsys early because we need overlay to read go.work file.
+       if err := fsys.Init(base.Cwd()); err != nil {
+               base.Fatal(err)
+       }
        workFilePath = FindGoWork(base.Cwd())
 }
 
 
 // ReadWorkFile reads and parses the go.work file at the given path.
 func ReadWorkFile(path string) (*modfile.WorkFile, error) {
-       workData, err := os.ReadFile(path)
+       path = base.ShortPath(path) // use short path in any errors
+       workData, err := fsys.ReadFile(path)
        if err != nil {
                return nil, err
        }
 
--- /dev/null
+# Test that overlays are respected when opening go.work files.
+
+# go.work in overlay, but not on disk.
+go list -overlay=overlay.json -m
+stdout example.com/a
+stdout example.com/b
+! stdout example.com/c
+
+# control case for go.work on disk and in overlay:
+# go.work is on disk but not in overlay.
+cp go.work.non-overlay go.work
+go list -m
+stdout example.com/a
+stdout example.com/b
+stdout example.com/c
+
+# go.work on disk and in overlay.
+go list -overlay=overlay.json -m
+stdout example.com/a
+stdout example.com/b
+! stdout example.com/c
+
+-- overlay.json --
+{"Replace": {"go.work": "overlaywork"}}
+-- overlaywork --
+use (
+    ./a
+    ./b
+)
+-- go.work.non-overlay --
+use (
+    ./a
+    ./b
+    ./c
+)
+-- a/go.mod --
+module example.com/a
+-- b/go.mod --
+module example.com/b
+-- c/go.mod --
+module example.com/c