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