summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2011-08-25 13:35:43 +0000
committerCorinna Vinschen <corinna@vinschen.de>2011-08-25 13:35:43 +0000
commit603ef545bdbdbf7495e1a0bbabffb8741fc2a5bb (patch)
treeec5e03c1477833fb0dfe38c7e4c4ba208d8421d4
parent929a140824b8150683df0ce1f8bef0beec3a7f6c (diff)
downloadcygnal-603ef545bdbdbf7495e1a0bbabffb8741fc2a5bb.tar.gz
cygnal-603ef545bdbdbf7495e1a0bbabffb8741fc2a5bb.tar.bz2
cygnal-603ef545bdbdbf7495e1a0bbabffb8741fc2a5bb.zip
* fhandler.cc (fhandler_base::open): Never open files with
FILE_OVERWITE/FILE_OVERWRITE_IF. Set file size to 0 explicitely if regular, existing file has been opened for writing with O_TRUNC flag set. Explain why.
-rw-r--r--winsup/cygwin/ChangeLog7
-rw-r--r--winsup/cygwin/fhandler.cc42
2 files changed, 37 insertions, 12 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index 1308ffa39..db2d9201a 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,10 @@
+2011-08-25 Corinna Vinschen <corinna@vinschen.de>
+
+ * fhandler.cc (fhandler_base::open): Never open files with
+ FILE_OVERWITE/FILE_OVERWRITE_IF. Set file size to 0 explicitely if
+ regular, existing file has been opened for writing with O_TRUNC flag
+ set. Explain why.
+
2011-08-24 Corinna Vinschen <corinna@vinschen.de>
* thread.cc (pthread::pthread): Drop setting parent_tls. Call
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 4e1c0a6c1..722f11102 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -540,20 +540,12 @@ fhandler_base::open (int flags, mode_t mode)
break;
}
- if ((flags & O_TRUNC) && ((flags & O_ACCMODE) != O_RDONLY))
- {
- if (flags & O_CREAT)
- create_disposition = FILE_OVERWRITE_IF;
- else
- create_disposition = FILE_OVERWRITE;
- }
- else if (flags & O_CREAT)
- create_disposition = FILE_OPEN_IF;
- else
- create_disposition = FILE_OPEN;
-
+ /* Don't use the FILE_OVERWRITE{_IF} flags here. See below for an
+ explanation, why that's not such a good idea. */
if ((flags & O_EXCL) && (flags & O_CREAT))
create_disposition = FILE_CREATE;
+ else
+ create_disposition = (flags & O_CREAT) ? FILE_OPEN_IF : FILE_OPEN;
if (get_device () == FH_FS)
{
@@ -664,6 +656,32 @@ fhandler_base::open (int flags, mode_t mode)
if (io.Information == FILE_CREATED && has_acls ())
set_file_attribute (fh, pc, ILLEGAL_UID, ILLEGAL_GID, S_JUSTCREATED | mode);
+ /* If you O_TRUNC a file on Linux, the data is truncated, but the EAs are
+ preserved. If you open a file on Windows with FILE_OVERWRITE{_IF} or
+ FILE_SUPERSEDE, all streams are truncated, including the EAs. So we don't
+ use the FILE_OVERWRITE{_IF} flags, but instead just open the file and set
+ the size of the data stream explicitely to 0. Apart from being more Linux
+ compatible, this implementation has the pleasant side-effect to be more
+ than 5% faster than using FILE_OVERWRITE{_IF} (tested on W7 32 bit). */
+ if ((flags & O_TRUNC)
+ && (flags & O_ACCMODE) != O_RDONLY
+ && io.Information != FILE_CREATED
+ && get_device () == FH_FS)
+ {
+ FILE_END_OF_FILE_INFORMATION feofi = { EndOfFile:{ QuadPart:0 } };
+ status = NtSetInformationFile (fh, &io, &feofi, sizeof feofi,
+ FileEndOfFileInformation);
+ /* In theory, truncating the file should never fail, since the opened
+ handle has FILE_READ_DATA permissions, which is all you need to
+ be allowed to truncate a file. Better safe than sorry. */
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ NtClose (fh);
+ goto done;
+ }
+ }
+
set_io_handle (fh);
set_flags (flags, pc.binmode ());