t.Fatal("comparing two directories:", err)
}
}
+
+func TestAppendDoesntOverwrite(t *testing.T) {
+ name := filepath.Join(t.TempDir(), "file")
+ if err := WriteFile(name, []byte("hello"), 0666); err != nil {
+ t.Fatal(err)
+ }
+ f, err := OpenFile(name, O_APPEND|O_WRONLY, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := f.Write([]byte(" world")); err != nil {
+ f.Close()
+ t.Fatal(err)
+ }
+ if err := f.Close(); err != nil {
+ t.Fatal(err)
+ }
+ got, err := ReadFile(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := "hello world"
+ if string(got) != want {
+ t.Fatalf("got %q, want %q", got, want)
+ }
+}
access |= GENERIC_WRITE
}
if flag&O_APPEND != 0 {
- access |= FILE_APPEND_DATA
- // Remove GENERIC_WRITE access unless O_TRUNC is set,
- // in which case we need it to truncate the file.
+ // Remove GENERIC_WRITE unless O_TRUNC is set, in which case we need it to truncate the file.
+ // We can't just remove FILE_WRITE_DATA because GENERIC_WRITE without FILE_WRITE_DATA
+ // starts appending at the beginning of the file rather than at the end.
if flag&O_TRUNC == 0 {
access &^= GENERIC_WRITE
}
+ // Set all access rights granted by GENERIC_WRITE except for FILE_WRITE_DATA.
+ access |= FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | _FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE
}
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE)
var sa *SecurityAttributes
FILE_LIST_DIRECTORY = 0x00000001
FILE_APPEND_DATA = 0x00000004
+ _FILE_WRITE_EA = 0x00000010
FILE_WRITE_ATTRIBUTES = 0x00000100
FILE_SHARE_READ = 0x00000001