OSDN Git Service

* fhandler.cc (fhandler_base::open): Never open files with
authorcorinna <corinna>
Thu, 25 Aug 2011 13:35:42 +0000 (13:35 +0000)
committercorinna <corinna>
Thu, 25 Aug 2011 13:35:42 +0000 (13:35 +0000)
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.

winsup/cygwin/ChangeLog
winsup/cygwin/fhandler.cc

index 1308ffa..db2d920 100644 (file)
@@ -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
index 4e1c0a6..722f111 100644 (file)
@@ -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 ());