Builds and runs on mac and linux,
passes tests on linux and vista.
Fixes http://bugs.winehq.org/show_bug.cgi?id=15679

It's worth discussing a bit before posting to wine-patches.
This patch uses filesystem extended attributes to
store the win32 attribute bits.  These are available
fairly widely finally - I think current Linux, Mac, and Solaris
all support some form of extended attributes.

Because the extended attribute API varies somewhat wildly
between operating systems, I went whole-hog and used
autoconf to detect the differences between the api on
linux and macosx.  Somebody else should be able to
add solaris support without much difficulty if this is approved.

The convention chosen is that the win32 file attributes
are stored as a four digit hex number in the extended
attribute named user.wine.attr.  (The 'user' prefix is suggested
by http://www.freedesktop.org/wiki/CommonExtendedAttributes .)

For backwards compatibility,
if a native file lacks extended attributes; it is reported as having
ARCHIVE attribute.
Likewise, if a file just has the ARCHIVE attribute set, there is no
native extended attribute.

Here's an example:
  touch foo.dat
  wine cmd /c attrib -a foo.dat
  xattr -l foo.dat
This outputs
  user.wine.attr: 0000

i.e. because the file has anything but the default attribute, we store
the entire win32 attribute bitmask in the extended attribute.

Another example:
  touch bar.dat
  wine cmd /c attrib +s bar.dat
  xattr -l bar.dat
This outputs
  user.wine.attr: 0024
or ARCHIVE | SYSTEM.

This convention should be thought over carefully before we go with it.
I also considered using individual xattrs for each attribute bit,
but discarded that idea as adding overhead and complexity
without any obvious payoff.

Finally, the implementation of attrib in cmd is lame -- it only handles
two arguments.  It's enough for the moment, but the real attrib
can handle multiple attribute flags, and we should support that sometime.

So, what do folks think?
- Dan
diff --git a/configure.ac b/configure.ac
index 0d2e860..bcff19b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -414,6 +414,7 @@ AC_CHECK_HEADERS(\
 	sys/utsname.h \
 	sys/vm86.h \
 	sys/wait.h \
+	sys/xattr.h \
 	syscall.h \
 	termios.h \
 	unistd.h \
@@ -1714,6 +1715,53 @@ then
     AC_CHECK_LIB(poll,poll,[AC_DEFINE(HAVE_POLL,1) AC_SUBST(LIBPOLL,"-lpoll")])
 fi
 
+dnl Check for -lattr for Linux; needed on some older versions.
+AC_SEARCH_LIBS(fgetxattr, attr)
+
+dnl Check for functions which might need -lattr
+AC_CHECK_FUNCS(\
+        getxattr \
+        fgetxattr \
+        fsetxattr \
+        fremovexattr \
+)
+
+AC_CACHE_CHECK([whether fgetxattr takes six arguments],
+   	wine_cv_six_arg_fgetxattr,
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/xattr.h>]],[[fgetxattr(0, "foo", "foo", 3, 0, 0);]])],
+                          [wine_cv_six_arg_fgetxattr=yes],[wine_cv_six_arg_fgetxattr=no]))
+if test "$wine_cv_six_arg_fgetxattr" = "yes"
+then
+  AC_DEFINE(HAVE_SIX_ARG_FGETXATTR, 1, [Define if fgetxattr takes six arguments, as in Mac OS X])
+fi
+
+AC_CACHE_CHECK([whether fsetxattr takes six arguments],
+   	wine_cv_six_arg_fsetxattr,
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/xattr.h>]],[[fsetxattr(0, "foo", "foo", 3, 0, 0);]])],
+                          [wine_cv_six_arg_fsetxattr=yes],[wine_cv_six_arg_fsetxattr=no]))
+if test "$wine_cv_six_arg_fsetxattr" = "yes"
+then
+  AC_DEFINE(HAVE_SIX_ARG_FSETXATTR, 1, [Define if fsetxattr takes six arguments, as in Mac OS X])
+fi
+
+AC_CACHE_CHECK([whether getxattr takes six arguments],
+   	wine_cv_six_arg_getxattr,
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/xattr.h>]],[[getxattr("foo", "foo", "foo", 3, 0, 0);]])],
+                          [wine_cv_six_arg_getxattr=yes],[wine_cv_six_arg_getxattr=no]))
+if test "$wine_cv_six_arg_getxattr" = "yes"
+then
+  AC_DEFINE(HAVE_SIX_ARG_GETXATTR, 1, [Define if getxattr takes six arguments, as in Mac OS X])
+fi
+
+AC_CACHE_CHECK([whether fremovexattr takes three arguments],
+   	wine_cv_three_arg_fremovexattr,
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/xattr.h>]],[[fremovexattr("foo", "foo", 0);]])],
+                          [wine_cv_three_arg_fremovexattr=yes],[wine_cv_three_arg_fremovexattr=no]))
+if test "$wine_cv_three_arg_fremovexattr" = "yes"
+then
+  AC_DEFINE(HAVE_THREE_ARG_FREMOVEXATTR, 1, [Define if fremovexattr takes three arguments, as in Mac OS X])
+fi
+
 dnl Check for -lnsl for Solaris
 AC_SEARCH_LIBS(gethostbyname, nsl)
 
diff --git a/dlls/kernel32/file.c b/dlls/kernel32/file.c
index d9fbddb..4758983 100644
--- a/dlls/kernel32/file.c
+++ b/dlls/kernel32/file.c
@@ -2260,7 +2260,8 @@ BOOL WINAPI SetFileAttributesW( LPCWSTR name, DWORD attributes )
         FILE_BASIC_INFORMATION info;
 
         memset( &info, 0, sizeof(info) );
-        info.FileAttributes = attributes | FILE_ATTRIBUTE_NORMAL;  /* make sure it's not zero */
+        if (!attributes) attributes |= FILE_ATTRIBUTE_NORMAL;  /* make sure it's not zero */
+        info.FileAttributes = attributes;
         status = NtSetInformationFile( handle, &io, &info, sizeof(info), FileBasicInformation );
         NtClose( handle );
     }
diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c
index 0cd531e..279995f 100644
--- a/dlls/ntdll/directory.c
+++ b/dlls/ntdll/directory.c
@@ -56,6 +56,9 @@
 #ifdef HAVE_SYS_MOUNT_H
 #include <sys/mount.h>
 #endif
+#ifdef HAVE_SYS_XATTR_H
+# include <sys/xattr.h>
+#endif
 #include <time.h>
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
@@ -742,23 +745,145 @@ static void init_options(void)
  *
  * Check if the specified file should be hidden based on its name and the show dot files option.
  */
-BOOL DIR_is_hidden_file( const UNICODE_STRING *name )
+static BOOL DIR_is_hidden_file( const char *long_name )
 {
-    WCHAR *p, *end;
-
     if (show_dot_files == -1) init_options();
-    if (show_dot_files) return FALSE;
-
-    end = p = name->Buffer + name->Length/sizeof(WCHAR);
-    while (p > name->Buffer && IS_SEPARATOR(p[-1])) p--;
-    while (p > name->Buffer && !IS_SEPARATOR(p[-1])) p--;
-    if (p == end || *p != '.') return FALSE;
-    /* make sure it isn't '.' or '..' */
-    if (p + 1 == end) return FALSE;
-    if (p[1] == '.' && p + 2 == end) return FALSE;
-    return TRUE;
+    return (!show_dot_files && long_name[0] == '.' && long_name[1] && (long_name[1] != '.' || long_name[2]));
+}
+
+/***********************************************************************
+ *           DIR_fget_attributes
+ *
+ * Retrieve file attributes given unix fd and mode bits.
+ */
+DWORD DIR_fget_attributes(int fd, int mode)
+{
+    DWORD attributes = 0;
+
+    if (S_ISDIR(mode))
+        attributes = FILE_ATTRIBUTE_DIRECTORY;
+
+#ifdef HAVE_FGETXATTR
+    {
+        char hexattr[10];
+        memset(hexattr, 0, sizeof(hexattr));
+#ifdef HAVE_SIX_ARG_FGETXATTR
+        if (fgetxattr(fd, XATTR_ATTRIBS_NAME, hexattr, 4, 0, 0))
+#else
+        if (fgetxattr(fd, XATTR_ATTRIBS_NAME, hexattr, 4))
+#endif
+        {
+            if (hexattr[0] == 0) {
+                if (!S_ISDIR(mode))
+                    attributes |= FILE_ATTRIBUTE_ARCHIVE;
+            } else
+                attributes |= strtol(hexattr, NULL, 16) & XATTR_ATTRIBS_MASK;
+        } else {
+            if (!S_ISDIR(mode))
+                attributes |= FILE_ATTRIBUTE_ARCHIVE;
+        }
+    }
+#else
+    if (!S_ISDIR(mode))
+        attributes |= FILE_ATTRIBUTE_ARCHIVE;
+#endif
+
+    if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+        attributes |= FILE_ATTRIBUTE_READONLY;
+
+   /*
+    * FIXME: should also set FILE_ATTRIBUTE_HIDDEN for some files
+    * based on unix filename (but we don't have it here)
+    */
+
+    if (attributes == 0)
+        attributes = FILE_ATTRIBUTE_NORMAL;
+
+    return attributes;
+}
+
+/***********************************************************************
+ *           DIR_get_attributes
+ *
+ * Retrieve file attributes given unix filename and mode bits.
+ * FIXME: merge with DIR_fget_attributes
+ */
+DWORD DIR_get_attributes(const char *long_name, int mode)
+{
+    DWORD attributes = 0;
+
+    if (S_ISDIR(mode))
+        attributes = FILE_ATTRIBUTE_DIRECTORY;
+
+#ifdef HAVE_GETXATTR
+    {
+        char hexattr[10];
+        memset(hexattr, 0, sizeof(hexattr));
+#ifdef HAVE_SIX_ARG_GETXATTR
+        if (getxattr(long_name, XATTR_ATTRIBS_NAME, hexattr, 4, 0, 0))
+#else
+        if (getxattr(long_name, XATTR_ATTRIBS_NAME, hexattr, 4))
+#endif
+        {
+            if (hexattr[0] == 0) {
+                if (!S_ISDIR(mode))
+                    attributes |= FILE_ATTRIBUTE_ARCHIVE;
+            } else
+                attributes |= strtol(hexattr, NULL, 16) & XATTR_ATTRIBS_MASK;
+        } else {
+            if (!S_ISDIR(mode))
+                attributes |= FILE_ATTRIBUTE_ARCHIVE;
+        }
+    }
+#else
+    if (!S_ISDIR(mode))
+        attributes |= FILE_ATTRIBUTE_ARCHIVE;
+#endif
+
+    if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+        attributes |= FILE_ATTRIBUTE_READONLY;
+
+    if (DIR_is_hidden_file(long_name))
+        attributes |= FILE_ATTRIBUTE_HIDDEN;
+
+    if (attributes == 0)
+        attributes = FILE_ATTRIBUTE_NORMAL;
+
+    return attributes;
 }
 
+/***********************************************************************
+ *           DIR_set_attributes
+ *
+ * Store file attributes given unix fd.
+ */
+void DIR_set_attributes(int fd, DWORD attributes)
+{
+    /* Try to save attributes in xattr.  We don't care too much if it doesn't work. */
+
+    /* FIXME: should strip hidden bit if implicit, but we can't because we don't have the name here */
+    attributes &= ~FILE_ATTRIBUTE_NORMAL;
+    if ((attributes & XATTR_ATTRIBS_MASK) != FILE_ATTRIBUTE_ARCHIVE)
+    {
+#ifdef HAVE_FSETXATTR
+	char hexattr[10];
+	sprintf(hexattr, "%04x", attributes & XATTR_ATTRIBS_MASK);
+#ifdef HAVE_SIX_ARG_FSETXATTR
+	fsetxattr(fd, XATTR_ATTRIBS_NAME, hexattr, 4, 0, 0);
+#else
+	fsetxattr(fd, XATTR_ATTRIBS_NAME, hexattr, 4, 0);
+#endif
+#endif
+    } else {
+#ifdef HAVE_FREMOVEXATTR
+#ifdef HAVE_THREE_ARG_FREMOVEXATTR
+	fremovexattr(fd, XATTR_ATTRIBS_NAME, 0);
+#else
+	fremovexattr(fd, XATTR_ATTRIBS_NAME);
+#endif
+#endif
+    }
+}
 
 /***********************************************************************
  *           hash_short_file_name
@@ -986,20 +1111,13 @@ static FILE_BOTH_DIR_INFORMATION *append_entry( void *info_ptr, ULONG_PTR *pos,
     if (S_ISDIR(st.st_mode))
     {
         info->EndOfFile.QuadPart = info->AllocationSize.QuadPart = 0;
-        info->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
     }
     else
     {
         info->EndOfFile.QuadPart = st.st_size;
         info->AllocationSize.QuadPart = (ULONGLONG)st.st_blocks * 512;
-        info->FileAttributes |= FILE_ATTRIBUTE_ARCHIVE;
     }
-
-    if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
-        info->FileAttributes |= FILE_ATTRIBUTE_READONLY;
-
-    if (!show_dot_files && long_name[0] == '.' && long_name[1] && (long_name[1] != '.' || long_name[2]))
-        info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
+    info->FileAttributes = DIR_get_attributes(long_name, st.st_mode);
 
     info->EaSize = 0; /* FIXME */
     info->ShortNameLength = short_len * sizeof(WCHAR);
@@ -1657,8 +1775,6 @@ NTSTATUS WINAPI NtQueryDirectoryFile( HANDLE handle, HANDLE event,
 
     RtlEnterCriticalSection( &dir_section );
 
-    if (show_dot_files == -1) init_options();
-
     cwd = open( ".", O_RDONLY );
     if (fchdir( fd ) != -1)
     {
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index 62f6743..f25802b 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -1582,10 +1582,8 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE hFile, PIO_STATUS_BLOCK io,
                 io->u.Status = STATUS_INVALID_INFO_CLASS;
             else
             {
-                if (S_ISDIR(st.st_mode)) info->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
-                else info->FileAttributes = FILE_ATTRIBUTE_ARCHIVE;
-                if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
-                    info->FileAttributes |= FILE_ATTRIBUTE_READONLY;
+                info->FileAttributes = DIR_fget_attributes(fd, st.st_mode);
+
                 RtlSecondsSince1970ToTime( st.st_mtime, &info->CreationTime);
                 RtlSecondsSince1970ToTime( st.st_mtime, &info->LastWriteTime);
                 RtlSecondsSince1970ToTime( st.st_ctime, &info->ChangeTime);
@@ -1668,7 +1666,6 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE hFile, PIO_STATUS_BLOCK io,
             {
                 if ((info->StandardInformation.Directory = S_ISDIR(st.st_mode)))
                 {
-                    info->BasicInformation.FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
                     info->StandardInformation.AllocationSize.QuadPart = 0;
                     info->StandardInformation.EndOfFile.QuadPart      = 0;
                     info->StandardInformation.NumberOfLinks           = 1;
@@ -1676,14 +1673,12 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE hFile, PIO_STATUS_BLOCK io,
                 }
                 else
                 {
-                    info->BasicInformation.FileAttributes = FILE_ATTRIBUTE_ARCHIVE;
                     info->StandardInformation.AllocationSize.QuadPart = (ULONGLONG)st.st_blocks * 512;
                     info->StandardInformation.EndOfFile.QuadPart      = st.st_size;
                     info->StandardInformation.NumberOfLinks           = st.st_nlink;
                     info->StandardInformation.DeletePending           = FALSE; /* FIXME */
                 }
-                if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
-                    info->BasicInformation.FileAttributes |= FILE_ATTRIBUTE_READONLY;
+                info->BasicInformation.FileAttributes = DIR_fget_attributes(fd, st.st_mode);
                 RtlSecondsSince1970ToTime( st.st_mtime, &info->BasicInformation.CreationTime);
                 RtlSecondsSince1970ToTime( st.st_mtime, &info->BasicInformation.LastWriteTime);
                 RtlSecondsSince1970ToTime( st.st_ctime, &info->BasicInformation.ChangeTime);
@@ -1870,6 +1865,9 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io,
                         st.st_mode |= (0600 | ((st.st_mode & 044) >> 1)) & (~FILE_umask);
                     }
                     if (fchmod( fd, st.st_mode ) == -1) io->u.Status = FILE_GetNtStatus();
+
+                    /* Try to save attributes.  We don't care too much if it doesn't work. */
+                    DIR_set_attributes(fd, info->FileAttributes);
                 }
             }
 
@@ -1987,24 +1985,19 @@ static NTSTATUS FILE_QueryFullAttributesFile( const OBJECT_ATTRIBUTES *attr,
         {
             if (S_ISDIR(st.st_mode))
             {
-                info->FileAttributes          = FILE_ATTRIBUTE_DIRECTORY;
                 info->AllocationSize.QuadPart = 0;
                 info->EndOfFile.QuadPart      = 0;
             }
             else
             {
-                info->FileAttributes          = FILE_ATTRIBUTE_ARCHIVE;
                 info->AllocationSize.QuadPart = (ULONGLONG)st.st_blocks * 512;
                 info->EndOfFile.QuadPart      = st.st_size;
             }
-            if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
-                info->FileAttributes |= FILE_ATTRIBUTE_READONLY;
+            info->FileAttributes = DIR_get_attributes(unix_name.Buffer, st.st_mode);
             RtlSecondsSince1970ToTime( st.st_mtime, &info->CreationTime );
             RtlSecondsSince1970ToTime( st.st_mtime, &info->LastWriteTime );
             RtlSecondsSince1970ToTime( st.st_ctime, &info->ChangeTime );
             RtlSecondsSince1970ToTime( st.st_atime, &info->LastAccessTime );
-            if (DIR_is_hidden_file( attr->ObjectName ))
-                info->FileAttributes |= FILE_ATTRIBUTE_HIDDEN;
         }
         RtlFreeAnsiString( &unix_name );
     }
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 179aa20..fd17a1c 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -39,6 +39,12 @@ struct drive_info
     ino_t ino;
 };
 
+/* FILE_ATTRIBUTE_* are encoded in the xattr as a four character hexadecimal string. */
+/* The absense of an xattr indicates FILE_ATTRIBUTE_ARCHIVE. */
+#define XATTR_ATTRIBS_NAME "user.wine.attr"
+/* Only the following attributes are stored in the xattr. */
+#define XATTR_ATTRIBS_MASK  (FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)
+
 /* exceptions */
 extern void wait_suspend( CONTEXT *context );
 extern NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *context );
@@ -138,7 +144,9 @@ extern NTSTATUS TAPE_DeviceIoControl(HANDLE hDevice,
 
 /* file I/O */
 extern NTSTATUS FILE_GetNtStatus(void);
-extern BOOL DIR_is_hidden_file( const UNICODE_STRING *name );
+extern DWORD DIR_fget_attributes(int fd, int mode);
+extern DWORD DIR_get_attributes(const char *long_name, int mode);
+extern void DIR_set_attributes(int fd, DWORD attributes);
 extern NTSTATUS DIR_unmount_device( HANDLE handle );
 extern NTSTATUS DIR_get_unix_cwd( char **cwd );
 extern unsigned int DIR_get_drives_info( struct drive_info info[MAX_DOS_DRIVES] );
diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in
index 3425b5c..6bc2623 100644
--- a/dlls/ntdll/tests/Makefile.in
+++ b/dlls/ntdll/tests/Makefile.in
@@ -8,6 +8,7 @@ IMPORTS   = kernel32
 CTESTS = \
 	atom.c \
 	change.c \
+	directory.c \
 	env.c \
 	error.c \
 	exception.c \
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c
index 26a854a..2e7fb06 100644
--- a/dlls/ntdll/tests/file.c
+++ b/dlls/ntdll/tests/file.c
@@ -64,6 +64,7 @@ static NTSTATUS (WINAPI *pNtQueryIoCompletion)(HANDLE, IO_COMPLETION_INFORMATION
 static NTSTATUS (WINAPI *pNtRemoveIoCompletion)(HANDLE, PULONG_PTR, PULONG_PTR, PIO_STATUS_BLOCK, PLARGE_INTEGER);
 static NTSTATUS (WINAPI *pNtSetIoCompletion)(HANDLE, ULONG_PTR, ULONG_PTR, NTSTATUS, ULONG);
 static NTSTATUS (WINAPI *pNtSetInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
+static NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FILE_INFORMATION_CLASS);
 
 static inline BOOL is_signaled( HANDLE obj )
 {
@@ -787,6 +788,164 @@ static void test_iocp_fileio(HANDLE h)
     CloseHandle( hPipeClt );
 }
 
+static void test_file_basic_information(void)
+{
+    IO_STATUS_BLOCK io;
+    FILE_BASIC_INFORMATION fbi;
+    HANDLE h;
+    int res;
+
+    if (!(h = create_temp_file(0))) return;
+
+    /* Check default first */
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
+    ok ( fbi.FileAttributes == FILE_ATTRIBUTE_ARCHIVE, "attribute %x not FILE_ATTRIBUTE_ARCHIVE\n", fbi.FileAttributes );
+
+    /* Then SYSTEM */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_SYSTEM;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    ok ( fbi.FileAttributes == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fbi.FileAttributes );
+
+    /* Then HIDDEN */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    ok ( fbi.FileAttributes == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fbi.FileAttributes );
+
+    /* Check NORMAL last of all (to make sure we can clear attributes) */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't set normal attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBasicInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    ok ( fbi.FileAttributes == FILE_ATTRIBUTE_NORMAL, "attribute %x not FILE_ATTRIBUTE_NORMAL\n", fbi.FileAttributes );
+
+    CloseHandle( h );
+}
+
+/* FIXME: use macros (ugh) to merge the various file class tests */
+static void test_file_all_information(void)
+{
+    IO_STATUS_BLOCK io;
+    FILE_ALL_INFORMATION fai;
+    HANDLE h;
+    int res;
+
+    skip("fails on vista because our FILE_ALL_INFORMATION is too small?\n");
+    return;
+
+    if (!(h = create_temp_file(0))) return;
+
+    /* Check default first */
+    memset(&fai, 0, sizeof(fai));
+    res = pNtQueryInformationFile(h, &io, &fai, sizeof fai, FileAllInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
+    ok ( fai.BasicInformation.FileAttributes == FILE_ATTRIBUTE_ARCHIVE, "attribute %x not FILE_ATTRIBUTE_ARCHIVE\n", fai.BasicInformation.FileAttributes );
+
+    /* Then SYSTEM */
+    memset(&fai, 0, sizeof(fai));
+    fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_SYSTEM;
+    res = pNtSetInformationFile(h, &io, &fai, sizeof fai, FileAllInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fai, 0, sizeof(fai));
+    res = pNtQueryInformationFile(h, &io, &fai, sizeof fai, FileAllInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
+    todo_wine ok ( fai.BasicInformation.FileAttributes == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fai.BasicInformation.FileAttributes );
+
+    /* Then HIDDEN */
+    memset(&fai, 0, sizeof(fai));
+    fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
+    res = pNtSetInformationFile(h, &io, &fai, sizeof fai, FileAllInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fai, 0, sizeof(fai));
+    res = pNtQueryInformationFile(h, &io, &fai, sizeof fai, FileAllInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( fai.BasicInformation.FileAttributes == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fai.BasicInformation.FileAttributes );
+
+    /* Check NORMAL last of all (to make sure we can clear attributes) */
+    memset(&fai, 0, sizeof(fai));
+    fai.BasicInformation.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+    res = pNtSetInformationFile(h, &io, &fai, sizeof fai, FileAllInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't set normal attribute\n");
+
+    memset(&fai, 0, sizeof(fai));
+    res = pNtQueryInformationFile(h, &io, &fai, sizeof fai, FileAllInformation);
+    ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    ok ( fai.BasicInformation.FileAttributes == FILE_ATTRIBUTE_NORMAL, "attribute %x not FILE_ATTRIBUTE_NORMAL\n", fai.BasicInformation.FileAttributes );
+
+    CloseHandle( h );
+}
+
+/* FIXME: use macros (ugh) to merge the various file class tests */
+static void test_file_both_information(void)
+{
+    IO_STATUS_BLOCK io;
+    FILE_BOTH_DIR_INFORMATION fbi;
+    HANDLE h;
+    int res;
+
+    if (!(h = create_temp_file(0))) return;
+
+    /* Check default first */
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't get attributes, res %x\n", res);
+    todo_wine ok ( fbi.FileAttributes == FILE_ATTRIBUTE_ARCHIVE, "attribute %x not FILE_ATTRIBUTE_ARCHIVE\n", fbi.FileAttributes );
+
+    /* Then SYSTEM */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_SYSTEM;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( fbi.FileAttributes == FILE_ATTRIBUTE_SYSTEM, "attribute %x not FILE_ATTRIBUTE_SYSTEM\n", fbi.FileAttributes );
+
+    /* Then HIDDEN */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't set system attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( fbi.FileAttributes == FILE_ATTRIBUTE_HIDDEN, "attribute %x not FILE_ATTRIBUTE_HIDDEN\n", fbi.FileAttributes );
+
+    /* Check NORMAL last of all (to make sure we can clear attributes) */
+    memset(&fbi, 0, sizeof(fbi));
+    fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;
+    res = pNtSetInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't set normal attribute\n");
+
+    memset(&fbi, 0, sizeof(fbi));
+    res = pNtQueryInformationFile(h, &io, &fbi, sizeof fbi, FileBothDirectoryInformation);
+    todo_wine ok ( res == STATUS_SUCCESS, "can't get attributes\n");
+    todo_wine ok ( fbi.FileAttributes == FILE_ATTRIBUTE_NORMAL, "attribute %x not FILE_ATTRIBUTE_NORMAL\n", fbi.FileAttributes );
+
+    CloseHandle( h );
+}
+
 static void test_iocompletion(void)
 {
     HANDLE h = INVALID_HANDLE_VALUE;
@@ -830,9 +989,13 @@ START_TEST(file)
     pNtRemoveIoCompletion   = (void *)GetProcAddress(hntdll, "NtRemoveIoCompletion");
     pNtSetIoCompletion      = (void *)GetProcAddress(hntdll, "NtSetIoCompletion");
     pNtSetInformationFile   = (void *)GetProcAddress(hntdll, "NtSetInformationFile");
+    pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile");
 
     delete_file_test();
     read_file_test();
     nt_mailslot_test();
     test_iocompletion();
+    test_file_basic_information();
+    test_file_all_information();
+    test_file_both_information();
 }
diff --git a/include/config.h.in b/include/config.h.in
index 6956963..d67f73f 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -90,6 +90,9 @@
 /* Define to 1 if you have the `ffs' function. */
 #undef HAVE_FFS
 
+/* Define to 1 if you have the `fgetxattr' function. */
+#undef HAVE_FGETXATTR
+
 /* Define to 1 if you have the `finite' function. */
 #undef HAVE_FINITE
 
@@ -150,9 +153,15 @@
 /* Define to 1 if you have the <freetype/tttables.h> header file. */
 #undef HAVE_FREETYPE_TTTABLES_H
 
+/* Define to 1 if you have the `fremovexattr' function. */
+#undef HAVE_FREMOVEXATTR
+
 /* Define to 1 if the system has the type `fsblkcnt_t'. */
 #undef HAVE_FSBLKCNT_T
 
+/* Define to 1 if you have the `fsetxattr' function. */
+#undef HAVE_FSETXATTR
+
 /* Define to 1 if the system has the type `fsfilcnt_t'. */
 #undef HAVE_FSFILCNT_T
 
@@ -222,6 +231,9 @@
 /* Define to 1 if you have the `getuid' function. */
 #undef HAVE_GETUID
 
+/* Define to 1 if you have the `getxattr' function. */
+#undef HAVE_GETXATTR
+
 /* Define to 1 if you have the <GL/glu.h> header file. */
 #undef HAVE_GL_GLU_H
 
@@ -687,6 +699,15 @@
 /* Define to 1 if the system has the type `sigset_t'. */
 #undef HAVE_SIGSET_T
 
+/* Define if fgetxattr takes six arguments */
+#undef HAVE_SIX_ARG_FGETXATTR
+
+/* Define if fsetxattr takes six arguments */
+#undef HAVE_SIX_ARG_FSETXATTR
+
+/* Define if getxattr takes six arguments */
+#undef HAVE_SIX_ARG_GETXATTR
+
 /* Define to 1 if the system has the type `size_t'. */
 #undef HAVE_SIZE_T
 
@@ -972,6 +993,9 @@
 /* Define to 1 if you have the <sys/wait.h> header file. */
 #undef HAVE_SYS_WAIT_H
 
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#undef HAVE_SYS_XATTR_H
+
 /* Define to 1 if you have the `tcgetattr' function. */
 #undef HAVE_TCGETATTR
 
@@ -981,6 +1005,9 @@
 /* Define to 1 if you have the `thr_kill2' function. */
 #undef HAVE_THR_KILL2
 
+/* Define if fremovexattr takes three arguments */
+#undef HAVE_THREE_ARG_FREMOVEXATTR
+
 /* Define to 1 if you have the `timegm' function. */
 #undef HAVE_TIMEGM
 
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 612e5d9..9af7c48 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1775,12 +1775,6 @@ void WCMD_endlocal (void) {
  *
  * Display and optionally sets DOS attributes on a file or directory
  *
- * FIXME: Wine currently uses the Unix stat() function to get file attributes.
- * As a result only the Readonly flag is correctly reported, the Archive bit
- * is always set and the rest are not implemented. We do the Right Thing anyway.
- *
- * FIXME: No SET functionality.
- *
  */
 
 void WCMD_setshow_attrib (void) {
@@ -1789,26 +1783,49 @@ void WCMD_setshow_attrib (void) {
   HANDLE hff;
   WIN32_FIND_DATA fd;
   WCHAR flags[9] = {' ',' ',' ',' ',' ',' ',' ',' ','\0'};
-
-  if (param1[0] == '-') {
-    WCMD_output (WCMD_LoadMessage(WCMD_NYI));
-    return;
+  WCHAR *name = param1;
+  DWORD attrib_set=0;
+  DWORD attrib_clear=0;
+
+  if (param1[0] == '+' || param1[0] == '-') {
+    DWORD attrib = 0;
+    /* FIXME: the real cmd can handle many more than two args; this should be in a loop */
+    switch (param1[1]) {
+    case 'H': case 'h': attrib |= FILE_ATTRIBUTE_HIDDEN; break;
+    case 'S': case 's': attrib |= FILE_ATTRIBUTE_SYSTEM; break;
+    case 'R': case 'r': attrib |= FILE_ATTRIBUTE_READONLY; break;
+    case 'A': case 'a': attrib |= FILE_ATTRIBUTE_ARCHIVE; break;
+    default:
+      WCMD_output (WCMD_LoadMessage(WCMD_NYI));
+      return;
+    }
+    switch (param1[0]) {
+    case '+': attrib_set = attrib; break;
+    case '-': attrib_clear = attrib; break;
+    }
+    name = param2;
   }
 
-  if (strlenW(param1) == 0) {
+  if (strlenW(name) == 0) {
     static const WCHAR slashStarW[]  = {'\\','*','\0'};
 
-    GetCurrentDirectory (sizeof(param1)/sizeof(WCHAR), param1);
-    strcatW (param1, slashStarW);
+    GetCurrentDirectory (sizeof(name)/sizeof(WCHAR), name);
+    strcatW (name, slashStarW);
   }
 
-  hff = FindFirstFile (param1, &fd);
+  hff = FindFirstFile (name, &fd);
   if (hff == INVALID_HANDLE_VALUE) {
-    WCMD_output (WCMD_LoadMessage(WCMD_FILENOTFOUND), param1);
+    WCMD_output (WCMD_LoadMessage(WCMD_FILENOTFOUND), name);
   }
   else {
     do {
-      if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+      if (attrib_set || attrib_clear) {
+        fd.dwFileAttributes &= ~attrib_clear;
+        fd.dwFileAttributes |= attrib_set;
+        if (!fd.dwFileAttributes)
+           fd.dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
+        SetFileAttributesW(name, fd.dwFileAttributes);
+      } else {
         static const WCHAR fmt[] = {'%','s',' ',' ',' ','%','s','\n','\0'};
         if (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
 	  flags[0] = 'H';
diff /dev/null b/dlls/ntdll/tests/directory.c
--- /dev/null	2009-07-15 09:33:55.000000000 -0700
+++ b/dlls/ntdll/tests/directory.c	2009-09-07 06:50:23.000000000 -0700
@@ -0,0 +1,206 @@
+/* Unit test suite for Ntdll directory functions
+ *
+ * Copyright 2007 Jeff Latimer
+ * Copyright 2007 Andrey Turkin
+ * Copyright 2008 Jeff Zaroyko
+ * Copyright 2009 Dan Kegel
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ * We use function pointers here as there is no import library for NTDLL on
+ * windows.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "ntstatus.h"
+/* Define WIN32_NO_STATUS so MSVC does not give us duplicate macro
+ * definition errors when we get to winnt.h
+ */
+#define WIN32_NO_STATUS
+
+#include "wine/test.h"
+#include "winternl.h"
+
+static NTSTATUS (WINAPI *pNtClose)( PHANDLE );
+static NTSTATUS (WINAPI *pNtOpenFile)    ( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG );
+static NTSTATUS (WINAPI *pNtQueryDirectoryFile)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,
+                                                PVOID,ULONG,FILE_INFORMATION_CLASS,BOOLEAN,PUNICODE_STRING,BOOLEAN);
+static BOOLEAN  (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING,LPCSTR);
+static BOOL     (WINAPI *pRtlDosPathNameToNtPathName_U)( LPCWSTR, PUNICODE_STRING, PWSTR*, CURDIR* );
+static VOID     (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
+static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
+                                                   LPCSTR src, DWORD srclen );
+
+static void test_NtQueryDirectoryFile(void)
+{
+    NTSTATUS ret;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING ntdirname;
+    char dirnameA[MAX_PATH];
+    WCHAR dirnameW[MAX_PATH];
+    static const char testdirA[] = "NtQueryDirectoryFile";
+    static const char normalfileA[] = "n";
+    static const char hiddenfileA[] = "h";
+    static const char systemfileA[] = "s";
+    char normalpathA[MAX_PATH];
+    char hiddenpathA[MAX_PATH];
+    char systempathA[MAX_PATH];
+    HANDLE normalh;
+    HANDLE hiddenh;
+    HANDLE systemh;
+    int normal_found;
+    int hidden_found;
+    int system_found;
+    HANDLE dirh;
+    IO_STATUS_BLOCK io;
+    UINT data_pos;
+    UINT data_len;    /* length of dir data */
+    BYTE data[98192];  /* directory data */
+    FILE_BOTH_DIRECTORY_INFORMATION *dir_info;
+    DWORD status;
+    int numfiles;
+
+    ret = GetTempPathA(MAX_PATH, dirnameA);
+    if (!ret)
+    {
+        ok(0, "couldn't get temp dir\n");
+        return;
+    }
+    if (ret + sizeof(testdirA)-1 + sizeof(normalfileA)-1 >= MAX_PATH)
+    {
+        ok(0, "MAX_PATH exceeded in constructing paths\n");
+        return;
+    }
+
+    strcat(dirnameA, testdirA);
+
+    ret = CreateDirectoryA(dirnameA, NULL);
+    ok(ret == TRUE, "couldn't create directory '%s', ret %x, error %d\n", dirnameA, ret, GetLastError());
+
+    /* Create one normal file, one hidden, and one system file */
+    GetTempFileNameA( dirnameA, normalfileA, 1, normalpathA );
+    normalh = CreateFileA(normalpathA, GENERIC_READ | GENERIC_WRITE, 0, NULL, 
+          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0);
+    ok( normalh != INVALID_HANDLE_VALUE, "failed to create temp file '%s'\n", normalpathA );
+
+    GetTempFileNameA( dirnameA, hiddenfileA, 1, hiddenpathA );
+    hiddenh = CreateFileA(hiddenpathA, GENERIC_READ | GENERIC_WRITE, 0, NULL, 
+          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0);
+    ok( hiddenh != INVALID_HANDLE_VALUE, "failed to create hidden temp file '%s'\n", hiddenpathA );
+
+    GetTempFileNameA( dirnameA, systemfileA, 2, systempathA );
+    systemh = CreateFileA(systempathA, GENERIC_READ | GENERIC_WRITE, 0, NULL, 
+          CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_DELETE_ON_CLOSE, 0);
+    ok( systemh != INVALID_HANDLE_VALUE, "failed to create sys temp file '%s'\n", systempathA );
+
+    pRtlMultiByteToUnicodeN(dirnameW, sizeof(dirnameW), NULL, dirnameA, strlen(dirnameA)+1);
+    if (!pRtlDosPathNameToNtPathName_U(dirnameW, &ntdirname, NULL, NULL))
+    {
+        ok(0,"RtlDosPathNametoNtPathName_U failed\n");
+        return;
+    }
+    /* Now read the directory and make sure both are found */
+    InitializeObjectAttributes(&attr, &ntdirname, 0, 0, NULL);
+    status = pNtOpenFile( &dirh, SYNCHRONIZE | FILE_LIST_DIRECTORY, &attr, &io,
+                         FILE_OPEN,
+                         FILE_SYNCHRONOUS_IO_NONALERT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_DIRECTORY_FILE);
+    ok (status == STATUS_SUCCESS, "failed to open dir '%s', ret 0x%x, error %d\n", dirnameA, status, GetLastError());
+    if (status != STATUS_SUCCESS) {
+       skip("can't test if we can't open the directory\n");
+       return;
+    }
+
+    pNtQueryDirectoryFile( dirh, NULL, NULL, NULL, &io, data, sizeof(data),
+                       FileBothDirectoryInformation, FALSE, NULL, TRUE );
+    ok (io.Status == STATUS_SUCCESS, "filed to query directory; status %x\n", io.Status);
+    data_len = io.Information;
+    ok (data_len >= sizeof(FILE_BOTH_DIRECTORY_INFORMATION), "not enough data in directory\n");
+
+    normal_found = hidden_found = system_found = 0;
+
+    data_pos = 0;
+    numfiles = 0;
+    while ((data_pos < data_len) && (numfiles < 5)) {
+        dir_info = (FILE_BOTH_DIRECTORY_INFORMATION *)(data + data_pos);
+        switch (dir_info->FileName[0]) {
+        case '.':
+            break;
+        case 'n':
+            ok(dir_info->FileNameLength == 6*sizeof(WCHAR), "expected six-char name\n");
+            ok(normal_found == 0, "too many normal files\n");
+            normal_found++;
+            break;
+        case 'h':
+            ok(dir_info->FileNameLength == 6*sizeof(WCHAR), "expected six-char name\n");
+            ok(hidden_found == 0, "too many hidden files\n");
+            hidden_found++;
+            break;
+        case 's':
+            ok(dir_info->FileNameLength == 6*sizeof(WCHAR), "expected six-char name\n");
+            ok(system_found == 0, "too many system files\n");
+            system_found++;
+            break;
+        default:
+            ok(FALSE, "unexpected filename found\n");
+        }
+        if (dir_info->NextEntryOffset == 0) {
+            pNtQueryDirectoryFile( dirh, 0, NULL, NULL, &io, data, sizeof(data),
+                               FileBothDirectoryInformation, FALSE, NULL, FALSE );
+            if (io.Status == STATUS_NO_MORE_FILES)
+                break;
+            ok (io.Status == STATUS_SUCCESS, "filed to query directory; status %x\n", io.Status);
+            data_len = io.Information;
+            if (data_len < sizeof(FILE_BOTH_DIRECTORY_INFORMATION))
+                break;
+            data_pos = 0;
+        } else {
+            data_pos += dir_info->NextEntryOffset;
+        }
+        numfiles++;
+    }
+    ok(numfiles < 5, "too many loops\n");
+    ok(normal_found > 0, "no normal files found\n");
+    ok(hidden_found > 0, "no hidden files found\n");
+    ok(system_found > 0, "no system files found\n");
+
+    pNtClose(dirh);
+    CloseHandle(normalh);
+    CloseHandle(hiddenh);
+    CloseHandle(systemh);
+    RemoveDirectoryW(dirnameW);
+}
+
+START_TEST(directory)
+{
+    HMODULE hntdll = GetModuleHandleA("ntdll.dll");
+    if (!hntdll)
+    {
+        skip("not running on NT, skipping test\n");
+        return;
+    }
+
+    pNtClose                = (void *)GetProcAddress(hntdll, "NtClose");
+    pNtOpenFile             = (void *)GetProcAddress(hntdll, "NtOpenFile");
+    pNtQueryDirectoryFile   = (void *)GetProcAddress(hntdll, "NtQueryDirectoryFile");
+    pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
+    pRtlDosPathNameToNtPathName_U = (void *)GetProcAddress(hntdll, "RtlDosPathNameToNtPathName_U");
+    pRtlInitUnicodeString   = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
+    pRtlMultiByteToUnicodeN = (void *)GetProcAddress(hntdll,"RtlMultiByteToUnicodeN");
+
+    test_NtQueryDirectoryFile();
+}


Reply via email to