[SCRRUN_WINETEST] Sync with Wine Staging 1.9.23. CORE-12409
[reactos.git] / rostests / winetests / scrrun / filesystem.c
index c258a0f..cd0e6d6 100644 (file)
@@ -20,6 +20,7 @@
 
 #define COBJMACROS
 #include <stdio.h>
+#include <limits.h>
 
 #include "windows.h"
 #include "ole2.h"
@@ -34,6 +35,9 @@
 
 static IFileSystem3 *fs3;
 
+/* w2k and 2k3 error code. */
+#define E_VAR_NOT_SET 0x800a005b
+
 static inline ULONG get_refcount(IUnknown *iface)
 {
     IUnknown_AddRef(iface);
@@ -41,10 +45,61 @@ static inline ULONG get_refcount(IUnknown *iface)
 }
 
 static const WCHAR crlfW[] = {'\r','\n',0};
+static const char utf16bom[] = {0xff,0xfe,0};
 
 #define GET_REFCOUNT(iface) \
     get_refcount((IUnknown*)iface)
 
+static inline void get_temp_path(const WCHAR *prefix, WCHAR *path)
+{
+    WCHAR buffW[MAX_PATH];
+
+    GetTempPathW(MAX_PATH, buffW);
+    GetTempFileNameW(buffW, prefix, 0, path);
+    DeleteFileW(path);
+}
+
+static IDrive *get_fixed_drive(void)
+{
+    IDriveCollection *drives;
+    IEnumVARIANT *iter;
+    IDrive *drive;
+    HRESULT hr;
+
+    hr = IFileSystem3_get_Drives(fs3, &drives);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = IDriveCollection_get__NewEnum(drives, (IUnknown**)&iter);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    IDriveCollection_Release(drives);
+
+    while (1) {
+        DriveTypeConst type;
+        VARIANT var;
+
+        hr = IEnumVARIANT_Next(iter, 1, &var, NULL);
+        if (hr == S_FALSE) {
+            drive = NULL;
+            break;
+        }
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+
+        hr = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IDrive, (void**)&drive);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        VariantClear(&var);
+
+        hr = IDrive_get_DriveType(drive, &type);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        if (type == Fixed)
+            break;
+
+        IDrive_Release(drive);
+    }
+
+    IEnumVARIANT_Release(iter);
+    return drive;
+}
+
 static void test_interfaces(void)
 {
     static const WCHAR nonexistent_dirW[] = {
@@ -129,15 +184,13 @@ static void test_interfaces(void)
 
 static void test_createfolder(void)
 {
-    WCHAR pathW[MAX_PATH], buffW[MAX_PATH];
+    WCHAR buffW[MAX_PATH];
     HRESULT hr;
     BSTR path;
     IFolder *folder;
     BOOL ret;
 
-    GetTempPathW(MAX_PATH, pathW);
-    GetTempFileNameW(pathW, NULL, 0, buffW);
-    DeleteFileW(buffW);
+    get_temp_path(NULL, buffW);
     ret = CreateDirectoryW(buffW, NULL);
     ok(ret, "got %d, %d\n", ret, GetLastError());
 
@@ -528,33 +581,31 @@ static void test_GetAbsolutePathName(void)
 
 static void test_GetFile(void)
 {
-    static const WCHAR get_file[] = {'g','e','t','_','f','i','l','e','.','t','s','t',0};
-
-    BSTR path = SysAllocString(get_file);
+    static const WCHAR slW[] = {'\\',0};
+    BSTR path, str;
+    WCHAR pathW[MAX_PATH];
     FileAttribute fa;
     VARIANT size;
-    DWORD gfa;
+    DWORD gfa, new_gfa;
     IFile *file;
     HRESULT hr;
     HANDLE hf;
+    BOOL ret;
+
+    get_temp_path(NULL, pathW);
 
+    path = SysAllocString(pathW);
     hr = IFileSystem3_GetFile(fs3, path, NULL);
     ok(hr == E_POINTER, "GetFile returned %x, expected E_POINTER\n", hr);
     hr = IFileSystem3_GetFile(fs3, NULL, &file);
     ok(hr == E_INVALIDARG, "GetFile returned %x, expected E_INVALIDARG\n", hr);
 
-    if(GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES) {
-        skip("File already exists, skipping GetFile tests\n");
-        SysFreeString(path);
-        return;
-    }
-
     file = (IFile*)0xdeadbeef;
     hr = IFileSystem3_GetFile(fs3, path, &file);
     ok(!file, "file != NULL\n");
     ok(hr == CTL_E_FILENOTFOUND, "GetFile returned %x, expected CTL_E_FILENOTFOUND\n", hr);
 
-    hf = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_READONLY, NULL);
+    hf = CreateFileW(pathW, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_READONLY, NULL);
     if(hf == INVALID_HANDLE_VALUE) {
         skip("Can't create temporary file\n");
         SysFreeString(path);
@@ -565,10 +616,38 @@ static void test_GetFile(void)
     hr = IFileSystem3_GetFile(fs3, path, &file);
     ok(hr == S_OK, "GetFile returned %x, expected S_OK\n", hr);
 
+    hr = IFile_get_Path(file, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    hr = IFile_get_Path(file, &str);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(!lstrcmpiW(str, pathW), "got %s\n", wine_dbgstr_w(str));
+    SysFreeString(str);
+
+#define FILE_ATTR_MASK (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
+        FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE | \
+        FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_COMPRESSED)
+
+    hr = IFile_get_Attributes(file, &fa);
+    gfa = GetFileAttributesW(pathW) & FILE_ATTR_MASK;
+    ok(hr == S_OK, "get_Attributes returned %x, expected S_OK\n", hr);
+    ok(fa == gfa, "fa = %x, expected %x\n", fa, gfa);
+
+    hr = IFile_put_Attributes(file, gfa | FILE_ATTRIBUTE_READONLY);
+    ok(hr == S_OK, "put_Attributes failed: %08x\n", hr);
+    new_gfa = GetFileAttributesW(pathW) & FILE_ATTR_MASK;
+    ok(new_gfa == (gfa|FILE_ATTRIBUTE_READONLY), "new_gfa = %x, expected %x\n", new_gfa, gfa|FILE_ATTRIBUTE_READONLY);
+
+    hr = IFile_get_Attributes(file, &fa);
+    ok(hr == S_OK, "get_Attributes returned %x, expected S_OK\n", hr);
+    ok(fa == new_gfa, "fa = %x, expected %x\n", fa, new_gfa);
+
+    hr = IFile_put_Attributes(file, gfa);
+    ok(hr == S_OK, "put_Attributes failed: %08x\n", hr);
+    new_gfa = GetFileAttributesW(pathW) & FILE_ATTR_MASK;
+    ok(new_gfa == gfa, "new_gfa = %x, expected %x\n", new_gfa, gfa);
+
     hr = IFile_get_Attributes(file, &fa);
-    gfa = GetFileAttributesW(get_file) & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
-            FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE |
-            FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_COMPRESSED);
     ok(hr == S_OK, "get_Attributes returned %x, expected S_OK\n", hr);
     ok(fa == gfa, "fa = %x, expected %x\n", fa, gfa);
 
@@ -589,6 +668,18 @@ static void test_GetFile(void)
     ok(hr == CTL_E_FILENOTFOUND, "DeleteFile returned %x, expected CTL_E_FILENOTFOUND\n", hr);
 
     SysFreeString(path);
+
+    /* try with directory */
+    lstrcatW(pathW, slW);
+    ret = CreateDirectoryW(pathW, NULL);
+    ok(ret, "got %d, error %d\n", ret, GetLastError());
+
+    path = SysAllocString(pathW);
+    hr = IFileSystem3_GetFile(fs3, path, &file);
+    ok(hr == CTL_E_FILENOTFOUND, "GetFile returned %x, expected S_OK\n", hr);
+    SysFreeString(path);
+
+    RemoveDirectoryW(pathW);
 }
 
 static inline BOOL create_file(const WCHAR *name)
@@ -858,9 +949,7 @@ static void test_FolderCollection(void)
     BSTR str;
     int found_a = 0, found_b = 0, found_c = 0;
 
-    GetTempPathW(MAX_PATH, pathW);
-    GetTempFileNameW(pathW, fooW, 0, buffW);
-    DeleteFileW(buffW);
+    get_temp_path(fooW, buffW);
     CreateDirectoryW(buffW, NULL);
 
     str = SysAllocString(buffW);
@@ -876,7 +965,7 @@ static void test_FolderCollection(void)
 
     hr = IFolder_get_Path(folder, &str);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-    ok(!lstrcmpW(buffW, str), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
+    ok(!lstrcmpiW(buffW, str), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
     SysFreeString(str);
 
     lstrcpyW(pathW, buffW);
@@ -1042,9 +1131,7 @@ static void test_FileCollection(void)
     HANDLE file_a, file_b, file_c;
     int found_a = 0, found_b = 0, found_c = 0;
 
-    GetTempPathW(MAX_PATH, pathW);
-    GetTempFileNameW(pathW, fooW, 0, buffW);
-    DeleteFileW(buffW);
+    get_temp_path(fooW, buffW);
     CreateDirectoryW(buffW, NULL);
 
     str = SysAllocString(buffW);
@@ -1070,9 +1157,7 @@ static void test_FileCollection(void)
 
     count = 0;
     hr = IFileCollection_get_Count(files, &count);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
-todo_wine
     ok(count == 2, "got %d\n", count);
 
     lstrcpyW(pathW, buffW);
@@ -1083,9 +1168,7 @@ todo_wine
     /* every time property is requested it scans directory */
     count = 0;
     hr = IFileCollection_get_Count(files, &count);
-todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
-todo_wine
     ok(count == 3, "got %d\n", count);
 
     hr = IFileCollection_get__NewEnum(files, NULL);
@@ -1278,20 +1361,29 @@ static void test_DriveCollection(void)
             V_VT(&size) = VT_EMPTY;
             hr = IDrive_get_TotalSize(drive, &size);
             ok(hr == S_OK, "got 0x%08x\n", hr);
-            ok(V_VT(&size) == VT_R8, "got %d\n", V_VT(&size));
-            ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
+            ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
+            if (V_VT(&size) == VT_R8)
+                ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
+            else
+                ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
 
             V_VT(&size) = VT_EMPTY;
             hr = IDrive_get_AvailableSpace(drive, &size);
             ok(hr == S_OK, "got 0x%08x\n", hr);
-            ok(V_VT(&size) == VT_R8, "got %d\n", V_VT(&size));
-            ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
+            ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
+            if (V_VT(&size) == VT_R8)
+                ok(V_R8(&size) > (double)INT_MAX, "got %f\n", V_R8(&size));
+            else
+                ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
 
             V_VT(&size) = VT_EMPTY;
             hr = IDrive_get_FreeSpace(drive, &size);
             ok(hr == S_OK, "got 0x%08x\n", hr);
-            ok(V_VT(&size) == VT_R8, "got %d\n", V_VT(&size));
-            ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
+            ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
+            if (V_VT(&size) == VT_R8)
+                ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
+            else
+                ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
         }
         VariantClear(&var);
     }
@@ -1304,8 +1396,7 @@ static void test_CreateTextFile(void)
 {
     static const WCHAR scrrunW[] = {'s','c','r','r','u','n','\\',0};
     static const WCHAR testfileW[] = {'t','e','s','t','.','t','x','t',0};
-    static const WCHAR bomAW[] = {0xff,0xfe,0};
-    WCHAR pathW[MAX_PATH], dirW[MAX_PATH];
+    WCHAR pathW[MAX_PATH], dirW[MAX_PATH], buffW[10];
     ITextStream *stream;
     BSTR nameW, str;
     HANDLE file;
@@ -1331,6 +1422,15 @@ static void test_CreateTextFile(void)
     hr = ITextStream_Read(stream, 1, &str);
     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
 
+    hr = ITextStream_Close(stream);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    hr = ITextStream_Read(stream, 1, &str);
+    ok(hr == CTL_E_BADFILEMODE || hr == E_VAR_NOT_SET, "got 0x%08x\n", hr);
+
+    hr = ITextStream_Close(stream);
+    ok(hr == S_FALSE || hr == E_VAR_NOT_SET, "got 0x%08x\n", hr);
+
     ITextStream_Release(stream);
 
     /* check it's created */
@@ -1352,11 +1452,16 @@ static void test_CreateTextFile(void)
     ok(hr == S_OK, "got 0x%08x\n", hr);
     ITextStream_Release(stream);
 
+    /* File was created in Unicode mode, it contains 0xfffe BOM. Opening it in non-Unicode mode
+       treats BOM like a valuable data with appropriate CP_ACP -> WCHAR conversion. */
+    buffW[0] = 0;
+    MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, sizeof(buffW)/sizeof(WCHAR));
+
     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
     ok(hr == S_OK, "got 0x%08x\n", hr);
     hr = ITextStream_ReadAll(stream, &str);
     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
-    ok(!lstrcmpW(str, bomAW), "got %s\n", wine_dbgstr_w(str));
+    ok(!lstrcmpW(str, buffW), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
     SysFreeString(str);
     ITextStream_Release(stream);
 
@@ -1493,7 +1598,9 @@ static void test_ReadAll(void)
     str = NULL;
     hr = ITextStream_ReadAll(stream, &str);
     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
-    ok(str[0] == 0x00ff && str[1] == 0x00fe, "got %s, %d\n", wine_dbgstr_w(str), SysStringLen(str));
+    buffW[0] = 0;
+    MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, sizeof(buffW)/sizeof(WCHAR));
+    ok(str[0] == buffW[0] && str[1] == buffW[1], "got %s, %d\n", wine_dbgstr_w(str), SysStringLen(str));
     SysFreeString(str);
     ITextStream_Release(stream);
 
@@ -1552,6 +1659,7 @@ todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
     str = SysAllocString(aW);
     hr = ITextStream_Write(stream, str);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
     SysFreeString(str);
     ITextStream_Release(stream);
 
@@ -1640,7 +1748,11 @@ static void test_Read(void)
     str = NULL;
     hr = ITextStream_Read(stream, 2, &str);
     ok(hr == S_OK, "got 0x%08x\n", hr);
-    ok(str[0] == 0x00ff && str[1] == 0x00fe, "got %s, %d\n", wine_dbgstr_w(str), SysStringLen(str));
+
+    buffW[0] = 0;
+    MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, sizeof(buffW)/sizeof(WCHAR));
+
+    ok(!lstrcmpW(str, buffW), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
     ok(SysStringLen(str) == 2, "got %d\n", SysStringLen(str));
     SysFreeString(str);
     ITextStream_Release(stream);
@@ -1700,6 +1812,7 @@ todo_wine
     ok(hr == S_OK, "got 0x%08x\n", hr);
     str = SysAllocString(aW);
     hr = ITextStream_Write(stream, str);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
     SysFreeString(str);
     ITextStream_Release(stream);
 
@@ -1718,6 +1831,340 @@ todo_wine
     SysFreeString(nameW);
 }
 
+struct driveexists_test {
+    const WCHAR drivespec[10];
+    const INT drivetype;
+    const VARIANT_BOOL expected_ret;
+};
+
+/* If 'drivetype' != -1, the first character of 'drivespec' will be replaced
+ * with the drive letter of a drive of this type. If such a drive does not exist,
+ * the test will be skipped. */
+static const struct driveexists_test driveexiststestdata[] = {
+    { {'N',':','\\',0}, DRIVE_NO_ROOT_DIR, VARIANT_FALSE },
+    { {'R',':','\\',0}, DRIVE_REMOVABLE, VARIANT_TRUE },
+    { {'F',':','\\',0}, DRIVE_FIXED, VARIANT_TRUE },
+    { {'F',':',0}, DRIVE_FIXED, VARIANT_TRUE },
+    { {'F','?',0}, DRIVE_FIXED, VARIANT_FALSE },
+    { {'F',0}, DRIVE_FIXED, VARIANT_TRUE },
+    { {'?',0}, -1, VARIANT_FALSE },
+    { { 0 } }
+};
+
+static void test_DriveExists(void)
+{
+    const struct driveexists_test *ptr = driveexiststestdata;
+    HRESULT hr;
+    VARIANT_BOOL ret;
+    BSTR drivespec;
+    WCHAR root[] = {'?',':','\\',0};
+
+    hr = IFileSystem3_DriveExists(fs3, NULL, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    ret = VARIANT_TRUE;
+    hr = IFileSystem3_DriveExists(fs3, NULL, &ret);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(ret == VARIANT_FALSE, "got %x\n", ret);
+
+    drivespec = SysAllocString(root);
+    hr = IFileSystem3_DriveExists(fs3, drivespec, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+    SysFreeString(drivespec);
+
+    for (; *ptr->drivespec; ptr++) {
+        drivespec = SysAllocString(ptr->drivespec);
+        if (ptr->drivetype != -1) {
+            for (root[0] = 'A'; root[0] <= 'Z'; root[0]++)
+                if (GetDriveTypeW(root) == ptr->drivetype)
+                    break;
+            if (root[0] > 'Z') {
+                skip("No drive with type 0x%x found, skipping test %s.\n",
+                        ptr->drivetype, wine_dbgstr_w(ptr->drivespec));
+                SysFreeString(drivespec);
+                continue;
+            }
+
+            /* Test both upper and lower case drive letters. */
+            drivespec[0] = root[0];
+            ret = ptr->expected_ret == VARIANT_TRUE ? VARIANT_FALSE : VARIANT_TRUE;
+            hr = IFileSystem3_DriveExists(fs3, drivespec, &ret);
+            ok(hr == S_OK, "got 0x%08x for drive spec %s (%s)\n",
+                    hr, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
+            ok(ret == ptr->expected_ret, "got %d, expected %d for drive spec %s (%s)\n",
+                    ret, ptr->expected_ret, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
+
+            drivespec[0] = tolower(root[0]);
+        }
+
+        ret = ptr->expected_ret == VARIANT_TRUE ? VARIANT_FALSE : VARIANT_TRUE;
+        hr = IFileSystem3_DriveExists(fs3, drivespec, &ret);
+        ok(hr == S_OK, "got 0x%08x for drive spec %s (%s)\n",
+                hr, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
+        ok(ret == ptr->expected_ret, "got %d, expected %d for drive spec %s (%s)\n",
+                ret, ptr->expected_ret, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
+
+        SysFreeString(drivespec);
+    }
+}
+
+struct getdrivename_test {
+    const WCHAR path[10];
+    const WCHAR drive[5];
+};
+
+static const struct getdrivename_test getdrivenametestdata[] = {
+    { {'C',':','\\','1','.','t','s','t',0}, {'C',':',0} },
+    { {'O',':','\\','1','.','t','s','t',0}, {'O',':',0} },
+    { {'O',':',0}, {'O',':',0} },
+    { {'o',':',0}, {'o',':',0} },
+    { {'O','O',':',0} },
+    { {':',0} },
+    { {'O',0} },
+    { { 0 } }
+};
+
+static void test_GetDriveName(void)
+{
+    const struct getdrivename_test *ptr = getdrivenametestdata;
+    HRESULT hr;
+    BSTR name;
+
+    hr = IFileSystem3_GetDriveName(fs3, NULL, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    name = (void*)0xdeadbeef;
+    hr = IFileSystem3_GetDriveName(fs3, NULL, &name);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(name == NULL, "got %p\n", name);
+
+    while (*ptr->path) {
+        BSTR path = SysAllocString(ptr->path);
+        name = (void*)0xdeadbeef;
+        hr = IFileSystem3_GetDriveName(fs3, path, &name);
+        ok(hr == S_OK, "got 0x%08x\n", hr);
+        if (name)
+            ok(!lstrcmpW(ptr->drive, name), "got %s, expected %s\n", wine_dbgstr_w(name), wine_dbgstr_w(ptr->drive));
+        else
+            ok(!*ptr->drive, "got %s, expected %s\n", wine_dbgstr_w(name), wine_dbgstr_w(ptr->drive));
+        SysFreeString(path);
+        SysFreeString(name);
+        ptr++;
+    }
+}
+
+struct getdrive_test {
+    const WCHAR drivespec[12];
+    HRESULT res;
+    const WCHAR driveletter[2];
+};
+
+static void test_GetDrive(void)
+{
+    HRESULT hr;
+    IDrive *drive_fixed, *drive;
+    BSTR dl_fixed, drivespec;
+    WCHAR root[] = {'?',':','\\',0};
+
+    drive = (void*)0xdeadbeef;
+    hr = IFileSystem3_GetDrive(fs3, NULL, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+    ok(drive == (void*)0xdeadbeef, "got %p\n", drive);
+
+    for (root[0] = 'A'; root[0] <= 'Z'; root[0]++)
+        if (GetDriveTypeW(root) == DRIVE_NO_ROOT_DIR)
+            break;
+
+    if (root[0] > 'Z')
+        skip("All drive letters are occupied, skipping test for nonexisting drive.\n");
+    else {
+        drivespec = SysAllocString(root);
+        drive = (void*)0xdeadbeef;
+        hr = IFileSystem3_GetDrive(fs3, drivespec, &drive);
+        ok(hr == CTL_E_DEVICEUNAVAILABLE, "got 0x%08x\n", hr);
+        ok(drive == NULL, "got %p\n", drive);
+        SysFreeString(drivespec);
+    }
+
+    drive_fixed = get_fixed_drive();
+    if (!drive_fixed) {
+        skip("No fixed drive found, skipping test.\n");
+        return;
+    }
+
+    hr = IDrive_get_DriveLetter(drive_fixed, &dl_fixed);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+
+    if (FAILED(hr))
+        skip("Could not retrieve drive letter of fixed drive, skipping test.\n");
+    else {
+        WCHAR dl_upper = toupper(dl_fixed[0]);
+        WCHAR dl_lower = tolower(dl_fixed[0]);
+        struct getdrive_test testdata[] = {
+            { {dl_upper,0}, S_OK, {dl_upper,0} },
+            { {dl_upper,':',0}, S_OK, {dl_upper,0} },
+            { {dl_upper,':','\\',0}, S_OK, {dl_upper,0} },
+            { {dl_lower,':','\\',0}, S_OK, {dl_upper,0} },
+            { {dl_upper,'\\',0}, E_INVALIDARG, { 0 } },
+            { {dl_lower,'\\',0}, E_INVALIDARG, { 0 } },
+            { {'$',':','\\',0}, E_INVALIDARG, { 0 } },
+            { {'\\','h','o','s','t','\\','s','h','a','r','e',0}, E_INVALIDARG, { 0 } },
+            { {'h','o','s','t','\\','s','h','a','r','e',0}, E_INVALIDARG, { 0 } },
+            { { 0 } },
+        };
+        struct getdrive_test *ptr = &testdata[0];
+
+        for (; *ptr->drivespec; ptr++) {
+            drivespec = SysAllocString(ptr->drivespec);
+            drive = (void*)0xdeadbeef;
+            hr = IFileSystem3_GetDrive(fs3, drivespec, &drive);
+            ok(hr == ptr->res, "got 0x%08x, expected 0x%08x for drive spec %s\n",
+                    hr, ptr->res, wine_dbgstr_w(ptr->drivespec));
+            ok(!lstrcmpW(ptr->drivespec, drivespec), "GetDrive modified its DriveSpec argument\n");
+            SysFreeString(drivespec);
+
+            if (*ptr->driveletter) {
+                BSTR driveletter;
+                hr = IDrive_get_DriveLetter(drive, &driveletter);
+                ok(hr == S_OK, "got 0x%08x for drive spec %s\n", hr, wine_dbgstr_w(ptr->drivespec));
+                if (SUCCEEDED(hr)) {
+                    ok(!lstrcmpW(ptr->driveletter, driveletter), "got %s, expected %s for drive spec %s\n",
+                            wine_dbgstr_w(driveletter), wine_dbgstr_w(ptr->driveletter),
+                            wine_dbgstr_w(ptr->drivespec));
+                    SysFreeString(driveletter);
+                }
+                IDrive_Release(drive);
+            } else
+                ok(drive == NULL, "got %p for drive spec %s\n", drive, wine_dbgstr_w(ptr->drivespec));
+        }
+        SysFreeString(dl_fixed);
+    }
+}
+
+static void test_SerialNumber(void)
+{
+    IDrive *drive;
+    LONG serial;
+    HRESULT hr;
+    BSTR name;
+
+    drive = get_fixed_drive();
+    if (!drive) {
+        skip("No fixed drive found, skipping test.\n");
+        return;
+    }
+
+    hr = IDrive_get_SerialNumber(drive, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    serial = 0xdeadbeef;
+    hr = IDrive_get_SerialNumber(drive, &serial);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(serial != 0xdeadbeef, "got %x\n", serial);
+
+    hr = IDrive_get_FileSystem(drive, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    name = NULL;
+    hr = IDrive_get_FileSystem(drive, &name);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(name != NULL, "got %p\n", name);
+    SysFreeString(name);
+
+    hr = IDrive_get_VolumeName(drive, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    name = NULL;
+    hr = IDrive_get_VolumeName(drive, &name);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ok(name != NULL, "got %p\n", name);
+    SysFreeString(name);
+
+    IDrive_Release(drive);
+}
+
+static const struct extension_test {
+    WCHAR path[20];
+    WCHAR ext[10];
+} extension_tests[] = {
+    { {'n','o','e','x','t',0}, {0} },
+    { {'n','.','o','.','e','x','t',0}, {'e','x','t',0} },
+    { {'n','.','o','.','e','X','t',0}, {'e','X','t',0} },
+    { { 0 } }
+};
+
+static void test_GetExtensionName(void)
+{
+    BSTR path, ext;
+    HRESULT hr;
+    int i;
+
+    for (i = 0; i < sizeof(extension_tests)/sizeof(extension_tests[0]); i++) {
+
+       path = SysAllocString(extension_tests[i].path);
+       ext = NULL;
+       hr = IFileSystem3_GetExtensionName(fs3, path, &ext);
+       ok(hr == S_OK, "got 0x%08x\n", hr);
+       if (*extension_tests[i].ext)
+           ok(!lstrcmpW(ext, extension_tests[i].ext), "%d: path %s, got %s, expected %s\n", i,
+               wine_dbgstr_w(path), wine_dbgstr_w(ext), wine_dbgstr_w(extension_tests[i].ext));
+       else
+           ok(ext == NULL, "%d: path %s, got %s, expected %s\n", i,
+               wine_dbgstr_w(path), wine_dbgstr_w(ext), wine_dbgstr_w(extension_tests[i].ext));
+
+       SysFreeString(path);
+       SysFreeString(ext);
+    }
+}
+
+static void test_GetSpecialFolder(void)
+{
+    WCHAR pathW[MAX_PATH];
+    IFolder *folder;
+    HRESULT hr;
+    DWORD ret;
+    BSTR path;
+
+    hr = IFileSystem3_GetSpecialFolder(fs3, WindowsFolder, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder+1, NULL);
+    ok(hr == E_POINTER, "got 0x%08x\n", hr);
+
+    hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder+1, &folder);
+    ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
+
+    hr = IFileSystem3_GetSpecialFolder(fs3, WindowsFolder, &folder);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    hr = IFolder_get_Path(folder, &path);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    GetWindowsDirectoryW(pathW, sizeof(pathW)/sizeof(WCHAR));
+    ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
+    SysFreeString(path);
+    IFolder_Release(folder);
+
+    hr = IFileSystem3_GetSpecialFolder(fs3, SystemFolder, &folder);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    hr = IFolder_get_Path(folder, &path);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    GetSystemDirectoryW(pathW, sizeof(pathW)/sizeof(WCHAR));
+    ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
+    SysFreeString(path);
+    IFolder_Release(folder);
+
+    hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder, &folder);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    hr = IFolder_get_Path(folder, &path);
+    ok(hr == S_OK, "got 0x%08x\n", hr);
+    ret = GetTempPathW(sizeof(pathW)/sizeof(WCHAR), pathW);
+    if (ret && pathW[ret-1] == '\\')
+        pathW[ret-1] = 0;
+
+    ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
+    SysFreeString(path);
+    IFolder_Release(folder);
+}
+
 START_TEST(filesystem)
 {
     HRESULT hr;
@@ -1750,6 +2197,12 @@ START_TEST(filesystem)
     test_WriteLine();
     test_ReadAll();
     test_Read();
+    test_DriveExists();
+    test_GetDriveName();
+    test_GetDrive();
+    test_SerialNumber();
+    test_GetExtensionName();
+    test_GetSpecialFolder();
 
     IFileSystem3_Release(fs3);