[PATCH] support for synchronous and non-buffered I/O

Dave Hansen haveblue at us.ibm.com
Wed Jan 16 01:10:37 CST 2008


I have a little Eye-Fi card (http://www.eye.fi).  The software to
control it is a windows application that writes to control files which
appear in a DOS filesystem on the SD card.  (you can read more
information here: http://dave-hansen.blogspot.com/)

However, the application does not quite work under wine.  The
application writes to the card, but doesn't get the results it expects.
It loops doing read()s of the files looking for results until it times
out.

I traced the problem down to I/O not being resubmitted each time the
application does a write to the disk.  This is because the
FILE_FLAG_NO_BUFFERING and FILE_FLAG_WRITE_THROUGH CreateFile()
attributes are being silently dropped by the FILE_ATTRIBUTE_VALID_FLAGS
mask inside of wine.

You can get more information on the semantics of those flags here:

	http://support.microsoft.com/kb/99794

This patch makes it valid for those flags to get passed into open_fd()
where they are converted into the appropriate Linux flags.  From the
descriptions on the Microsoft pages, I think FILE_FLAG_NO_BUFFERING is
effectively O_DIRECT and FILE_FLAG_WRITE_THROUGH is effectively O_SYNC.

I've chosen to implement them by a fcntl() instead of passing them into
open() so that filesystems that don't support them (like NTFS on Linux)
won't error out.  I would really appreciate suggestions for a better way
to print out a warning in that case.

-- Dave

diff --git a/include/winternl.h b/include/winternl.h
index 79f3184..71877c8 100644
--- a/include/winternl.h
+++ b/include/winternl.h
@@ -1416,7 +1416,7 @@ typedef struct _RTL_HANDLE_TABLE
 #define FILE_OPEN_OFFLINE_FILE          0x00400000
 #define FILE_OPEN_FOR_FREE_SPACE_QUERY  0x00800000
 
-#define FILE_ATTRIBUTE_VALID_FLAGS      0x00007fb7
+#define FILE_ATTRIBUTE_VALID_FLAGS      0x00007fb7|FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH
 #define FILE_ATTRIBUTE_VALID_SET_FLAGS  0x000031a7
 
 /* status for NtCreateFile or NtOpenFile */
diff --git a/server/fd.c b/server/fd.c
index c7f20d5..b3c341f 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -1521,6 +1521,12 @@ struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int acce
     struct fd *fd;
     const char *unlink_name = "";
     int rw_mode;
+    int do_truncate = 0;
+    int open_flags;
+
+    if (flags & O_TRUNC)
+        do_truncate = 1;
+    flags &= ~O_TRUNC;
 
     if ((options & FILE_DELETE_ON_CLOSE) && !(access & DELETE))
     {
@@ -1549,7 +1555,8 @@ struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int acce
                 goto error;
             }
         }
-        flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
+        flags &= ~(O_CREAT | O_EXCL);
+        do_truncate = 0;
     }
 
     if ((access & FILE_UNIX_WRITE_ACCESS) && !(options & FILE_DIRECTORY_FILE))
@@ -1559,17 +1566,31 @@ struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int acce
     }
     else rw_mode = O_RDONLY;
 
-    if ((fd->unix_fd = open( name, rw_mode | (flags & ~O_TRUNC), *mode )) == -1)
+    /*
+     * Some filesystems and devices do not support O_DIRECT/SYNC,
+     * and this can cause an open() to fail.  For now, we want to
+     * be able to fall back, so we'll attempt to _add_ these in
+     * a second.  But, for now, leave them out.
+     */
+    open_flags = flags & ~(O_DIRECT|O_SYNC);
+    if ((fd->unix_fd = open( name, rw_mode | open_flags, *mode )) == -1)
     {
         /* if we tried to open a directory for write access, retry read-only */
         if (errno != EISDIR ||
             !(access & FILE_UNIX_WRITE_ACCESS) ||
-            (fd->unix_fd = open( name, O_RDONLY | (flags & ~O_TRUNC), *mode )) == -1)
+            (fd->unix_fd = open( name, O_RDONLY | open_flags, *mode )) == -1)
         {
             file_set_error();
             goto error;
         }
     }
+    if (open_flags != flags) {
+        int err;
+        err = fcntl(fd->unix_fd, F_SETFL, flags);
+        if (err)
+            printf("warning: unable to set flags: 0x%08x on file '%s': %d\n",
+                   flags, name, errno);
+    }
 
     closed_fd->unix_fd = fd->unix_fd;
     closed_fd->unlink[0] = 0;
@@ -1612,7 +1633,7 @@ struct fd *open_fd( const char *name, int flags, mode_t *mode, unsigned int acce
             return NULL;
         }
         strcpy( closed_fd->unlink, unlink_name );
-        if (flags & O_TRUNC) ftruncate( fd->unix_fd, 0 );
+        if (do_truncate) ftruncate( fd->unix_fd, 0 );
     }
     else  /* special file */
     {
diff --git a/server/file.c b/server/file.c
index 5224e85..376b95e 100644
--- a/server/file.c
+++ b/server/file.c
@@ -202,6 +202,11 @@ static struct object *create_file( const char *nameptr, data_size_t len, unsigne
 
     access = generic_file_map_access( access );
 
+    if (attrs & FILE_FLAG_NO_BUFFERING)
+	    flags |= O_DIRECT;
+    if (attrs & FILE_FLAG_WRITE_THROUGH)
+	    flags |= O_SYNC;
+
     /* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */
     fd = open_fd( name, flags | O_NONBLOCK | O_LARGEFILE, &mode, access, sharing, options );
     if (!fd) goto done;





More information about the wine-patches mailing list