]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: allow symlinks of non-directory files in embed
authorMichael Matloob <matloob@golang.org>
Thu, 16 Jan 2025 20:44:55 +0000 (15:44 -0500)
committerMichael Matloob <matloob@golang.org>
Tue, 11 Mar 2025 14:51:38 +0000 (07:51 -0700)
We previously disallowed all non-regular files being embedded. This CL
relaxes the restriction a little: if the GODEBUG embedfollowsymlinks=1
is set, we allow the leaf files being embedded (not the directories
containing them) to be symlinks. The files pointed to by the symlinks
must still be regular files.

This will be used when a Bazel build action executing the Go command is
running in a symlink-based sandbox. It's not something we want to enable
in general for now, so it's behind a GODEBUG.

Fixes #59924

Change-Id: I895be14c12de55b7d1b663d81bdda1df37d54804
Reviewed-on: https://go-review.googlesource.com/c/go/+/643215
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
doc/godebug.md
src/cmd/go/internal/load/pkg.go
src/cmd/go/testdata/script/embed.txt
src/internal/godebugs/table.go
src/runtime/metrics/doc.go

index 650a8e20bf7a809a424b492a4f417c10c0b1ccd1..f3ad820d3cddea879b43a97eb541113504b6cbf1 100644 (file)
@@ -164,6 +164,11 @@ reverts to the pre-Go 1.25 behavior. This setting is fixed at program startup
 time, and can't be modified by changing the `GODEBUG` environment variable
 after the program starts.
 
+Go 1.25 added a new `embedfollowsymlinks` setting that controls whether the
+Go command will follow symlinks to regular files embedding files.
+The default value `embedfollowsymlinks=0` does not allow following
+symlinks. `embedfollowsymlinks=1` will allow following symlinks.
+
 ### Go 1.24
 
 Go 1.24 added a new `fips140` setting that controls whether the Go
index 0c4639ce82c321d9692f2ab7282eb1498aefc7c9..8f62abe663260e37a1b592596d6d9e2d6e4d8dbe 100644 (file)
@@ -14,6 +14,7 @@ import (
        "go/build"
        "go/scanner"
        "go/token"
+       "internal/godebug"
        "internal/platform"
        "io/fs"
        "os"
@@ -2110,6 +2111,8 @@ func ResolveEmbed(dir string, patterns []string) ([]string, error) {
        return files, err
 }
 
+var embedfollowsymlinks = godebug.New("embedfollowsymlinks")
+
 // resolveEmbed resolves //go:embed patterns to precise file lists.
 // It sets files to the list of unique files matched (for go list),
 // and it sets pmap to the more precise mapping from
@@ -2194,6 +2197,24 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st
                                        list = append(list, rel)
                                }
 
+                       // If the embedfollowsymlinks GODEBUG is set to 1, allow the leaf file to be a
+                       // symlink (#59924). We don't allow directories to be symlinks and have already
+                       // checked that none of the parent directories of the file are symlinks in the
+                       // loop above. The file pointed to by the symlink must be a regular file.
+                       case embedfollowsymlinks.Value() == "1" && info.Mode()&fs.ModeType == fs.ModeSymlink:
+                               info, err := fsys.Stat(file)
+                               if err != nil {
+                                       return nil, nil, err
+                               }
+                               if !info.Mode().IsRegular() {
+                                       return nil, nil, fmt.Errorf("cannot embed irregular file %s", rel)
+                               }
+                               if have[rel] != pid {
+                                       embedfollowsymlinks.IncNonDefault()
+                                       have[rel] = pid
+                                       list = append(list, rel)
+                               }
+
                        case info.IsDir():
                                // Gather all files in the named directory, stopping at module boundaries
                                // and ignoring files that wouldn't be packaged into a module.
index 5f7f6edd77e7f9a4d8fee9c8b26ee278a4d04e8a..0e6bb63737e047d7a77416a6f2484ef2710469aa 100644 (file)
@@ -31,11 +31,16 @@ cp x.txt .git
 stderr '^x.go:5:12: pattern [*]t: cannot embed file [.]git: invalid name [.]git$'
 rm .git
 
-# build rejects symlinks
+# build rejects symlinks by default
 [symlink] symlink x.tzt -> x.txt
 [symlink] ! go build -x
 [symlink] stderr 'pattern [*]t: cannot embed irregular file x.tzt'
+# with GODEBUG embedfollowsymlinks=1, build allows symlinks of leaf files
+[symlink] env 'GODEBUG=embedfollowsymlinks=1'
+[symlink] go build -x
+[symlink] stderr 'x.tzt'
 [symlink] rm x.tzt
+[symlink] env 'GODEBUG='
 
 # build rejects empty directories
 mkdir t
@@ -72,6 +77,24 @@ rm t/.x.txt
 cp x.txt t/_x.txt
 go build -x
 
+# build disallows symlinks of directories
+[symlink] symlink symdir -> symdirdst
+[symlink] cp x.go4 x.go
+[symlink] ! go build -x
+[symlink] stderr 'x.go:5:12: pattern symdir/[*]: cannot embed file symdir[\\/]x.txt: in non-directory symdir'
+[symlink] cp x.go5 x.go
+[symlink] ! go build -x
+[symlink] stderr 'x.go:5:12: pattern symdir/x.txt: cannot embed file symdir[\\/]x.txt: in non-directory symdir'
+# even with GODEBUG=embedfollowsymlinks=1
+[symlink] env 'GODEBUG=embedfollowsymlinks=1'
+[symlink] cp x.go4 x.go
+[symlink] ! go build -x
+[symlink] stderr 'x.go:5:12: pattern symdir/[*]: cannot embed file symdir[\\/]x.txt: in non-directory symdir'
+[symlink] cp x.go5 x.go
+[symlink] ! go build -x
+[symlink] stderr 'x.go:5:12: pattern symdir/x.txt: cannot embed file symdir[\\/]x.txt: in non-directory symdir'
+[symlink] env 'GODEBUG='
+
 -- x.go --
 package p
 
@@ -112,6 +135,20 @@ import "embed"
 //go:embed all:t
 var X embed.FS
 
+-- x.go4 --
+package p
+
+import "embed"
+
+//go:embed symdir/*
+var X embed.FS
+-- x.go5 --
+package p
+
+import "embed"
+
+//go:embed symdir/x.txt
+var Z string
 -- x.txt --
 hello
 
@@ -124,6 +161,7 @@ not hello
 package use
 
 import _ "m"
+-- symdirdst/x.txt --
 -- go.mod --
 module m
 
index 214de6bdbe54c7e9b1bc8171d9344a019e4dbd80..26d079ca1fb7a1f74ad010e38b104aeffab2cd3a 100644 (file)
@@ -28,6 +28,7 @@ var All = []Info{
        {Name: "asynctimerchan", Package: "time", Changed: 23, Old: "1"},
        {Name: "dataindependenttiming", Package: "crypto/subtle", Opaque: true},
        {Name: "decoratemappings", Package: "runtime", Opaque: true, Changed: 25, Old: "0"},
+       {Name: "embedfollowsymlinks", Package: "cmd/go"},
        {Name: "execerrdot", Package: "os/exec"},
        {Name: "fips140", Package: "crypto/fips140", Opaque: true},
        {Name: "gocachehash", Package: "cmd/go"},
index 563ddf4c9594055985d3c18972a64672e6704f06..0d35314e06f109e80981ad525d6585d8b0744055 100644 (file)
@@ -234,6 +234,11 @@ Below is the full list of supported metrics, ordered lexicographically.
                The number of non-default behaviors executed by the time package
                due to a non-default GODEBUG=asynctimerchan=... setting.
 
+       /godebug/non-default-behavior/embedfollowsymlinks:events
+               The number of non-default behaviors executed by the cmd/go
+               package due to a non-default GODEBUG=embedfollowsymlinks=...
+               setting.
+
        /godebug/non-default-behavior/execerrdot:events
                The number of non-default behaviors executed by the os/exec
                package due to a non-default GODEBUG=execerrdot=... setting.