diff --git a/reactos/dll/shellext/CMakeLists.txt b/reactos/dll/shellext/CMakeLists.txt
index 1049d5b2cef..f031c3fd7be 100644
--- a/reactos/dll/shellext/CMakeLists.txt
+++ b/reactos/dll/shellext/CMakeLists.txt
@@ -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
index 00000000000..454179b71d6
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/CMakeLists.txt
@@ -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
index 00000000000..f04228b53aa
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/contextmenu.cpp
@@ -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 . */
+
+#ifndef __REACTOS__
+#include
+#include
+#else
+#define WIN32_NO_STATUS
+#include
+#include
+#include
+#include
+#endif
+
+#define NO_SHLWAPI_STRFCNS
+#include
+
+#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(this);
+ AddRef();
+ return S_OK;
+ } else if (riid == IID_IShellExtInit) {
+ *ppObj = static_cast(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
index 00000000000..0ea33abb422
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/contextmenu.h
@@ -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 . */
+
+#include
+
+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
index 00000000000..dcde8334412
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/factory.cpp
@@ -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 . */
+
+#include
+#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(this);
+ } else {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+
+ reinterpret_cast(*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
index 00000000000..07790393a8c
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/factory.h
@@ -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 . */
+
+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
index 00000000000..1102c43cd64
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/iconoverlay.cpp
@@ -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 . */
+
+#ifndef __REACTOS__
+#include
+#include
+#else
+#define WIN32_NO_STATUS
+#include
+#include
+#include
+#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(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
index 00000000000..fa50abbb2f1
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/iconoverlay.h
@@ -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 . */
+
+#include
+
+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
index 00000000000..ddf04acedf5
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/main.cpp
@@ -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 . */
+
+#ifndef __REACTOS__
+#define _WIN32_WINNT 0x0600
+#endif
+
+#include
+#include
+#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
index 00000000000..9406b84941a
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/propsheet.cpp
@@ -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 . */
+
+#define ISOLATION_AWARE_ENABLED 1
+#define STRSAFE_NO_DEPRECATE
+
+#ifndef __REACTOS__
+#include
+#include
+#include
+#else
+#define WIN32_NO_STATUS
+#include
+#include
+#include
+#include
+#endif
+
+#define NO_SHLWAPI_STRFCNS
+#include
+#include
+
+#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(this);
+ AddRef();
+ return S_OK;
+ } else if (riid == IID_IShellExtInit) {
+ *ppObj = static_cast(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
index 00000000000..d4606928439
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/propsheet.h
@@ -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 . */
+
+#include
+#include
+#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 search_list;
+};
diff --git a/reactos/dll/shellext/shellbtrfs/resource.h b/reactos/dll/shellext/shellbtrfs/resource.h
new file mode 100644
index 00000000000..b778b22e296
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/resource.h
@@ -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
index 00000000000..970c8617396
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/shellbtrfs.manifest
@@ -0,0 +1,10 @@
+
+
+
+ WinBtrfs shell extension
+
+
+
+
+
+
diff --git a/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc b/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc
new file mode 100644
index 00000000000..0bfb971d625
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/shellbtrfs.rc
@@ -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 (Details)",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
index 00000000000..b16365d0c9f
--- /dev/null
+++ b/reactos/dll/shellext/shellbtrfs/shellbtrfs.spec
@@ -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
index 00000000000..bae2bfc3c32
Binary files /dev/null and b/reactos/dll/shellext/shellbtrfs/subvol.ico differ