windows-nt/Source/XPSP1/NT/shell/osshell/dskquota/ui/ownerdlg.cpp
2020-09-26 16:20:57 +08:00

1830 lines
53 KiB
C++

#include "pch.h"
#pragma hdrstop
#include "ownerdlg.h"
#include "ownerlst.h"
#include "resource.h"
#include "uihelp.h"
#include "uiutils.h"
//
// Private message used to indicate that the owner list thread
// has completed it's work.
//
const UINT PWM_OWNERLIST_COMPLETE = WM_USER + 1;
//
// Mask bits indicating what operations are allowed for a given
// file selection set in the listview.
//
const DWORD ACTION_NONE = 0x00000000;
const DWORD ACTION_TAKEOWNERSHIP = 0x00000001;
const DWORD ACTION_MOVE = 0x00000002;
const DWORD ACTION_DELETE = 0x00000004;
const DWORD ACTION_ANY = 0x00000007;
const static DWORD rgFileOwnerDialogHelpIDs[] =
{
IDC_CMB_OWNERDLG_OWNERS, IDH_CMB_OWNERDLG_OWNERS,
IDC_LV_OWNERDLG, IDH_LV_OWNERDLG,
IDC_BTN_OWNERDLG_DELETE, IDH_BTN_OWNERDLG_DELETE,
IDC_BTN_OWNERDLG_MOVETO, IDH_BTN_OWNERDLG_MOVETO,
IDC_BTN_OWNERDLG_TAKE, IDH_BTN_OWNERDLG_TAKE,
IDC_BTN_OWNERDLG_BROWSE, IDH_BTN_OWNERDLG_BROWSE,
IDC_EDIT_OWNERDLG_MOVETO, IDH_EDIT_OWNERDLG_MOVETO,
0,0
};
CFileOwnerDialog::CFileOwnerDialog(HINSTANCE hInstance,
HWND hwndParent,
LPCTSTR pszVolumeRoot,
const CArray<IDiskQuotaUser *>& rgpOwners
) : m_hInstance(hInstance),
m_hwndParent(hwndParent),
m_hwndDlg(NULL),
m_hwndLV(NULL),
m_hwndOwnerCombo(NULL),
m_hwndEditMoveTo(NULL),
m_iLastColSorted(-1),
m_bSortAscending(true),
m_bAbort(false),
m_hOwnerListThread(NULL),
m_rgpOwners(rgpOwners),
m_strVolumeRoot(pszVolumeRoot)
{
}
CFileOwnerDialog::~CFileOwnerDialog(
void
)
{
if (NULL != m_hOwnerListThread)
{
CloseHandle(m_hOwnerListThread);
}
}
INT_PTR
CFileOwnerDialog::Run(
void
)
{
DBGTRACE((DM_VIEW, DL_HIGH, TEXT("CFileOwnerDialog::Run")));
return DialogBoxParam(m_hInstance,
MAKEINTRESOURCE(IDD_OWNERSANDFILES),
m_hwndParent,
DlgProc,
(LPARAM)this);
}
INT_PTR CALLBACK
CFileOwnerDialog::DlgProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
//
// Retrieve the dialog object's ptr from the window's userdata.
// Place there in response to WM_INITDIALOG.
//
CFileOwnerDialog *pThis = (CFileOwnerDialog *)GetWindowLongPtr(hwnd, DWLP_USER);
try
{
switch(uMsg)
{
case WM_INITDIALOG:
//
// Store "this" ptr in window's userdata.
//
SetWindowLongPtr(hwnd, DWLP_USER, (INT_PTR)lParam);
pThis = (CFileOwnerDialog *)lParam;
//
// Save the HWND in our object. We'll need it later.
//
pThis->m_hwndDlg = hwnd;
return pThis->OnInitDialog(hwnd);
case WM_DESTROY:
return pThis->OnDestroy(hwnd);
case WM_COMMAND:
return pThis->OnCommand(hwnd, wParam, lParam);
case WM_NOTIFY:
return pThis->OnNotify(hwnd, wParam, lParam);
case WM_CONTEXTMENU:
return pThis->OnContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));
break;
case PWM_OWNERLIST_COMPLETE:
pThis->OnOwnerListComplete();
break;
case WM_SETCURSOR:
return pThis->OnSetCursor(hwnd);
break;
case WM_HELP:
WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, STR_DSKQUOUI_HELPFILE,
HELP_WM_HELP, (DWORD_PTR)(LPTSTR) rgFileOwnerDialogHelpIDs);
return TRUE;
}
}
catch(CAllocException& me)
{
//
// Announce any out-of-memory errors associated with running the dlg.
//
DiskQuotaMsgBox(GetDesktopWindow(),
IDS_OUTOFMEMORY,
IDS_TITLE_DISK_QUOTA,
MB_ICONERROR | MB_OK);
}
return FALSE;
}
INT_PTR
CFileOwnerDialog::OnInitDialog(
HWND hwnd
)
{
DBGTRACE((DM_VIEW, DL_HIGH, TEXT("CFileOwnerDialog::OnInitDialog")));
//
// Save HWNDs of controls we'll need later.
//
m_hwndLV = GetDlgItem(hwnd, IDC_LV_OWNERDLG);
m_hwndOwnerCombo = GetDlgItem(hwnd, IDC_CMB_OWNERDLG_OWNERS);
m_hwndEditMoveTo = GetDlgItem(hwnd, IDC_EDIT_OWNERDLG_MOVETO);
//
// We want these controls to be disabled until the owner list
// has been populated.
//
EnableWindow(m_hwndLV, FALSE);
EnableWindow(m_hwndOwnerCombo, FALSE);
EnableWindow(m_hwndEditMoveTo, FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_BTN_OWNERDLG_BROWSE), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_CBX_OWNERDLG_EXCLUDEDIRS), FALSE);
EnableWindow(GetDlgItem(hwnd, IDC_CBX_OWNERDLG_EXCLUDEFILES), FALSE);
//
// Build the list of owners and filenames on the volume.
// This can take a while depending on how many owners
// are in m_rgpOwners, the size of the volume and how many
// files each owner owns. First clear the owner list in case Run()
// is being called multiple times on the same dialog object.
// Note that we build this list on a background thread so that
// we don't block the UI while it's building.
//
m_OwnerList.Clear();
DWORD idThread;
m_hOwnerListThread = CreateThread(NULL,
0,
OwnerListThreadProc,
this,
0,
&idThread);
return TRUE;
}
INT_PTR
CFileOwnerDialog::OnSetCursor(
HWND hwnd
)
{
if (m_hOwnerListThread && (WAIT_TIMEOUT == WaitForSingleObject(m_hOwnerListThread, 0)))
{
SetCursor(LoadCursor(NULL, IDC_WAIT));
return TRUE;
}
return FALSE;
}
INT_PTR
CFileOwnerDialog::OnDestroy(
HWND hwnd
)
{
m_bAbort = true;
if (NULL != m_hOwnerListThread)
{
WaitForSingleObject(m_hOwnerListThread, INFINITE);
}
return TRUE;
}
DWORD
CFileOwnerDialog::OwnerListThreadProc( // [static]
LPVOID pvParam
)
{
CFileOwnerDialog *pThis = (CFileOwnerDialog *)pvParam;
pThis->BuildFileOwnerList(pThis->m_strVolumeRoot,
pThis->m_rgpOwners,
&(pThis->m_OwnerList));
PostMessage(pThis->m_hwndDlg, PWM_OWNERLIST_COMPLETE, 0, 0);
return 0;
}
//
// Called in response to PWM_OWNERLIST_COMPLETE which is posted
// to the dialog when the OwnerListThreadProc has completed
// it's processing.
//
void
CFileOwnerDialog::OnOwnerListComplete(
void
)
{
//
// Set the message in the top of the dialog.
//
CString s(m_hInstance, IDS_FMT_OWNERDLG_HEADER, m_OwnerList.OwnerCount());
SetWindowText(GetDlgItem(m_hwndDlg, IDC_TXT_OWNERDLG_HEADER), s);
//
// Populate the listview and owner combo.
//
InitializeList(m_OwnerList, m_hwndLV);
InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo);
//
// Now we can enable the controls we disabled in OnInitDialog().
//
EnableWindow(m_hwndLV, TRUE);
EnableWindow(m_hwndOwnerCombo, TRUE);
EnableWindow(m_hwndEditMoveTo, TRUE);
EnableWindow(GetDlgItem(m_hwndDlg, IDC_BTN_OWNERDLG_BROWSE), TRUE);
EnableWindow(GetDlgItem(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEDIRS), TRUE);
EnableWindow(GetDlgItem(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEFILES), TRUE);
}
INT_PTR
CFileOwnerDialog::OnCommand(
HWND hwnd,
WPARAM wParam,
LPARAM lParam
)
{
BOOL bResult = TRUE; // Assume not handled.
WORD wID = LOWORD(wParam);
WORD wNotifyCode = HIWORD(wParam);
HWND hCtl = (HWND)lParam;
switch(wID)
{
case IDCANCEL:
EndDialog(hwnd, 0);
bResult = FALSE;
break;
case IDC_CMB_OWNERDLG_OWNERS:
if (CBN_SELCHANGE == wNotifyCode)
{
int iOwner = ComboBox_GetCurSel(m_hwndOwnerCombo);
if (1 < m_OwnerList.OwnerCount())
{
//
// Owner list contains more than one owner. The combo
// contains a leading "All owners" entry.
//
iOwner--;
}
DBGASSERT((-1 <= iOwner));
CAutoSetRedraw autoredraw(m_hwndLV, false);
//
// Only show "owner" column if user has selected "all owners" combo item.
//
CreateListColumns(m_hwndLV, -1 == iOwner);
FillListView(m_OwnerList, m_hwndLV, iOwner);
}
bResult = FALSE;
break;
case IDC_BTN_OWNERDLG_BROWSE:
{
CString s;
if (BrowseForFolder(hwnd, &s))
SetWindowText(m_hwndEditMoveTo, s);
break;
}
case IDC_BTN_OWNERDLG_DELETE:
DeleteSelectedFiles(m_hwndLV);
bResult = FALSE;
break;
case IDC_BTN_OWNERDLG_MOVETO:
{
CPath strDest;
CPath strRoot;
int cchEdit = Edit_GetTextLength(m_hwndEditMoveTo);
Edit_GetText(m_hwndEditMoveTo,
strDest.GetBuffer(cchEdit + 1),
cchEdit + 1);
strDest.ReleaseBuffer();
strDest.Trim();
strDest.GetRoot(&strRoot);
if (IsSameVolume(strRoot, m_strVolumeRoot))
{
//
// Don't let operator move files to a folder
// on the same volume.
//
DiskQuotaMsgBox(m_hwndDlg,
IDS_ERROR_MOVETO_SAMEVOL,
IDS_TITLE_DISK_QUOTA,
MB_ICONINFORMATION | MB_OK);
SetWindowText(m_hwndEditMoveTo, strDest);
SetFocus(m_hwndEditMoveTo);
}
else
{
//
// Eveything looks OK. Try to move the files.
//
MoveSelectedFiles(m_hwndLV, strDest);
}
bResult = FALSE;
break;
}
case IDC_BTN_OWNERDLG_TAKE:
{
HRESULT hr = TakeOwnershipOfSelectedFiles(m_hwndLV);
if (FAILED(hr))
{
DBGERROR((TEXT("TakeOwnershipOfSelectedFiles failed with hr = 0x%08X"), hr));
}
break;
}
case IDC_EDIT_OWNERDLG_MOVETO:
if (EN_UPDATE == wNotifyCode)
{
//
// Disable the "Move" button if the destination edit field
// is blank.
//
HWND hwnd = GetDlgItem(m_hwndDlg, IDC_BTN_OWNERDLG_MOVETO);
bool bEnable = ShouldEnableControl(IDC_BTN_OWNERDLG_MOVETO);
if (bEnable != boolify(IsWindowEnabled(hwnd)))
{
EnableWindow(hwnd, bEnable);
}
}
break;
case IDC_CBX_OWNERDLG_EXCLUDEFILES:
case IDC_CBX_OWNERDLG_EXCLUDEDIRS:
if (BN_CLICKED == wNotifyCode)
{
//
// The allowable states for these two checkboxes are:
//
// Excl Files Excl Dirs
// --------------- ----------------
// Checked Unchecked
// Unchecked Checked
// Unchecked Unchecked
//
// It makes no sense to have both checkboxes checked.
// This would cause the list to be empty and might
// generate user confusion.
//
if (IsDlgButtonChecked(m_hwndDlg, wID))
{
UINT idOther = IDC_CBX_OWNERDLG_EXCLUDEFILES;
if (IDC_CBX_OWNERDLG_EXCLUDEFILES == wID)
{
idOther = IDC_CBX_OWNERDLG_EXCLUDEDIRS;
}
CheckDlgButton(m_hwndDlg, idOther, BST_UNCHECKED);
}
FillListView(m_OwnerList, m_hwndLV, ComboBox_GetCurSel(m_hwndOwnerCombo) - 1);
}
break;
}
return bResult;
}
INT_PTR
CFileOwnerDialog::OnContextMenu(
HWND hwndItem,
int xPos,
int yPos
)
{
int idCtl = GetDlgCtrlID(hwndItem);
WinHelp(hwndItem,
UseWindowsHelp(idCtl) ? NULL : STR_DSKQUOUI_HELPFILE,
HELP_CONTEXTMENU,
(DWORD_PTR)((LPTSTR)rgFileOwnerDialogHelpIDs));
return FALSE;
}
//
// Determine what actions are allowed for the current selection.
//
DWORD
CFileOwnerDialog::GetAllowedActions(
HWND hwndLV
)
{
CArray<COwnerListItemHandle> rgItemHandles;
BuildListOfSelectedFiles(hwndLV, NULL, &rgItemHandles);
if (0 != rgItemHandles.Count())
{
const int cHandles = rgItemHandles.Count();
for(int i = 0; i < cHandles; i++)
{
COwnerListItemHandle handle = rgItemHandles[i];
int iOwner = handle.OwnerIndex();
int iFile = handle.FileIndex();
if (m_OwnerList.IsFileDirectory(iOwner, iFile))
{
//
// If any directory exists in the selection,
// "take ownership" is the only allowed action.
//
return ACTION_TAKEOWNERSHIP;
}
}
}
return ACTION_ANY;
}
//
// Determine if one of the move/delete/take buttons should be enabled
// or disabled.
//
bool
CFileOwnerDialog::ShouldEnableControl(
UINT idCtl
)
{
bool bEnable = true;
int cLVSel = ListView_GetSelectedCount(m_hwndLV);
DWORD actions = GetAllowedActions(m_hwndLV);
switch(idCtl)
{
case IDC_BTN_OWNERDLG_DELETE:
bEnable = (0 != (ACTION_DELETE & actions)) && (0 < cLVSel);
break;
case IDC_BTN_OWNERDLG_TAKE:
bEnable = (0 != (ACTION_TAKEOWNERSHIP & actions)) && (0 < cLVSel);
break;
case IDC_BTN_OWNERDLG_MOVETO:
bEnable = (0 != (ACTION_MOVE & actions));
if (bEnable && 0 < cLVSel)
{
CPath s;
int cch = Edit_GetTextLength(m_hwndEditMoveTo);
Edit_GetText(m_hwndEditMoveTo, s.GetBuffer(cch + 1), cch + 1);
s.ReleaseBuffer();
s.Trim();
bEnable = 0 < s.Length();
}
break;
case IDC_BTN_OWNERDLG_BROWSE:
case IDC_EDIT_OWNERDLG_MOVETO:
bEnable = (0 != (ACTION_MOVE & actions));
break;
default:
break;
}
return bEnable;
}
INT_PTR
CFileOwnerDialog::OnNotify(
HWND hwnd,
WPARAM wParam,
LPARAM lParam
)
{
BOOL bResult = TRUE;
LPNMHDR pnm = (LPNMHDR)lParam;
switch(pnm->code)
{
case LVN_GETDISPINFO:
OnLVN_GetDispInfo((LV_DISPINFO *)lParam);
break;
case LVN_COLUMNCLICK:
OnLVN_ColumnClick((NM_LISTVIEW *)lParam);
break;
case LVN_ITEMCHANGED:
OnLVN_ItemChanged((NM_LISTVIEW *)lParam);
break;
case LVN_KEYDOWN:
OnLVN_KeyDown((NMLVKEYDOWN *)lParam);
break;
default:
break;
}
return bResult;
}
void
CFileOwnerDialog::OnLVN_GetDispInfo(
LV_DISPINFO *plvdi
)
{
static CPath strPath;
static CString strOwner;
COwnerListItemHandle hItem(plvdi->item.lParam);
int iOwner = hItem.OwnerIndex();
int iFile = hItem.FileIndex();
if (LVIF_TEXT & plvdi->item.mask)
{
switch(plvdi->item.iSubItem)
{
case iLVSUBITEM_FILE:
{
CPath s;
m_OwnerList.GetFileName(iOwner, iFile, &s);
if (m_OwnerList.IsFileDirectory(iOwner, iFile))
{
strPath.Format(m_hInstance, IDS_FMT_OWNERDLG_FOLDERNAME, s.Cstr());
}
else
{
strPath = s;
}
plvdi->item.pszText = (LPTSTR)strPath.Cstr();
}
break;
case iLVSUBITEM_FOLDER:
m_OwnerList.GetFolderName(iOwner, iFile, &strPath);
plvdi->item.pszText = (LPTSTR)strPath.Cstr();
break;
case iLVSUBITEM_OWNER:
m_OwnerList.GetOwnerName(iOwner, &strOwner);
plvdi->item.pszText = (LPTSTR)strOwner.Cstr();
break;
}
}
if (LVIF_IMAGE & plvdi->item.mask)
{
//
// Not displaying any images. This is just a placeholder.
// Should be optimized out by compiler.
//
}
}
int CALLBACK
CFileOwnerDialog::CompareLVItems(
LPARAM lParam1,
LPARAM lParam2,
LPARAM lParamSort
)
{
CFileOwnerDialog *pdlg = reinterpret_cast<CFileOwnerDialog *>(lParamSort);
int diff = 0;
try
{
COwnerListItemHandle h1(lParam1);
COwnerListItemHandle h2(lParam2);
int iOwner1 = h1.OwnerIndex();
int iOwner2 = h2.OwnerIndex();
int iFile1 = h1.FileIndex();
int iFile2 = h2.FileIndex();
static CPath s1, s2;
//
// This array controls the comparison column IDs used when
// values for the selected column are equal. These should
// remain in order of the iLVSUBITEM_xxxxx enumeration with
// respect to the first element in each row.
//
static const int rgColComp[3][3] = {
{ iLVSUBITEM_FILE, iLVSUBITEM_FOLDER, iLVSUBITEM_OWNER },
{ iLVSUBITEM_FOLDER, iLVSUBITEM_FILE, iLVSUBITEM_OWNER },
{ iLVSUBITEM_OWNER, iLVSUBITEM_FILE, iLVSUBITEM_FOLDER }
};
int iCompare = 0;
while(0 == diff && iCompare < ARRAYSIZE(rgColComp))
{
switch(rgColComp[pdlg->m_iLastColSorted][iCompare++])
{
case iLVSUBITEM_FILE:
pdlg->m_OwnerList.GetFileName(iOwner1, iFile1, &s1);
pdlg->m_OwnerList.GetFileName(iOwner2, iFile2, &s2);
break;
case iLVSUBITEM_FOLDER:
pdlg->m_OwnerList.GetFolderName(iOwner1, iFile1, &s1);
pdlg->m_OwnerList.GetFolderName(iOwner2, iFile2, &s2);
break;
case iLVSUBITEM_OWNER:
//
// Can use CPath (s1 and s2) in place of CString arg since
// CPath is derived from CString.
//
pdlg->m_OwnerList.GetOwnerName(iOwner1, &s1);
pdlg->m_OwnerList.GetOwnerName(iOwner2, &s2);
break;
default:
//
// If you hit this, you need to update this function
// to handle the new column you've added to the listview.
//
DBGASSERT((false));
break;
}
diff = s1.Compare(s2);
}
//
// Don't need contents of static strings between function invocations.
// The strings are static to avoid repeated construction/destruction.
// It's only a minor optimization.
//
s1.Empty();
s2.Empty();
}
catch(CAllocException& e)
{
//
// Do nothing. Just return diff "as is".
// Don't want to throw an exception back into comctl32.
//
}
return pdlg->m_bSortAscending ? diff : -1 * diff;
}
void
CFileOwnerDialog::OnLVN_ColumnClick(
NM_LISTVIEW *pnmlv
)
{
DBGTRACE((DM_VIEW, DL_LOW, TEXT("CFileOwnerDialog::OnLVN_ColumnClick")));
if (m_iLastColSorted != pnmlv->iSubItem)
{
m_bSortAscending = true;
m_iLastColSorted = pnmlv->iSubItem;
}
else
{
m_bSortAscending = !m_bSortAscending;
}
ListView_SortItems(m_hwndLV, CompareLVItems, LPARAM(this));
}
//
// Called whenever a listview item has changed state.
// I'm using this to update the "enabledness" of the
// dialog buttons. If there's nothing selected in the listview,
// the move/delete/take buttons are disabled.
//
void
CFileOwnerDialog::OnLVN_ItemChanged(
NM_LISTVIEW *pnmlv
)
{
static const int rgCtls[] = { IDC_BTN_OWNERDLG_DELETE,
IDC_BTN_OWNERDLG_TAKE,
IDC_BTN_OWNERDLG_MOVETO,
IDC_BTN_OWNERDLG_BROWSE,
IDC_EDIT_OWNERDLG_MOVETO};
//
// LVN_ITEMCHANGED is sent multiple times when you move the
// highlight bar in a listview.
// Only run this code when the "focused" state bit is set
// for the "new state". This should be the last call in
// the series.
//
if (LVIS_FOCUSED & pnmlv->uNewState)
{
for (int i = 0; i < ARRAYSIZE(rgCtls); i++)
{
HWND hwnd = GetDlgItem(m_hwndDlg, rgCtls[i]);
bool bEnable = ShouldEnableControl(rgCtls[i]);
if (bEnable != boolify(IsWindowEnabled(hwnd)))
{
EnableWindow(hwnd, bEnable);
}
}
}
}
void
CFileOwnerDialog::OnLVN_KeyDown(
NMLVKEYDOWN *plvkd
)
{
if (VK_DELETE == plvkd->wVKey)
{
DeleteSelectedFiles(m_hwndLV);
FocusOnSomethingInListview(m_hwndLV);
}
}
void
CFileOwnerDialog::FocusOnSomethingInListview(
HWND hwndLV
)
{
//
// Focus on something.
//
int iFocus = ListView_GetNextItem(hwndLV, -1, LVNI_FOCUSED);
if (-1 == iFocus)
iFocus = 0;
ListView_SetItemState(hwndLV, iFocus, LVIS_FOCUSED | LVIS_SELECTED,
LVIS_FOCUSED | LVIS_SELECTED);
}
//
// Creates the listview columns and populates the listview
// with filenames.
//
void
CFileOwnerDialog::InitializeList(
const COwnerList& fol, // file & owner list
HWND hwndList
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::InitializeList")));
CreateListColumns(hwndList, 1 < m_OwnerList.OwnerCount());
FillListView(fol, hwndList);
ListView_SetExtendedListViewStyle(hwndList, LVS_EX_FULLROWSELECT);
}
void
CFileOwnerDialog::CreateListColumns(
HWND hwndList,
bool bShowOwner // Default is true.
)
{
//
// Clear out the listview and header.
//
ListView_DeleteAllItems(hwndList);
HWND hwndHeader = ListView_GetHeader(hwndList);
if (NULL != hwndHeader)
{
while(0 < Header_GetItemCount(hwndHeader))
ListView_DeleteColumn(hwndList, 0);
}
//
// Create the header titles.
//
CString strFile(m_hInstance, IDS_OWNERDLG_HDR_FILE);
CString strFolder(m_hInstance, IDS_OWNERDLG_HDR_FOLDER);
CString strOwner(m_hInstance, IDS_OWNERDLG_HDR_OWNER);
//
// FEATURE: Should probably allow for vertical scroll bar also.
//
RECT rcList;
GetClientRect(hwndList, &rcList);
int cxCol = (rcList.right - rcList.left) / (bShowOwner ? 3 : 2);
#define LVCOLMASK (LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM)
LV_COLUMN rgCols[] = {
{ LVCOLMASK, LVCFMT_LEFT, cxCol, strFile, 0, iLVSUBITEM_FILE },
{ LVCOLMASK, LVCFMT_LEFT, cxCol, strFolder, 0, iLVSUBITEM_FOLDER },
{ LVCOLMASK, LVCFMT_LEFT, cxCol, strOwner, 0, iLVSUBITEM_OWNER }
};
//
// Add the columns to the listview.
//
int cCols = bShowOwner ? ARRAYSIZE(rgCols) : ARRAYSIZE(rgCols) - 1;
for (INT i = 0; i < cCols; i++)
{
if (-1 == ListView_InsertColumn(hwndList, i, &rgCols[i]))
{
DBGERROR((TEXT("CFileOwnerDialog::CreateListColumns failed to add column %d"), i));
}
}
}
void
CFileOwnerDialog::FillListView(
const COwnerList& fol, // file & owner list
HWND hwndList,
int iOwner // default is -1 (all owners)
)
{
ListView_DeleteAllItems(hwndList);
LV_ITEM item;
item.iSubItem = 0;
item.mask = LVIF_TEXT | LVIF_STATE | LVIF_IMAGE | LVIF_PARAM;
item.state = 0;
item.stateMask = 0;
item.pszText = LPSTR_TEXTCALLBACK;
item.iImage = I_IMAGECALLBACK;
int iFirst = iOwner;
int iLast = iOwner;
if (-1 == iOwner)
{
iFirst = 0;
iLast = fol.OwnerCount() - 1;
}
int iItem = 0;
const bool bExclFiles = IsDlgButtonChecked(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEFILES);
const bool bExclDirs = IsDlgButtonChecked(m_hwndDlg, IDC_CBX_OWNERDLG_EXCLUDEDIRS);
//
// WARNING: Reusing formal arg iOwner. It's safe to do, but you
// should be aware that I'm doing it.
//
for (iOwner = iFirst; iOwner <= iLast; iOwner++)
{
int cFiles = fol.FileCount(iOwner, true);
for (int iFile = 0; iFile < cFiles; iFile++)
{
bool bDirectory = fol.IsFileDirectory(iOwner, iFile);
bool bFile = !bDirectory;
if ((bDirectory && !bExclDirs) || (bFile && !bExclFiles))
{
if (!fol.IsFileDeleted(iOwner, iFile))
{
item.lParam = COwnerListItemHandle(iOwner, iFile);
item.iItem = iItem++;
if (-1 == ListView_InsertItem(hwndList, &item))
DBGERROR((TEXT("Error adding LV item for owner %d, file %d"), iOwner, iFile));
}
}
}
}
}
void
CFileOwnerDialog::InitializeOwnerCombo(
const COwnerList& fol, // file & owner list
HWND hwndCombo
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::InitializeList")));
int iSelected = ComboBox_GetCurSel(hwndCombo);
ComboBox_ResetContent(hwndCombo);
CString s, s2;
int cOwners = fol.OwnerCount();
if (1 < cOwners)
{
//
// Add "all owners" entry.
//
s.Format(m_hInstance, IDS_FMT_ALLOWNERS, fol.FileCount());
ComboBox_InsertString(hwndCombo, -1, s);
}
for (int iOwner = 0; iOwner < cOwners; iOwner++)
{
fol.GetOwnerName(iOwner, &s2);
s.Format(m_hInstance, IDS_FMT_OWNER, s2.Cstr(), fol.FileCount(iOwner));
ComboBox_InsertString(hwndCombo, -1, s);
}
ComboBox_SetCurSel(hwndCombo, CB_ERR != iSelected ? iSelected : 0);
//
// Set the max height of the owner combo
//
RECT rcCombo;
GetClientRect(m_hwndOwnerCombo, &rcCombo);
SetWindowPos(m_hwndOwnerCombo,
NULL,
0, 0,
rcCombo.right - rcCombo.left,
200,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
}
//
// Determine if two volume root strings refer to the same volume.
// With volume mount points, "C:\" and "D:\DriveC" could refer to the
// same physical volume. To differentiate we need to examine the unique
// volume name GUID strings.
//
bool
CFileOwnerDialog::IsSameVolume(
LPCTSTR pszRoot1,
LPCTSTR pszRoot2
)
{
TCHAR szVolGUID1[MAX_PATH];
TCHAR szTemp[MAX_PATH];
bool bSameVolume = false;
//
// GetVolumeNameForVolumeMountPoint requires trailing backslash on paths.
//
lstrcpyn(szTemp, pszRoot1, ARRAYSIZE(szTemp));
PathAddBackslash(szTemp);
if (GetVolumeNameForVolumeMountPoint(szTemp, szVolGUID1, ARRAYSIZE(szVolGUID1)))
{
TCHAR szVolGUID2[MAX_PATH];
lstrcpyn(szTemp, pszRoot2, ARRAYSIZE(szTemp));
PathAddBackslash(szTemp);
if (GetVolumeNameForVolumeMountPoint(szTemp, szVolGUID2, ARRAYSIZE(szVolGUID2)))
{
if (0 == lstrcmpi(szVolGUID1, szVolGUID2))
bSameVolume = true;
}
}
return bSameVolume;
}
//
// Let the user browse for a folder.
// The selected folder path is returned in *pstrFolder.
//
bool
CFileOwnerDialog::BrowseForFolder(
HWND hwndParent,
CString *pstrFolder
)
{
bool bResult = false;
BROWSEINFO bi;
ZeroMemory(&bi, sizeof(bi));
CString strTitle(m_hInstance, IDS_BROWSEFORFOLDER);
bi.hwndOwner = hwndParent;
bi.pidlRoot = NULL; // Start at desktop.
bi.pszDisplayName = NULL;
bi.lpszTitle = strTitle.Cstr();
//
// FEATURE: Setting the BIF_EDITBOX flag causes SHBrowseForFolder to invoke
// autocomplete through SHAutoComplete (in shlwapi). SHAutoComplete
// loads browseui.dll to implement the autocomplete feature. The bad
// part is that SHAutoComplete also unloads browseui.dll before it
// returns, resulting in calls to the unloaded WndProc. I've notified
// ReinerF about this. Turning off the BIF_EDITBOX bit prevents
// autocomplete from being used and thus prevents the problem.
// I want the edit box. Turn it back on once they fix this bug.
//
// brianau [1/30/97]
//
bi.ulFlags = BIF_RETURNONLYFSDIRS; // | BIF_EDITBOX;
bi.lpfn = BrowseForFolderCallback;
bi.lParam = (LPARAM)pstrFolder;
bi.iImage = 0;
bResult = boolify(SHBrowseForFolder(&bi));
return bResult;
}
//
// Callback called by SHBrowseForFolder. Writes selected folder path
// to CString object who's pointer is passed in lpData arg.
//
int
CFileOwnerDialog::BrowseForFolderCallback(
HWND hwnd,
UINT uMsg,
LPARAM lParam,
LPARAM lpData
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BrowseForFolderCallback")));
CString *pstrFolder = (CString *)lpData;
if (BFFM_SELCHANGED == uMsg)
{
SHGetPathFromIDList((LPCITEMIDLIST)lParam, pstrFolder->GetBuffer(MAX_PATH));
pstrFolder->ReleaseBuffer();
}
return 0;
}
//
// Builds a double-nul terminated list of file paths from the listview
// along with an array of "item handle" objects that acts as a cross-
// reference between the list items, items in the listview and items
// in the file owner list. Each handle contains an owner index and
// file index into the file owner list. Each handle is also the value
// stored as the lParam in the listview items.
// Both pList and prgItemHandles arguments are optional. Although,
// calling with neither non-null is sort of useless.
//
void
CFileOwnerDialog::BuildListOfSelectedFiles(
HWND hwndLV,
DblNulTermList *pList,
CArray<COwnerListItemHandle> *prgItemHandles
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BuildListOfSelectedFiles")));
int iItem = -1;
CPath strPath;
LV_ITEM item;
if (NULL != prgItemHandles)
prgItemHandles->Clear();
while(-1 != (iItem = ListView_GetNextItem(hwndLV, iItem, LVNI_SELECTED)))
{
item.iSubItem = 0;
item.iItem = iItem;
item.mask = LVIF_PARAM;
if (-1 != ListView_GetItem(hwndLV, &item))
{
COwnerListItemHandle hItem(item.lParam);
m_OwnerList.GetFileFullPath(hItem.OwnerIndex(),
hItem.FileIndex(),
&strPath);
if (pList)
pList->AddString(strPath);
if (prgItemHandles)
prgItemHandles->Append(hItem);
}
}
}
//
// Given an item "handle", find it's entry in the listview.
//
int
CFileOwnerDialog::FindItemFromHandle(
HWND hwndLV,
const COwnerListItemHandle& handle
)
{
LV_FINDINFO lvfi;
lvfi.flags = LVFI_PARAM;
lvfi.lParam = handle;
return ListView_FindItem(hwndLV, -1, &lvfi);
}
//
// Scans an array of item handles and removes all corresponding
// items from the listview.
//
void
CFileOwnerDialog::RemoveListViewItems(
HWND hwndLV,
const CArray<COwnerListItemHandle>& rgItemHandles
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::RemoveListViewItems")));
LV_ITEM item;
CPath strPath;
CAutoSetRedraw autoredraw(hwndLV, false);
int cHandles = rgItemHandles.Count();
for (int iHandle = 0; iHandle < cHandles; iHandle++)
{
COwnerListItemHandle handle = rgItemHandles[iHandle];
int iItem = FindItemFromHandle(hwndLV, handle);
if (-1 != iItem)
{
int iOwner = handle.OwnerIndex();
int iFile = handle.FileIndex();
m_OwnerList.GetFileFullPath(iOwner, iFile, &strPath);
if ((DWORD)-1 == GetFileAttributes(strPath))
{
//
// File doesn't exist any more.
// Delete from the listview.
// Mark it as "deleted" in the ownerlist container.
//
ListView_DeleteItem(hwndLV, iItem);
m_OwnerList.MarkFileDeleted(iOwner, iFile);
DBGPRINT((DM_VIEW, DL_LOW, TEXT("Removed item %d \"%s\""),
iItem, strPath.Cstr()));
}
}
}
//
// Refresh the owner combo to update the file counts.
//
InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo);
}
//
// Delete the files selected in the listview.
// Files deleted are removed from the listview.
//
void
CFileOwnerDialog::DeleteSelectedFiles(
HWND hwndLV
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::DeleteSelectedFiles")));
DblNulTermList list(1024); // 1024 is the buffer growth size in chars.
CArray<COwnerListItemHandle> rgItemHandles;
BuildListOfSelectedFiles(hwndLV, &list, &rgItemHandles);
if (0 < list.Count())
{
SHFILEOPSTRUCT fo;
fo.hwnd = m_hwndDlg;
fo.wFunc = FO_DELETE;
fo.pFrom = list;
fo.pTo = NULL;
fo.fFlags = 0;
if (0 != SHFileOperation(&fo))
{
DBGERROR((TEXT("SHFileOperation [FO_DELETE] failed")));
}
//
// Remove listview items if their files were really deleted.
//
RemoveListViewItems(hwndLV, rgItemHandles);
}
}
//
// Move the selected files to a new location.
// Moved files are removed from the listview.
//
void
CFileOwnerDialog::MoveSelectedFiles(
HWND hwndLV,
LPCTSTR pszDest
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::DeleteSelectedFiles")));
DblNulTermList list(1024); // 1024 is the buffer growth size in chars.
CArray<COwnerListItemHandle> rgItemHandles;
BuildListOfSelectedFiles(hwndLV, &list, &rgItemHandles);
if (0 < list.Count())
{
CPath strDest(pszDest);
if (1 == list.Count())
{
//
// If we have only a single file we MUST create a fully-qualified
// path to the destination file. Oddities in the shell's move/copy
// engine won't let us pass merely a destination folder in the
// case where that folder doesn't exist. If we give the full path
// including filename we'll get the "folder doesn't exist, create
// now?" messagebox as we would expect. If we're moving multiple
// files the shell accepts a single directory path.
//
LPCTSTR psz;
DblNulTermListIter iter(list);
if (iter.Next(&psz))
{
CPath strSrc(psz); // Copy the source
CPath strFile;
strSrc.GetFileSpec(&strFile);// Extract the filename.
strDest.Append(strFile); // Append to the dest path.
}
}
SHFILEOPSTRUCT fo;
fo.hwnd = m_hwndDlg;
fo.wFunc = FO_MOVE;
fo.pFrom = list;
fo.pTo = strDest;
fo.fFlags = FOF_RENAMEONCOLLISION;
if (0 != SHFileOperation(&fo))
{
DBGERROR((TEXT("SHFileOperation [FO_MOVE] failed")));
}
//
// Remove listview items if their file was really deleted.
//
RemoveListViewItems(hwndLV, rgItemHandles);
}
}
//
// Get the SID to use for taking ownership of files.
// First try to get the first group SID with the SE_GROUP_OWNER attribute.
// If none found, use the operator's account SID. The SID is in a
// dynamic buffer attached to the ptrSid autoptr argument.
//
HRESULT
CFileOwnerDialog::GetOwnershipSid(
array_autoptr<BYTE> *ptrSid
)
{
HRESULT hr = E_FAIL;
DWORD dwErr = 0;
//
// Get the token handle. First try the thread token then the process
// token. If these fail we return early. No sense in continuing
// on if we can't get a user token.
//
CWin32Handle hToken;
if (!OpenThreadToken(GetCurrentThread(),
TOKEN_READ,
TRUE,
hToken.HandlePtr()))
{
if (ERROR_NO_TOKEN == GetLastError())
{
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_READ,
hToken.HandlePtr()))
{
dwErr = GetLastError();
DBGERROR((TEXT("Error %d opening process token"), dwErr));
return HRESULT_FROM_WIN32(dwErr);
}
}
else
{
dwErr = GetLastError();
DBGERROR((TEXT("Error %d opening thread token"), dwErr));
return HRESULT_FROM_WIN32(dwErr);
}
}
//
// Get the required size of the group token information buffer.
//
array_autoptr<BYTE> ptrTokenInfo;
DWORD cbTokenInfo = 0;
if (!GetTokenInformation(hToken,
TokenGroups,
NULL,
cbTokenInfo,
&cbTokenInfo))
{
dwErr = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == dwErr)
{
ptrTokenInfo = new BYTE[cbTokenInfo];
}
else
{
dwErr = GetLastError();
DBGERROR((TEXT("Error %d getting TokenGroups info [for size]"), dwErr));
hr = HRESULT_FROM_WIN32(dwErr);
}
}
//
// Get the group token information.
//
if (NULL != ptrTokenInfo.get())
{
if (!GetTokenInformation(hToken,
TokenGroups,
ptrTokenInfo.get(),
cbTokenInfo,
&cbTokenInfo))
{
dwErr = GetLastError();
DBGERROR((TEXT("Error %d getting TokenGroups info"), dwErr));
hr = HRESULT_FROM_WIN32(dwErr);
}
else
{
//
// Extract the first SID with the GROUP_OWNER bit set.
//
TOKEN_GROUPS *ptg = (TOKEN_GROUPS *)ptrTokenInfo.get();
DBGASSERT((NULL != ptg));
for (DWORD i = 0; i < ptg->GroupCount; i++)
{
SID_AND_ATTRIBUTES *psa = (SID_AND_ATTRIBUTES *)&ptg->Groups[i];
DBGASSERT((NULL != psa));
if (SE_GROUP_OWNER & psa->Attributes)
{
int cbSid = GetLengthSid(psa->Sid);
*ptrSid = new BYTE[cbSid];
CopySid(cbSid, ptrSid->get(), psa->Sid);
hr = NOERROR;
break;
}
}
}
}
if (FAILED(hr))
{
//
// Didn't find a SID from the group information.
// Use the operator's SID.
//
cbTokenInfo = 0;
if (!GetTokenInformation(hToken,
TokenUser,
NULL,
cbTokenInfo,
&cbTokenInfo))
{
dwErr = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == dwErr)
{
ptrTokenInfo = new BYTE[cbTokenInfo];
}
else
{
DBGERROR((TEXT("Error %d getting TokenUser info [for size]"), dwErr));
hr = HRESULT_FROM_WIN32(dwErr);
}
}
if (SUCCEEDED(hr))
{
//
// Get the user token information.
//
if (!GetTokenInformation(hToken,
TokenUser,
ptrTokenInfo.get(),
cbTokenInfo,
&cbTokenInfo))
{
dwErr = GetLastError();
DBGERROR((TEXT("Error %d getting TokenUser info"), dwErr));
hr = HRESULT_FROM_WIN32(dwErr);
}
else
{
SID_AND_ATTRIBUTES *psa = (SID_AND_ATTRIBUTES *)ptrTokenInfo.get();
DBGASSERT((NULL != psa));
int cbSid = GetLengthSid(psa->Sid);
*ptrSid = new BYTE[cbSid];
CopySid(cbSid, ptrSid->get(), psa->Sid);
hr = NOERROR;
}
}
}
if (SUCCEEDED(hr) && NULL != ptrSid->get() && !IsValidSid(ptrSid->get()))
{
hr = HRESULT_FROM_WIN32(ERROR_INVALID_SID);
}
return hr;
}
//
// Transfers ownership of selected files in the listview to the
// currently logged-on user.
//
HRESULT
CFileOwnerDialog::TakeOwnershipOfSelectedFiles(
HWND hwndLV
)
{
HRESULT hr = NOERROR;
DWORD dwErr = 0;
CArray<COwnerListItemHandle> rgItemHandles;
BuildListOfSelectedFiles(hwndLV, NULL, &rgItemHandles);
if (0 == rgItemHandles.Count())
return S_OK;
array_autoptr<BYTE> ptrSid;
hr = GetOwnershipSid(&ptrSid);
if (FAILED(hr))
return hr;
CPath strFile;
int cHandles = rgItemHandles.Count();
for (int i = 0; i < cHandles; i++)
{
COwnerListItemHandle handle = rgItemHandles[i];
int iItem = FindItemFromHandle(hwndLV, handle);
if (-1 != iItem)
{
SECURITY_DESCRIPTOR sd;
if (InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
{
int iOwner = handle.OwnerIndex();
int iFile = handle.FileIndex();
m_OwnerList.GetFileFullPath(iOwner, iFile, &strFile);
if (SetSecurityDescriptorOwner(&sd, ptrSid.get(), FALSE))
{
if (SetFileSecurity(strFile, OWNER_SECURITY_INFORMATION, &sd))
{
ListView_DeleteItem(hwndLV, iItem);
m_OwnerList.MarkFileDeleted(iOwner, iFile);
}
else
{
dwErr = GetLastError();
DBGERROR((TEXT("Error %d setting new owner for \"%s\""),
dwErr, strFile.Cstr()));
hr = HRESULT_FROM_WIN32(dwErr);
}
}
else
{
dwErr = GetLastError();
DBGERROR((TEXT("Error %d setting security descriptor owner"), dwErr));
hr = HRESULT_FROM_WIN32(dwErr);
}
}
else
{
dwErr = GetLastError();
DBGERROR((TEXT("Error %d initing security descriptor"), GetLastError()));
hr = HRESULT_FROM_WIN32(dwErr);
}
}
else
{
DBGERROR((TEXT("Can't find listview item for owner %d, file %d"),
handle.OwnerIndex(), handle.FileIndex()));
}
}
//
// Refresh the owner combo with the new file counts.
//
InitializeOwnerCombo(m_OwnerList, m_hwndOwnerCombo);
return hr;
}
//
// The original code for listing files owned by a user was
// contributed by MarkZ. I made some minor modifications
// to fit it into the diskquota project and make it more
// exception safe.
//
inline VOID *
Add2Ptr(VOID *pv, ULONG cb)
{
return((BYTE *) pv + cb);
}
inline ULONG
QuadAlign( ULONG Value )
{
return (Value + 7) & ~7;
}
//
// Add files owned by a particular user on a particular volume.
//
HRESULT
CFileOwnerDialog::AddFilesToOwnerList(
LPCTSTR pszVolumeRoot,
HANDLE hVolumeRoot,
IDiskQuotaUser *pOwner,
COwnerList *pOwnerList
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::AddFilesToOwnerList")));
DBGASSERT((NULL != hVolumeRoot));
DBGASSERT((NULL != pOwner));
DBGASSERT((NULL != pOwnerList));
struct
{
ULONG Restart;
BYTE Sid[MAX_SID_LEN];
}FsCtlInput;
NTSTATUS status = ERROR_SUCCESS;
if (m_bAbort)
{
return S_OK;
}
//
// Get owner's SID.
//
HRESULT hr = pOwner->GetSid(FsCtlInput.Sid, sizeof(FsCtlInput.Sid));
if (FAILED(hr))
{
DBGERROR((TEXT("IDiskQuotaUser::GetSid failed with hr = 0x%08X"), hr));
return hr;
}
//
// Add the owner to the owner-file list.
//
int iOwner = pOwnerList->AddOwner(pOwner);
IO_STATUS_BLOCK iosb;
FsCtlInput.Restart = 1;
BYTE Output[1024];
bool bPathIsRemote = false;
FILE_FS_DEVICE_INFORMATION DeviceInfo;
//
// Determine if the volume is a remote device. This will affect
// our handling of the paths returned by NtQueryInformationFile.
//
status = NtQueryVolumeInformationFile(
hVolumeRoot,
&iosb,
&DeviceInfo,
sizeof(DeviceInfo),
FileFsDeviceInformation);
if (NT_SUCCESS(status))
{
bPathIsRemote = (FILE_REMOTE_DEVICE == DeviceInfo.Characteristics);
}
while (!m_bAbort)
{
status = NtFsControlFile(hVolumeRoot,
NULL,
NULL,
NULL,
&iosb,
FSCTL_FIND_FILES_BY_SID,
&FsCtlInput,
sizeof(FsCtlInput),
Output,
sizeof(Output));
FsCtlInput.Restart = 0;
if (!NT_SUCCESS(status) && STATUS_BUFFER_OVERFLOW != status)
{
DBGERROR((TEXT("NtFsControlFile failed with status 0x%08X"), status));
return HRESULT_FROM_NT(status);
}
if (0 == iosb.Information)
{
//
// No more data.
//
break;
}
PFILE_NAME_INFORMATION pFileNameInfo = (PFILE_NAME_INFORMATION)Output;
while (!m_bAbort && ((PBYTE)pFileNameInfo < Output + iosb.Information))
{
ULONG Length = sizeof(FILE_NAME_INFORMATION) - sizeof(WCHAR) +
pFileNameInfo->FileNameLength;
CNtHandle hChild;
WCHAR szChild[MAX_PATH];
RtlMoveMemory(szChild, pFileNameInfo->FileName, pFileNameInfo->FileNameLength);
szChild[pFileNameInfo->FileNameLength / sizeof(WCHAR)] = L'\0';
status = OpenNtObject(szChild,
hVolumeRoot,
FILE_SYNCHRONOUS_IO_NONALERT,
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
hChild.HandlePtr());
if (!NT_SUCCESS(status))
{
DBGERROR((TEXT("Unable to open file \"%s\". Status = 0x%08X"),
szChild, status));
}
else if (!m_bAbort)
{
//
// Directory entries get a slightly different treatment so
// we need to know if an entry is a directory or not.
//
bool bIsDirectory = false;
IO_STATUS_BLOCK iosb2;
FILE_BASIC_INFORMATION fbi;
status = NtQueryInformationFile(hChild,
&iosb2,
&fbi,
sizeof(fbi),
FileBasicInformation);
if (!NT_SUCCESS(status))
{
DBGERROR((TEXT("NtQueryInformationFile failed with status 0x%08X for \"%s\""),
status, szChild));
}
else if (0 != (FILE_ATTRIBUTE_DIRECTORY & fbi.FileAttributes))
{
bIsDirectory = true;
}
//
// Get the file's name (full path).
//
WCHAR szFile[MAX_PATH + 10];
status = NtQueryInformationFile(hChild,
&iosb2,
szFile,
sizeof(szFile),
FileNameInformation);
if (!NT_SUCCESS(status))
{
DBGERROR((TEXT("NtQueryInformation file failed with status 0x%08X for \"%s\""),
status, szChild));
}
else if (!m_bAbort)
{
PFILE_NAME_INFORMATION pfn = (PFILE_NAME_INFORMATION)szFile;
pfn->FileName[pfn->FileNameLength / sizeof(WCHAR)] = L'\0';
CPath path;
//
// If the path is remote, NtQueryInformationFile returns
// a string like this:
//
// \server\share\dir1\dir2\file.ext
//
// If the path is local, NtQueryInformationFile returns
// a string like this:
//
// \dir1\dir2\file.ext
//
// For remote paths we merely prepend a '\' to create a
// valid UNC path. For local paths we prepend the local
// drive specification.
//
if (bPathIsRemote)
{
path = L"\\";
path += CString(pfn->FileName);
}
else
{
path = pszVolumeRoot;
path.Append(pfn->FileName);
}
DBGPRINT((DM_VIEW, DL_LOW, TEXT("Adding \"%s\""), path.Cstr()));
pOwnerList->AddFile(iOwner, path, bIsDirectory);
}
}
hChild.Close();
pFileNameInfo =
(PFILE_NAME_INFORMATION) Add2Ptr(pFileNameInfo, QuadAlign(Length));
}
}
return NOERROR;
}
//
// Build a list of files owned by a set of users on a particular volume.
// pszVolumeRoot is the volume root directory (i.e. "C:\").
// rgpOwners is an array of user object pointers, one for each owner.
// pOwnerList is the container where the resulting filenames are placed.
// Calls AddFilesToOwnerList() for each owner in rgpOwners.
//
HRESULT
CFileOwnerDialog::BuildFileOwnerList(
LPCTSTR pszVolumeRoot,
const CArray<IDiskQuotaUser *>& rgpOwners,
COwnerList *pOwnerList
)
{
DBGTRACE((DM_VIEW, DL_MID, TEXT("CFileOwnerDialog::BuildFileOwnerList")));
HRESULT hr = NOERROR;
CNtHandle hVolumeRoot;
NTSTATUS status = OpenNtObject(pszVolumeRoot,
NULL,
FILE_SYNCHRONOUS_IO_NONALERT,
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
hVolumeRoot.HandlePtr());
if (!NT_SUCCESS(status))
return HRESULT_FROM_NT(status);
int cOwners = rgpOwners.Count();
for (int i = 0; i < cOwners && !m_bAbort; i++)
{
hr = AddFilesToOwnerList(pszVolumeRoot, hVolumeRoot, rgpOwners[i], pOwnerList);
}
return hr;
}
//
// MarkZ had this function in his original implementation so I just
// kept it. I did need to fix a bug in the original code. He was
// calling RtlFreeHeap() on str.Buffer for all cases. This is was
// not applicable in the RtlInitUnicodeString() case where the
// unicode string is merely bound to the pszFile argument.
//
NTSTATUS
CFileOwnerDialog::OpenNtObject (
LPCWSTR pszFile,
HANDLE RelatedObject,
ULONG CreateOptions,
ULONG DesiredAccess,
ULONG ShareAccess,
ULONG CreateDisposition,
HANDLE *ph)
{
NTSTATUS status;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING str;
IO_STATUS_BLOCK isb;
bool bFreeString = false;
if (NULL == RelatedObject)
{
RtlDosPathNameToNtPathName_U(pszFile, &str, NULL, NULL);
bFreeString = true;
}
else
{
//
// This just attaches pszFile to the rtl string.
// We don't free it.
//
RtlInitUnicodeString(&str, pszFile);
}
InitializeObjectAttributes(&oa,
&str,
OBJ_CASE_INSENSITIVE,
RelatedObject,
NULL);
status = NtCreateFile(ph,
DesiredAccess | SYNCHRONIZE,
&oa,
&isb,
NULL, // pallocationsize (none!)
FILE_ATTRIBUTE_NORMAL,
ShareAccess,
CreateDisposition,
CreateOptions,
NULL, // EA buffer (none!)
0);
if (bFreeString)
RtlFreeHeap(RtlProcessHeap(), 0, str.Buffer);
return(status);
}