[BTRFS]
authorPierre Schweitzer <pierre@reactos.org>
Wed, 2 Nov 2016 19:36:59 +0000 (19:36 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Wed, 2 Nov 2016 19:36:59 +0000 (19:36 +0000)
Import the BTRFS driver shell extension. It allows having a finer control on btrfs volumes.
Note that not everything works in ReactOS and its usage may lead to bugchecks...

CORE-10892

svn path=/trunk/; revision=73100

16 files changed:
reactos/dll/shellext/CMakeLists.txt
reactos/dll/shellext/shellbtrfs/CMakeLists.txt [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/contextmenu.cpp [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/contextmenu.h [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/factory.cpp [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/factory.h [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/iconoverlay.cpp [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/iconoverlay.h [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/main.cpp [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/propsheet.cpp [new file with mode: 0755]
reactos/dll/shellext/shellbtrfs/propsheet.h [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/resource.h [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/shellbtrfs.manifest [new file with mode: 0755]
reactos/dll/shellext/shellbtrfs/shellbtrfs.rc [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/shellbtrfs.spec [new file with mode: 0644]
reactos/dll/shellext/shellbtrfs/subvol.ico [new file with mode: 0644]

index 1049d5b..f031c3f 100644 (file)
@@ -6,5 +6,6 @@ add_subdirectory(devcpux)
 add_subdirectory(fontext)
 add_subdirectory(netshell)
 add_subdirectory(ntobjshex)
+add_subdirectory(shellbtrfs)
 add_subdirectory(slayer)
-add_subdirectory(stobject)
\ No newline at end of file
+add_subdirectory(stobject)
diff --git a/reactos/dll/shellext/shellbtrfs/CMakeLists.txt b/reactos/dll/shellext/shellbtrfs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..454179b
--- /dev/null
@@ -0,0 +1,23 @@
+set_cpp(WITH_RUNTIME WITH_EXCEPTIONS WITH_STL)
+
+remove_definitions(-D_WIN32_WINNT=0x502)
+add_definitions(-D_WIN32_WINNT=0x600)
+
+spec2def(shellbtrfs.dll shellbtrfs.spec)
+
+list(APPEND SOURCE
+    contextmenu.cpp
+    factory.cpp
+    iconoverlay.cpp
+    main.cpp
+    propsheet.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/shellbtrfs.def)
+
+file(GLOB shellbtrfs_rc_deps *.ico)
+add_rc_deps(shellbtrfs.rc ${shellbtrfs_rc_deps})
+
+add_library(shellbtrfs SHARED ${SOURCE} shellbtrfs.rc)
+set_module_type(shellbtrfs win32dll UNICODE)
+target_link_libraries(shellbtrfs uuid)
+add_importlibs(shellbtrfs advapi32 advapi32_vista ole32 shell32 shlwapi user32 comctl32 uxtheme msvcrt kernel32 ntdll)
+add_cd_file(TARGET shellbtrfs DESTINATION reactos/system32 FOR all)
diff --git a/reactos/dll/shellext/shellbtrfs/contextmenu.cpp b/reactos/dll/shellext/shellbtrfs/contextmenu.cpp
new file mode 100644 (file)
index 0000000..f04228b
--- /dev/null
@@ -0,0 +1,473 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __REACTOS__
+#include <windows.h>
+#include <winternl.h>
+#else
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <strsafe.h>
+#include <ndk/iofuncs.h>
+#endif
+
+#define NO_SHLWAPI_STRFCNS
+#include <shlwapi.h>
+
+#include "contextmenu.h"
+#include "resource.h"
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "../../drivers/filesystems/btrfs/btrfsioctl.h"
+#endif
+
+#define NEW_SUBVOL_VERBA "newsubvol"
+#define NEW_SUBVOL_VERBW L"newsubvol"
+#define SNAPSHOT_VERBA "snapshot"
+#define SNAPSHOT_VERBW L"snapshot"
+
+#ifndef __REACTOS__
+// FIXME - is there a way to link to the proper header files without breaking everything?
+#ifdef __cplusplus
+extern "C" {
+#endif
+NTSYSCALLAPI NTSTATUS NTAPI NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
+#ifdef __cplusplus
+}
+#endif
+
+#define STATUS_SUCCESS          (NTSTATUS)0x00000000
+#endif
+
+typedef struct _KEY_NAME_INFORMATION {
+    ULONG NameLength;
+    WCHAR Name[1];
+} KEY_NAME_INFORMATION;
+
+typedef ULONG (WINAPI *_RtlNtStatusToDosError)(NTSTATUS Status);
+
+extern HMODULE module;
+
+// FIXME - don't assume subvol's top inode is 0x100
+
+HRESULT __stdcall BtrfsContextMenu::QueryInterface(REFIID riid, void **ppObj) {
+    if (riid == IID_IUnknown || riid == IID_IContextMenu) {
+        *ppObj = static_cast<IContextMenu*>(this); 
+        AddRef();
+        return S_OK;
+    } else if (riid == IID_IShellExtInit) {
+        *ppObj = static_cast<IShellExtInit*>(this); 
+        AddRef();
+        return S_OK;
+    }
+
+    *ppObj = NULL;
+    return E_NOINTERFACE;
+}
+
+HRESULT __stdcall BtrfsContextMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
+    HANDLE h;
+    IO_STATUS_BLOCK iosb;
+    btrfs_get_file_ids bgfi;
+    NTSTATUS Status;
+    
+    if (!pidlFolder) {
+        FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+        UINT num_files, i;
+        WCHAR fn[MAX_PATH];
+        HDROP hdrop;
+        
+        if (!pdtobj)
+            return E_FAIL;
+        
+        stgm.tymed = TYMED_HGLOBAL;
+        
+        if (FAILED(pdtobj->GetData(&format, &stgm)))
+            return E_INVALIDARG;
+        
+        stgm_set = TRUE;
+        
+        hdrop = (HDROP)GlobalLock(stgm.hGlobal);
+        
+        if (!hdrop) {
+            ReleaseStgMedium(&stgm);
+            stgm_set = FALSE;
+            return E_INVALIDARG;
+        }
+         
+        num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+        
+        for (i = 0; i < num_files; i++) {
+            if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+                h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    
+                if (h != INVALID_HANDLE_VALUE) {
+                    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
+                        
+                    if (Status == STATUS_SUCCESS && bgfi.inode == 0x100 && !bgfi.top) {
+                        WCHAR parpath[MAX_PATH];
+                        HANDLE h2;
+                        
+                        StringCchCopyW(parpath, sizeof(parpath) / sizeof(WCHAR), fn);
+                        
+                        PathRemoveFileSpecW(parpath);
+                        
+                        h2 = CreateFileW(parpath, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    
+                        if (h2 == INVALID_HANDLE_VALUE) {
+                            CloseHandle(h);
+                            return E_FAIL;
+                        }
+                        
+                        ignore = FALSE;
+                        bg = FALSE;
+                        
+                        CloseHandle(h2);
+                        CloseHandle(h);
+                        return S_OK;
+                    }
+                    
+                    CloseHandle(h);
+                }
+            }
+        }
+        
+        return S_OK;
+    }
+
+    if (!SHGetPathFromIDListW(pidlFolder, path))
+        return E_FAIL;
+    
+    // check we have permissions to create new subdirectory
+    
+    h = CreateFileW(path, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    
+    if (h == INVALID_HANDLE_VALUE)
+        return E_FAIL;
+    
+    // check is Btrfs volume
+    
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
+    
+    if (Status != STATUS_SUCCESS) {
+        CloseHandle(h);
+        return E_FAIL;
+    }
+    
+    CloseHandle(h);
+    
+    ignore = FALSE;
+    bg = TRUE;
+    
+    return S_OK;
+}
+
+HRESULT __stdcall BtrfsContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) {
+    WCHAR str[256];
+    
+    if (ignore)
+        return E_INVALIDARG;
+    
+    if (uFlags & CMF_DEFAULTONLY)
+        return S_OK;
+    
+    if (!bg) {
+        if (LoadStringW(module, IDS_CREATE_SNAPSHOT, str, sizeof(str) / sizeof(WCHAR)) == 0)
+            return E_FAIL;
+
+        if (!InsertMenuW(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst, str))
+            return E_FAIL;
+
+        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
+    }
+    
+    if (LoadStringW(module, IDS_NEW_SUBVOL, str, sizeof(str) / sizeof(WCHAR)) == 0)
+        return E_FAIL;
+
+    if (!InsertMenuW(hmenu, indexMenu, MF_BYPOSITION, idCmdFirst, str))
+        return E_FAIL;
+
+    return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 1);
+}
+
+static void ShowError(HWND hwnd, ULONG err) {
+    WCHAR* buf;
+    
+    if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+                       err, 0, (WCHAR*)&buf, 0, NULL) == 0) {
+        MessageBoxW(hwnd, L"FormatMessage failed", L"Error", MB_ICONERROR);
+        return;
+    }
+    
+    MessageBoxW(hwnd, buf, L"Error", MB_ICONERROR);
+    
+    LocalFree(buf);
+}
+
+void ShowNtStatusError(HWND hwnd, NTSTATUS Status) {
+    _RtlNtStatusToDosError RtlNtStatusToDosError;
+    HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
+    
+    if (!ntdll) {
+        MessageBoxW(hwnd, L"Error loading ntdll.dll", L"Error", MB_ICONERROR);
+        return;
+    }
+    
+    RtlNtStatusToDosError = (_RtlNtStatusToDosError)GetProcAddress(ntdll, "RtlNtStatusToDosError");
+    
+    if (!ntdll) {
+        MessageBoxW(hwnd, L"Error loading RtlNtStatusToDosError in ntdll.dll", L"Error", MB_ICONERROR);
+        FreeLibrary(ntdll);
+        return;
+    }
+    
+    ShowError(hwnd, RtlNtStatusToDosError(Status));
+    
+    FreeLibrary(ntdll);
+}
+
+static void create_snapshot(HWND hwnd, WCHAR* fn) {
+    HANDLE h;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_get_file_ids bgfi;
+    
+    h = CreateFileW(fn, FILE_TRAVERSE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    
+    if (h != INVALID_HANDLE_VALUE) {
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
+            
+        if (Status == STATUS_SUCCESS && bgfi.inode == 0x100 && !bgfi.top) {
+            WCHAR parpath[MAX_PATH], subvolname[MAX_PATH], templ[MAX_PATH], name[MAX_PATH], searchpath[MAX_PATH];
+            HANDLE h2, fff;
+            btrfs_create_snapshot* bcs;
+            ULONG namelen, pathend;
+            WIN32_FIND_DATAW wfd;
+            SYSTEMTIME time;
+            
+            StringCchCopyW(parpath, sizeof(parpath) / sizeof(WCHAR), fn);
+            PathRemoveFileSpecW(parpath);
+            
+            StringCchCopyW(subvolname, sizeof(subvolname) / sizeof(WCHAR), fn);
+            PathStripPathW(subvolname);
+            
+            h2 = CreateFileW(parpath, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+            if (h2 == INVALID_HANDLE_VALUE) {
+                ShowError(hwnd, GetLastError());
+                CloseHandle(h);
+                return;
+            }
+            
+            if (!LoadStringW(module, IDS_SNAPSHOT_FILENAME, templ, MAX_PATH)) {
+                ShowError(hwnd, GetLastError());
+                CloseHandle(h);
+                CloseHandle(h2);
+                return;
+            }
+            
+            GetLocalTime(&time);
+            
+            if (StringCchPrintfW(name, sizeof(name) / sizeof(WCHAR), templ, subvolname, time.wYear, time.wMonth, time.wDay) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+                MessageBoxW(hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR);
+                CloseHandle(h);
+                CloseHandle(h2);
+                return;
+            }
+            
+            StringCchCopyW(searchpath, sizeof(searchpath) / sizeof(WCHAR), parpath);
+            StringCchCatW(searchpath, sizeof(searchpath) / sizeof(WCHAR), L"\\");
+            pathend = wcslen(searchpath);
+            
+            StringCchCatW(searchpath, sizeof(searchpath) / sizeof(WCHAR), name);
+            
+            fff = FindFirstFileW(searchpath, &wfd);
+            
+            if (fff != INVALID_HANDLE_VALUE) {
+                ULONG i = wcslen(searchpath), num = 2;
+                
+                do {
+                    FindClose(fff);
+                    
+                    searchpath[i] = 0;
+                    if (StringCchPrintfW(searchpath, sizeof(searchpath) / sizeof(WCHAR), L"%s (%u)", searchpath, num) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+                        MessageBoxW(hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR);
+                        CloseHandle(h);
+                        CloseHandle(h2);
+                        return;
+                    }
+
+                    fff = FindFirstFileW(searchpath, &wfd);
+                    num++;
+                } while (fff != INVALID_HANDLE_VALUE);
+            }
+            
+            namelen = wcslen(&searchpath[pathend]) * sizeof(WCHAR);
+                        
+            bcs = (btrfs_create_snapshot*)malloc(sizeof(btrfs_create_snapshot) - 1 + namelen);
+            bcs->subvol = h;
+            bcs->namelen = namelen;
+            memcpy(bcs->name, &searchpath[pathend], namelen);
+            
+            Status = NtFsControlFile(h2, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SNAPSHOT, NULL, 0, bcs, sizeof(btrfs_create_snapshot) - 1 + namelen);
+        
+            if (Status != STATUS_SUCCESS)
+                ShowNtStatusError(hwnd, Status);
+            
+            CloseHandle(h2);
+        }
+        
+        CloseHandle(h);
+    } else
+        ShowError(hwnd, GetLastError());
+}
+
+HRESULT __stdcall BtrfsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici) {
+    if (ignore)
+        return E_INVALIDARG;
+    
+    if (!bg) {
+        if ((IS_INTRESOURCE(pici->lpVerb) && pici->lpVerb == 0) || !strcmp(pici->lpVerb, SNAPSHOT_VERBA)) {
+            UINT num_files, i;
+            WCHAR fn[MAX_PATH];
+            
+            if (!stgm_set)
+                return E_FAIL;
+            
+            num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+            
+            if (num_files == 0)
+                return E_FAIL;
+            
+            for (i = 0; i < num_files; i++) {
+                if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+                    create_snapshot(pici->hwnd, fn);
+                }
+            }
+
+            return S_OK;
+        }
+        
+        return E_FAIL;
+    }
+    
+    if ((IS_INTRESOURCE(pici->lpVerb) && pici->lpVerb == 0) || !strcmp(pici->lpVerb, NEW_SUBVOL_VERBA)) {
+        HANDLE h;
+        IO_STATUS_BLOCK iosb;
+        NTSTATUS Status;
+        ULONG pathlen, searchpathlen, pathend;
+        WCHAR name[MAX_PATH], *searchpath;
+        HANDLE fff;
+        WIN32_FIND_DATAW wfd;
+        
+        if (!LoadStringW(module, IDS_NEW_SUBVOL_FILENAME, name, MAX_PATH)) {
+            ShowError(pici->hwnd, GetLastError());
+            return E_FAIL;
+        }
+        
+        h = CreateFileW(path, FILE_ADD_SUBDIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    
+        if (h == INVALID_HANDLE_VALUE) {
+            ShowError(pici->hwnd, GetLastError());
+            return E_FAIL;
+        }
+        
+        pathlen = wcslen(path);
+        
+        searchpathlen = pathlen + wcslen(name) + 10;
+        searchpath = (WCHAR*)malloc(searchpathlen * sizeof(WCHAR));
+        
+        StringCchCopyW(searchpath, searchpathlen, path);
+        StringCchCatW(searchpath, searchpathlen, L"\\");
+        pathend = wcslen(searchpath);
+        
+        StringCchCatW(searchpath, searchpathlen, name);
+        
+        fff = FindFirstFileW(searchpath, &wfd);
+        
+        if (fff != INVALID_HANDLE_VALUE) {
+            ULONG i = wcslen(searchpath), num = 2;
+            
+            do {
+                FindClose(fff);
+                
+                searchpath[i] = 0;
+                if (StringCchPrintfW(searchpath, searchpathlen, L"%s (%u)", searchpath, num) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+                    MessageBoxW(pici->hwnd, L"Filename too long.\n", L"Error", MB_ICONERROR);
+                    CloseHandle(h);
+                    return E_FAIL;
+                }
+
+                fff = FindFirstFileW(searchpath, &wfd);
+                num++;
+            } while (fff != INVALID_HANDLE_VALUE);
+        }
+        
+        Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_CREATE_SUBVOL, NULL, 0, &searchpath[pathend], wcslen(&searchpath[pathend]) * sizeof(WCHAR));
+        
+        free(searchpath);
+        
+        if (Status != STATUS_SUCCESS) {
+            CloseHandle(h);
+            ShowNtStatusError(pici->hwnd, Status);
+            return E_FAIL;
+        }
+        
+        CloseHandle(h);
+        
+        return S_OK;
+    }
+    
+    return E_FAIL;
+}
+
+HRESULT __stdcall BtrfsContextMenu::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax) {
+    if (ignore)
+        return E_INVALIDARG;
+    
+    if (idCmd != 0)
+        return E_INVALIDARG;
+    
+    switch (uFlags) {
+        case GCS_HELPTEXTA:
+            if (LoadStringA(module, bg ? IDS_NEW_SUBVOL_HELP_TEXT : IDS_CREATE_SNAPSHOT_HELP_TEXT, pszName, cchMax))
+                return S_OK;
+            else
+                return E_FAIL;
+            
+        case GCS_HELPTEXTW:
+            if (LoadStringW(module, bg ? IDS_NEW_SUBVOL_HELP_TEXT : IDS_CREATE_SNAPSHOT_HELP_TEXT, (LPWSTR)pszName, cchMax))
+                return S_OK;
+            else
+                return E_FAIL;
+            
+        case GCS_VALIDATEA:
+        case GCS_VALIDATEW:
+            return S_OK;
+            
+        case GCS_VERBA:
+            return StringCchCopyA(pszName, cchMax, bg ? NEW_SUBVOL_VERBA : SNAPSHOT_VERBA);
+            
+        case GCS_VERBW:
+            return StringCchCopyW((STRSAFE_LPWSTR)pszName, cchMax, bg ? NEW_SUBVOL_VERBW : SNAPSHOT_VERBW);
+            
+        default:
+            return E_INVALIDARG;
+    }
+}
diff --git a/reactos/dll/shellext/shellbtrfs/contextmenu.h b/reactos/dll/shellext/shellbtrfs/contextmenu.h
new file mode 100644 (file)
index 0000000..0ea33ab
--- /dev/null
@@ -0,0 +1,74 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <shlobj.h>
+
+extern LONG objs_loaded;
+
+class BtrfsContextMenu : public IShellExtInit, IContextMenu {
+public:
+    BtrfsContextMenu() {
+        refcount = 0;
+        ignore = TRUE;
+        stgm_set = FALSE;
+        InterlockedIncrement(&objs_loaded);
+    }
+
+    virtual ~BtrfsContextMenu() {
+        if (stgm_set) {
+            GlobalUnlock(stgm.hGlobal);
+            ReleaseStgMedium(&stgm);
+        }
+        
+        InterlockedDecrement(&objs_loaded);
+    }
+
+    // IUnknown
+    
+    HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
+    
+    ULONG __stdcall AddRef() {
+        return InterlockedIncrement(&refcount);
+    }
+
+    ULONG __stdcall Release() {
+        LONG rc = InterlockedDecrement(&refcount);
+        
+        if (rc == 0)
+            delete this;
+        
+        return rc;
+    }
+    
+    // IShellExtInit
+    
+    virtual HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID);
+    
+    // IContextMenu
+    
+    virtual HRESULT __stdcall QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
+    virtual HRESULT __stdcall InvokeCommand(LPCMINVOKECOMMANDINFO pici);
+    virtual HRESULT __stdcall GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT* pwReserved, LPSTR pszName, UINT cchMax);
+
+private:
+    LONG refcount;
+    BOOL ignore;
+    BOOL bg;
+    WCHAR path[MAX_PATH];
+    STGMEDIUM stgm;
+    BOOL stgm_set;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/factory.cpp b/reactos/dll/shellext/shellbtrfs/factory.cpp
new file mode 100644 (file)
index 0000000..dcde833
--- /dev/null
@@ -0,0 +1,82 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <windows.h>
+#include "factory.h"
+#include "iconoverlay.h"
+#include "contextmenu.h"
+#include "propsheet.h"
+
+HRESULT __stdcall Factory::QueryInterface(const IID& iid, void** ppv) {    
+    if (iid == IID_IUnknown || iid == IID_IClassFactory) {
+        *ppv = static_cast<IClassFactory*>(this);
+    } else {
+        *ppv = NULL;
+        return E_NOINTERFACE;
+    }
+
+    reinterpret_cast<IUnknown*>(*ppv)->AddRef();
+
+    return S_OK;
+}
+
+HRESULT __stdcall Factory::LockServer(BOOL bLock) {
+    return E_NOTIMPL;
+}
+
+HRESULT __stdcall Factory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv) {
+    if (pUnknownOuter)
+        return CLASS_E_NOAGGREGATION;
+
+    switch (type) {
+        case FactoryIconHandler:
+            if (iid == IID_IUnknown || iid == IID_IShellIconOverlayIdentifier) {
+                BtrfsIconOverlay* bio = new BtrfsIconOverlay;
+                if (!bio)
+                    return E_OUTOFMEMORY;
+
+                return bio->QueryInterface(iid, ppv);
+            }
+            break;
+            
+        case FactoryContextMenu:
+            if (iid == IID_IUnknown || iid == IID_IContextMenu || iid == IID_IShellExtInit) {
+                BtrfsContextMenu* bcm = new BtrfsContextMenu;
+                if (!bcm)
+                    return E_OUTOFMEMORY;
+                
+                return bcm->QueryInterface(iid, ppv);
+            }
+            break;
+            
+        case FactoryPropSheet:
+            if (iid == IID_IUnknown || iid == IID_IShellPropSheetExt || iid == IID_IShellExtInit) {
+                BtrfsPropSheet* bps = new BtrfsPropSheet;
+                if (!bps)
+                    return E_OUTOFMEMORY;
+                
+                return bps->QueryInterface(iid, ppv);
+            }
+            break;
+            
+        default:
+            break;
+    }
+    
+    *ppv = NULL;
+    return E_NOINTERFACE;
+}
diff --git a/reactos/dll/shellext/shellbtrfs/factory.h b/reactos/dll/shellext/shellbtrfs/factory.h
new file mode 100644 (file)
index 0000000..0779039
--- /dev/null
@@ -0,0 +1,65 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+extern LONG objs_loaded;
+
+typedef enum {
+    FactoryUnknown,
+    FactoryIconHandler,
+    FactoryContextMenu,
+    FactoryPropSheet
+} factory_type;
+
+class Factory : public IClassFactory {
+public:
+    Factory() {
+        refcount = 0;
+        type = FactoryUnknown;
+        InterlockedIncrement(&objs_loaded);
+    }
+
+    virtual ~Factory() {
+        InterlockedDecrement(&objs_loaded);
+    }
+
+    // IUnknown
+    
+    HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
+    
+    ULONG __stdcall AddRef() {
+        return InterlockedIncrement(&refcount);
+    }
+
+    ULONG __stdcall Release() {
+        LONG rc = InterlockedDecrement(&refcount);
+        
+        if (rc == 0)
+            delete this;
+        
+        return rc;
+    }
+
+    // IClassFactory
+    
+    virtual HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv);
+    virtual HRESULT __stdcall LockServer(BOOL bLock);
+    
+    factory_type type;
+
+private:
+    LONG refcount;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/iconoverlay.cpp b/reactos/dll/shellext/shellbtrfs/iconoverlay.cpp
new file mode 100644 (file)
index 0000000..1102c43
--- /dev/null
@@ -0,0 +1,109 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __REACTOS__
+#include <windows.h>
+#include <winternl.h>
+#else
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <ndk/iofuncs.h>
+#endif
+#include "iconoverlay.h"
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "../../drivers/filesystems/btrfs/btrfsioctl.h"
+#endif
+
+#ifndef __REACTOS__
+#ifdef __cplusplus
+extern "C" {
+#endif
+NTSYSCALLAPI NTSTATUS NTAPI NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
+#ifdef __cplusplus
+}
+#endif
+#define STATUS_SUCCESS 0
+#endif
+
+extern HMODULE module;
+
+HRESULT __stdcall BtrfsIconOverlay::QueryInterface(REFIID riid, void **ppObj) {
+    if (riid == IID_IUnknown || riid == IID_IShellIconOverlayIdentifier) {
+        *ppObj = static_cast<IShellIconOverlayIdentifier*>(this); 
+        AddRef();
+        return S_OK;
+    }
+
+    *ppObj = NULL;
+    return E_NOINTERFACE;
+}
+
+HRESULT __stdcall BtrfsIconOverlay::GetOverlayInfo(PWSTR pwszIconFile, int cchMax, int* pIndex, DWORD* pdwFlags) {
+    WCHAR dllpath[MAX_PATH];
+    
+    GetModuleFileNameW(module, dllpath, sizeof(dllpath));
+    
+    if (cchMax < (int)wcslen(dllpath))
+        return E_INVALIDARG;
+    
+    if (!pIndex)
+        return E_INVALIDARG;
+    
+    if (!pdwFlags)
+        return E_INVALIDARG;
+    
+    wcscpy(pwszIconFile, dllpath);
+    *pIndex = 0;
+    *pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
+    
+    return S_OK;
+}
+
+HRESULT __stdcall BtrfsIconOverlay::GetPriority(int *pPriority) {
+    if (!pPriority)
+        return E_INVALIDARG;
+    
+    *pPriority = 0;
+    
+    return S_OK;
+}
+
+HRESULT __stdcall BtrfsIconOverlay::IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib) {
+    HANDLE h;
+    NTSTATUS Status;
+    IO_STATUS_BLOCK iosb;
+    btrfs_get_file_ids bgfi;
+    
+    h = CreateFileW(pwszPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+    
+    if (h == INVALID_HANDLE_VALUE)
+        return S_FALSE;
+    
+    Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_FILE_IDS, NULL, 0, &bgfi, sizeof(btrfs_get_file_ids));
+    
+    if (Status != STATUS_SUCCESS) {
+        CloseHandle(h);
+        return S_FALSE;
+    }
+
+    CloseHandle(h);
+    
+    return (bgfi.inode == 0x100 && !bgfi.top) ? S_OK : S_FALSE;
+}
diff --git a/reactos/dll/shellext/shellbtrfs/iconoverlay.h b/reactos/dll/shellext/shellbtrfs/iconoverlay.h
new file mode 100644 (file)
index 0000000..fa50abb
--- /dev/null
@@ -0,0 +1,58 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <shlobj.h>
+
+extern LONG objs_loaded;
+
+class BtrfsIconOverlay : public IShellIconOverlayIdentifier {
+public:
+    BtrfsIconOverlay() {
+        refcount = 0;
+        InterlockedIncrement(&objs_loaded);
+    }
+
+    virtual ~BtrfsIconOverlay() {
+        InterlockedDecrement(&objs_loaded);
+    }
+
+    // IUnknown
+    
+    HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
+    
+    ULONG __stdcall AddRef() {
+        return InterlockedIncrement(&refcount);
+    }
+
+    ULONG __stdcall Release() {
+        LONG rc = InterlockedDecrement(&refcount);
+        
+        if (rc == 0)
+            delete this;
+        
+        return rc;
+    }
+
+    // IShellIconOverlayIdentifier
+    
+    virtual HRESULT __stdcall GetOverlayInfo(PWSTR pwszIconFile, int cchMax, int* pIndex, DWORD* pdwFlags);
+    virtual HRESULT __stdcall GetPriority(int *pPriority);
+    virtual HRESULT __stdcall IsMemberOf(PCWSTR pwszPath, DWORD dwAttrib);
+
+private:
+    LONG refcount;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/main.cpp b/reactos/dll/shellext/shellbtrfs/main.cpp
new file mode 100644 (file)
index 0000000..ddf04ac
--- /dev/null
@@ -0,0 +1,367 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef __REACTOS__
+#define _WIN32_WINNT 0x0600
+#endif
+
+#include <windows.h>
+#include <commctrl.h>
+#include "factory.h"
+
+static const GUID CLSID_ShellBtrfsIconHandler = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf0 } };
+static const GUID CLSID_ShellBtrfsContextMenu = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf1 } };
+static const GUID CLSID_ShellBtrfsPropSheet = { 0x2690b74f, 0xf353, 0x422d, { 0xbb, 0x12, 0x40, 0x15, 0x81, 0xee, 0xf8, 0xf2 } };
+
+#define COM_DESCRIPTION_ICON_HANDLER L"WinBtrfs shell extension (icon handler)"
+#define COM_DESCRIPTION_CONTEXT_MENU L"WinBtrfs shell extension (context menu)"
+#define COM_DESCRIPTION_PROP_SHEET L"WinBtrfs shell extension (property sheet)"
+#define ICON_OVERLAY_NAME L"WinBtrfs"
+
+HMODULE module;
+LONG objs_loaded = 0;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+STDAPI DllCanUnloadNow(void) {
+    return objs_loaded == 0 ? S_OK : S_FALSE;
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
+    if (rclsid == CLSID_ShellBtrfsIconHandler) {
+        Factory* fact = new Factory;
+        if (!fact)
+            return E_OUTOFMEMORY;
+        else {
+            fact->type = FactoryIconHandler;
+            
+            return fact->QueryInterface(riid, ppv);
+        }
+    } else if (rclsid == CLSID_ShellBtrfsContextMenu) {
+        Factory* fact = new Factory;
+        if (!fact)
+            return E_OUTOFMEMORY;
+        else {
+            fact->type = FactoryContextMenu;
+            
+            return fact->QueryInterface(riid, ppv);
+        }
+    } else if (rclsid == CLSID_ShellBtrfsPropSheet) {
+        Factory* fact = new Factory;
+        if (!fact)
+            return E_OUTOFMEMORY;
+        else {
+            fact->type = FactoryPropSheet;
+            
+            return fact->QueryInterface(riid, ppv);
+        }
+    }
+
+    return CLASS_E_CLASSNOTAVAILABLE;
+}
+
+static BOOL write_reg_key(HKEY root, const WCHAR* keyname, const WCHAR* val, DWORD type, const BYTE* data, DWORD datasize) {
+    LONG l;
+    HKEY hk;
+    DWORD dispos;
+   
+    l = RegCreateKeyExW(root, keyname, NULL, NULL, 0, KEY_ALL_ACCESS, NULL, &hk, &dispos);
+    if (l != ERROR_SUCCESS) {
+        WCHAR s[255];
+        wsprintfW(s, L"RegCreateKey returned %08x", l);
+        MessageBoxW(0, s, NULL, MB_ICONERROR);
+        
+        return FALSE;
+    }
+
+    l = RegSetValueExW(hk, val, NULL, type, data, datasize);
+    if (l != ERROR_SUCCESS) {
+        WCHAR s[255];
+        wsprintfW(s, L"RegSetValueEx returned %08x", l);
+        MessageBoxW(0, s, NULL, MB_ICONERROR);
+        
+        return FALSE;
+    }
+    
+    l = RegCloseKey(hk);
+    if (l != ERROR_SUCCESS) {
+        WCHAR s[255];
+        wsprintfW(s, L"RegCloseKey returned %08x", l);
+        MessageBoxW(0, s, NULL, MB_ICONERROR);
+        
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+static BOOL register_clsid(const GUID clsid, const WCHAR* description) {
+    WCHAR* clsidstring;
+    WCHAR inproc[MAX_PATH], progid[MAX_PATH], clsidkeyname[MAX_PATH], dllpath[MAX_PATH];
+    BOOL ret = FALSE;
+    
+    StringFromCLSID(clsid, &clsidstring);
+    
+    wsprintfW(inproc, L"CLSID\\%s\\InprocServer32", clsidstring);
+    wsprintfW(progid, L"CLSID\\%s\\ProgId", clsidstring);
+    wsprintfW(clsidkeyname, L"CLSID\\%s", clsidstring);
+    
+    if (!write_reg_key(HKEY_CLASSES_ROOT, clsidkeyname, NULL, REG_SZ, (BYTE*)description, (wcslen(description) + 1) * sizeof(WCHAR)))
+        goto end;
+    
+    GetModuleFileNameW(module, dllpath, sizeof(dllpath));
+    
+    if (!write_reg_key(HKEY_CLASSES_ROOT, inproc, NULL, REG_SZ, (BYTE*)dllpath, (wcslen(dllpath) + 1) * sizeof(WCHAR)))
+        goto end;
+    
+    if (!write_reg_key(HKEY_CLASSES_ROOT, inproc, L"ThreadingModel", REG_SZ, (BYTE*)L"Apartment", (wcslen(L"Apartment") + 1) * sizeof(WCHAR)))
+        goto end;
+    
+    ret = TRUE;
+    
+end:
+    CoTaskMemFree(clsidstring);
+
+    return ret;
+}
+
+static BOOL unregister_clsid(const GUID clsid) {
+    WCHAR* clsidstring;
+    WCHAR clsidkeyname[MAX_PATH];
+    BOOL ret = FALSE;
+    LONG l;
+    
+    StringFromCLSID(clsid, &clsidstring);
+    wsprintfW(clsidkeyname, L"CLSID\\%s", clsidstring);
+    
+    l = RegDeleteTreeW(HKEY_CLASSES_ROOT, clsidkeyname);
+    
+    if (l != ERROR_SUCCESS) {
+        WCHAR s[255];
+        wsprintfW(s, L"RegDeleteTree returned %08x", l);
+        MessageBoxW(0, s, NULL, MB_ICONERROR);
+        
+        ret = FALSE;
+    } else    
+        ret = TRUE;
+    
+    CoTaskMemFree(clsidstring);
+
+    return ret;
+}
+
+static BOOL reg_icon_overlay(const GUID clsid, const WCHAR* name) {
+    WCHAR path[MAX_PATH];
+    WCHAR* clsidstring;
+    BOOL ret = FALSE;
+    
+    StringFromCLSID(clsid, &clsidstring);
+    
+    wcscpy(path, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\");
+    wcscat(path, name);
+    
+    if (!write_reg_key(HKEY_LOCAL_MACHINE, path, NULL, REG_SZ, (BYTE*)clsidstring, (wcslen(clsidstring) + 1) * sizeof(WCHAR)))
+        goto end;
+
+    ret = TRUE;
+    
+end:
+    CoTaskMemFree(clsidstring);
+
+    return ret;
+}
+
+static BOOL unreg_icon_overlay(const WCHAR* name) {
+    WCHAR path[MAX_PATH];
+    LONG l;
+    
+    wcscpy(path, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\");
+    wcscat(path, name);
+    
+    l = RegDeleteTreeW(HKEY_LOCAL_MACHINE, path);
+    
+    if (l != ERROR_SUCCESS) {
+        WCHAR s[255];
+        wsprintfW(s, L"RegDeleteTree returned %08x", l);
+        MessageBoxW(0, s, NULL, MB_ICONERROR);
+        
+        return FALSE;
+    } else    
+        return TRUE;
+}
+
+static BOOL reg_context_menu_handler(const GUID clsid, const WCHAR* filetype, const WCHAR* name) {
+    WCHAR path[MAX_PATH];
+    WCHAR* clsidstring;
+    BOOL ret = FALSE;
+    
+    StringFromCLSID(clsid, &clsidstring);
+    
+    wcscpy(path, filetype);
+    wcscat(path, L"\\ShellEx\\ContextMenuHandlers\\");
+    wcscat(path, name);
+    
+    if (!write_reg_key(HKEY_CLASSES_ROOT, path, NULL, REG_SZ, (BYTE*)clsidstring, (wcslen(clsidstring) + 1) * sizeof(WCHAR)))
+        goto end;
+
+    ret = TRUE;
+    
+end:
+    CoTaskMemFree(clsidstring);
+
+    return ret;
+}
+
+static BOOL unreg_context_menu_handler(const WCHAR* filetype, const WCHAR* name) {
+    WCHAR path[MAX_PATH];
+    LONG l;
+    
+    wcscpy(path, filetype);
+    wcscat(path, L"\\ShellEx\\ContextMenuHandlers\\");
+    wcscat(path, name);
+    
+    l = RegDeleteTreeW(HKEY_CLASSES_ROOT, path);
+    
+    if (l != ERROR_SUCCESS) {
+        WCHAR s[255];
+        wsprintfW(s, L"RegDeleteTree returned %08x", l);
+        MessageBoxW(0, s, NULL, MB_ICONERROR);
+        
+        return FALSE;
+    } else    
+        return TRUE;
+}
+
+static BOOL reg_prop_sheet_handler(const GUID clsid, const WCHAR* filetype, const WCHAR* name) {
+    WCHAR path[MAX_PATH];
+    WCHAR* clsidstring;
+    BOOL ret = FALSE;
+    
+    StringFromCLSID(clsid, &clsidstring);
+    
+    wcscpy(path, filetype);
+    wcscat(path, L"\\ShellEx\\PropertySheetHandlers\\");
+    wcscat(path, name);
+    
+    if (!write_reg_key(HKEY_CLASSES_ROOT, path, NULL, REG_SZ, (BYTE*)clsidstring, (wcslen(clsidstring) + 1) * sizeof(WCHAR)))
+        goto end;
+
+    ret = TRUE;
+    
+end:
+    CoTaskMemFree(clsidstring);
+
+    return ret;
+}
+
+static BOOL unreg_prop_sheet_handler(const WCHAR* filetype, const WCHAR* name) {
+    WCHAR path[MAX_PATH];
+    LONG l;
+    
+    wcscpy(path, filetype);
+    wcscat(path, L"\\ShellEx\\PropertySheetHandlers\\");
+    wcscat(path, name);
+    
+    l = RegDeleteTreeW(HKEY_CLASSES_ROOT, path);
+    
+    if (l != ERROR_SUCCESS) {
+        WCHAR s[255];
+        wsprintfW(s, L"RegDeleteTree returned %08x", l);
+        MessageBoxW(0, s, NULL, MB_ICONERROR);
+        
+        return FALSE;
+    } else    
+        return TRUE;
+}
+
+STDAPI DllRegisterServer(void) {
+    if (!register_clsid(CLSID_ShellBtrfsIconHandler, COM_DESCRIPTION_ICON_HANDLER))
+        return E_FAIL;
+    
+    if (!register_clsid(CLSID_ShellBtrfsContextMenu, COM_DESCRIPTION_CONTEXT_MENU))
+        return E_FAIL;
+    
+    if (!register_clsid(CLSID_ShellBtrfsPropSheet, COM_DESCRIPTION_PROP_SHEET))
+        return E_FAIL;
+    
+    if (!reg_icon_overlay(CLSID_ShellBtrfsIconHandler, ICON_OVERLAY_NAME)) {
+        MessageBoxW(0, L"Failed to register icon overlay.", NULL, MB_ICONERROR);
+        return E_FAIL;
+    }
+    
+    if (!reg_context_menu_handler(CLSID_ShellBtrfsContextMenu, L"Directory\\Background", ICON_OVERLAY_NAME)) {
+        MessageBoxW(0, L"Failed to register context menu handler.", NULL, MB_ICONERROR);
+        return E_FAIL;
+    }
+    
+    if (!reg_context_menu_handler(CLSID_ShellBtrfsContextMenu, L"Folder", ICON_OVERLAY_NAME)) {
+        MessageBoxW(0, L"Failed to register context menu handler.", NULL, MB_ICONERROR);
+        return E_FAIL;
+    }
+    
+    if (!reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet, L"Folder", ICON_OVERLAY_NAME)) {
+        MessageBoxW(0, L"Failed to register property sheet handler.", NULL, MB_ICONERROR);
+        return E_FAIL;
+    }
+    
+    if (!reg_prop_sheet_handler(CLSID_ShellBtrfsPropSheet, L"*", ICON_OVERLAY_NAME)) {
+        MessageBoxW(0, L"Failed to register property sheet handler.", NULL, MB_ICONERROR);
+        return E_FAIL;
+    }
+    
+    return S_OK;
+}
+
+STDAPI DllUnregisterServer(void) {
+    unreg_prop_sheet_handler(L"Folder", ICON_OVERLAY_NAME);
+    unreg_prop_sheet_handler(L"*", ICON_OVERLAY_NAME);
+    unreg_context_menu_handler(L"Folder", ICON_OVERLAY_NAME);
+    unreg_context_menu_handler(L"Directory\\Background", ICON_OVERLAY_NAME);
+    unreg_icon_overlay(ICON_OVERLAY_NAME);
+    
+    if (!unregister_clsid(CLSID_ShellBtrfsPropSheet))
+        return E_FAIL;
+    
+    if (!unregister_clsid(CLSID_ShellBtrfsContextMenu))
+        return E_FAIL;
+    
+    if (!unregister_clsid(CLSID_ShellBtrfsIconHandler))
+        return E_FAIL;
+
+    return S_OK;
+}
+
+STDAPI DllInstall(BOOL bInstall, LPCWSTR pszCmdLine) {
+    if (bInstall)
+        return DllRegisterServer();
+    else
+        return DllUnregisterServer();
+}
+
+BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, void* lpReserved) {
+    if (dwReason == DLL_PROCESS_ATTACH)
+        module = (HMODULE)hModule;
+        
+    return TRUE;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/reactos/dll/shellext/shellbtrfs/propsheet.cpp b/reactos/dll/shellext/shellbtrfs/propsheet.cpp
new file mode 100755 (executable)
index 0000000..9406b84
--- /dev/null
@@ -0,0 +1,1044 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#define ISOLATION_AWARE_ENABLED 1
+#define STRSAFE_NO_DEPRECATE
+
+#ifndef __REACTOS__
+#include <windows.h>
+#include <strsafe.h>
+#include <winternl.h>
+#else
+#define WIN32_NO_STATUS
+#include <windef.h>
+#include <winbase.h>
+#include <strsafe.h>
+#include <ndk/iofuncs.h>
+#endif
+
+#define NO_SHLWAPI_STRFCNS
+#include <shlwapi.h>
+#include <uxtheme.h>
+
+#include "propsheet.h"
+#include "resource.h"
+
+#ifndef __REACTOS__
+// FIXME - is there a way to link to the proper header files without breaking everything?
+#ifdef __cplusplus
+extern "C" {
+#endif
+NTSYSCALLAPI NTSTATUS NTAPI NtFsControlFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG FsControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength);
+#ifdef __cplusplus
+}
+#endif
+
+#define STATUS_SUCCESS          (NTSTATUS)0x00000000
+#endif
+
+#define BTRFS_TYPE_FILE      1
+#define BTRFS_TYPE_DIRECTORY 2
+#define BTRFS_TYPE_CHARDEV   3
+#define BTRFS_TYPE_BLOCKDEV  4
+#define BTRFS_TYPE_FIFO      5
+#define BTRFS_TYPE_SOCKET    6
+#define BTRFS_TYPE_SYMLINK   7
+
+extern HMODULE module;
+
+extern void ShowNtStatusError(HWND hwnd, NTSTATUS Status);
+static void format_size(UINT64 size, WCHAR* s, ULONG len);
+static void ShowError(HWND hwnd, ULONG err);
+
+HRESULT __stdcall BtrfsPropSheet::QueryInterface(REFIID riid, void **ppObj) {
+    if (riid == IID_IUnknown || riid == IID_IShellPropSheetExt) {
+        *ppObj = static_cast<IShellPropSheetExt*>(this); 
+        AddRef();
+        return S_OK;
+    } else if (riid == IID_IShellExtInit) {
+        *ppObj = static_cast<IShellExtInit*>(this); 
+        AddRef();
+        return S_OK;
+    }
+
+    *ppObj = NULL;
+    return E_NOINTERFACE;
+}
+
+void BtrfsPropSheet::add_to_search_list(WCHAR* fn) {
+    WCHAR* s;
+    
+    s = (WCHAR*)malloc((wcslen(fn) + 1) * sizeof(WCHAR));
+    if (!s)
+        return;
+    
+    memcpy(s, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
+    
+    search_list.push_back(s);
+}
+
+void BtrfsPropSheet::do_search(WCHAR* fn) {
+    HANDLE h;
+    WCHAR* ss;
+    WIN32_FIND_DATAW ffd;
+    
+    ss = (WCHAR*)malloc((wcslen(fn) + 3) * sizeof(WCHAR));
+    if (!ss)
+        return;
+    
+    memcpy(ss, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
+    wcscat(ss, L"\\*");
+    
+    h = FindFirstFileW(ss, &ffd);
+    if (h == INVALID_HANDLE_VALUE)
+        return;
+    
+    do {
+        if (ffd.cFileName[0] != '.' || ((ffd.cFileName[1] != 0) && (ffd.cFileName[1] != '.' || ffd.cFileName[2] != 0))) {
+            WCHAR* fn2 = (WCHAR*)malloc((wcslen(fn) + 1 + wcslen(ffd.cFileName) + 1) * sizeof(WCHAR));
+                
+            memcpy(fn2, fn, (wcslen(fn) + 1) * sizeof(WCHAR));
+            wcscat(fn2, L"\\");
+            wcscat(fn2, ffd.cFileName);
+            
+            if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+                add_to_search_list(fn2);
+            } else {
+                HANDLE fh;
+                
+                fh = CreateFileW(fn2, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                                 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+                
+                if (fh != INVALID_HANDLE_VALUE) {
+                    NTSTATUS Status;
+                    IO_STATUS_BLOCK iosb;
+                    btrfs_inode_info bii2;
+                    
+                    Status = NtFsControlFile(fh, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
+                
+                    if (Status == STATUS_SUCCESS) {
+                        sizes[0] += bii2.inline_length;
+                        sizes[1] += bii2.disk_size[0];
+                        sizes[2] += bii2.disk_size[1];
+                        sizes[3] += bii2.disk_size[2];
+                        totalsize += bii2.inline_length + bii2.disk_size[0] + bii2.disk_size[1] + bii2.disk_size[2];
+                    }
+                    
+                    CloseHandle(fh);
+                }
+                
+                free(fn2);
+            }
+        }
+    } while (FindNextFileW(h, &ffd));
+    
+    FindClose(h);
+}
+
+DWORD BtrfsPropSheet::search_list_thread() {
+    while (!search_list.empty()) {
+        WCHAR* fn = search_list.front();
+        
+        do_search(fn);
+        
+        search_list.pop_front();
+        free(fn);
+    }
+    
+    thread = NULL;
+    
+    return 0;
+}
+
+static DWORD WINAPI global_search_list_thread(LPVOID lpParameter) {
+    BtrfsPropSheet* bps = (BtrfsPropSheet*)lpParameter;
+    
+    return bps->search_list_thread();
+}
+
+HRESULT __stdcall BtrfsPropSheet::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID) {
+    HANDLE h;
+    IO_STATUS_BLOCK iosb;
+    NTSTATUS Status;
+    FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+    UINT num_files, i;
+    WCHAR fn[MAX_PATH];
+    HDROP hdrop;
+    
+    if (pidlFolder)
+        return E_FAIL;
+        
+    
+    if (!pdtobj)
+        return E_FAIL;
+    
+    stgm.tymed = TYMED_HGLOBAL;
+    
+    if (FAILED(pdtobj->GetData(&format, &stgm)))
+        return E_INVALIDARG;
+    
+    stgm_set = TRUE;
+    
+    hdrop = (HDROP)GlobalLock(stgm.hGlobal);
+    
+    if (!hdrop) {
+        ReleaseStgMedium(&stgm);
+        stgm_set = FALSE;
+        return E_INVALIDARG;
+    }
+        
+    num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+    
+    min_mode = 0;
+    max_mode = 0;
+    min_flags = 0;
+    max_flags = 0;
+    various_subvols = various_inodes = various_types = various_uids = various_gids = FALSE;
+    
+    can_change_perms = TRUE;
+    can_change_owner = TRUE;
+    can_change_nocow = TRUE;
+    
+    for (i = 0; i < num_files; i++) {
+        if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+            h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES | WRITE_DAC, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+            
+            if (h != INVALID_HANDLE_VALUE)
+                CloseHandle(h);
+            else
+                can_change_perms = FALSE;
+            
+            h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES | WRITE_OWNER, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+            
+            if (h != INVALID_HANDLE_VALUE)
+                CloseHandle(h);
+            else
+                can_change_owner = FALSE;
+            
+            h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+            if (h == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_WRITE_PROTECT)) {
+                h = CreateFileW(fn, FILE_TRAVERSE | FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
+                                FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+                
+                readonly = TRUE;
+            }
+            
+            if (h != INVALID_HANDLE_VALUE) {
+                BY_HANDLE_FILE_INFORMATION bhfi;
+                btrfs_inode_info bii2;
+                
+                if (GetFileInformationByHandle(h, &bhfi) && bhfi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+                    add_to_search_list(fn);
+                
+                Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
+                    
+                if (Status == STATUS_SUCCESS && !bii2.top) {
+                    int j;
+                    
+                    LARGE_INTEGER filesize;
+                    
+                    if (i == 0) {
+                        subvol = bii2.subvol;
+                        inode = bii2.inode;
+                        type = bii2.type;
+                        uid = bii2.st_uid;
+                        gid = bii2.st_gid;
+                        rdev = bii2.st_rdev;
+                    } else {
+                        if (subvol != bii2.subvol)
+                            various_subvols = TRUE;
+                        
+                        if (inode != bii2.inode)
+                            various_inodes = TRUE;
+                        
+                        if (type != bii2.type)
+                            various_types = TRUE;
+                        
+                        if (uid != bii2.st_uid)
+                            various_uids = TRUE;
+
+                        if (gid != bii2.st_gid)
+                            various_gids = TRUE;
+                    }
+
+                    if (bii2.inline_length > 0) {
+                        totalsize += bii2.inline_length;
+                        sizes[0] += bii2.inline_length;
+                    }
+                    
+                    for (j = 0; j < 3; j++) {
+                        if (bii2.disk_size[j] > 0) {
+                            totalsize += bii2.disk_size[j];
+                            sizes[j + 1] += bii2.disk_size[j];
+                        }
+                    }
+                    
+                    min_mode |= ~bii2.st_mode;
+                    max_mode |= bii2.st_mode;
+                    min_flags |= ~bii2.flags;
+                    max_flags |= bii2.flags;
+                    
+                    ignore = FALSE;
+                    
+                    if (bii2.type != BTRFS_TYPE_DIRECTORY && GetFileSizeEx(h, &filesize)) {
+                        if (filesize.QuadPart != 0)
+                            can_change_nocow = FALSE;
+                    }
+                    
+                    CloseHandle(h);
+                } else {
+                    CloseHandle(h);
+                    return E_FAIL;
+                }
+            } else
+                return E_FAIL;
+        } else
+            return E_FAIL;
+    }
+    
+    min_mode = ~min_mode;
+    min_flags = ~min_flags;
+    
+    mode = min_mode;
+    mode_set = ~(min_mode ^ max_mode);
+    
+    flags = min_flags;
+    flags_set = ~(min_flags ^ max_flags);
+    
+    if (search_list.size() > 0) {
+        thread = CreateThread(NULL, 0, global_search_list_thread, this, 0, NULL);
+        
+        if (!thread)
+            ShowError(NULL, GetLastError());
+    }
+
+    return S_OK;
+}
+
+static ULONG inode_type_to_string_ref(UINT8 type) {
+    switch (type) {    
+        case BTRFS_TYPE_FILE:
+            return IDS_INODE_FILE;
+        
+        case BTRFS_TYPE_DIRECTORY:
+            return IDS_INODE_DIR;
+            
+        case BTRFS_TYPE_CHARDEV:
+            return IDS_INODE_CHAR;
+            
+        case BTRFS_TYPE_BLOCKDEV:
+            return IDS_INODE_BLOCK;
+            
+        case BTRFS_TYPE_FIFO:
+            return IDS_INODE_FIFO;
+            
+        case BTRFS_TYPE_SOCKET:
+            return IDS_INODE_SOCKET;
+            
+        case BTRFS_TYPE_SYMLINK:
+            return IDS_INODE_SYMLINK;
+            
+        default:
+            return IDS_INODE_UNKNOWN;
+    }
+}
+
+static void ShowError(HWND hwnd, ULONG err) {
+    WCHAR* buf;
+    
+    if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+                       err, 0, (WCHAR*)&buf, 0, NULL) == 0) {
+        MessageBoxW(hwnd, L"FormatMessage failed", L"Error", MB_ICONERROR);
+        return;
+    }
+    
+    MessageBoxW(hwnd, buf, L"Error", MB_ICONERROR);
+    
+    LocalFree(buf);
+}
+
+void BtrfsPropSheet::change_inode_flag(HWND hDlg, UINT64 flag, UINT state) {
+    if (flag & BTRFS_INODE_NODATACOW)
+        flag |= BTRFS_INODE_NODATASUM;
+    
+    if (state == BST_CHECKED) {
+        flags |= flag;
+        flags_set |= flag;
+    } else if (state == BST_UNCHECKED) {
+        flags &= ~flag;
+        flags_set |= flag;
+    } else if (state == BST_INDETERMINATE) {
+        flags_set = ~flag;
+    }
+    
+    flags_changed = TRUE;
+    
+    SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
+}
+
+void BtrfsPropSheet::apply_changes(HWND hDlg) {
+    UINT num_files, i;
+    WCHAR fn[MAX_PATH]; // FIXME - is this long enough?
+    HANDLE h;
+    IO_STATUS_BLOCK iosb;
+    NTSTATUS Status;
+    btrfs_set_inode_info bsii;
+    
+    if (various_uids)
+        uid_changed = FALSE;
+    
+    if (various_gids)
+        gid_changed = FALSE;
+    
+    if (!flags_changed && !perms_changed && !uid_changed && !gid_changed)
+        return;
+
+    num_files = DragQueryFileW((HDROP)stgm.hGlobal, 0xFFFFFFFF, NULL, 0);
+    
+    for (i = 0; i < num_files; i++) {
+        if (DragQueryFileW((HDROP)stgm.hGlobal, i, fn, sizeof(fn) / sizeof(MAX_PATH))) {
+            ULONG perms = FILE_TRAVERSE | FILE_READ_ATTRIBUTES;
+            
+            if (flags_changed)
+                perms |= FILE_WRITE_ATTRIBUTES;
+            
+            if (perms_changed)
+                perms |= WRITE_DAC;
+            
+            if (uid_changed || gid_changed)
+                perms |= WRITE_OWNER;
+            
+            h = CreateFileW(fn, perms, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+                            OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
+
+            if (h == INVALID_HANDLE_VALUE) {
+                ShowError(hDlg, GetLastError());
+                return;
+            }
+            
+            ZeroMemory(&bsii, sizeof(btrfs_set_inode_info));
+            
+            btrfs_inode_info bii2;
+                    
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_GET_INODE_INFO, NULL, 0, &bii2, sizeof(btrfs_inode_info));
+        
+            if (Status != STATUS_SUCCESS) {
+                ShowNtStatusError(hDlg, Status);
+                CloseHandle(h);
+                return;
+            }
+            
+            if (flags_changed) {
+                bsii.flags_changed = TRUE;
+                bsii.flags = (bii2.flags & ~flags_set) | (flags & flags_set);
+            }
+            
+            if (perms_changed) {
+                bsii.mode_changed = TRUE;
+                bsii.st_mode = (bii2.st_mode & ~mode_set) | (mode & mode_set);
+            }
+            
+            if (uid_changed) {
+                bsii.uid_changed = TRUE;
+                bsii.st_uid = uid;
+            }
+            
+            if (gid_changed) {
+                bsii.gid_changed = TRUE;
+                bsii.st_gid = gid;
+            }
+            
+            Status = NtFsControlFile(h, NULL, NULL, NULL, &iosb, FSCTL_BTRFS_SET_INODE_INFO, NULL, 0, &bsii, sizeof(btrfs_set_inode_info));
+            CloseHandle(h);
+
+            if (Status != STATUS_SUCCESS) {
+                WCHAR s[255], t[255];
+                
+                if (!LoadStringW(module, IDS_SET_INODE_INFO_ERROR, t, sizeof(t) / sizeof(WCHAR))) {
+                    ShowError(hDlg, GetLastError());
+                    return;
+                }
+                
+                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, Status) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+                    ShowError(hDlg, ERROR_INSUFFICIENT_BUFFER);
+                    return;
+                }
+                
+                MessageBoxW(hDlg, s, L"Error", MB_ICONERROR);
+            }
+        }
+    }
+    
+    flags_changed = FALSE;
+    perms_changed = FALSE;
+    uid_changed = FALSE;
+    gid_changed = FALSE;
+}
+
+void BtrfsPropSheet::set_size_on_disk(HWND hwndDlg) {
+    WCHAR size_on_disk[1024], s[1024], old_text[1024];
+    
+    format_size(totalsize, size_on_disk, sizeof(size_on_disk) / sizeof(WCHAR));
+    
+    if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), size_format, size_on_disk) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+        ShowError(hwndDlg, ERROR_INSUFFICIENT_BUFFER);
+        return;
+    }
+    
+    GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, old_text, sizeof(old_text) / sizeof(WCHAR));
+    
+    if (wcscmp(s, old_text))
+        SetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, s);
+}
+
+void BtrfsPropSheet::change_perm_flag(HWND hDlg, ULONG flag, UINT state) {
+    if (state == BST_CHECKED) {
+        mode |= flag;
+        mode_set |= flag;
+    } else if (state == BST_UNCHECKED) {
+        mode &= ~flag;
+        mode_set |= flag;
+    } else if (state == BST_INDETERMINATE) {
+        mode_set = ~flag;
+    }
+    
+    perms_changed = TRUE;
+    
+    SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
+}
+
+void BtrfsPropSheet::change_uid(HWND hDlg, UINT32 uid) {
+    this->uid = uid;
+    
+    uid_changed = TRUE;
+    
+    SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
+}
+
+void BtrfsPropSheet::change_gid(HWND hDlg, UINT32 gid) {
+    this->gid = gid;
+    
+    gid_changed = TRUE;
+    
+    SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
+}
+
+void BtrfsPropSheet::update_size_details_dialog(HWND hDlg) {
+    WCHAR size[1024], old_text[1024];
+    int i;
+    ULONG items[] = { IDC_SIZE_INLINE, IDC_SIZE_UNCOMPRESSED, IDC_SIZE_ZLIB, IDC_SIZE_LZO };
+    
+    for (i = 0; i < 4; i++) {
+        format_size(sizes[i], size, sizeof(size) / sizeof(WCHAR));
+        
+        GetDlgItemTextW(hDlg, items[i], old_text, sizeof(old_text) / sizeof(WCHAR));
+        
+        if (wcscmp(size, old_text))
+            SetDlgItemTextW(hDlg, items[i], size);
+    }
+}
+
+static INT_PTR CALLBACK SizeDetailsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            BtrfsPropSheet* bps = (BtrfsPropSheet*)lParam;
+            
+            SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
+            
+            bps->update_size_details_dialog(hwndDlg);
+            
+            if (bps->thread)
+                SetTimer(hwndDlg, 1, 250, NULL);
+            
+            return TRUE;
+        }
+            
+        case WM_COMMAND:
+            if (HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)) {
+                EndDialog(hwndDlg, 0);
+                return TRUE;
+            }
+        break;
+        
+        case WM_TIMER:
+        {
+            BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+            
+            if (bps) {
+                bps->update_size_details_dialog(hwndDlg);
+                
+                if (!bps->thread)
+                    KillTimer(hwndDlg, 1);
+            }
+            
+            break;
+        }
+    }
+    
+    return FALSE;
+}
+
+static void set_check_box(HWND hwndDlg, ULONG id, UINT64 min, UINT64 max) {
+    if (min && max) {
+        SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_CHECKED, 0);
+    } else if (!min && !max) {
+        SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_UNCHECKED, 0);
+    } else {
+        LONG_PTR style;
+        
+        style = GetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE);
+        style &= ~BS_AUTOCHECKBOX;
+        style |= BS_AUTO3STATE;
+        SetWindowLongPtr(GetDlgItem(hwndDlg, id), GWL_STYLE, style);
+        
+        SendDlgItemMessage(hwndDlg, id, BM_SETCHECK, BST_INDETERMINATE, 0);
+    }
+}
+
+static INT_PTR CALLBACK PropSheetDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+    switch (uMsg) {
+        case WM_INITDIALOG:
+        {
+            PROPSHEETPAGE* psp = (PROPSHEETPAGE*)lParam;
+            BtrfsPropSheet* bps = (BtrfsPropSheet*)psp->lParam;
+            WCHAR s[255];
+            ULONG sr;
+            int i;
+            
+            static ULONG perm_controls[] = { IDC_USERR, IDC_USERW, IDC_USERX, IDC_GROUPR, IDC_GROUPW, IDC_GROUPX, IDC_OTHERR, IDC_OTHERW, IDC_OTHERX, 0 };
+            static ULONG perms[] = { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH, 0 };
+            
+            EnableThemeDialogTexture(hwndDlg, ETDT_ENABLETAB);
+            
+            SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)bps);
+            
+            if (bps->various_subvols) {
+                if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return FALSE;
+                }
+            } else {
+                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llx", bps->subvol) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return FALSE;
+            }
+            
+            SetDlgItemTextW(hwndDlg, IDC_SUBVOL, s);
+            
+            if (bps->various_inodes) {
+                if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return FALSE;
+                }
+            } else {
+                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%llx", bps->inode) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return FALSE;
+            }
+            
+            SetDlgItemTextW(hwndDlg, IDC_INODE, s);
+            
+            if (bps->various_types)
+                sr = IDS_VARIOUS;
+            else
+                sr = inode_type_to_string_ref(bps->type);
+            
+            if (bps->various_inodes) {
+                if (sr == IDS_INODE_CHAR)
+                    sr = IDS_INODE_CHAR_SIMPLE;
+                else if (sr == IDS_INODE_BLOCK)
+                    sr = IDS_INODE_BLOCK_SIMPLE;
+            }
+            
+            if (sr == IDS_INODE_UNKNOWN) {
+                WCHAR t[255];
+                
+                if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return FALSE;
+                }
+                
+                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, bps->type) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return FALSE;
+            } else if (sr == IDS_INODE_CHAR || sr == IDS_INODE_BLOCK) {
+                WCHAR t[255];
+                
+                if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return FALSE;
+                }
+                
+                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), t, (UINT64)((bps->rdev & 0xFFFFFFFFFFF) >> 20), (UINT32)(bps->rdev & 0xFFFFF)) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return FALSE;
+            } else {
+                if (!LoadStringW(module, sr, s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return FALSE;
+                }
+            }
+            
+            SetDlgItemTextW(hwndDlg, IDC_TYPE, s);
+            
+            GetDlgItemTextW(hwndDlg, IDC_SIZE_ON_DISK, bps->size_format, sizeof(bps->size_format) / sizeof(WCHAR));
+            bps->set_size_on_disk(hwndDlg);
+            
+            if (bps->thread)
+                SetTimer(hwndDlg, 1, 250, NULL);
+            
+            set_check_box(hwndDlg, IDC_NODATACOW, bps->min_flags & BTRFS_INODE_NODATACOW, bps->max_flags & BTRFS_INODE_NODATACOW);
+            set_check_box(hwndDlg, IDC_COMPRESS, bps->min_flags & BTRFS_INODE_COMPRESS, bps->max_flags & BTRFS_INODE_COMPRESS);
+            
+            i = 0;
+            while (perm_controls[i] != 0) {
+                set_check_box(hwndDlg, perm_controls[i], bps->min_mode & perms[i], bps->max_mode & perms[i]);
+                i++;
+            }
+            
+            if (bps->various_uids) {
+                if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return FALSE;
+                }
+                
+                EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
+            } else {
+                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%u", bps->uid) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return FALSE;
+            }
+            
+            SetDlgItemTextW(hwndDlg, IDC_UID, s);
+            
+            if (bps->various_gids) {
+                if (!LoadStringW(module, IDS_VARIOUS, s, sizeof(s) / sizeof(WCHAR))) {
+                    ShowError(hwndDlg, GetLastError());
+                    return FALSE;
+                }
+                
+                EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
+            } else {
+                if (StringCchPrintfW(s, sizeof(s) / sizeof(WCHAR), L"%u", bps->gid) == STRSAFE_E_INSUFFICIENT_BUFFER)
+                    return FALSE;
+            }
+            
+            SetDlgItemTextW(hwndDlg, IDC_GID, s);
+            
+            if (!bps->can_change_nocow)
+                EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
+            
+            if (!bps->can_change_owner) {
+                EnableWindow(GetDlgItem(hwndDlg, IDC_UID), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_GID), 0);
+            }
+            
+            if (!bps->can_change_perms) {
+                EnableWindow(GetDlgItem(hwndDlg, IDC_USERR), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_USERW), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_USERX), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_GROUPR), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_GROUPW), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_GROUPX), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_OTHERR), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_OTHERW), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_OTHERX), 0);
+            }
+            
+            if (bps->readonly) {
+                EnableWindow(GetDlgItem(hwndDlg, IDC_NODATACOW), 0);
+                EnableWindow(GetDlgItem(hwndDlg, IDC_COMPRESS), 0);
+            }
+            
+            return FALSE;
+        }
+        
+        case WM_COMMAND:
+        {
+            BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+            
+            if (bps && !bps->readonly) {
+                switch (HIWORD(wParam)) {
+                    case BN_CLICKED: {
+                        switch (LOWORD(wParam)) {
+                            case IDC_NODATACOW:
+                                bps->change_inode_flag(hwndDlg, BTRFS_INODE_NODATACOW, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_COMPRESS:
+                                bps->change_inode_flag(hwndDlg, BTRFS_INODE_COMPRESS, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_USERR:
+                                bps->change_perm_flag(hwndDlg, S_IRUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_USERW:
+                                bps->change_perm_flag(hwndDlg, S_IWUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_USERX:
+                                bps->change_perm_flag(hwndDlg, S_IXUSR, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_GROUPR:
+                                bps->change_perm_flag(hwndDlg, S_IRGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_GROUPW:
+                                bps->change_perm_flag(hwndDlg, S_IWGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_GROUPX:
+                                bps->change_perm_flag(hwndDlg, S_IXGRP, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_OTHERR:
+                                bps->change_perm_flag(hwndDlg, S_IROTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_OTHERW:
+                                bps->change_perm_flag(hwndDlg, S_IWOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                            
+                            case IDC_OTHERX:
+                                bps->change_perm_flag(hwndDlg, S_IXOTH, IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);
+                            break;
+                        }
+                    }
+                    
+                    case EN_CHANGE: {
+                        switch (LOWORD(wParam)) {
+                            case IDC_UID: {
+                                WCHAR s[255];
+                                
+                                GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
+                                
+                                bps->change_uid(hwndDlg, _wtoi(s));
+                                break;
+                            }
+                            
+                            case IDC_GID: {
+                                WCHAR s[255];
+                                
+                                GetDlgItemTextW(hwndDlg, LOWORD(wParam), s, sizeof(s) / sizeof(WCHAR));
+                                
+                                bps->change_gid(hwndDlg, _wtoi(s));
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+            
+            break;
+        }
+        
+        case WM_NOTIFY:
+        {
+            switch (((LPNMHDR)lParam)->code) {
+                case PSN_KILLACTIVE:
+                    SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, FALSE);
+                break;
+                    
+                case PSN_APPLY: {
+                    BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+                    
+                    bps->apply_changes(hwndDlg);
+                    SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
+                    break;
+                }
+                
+                case NM_CLICK:
+                case NM_RETURN: {
+                    if (((LPNMHDR)lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_SIZE_ON_DISK)) {
+                        PNMLINK pNMLink = (PNMLINK)lParam;
+                        
+                        if (pNMLink->item.iLink == 0)
+                            DialogBoxParamW(module, MAKEINTRESOURCEW(IDD_SIZE_DETAILS), hwndDlg, SizeDetailsDlgProc, GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
+                    }
+                    break;
+                }
+            }
+        }
+        
+        case WM_TIMER:
+        {
+            BtrfsPropSheet* bps = (BtrfsPropSheet*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+            
+            if (bps) {
+                bps->set_size_on_disk(hwndDlg);
+                
+                if (!bps->thread)
+                    KillTimer(hwndDlg, 1);
+            }
+            
+            break;
+        }
+    }
+    
+    return FALSE;
+}
+
+static void format_size(UINT64 size, WCHAR* s, ULONG len) {
+    WCHAR nb[255], nb2[255], t[255], bytes[255];
+    WCHAR kb[255];
+    ULONG sr;
+    float f;
+    NUMBERFMTW fmt;
+    WCHAR thou[4], grouping[64], *c;
+    
+    _i64tow(size, nb, 10);
+    
+    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, thou, sizeof(thou) / sizeof(WCHAR));
+    
+    fmt.NumDigits = 0;
+    fmt.LeadingZero = 1;
+    fmt.lpDecimalSep = (LPWSTR)L"."; // not used
+    fmt.lpThousandSep = thou;
+    fmt.NegativeOrder = 0;
+    
+    // Grouping code copied from dlls/shlwapi/string.c in Wine - thank you
+    
+    fmt.Grouping = 0;
+    GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, grouping, sizeof(grouping) / sizeof(WCHAR));
+    
+    c = grouping;
+    while (*c) {
+        if (*c >= '0' && *c < '9') {
+            fmt.Grouping *= 10;
+            fmt.Grouping += *c - '0';
+        }
+        
+        c++;
+    }
+
+    if (fmt.Grouping % 10 == 0)
+        fmt.Grouping /= 10;
+    else
+        fmt.Grouping *= 10;
+    
+    GetNumberFormatW(LOCALE_USER_DEFAULT, 0, nb, &fmt, nb2, sizeof(nb2) / sizeof(WCHAR));
+    
+    if (size < 1024) {
+        if (!LoadStringW(module, size == 1 ? IDS_SIZE_BYTE : IDS_SIZE_BYTES, t, sizeof(t) / sizeof(WCHAR))) {
+            ShowError(NULL, GetLastError());
+            return;
+        }
+        
+        if (StringCchPrintfW(s, len, t, nb2) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+            ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+            return;
+        }
+        
+        return;
+    }
+    
+    if (!LoadStringW(module, IDS_SIZE_BYTES, t, sizeof(t) / sizeof(WCHAR))) {
+        ShowError(NULL, GetLastError());
+        return;
+    }
+    
+    if (StringCchPrintfW(bytes, sizeof(bytes) / sizeof(WCHAR), t, nb2) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+        ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+        return;
+    }
+    
+    if (size >= 1152921504606846976) {
+        sr = IDS_SIZE_EB;
+        f = (float)size / 1152921504606846976.0f;
+    } else if (size >= 1125899906842624) {
+        sr = IDS_SIZE_PB;
+        f = (float)size / 1125899906842624.0f;
+    } else if (size >= 1099511627776) {
+        sr = IDS_SIZE_TB;
+        f = (float)size / 1099511627776.0f;
+    } else if (size >= 1073741824) {
+        sr = IDS_SIZE_GB;
+        f = (float)size / 1073741824.0f;
+    } else if (size >= 1048576) {
+        sr = IDS_SIZE_MB;
+        f = (float)size / 1048576.0f;
+    } else {
+        sr = IDS_SIZE_KB;
+        f = (float)size / 1024.0f;
+    }
+    
+    if (!LoadStringW(module, sr, t, sizeof(t) / sizeof(WCHAR))) {
+        ShowError(NULL, GetLastError());
+        return;
+    }
+    
+    if (StringCchPrintfW(kb, sizeof(kb) / sizeof(WCHAR), t, f) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+        ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+        return;
+    }
+    
+    if (!LoadStringW(module, IDS_SIZE_LARGE, t, sizeof(t) / sizeof(WCHAR))) {
+        ShowError(NULL, GetLastError());
+        return;
+    }
+    
+    if (StringCchPrintfW(s, len, t, kb, bytes) == STRSAFE_E_INSUFFICIENT_BUFFER) {
+        ShowError(NULL, ERROR_INSUFFICIENT_BUFFER);
+        return;
+    }
+}
+
+HRESULT __stdcall BtrfsPropSheet::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) {
+    PROPSHEETPAGE psp;
+    HPROPSHEETPAGE hPage;
+    INITCOMMONCONTROLSEX icex;
+    
+    if (ignore)
+        return S_OK;
+    
+    icex.dwSize = sizeof(icex);
+    icex.dwICC = ICC_LINK_CLASS;
+    
+    if (!InitCommonControlsEx(&icex)) {
+        MessageBoxW(NULL, L"InitCommonControlsEx failed", L"Error", MB_ICONERROR);
+    }
+    
+    psp.dwSize = sizeof(psp);
+    psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
+    psp.hInstance = module;
+    psp.pszTemplate = MAKEINTRESOURCE(IDD_PROP_SHEET);
+    psp.hIcon = 0;
+    psp.pszTitle = MAKEINTRESOURCE(IDS_PROP_SHEET_TITLE);
+    psp.pfnDlgProc = (DLGPROC)PropSheetDlgProc;
+    psp.pcRefParent = (UINT*)&objs_loaded;
+    psp.pfnCallback = NULL;
+    psp.lParam = (LPARAM)this;
+
+    hPage = CreatePropertySheetPage(&psp);
+            
+    if (hPage) {
+        if (pfnAddPage(hPage, lParam)) {
+            this->AddRef();
+            return S_OK;
+        } else
+            DestroyPropertySheetPage(hPage);
+    } else
+        return E_OUTOFMEMORY;
+    
+    return E_FAIL;
+}
+
+HRESULT __stdcall BtrfsPropSheet::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) {
+    return S_OK;
+}
diff --git a/reactos/dll/shellext/shellbtrfs/propsheet.h b/reactos/dll/shellext/shellbtrfs/propsheet.h
new file mode 100644 (file)
index 0000000..d460692
--- /dev/null
@@ -0,0 +1,167 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs 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 Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <shlobj.h>
+#include <deque>
+#ifndef __REACTOS__
+#include "../btrfsioctl.h"
+#else
+#include "../../drivers/filesystems/btrfs/btrfsioctl.h"
+#endif
+
+#ifndef S_IRUSR
+#define S_IRUSR 0000400
+#endif
+
+#ifndef S_IWUSR
+#define S_IWUSR 0000200
+#endif
+
+#ifndef S_IXUSR
+#define S_IXUSR 0000100
+#endif
+
+#ifndef S_IRGRP
+#define S_IRGRP (S_IRUSR >> 3)
+#endif
+
+#ifndef S_IWGRP
+#define S_IWGRP (S_IWUSR >> 3)
+#endif
+
+#ifndef S_IXGRP
+#define S_IXGRP (S_IXUSR >> 3)
+#endif
+
+#ifndef S_IROTH
+#define S_IROTH (S_IRGRP >> 3)
+#endif
+
+#ifndef S_IWOTH
+#define S_IWOTH (S_IWGRP >> 3)
+#endif
+
+#ifndef S_IXOTH
+#define S_IXOTH (S_IXGRP >> 3)
+#endif
+
+#define BTRFS_INODE_NODATASUM   0x001
+#define BTRFS_INODE_NODATACOW   0x002
+#define BTRFS_INODE_READONLY    0x004
+#define BTRFS_INODE_NOCOMPRESS  0x008
+#define BTRFS_INODE_PREALLOC    0x010
+#define BTRFS_INODE_SYNC        0x020
+#define BTRFS_INODE_IMMUTABLE   0x040
+#define BTRFS_INODE_APPEND      0x080
+#define BTRFS_INODE_NODUMP      0x100
+#define BTRFS_INODE_NOATIME     0x200
+#define BTRFS_INODE_DIRSYNC     0x400
+#define BTRFS_INODE_COMPRESS    0x800
+
+extern LONG objs_loaded;
+
+class BtrfsPropSheet : public IShellExtInit, IShellPropSheetExt {
+public:
+    BtrfsPropSheet() {
+        refcount = 0;
+        ignore = TRUE;
+        stgm_set = FALSE;
+        readonly = FALSE;
+        flags_changed = FALSE;
+        perms_changed = FALSE;
+        uid_changed = FALSE;
+        gid_changed = FALSE;
+        can_change_perms = FALSE;
+        can_change_owner = FALSE;
+        thread = NULL;
+        mode = mode_set = 0;
+        flags = flags_set = 0;
+        
+        sizes[0] = sizes[1] = sizes[2] = sizes[3] = 0;
+        totalsize = 0;
+        
+        InterlockedIncrement(&objs_loaded);
+    }
+
+    virtual ~BtrfsPropSheet() {
+        if (stgm_set) {
+            GlobalUnlock(stgm.hGlobal);
+            ReleaseStgMedium(&stgm);
+        }
+        
+        InterlockedDecrement(&objs_loaded);
+    }
+
+    // IUnknown
+    
+    HRESULT __stdcall QueryInterface(REFIID riid, void **ppObj);
+    
+    ULONG __stdcall AddRef() {
+        return InterlockedIncrement(&refcount);
+    }
+
+    ULONG __stdcall Release() {
+        LONG rc = InterlockedDecrement(&refcount);
+        
+        if (rc == 0)
+            delete this;
+        
+        return rc;
+    }
+    
+    // IShellExtInit
+    
+    virtual HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID);
+    
+    // IShellPropSheetExt
+    
+    virtual HRESULT __stdcall AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
+    virtual HRESULT __stdcall ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam);
+    
+    void change_inode_flag(HWND hDlg, UINT64 flag, UINT state);
+    void change_perm_flag(HWND hDlg, ULONG perm, UINT state);
+    void change_uid(HWND hDlg, UINT32 uid);
+    void change_gid(HWND hDlg, UINT32 gid);
+    void apply_changes(HWND hDlg);
+    void set_size_on_disk(HWND hwndDlg);
+    void add_to_search_list(WCHAR* fn);
+    DWORD search_list_thread();
+    void do_search(WCHAR* fn);
+    void update_size_details_dialog(HWND hDlg);
+    BOOL readonly;
+    BOOL can_change_perms;
+    BOOL can_change_owner;
+    BOOL can_change_nocow;
+    WCHAR size_format[255];
+    HANDLE thread;
+    UINT32 min_mode, max_mode, mode, mode_set;
+    UINT64 min_flags, max_flags, flags, flags_set;
+    UINT64 subvol, inode, rdev;
+    UINT8 type;
+    UINT32 uid, gid;
+    BOOL various_subvols, various_inodes, various_types, various_uids, various_gids;
+    
+private:
+    LONG refcount;
+    BOOL ignore;
+    STGMEDIUM stgm;
+    BOOL stgm_set;
+    BOOL flags_changed, perms_changed, uid_changed, gid_changed;
+    UINT64 sizes[4], totalsize;
+    std::deque<WCHAR*> search_list;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/resource.h b/reactos/dll/shellext/shellbtrfs/resource.h
new file mode 100644 (file)
index 0000000..b778b22
--- /dev/null
@@ -0,0 +1,69 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by shellbtrfs.rc
+//
+#define IDI_ICON1                       101
+#define IDS_NEW_SUBVOL_HELP_TEXT        102
+#define IDS_NEW_SUBVOL                  103
+#define IDD_SIZE_DETAILS                103
+#define IDS_NEW_SUBVOL_FILENAME         104
+#define IDS_CREATE_SNAPSHOT             105
+#define IDS_CREATE_SNAPSHOT_HELP_TEXT   106
+#define IDS_SNAPSHOT_FILENAME           107
+#define IDD_PROP_SHEET                  107
+#define IDS_PROP_SHEET_TITLE            108
+#define IDS_INODE_FILE                  109
+#define IDS_INODE_DIR                   110
+#define IDS_INODE_CHAR                  111
+#define IDS_INODE_BLOCK                 112
+#define IDS_INODE_FIFO                  113
+#define IDS_INODE_SOCKET                114
+#define IDS_INODE_SYMLINK               115
+#define IDS_INODE_UNKNOWN               116
+#define IDS_SET_INODE_INFO_ERROR        117
+#define IDS_SIZE_BYTE                   118
+#define IDS_SIZE_BYTES                  119
+#define IDS_SIZE_KB                     120
+#define IDS_SIZE_MB                     121
+#define IDS_SIZE_GB                     122
+#define IDS_SIZE_TB                     123
+#define IDS_SIZE_PB                     124
+#define IDS_SIZE_EB                     125
+#define IDS_VARIOUS                     126
+#define IDS_INODE_CHAR_SIMPLE           127
+#define IDS_INODE_BLOCK_SIMPLE          128
+#define IDS_SIZE_LARGE                  130
+#define IDC_UID                         1001
+#define IDC_GID                         1002
+#define IDC_USERR                       1003
+#define IDC_GROUPR                      1004
+#define IDC_OTHERR                      1005
+#define IDC_USERW                       1006
+#define IDC_GROUPW                      1007
+#define IDC_OTHERW                      1008
+#define IDC_USERX                       1009
+#define IDC_GROUPX                      1010
+#define IDC_OTHERX                      1011
+#define IDC_NODATACOW                   1012
+#define IDC_SUBVOL                      1013
+#define IDC_INODE                       1014
+#define IDC_TYPE                        1015
+#define IDC_COMPRESS                    1016
+#define IDC_SIZE_ON_DISK                1017
+#define IDC_GROUP_INFORMATION           1018
+#define IDC_SIZE_INLINE                 1019
+#define IDC_SIZE_UNCOMPRESSED           1020
+#define IDC_SIZE_ZLIB                   1021
+#define IDC_SIZE_ZLIB2                  1022
+#define IDC_SIZE_LZO                    1022
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        104
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1020
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/reactos/dll/shellext/shellbtrfs/shellbtrfs.manifest b/reactos/dll/shellext/shellbtrfs/shellbtrfs.manifest
new file mode 100755 (executable)
index 0000000..970c861
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+    <assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="WinBtrfs.shellext" type="win32" />
+    <description>WinBtrfs shell extension</description>
+    <dependency>
+        <dependentAssembly>
+            <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
+        </dependentAssembly>
+    </dependency>
+</assembly>
diff --git a/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc b/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc
new file mode 100644 (file)
index 0000000..0bfb971
--- /dev/null
@@ -0,0 +1,251 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1               ICON                    "subvol.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "080904b0"
+        BEGIN
+            VALUE "FileDescription", "WinBtrfs shell extension"
+            VALUE "FileVersion", "0.7"
+            VALUE "InternalName", "btrfs"
+            VALUE "LegalCopyright", "Copyright (c) Mark Harmstone 2016"
+            VALUE "OriginalFilename", "shellbtrfs.dll"
+            VALUE "ProductName", "WinBtrfs"
+            VALUE "ProductVersion", "0.7"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x809, 1200
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_PROP_SHEET DIALOGEX 0, 0, 235, 245
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_DISABLED | WS_CAPTION
+CAPTION "s"
+FONT 8, "MS Shell Dlg", 400, 0, 0x0
+BEGIN
+    LTEXT           "Subvolume:",IDC_STATIC,14,21,38,8
+    LTEXT           "Inode:",IDC_STATIC,14,35,21,8
+    GROUPBOX        "Information",IDC_GROUP_INFORMATION,7,7,221,71
+    LTEXT           "Type:",IDC_STATIC,14,49,18,8
+    GROUPBOX        "POSIX permissions",IDC_STATIC,7,82,221,102
+    LTEXT           "User:",IDC_STATIC,14,97,17,8
+    LTEXT           "Group:",IDC_STATIC,14,113,22,8
+    EDITTEXT        IDC_UID,94,95,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    EDITTEXT        IDC_GID,94,111,40,14,ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "User",IDC_STATIC,14,144,15,8
+    LTEXT           "Group",IDC_STATIC,14,156,20,8
+    LTEXT           "Others",IDC_STATIC,14,168,22,8
+    LTEXT           "Read",IDC_STATIC,50,134,17,8
+    LTEXT           "Write",IDC_STATIC,89,134,18,8
+    LTEXT           "Execute",IDC_STATIC,129,134,30,8
+    CONTROL         "",IDC_USERR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,146,16,10
+    CONTROL         "",IDC_GROUPR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,157,16,10
+    CONTROL         "",IDC_OTHERR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,167,16,10
+    CONTROL         "",IDC_USERW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,93,147,16,10
+    CONTROL         "",IDC_GROUPW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,93,158,16,10
+    CONTROL         "",IDC_OTHERW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,93,168,16,10
+    CONTROL         "",IDC_USERX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,135,147,16,10
+    CONTROL         "",IDC_GROUPX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,135,158,16,10
+    CONTROL         "",IDC_OTHERX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,135,168,16,10
+    GROUPBOX        "Flags",IDC_STATIC,7,190,221,48
+    CONTROL         "Disable Copy-on-Write",IDC_NODATACOW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,204,86,10
+    LTEXT           "(blank)",IDC_SUBVOL,78,21,99,8
+    LTEXT           "(blank)",IDC_INODE,78,35,100,8
+    LTEXT           "(blank)",IDC_TYPE,78,49,116,8
+    CONTROL         "Compress",IDC_COMPRESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,218,46,10
+    LTEXT           "Size on disk:",IDC_STATIC,14,63,61,8
+    CONTROL         "%s (<a>Details</a>)",IDC_SIZE_ON_DISK,"SysLink",WS_TABSTOP,78,63,142,8
+END
+
+IDD_SIZE_DETAILS DIALOGEX 0, 0, 212, 85
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Size details"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,81,64,50,14
+    LTEXT           "Inline:",IDC_STATIC,7,7,21,8
+    LTEXT           "Uncompressed:",IDC_STATIC,7,20,49,8
+    LTEXT           "ZLIB:",IDC_STATIC,7,33,18,8
+    LTEXT           "LZO:",IDC_STATIC,7,46,16,8
+    LTEXT           "(blank)",IDC_SIZE_INLINE,63,7,142,8
+    LTEXT           "(blank)",IDC_SIZE_UNCOMPRESSED,63,20,142,8
+    LTEXT           "(blank)",IDC_SIZE_ZLIB,63,33,142,8
+    LTEXT           "(blank)",IDC_SIZE_LZO,63,46,142,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO 
+BEGIN
+    IDD_PROP_SHEET, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 228
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 238
+    END
+
+    IDD_SIZE_DETAILS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 205
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 78
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RT_MANIFEST
+//
+
+2                       RT_MANIFEST             "shellbtrfs.manifest"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE 
+BEGIN
+    IDS_NEW_SUBVOL_HELP_TEXT "Creates a new Btrfs subvolume."
+    IDS_NEW_SUBVOL          "New subvolume"
+    IDS_NEW_SUBVOL_FILENAME "New subvolume"
+    IDS_CREATE_SNAPSHOT     "Create snapshot"
+    IDS_CREATE_SNAPSHOT_HELP_TEXT "Creates a snapshot of a Btrfs subvolume."
+    IDS_SNAPSHOT_FILENAME   "Snapshot of %s (%04u-%02u-%02u)"
+    IDS_PROP_SHEET_TITLE    "Btrfs properties"
+    IDS_INODE_FILE          "File"
+    IDS_INODE_DIR           "Directory"
+    IDS_INODE_CHAR          "Character device (major %llu, minor %u)"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_INODE_BLOCK         "Block device (major %llu, minor %u)"
+    IDS_INODE_FIFO          "FIFO"
+    IDS_INODE_SOCKET        "Socket"
+    IDS_INODE_SYMLINK       "Symbolic link"
+    IDS_INODE_UNKNOWN       "Unknown inode type %x"
+    IDS_SET_INODE_INFO_ERROR "FSCTL_BTRFS_SET_INODE_INFO returned %08x"
+    IDS_SIZE_BYTE           "%s byte"
+    IDS_SIZE_BYTES          "%s bytes"
+    IDS_SIZE_KB             "%1.1f KB"
+    IDS_SIZE_MB             "%1.1f MB"
+    IDS_SIZE_GB             "%1.1f GB"
+    IDS_SIZE_TB             "%1.1f TB"
+    IDS_SIZE_PB             "%1.1f PB"
+    IDS_SIZE_EB             "%1.1f EB"
+    IDS_VARIOUS             "(various)"
+    IDS_INODE_CHAR_SIMPLE   "Character device"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_INODE_BLOCK_SIMPLE  "Block device"
+    IDS_SIZE_LARGE          "%s (%s)"
+END
+
+#endif    // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/reactos/dll/shellext/shellbtrfs/shellbtrfs.spec b/reactos/dll/shellext/shellbtrfs/shellbtrfs.spec
new file mode 100644 (file)
index 0000000..b16365d
--- /dev/null
@@ -0,0 +1,4 @@
+@ stdcall -private DllCanUnloadNow()
+@ stdcall -private DllGetClassObject(ptr ptr ptr)
+@ stdcall -private DllRegisterServer()
+@ stdcall -private DllUnregisterServer()
diff --git a/reactos/dll/shellext/shellbtrfs/subvol.ico b/reactos/dll/shellext/shellbtrfs/subvol.ico
new file mode 100644 (file)
index 0000000..bae2bfc
Binary files /dev/null and b/reactos/dll/shellext/shellbtrfs/subvol.ico differ