]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.14] syscall: preserve Windows file permissions for O_CREAT|O_TRUNC
authorIan Lance Taylor <iant@golang.org>
Wed, 20 May 2020 03:23:58 +0000 (20:23 -0700)
committerIan Lance Taylor <iant@golang.org>
Sat, 23 May 2020 21:08:37 +0000 (21:08 +0000)
On Windows, calling syscall.Open(file, O_CREAT|O_TRUNC, 0) for a file
that already exists would change the file to be read-only.
That is not how the Unix syscall.Open behaves, so avoid it on
Windows by calling CreateFile twice if necessary.

For #38225
Fixes #39158

Change-Id: I70097fca8863df427cc8a97b9376a9ffc69c6318
Reviewed-on: https://go-review.googlesource.com/c/go/+/234534
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
(cherry picked from commit 567556d78657326c99b8fa84ec2a5ee511a0941b)
Reviewed-on: https://go-review.googlesource.com/c/go/+/234686
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
src/os/os_test.go
src/syscall/syscall_windows.go

index 278c19e44be1bbd38c13f60cdb4005dee0bba5f0..31b3e673194f88bda3a4cc5a8b1b39e57bec89b9 100644 (file)
@@ -2448,3 +2448,38 @@ func TestDirSeek(t *testing.T) {
                }
        }
 }
+
+// Test that opening a file does not change its permissions.  Issue 38225.
+func TestOpenFileKeepsPermissions(t *testing.T) {
+       t.Parallel()
+       dir, err := ioutil.TempDir("", "TestOpenFileKeepsPermissions")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer RemoveAll(dir)
+       name := filepath.Join(dir, "x")
+       f, err := Create(name)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := f.Close(); err != nil {
+               t.Error(err)
+       }
+       f, err = OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if fi, err := f.Stat(); err != nil {
+               t.Error(err)
+       } else if fi.Mode()&0222 == 0 {
+               t.Errorf("f.Stat.Mode after OpenFile is %v, should be writable", fi.Mode())
+       }
+       if err := f.Close(); err != nil {
+               t.Error(err)
+       }
+       if fi, err := Stat(name); err != nil {
+               t.Error(err)
+       } else if fi.Mode()&0222 == 0 {
+               t.Errorf("Stat after OpenFile is %v, should be writable", fi.Mode())
+       }
+}
index 950c281e4db4fce2f66efd6f12cf4d986aacc0d2..41bc42a44e1b8aa7452ca04471185b9b351f601a 100644 (file)
@@ -334,6 +334,26 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
        var attrs uint32 = FILE_ATTRIBUTE_NORMAL
        if perm&S_IWRITE == 0 {
                attrs = FILE_ATTRIBUTE_READONLY
+               if createmode == CREATE_ALWAYS {
+                       // We have been asked to create a read-only file.
+                       // If the file already exists, the semantics of
+                       // the Unix open system call is to preserve the
+                       // existing permissions. If we pass CREATE_ALWAYS
+                       // and FILE_ATTRIBUTE_READONLY to CreateFile,
+                       // and the file already exists, CreateFile will
+                       // change the file permissions.
+                       // Avoid that to preserve the Unix semantics.
+                       h, e := CreateFile(pathp, access, sharemode, sa, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
+                       switch e {
+                       case ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, ERROR_PATH_NOT_FOUND:
+                               // File does not exist. These are the same
+                               // errors as Errno.Is checks for ErrNotExist.
+                               // Carry on to create the file.
+                       default:
+                               // Success or some different error.
+                               return h, e
+                       }
+               }
        }
        h, e := CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
        return h, e