Added support for FILE_DIRECTORY_FILE and FILE_NON_DIRECTORY_FILE open
options.

diff --git a/files/file.c b/files/file.c
index b57b9a0..5c673de 100644
--- a/files/file.c
+++ b/files/file.c
@@ -204,6 +204,8 @@
     options = 0;
     if (attributes & FILE_FLAG_BACKUP_SEMANTICS)
         options |= FILE_OPEN_FOR_BACKUP_INTENT;
+    else
+        options |= FILE_NON_DIRECTORY_FILE;
     if (attributes & FILE_FLAG_DELETE_ON_CLOSE)
         options |= FILE_DELETE_ON_CLOSE;
     if (!(attributes & FILE_FLAG_OVERLAPPED))
diff --git a/server/fd.c b/server/fd.c
index 1b668d7..68460d9 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -26,6 +26,7 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <signal.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
@@ -43,6 +44,10 @@
 #include "process.h"
 #include "request.h"
 
+#include "winbase.h"
+#include "winreg.h"
+#include "winternl.h"
+
 /* Because of the stupid Posix locking semantics, we need to keep
  * track of all file descriptors referencing a given file, and not
  * close a single one until all the locks are gone (sigh).
@@ -415,7 +420,10 @@
             /* make sure it is still the same file */
             struct stat st;
             if (!stat( fd->unlink, &st ) && st.st_dev == inode->dev && st.st_ino == inode->ino)
-                unlink( fd->unlink );
+            {
+                if (S_ISDIR(st.st_mode)) rmdir( fd->unlink );
+                else unlink( fd->unlink );
+            }
         }
         free( fd );
     }
@@ -907,13 +915,15 @@
 /* the fd must have been created with alloc_fd */
 /* on error the fd object is released */
 struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
-                    unsigned int access, unsigned int sharing, const char *unlink_name )
+                    unsigned int access, unsigned int sharing, unsigned int options )
 {
     struct stat st;
     struct closed_fd *closed_fd;
+    const char *unlink_name = "";
 
     assert( fd->unix_fd == -1 );
 
+    if (options & FILE_DELETE_ON_CLOSE) unlink_name = name;
     if (!(closed_fd = mem_alloc( sizeof(*closed_fd) + strlen(unlink_name) )))
     {
         release_object( fd );
@@ -931,7 +941,20 @@
     fstat( fd->unix_fd, &st );
     *mode = st.st_mode;
 
-    if (S_ISREG(st.st_mode))  /* only bother with an inode for normal files */
+    /* check directory options */
+    if ((options & FILE_DIRECTORY_FILE) && !S_ISDIR(st.st_mode))
+    {
+        set_error( STATUS_NOT_A_DIRECTORY );
+        goto error;
+    }
+    if ((options & FILE_NON_DIRECTORY_FILE) && S_ISDIR(st.st_mode))
+    {
+        set_error( STATUS_FILE_IS_A_DIRECTORY );
+        goto error;
+    }
+
+    /* only bother with an inode for normal files and directories */
+    if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))
     {
         struct inode *inode = get_inode( st.st_dev, st.st_ino );
 
@@ -940,9 +963,7 @@
             /* we can close the fd because there are no others open on the same file,
              * otherwise we wouldn't have failed to allocate a new inode
              */
-            release_object( fd );
-            free( closed_fd );
-            return NULL;
+            goto error;
         }
         fd->inode = inode;
         fd->closed = closed_fd;
@@ -957,15 +978,19 @@
     }
     else
     {
-        free( closed_fd );
         if (unlink_name[0])  /* we can't unlink special files */
         {
-            release_object( fd );
             set_error( STATUS_INVALID_PARAMETER );
-            return NULL;
+            goto error;
         }
+        free( closed_fd );
     }
     return fd;
+
+error:
+    release_object( fd );
+    free( closed_fd );
+    return NULL;
 }
 
 /* create an fd for an anonymous file */
diff --git a/server/file.c b/server/file.c
index 509a7c0..a748766 100644
--- a/server/file.c
+++ b/server/file.c
@@ -167,8 +167,7 @@
     /* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */
     if (!(file->fd = alloc_fd( &file_fd_ops, &file->obj )) ||
         !(file->fd = open_fd( file->fd, name, flags | O_NONBLOCK | O_LARGEFILE,
-                              &mode, access, sharing,
-                              (options & FILE_DELETE_ON_CLOSE) ? name : "" )))
+                              &mode, access, sharing, options )))
     {
         free( name );
         release_object( file );
@@ -176,13 +175,6 @@
     }
     free( name );
 
-    /* refuse to open a directory */
-    if (S_ISDIR(mode) && !(options & FILE_OPEN_FOR_BACKUP_INTENT))
-    {
-        set_error( STATUS_ACCESS_DENIED );
-        release_object( file );
-        return NULL;
-    }
     /* check for serial port */
     if (S_ISCHR(mode) && is_serial_fd( file->fd ))
     {
diff --git a/server/file.h b/server/file.h
index 159b630..92321e4 100644
--- a/server/file.h
+++ b/server/file.h
@@ -46,7 +46,7 @@
 
 extern struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user );
 extern struct fd *open_fd( struct fd *fd, const char *name, int flags, mode_t *mode,
-                           unsigned int access, unsigned int sharing, const char *unlink_name );
+                           unsigned int access, unsigned int sharing, unsigned int options );
 extern struct fd *create_anonymous_fd( const struct fd_ops *fd_user_ops,
                                        int unix_fd, struct object *user );
 extern void *get_fd_user( struct fd *fd );
diff --git a/server/trace.c b/server/trace.c
index 81afe59..3d9d7b9 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -3082,6 +3082,7 @@
         NAME(DIRECTORY_NOT_EMPTY),
         NAME(DISK_FULL),
         NAME(DLL_NOT_FOUND),
+        NAME(FILE_IS_A_DIRECTORY),
         NAME(FILE_LOCK_CONFLICT),
         NAME(INVALID_FILE_FOR_SECTION),
         NAME(INVALID_HANDLE),
@@ -3089,6 +3090,7 @@
         NAME(KEY_DELETED),
         NAME(MEDIA_WRITE_PROTECTED),
         NAME(MUTANT_NOT_OWNED),
+        NAME(NOT_A_DIRECTORY),
         NAME(NOT_IMPLEMENTED),
         NAME(NOT_REGISTRY_FILE),
         NAME(NO_DATA_DETECTED),