From 13e6fadd96dc00c611a3d2f26a1a6d7a0a29ea27 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 18 Feb 2021 18:54:38 -0800 Subject: libgo: update to Go1.16 release Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/293793 --- libgo/go/archive/tar/strconv.go | 21 ++++++- libgo/go/archive/tar/strconv_test.go | 7 +++ libgo/go/cmd/go/alldocs.go | 30 +++++++--- libgo/go/cmd/go/internal/help/helpdoc.go | 2 +- libgo/go/cmd/go/internal/load/pkg.go | 5 ++ libgo/go/cmd/go/internal/modget/get.go | 37 ++++++++---- libgo/go/cmd/go/internal/modload/help.go | 13 ++++- libgo/go/cmd/go/internal/vet/vetflag.go | 3 +- libgo/go/cmd/go/testdata/script/build_trimpath.txt | 3 +- libgo/go/cmd/go/testdata/script/help.txt | 7 ++- libgo/go/embed/embed.go | 40 ++++++++----- libgo/go/internal/poll/copy_file_range_linux.go | 10 +++- libgo/go/internal/poll/sendfile_bsd.go | 4 ++ libgo/go/internal/poll/sendfile_glibc.go | 6 ++ libgo/go/internal/poll/sendfile_solaris.go | 3 + libgo/go/io/fs/fs.go | 22 +++++--- libgo/go/io/fs/fs_test.go | 7 ++- libgo/go/io/fs/glob.go | 5 +- libgo/go/io/fs/glob_test.go | 3 +- libgo/go/math/big/nat.go | 2 +- libgo/go/net/sendfile_test.go | 65 ++++++++++++++++++++++ libgo/go/os/file.go | 13 ++++- libgo/go/os/os_test.go | 34 +++++++++++ libgo/go/os/readfrom_linux_test.go | 32 +++++++++++ libgo/go/runtime/histogram.go | 2 +- libgo/go/runtime/metrics/doc.go | 5 +- libgo/go/runtime/metrics/example_test.go | 2 +- libgo/go/runtime/metrics/sample.go | 8 +-- libgo/go/runtime/metrics/value.go | 2 +- libgo/go/runtime/stubs.go | 8 +++ libgo/go/testing/fstest/testfs.go | 25 ++++++--- libgo/go/testing/fstest/testfs_test.go | 31 +++++++++++ 32 files changed, 378 insertions(+), 79 deletions(-) create mode 100644 libgo/go/testing/fstest/testfs_test.go (limited to 'libgo/go') diff --git a/libgo/go/archive/tar/strconv.go b/libgo/go/archive/tar/strconv.go index 6d0a403..f0b61e6 100644 --- a/libgo/go/archive/tar/strconv.go +++ b/libgo/go/archive/tar/strconv.go @@ -265,8 +265,27 @@ func parsePAXRecord(s string) (k, v, r string, err error) { return "", "", s, ErrHeader } + afterSpace := int64(sp + 1) + beforeLastNewLine := n - 1 + // In some cases, "length" was perhaps padded/malformed, and + // trying to index past where the space supposedly is goes past + // the end of the actual record. + // For example: + // "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319" + // ^ ^ + // | | + // | afterSpace=35 + // | + // beforeLastNewLine=29 + // yet indexOf(firstSpace) MUST BE before endOfRecord. + // + // See https://golang.org/issues/40196. + if afterSpace >= beforeLastNewLine { + return "", "", s, ErrHeader + } + // Extract everything between the space and the final newline. - rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:] + rec, nl, rem := s[afterSpace:beforeLastNewLine], s[beforeLastNewLine:n], s[n:] if nl != "\n" { return "", "", s, ErrHeader } diff --git a/libgo/go/archive/tar/strconv_test.go b/libgo/go/archive/tar/strconv_test.go index dd3505a..add65e2 100644 --- a/libgo/go/archive/tar/strconv_test.go +++ b/libgo/go/archive/tar/strconv_test.go @@ -368,6 +368,13 @@ func TestParsePAXRecord(t *testing.T) { {"16 longkeyname=hahaha\n", "16 longkeyname=hahaha\n", "", "", false}, {"3 somelongkey=\n", "3 somelongkey=\n", "", "", false}, {"50 tooshort=\n", "50 tooshort=\n", "", "", false}, + {"0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319", "0000000000000000000000000000000030 mtime=1432668921.098285006\n30 ctime=2147483649.15163319", "mtime", "1432668921.098285006", false}, + {"06 k=v\n", "06 k=v\n", "", "", false}, + {"00006 k=v\n", "00006 k=v\n", "", "", false}, + {"000006 k=v\n", "000006 k=v\n", "", "", false}, + {"000000 k=v\n", "000000 k=v\n", "", "", false}, + {"0 k=v\n", "0 k=v\n", "", "", false}, + {"+0000005 x=\n", "+0000005 x=\n", "", "", false}, } for _, v := range vectors { diff --git a/libgo/go/cmd/go/alldocs.go b/libgo/go/cmd/go/alldocs.go index 49d3902..e7c63f0 100644 --- a/libgo/go/cmd/go/alldocs.go +++ b/libgo/go/cmd/go/alldocs.go @@ -1808,7 +1808,7 @@ // The directory where the go command will write // temporary source files, packages, and binaries. // GOVCS -// Lists version control commands that may be used with matching servers. +// Lists version control commands that may be used with matching servers. // See 'go help vcs'. // // Environment variables for use with cgo: @@ -2410,6 +2410,17 @@ // // For a detailed reference on modules, see https://golang.org/ref/mod. // +// By default, the go command may download modules from https://proxy.golang.org. +// It may authenticate modules using the checksum database at +// https://sum.golang.org. Both services are operated by the Go team at Google. +// The privacy policies for these services are available at +// https://proxy.golang.org/privacy and https://sum.golang.org/privacy, +// respectively. +// +// The go command's download behavior may be configured using GOPROXY, GOSUMDB, +// GOPRIVATE, and other environment variables. See 'go help environment' +// and https://golang.org/ref/mod#private-module-privacy for more information. +// // // Module authentication using go.sum // @@ -2868,20 +2879,23 @@ // legal reasons). Therefore, clients can still access public code served from // Bazaar, Fossil, or Subversion repositories by default, because those downloads // use the Go module mirror, which takes on the security risk of running the -// version control commands, using a custom sandbox. +// version control commands using a custom sandbox. // // The GOVCS variable can be used to change the allowed version control systems // for specific packages (identified by a module or import path). -// The GOVCS variable applies both when using modules and when using GOPATH. -// When using modules, the patterns match against the module path. -// When using GOPATH, the patterns match against the import path -// corresponding to the root of the version control repository. +// The GOVCS variable applies when building package in both module-aware mode +// and GOPATH mode. When using modules, the patterns match against the module path. +// When using GOPATH, the patterns match against the import path corresponding to +// the root of the version control repository. // // The general form of the GOVCS setting is a comma-separated list of // pattern:vcslist rules. The pattern is a glob pattern that must match // one or more leading elements of the module or import path. The vcslist // is a pipe-separated list of allowed version control commands, or "all" -// to allow use of any known command, or "off" to allow nothing. +// to allow use of any known command, or "off" to disallow all commands. +// Note that if a module matches a pattern with vcslist "off", it may still be +// downloaded if the origin server uses the "mod" scheme, which instructs the +// go command to download the module using the GOPROXY protocol. // The earliest matching pattern in the list applies, even if later patterns // might also match. // @@ -2889,7 +2903,7 @@ // // GOVCS=github.com:git,evil.com:off,*:git|hg // -// With this setting, code with an module or import path beginning with +// With this setting, code with a module or import path beginning with // github.com/ can only use git; paths on evil.com cannot use any version // control command, and all other paths (* matches everything) can use // only git or hg. diff --git a/libgo/go/cmd/go/internal/help/helpdoc.go b/libgo/go/cmd/go/internal/help/helpdoc.go index e07ad0e..57cee4f 100644 --- a/libgo/go/cmd/go/internal/help/helpdoc.go +++ b/libgo/go/cmd/go/internal/help/helpdoc.go @@ -542,7 +542,7 @@ General-purpose environment variables: The directory where the go command will write temporary source files, packages, and binaries. GOVCS - Lists version control commands that may be used with matching servers. + Lists version control commands that may be used with matching servers. See 'go help vcs'. Environment variables for use with cgo: diff --git a/libgo/go/cmd/go/internal/load/pkg.go b/libgo/go/cmd/go/internal/load/pkg.go index 8757d66..e83f1d2 100644 --- a/libgo/go/cmd/go/internal/load/pkg.go +++ b/libgo/go/cmd/go/internal/load/pkg.go @@ -36,6 +36,8 @@ import ( "cmd/go/internal/str" "cmd/go/internal/trace" "cmd/internal/sys" + + "golang.org/x/mod/module" ) var IgnoreImports bool // control whether we ignore imports in packages @@ -2096,6 +2098,9 @@ func validEmbedPattern(pattern string) bool { // can't or won't be included in modules and therefore shouldn't be treated // as existing for embedding. func isBadEmbedName(name string) bool { + if err := module.CheckFilePath(name); err != nil { + return true + } switch name { // Empty string should be impossible but make it bad. case "": diff --git a/libgo/go/cmd/go/internal/modget/get.go b/libgo/go/cmd/go/internal/modget/get.go index 574f3e1..dccacd3 100644 --- a/libgo/go/cmd/go/internal/modget/get.go +++ b/libgo/go/cmd/go/internal/modget/get.go @@ -176,20 +176,23 @@ packages or when the mirror refuses to serve a public package (typically for legal reasons). Therefore, clients can still access public code served from Bazaar, Fossil, or Subversion repositories by default, because those downloads use the Go module mirror, which takes on the security risk of running the -version control commands, using a custom sandbox. +version control commands using a custom sandbox. The GOVCS variable can be used to change the allowed version control systems for specific packages (identified by a module or import path). -The GOVCS variable applies both when using modules and when using GOPATH. -When using modules, the patterns match against the module path. -When using GOPATH, the patterns match against the import path -corresponding to the root of the version control repository. +The GOVCS variable applies when building package in both module-aware mode +and GOPATH mode. When using modules, the patterns match against the module path. +When using GOPATH, the patterns match against the import path corresponding to +the root of the version control repository. The general form of the GOVCS setting is a comma-separated list of pattern:vcslist rules. The pattern is a glob pattern that must match one or more leading elements of the module or import path. The vcslist is a pipe-separated list of allowed version control commands, or "all" -to allow use of any known command, or "off" to allow nothing. +to allow use of any known command, or "off" to disallow all commands. +Note that if a module matches a pattern with vcslist "off", it may still be +downloaded if the origin server uses the "mod" scheme, which instructs the +go command to download the module using the GOPROXY protocol. The earliest matching pattern in the list applies, even if later patterns might also match. @@ -197,7 +200,7 @@ For example, consider: GOVCS=github.com:git,evil.com:off,*:git|hg -With this setting, code with an module or import path beginning with +With this setting, code with a module or import path beginning with github.com/ can only use git; paths on evil.com cannot use any version control command, and all other paths (* matches everything) can use only git or hg. @@ -380,10 +383,9 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) { pkgs := load.PackagesAndErrors(ctx, pkgPatterns) load.CheckPackageErrors(pkgs) work.InstallPackages(ctx, pkgPatterns, pkgs) - // TODO(#40276): After Go 1.16, print a deprecation notice when building - // and installing main packages. 'go install pkg' or - // 'go install pkg@version' should be used instead. - // Give the specific argument to use if possible. + // TODO(#40276): After Go 1.16, print a deprecation notice when building and + // installing main packages. 'go install pkg' or 'go install pkg@version' + // should be used instead. Give the specific argument to use if possible. } if !modload.HasModRoot() { @@ -1453,7 +1455,18 @@ func (r *resolver) checkPackagesAndRetractions(ctx context.Context, pkgPatterns } } for _, pkg := range pkgs { - if _, _, err := modload.Lookup("", false, pkg); err != nil { + if dir, _, err := modload.Lookup("", false, pkg); err != nil { + if dir != "" && errors.Is(err, imports.ErrNoGo) { + // Since dir is non-empty, we must have located source files + // associated with either the package or its test — ErrNoGo must + // indicate that none of those source files happen to apply in this + // configuration. If we are actually building the package (no -d + // flag), the compiler will report the problem; otherwise, assume that + // the user is going to build or test it in some other configuration + // and suppress the error. + continue + } + base.SetExitStatus(1) if ambiguousErr := (*modload.AmbiguousImportError)(nil); errors.As(err, &ambiguousErr) { for _, m := range ambiguousErr.Modules { diff --git a/libgo/go/cmd/go/internal/modload/help.go b/libgo/go/cmd/go/internal/modload/help.go index 1cb5896..fd39ddd 100644 --- a/libgo/go/cmd/go/internal/modload/help.go +++ b/libgo/go/cmd/go/internal/modload/help.go @@ -6,8 +6,6 @@ package modload import "cmd/go/internal/base" -// TODO(rsc): The "module code layout" section needs to be written. - var HelpModules = &base.Command{ UsageLine: "modules", Short: "modules, module versions, and more", @@ -22,6 +20,17 @@ For a series of tutorials on modules, see https://golang.org/doc/tutorial/create-module. For a detailed reference on modules, see https://golang.org/ref/mod. + +By default, the go command may download modules from https://proxy.golang.org. +It may authenticate modules using the checksum database at +https://sum.golang.org. Both services are operated by the Go team at Google. +The privacy policies for these services are available at +https://proxy.golang.org/privacy and https://sum.golang.org/privacy, +respectively. + +The go command's download behavior may be configured using GOPROXY, GOSUMDB, +GOPRIVATE, and other environment variables. See 'go help environment' +and https://golang.org/ref/mod#private-module-privacy for more information. `, } diff --git a/libgo/go/cmd/go/internal/vet/vetflag.go b/libgo/go/cmd/go/internal/vet/vetflag.go index 5bf5cf4..b5b3c46 100644 --- a/libgo/go/cmd/go/internal/vet/vetflag.go +++ b/libgo/go/cmd/go/internal/vet/vetflag.go @@ -184,7 +184,8 @@ func exitWithUsage() { if vetTool != "" { cmd = vetTool } - fmt.Fprintf(os.Stderr, "Run '%s -help' for the vet tool's flags.\n", cmd) + fmt.Fprintf(os.Stderr, "Run '%s help' for a full list of flags and analyzers.\n", cmd) + fmt.Fprintf(os.Stderr, "Run '%s -help' for an overview.\n", cmd) base.SetExitStatus(2) base.Exit() diff --git a/libgo/go/cmd/go/testdata/script/build_trimpath.txt b/libgo/go/cmd/go/testdata/script/build_trimpath.txt index e1ea0a4..2c3bee8 100644 --- a/libgo/go/cmd/go/testdata/script/build_trimpath.txt +++ b/libgo/go/cmd/go/testdata/script/build_trimpath.txt @@ -121,6 +121,7 @@ package main import ( "bytes" "fmt" + "io/ioutil" "log" "os" "os/exec" @@ -130,7 +131,7 @@ import ( func main() { exe := os.Args[1] - data, err := os.ReadFile(exe) + data, err := ioutil.ReadFile(exe) if err != nil { log.Fatal(err) } diff --git a/libgo/go/cmd/go/testdata/script/help.txt b/libgo/go/cmd/go/testdata/script/help.txt index 9752ede..26a0194 100644 --- a/libgo/go/cmd/go/testdata/script/help.txt +++ b/libgo/go/cmd/go/testdata/script/help.txt @@ -34,9 +34,10 @@ stderr 'Run ''go help mod'' for usage.' # Earlier versions of Go printed the same as 'go -h' here. # Also make sure we print the short help line. ! go vet -h -stderr 'usage: go vet' -stderr 'Run ''go help vet'' for details' -stderr 'Run ''go tool vet -help'' for the vet tool''s flags' +stderr 'usage: go vet .*' +stderr 'Run ''go help vet'' for details.' +stderr 'Run ''go tool vet help'' for a full list of flags and analyzers.' +stderr 'Run ''go tool vet -help'' for an overview.' # Earlier versions of Go printed a large document here, instead of these two # lines. diff --git a/libgo/go/embed/embed.go b/libgo/go/embed/embed.go index cc6855e..98da870 100644 --- a/libgo/go/embed/embed.go +++ b/libgo/go/embed/embed.go @@ -9,18 +9,28 @@ // files read from the package directory or subdirectories at compile time. // // For example, here are three ways to embed a file named hello.txt -// and then print its contents at run time: +// and then print its contents at run time. // -// import "embed" +// Embedding one file into a string: +// +// import _ "embed" // // //go:embed hello.txt // var s string // print(s) // +// Embedding one file into a slice of bytes: +// +// import _ "embed" +// // //go:embed hello.txt // var b []byte // print(string(b)) // +// Embedded one or more files into a file system: +// +// import "embed" +// // //go:embed hello.txt // var f embed.FS // data, _ := f.ReadFile("hello.txt") @@ -34,8 +44,8 @@ // The directive must immediately precede a line containing the declaration of a single variable. // Only blank lines and ‘//’ line comments are permitted between the directive and the declaration. // -// The variable must be of type string, []byte, or FS exactly. Named types or type aliases -// derived from those types are not allowed. +// The type of the variable must be a string type, or a slice of a byte type, +// or FS (or an alias of FS). // // For example: // @@ -51,12 +61,15 @@ // The Go build system will recognize the directives and arrange for the declared variable // (in the example above, content) to be populated with the matching files from the file system. // -// The //go:embed directive accepts multiple space-separated patterns for brevity, -// but it can also be repeated, to avoid very long lines when there are many patterns. -// The patterns are interpreted relative to the package directory containing the source file. -// The path separator is a forward slash, even on Windows systems. -// To allow for naming files with spaces in their names, patterns can be written -// as Go double-quoted or back-quoted string literals. +// The //go:embed directive accepts multiple space-separated patterns for +// brevity, but it can also be repeated, to avoid very long lines when there are +// many patterns. The patterns are interpreted relative to the package directory +// containing the source file. The path separator is a forward slash, even on +// Windows systems. Patterns may not contain ‘.’ or ‘..’ or empty path elements, +// nor may they begin or end with a slash. To match everything in the current +// directory, use ‘*’ instead of ‘.’. To allow for naming files with spaces in +// their names, patterns can be written as Go double-quoted or back-quoted +// string literals. // // If a pattern names a directory, all files in the subtree rooted at that directory are // embedded (recursively), except that files with names beginning with ‘.’ or ‘_’ @@ -70,16 +83,13 @@ // // The //go:embed directive can be used with both exported and unexported variables, // depending on whether the package wants to make the data available to other packages. -// Similarly, it can be used with both global and function-local variables, -// depending on what is more convenient in context. +// It can only be used with global variables at package scope, +// not with local variables. // // Patterns must not match files outside the package's module, such as ‘.git/*’ or symbolic links. // Matches for empty directories are ignored. After that, each pattern in a //go:embed line // must match at least one file or non-empty directory. // -// Patterns must not contain ‘.’ or ‘..’ path elements nor begin with a leading slash. -// To match everything in the current directory, use ‘*’ instead of ‘.’. -// // If any patterns are invalid or have invalid matches, the build will fail. // // Strings and Bytes diff --git a/libgo/go/internal/poll/copy_file_range_linux.go b/libgo/go/internal/poll/copy_file_range_linux.go index fc34aef..01b242a 100644 --- a/libgo/go/internal/poll/copy_file_range_linux.go +++ b/libgo/go/internal/poll/copy_file_range_linux.go @@ -112,7 +112,15 @@ func CopyFileRange(dst, src *FD, remain int64) (written int64, handled bool, err return 0, false, nil case nil: if n == 0 { - // src is at EOF, which means we are done. + // If we did not read any bytes at all, + // then this file may be in a file system + // where copy_file_range silently fails. + // https://lore.kernel.org/linux-fsdevel/20210126233840.GG4626@dread.disaster.area/T/#m05753578c7f7882f6e9ffe01f981bc223edef2b0 + if written == 0 { + return 0, false, nil + } + // Otherwise src is at EOF, which means + // we are done. return written, true, nil } remain -= n diff --git a/libgo/go/internal/poll/sendfile_bsd.go b/libgo/go/internal/poll/sendfile_bsd.go index a24e41d..66005a9 100644 --- a/libgo/go/internal/poll/sendfile_bsd.go +++ b/libgo/go/internal/poll/sendfile_bsd.go @@ -18,6 +18,10 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) { return 0, err } defer dstFD.writeUnlock() + if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil { + return 0, err + } + dst := int(dstFD.Sysfd) var written int64 var err error diff --git a/libgo/go/internal/poll/sendfile_glibc.go b/libgo/go/internal/poll/sendfile_glibc.go index 6652e3f..d95e496 100644 --- a/libgo/go/internal/poll/sendfile_glibc.go +++ b/libgo/go/internal/poll/sendfile_glibc.go @@ -18,6 +18,9 @@ func SendFile(dstFD *FD, src int, remain int64) (int64, error) { return 0, err } defer dstFD.writeUnlock() + if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil { + return 0, err + } dst := int(dstFD.Sysfd) var written int64 @@ -34,6 +37,9 @@ func SendFile(dstFD *FD, src int, remain int64) (int64, error) { } else if n == 0 && err1 == nil { break } + if err1 == syscall.EINTR { + continue + } if err1 == syscall.EAGAIN { if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil { continue diff --git a/libgo/go/internal/poll/sendfile_solaris.go b/libgo/go/internal/poll/sendfile_solaris.go index 762992e..748c851 100644 --- a/libgo/go/internal/poll/sendfile_solaris.go +++ b/libgo/go/internal/poll/sendfile_solaris.go @@ -20,6 +20,9 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) { return 0, err } defer dstFD.writeUnlock() + if err := dstFD.pd.prepareWrite(dstFD.isFile); err != nil { + return 0, err + } dst := int(dstFD.Sysfd) var written int64 diff --git a/libgo/go/io/fs/fs.go b/libgo/go/io/fs/fs.go index b691a86..3d2e2ee 100644 --- a/libgo/go/io/fs/fs.go +++ b/libgo/go/io/fs/fs.go @@ -10,6 +10,7 @@ package fs import ( "internal/oserror" "time" + "unicode/utf8" ) // An FS provides access to a hierarchical file system. @@ -32,14 +33,22 @@ type FS interface { // ValidPath reports whether the given path name // is valid for use in a call to Open. -// Path names passed to open are unrooted, slash-separated -// sequences of path elements, like “x/y/z”. -// Path names must not contain a “.” or “..” or empty element, +// +// Path names passed to open are UTF-8-encoded, +// unrooted, slash-separated sequences of path elements, like “x/y/z”. +// Path names must not contain an element that is “.” or “..” or the empty string, // except for the special case that the root directory is named “.”. +// Paths must not start or end with a slash: “/x” and “x/” are invalid. // -// Paths are slash-separated on all systems, even Windows. -// Backslashes must not appear in path names. +// Note that paths are slash-separated on all systems, even Windows. +// Paths containing other characters such as backslash and colon +// are accepted as valid, but those characters must never be +// interpreted by an FS implementation as path element separators. func ValidPath(name string) bool { + if !utf8.ValidString(name) { + return false + } + if name == "." { // special case return true @@ -49,9 +58,6 @@ func ValidPath(name string) bool { for { i := 0 for i < len(name) && name[i] != '/' { - if name[i] == '\\' { - return false - } i++ } elem := name[:i] diff --git a/libgo/go/io/fs/fs_test.go b/libgo/go/io/fs/fs_test.go index 8d395fc..aae1a76 100644 --- a/libgo/go/io/fs/fs_test.go +++ b/libgo/go/io/fs/fs_test.go @@ -33,9 +33,10 @@ var isValidPathTests = []struct { {"x/..", false}, {"x/../y", false}, {"x//y", false}, - {`x\`, false}, - {`x\y`, false}, - {`\x`, false}, + {`x\`, true}, + {`x\y`, true}, + {`x:y`, true}, + {`\x`, true}, } func TestValidPath(t *testing.T) { diff --git a/libgo/go/io/fs/glob.go b/libgo/go/io/fs/glob.go index 549f217..45d9cb6 100644 --- a/libgo/go/io/fs/glob.go +++ b/libgo/go/io/fs/glob.go @@ -6,7 +6,6 @@ package fs import ( "path" - "runtime" ) // A GlobFS is a file system with a Glob method. @@ -111,8 +110,8 @@ func glob(fs FS, dir, pattern string, matches []string) (m []string, e error) { // recognized by path.Match. func hasMeta(path string) bool { for i := 0; i < len(path); i++ { - c := path[i] - if c == '*' || c == '?' || c == '[' || runtime.GOOS == "windows" && c == '\\' { + switch path[i] { + case '*', '?', '[', '\\': return true } } diff --git a/libgo/go/io/fs/glob_test.go b/libgo/go/io/fs/glob_test.go index 440ebd6..bcd2e1b 100644 --- a/libgo/go/io/fs/glob_test.go +++ b/libgo/go/io/fs/glob_test.go @@ -17,6 +17,7 @@ var globTests = []struct { }{ {os.DirFS("."), "glob.go", "glob.go"}, {os.DirFS("."), "gl?b.go", "glob.go"}, + {os.DirFS("."), `gl\ob.go`, "glob.go"}, {os.DirFS("."), "*", "glob.go"}, // This test fails on gofrontend because the directory structure // is different. @@ -34,7 +35,7 @@ func TestGlob(t *testing.T) { t.Errorf("Glob(%#q) = %#v want %v", tt.pattern, matches, tt.result) } } - for _, pattern := range []string{"no_match", "../*/no_match"} { + for _, pattern := range []string{"no_match", "../*/no_match", `\*`} { matches, err := Glob(os.DirFS("."), pattern) if err != nil { t.Errorf("Glob error for %q: %s", pattern, err) diff --git a/libgo/go/math/big/nat.go b/libgo/go/math/big/nat.go index 068176e..bbd6c88 100644 --- a/libgo/go/math/big/nat.go +++ b/libgo/go/math/big/nat.go @@ -881,7 +881,7 @@ func (z nat) divRecursiveStep(u, v nat, depth int, tmp *nat, temps []*nat) { // then floor(u1/v1) >= floor(u/v) // // Moreover, the difference is at most 2 if len(v1) >= len(u/v) - // We choose s = B-1 since len(v)-B >= B+1 >= len(u/v) + // We choose s = B-1 since len(v)-s >= B+1 >= len(u/v) s := (B - 1) // Except for the first step, the top bits are always // a division remainder, so the quotient length is <= n. diff --git a/libgo/go/net/sendfile_test.go b/libgo/go/net/sendfile_test.go index 657a365..d6057fd 100644 --- a/libgo/go/net/sendfile_test.go +++ b/libgo/go/net/sendfile_test.go @@ -10,8 +10,10 @@ import ( "bytes" "crypto/sha256" "encoding/hex" + "errors" "fmt" "io" + "io/ioutil" "os" "runtime" "sync" @@ -313,3 +315,66 @@ func TestSendfilePipe(t *testing.T) { wg.Wait() } + +// Issue 43822: tests that returns EOF when conn write timeout. +func TestSendfileOnWriteTimeoutExceeded(t *testing.T) { + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + errc := make(chan error, 1) + go func(ln Listener) (retErr error) { + defer func() { + errc <- retErr + close(errc) + }() + + conn, err := ln.Accept() + if err != nil { + return err + } + defer conn.Close() + + // Set the write deadline in the past(1h ago). It makes + // sure that it is always write timeout. + if err := conn.SetWriteDeadline(time.Now().Add(-1 * time.Hour)); err != nil { + return err + } + + f, err := os.Open(newton) + if err != nil { + return err + } + defer f.Close() + + _, err = io.Copy(conn, f) + if errors.Is(err, os.ErrDeadlineExceeded) { + return nil + } + + if err == nil { + err = fmt.Errorf("expected ErrDeadlineExceeded, but got nil") + } + return err + }(ln) + + conn, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + n, err := io.Copy(ioutil.Discard, conn) + if err != nil { + t.Fatalf("expected nil error, but got %v", err) + } + if n != 0 { + t.Fatalf("expected receive zero, but got %d byte(s)", n) + } + + if err := <-errc; err != nil { + t.Fatal(err) + } +} diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index 416bc0e..52dd943 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -620,10 +620,21 @@ func DirFS(dir string) fs.FS { return dirFS(dir) } +func containsAny(s, chars string) bool { + for i := 0; i < len(s); i++ { + for j := 0; j < len(chars); j++ { + if s[i] == chars[j] { + return true + } + } + } + return false +} + type dirFS string func (dir dirFS) Open(name string) (fs.File, error) { - if !fs.ValidPath(name) { + if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid} } f, err := Open(string(dir) + "/" + name) diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 40ba056..734c655 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -2721,6 +2721,40 @@ func TestDirFS(t *testing.T) { if err := fstest.TestFS(DirFS("./testdata/dirfs"), "a", "b", "dir/x"); err != nil { t.Fatal(err) } + + // Test that Open does not accept backslash as separator. + d := DirFS(".") + _, err := d.Open(`testdata\dirfs`) + if err == nil { + t.Fatalf(`Open testdata\dirfs succeeded`) + } +} + +func TestDirFSPathsValid(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skipf("skipping on Windows") + } + + d := t.TempDir() + if err := os.WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil { + t.Fatal(err) + } + + fsys := os.DirFS(d) + err := fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error { + if fs.ValidPath(e.Name()) { + t.Logf("%q ok", e.Name()) + } else { + t.Errorf("%q INVALID", e.Name()) + } + return nil + }) + if err != nil { + t.Fatal(err) + } } func TestReadFileProc(t *testing.T) { diff --git a/libgo/go/os/readfrom_linux_test.go b/libgo/go/os/readfrom_linux_test.go index 3704717..1d145da 100644 --- a/libgo/go/os/readfrom_linux_test.go +++ b/libgo/go/os/readfrom_linux_test.go @@ -361,3 +361,35 @@ func (h *copyFileRangeHook) install() { func (h *copyFileRangeHook) uninstall() { *PollCopyFileRangeP = h.original } + +// On some kernels copy_file_range fails on files in /proc. +func TestProcCopy(t *testing.T) { + const cmdlineFile = "/proc/self/cmdline" + cmdline, err := os.ReadFile(cmdlineFile) + if err != nil { + t.Skipf("can't read /proc file: %v", err) + } + in, err := os.Open(cmdlineFile) + if err != nil { + t.Fatal(err) + } + defer in.Close() + outFile := filepath.Join(t.TempDir(), "cmdline") + out, err := os.Create(outFile) + if err != nil { + t.Fatal(err) + } + if _, err := io.Copy(out, in); err != nil { + t.Fatal(err) + } + if err := out.Close(); err != nil { + t.Fatal(err) + } + copy, err := os.ReadFile(outFile) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(cmdline, copy) { + t.Errorf("copy of %q got %q want %q\n", cmdlineFile, copy, cmdline) + } +} diff --git a/libgo/go/runtime/histogram.go b/libgo/go/runtime/histogram.go index 42baa6c..da4910d 100644 --- a/libgo/go/runtime/histogram.go +++ b/libgo/go/runtime/histogram.go @@ -26,7 +26,7 @@ const ( // The number of super-buckets (timeHistNumSuperBuckets), on the // other hand, defines the range. To reserve room for sub-buckets, // bit timeHistSubBucketBits is the first bit considered for - // super-buckets, so super-bucket indicies are adjusted accordingly. + // super-buckets, so super-bucket indices are adjusted accordingly. // // As an example, consider 45 super-buckets with 16 sub-buckets. // diff --git a/libgo/go/runtime/metrics/doc.go b/libgo/go/runtime/metrics/doc.go index 021a0bd..7f790af 100644 --- a/libgo/go/runtime/metrics/doc.go +++ b/libgo/go/runtime/metrics/doc.go @@ -16,13 +16,12 @@ Interface Metrics are designated by a string key, rather than, for example, a field name in a struct. The full list of supported metrics is always available in the slice of Descriptions returned by All. Each Description also includes useful information -about the metric, such as how to display it (e.g. gauge vs. counter) and how difficult -or disruptive it is to obtain it (e.g. do you need to stop the world?). +about the metric. Thus, users of this API are encouraged to sample supported metrics defined by the slice returned by All to remain compatible across Go versions. Of course, situations arise where reading specific metrics is critical. For these cases, users are -encouranged to use build tags, and although metrics may be deprecated and removed, +encouraged to use build tags, and although metrics may be deprecated and removed, users should consider this to be an exceptional and rare event, coinciding with a very large change in a particular Go implementation. diff --git a/libgo/go/runtime/metrics/example_test.go b/libgo/go/runtime/metrics/example_test.go index cade0c3..624d9d8 100644 --- a/libgo/go/runtime/metrics/example_test.go +++ b/libgo/go/runtime/metrics/example_test.go @@ -88,7 +88,7 @@ func medianBucket(h *metrics.Float64Histogram) float64 { total = 0 for i, count := range h.Counts { total += count - if total > thresh { + if total >= thresh { return h.Buckets[i] } } diff --git a/libgo/go/runtime/metrics/sample.go b/libgo/go/runtime/metrics/sample.go index 35534dd..4cf8cdf 100644 --- a/libgo/go/runtime/metrics/sample.go +++ b/libgo/go/runtime/metrics/sample.go @@ -14,7 +14,7 @@ type Sample struct { // Name is the name of the metric sampled. // // It must correspond to a name in one of the metric descriptions - // returned by Descriptions. + // returned by All. Name string // Value is the value of the metric sample. @@ -32,9 +32,9 @@ func runtime_readMetrics(unsafe.Pointer, int, int) // // Note that re-use has some caveats. Notably, Values should not be read or // manipulated while a Read with that value is outstanding; that is a data race. -// This property includes pointer-typed Values (e.g. Float64Histogram) whose -// underlying storage will be reused by Read when possible. To safely use such -// values in a concurrent setting, all data must be deep-copied. +// This property includes pointer-typed Values (for example, Float64Histogram) +// whose underlying storage will be reused by Read when possible. To safely use +// such values in a concurrent setting, all data must be deep-copied. // // It is safe to execute multiple Read calls concurrently, but their arguments // must share no underlying memory. When in doubt, create a new []Sample from diff --git a/libgo/go/runtime/metrics/value.go b/libgo/go/runtime/metrics/value.go index 61e8a19..ed9a33d 100644 --- a/libgo/go/runtime/metrics/value.go +++ b/libgo/go/runtime/metrics/value.go @@ -33,7 +33,7 @@ type Value struct { pointer unsafe.Pointer // contains non-scalar values. } -// Kind returns the a tag representing the kind of value this is. +// Kind returns the tag representing the kind of value this is. func (v Value) Kind() ValueKind { return v.kind } diff --git a/libgo/go/runtime/stubs.go b/libgo/go/runtime/stubs.go index b0c5b38..6d20c38 100644 --- a/libgo/go/runtime/stubs.go +++ b/libgo/go/runtime/stubs.go @@ -89,7 +89,15 @@ func badsystemstack() { // *ptr is uninitialized memory (e.g., memory that's being reused // for a new allocation) and hence contains only "junk". // +// memclrNoHeapPointers ensures that if ptr is pointer-aligned, and n +// is a multiple of the pointer size, then any pointer-aligned, +// pointer-sized portion is cleared atomically. Despite the function +// name, this is necessary because this function is the underlying +// implementation of typedmemclr and memclrHasPointers. See the doc of +// memmove for more details. +// // The (CPU-specific) implementations of this function are in memclr_*.s. +// //go:noescape func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) diff --git a/libgo/go/testing/fstest/testfs.go b/libgo/go/testing/fstest/testfs.go index a7f8007..8fc8aca 100644 --- a/libgo/go/testing/fstest/testfs.go +++ b/libgo/go/testing/fstest/testfs.go @@ -403,9 +403,10 @@ func (t *fsTester) checkStat(path string, entry fs.DirEntry) { return } fentry := formatEntry(entry) - finfo := formatInfoEntry(info) - if fentry != finfo { - t.errorf("%s: mismatch:\n\tentry = %s\n\tfile.Stat() = %s", path, fentry, finfo) + fientry := formatInfoEntry(info) + // Note: mismatch here is OK for symlink, because Open dereferences symlink. + if fentry != fientry && entry.Type()&fs.ModeSymlink == 0 { + t.errorf("%s: mismatch:\n\tentry = %s\n\tfile.Stat() = %s", path, fentry, fientry) } einfo, err := entry.Info() @@ -413,12 +414,22 @@ func (t *fsTester) checkStat(path string, entry fs.DirEntry) { t.errorf("%s: entry.Info: %v", path, err) return } - fentry = formatInfo(einfo) - finfo = formatInfo(info) - if fentry != finfo { - t.errorf("%s: mismatch:\n\tentry.Info() = %s\n\tfile.Stat() = %s\n", path, fentry, finfo) + finfo := formatInfo(info) + if entry.Type()&fs.ModeSymlink != 0 { + // For symlink, just check that entry.Info matches entry on common fields. + // Open deferences symlink, so info itself may differ. + feentry := formatInfoEntry(einfo) + if fentry != feentry { + t.errorf("%s: mismatch\n\tentry = %s\n\tentry.Info() = %s\n", path, fentry, feentry) + } + } else { + feinfo := formatInfo(einfo) + if feinfo != finfo { + t.errorf("%s: mismatch:\n\tentry.Info() = %s\n\tfile.Stat() = %s\n", path, feinfo, finfo) + } } + // Stat should be the same as Open+Stat, even for symlinks. info2, err := fs.Stat(t.fsys, path) if err != nil { t.errorf("%s: fs.Stat: %v", path, err) diff --git a/libgo/go/testing/fstest/testfs_test.go b/libgo/go/testing/fstest/testfs_test.go new file mode 100644 index 0000000..5b8813c --- /dev/null +++ b/libgo/go/testing/fstest/testfs_test.go @@ -0,0 +1,31 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fstest + +import ( + "internal/testenv" + "os" + "path/filepath" + "testing" +) + +func TestSymlink(t *testing.T) { + testenv.MustHaveSymlink(t) + + tmp := t.TempDir() + tmpfs := os.DirFS(tmp) + + if err := os.WriteFile(filepath.Join(tmp, "hello"), []byte("hello, world\n"), 0644); err != nil { + t.Fatal(err) + } + + if err := os.Symlink(filepath.Join(tmp, "hello"), filepath.Join(tmp, "hello.link")); err != nil { + t.Fatal(err) + } + + if err := TestFS(tmpfs, "hello", "hello.link"); err != nil { + t.Fatal(err) + } +} -- cgit v1.1