Update to go1.24.4
This commit is contained in:
4
VERSION
4
VERSION
@@ -1,2 +1,2 @@
|
|||||||
go1.24.3
|
go1.24.4
|
||||||
time 2025-04-30T18:13:34Z
|
time 2025-05-29T19:37:36Z
|
||||||
|
|||||||
1
lib/fips140/inprocess.txt
Normal file
1
lib/fips140/inprocess.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
v1.0.0
|
||||||
@@ -452,33 +452,50 @@ func (st *loadState) addSym(name string, ver int, r *oReader, li uint32, kind in
|
|||||||
if oldsym.Dupok() {
|
if oldsym.Dupok() {
|
||||||
return oldi
|
return oldi
|
||||||
}
|
}
|
||||||
// If one is a DATA symbol (i.e. has content, DataSize != 0)
|
// If one is a DATA symbol (i.e. has content, DataSize != 0,
|
||||||
// and the other is BSS, the one with content wins.
|
// including RODATA) and the other is BSS, the one with content wins.
|
||||||
// If both are BSS, the one with larger size wins.
|
// If both are BSS, the one with larger size wins.
|
||||||
// Specifically, the "overwrite" variable and the final result are
|
|
||||||
//
|
//
|
||||||
// new sym old sym overwrite
|
// For a special case, we allow a TEXT symbol overwrites a BSS symbol
|
||||||
|
// even if the BSS symbol has larger size. This is because there is
|
||||||
|
// code like below to take the address of a function
|
||||||
|
//
|
||||||
|
// //go:linkname fn
|
||||||
|
// var fn uintptr
|
||||||
|
// var fnAddr = uintptr(unsafe.Pointer(&fn))
|
||||||
|
//
|
||||||
|
// TODO: maybe limit this case to just pointer sized variable?
|
||||||
|
//
|
||||||
|
// In summary, the "overwrite" variable and the final result are
|
||||||
|
//
|
||||||
|
// new sym old sym result
|
||||||
// ---------------------------------------------
|
// ---------------------------------------------
|
||||||
// DATA DATA true => ERROR
|
// TEXT BSS new wins
|
||||||
// DATA lg/eq BSS sm/eq true => new wins
|
// DATA DATA ERROR
|
||||||
// DATA small BSS large true => ERROR
|
// DATA lg/eq BSS sm/eq new wins
|
||||||
// BSS large DATA small true => ERROR
|
// DATA small BSS large ERROR
|
||||||
// BSS large BSS small true => new wins
|
// BSS large DATA small ERROR
|
||||||
// BSS sm/eq D/B lg/eq false => old wins
|
// BSS large BSS small new wins
|
||||||
overwrite := r.DataSize(li) != 0 || oldsz < sz
|
// BSS sm/eq D/B lg/eq old wins
|
||||||
if overwrite {
|
// BSS TEXT old wins
|
||||||
// new symbol overwrites old symbol.
|
|
||||||
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
|
oldtyp := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
|
||||||
if !(oldtyp.IsData() && oldr.DataSize(oldli) == 0) || oldsz > sz {
|
newtyp := sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type())]
|
||||||
log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
|
oldIsText := oldtyp.IsText()
|
||||||
}
|
newIsText := newtyp.IsText()
|
||||||
|
oldHasContent := oldr.DataSize(oldli) != 0
|
||||||
|
newHasContent := r.DataSize(li) != 0
|
||||||
|
oldIsBSS := oldtyp.IsData() && !oldHasContent
|
||||||
|
newIsBSS := newtyp.IsData() && !newHasContent
|
||||||
|
switch {
|
||||||
|
case newIsText && oldIsBSS,
|
||||||
|
newHasContent && oldIsBSS && sz >= oldsz,
|
||||||
|
newIsBSS && oldIsBSS && sz > oldsz:
|
||||||
|
// new symbol overwrites old symbol.
|
||||||
l.objSyms[oldi] = objSym{r.objidx, li}
|
l.objSyms[oldi] = objSym{r.objidx, li}
|
||||||
} else {
|
case newIsBSS && (oldsz >= sz || oldIsText):
|
||||||
// old symbol overwrites new symbol.
|
// old win, just ignore the new symbol.
|
||||||
typ := sym.AbiSymKindToSymKind[objabi.SymKind(oldsym.Type())]
|
default:
|
||||||
if !typ.IsData() { // only allow overwriting data symbol
|
log.Fatalf("duplicated definition of symbol %s, from %s (type %s size %d) and %s (type %s size %d)", name, r.unit.Lib.Pkg, newtyp, sz, oldr.unit.Lib.Pkg, oldtyp, oldsz)
|
||||||
log.Fatalf("duplicated definition of symbol %s, from %s and %s", name, r.unit.Lib.Pkg, oldr.unit.Lib.Pkg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return oldi
|
return oldi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1512,6 +1512,9 @@ func TestCheckLinkname(t *testing.T) {
|
|||||||
{"ok.go", true},
|
{"ok.go", true},
|
||||||
// push linkname is ok
|
// push linkname is ok
|
||||||
{"push.go", true},
|
{"push.go", true},
|
||||||
|
// using a linknamed variable to reference an assembly
|
||||||
|
// function in the same package is ok
|
||||||
|
{"textvar", true},
|
||||||
// pull linkname of blocked symbol is not ok
|
// pull linkname of blocked symbol is not ok
|
||||||
{"coro.go", false},
|
{"coro.go", false},
|
||||||
{"coro_var.go", false},
|
{"coro_var.go", false},
|
||||||
@@ -1529,7 +1532,7 @@ func TestCheckLinkname(t *testing.T) {
|
|||||||
test := test
|
test := test
|
||||||
t.Run(test.src, func(t *testing.T) {
|
t.Run(test.src, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
src := filepath.Join("testdata", "linkname", test.src)
|
src := "./testdata/linkname/" + test.src
|
||||||
exe := filepath.Join(tmpdir, test.src+".exe")
|
exe := filepath.Join(tmpdir, test.src+".exe")
|
||||||
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
|
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
|
|||||||
6
src/cmd/link/testdata/linkname/textvar/asm.s
vendored
Normal file
6
src/cmd/link/testdata/linkname/textvar/asm.s
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Copyright 2024 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.
|
||||||
|
|
||||||
|
TEXT ·asmfunc(SB),0,$0-0
|
||||||
|
RET
|
||||||
17
src/cmd/link/testdata/linkname/textvar/main.go
vendored
Normal file
17
src/cmd/link/testdata/linkname/textvar/main.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2024 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.
|
||||||
|
|
||||||
|
// Using a linknamed variable to reference an assembly
|
||||||
|
// function in the same package is ok.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import _ "unsafe"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
println(&asmfunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname asmfunc
|
||||||
|
var asmfunc uintptr
|
||||||
@@ -841,31 +841,45 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.KeyUsages) == 0 {
|
|
||||||
opts.KeyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, eku := range opts.KeyUsages {
|
|
||||||
if eku == ExtKeyUsageAny {
|
|
||||||
// If any key usage is acceptable, no need to check the chain for
|
|
||||||
// key usages.
|
|
||||||
return candidateChains, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chains = make([][]*Certificate, 0, len(candidateChains))
|
chains = make([][]*Certificate, 0, len(candidateChains))
|
||||||
var incompatibleKeyUsageChains, invalidPoliciesChains int
|
|
||||||
|
var invalidPoliciesChains int
|
||||||
for _, candidate := range candidateChains {
|
for _, candidate := range candidateChains {
|
||||||
if !checkChainForKeyUsage(candidate, opts.KeyUsages) {
|
|
||||||
incompatibleKeyUsageChains++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !policiesValid(candidate, opts) {
|
if !policiesValid(candidate, opts) {
|
||||||
invalidPoliciesChains++
|
invalidPoliciesChains++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
chains = append(chains, candidate)
|
chains = append(chains, candidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(chains) == 0 {
|
||||||
|
return nil, CertificateInvalidError{c, NoValidChains, "all candidate chains have invalid policies"}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, eku := range opts.KeyUsages {
|
||||||
|
if eku == ExtKeyUsageAny {
|
||||||
|
// If any key usage is acceptable, no need to check the chain for
|
||||||
|
// key usages.
|
||||||
|
return chains, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.KeyUsages) == 0 {
|
||||||
|
opts.KeyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
|
||||||
|
}
|
||||||
|
|
||||||
|
candidateChains = chains
|
||||||
|
chains = chains[:0]
|
||||||
|
|
||||||
|
var incompatibleKeyUsageChains int
|
||||||
|
for _, candidate := range candidateChains {
|
||||||
|
if !checkChainForKeyUsage(candidate, opts.KeyUsages) {
|
||||||
|
incompatibleKeyUsageChains++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
chains = append(chains, candidate)
|
||||||
|
}
|
||||||
|
|
||||||
if len(chains) == 0 {
|
if len(chains) == 0 {
|
||||||
var details []string
|
var details []string
|
||||||
if incompatibleKeyUsageChains > 0 {
|
if incompatibleKeyUsageChains > 0 {
|
||||||
|
|||||||
@@ -3012,3 +3012,39 @@ func TestPoliciesValid(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInvalidPolicyWithAnyKeyUsage(t *testing.T) {
|
||||||
|
loadTestCert := func(t *testing.T, path string) *Certificate {
|
||||||
|
b, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p, _ := pem.Decode(b)
|
||||||
|
c, err := ParseCertificate(p.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
testOID3 := mustNewOIDFromInts([]uint64{1, 2, 840, 113554, 4, 1, 72585, 2, 3})
|
||||||
|
root, intermediate, leaf := loadTestCert(t, "testdata/policy_root.pem"), loadTestCert(t, "testdata/policy_intermediate_require.pem"), loadTestCert(t, "testdata/policy_leaf.pem")
|
||||||
|
|
||||||
|
expectedErr := "x509: no valid chains built: all candidate chains have invalid policies"
|
||||||
|
|
||||||
|
roots, intermediates := NewCertPool(), NewCertPool()
|
||||||
|
roots.AddCert(root)
|
||||||
|
intermediates.AddCert(intermediate)
|
||||||
|
|
||||||
|
_, err := leaf.Verify(VerifyOptions{
|
||||||
|
Roots: roots,
|
||||||
|
Intermediates: intermediates,
|
||||||
|
KeyUsages: []ExtKeyUsage{ExtKeyUsageAny},
|
||||||
|
CertificatePolicies: []OID{testOID3},
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("unexpected success, invalid policy shouldn't be bypassed by passing VerifyOptions.KeyUsages with ExtKeyUsageAny")
|
||||||
|
} else if err.Error() != expectedErr {
|
||||||
|
t.Fatalf("unexpected error, got %q, want %q", err, expectedErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ func appendT(h *Hash, v reflect.Value) {
|
|||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
h.WriteByte(btoi(v.Bool()))
|
h.WriteByte(btoi(v.Bool()))
|
||||||
return
|
return
|
||||||
case reflect.UnsafePointer, reflect.Pointer:
|
case reflect.UnsafePointer, reflect.Pointer, reflect.Chan:
|
||||||
var buf [8]byte
|
var buf [8]byte
|
||||||
// because pointing to the abi.Escape call in comparableReady,
|
// because pointing to the abi.Escape call in comparableReady,
|
||||||
// So this is ok to hash pointer,
|
// So this is ok to hash pointer,
|
||||||
|
|||||||
@@ -253,12 +253,17 @@ func TestComparable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
testComparable(t, s1, s2)
|
testComparable(t, s1, s2)
|
||||||
testComparable(t, s1.s, s2.s)
|
testComparable(t, s1.s, s2.s)
|
||||||
|
c1 := make(chan struct{})
|
||||||
|
c2 := make(chan struct{})
|
||||||
|
testComparable(t, c1, c1)
|
||||||
|
testComparable(t, chan struct{}(nil))
|
||||||
testComparable(t, float32(0), negativeZero[float32]())
|
testComparable(t, float32(0), negativeZero[float32]())
|
||||||
testComparable(t, float64(0), negativeZero[float64]())
|
testComparable(t, float64(0), negativeZero[float64]())
|
||||||
testComparableNoEqual(t, math.NaN(), math.NaN())
|
testComparableNoEqual(t, math.NaN(), math.NaN())
|
||||||
testComparableNoEqual(t, [2]string{"a", ""}, [2]string{"", "a"})
|
testComparableNoEqual(t, [2]string{"a", ""}, [2]string{"", "a"})
|
||||||
testComparableNoEqual(t, struct{ a, b string }{"foo", ""}, struct{ a, b string }{"", "foo"})
|
testComparableNoEqual(t, struct{ a, b string }{"foo", ""}, struct{ a, b string }{"", "foo"})
|
||||||
testComparableNoEqual(t, struct{ a, b any }{int(0), struct{}{}}, struct{ a, b any }{struct{}{}, int(0)})
|
testComparableNoEqual(t, struct{ a, b any }{int(0), struct{}{}}, struct{ a, b any }{struct{}{}, int(0)})
|
||||||
|
testComparableNoEqual(t, c1, c2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testComparableNoEqual[T comparable](t *testing.T, v1, v2 T) {
|
func testComparableNoEqual[T comparable](t *testing.T, v1, v2 T) {
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ func Openat(dirfd syscall.Handle, name string, flag int, perm uint32) (_ syscall
|
|||||||
switch {
|
switch {
|
||||||
case flag&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
|
case flag&(syscall.O_CREAT|syscall.O_EXCL) == (syscall.O_CREAT | syscall.O_EXCL):
|
||||||
disposition = FILE_CREATE
|
disposition = FILE_CREATE
|
||||||
|
options |= FILE_OPEN_REPARSE_POINT // don't follow symlinks
|
||||||
case flag&syscall.O_CREAT == syscall.O_CREAT:
|
case flag&syscall.O_CREAT == syscall.O_CREAT:
|
||||||
disposition = FILE_OPEN_IF
|
disposition = FILE_OPEN_IF
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -805,7 +805,8 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensit
|
|||||||
for k, vv := range ireqhdr {
|
for k, vv := range ireqhdr {
|
||||||
sensitive := false
|
sensitive := false
|
||||||
switch CanonicalHeaderKey(k) {
|
switch CanonicalHeaderKey(k) {
|
||||||
case "Authorization", "Www-Authenticate", "Cookie", "Cookie2":
|
case "Authorization", "Www-Authenticate", "Cookie", "Cookie2",
|
||||||
|
"Proxy-Authorization", "Proxy-Authenticate":
|
||||||
sensitive = true
|
sensitive = true
|
||||||
}
|
}
|
||||||
if !(sensitive && stripSensitiveHeaders) {
|
if !(sensitive && stripSensitiveHeaders) {
|
||||||
|
|||||||
@@ -1547,6 +1547,8 @@ func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) {
|
|||||||
if r.Host+r.URL.Path != "a.example.com/" {
|
if r.Host+r.URL.Path != "a.example.com/" {
|
||||||
if h := r.Header.Get("Authorization"); h != "" {
|
if h := r.Header.Get("Authorization"); h != "" {
|
||||||
t.Errorf("on request to %v%v, Authorization=%q, want no header", r.Host, r.URL.Path, h)
|
t.Errorf("on request to %v%v, Authorization=%q, want no header", r.Host, r.URL.Path, h)
|
||||||
|
} else if h := r.Header.Get("Proxy-Authorization"); h != "" {
|
||||||
|
t.Errorf("on request to %v%v, Proxy-Authorization=%q, want no header", r.Host, r.URL.Path, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Follow a chain of redirects from a to b and back to a.
|
// Follow a chain of redirects from a to b and back to a.
|
||||||
@@ -1575,6 +1577,7 @@ func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) {
|
|||||||
req, _ := NewRequest("GET", proto+"://a.example.com/", nil)
|
req, _ := NewRequest("GET", proto+"://a.example.com/", nil)
|
||||||
req.Header.Add("Cookie", "foo=bar")
|
req.Header.Add("Cookie", "foo=bar")
|
||||||
req.Header.Add("Authorization", "secretpassword")
|
req.Header.Add("Authorization", "secretpassword")
|
||||||
|
req.Header.Add("Proxy-Authorization", "secretpassword")
|
||||||
res, err := c.Do(req)
|
res, err := c.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -2223,6 +2223,31 @@ func TestFilePermissions(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenFileCreateExclDanglingSymlink(t *testing.T) {
|
||||||
|
testMaybeRooted(t, func(t *testing.T, r *Root) {
|
||||||
|
const link = "link"
|
||||||
|
if err := Symlink("does_not_exist", link); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var f *File
|
||||||
|
var err error
|
||||||
|
if r == nil {
|
||||||
|
f, err = OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
|
||||||
|
} else {
|
||||||
|
f, err = r.OpenFile(link, O_WRONLY|O_CREATE|O_EXCL, 0o444)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
if !errors.Is(err, ErrExist) {
|
||||||
|
t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL = %v, want ErrExist", err)
|
||||||
|
}
|
||||||
|
if _, err := Stat(link); err == nil {
|
||||||
|
t.Errorf("OpenFile of a dangling symlink with O_CREATE|O_EXCL created a file")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// TestFileRDWRFlags tests the O_RDONLY, O_WRONLY, and O_RDWR flags.
|
// TestFileRDWRFlags tests the O_RDONLY, O_WRONLY, and O_RDWR flags.
|
||||||
func TestFileRDWRFlags(t *testing.T) {
|
func TestFileRDWRFlags(t *testing.T) {
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ type Module struct {
|
|||||||
// - CGO_CPPFLAGS: the effective CGO_CPPFLAGS environment variable
|
// - CGO_CPPFLAGS: the effective CGO_CPPFLAGS environment variable
|
||||||
// - CGO_CXXFLAGS: the effective CGO_CXXFLAGS environment variable
|
// - CGO_CXXFLAGS: the effective CGO_CXXFLAGS environment variable
|
||||||
// - CGO_LDFLAGS: the effective CGO_LDFLAGS environment variable
|
// - CGO_LDFLAGS: the effective CGO_LDFLAGS environment variable
|
||||||
|
// - DefaultGODEBUG: the effective GODEBUG settings
|
||||||
// - GOARCH: the architecture target
|
// - GOARCH: the architecture target
|
||||||
// - GOAMD64/GOARM/GO386/etc: the architecture feature level for GOARCH
|
// - GOAMD64/GOARM/GO386/etc: the architecture feature level for GOARCH
|
||||||
// - GOOS: the operating system target
|
// - GOOS: the operating system target
|
||||||
|
|||||||
@@ -376,20 +376,6 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
|
|||||||
if flag&O_CLOEXEC == 0 {
|
if flag&O_CLOEXEC == 0 {
|
||||||
sa = makeInheritSa()
|
sa = makeInheritSa()
|
||||||
}
|
}
|
||||||
// We don't use CREATE_ALWAYS, because when opening a file with
|
|
||||||
// FILE_ATTRIBUTE_READONLY these will replace an existing file
|
|
||||||
// with a new, read-only one. See https://go.dev/issue/38225.
|
|
||||||
//
|
|
||||||
// Instead, we ftruncate the file after opening when O_TRUNC is set.
|
|
||||||
var createmode uint32
|
|
||||||
switch {
|
|
||||||
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
|
||||||
createmode = CREATE_NEW
|
|
||||||
case flag&O_CREAT == O_CREAT:
|
|
||||||
createmode = OPEN_ALWAYS
|
|
||||||
default:
|
|
||||||
createmode = OPEN_EXISTING
|
|
||||||
}
|
|
||||||
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
|
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
|
||||||
if perm&S_IWRITE == 0 {
|
if perm&S_IWRITE == 0 {
|
||||||
attrs = FILE_ATTRIBUTE_READONLY
|
attrs = FILE_ATTRIBUTE_READONLY
|
||||||
@@ -404,6 +390,21 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
|
|||||||
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
||||||
attrs |= _FILE_FLAG_WRITE_THROUGH
|
attrs |= _FILE_FLAG_WRITE_THROUGH
|
||||||
}
|
}
|
||||||
|
// We don't use CREATE_ALWAYS, because when opening a file with
|
||||||
|
// FILE_ATTRIBUTE_READONLY these will replace an existing file
|
||||||
|
// with a new, read-only one. See https://go.dev/issue/38225.
|
||||||
|
//
|
||||||
|
// Instead, we ftruncate the file after opening when O_TRUNC is set.
|
||||||
|
var createmode uint32
|
||||||
|
switch {
|
||||||
|
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||||
|
createmode = CREATE_NEW
|
||||||
|
attrs |= FILE_FLAG_OPEN_REPARSE_POINT // don't follow symlinks
|
||||||
|
case flag&O_CREAT == O_CREAT:
|
||||||
|
createmode = OPEN_ALWAYS
|
||||||
|
default:
|
||||||
|
createmode = OPEN_EXISTING
|
||||||
|
}
|
||||||
h, err := createFile(namep, access, sharemode, sa, createmode, attrs, 0)
|
h, err := createFile(namep, access, sharemode, sa, createmode, attrs, 0)
|
||||||
if h == InvalidHandle {
|
if h == InvalidHandle {
|
||||||
if err == ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {
|
if err == ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user