Skip to content

Commit eba71b1

Browse files
committed
Go 1.24.1.
1 parent d78239b commit eba71b1

File tree

1 file changed

+56
-45
lines changed

1 file changed

+56
-45
lines changed

util/osutil/open_windows.go

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,96 +15,105 @@ func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
1515
if name == "" {
1616
return nil, &os.PathError{Op: "open", Path: name, Err: ENOENT}
1717
}
18-
r, e := syscallOpen(name, flag|O_CLOEXEC, uint32(perm.Perm()))
19-
if e != nil {
20-
return nil, &os.PathError{Op: "open", Path: name, Err: e}
18+
r, err := syscallOpen(name, flag|O_CLOEXEC, uint32(perm.Perm()))
19+
if err != nil {
20+
return nil, &os.PathError{Op: "open", Path: name, Err: err}
2121
}
2222
return os.NewFile(uintptr(r), name), nil
2323
}
2424

2525
// syscallOpen is a copy of [syscall.Open]
26-
// that uses [syscall.FILE_SHARE_DELETE].
26+
// that uses [syscall.FILE_SHARE_DELETE],
27+
// and supports [syscall.FILE_FLAG_OVERLAPPED].
2728
//
2829
// https://go.dev/src/syscall/syscall_windows.go
29-
func syscallOpen(path string, mode int, perm uint32) (fd Handle, err error) {
30-
if len(path) == 0 {
30+
func syscallOpen(name string, flag int, perm uint32) (fd Handle, err error) {
31+
if len(name) == 0 {
3132
return InvalidHandle, ERROR_FILE_NOT_FOUND
3233
}
33-
pathp, err := UTF16PtrFromString(path)
34+
namep, err := UTF16PtrFromString(name)
3435
if err != nil {
3536
return InvalidHandle, err
3637
}
3738
var access uint32
38-
switch mode & (O_RDONLY | O_WRONLY | O_RDWR) {
39+
switch flag & (O_RDONLY | O_WRONLY | O_RDWR) {
3940
case O_RDONLY:
4041
access = GENERIC_READ
4142
case O_WRONLY:
4243
access = GENERIC_WRITE
4344
case O_RDWR:
4445
access = GENERIC_READ | GENERIC_WRITE
4546
}
46-
if mode&O_CREAT != 0 {
47+
if flag&O_CREAT != 0 {
4748
access |= GENERIC_WRITE
4849
}
49-
if mode&O_APPEND != 0 {
50-
access &^= GENERIC_WRITE
51-
access |= FILE_APPEND_DATA
50+
if flag&O_APPEND != 0 {
51+
// Remove GENERIC_WRITE unless O_TRUNC is set, in which case we need it to truncate the file.
52+
// We can't just remove FILE_WRITE_DATA because GENERIC_WRITE without FILE_WRITE_DATA
53+
// starts appending at the beginning of the file rather than at the end.
54+
if flag&O_TRUNC == 0 {
55+
access &^= GENERIC_WRITE
56+
}
57+
// Set all access rights granted by GENERIC_WRITE except for FILE_WRITE_DATA.
58+
access |= FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | _FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE
5259
}
5360
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
5461
var sa *SecurityAttributes
55-
if mode&O_CLOEXEC == 0 {
62+
if flag&O_CLOEXEC == 0 {
5663
sa = makeInheritSa()
5764
}
65+
// We don't use CREATE_ALWAYS, because when opening a file with
66+
// FILE_ATTRIBUTE_READONLY these will replace an existing file
67+
// with a new, read-only one. See https://go.dev/issue/38225.
68+
//
69+
// Instead, we ftruncate the file after opening when O_TRUNC is set.
5870
var createmode uint32
5971
switch {
60-
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
72+
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
6173
createmode = CREATE_NEW
62-
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
63-
createmode = CREATE_ALWAYS
64-
case mode&O_CREAT == O_CREAT:
74+
case flag&O_CREAT == O_CREAT:
6575
createmode = OPEN_ALWAYS
66-
case mode&O_TRUNC == O_TRUNC:
67-
createmode = TRUNCATE_EXISTING
6876
default:
6977
createmode = OPEN_EXISTING
7078
}
7179
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
7280
if perm&S_IWRITE == 0 {
7381
attrs = FILE_ATTRIBUTE_READONLY
74-
if createmode == CREATE_ALWAYS {
75-
const _ERROR_BAD_NETPATH = Errno(53)
76-
// We have been asked to create a read-only file.
77-
// If the file already exists, the semantics of
78-
// the Unix open system call is to preserve the
79-
// existing permissions. If we pass CREATE_ALWAYS
80-
// and FILE_ATTRIBUTE_READONLY to CreateFile,
81-
// and the file already exists, CreateFile will
82-
// change the file permissions.
83-
// Avoid that to preserve the Unix semantics.
84-
h, e := CreateFile(pathp, access, sharemode, sa, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
85-
switch e {
86-
case ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, ERROR_PATH_NOT_FOUND:
87-
// File does not exist. These are the same
88-
// errors as Errno.Is checks for ErrNotExist.
89-
// Carry on to create the file.
90-
default:
91-
// Success or some different error.
92-
return h, e
93-
}
94-
}
9582
}
96-
if createmode == OPEN_EXISTING && access == GENERIC_READ {
97-
// Necessary for opening directory handles.
83+
if flag&O_WRONLY == 0 && flag&O_RDWR == 0 {
84+
// We might be opening or creating a directory.
85+
// CreateFile requires FILE_FLAG_BACKUP_SEMANTICS
86+
// to work with directories.
9887
attrs |= FILE_FLAG_BACKUP_SEMANTICS
9988
}
100-
if mode&O_SYNC != 0 {
89+
if flag&O_SYNC != 0 {
10190
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
10291
attrs |= _FILE_FLAG_WRITE_THROUGH
10392
}
104-
if mode&O_NONBLOCK != 0 {
93+
if flag&O_NONBLOCK != 0 {
10594
attrs |= FILE_FLAG_OVERLAPPED
10695
}
107-
return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
96+
h, err := CreateFile(namep, access, sharemode, sa, createmode, attrs, 0)
97+
if h == InvalidHandle {
98+
if err == ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {
99+
// We should return EISDIR when we are trying to open a directory with write access.
100+
fa, e1 := GetFileAttributes(namep)
101+
if e1 == nil && fa&FILE_ATTRIBUTE_DIRECTORY != 0 {
102+
err = EISDIR
103+
}
104+
}
105+
return h, err
106+
}
107+
// Ignore O_TRUNC if the file has just been created.
108+
if flag&O_TRUNC == O_TRUNC &&
109+
(createmode == OPEN_EXISTING || (createmode == OPEN_ALWAYS /*&& err == ERROR_ALREADY_EXISTS*/)) {
110+
err = Ftruncate(h, 0)
111+
if err != nil {
112+
CloseHandle(h)
113+
return InvalidHandle, err
114+
}
115+
}
116+
return h, nil
108117
}
109118

110119
func makeInheritSa() *SecurityAttributes {
@@ -113,3 +122,5 @@ func makeInheritSa() *SecurityAttributes {
113122
sa.InheritHandle = 1
114123
return &sa
115124
}
125+
126+
const _FILE_WRITE_EA = 0x00000010

0 commit comments

Comments
 (0)