Skip to content

Commit 4b5a64f

Browse files
committed
os: set FILE_FLAG_BACKUP_SEMANTICS when opening without I/O access
FILE_FLAG_BACKUP_SEMANTICS is necessary to open directories on Windows, and to enable backup applications do extended operations on files if they hold the SE_BACKUP_NAME and SE_RESTORE_NAME privileges. os.OpenFile currently sets FILE_FLAG_BACKUP_SEMANTICS for all supported cases except when the file is opened with O_WRONLY | O_RDWR (that is, access mode 3). This access mode doesn't correspond to any of the standard POSIX access modes, but some OSes special case it to mean different things. For example, on Linux, O_WRONLY | O_RDWR means check for read and write permission on the file and return a file descriptor that can't be used for reading or writing. On Windows, os.OpenFile has historically mapped O_WRONLY | O_RDWR to a 0 access mode, which Windows internally interprets as FILE_READ_ATTRIBUTES. Additionally, it doesn't prepare the file for I/O, given that the read attributes permission doesn't allow reading or writing (not that this is similar to what happens on Linux). This makes opening the file around 50% faster, and one can still use the handle to stat it, so some projects have been using this behavior to open files without I/O access. This CL updates os.OpenFile so that directories can also be opened without I/O access. This effectively closes #23312, as all the remaining cases where we don't set FILE_FLAG_BACKUP_SEMANTICS imply opening with O_WRONLY or O_RDWR, and that's not allowed by Unix's open. Closes #23312. Change-Id: I77c4f55e1ca377789aef75bd8a9bce2b7499f91d Reviewed-on: https://go-review.googlesource.com/c/go/+/673035 Reviewed-by: Damien Neil <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 7b4a3d9 commit 4b5a64f

File tree

2 files changed

+14
-6
lines changed

2 files changed

+14
-6
lines changed

src/syscall/syscall_windows.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,9 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
370370
if err != nil {
371371
return InvalidHandle, err
372372
}
373+
accessFlags := flag & (O_RDONLY | O_WRONLY | O_RDWR)
373374
var access uint32
374-
switch flag & (O_RDONLY | O_WRONLY | O_RDWR) {
375+
switch accessFlags {
375376
case O_RDONLY:
376377
access = GENERIC_READ
377378
case O_WRONLY:
@@ -416,9 +417,15 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
416417
if perm&S_IWRITE == 0 {
417418
attrs = FILE_ATTRIBUTE_READONLY
418419
}
419-
if flag&O_WRONLY == 0 && flag&O_RDWR == 0 {
420-
// We might be opening or creating a directory.
421-
// CreateFile requires FILE_FLAG_BACKUP_SEMANTICS
420+
switch accessFlags {
421+
case O_WRONLY, O_RDWR:
422+
// Unix doesn't allow opening a directory with O_WRONLY
423+
// or O_RDWR, so we don't set the flag in that case,
424+
// which will make CreateFile fail with ERROR_ACCESS_DENIED.
425+
// We will map that to EISDIR if the file is a directory.
426+
default:
427+
// We might be opening a directory for reading,
428+
// and CreateFile requires FILE_FLAG_BACKUP_SEMANTICS
422429
// to work with directories.
423430
attrs |= FILE_FLAG_BACKUP_SEMANTICS
424431
}
@@ -428,7 +435,7 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
428435
}
429436
h, err := createFile(namep, access, sharemode, sa, createmode, attrs, 0)
430437
if h == InvalidHandle {
431-
if err == ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {
438+
if err == ERROR_ACCESS_DENIED && (attrs&FILE_FLAG_BACKUP_SEMANTICS == 0) {
432439
// We should return EISDIR when we are trying to open a directory with write access.
433440
fa, e1 := GetFileAttributes(namep)
434441
if e1 == nil && fa&FILE_ATTRIBUTE_DIRECTORY != 0 {

src/syscall/syscall_windows_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ func TestOpen(t *testing.T) {
3636
{dir, syscall.O_RDONLY | syscall.O_CREAT, nil},
3737
{file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE, nil},
3838
{file, syscall.O_APPEND | syscall.O_WRONLY | os.O_CREATE | os.O_TRUNC, nil},
39+
{file, syscall.O_WRONLY | syscall.O_RDWR, nil},
40+
{dir, syscall.O_WRONLY | syscall.O_RDWR, nil},
3941
{dir, syscall.O_RDONLY | syscall.O_TRUNC, syscall.ERROR_ACCESS_DENIED},
40-
{dir, syscall.O_WRONLY | syscall.O_RDWR, syscall.EISDIR},
4142
{dir, syscall.O_WRONLY, syscall.EISDIR},
4243
{dir, syscall.O_RDWR, syscall.EISDIR},
4344
}

0 commit comments

Comments
 (0)