/////////////////////////////////////////////////////////////////////////////// /* File: control.cpp Description: Contains member function definitions for class DiskQuotaControl. This class is the main point of focus for managing disk quota information through the DSKQUOTA library. The user creates an instance of a DiskQuotaControl object through CoCreateInstance and manages quota information through it's IDiskQuotaControl interface. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// #include "pch.h" // PCH #pragma hdrstop #include "connect.h" #include "control.h" #include "guidsp.h" // Private GUIDs. #include "registry.h" #include "sidcache.h" #include "userbat.h" #include "userenum.h" #include "resource.h" // For IDS_NO_LIMIT. #include // OLE automation #include #include // // Verify that build is UNICODE. // #if !defined(UNICODE) # error This module must be compiled UNICODE. #endif // // Size of user enumerator's buffer. Thought about this being a reg entry. // Didn't make a lot of sense. // const UINT ENUMUSER_BUF_LEN = 2048; // // To add support for a new connection point type, just add a new IID to this // array. Also add a corresponding enumeration constant in the DiskQuotaControl // class declaration that identifies the location of the conn pt IID in // m_rgpIConnPtsSupported[]. // const IID * const DiskQuotaControl::m_rgpIConnPtsSupported[] = { &IID_IDiskQuotaEvents, &IID_DIDiskQuotaControlEvents }; /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::DiskQuotaControl Description: Constructor. Arguments: None. Returns: Nothing. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 08/15/97 Added m_bInitialized member. BrianAu */ /////////////////////////////////////////////////////////////////////////////// DiskQuotaControl::DiskQuotaControl( VOID ) : m_cRef(0), m_bInitialized(FALSE), m_pFSObject(NULL), m_dwFlags(0), m_pSidNameResolver(NULL), m_rgConnPts(NULL), m_cConnPts(0), m_llDefaultQuotaThreshold(0), m_llDefaultQuotaLimit(0) { DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControl::DiskQuotaControl"))); InterlockedIncrement(&g_cRefThisDll); } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::~DiskQuotaControl Description: Destructor. Releases FSObject pointer. Arguments: None. Returns: Nothing. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// DiskQuotaControl::~DiskQuotaControl( VOID ) { DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControl::~DiskQuotaControl"))); // // See the comment in NotifyUserNameChanged for a discussion on the // use of this mutex. In short, it prevents a deadlock between // the resolver's thread and a client receiving a name-change // notification. The wait here is INFINITE while the corresponding // wait in NotifyUserNameChanged is limited. // AutoLockMutex lock(m_mutex, INFINITE); if (NULL != m_pFSObject) { m_pFSObject->Release(); m_pFSObject = NULL; } ShutdownNameResolution(); if (NULL != m_rgConnPts) { for (UINT i = 0; i < m_cConnPts; i++) { if (NULL != m_rgConnPts[i]) { m_rgConnPts[i]->Release(); m_rgConnPts[i] = NULL; } } delete[] m_rgConnPts; } InterlockedDecrement(&g_cRefThisDll); } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::QueryInterface Description: Returns an interface pointer to the object's IUnknown, IDiskQuotaControl or IConnectionPointContainer interface. The object referenced by the returned interface pointer is uninitialized. The recipient of the pointer must call Initialize() before the object is usable. Arguments: riid - Reference to requested interface ID. ppvOut - Address of interface pointer variable to accept interface ptr. Returns: NOERROR - Success. E_NOINTERFACE - Requested interface not supported. E_INVALIDARG - ppvOut argument was NULL. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::QueryInterface( REFIID riid, LPVOID *ppvOut ) { DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControl::QueryInterface"))); DBGPRINTIID(DM_CONTROL, DL_MID, riid); if (NULL == ppvOut) return E_INVALIDARG; HRESULT hr = E_NOINTERFACE; try { *ppvOut = NULL; if (IID_IUnknown == riid || IID_IDiskQuotaControl == riid) { *ppvOut = this; } else if (IID_IConnectionPointContainer == riid) { hr = InitConnectionPoints(); if (SUCCEEDED(hr)) { *ppvOut = static_cast(this); } } else if (IID_IDispatch == riid || IID_DIDiskQuotaControl == riid) { DiskQuotaControlDisp *pQCDisp = new DiskQuotaControlDisp(static_cast(this)); *ppvOut = static_cast(pQCDisp); } if (NULL != *ppvOut) { ((LPUNKNOWN)*ppvOut)->AddRef(); hr = NOERROR; } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); *ppvOut = NULL; hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::AddRef Description: Increments object reference count. Arguments: None. Returns: New reference count value. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) DiskQuotaControl::AddRef( VOID ) { DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::AddRef"))); DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, m_cRef, m_cRef + 1)); ULONG ulReturn = m_cRef + 1; InterlockedIncrement(&m_cRef); return ulReturn; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::Release Description: Decrements object reference count. If count drops to 0, object is deleted. Arguments: None. Returns: New reference count value. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) DiskQuotaControl::Release( VOID ) { DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::Release"))); DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, m_cRef, m_cRef - 1)); ULONG ulReturn = m_cRef - 1; if (InterlockedDecrement(&m_cRef) == 0) { delete this; ulReturn = 0; } return ulReturn; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::Initialize Description: Initializes a quota controller object by opening the NTFS "device" associated with the quota information. The caller passes the name of an NTFS volume device to open. A C++ object is created which encapsulates the required NTFS functionality. This object is known as a "file system object" or FSObject. Currently, NTFS only supports quotas on volumes. However, there is talk of providing quotas for directories in the future. This library has been designed with this expansion in mind. By using an object hierarchy to represent the FSObject, we are able to shield the quota control object from differences in NTIO API functions dealing with volumes, directories and both local and remote flavors of both. Arguments: pszPath - Name of NTFS path to open. bReadWrite - TRUE = Read/write. FALSE = Read only. Returns: NOERROR - Success. E_INVALIDARG - pszPath arg was NULL. E_OUTOFMEMORY - Insufficient memory. E_UNEXPECTED - Unexpected exception. E_FAIL - Error getting volume information. ERROR_ACCESS_DENIED (hr) - Insufficient access to open FS object. ERROR_FILE_NOT_FOUND (hr) - Specified volume doesn't exist. ERROR_PATH_NOT_FOUND (hr) - Specified volume doesn't exist. ERROR_BAD_PATHNAME (hr) - Invalid path name provided. ERROR_INVALID_NAME (hr) - Invalid path name provided. ERROR_NOT_SUPPORTED (hr) - Quotas not supported by volume. ERROR_ALREADY_INITIALIZED (hr) - Controller is already initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 06/06/96 Added ansi-unicode thunk. BrianAu 06/11/96 Added return of access granted value. BrianAu 09/05/96 Added exception handling. BrianAu 09/23/96 Take a "lazy" position on creating the BrianAu SidNameResolver object. Should only create it when it will be needed (user enumeration). Moved creation from here to CreateEnumUsers. 07/03/97 Added dwAccess argument. BrianAu 08/15/97 Added "already initialized" check. BrianAu Removed InitializeA(). */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::Initialize( LPCWSTR pszPath, BOOL bReadWrite ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::Initialize"))); DBGPRINT((DM_CONTROL, DL_MID, TEXT("\tpath = \"%s\", bReadWrite = %d"), pszPath ? pszPath : TEXT(""), bReadWrite)); HRESULT hr = NOERROR; if (m_bInitialized) { // // Controller has already been initialized. // Re-initialization is not allowed. // hr = HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); } else { if (NULL == pszPath) return E_INVALIDARG; try { DWORD dwAccess = GENERIC_READ | (bReadWrite ? GENERIC_WRITE : 0); hr = FSObject::Create(pszPath, dwAccess, &m_pFSObject); m_bInitialized = SUCCEEDED(hr); } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::CreateEnumUsers Description: Create a new enumerator object for enumerating over the users in a volume's quota information file. The returned interface supports the normal OLE 2 enumeration members Next(), Reset(), Skip() and Clone(). Arguments: rgpSids [optional] - Pointer to a list of SID pointers. If provided, only those users with SIDs included in the list are returned. This argument may be NULL in which case ALL users are included. Any element containing a NULL pointer will terminate the list. cpSids [optional] - If pSidList is not NULL, this arg contains the count of entries in rgpSids. If rgpSids is not NULL and this argument contains 0, rgpSids is assumed to contain a terminating NULL pointer entry. fNameResolution - Can be one of the following: DISKQUOTA_USERNAME_RESOLVE_NONE DISKQUOTA_USERNAME_RESOLVE_SYNC DISKQUOTA_USERNAME_RESOLVE_ASYNC ppEnum - Address of interface variable to accept the IEnumDiskQuotaUser interface pointer. Returns: NOERROR - Success. E_INVALIDARG - ppEnum arg is NULL. E_OUTOFMEMORY - Insufficient memory to create enumerator object. ERROR_ACCESS_DENIED (hr) - Need READ access to create enumerator. ERROR_NOT_READY (hr) - Object not initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 08/11/96 Added access control. BrianAu 09/05/96 Added exception handling. BrianAu 09/23/96 Added lazy creation of SidNameResolver object. BrianAu Moved it from InitializeW(). */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaControl::CreateEnumUsers( PSID *rgpSids, DWORD cpSids, DWORD fNameResolution, IEnumDiskQuotaUsers **ppEnum ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::CreateEnumUsers"))); HRESULT hr = E_FAIL; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == ppEnum) return E_INVALIDARG; if (!m_pFSObject->GrantedAccess(GENERIC_READ)) { hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); } else { DiskQuotaUserEnum *pEnumUsers = NULL; try { if (NULL == m_pSidNameResolver) { // // If there's no SID/Name resolver object, create one. // We do this "as needed" because user enumeration is // the only controller function that requires a resolver. // If the client doesn't need a resolver, why create one? // SidNameResolver *pResolver = NULL; // // Create user SID/Name resolver object. // pResolver = new SidNameResolver(*this); hr = pResolver->QueryInterface(IID_ISidNameResolver, (LPVOID *)&m_pSidNameResolver); if (SUCCEEDED(hr)) { hr = m_pSidNameResolver->Initialize(); if (FAILED(hr)) { // // If resolver initialization fails, we can assume // that the resolver's thread hasn't been created so // it's OK to just call Release() instead of // Shutdown() followed by Release(). This is strongly // dependent on the initialization logic in the resolver's // Initialize method. There's a comment there also. // m_pSidNameResolver->Release(); m_pSidNameResolver = NULL; pResolver = NULL; } } } if (NULL != m_pSidNameResolver) { // // Create and initialize the enumerator object. // pEnumUsers = new DiskQuotaUserEnum(static_cast(this), m_pSidNameResolver, m_pFSObject); // // This can throw OutOfMemory. // hr = pEnumUsers->Initialize(fNameResolution, ENUMUSER_BUF_LEN, rgpSids, cpSids); if (SUCCEEDED(hr)) { hr = pEnumUsers->QueryInterface(IID_IEnumDiskQuotaUsers, (LPVOID *)ppEnum); } else { // // Something failed after enumerator object was created. // delete pEnumUsers; } } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); delete pEnumUsers; hr = E_OUTOFMEMORY; } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::CreateUserBatch Description: Create a new user batch control object. Batch control is provided to take advantage of the inherent batching properties of the NTIOAPI. If many user records are being altered at one time, it is much more efficient to mark each of the users for "deferred update", submit each user object to the batch and then flush the batch to disk. Arguments: ppUserBatch - Address of interface variable to accept the IDiskQuotaUserBatch interface pointer. Returns: NOERROR - Success. E_INVALIDARG - ppOut arg is NULL. E_OUTOFMEMORY - Insufficient memory to create batch object. ERROR_ACCESS_DENIED (hr) - Need WRITE access to create batch. ERROR_NOT_READY (hr) - Object not initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/06/96 Initial creation. BrianAu 08/11/96 Added access control. BrianAu 09/05/96 Added exception handling. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::CreateUserBatch( PDISKQUOTA_USER_BATCH *ppUserBatch ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::CreateUserBatch"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == ppUserBatch) return E_INVALIDARG; if (!m_pFSObject->GrantedAccess(GENERIC_WRITE)) { hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); } else { try { DiskQuotaUserBatch *pUserBatch = new DiskQuotaUserBatch(m_pFSObject); hr = pUserBatch->QueryInterface(IID_IDiskQuotaUserBatch, (LPVOID *)ppUserBatch); } catch(CAllocException& e) // From new or m_UserList ctor. { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::AddUserSid Description: Adds a new user to the volume's quota information file. If successful, returns an interface to the new user object. When the caller is finished with the interface, they must call Release() through that interface pointer. Uses the default limit and threshold. Arguments: pSid - Pointer to single SID structure. fNameResolution - Method of SID-to-name resolution. Can be one of the following: DISKQUOTA_USERNAME_RESOLVE_NONE DISKQUOTA_USERNAME_RESOLVE_SYNC DISKQUOTA_USERNAME_RESOLVE_ASYNC ppUser - Address of interface pointer variable to accept pointer to the new user object's IDiskQuotaUser interface. Returns: SUCCESS - Success. S_FALSE - User already exists. Not added. E_OUTOFMEMORY - Insufficient memory. E_UNEXPECTED - Unexpected exception. E_INVALIDARG - pSid or ppUser were NULL. ERROR_NOT_READY (hr) - Object not initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 09/30/96 Added implementation. Was E_NOTIMPL. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::AddUserSid( PSID pSid, DWORD fNameResolution, PDISKQUOTA_USER *ppUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::AddUserSid"))); HRESULT hr = E_FAIL; PDISKQUOTA_USER pIUser = NULL; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == pSid || NULL == ppUser) return E_INVALIDARG; LONGLONG llLimit = 0; LONGLONG llThreshold = 0; LONGLONG llUsed = 0; *ppUser = NULL; // // Check to see if the user already exists in the quota file. // try { hr = FindUserSid(pSid, DISKQUOTA_USERNAME_RESOLVE_NONE, &pIUser); if (SUCCEEDED(hr)) { // // The NTIOAPI says the user exists. // We'll need the quota info to determine if we // still allow addition of the "new" user. This is needed because // of the weird way the NTIOAPI enumerates users. If you ask it to // enumerate specified user(s), the returned information will include // info for users that do not have (but could have) a record in the // quota file. Since the quota system allows automatic addition of // users, it considers any users with write access to have a record // in the quota file. Such users are returned with a quota threshold // and limit of 0. Therefore, we treat records marked for deletion // or those with 0 used, 0 limit and 0 threshold as "non existing". // I use the term "ghost" for these users. // pIUser->GetQuotaLimit(&llLimit); pIUser->GetQuotaThreshold(&llThreshold); pIUser->GetQuotaUsed(&llUsed); ULARGE_INTEGER a,b,c; a.QuadPart = llLimit; b.QuadPart = llThreshold; c.QuadPart = llUsed; DBGPRINT((DM_CONTROL, DL_LOW, TEXT("Found user: Limit = 0x%08X 0x%08X, Threshold = 0x%08X 0x%08X, Used = 0x%08X 0x%08X"), a.HighPart, a.LowPart, b.HighPart, b.LowPart, c.HighPart, c.LowPart)); BOOL bIsGhost = ((MARK4DEL == llLimit) || ( 0 == llLimit && 0 == llThreshold && 0 == llUsed)); if (!bIsGhost) { // // User already exists. // hr = S_FALSE; } else { DWORD cbSid = GetLengthSid(pSid); // // User not in quota file OR in quota file but marked for deletion. // Just set it's limit and threshold to the volume defaults. // pIUser->SetQuotaThreshold(m_llDefaultQuotaThreshold, TRUE); hr = pIUser->SetQuotaLimit(m_llDefaultQuotaLimit, TRUE); if (SUCCEEDED(hr) && NULL != m_pSidNameResolver) { // // We have a good user object and have set the quota parameters. // Get the user's domain, name and full name from the network DC // using the resolution type specified by the caller. // switch(fNameResolution) { case DISKQUOTA_USERNAME_RESOLVE_ASYNC: m_pSidNameResolver->FindUserNameAsync(pIUser); break; case DISKQUOTA_USERNAME_RESOLVE_SYNC: m_pSidNameResolver->FindUserName(pIUser); break; case DISKQUOTA_USERNAME_RESOLVE_NONE: default: break; } } } *ppUser = pIUser; } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } if (FAILED(hr)) { *ppUser = NULL; if (NULL != pIUser) { pIUser->Release(); } } return hr; } STDMETHODIMP DiskQuotaControl::AddUserName( LPCWSTR pszLogonName, DWORD fNameResolution, PDISKQUOTA_USER *ppUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::AddUserName"))); if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == pszLogonName || NULL == ppUser) return E_INVALIDARG; HRESULT hr = E_FAIL; try { BYTE Sid[MAX_SID_LEN]; DWORD cbSid = sizeof(Sid); SID_NAME_USE eSidUse; if (SUCCEEDED(m_NTDS.LookupAccountByName(NULL, // system pszLogonName, // key NULL, // no container ret NULL, // no display name ret &Sid[0], &cbSid, &eSidUse))) { hr = AddUserSid(&Sid[0], fNameResolution, ppUser); } else { hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER); } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::ShutdownNameResolution Description: Release the SID/Name resolver. This terminates the resolver thread for clients who don't want to wait for the controller object to be destroyed. Note that subsequent calls to CreateEnumUsers, AddUserSid, AddUserName, FindUserSid or FindUserName can restart the resolver. Arguments: None. Returns: Always returns NOERROR Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 08/29/97 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::ShutdownNameResolution( VOID ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::ShutdownNameResolution"))); if (NULL != m_pSidNameResolver) { // // Shutdown and release the resolver. // Since it's running on it's own thread, we must wait for the thread // to exit. // Note that if the thread is off resolving a name from the DC, this could // take a bit. // m_pSidNameResolver->Shutdown(TRUE); // TRUE == Wait for thread exit. m_pSidNameResolver->Release(); m_pSidNameResolver = NULL; } return NOERROR; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::GiveUserNameResolutionPriority Description: A very long name for a very simple function. This function merely finds the user object in the name resolver's input queue and moves it to the head of the queue. Arguments: pUser - Address of interface pointer for the user object's IDiskQuotaUser interface. Returns: NOERROR - Success. S_FALSE - User object not in resolver queue. E_OUTOFMEMORY - Insufficient memory. E_INVALIDARG - pUser is NULL. E_UNEXPECTED - Unexpected error. Caught an exception or the Sid-Name resolver hasn't been created. ERROR_NOT_READY (hr) - Object not initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/18/97 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::GiveUserNameResolutionPriority( PDISKQUOTA_USER pUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GiveUserNameResolutionPriority"))); HRESULT hr = E_UNEXPECTED; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == pUser) return E_INVALIDARG; // // SidNameResolver::PromoteUserToQueueHeader catches exceptions and // converts them to HRESULTs. No need for try-catch block here. // if (NULL != m_pSidNameResolver) { hr = m_pSidNameResolver->PromoteUserToQueueHead(pUser); } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::FindUserSid Description: Finds a single user record in a volume's quota information file. Returns an interface to the corresponding user object. When the caller is finished with the interface, they must call Release() through that interface pointer. >>>>>>>>> IMPORTANT NOTE <<<<<<<<< This method will return a user object even if there is no quota record for the user in the quota file. While that may sound strange, it is consistent with the idea of automatic user addition and default quota settings. If there is currently no user record for the requested user, and the user would be added to the quota file if they were to request disk space, the returned user object will have a quota threshold of 0 and a quota limit of 0. Arguments: pSid - Pointer to single SID structure identifying the user. fNameResolution - Can be one of the following: DISKQUOTA_USERNAME_RESOLVE_NONE DISKQUOTA_USERNAME_RESOLVE_SYNC DISKQUOTA_USERNAME_RESOLVE_ASYNC ppUser - Address of interface pointer variable to accept pointer to the user object's IDiskQuotaUser interface. Returns: NOERROR - Success. E_INVALIDARG - Either pSid or ppUser were NULL. E_OUTOFMEMORY - Insufficient memory. E_UNEXPECTED - Unexpected exception. ERROR_INVALID_SID (hr) - Invalid SID. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. ERROR_NO_SUCH_USER (hr) - User not found in volume's quota information. ERROR_NOT_READY (hr) - Object not initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 08/14/96 Changed name from FindUser to FindUserSid to BrianAu accomodate the addition of the FindUserName methods. No change in functionality. 09/05/96 Added exception handling. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::FindUserSid( PSID pSid, DWORD fNameResolution, PDISKQUOTA_USER *ppUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::FindUserSid"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == pSid || NULL == ppUser) return E_INVALIDARG; if (!m_pFSObject->GrantedAccess(GENERIC_READ)) { hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); } else { if (!IsValidSid(pSid)) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_SID); } else { PENUM_DISKQUOTA_USERS pEnumUsers = NULL; try { DWORD cbSid = GetLengthSid(pSid); *ppUser = NULL; // // Create a user enumerator for the user's SID. // Can throw OutOfMemory. // hr = CreateEnumUsers(&pSid, 1, fNameResolution, &pEnumUsers); if (SUCCEEDED(hr)) { DWORD dwUsersFound = 0; PDISKQUOTA_USER pUser = NULL; // // Enumerate 1 record to get the user's info. // Only one record required since the enumerator object // was created from a single SID. Can throw OutOfMemory. // hr = pEnumUsers->Next(1, &pUser, &dwUsersFound); if (S_OK == hr) { // // Return user object interface to caller. // *ppUser = pUser; } else if (S_FALSE == hr) { // // Note: We should never hit this. // The quota system always returns a user record // for a user SID. If the record doesn't currently // exist, one with default limit and threshold is // returned. This is consistent with the idea // of automatic user record addition implemented // by the NTFS quota system. Just in case we do, // I want to return something intelligent. // hr = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER); } } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } if (NULL != pEnumUsers) { // // Release the enumerator. // pEnumUsers->Release(); } } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::FindUserName Description: Finds a single user record in a volume's quota information file. Returns an interface to the corresponding user object. When the caller is finished with the interface, they must call Release() through that interface pointer. If the name is not already cached in the SidNameCache, the function queries the network domain controller. This operation may take some time (on the order of 0 - 10 seconds). Arguments: pszLogonName - Address of user's logon name string. i.e. "REDMOND\brianau" or "brianau@microsoft.com" ppUser - Address of interface pointer variable to accept pointer to the user object's IDiskQuotaUser interface. Returns: NOERROR - Success. E_INVALIDARG - Name string is blank or NUL ptr was passed. E_OUTOFMEMORY - Insufficient memory. E_UNEXPECTED - Unexpected exception. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. ERROR_NO_SUCH_USER (hr) - User not found in quota file. ERROR_NOT_READY (hr) - Object not initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 08/14/96 Initial creation. BrianAu 09/05/96 Added domain name string. BrianAu Added exception handling. 08/15/97 Removed ANSI version. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::FindUserName( LPCWSTR pszLogonName, PDISKQUOTA_USER *ppUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::FindUserName"))); HRESULT hr = E_FAIL; // Assume failure. BOOL bAskDomainController = TRUE; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == pszLogonName || NULL == ppUser) return E_INVALIDARG; if (TEXT('\0') == *pszLogonName) return E_INVALIDARG; // // Check for client's access to quota file before we do any // time-expensive operations. // if (!m_pFSObject->GrantedAccess(GENERIC_READ)) { hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); } else { PSID pSid = NULL; // For cache query. SID Sid[MAX_SID_LEN]; // For DC query. // // These nested try-catch blocks look really gross and may // be unnecessary. I should probably just punt if one of the // inner blocks really does throw an exception and return // E_UNEXPECTED instead of trying to continue. [brianau] // try { SidNameCache *pSidCache; hr = SidNameCache_Get(&pSidCache); if (SUCCEEDED(hr)) { // // See if the SID/Name pair is in the cache. // try { hr = pSidCache->Lookup(pszLogonName, &pSid); if (SUCCEEDED(hr)) { // // We have a SID. No need to ask DC. // bAskDomainController = FALSE; } } catch(CAllocException& e) { // // Just catch the exception. // This will cause us to go to the DC for the SID. // DBGERROR((TEXT("C++ exception during SID cache lookup in FindUserName"))); pSid = &Sid[0]; } } if (bAskDomainController) { DBGASSERT((FAILED(hr))); // // Still don't have a SID. Ask the DC. // This can take some time (Ho Hum.........) // CString strDisplayName; CString strContainerName; SID_NAME_USE eUse; DWORD cbSid = sizeof(Sid); if (SUCCEEDED(m_NTDS.LookupAccountByName(NULL, pszLogonName, &strContainerName, &strDisplayName, &Sid[0], &cbSid, &eUse))) { pSid = &Sid[0]; // // Add it to the cache for later use. // if (NULL != pSidCache) { pSidCache->Add(&Sid[0], strContainerName, pszLogonName, strDisplayName); } hr = NOERROR; } } if (SUCCEEDED(hr)) { // // We have a SID. // Now create the actual user object using FindUserSid(). // hr = FindUserSid(pSid, DISKQUOTA_USERNAME_RESOLVE_SYNC, ppUser); } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } if (&Sid[0] != pSid) { // // We received a heap-allocated SID from SidNameCache::Lookup. // Need to free the buffer. // delete[] pSid; } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::DeleteUser Description: Deletes a user from a volume's quota information and quota tracking. The IDiskQuotaUser pointer may be obtained either through enumeration or DiskQuotaControl::FindUser(). NOTE: At this time, we're not sure how (or if) deletion will be done. This function remains un-implemented until we figure it out. Arguments: pUser - Pointer to quota user object's IDiskQuotaUser interface. Returns: NOERROR - Success. E_OUTOFMEMORY - Insufficient memory. E_UNEXPECTED - Unexpected exception. E_FAIL - NTIO error writing user data. E_INVALIDARG - pUser argument was NULL. ERROR_FILE_EXISTS (hr) - Couldn't delete. User still has bytes charge. ERROR_ACCESS_DENIED (hr) - Insufficient access. ERROR_NOT_READY (hr) - Object not initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/22/96 Initial creation. BrianAu 09/28/96 Added implementation. Was E_NOTIMPL. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::DeleteUser( PDISKQUOTA_USER pUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::DeleteUser"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == pUser) return E_INVALIDARG; try { LONGLONG llValue; // // Invalidate user object to force a refresh of data from the // quota file. Want to make sure this is current information before // we tell the caller that the user can't be deleted. // pUser->Invalidate(); hr = pUser->GetQuotaUsed(&llValue); if (SUCCEEDED(hr)) { if (0 == llValue) { // // User has 0 bytes in use. OK to delete. // Write -2 to user's limit and threshold. // pUser->SetQuotaThreshold(MARK4DEL, TRUE); hr = pUser->SetQuotaLimit(MARK4DEL, TRUE); } else { hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS); } } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::QueryQuotaInformation Description: Read quota information from disk to member variables. Arguments: None. Returns: NOERROR - Success. E_FAIL - Any other error. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/23/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaControl::QueryQuotaInformation( VOID ) { DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::QueryQuotaInformation"))); HRESULT hr = NOERROR; DISKQUOTA_FSOBJECT_INFORMATION Info; hr = m_pFSObject->QueryObjectQuotaInformation(&Info); if (SUCCEEDED(hr)) { m_llDefaultQuotaThreshold = Info.DefaultQuotaThreshold; m_llDefaultQuotaLimit = Info.DefaultQuotaLimit; m_dwFlags = (Info.FileSystemControlFlags & DISKQUOTA_FLAGS_MASK); } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::SetQuotaInformation Description: Writes quota information from member variables to disk. Arguments: dwChangeMask - A bit mask with one or more of the following bits set: FSObject::ChangeState FSObject::ChangeLogFlags FSObject::ChangeThreshold FSObject::ChangeLimit Returns: NOERROR - Success. ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/23/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaControl::SetQuotaInformation( DWORD dwChangeMask ) { DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::SetQuotaInformation"))); HRESULT hr = NOERROR; DISKQUOTA_FSOBJECT_INFORMATION Info; Info.DefaultQuotaThreshold = m_llDefaultQuotaThreshold; Info.DefaultQuotaLimit = m_llDefaultQuotaLimit; Info.FileSystemControlFlags = m_dwFlags; hr = m_pFSObject->SetObjectQuotaInformation(&Info, dwChangeMask); return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::SetDefaultQuotaThreshold Description: Sets the default quota threshold value applied to new user quota records. Value is in bytes. Arguments: llThreshold - Threshold value. Returns: NOERROR - Success. ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device. ERROR_NOT_READY (hr) - Object not initialized. ERROR_INVALID_PARAMETER - llThreshold was less than -2. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/23/96 Initial creation. BrianAu 11/11/98 Added check for value < -2. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::SetDefaultQuotaThreshold( LONGLONG llThreshold ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::SetDefaultQuotaThreshold"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (MARK4DEL > llThreshold) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); m_llDefaultQuotaThreshold = llThreshold; hr = SetQuotaInformation(FSObject::ChangeThreshold); return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::SetDefaultQuotaLimit Description: Sets the default quota limit value applied to new user quota records. Value is in bytes. Arguments: llThreshold - Limit value. Returns: NOERROR - Success. ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device. ERORR_NOT_READY (hr) - Object not initialized. ERROR_INVALID_PARAMETER - llLimit was less than -2. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/23/96 Initial creation. BrianAu 11/11/98 Added check for value < -2. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::SetDefaultQuotaLimit( LONGLONG llLimit ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::SetDefaultQuotaLimit"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (MARK4DEL > llLimit) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); m_llDefaultQuotaLimit = llLimit; hr = SetQuotaInformation(FSObject::ChangeLimit); return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::GetDefaultQuotaItem Description: Retrieves one of the default quota items (limit, threshold) applied to new user quota records. Value is in bytes. Arguments: pllItem - Address of item (limit, threshold, used) value item to retrieve (member variable). pllValueOut - Address of LONGLONG variable to receive value. Returns: NOERROR - Success. E_INVALIDARG - pdwLowPart or pdwHighPart is NULL. E_UNEXPECTED - Unexpected exception. E_OUTOFMEMORY - Insufficient memory. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/23/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaControl::GetDefaultQuotaItem( PLONGLONG pllItem, PLONGLONG pllValueOut ) { DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControl::GetDefaultQuotaItem"))); HRESULT hr = NOERROR; if (NULL == pllItem || NULL == pllValueOut) return E_INVALIDARG; try { hr = QueryQuotaInformation(); if (SUCCEEDED(hr)) { *pllValueOut = *pllItem; } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::GetDefaultQuotaThreshold Description: Retrieves the default quota threshold applied to new user quota records. Value is in bytes. Arguments: pllThreshold - Address of LONGLONG to receive threshold value. Returns: NOERROR - Success. E_INVALIDARG - pdwLowPart or pdwHighPart is NULL. E_UNEXPECTED - Unexpected exception. E_OUTOFMEMORY - Insufficient memory. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. ERROR_NOT_READY (hr) - Object not initialized. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/23/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::GetDefaultQuotaThreshold( PLONGLONG pllThreshold ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetDefaultQuotaThreshold"))); if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); return GetDefaultQuotaItem(&m_llDefaultQuotaThreshold, pllThreshold); } STDMETHODIMP DiskQuotaControl::GetDefaultQuotaThresholdText( LPWSTR pszText, DWORD cchText ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetDefaultQuotaThresholdText"))); if (NULL == pszText) return E_INVALIDARG; LONGLONG llValue; HRESULT hr = GetDefaultQuotaThreshold(&llValue); if (SUCCEEDED(hr)) { if (NOLIMIT == llValue) { LoadString(g_hInstDll, IDS_NO_LIMIT, pszText, cchText); } else { XBytes::FormatByteCountForDisplay(llValue, pszText, cchText); } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::GetDefaultQuotaLimit Description: Retrieves the default quota limit applied to new user quota records. Value is in bytes. Arguments: pllThreshold - Address of LONGLONG to receive limit value. Returns: NOERROR - Success. E_INVALIDARG - pdwLowPart or pdwHighPart is NULL. E_UNEXPECTED - Unexpected exception. E_OUTOFMEMORY - Insufficient memory. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. ERROR_NOT_READY (hr) - Object not initialized. E_FAIL - Any other error. // BUBUG: conflict? Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 05/23/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::GetDefaultQuotaLimit( PLONGLONG pllLimit ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetDefaultQuotaLimit"))); if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); return GetDefaultQuotaItem(&m_llDefaultQuotaLimit, pllLimit); } STDMETHODIMP DiskQuotaControl::GetDefaultQuotaLimitText( LPWSTR pszText, DWORD cchText ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetDefaultQuotaLimitText"))); if (NULL == pszText) return E_INVALIDARG; LONGLONG llValue; HRESULT hr = GetDefaultQuotaLimit(&llValue); if (SUCCEEDED(hr)) { if (NOLIMIT == llValue) { LoadString(g_hInstDll, IDS_NO_LIMIT, pszText, cchText); } else { XBytes::FormatByteCountForDisplay(llValue, pszText, cchText); } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::GetQuotaState Description: Retrieve the state of the quota system. Arguments: pdwState - Address of DWORD to accept the quota state value. Returned value is formatted as follows: Bit(s) Definition ------- --------------------------------------------------- 00-01 0 = Disabled (DISKQUOTA_STATE_DISABLED) 1 = Tracking (DISKQUOTA_STATE_TRACK) 2 = Enforcing (DISKQUOTA_STATE_ENFORCE) 3 = Invalid value. 02-07 Reserved 08 1 = Quota file incomplete. 09 1 = Rebuilding quota file. 10-31 Reserved. Use the macros defined in dskquota.h to query the values and bits in this state DWORD. Returns: NOERROR - Success. E_INVALIDARG - pdwState arg is NULL. E_UNEXPECTED - Unexpected exception. E_OUTOFMEMORY - Insufficient memory. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. ERROR_NOT_READY (hr) - Object not initialized. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/02/96 Initial creation. BrianAu 08/19/96 Added DISKQUOTA_FILEFLAG_MASK. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::GetQuotaState( LPDWORD pdwState ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetQuotaState"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == pdwState) return E_INVALIDARG; try { hr = QueryQuotaInformation(); if (SUCCEEDED(hr)) { DWORD dwMask = DISKQUOTA_STATE_MASK | DISKQUOTA_FILEFLAG_MASK; *pdwState = (m_dwFlags & dwMask); } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::SetQuotaState Description: Sets the quota state flags in the volume's quota info file. The quota state may be one of the following: - Disabled. - Tracking quotas (no enforcement). - Enforcing quota limits. Arguments: dwState - New state of quota system. The bits in this DWORD are defined as follows: Bit(s) Definition ------- --------------------------------------------------- 00-01 0 = Disabled (DISKQUOTA_STATE_DISABLED) 1 = Tracking (DISKQUOTA_STATE_TRACK) 2 = Enforcing (DISKQUOTA_STATE_ENFORCE) 3 = Invalid value. 02-07 Reserved 08 1 = Quota file incomplete (read only) 09 1 = Rebuilding quota file (read only) 10-31 Reserved. Use the macros defined in dskquota.h to set the values and bits in this state DWORD. Returns: NOERROR - Success. E_INVALIDARG - Invalid state value. E_UNEXPECTED - Unexpected exception. E_OUTOFMEMORY - Insufficient memory. ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device. ERROR_NOT_READY (hr) - Object not initialized. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/02/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::SetQuotaState( DWORD dwState ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::SetQuotaState"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); try { if (dwState <= DISKQUOTA_STATE_MASK) { m_dwFlags &= ~DISKQUOTA_STATE_MASK; // Clear current state bits. m_dwFlags |= dwState; // Set new state bits. hr = SetQuotaInformation(FSObject::ChangeState); } else { hr = E_INVALIDARG; } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::GetQuotaLogFlags Description: Retrieve the state of the quota logging system. Arguments: pdwFlags - Address of DWORD to accept the quota logging flags. The bits in the flags DWORD are defined as follows: Bit(s) Definition ------- --------------------------------------------------- 00 1 = Logging user threshold violations. 01 1 = Logging user limit violations. 02 1 = Logging volume threshold violations. 03 1 = Logging volume limit violations. 04-31 Reserved. Use the macros defined in dskquota.h to query the values and bits in this flags DWORD. Returns: NOERROR - Success. E_INVALIDARG - pdwFlags arg is NULL. E_UNEXPECTED - Unexpected exception. E_OUTOFMEMORY - Insufficient memory. ERROR_ACCESS_DENIED (hr) - No READ access to quota device. ERROR_NOT_READY (hr) - Object not initialized. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/02/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::GetQuotaLogFlags( LPDWORD pdwFlags ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::GetQuotaLogFlags"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); if (NULL == pdwFlags) return E_INVALIDARG; try { hr = QueryQuotaInformation(); if (SUCCEEDED(hr)) { *pdwFlags = ((m_dwFlags & DISKQUOTA_LOGFLAG_MASK) >> DISKQUOTA_LOGFLAG_SHIFT); } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::SetQuotaLogFlags Description: Sets the quota logging state flags in the volume's quota info file. Arguments: dwFlags - New state of quota logging. The bits in the flags DWORD are defined as follows: Bit(s) Definition ------- --------------------------------------------------- 00 1 = Logging user threshold violations. 01 1 = Logging user limit violations. 02 1 = Logging volume threshold violations. 03 1 = Logging volume limit violations. 04-31 Reserved. Use the macros defined in dskquota.h to set the values and bits in this flags DWORD. Returns: NOERROR - Success. E_UNEXPECTED - Unexpected exception. E_OUTOFMEMORY - Insufficient memory. ERROR_ACCESS_DENIED (hr) - No WRITE access to quota device. ERROR_NOT_READY (hr) - Object not initialized. E_FAIL - Any other error. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/02/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::SetQuotaLogFlags( DWORD dwFlags ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::SetQuotaLogFlags"))); HRESULT hr = NOERROR; if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); try { m_dwFlags &= ~DISKQUOTA_LOGFLAG_MASK; m_dwFlags |= (dwFlags << DISKQUOTA_LOGFLAG_SHIFT); hr = SetQuotaInformation(FSObject::ChangeLogFlags); } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::InitConnectionPoints Description: Private function for initializing the connection point objects supported by IConnectionPointContainer. Called from DiskQuotaControl::Initialize(). To add a new connection point type, merely add a new record to the m_rgConnPtDesc[] array in the DiskQuotaControl class declaration. All of the other related code in DiskQuotaControl will adjust to it automatically. Arguments: None. Returns: NOERROR - Success. E_UNEXPECTED - A connection point pointer was non-NULL. Exceptions: OutOfMemory. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/19/96 Initial creation. BrianAu 09/05/96 Added exception handling. BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaControl::InitConnectionPoints( VOID ) { DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControl::InitConnectionPoints"))); if (NULL != m_rgConnPts) { // // Already initialized. // return NOERROR; } HRESULT hr = NOERROR; ConnectionPoint *pConnPt = NULL; m_cConnPts = ARRAYSIZE(m_rgpIConnPtsSupported); try { m_rgConnPts = new PCONNECTIONPOINT[m_cConnPts]; // // For each of the connection point IIDs in m_rgpIConnPtsSupported[]... // for (UINT i = 0; i < m_cConnPts && SUCCEEDED(hr); i++) { m_rgConnPts[i] = NULL; // // Create connection point object and query for IConnectionPoint interface. // pConnPt = new ConnectionPoint(static_cast(this), *m_rgpIConnPtsSupported[i]); hr = pConnPt->QueryInterface(IID_IConnectionPoint, (LPVOID *)&m_rgConnPts[i]); if (FAILED(hr)) { // // Either Initialize or QI failed. // delete pConnPt; pConnPt = NULL; } } } catch(CAllocException& e) { delete pConnPt; hr = E_OUTOFMEMORY; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::FindConnectionPoint Description: Queries the quota control object for a specific connection point type. If that type is supported, a pointer to the connection point's IConnectionPoint interface is returned. Arguments: riid - Interface ID of desired connection point interface. Supported interfaces: IID_IDiskQuotaUserEvents - OnNameChanged() Returns: NOERROR - Success. E_INVALIDARG - ppConnPtOut arg is NULL. E_NOINTERFACE - Requested interface is not supported. E_UNEXPECTED - Connection point object pointer is NULL. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/19/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::FindConnectionPoint( REFIID riid, IConnectionPoint **ppConnPtOut ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::FindConnectionPoint"))); DBGPRINTIID(DM_CONTROL, DL_HIGH, riid); HRESULT hr = E_NOINTERFACE; if (NULL == ppConnPtOut) return E_INVALIDARG; *ppConnPtOut = NULL; for (UINT i = 0; i < m_cConnPts && NULL == *ppConnPtOut; i++) { if (*m_rgpIConnPtsSupported[i] == riid) { if (NULL != m_rgConnPts[i]) { // // We have an IID match. // Now get the conn pt interface pointer. // hr = m_rgConnPts[i]->QueryInterface(IID_IConnectionPoint, (LPVOID *)ppConnPtOut); } else { hr = E_UNEXPECTED; break; } } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::EnumConnectionPoints Description: Creates a connection point enumerator object. Using this object, the client can enumerate through all of the connection point interfaces supported by the quota controller. Arguments: ppEnum - Address of interface pointer variable to receive the IEnumConnectionPoints interface. Returns: NOERROR - Success. E_OUTOFMEMORY - Insufficient memory to create object(s). E_INVALIDARG - ppEnum arg was NULL. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 06/19/96 Initial creation. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::EnumConnectionPoints( IEnumConnectionPoints **ppEnum ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::EnumConnectionPoints"))); HRESULT hr = NOERROR; if (NULL == ppEnum) return E_INVALIDARG; PCONNECTIONPOINT rgCP[ARRAYSIZE(m_rgpIConnPtsSupported)]; ConnectionPointEnum *pEnum = NULL; *ppEnum = NULL; for (UINT i = 0; i < m_cConnPts; i++) { // // Make a copy of each connection point pointer // to give to the enumerator's Initialize() method. // m_rgConnPts[i]->AddRef(); rgCP[i] = m_rgConnPts[i]; } try { pEnum = new ConnectionPointEnum(static_cast(this), m_cConnPts, rgCP); hr = pEnum->QueryInterface(IID_IEnumConnectionPoints, (LPVOID *)ppEnum); } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } if (FAILED(hr) && NULL != pEnum) { delete pEnum; *ppEnum = NULL; } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::InvalidateSidNameCache Description: Invalidates the contents of the SidNameCache so that future requests for account names from the cache must be resolved through the DC. As names are resolved, they are again added to the cache. Arguments: None. Returns: NOERROR - Cache invalidated. E_OUTOFMEMORY - Insufficient memory. E_UNEXPECTED - Unexpected exception. E_FAIL - No cache object available or couldn't get lock on cache files. Either way, cache wasn't invalidated. ERROR_NOT_READY (hr) - Object not initialized. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 07/24/96 Initial creation. BrianAu 09/20/96 Updated for new cache design. BrianAu */ /////////////////////////////////////////////////////////////////////////////// STDMETHODIMP DiskQuotaControl::InvalidateSidNameCache( VOID ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::InvalidateSidNameCache"))); if (!m_bInitialized) return HRESULT_FROM_WIN32(ERROR_NOT_READY); SidNameCache *pCache; HRESULT hr = SidNameCache_Get(&pCache); if (SUCCEEDED(hr)) { if (!pCache->Clear()) { hr = E_FAIL; } } return hr; } /////////////////////////////////////////////////////////////////////////////// /* Function: DiskQuotaControl::NotifyUserNameChanged Description: Notify all user IDiskQuotaControl Event connections that a user's name has changed. Arguments: pUser - Address of user object's IDiskQuotaUser interface. Returns: NOERROR - Success. E_OUTOFMEMORY - Insufficient memory to create enumerator. Revision History: Date Description Programmer -------- --------------------------------------------------- ---------- 07/22/96 Initial creation. BrianAu 08/25/97 Added support for IPropertyNotifySink BrianAu */ /////////////////////////////////////////////////////////////////////////////// HRESULT DiskQuotaControl::NotifyUserNameChanged( PDISKQUOTA_USER pUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControl::NotifyUserNameChanged"))); HRESULT hr = NOERROR; PCONNECTIONPOINT pConnPt = NULL; bool bAbort = false; INT rgiConnPt[] = { ConnPt_iQuotaEvents, ConnPt_iQuotaEventsDisp }; for (INT i = 0; i < ARRAYSIZE(rgiConnPt) && !bAbort; i++) { if (NULL != (pConnPt = m_rgConnPts[ rgiConnPt[i] ])) { PENUMCONNECTIONS pEnum = NULL; pConnPt->AddRef(); hr = pConnPt->EnumConnections(&pEnum); if (SUCCEEDED(hr)) { CONNECTDATA cd; while(!bAbort && NOERROR == pEnum->Next(1, &cd, NULL)) { DBGASSERT((NULL != cd.pUnk)); LPUNKNOWN pEventSink = NULL; hr = cd.pUnk->QueryInterface(*(m_rgpIConnPtsSupported[ rgiConnPt[i] ]), (LPVOID *)&pEventSink); // // Guard with a critical section mutex. The NT5 quota UI // may deadlock after closing the details view window // without this critical section. It's possible, other // clients of the quota controller could do the same. // Here's what happens: // The controller calls OnUserNameChanged which // is implemented by the DetailsView object. This // function updates the details view for the specified // quota user. Update involves sending/posting // messages to the listview object. On destruction // of the listview window (user closing the window), // the quota controller is released. If the // DetailsView held the last ref count to the // controller, the controller commands the SID/Name // resolver to shutdown. The resolver's Shutdown // command posts a WM_QUIT to the resolver's input // queue and blocks until the resolver's thread // exits normally. The problem is that the DetailsView // thread is blocked waiting for the resolver's thread // to exit but the resolver's thread is blocked // because the DetailsView thread can't process it's // listview update messages. This results in deadlock. // This critical section prevents this. // if (SUCCEEDED(hr)) { if (WAIT_OBJECT_0 != m_mutex.Wait(2000)) { // // DiskQuotaControl dtor must own this mutex. // Since the control is being destroyed, no sense in // continuing. // DBGERROR((TEXT("Mutex timeout in DiskQuotaControl::NotifyUserNameChanged"))); bAbort = true; } else { AutoLockMutex lock(m_mutex); // Exception-safe release. try { // // Calling client code. Handle any exceptions. // switch(rgiConnPt[i]) { case ConnPt_iQuotaEvents: hr = ((PDISKQUOTA_EVENTS)pEventSink)->OnUserNameChanged(pUser); break; case ConnPt_iQuotaEventsDisp: { IDispatch *pEventDisp = NULL; hr = pEventSink->QueryInterface(IID_IDispatch, (LPVOID *)&pEventDisp); if (SUCCEEDED(hr)) { IDispatch *pUserDisp = NULL; hr = pUser->QueryInterface(IID_IDispatch, (LPVOID *)&pUserDisp); if (SUCCEEDED(hr)) { UINT uArgErr; VARIANTARG va; DISPPARAMS params; VariantClear(&va); V_VT(&va) = VT_DISPATCH; V_DISPATCH(&va) = pUserDisp; params.rgvarg = &va; params.rgdispidNamedArgs = NULL; params.cArgs = 1; params.cNamedArgs = 0; hr = pEventDisp->Invoke(DISPID_DISKQUOTAEVENTS_USERNAMECHANGED, IID_NULL, GetThreadLocale(), DISPATCH_METHOD, ¶ms, NULL, NULL, &uArgErr); if (FAILED(hr)) { DBGERROR((TEXT("Error 0x%08X firing async notification event with IDispatch::Invoke"), hr)); } pUserDisp->Release(); } else { DBGERROR((TEXT("Error 0x%08X getting IDispatch interface from user object for async notification."), hr)); } pEventDisp->Release(); } else { DBGERROR((TEXT("Error 0x%08X getting IDispatch interface from connection point for async notification."), hr)); } break; } default: // // Shouldn't hit this. // DBGERROR((TEXT("Invalid connection point ID"))); break; } } catch(CAllocException& e) { // // Ignore an allocation exception and try to continue. // } } pEventSink->Release(); } cd.pUnk->Release(); } pEnum->Release(); } pConnPt->Release(); } } return hr; } DiskQuotaControlDisp::DiskQuotaControlDisp( PDISKQUOTA_CONTROL pQC ) : m_cRef(0), m_pQC(pQC), m_pUserEnum(NULL) { DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControlDisp::DiskQuotaControlDisp"))); if (NULL != m_pQC) { m_pQC->AddRef(); } // // This is the default resolution style for OLE automation. // I've used ASYNC as the default so it won't hang the caller // on enumeration if many of the names aren't resolved. // If they want sync resolution, they can set the // UserNameResolution property. // m_fOleAutoNameResolution = DISKQUOTA_USERNAME_RESOLVE_ASYNC; m_Dispatch.Initialize(static_cast(this), LIBID_DiskQuotaTypeLibrary, IID_DIDiskQuotaControl, L"DSKQUOTA.DLL"); } DiskQuotaControlDisp::~DiskQuotaControlDisp( VOID ) { DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControlDisp::~DiskQuotaControlDisp"))); if (NULL != m_pUserEnum) { m_pUserEnum->Release(); } if (NULL != m_pQC) { m_pQC->Release(); } } STDMETHODIMP_(ULONG) DiskQuotaControlDisp::AddRef( VOID ) { DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControlDisp::AddRef"))); DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, m_cRef, m_cRef + 1)); ULONG ulReturn = m_cRef + 1; InterlockedIncrement(&m_cRef); return ulReturn; } STDMETHODIMP_(ULONG) DiskQuotaControlDisp::Release( VOID ) { DBGTRACE((DM_CONTROL, DL_LOW, TEXT("DiskQuotaControlDisp::Release"))); DBGPRINT((DM_CONTROL, DL_LOW, TEXT("\t0x%08X %d -> %d"), this, m_cRef, m_cRef - 1)); ULONG ulReturn = m_cRef - 1; if (InterlockedDecrement(&m_cRef) == 0) { delete this; ulReturn = 0; } return ulReturn; } STDMETHODIMP DiskQuotaControlDisp::QueryInterface( REFIID riid, LPVOID *ppvOut ) { DBGTRACE((DM_CONTROL, DL_MID, TEXT("DiskQuotaControlDisp::QueryInterface"))); DBGPRINTIID(DM_CONTROL, DL_MID, riid); HRESULT hr = E_NOINTERFACE; if (NULL == ppvOut) return E_INVALIDARG; *ppvOut = NULL; if (IID_IUnknown == riid) { *ppvOut = this; } else if (IID_IDispatch == riid) { *ppvOut = static_cast(this); } else if (IID_DIDiskQuotaControl == riid) { *ppvOut = static_cast(this); } else if (IID_IDiskQuotaControl == riid || IID_IConnectionPointContainer == riid) { // // Return the quota controller's vtable interface. // This allows code to "typecast" (COM-style) between // the dispatch interface and vtable interface. // return m_pQC->QueryInterface(riid, ppvOut); } if (NULL != *ppvOut) { ((LPUNKNOWN)*ppvOut)->AddRef(); hr = NOERROR; } return hr; } // // IDispatch::GetIDsOfNames // STDMETHODIMP DiskQuotaControlDisp::GetIDsOfNames( REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::GetIDsOfNames"))); // // Let our dispatch object handle this. // return m_Dispatch.GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId); } // // IDispatch::GetTypeInfo // STDMETHODIMP DiskQuotaControlDisp::GetTypeInfo( UINT iTInfo, LCID lcid, ITypeInfo **ppTypeInfo ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::GetTypeInfo"))); // // Let our dispatch object handle this. // return m_Dispatch.GetTypeInfo(iTInfo, lcid, ppTypeInfo); } // // IDispatch::GetTypeInfoCount // STDMETHODIMP DiskQuotaControlDisp::GetTypeInfoCount( UINT *pctinfo ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::GetTypeInfoCount"))); // // Let our dispatch object handle this. // return m_Dispatch.GetTypeInfoCount(pctinfo); } // // IDispatch::Invoke // STDMETHODIMP DiskQuotaControlDisp::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::Invoke"))); // // Let our dispatch object handle this. // return m_Dispatch.Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); } // // Dispatch property "QuotaState" (put) // // Sets the state of the quota system on the volume. // See DiskQuotaControl::SetQuotaState for details. // // Valid states: 0 = Disabled. // 1 = Tracking // 2 = Enforcing STDMETHODIMP DiskQuotaControlDisp::put_QuotaState( QuotaStateConstants State ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_QuotaState"))); if (dqStateMaxValue < State) { // // State can only be 0, 1 or 2. // return E_INVALIDARG; } // // No exception handling required. // DiskQuotaControl::SetQuotaState handles exceptions. // return m_pQC->SetQuotaState(State); } // // Dispatch property "QuotaState" (get) // // Retrieves the state of the quota system on the volume. // See DiskQuotaControl::GetQuotaState for details. // // State returned: 0 = Disabled. // 1 = Tracking // 2 = Enforcing // STDMETHODIMP DiskQuotaControlDisp::get_QuotaState( QuotaStateConstants *pState ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_QuotaState"))); DWORD dwState; // // No exception handling required. // DiskQuotaControl::GetQuotaState handles exceptions. // HRESULT hr = m_pQC->GetQuotaState(&dwState); if (SUCCEEDED(hr)) { *pState = (QuotaStateConstants)(dwState & DISKQUOTA_STATE_MASK); } return hr; } // // Dispatch property "QuotaFileIncomplete" (get) // // Determines if the state of the quota file is "incomplete". // STDMETHODIMP DiskQuotaControlDisp::get_QuotaFileIncomplete( VARIANT_BOOL *pbIncomplete ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_QuotaFileIncomplete"))); DWORD dwState; // // No exception handling required. // DiskQuotaControl::GetQuotaState handles exceptions. // HRESULT hr = m_pQC->GetQuotaState(&dwState); if (SUCCEEDED(hr)) { *pbIncomplete = DISKQUOTA_FILE_INCOMPLETE(dwState) ? VARIANT_TRUE : VARIANT_FALSE; } return hr; } // // Dispatch property "QuotaFileRebuilding" (get) // // Determines if the state of the quota file is "rebuilding". // STDMETHODIMP DiskQuotaControlDisp::get_QuotaFileRebuilding( VARIANT_BOOL *pbRebuilding ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_QuotaFileRebuilding"))); DWORD dwState; // // No exception handling required. // DiskQuotaControl::GetQuotaState handles exceptions. // HRESULT hr = m_pQC->GetQuotaState(&dwState); if (SUCCEEDED(hr)) { *pbRebuilding = DISKQUOTA_FILE_REBUILDING(dwState) ? VARIANT_TRUE : VARIANT_FALSE; } return hr; } // // Dispatch property "LogQuotaThreshold" (put) // // Sets the "log warning threshold" flag on the volume. // STDMETHODIMP DiskQuotaControlDisp::put_LogQuotaThreshold( VARIANT_BOOL bLog ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_LogQuotaThreshold"))); DWORD dwFlags; // // No exception handling required. // DiskQuotaControl::GetQuotaLogFlags and SetQuotaLogFlags handle // exceptions. // HRESULT hr = m_pQC->GetQuotaLogFlags(&dwFlags); if (SUCCEEDED(hr)) { hr = m_pQC->SetQuotaLogFlags(DISKQUOTA_SET_LOG_USER_THRESHOLD(dwFlags, VARIANT_TRUE == bLog)); } return hr; } // // Dispatch property "LogQuotaThreshold" (get) // // Retrieves the "log warning threshold" flag on the volume. // STDMETHODIMP DiskQuotaControlDisp::get_LogQuotaThreshold( VARIANT_BOOL *pbLog ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_LogQuotaThreshold"))); DWORD dwFlags; // // No exception handling required. // DiskQuotaControl::GetQuotaLogFlags handles exceptions. // HRESULT hr = m_pQC->GetQuotaLogFlags(&dwFlags); if (SUCCEEDED(hr)) { *pbLog = DISKQUOTA_IS_LOGGED_USER_THRESHOLD(dwFlags) ? VARIANT_TRUE : VARIANT_FALSE; } return hr; } // // Dispatch property "LogQuotaLimit" (put) // // Sets the "log quota limit" flag on the volume. // STDMETHODIMP DiskQuotaControlDisp::put_LogQuotaLimit( VARIANT_BOOL bLog ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_LogQuotaLimit"))); DWORD dwFlags; // // No exception handling required. // DiskQuotaControl::GetQuotaLogFlags handles exceptions. // HRESULT hr = m_pQC->GetQuotaLogFlags(&dwFlags); if (SUCCEEDED(hr)) { hr = m_pQC->SetQuotaLogFlags(DISKQUOTA_SET_LOG_USER_LIMIT(dwFlags, VARIANT_TRUE == bLog)); } return hr; } // // Dispatch property "LogQuotaLimit" (get) // // Retrieves the "log quota limit" flag on the volume. // STDMETHODIMP DiskQuotaControlDisp::get_LogQuotaLimit( VARIANT_BOOL *pbLog ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_LogQuotaLimit"))); DWORD dwFlags; // // No exception handling required. // DiskQuotaControl::GetQuotaLogFlags handles exceptions. // HRESULT hr = m_pQC->GetQuotaLogFlags(&dwFlags); if (SUCCEEDED(hr)) { *pbLog = DISKQUOTA_IS_LOGGED_USER_LIMIT(dwFlags) ? VARIANT_TRUE : VARIANT_FALSE; } return hr; } // // Dispatch property "DefaultQuotaThreshold" (put) // // Sets the default quota threshold on the volume. // STDMETHODIMP DiskQuotaControlDisp::put_DefaultQuotaThreshold( double Threshold ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_DefaultQuotaThreshold"))); if (MAXLONGLONG < Threshold) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); // // No exception handling required. // DiskQuotaControl::GetDefaultQuotaThreshold handles exceptions. // return m_pQC->SetDefaultQuotaThreshold((LONGLONG)Threshold); } // // Dispatch property "DefaultQuotaThreshold" (get) // // Retrieves the default quota threshold on the volume. // STDMETHODIMP DiskQuotaControlDisp::get_DefaultQuotaThreshold( double *pThreshold ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_DefaultQuotaThreshold"))); LONGLONG llTemp; // // No exception handling required. // DiskQuotaControl::GetDefaultQuotaThreshold handles exceptions. // HRESULT hr = m_pQC->GetDefaultQuotaThreshold(&llTemp); if (SUCCEEDED(hr)) { *pThreshold = (double)llTemp; } return hr; } STDMETHODIMP DiskQuotaControlDisp::get_DefaultQuotaThresholdText( BSTR *pThresholdText ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_DefaultQuotaThresholdText"))); TCHAR szValue[40]; HRESULT hr; hr = m_pQC->GetDefaultQuotaThresholdText(szValue, ARRAYSIZE(szValue)); if (SUCCEEDED(hr)) { *pThresholdText = SysAllocString(szValue); if (NULL == *pThresholdText) { hr = E_OUTOFMEMORY; } } return hr; } // // Dispatch property "DefaultQuotaLimit" (put) // // Sets the default quota limit on the volume. // STDMETHODIMP DiskQuotaControlDisp::put_DefaultQuotaLimit( double Limit ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_DefaultQuotaLimit"))); if (MAXLONGLONG < Limit) return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); // // No exception handling required. // DiskQuotaControl::SetDefaultQuotaLimit handles exceptions. // return m_pQC->SetDefaultQuotaLimit((LONGLONG)Limit); } // // Dispatch property "DefaultQuotaLimit" (get) // // Retrieves the default quota limit on the volume. // STDMETHODIMP DiskQuotaControlDisp::get_DefaultQuotaLimit( double *pLimit ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_DefaultQuotaLimit"))); LONGLONG llTemp; // // No exception handling required. // DiskQuotaControl::GetDefaultQuotaLimit handles exceptions. // HRESULT hr = m_pQC->GetDefaultQuotaLimit(&llTemp); if (SUCCEEDED(hr)) { *pLimit = (double)llTemp; } return hr; } STDMETHODIMP DiskQuotaControlDisp::get_DefaultQuotaLimitText( BSTR *pLimitText ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_DefaultQuotaLimitText"))); TCHAR szValue[40]; HRESULT hr = m_pQC->GetDefaultQuotaLimitText(szValue, ARRAYSIZE(szValue)); if (SUCCEEDED(hr)) { *pLimitText = SysAllocString(szValue); if (NULL == *pLimitText) { hr = E_OUTOFMEMORY; } } return hr; } STDMETHODIMP DiskQuotaControlDisp::put_UserNameResolution( UserNameResolutionConstants ResolutionType ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::put_UserNameResolution"))); if (dqResolveMaxValue < ResolutionType) { return E_INVALIDARG; } m_fOleAutoNameResolution = (DWORD)ResolutionType; return NOERROR; } STDMETHODIMP DiskQuotaControlDisp::get_UserNameResolution( UserNameResolutionConstants *pResolutionType ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::get_UserNameResolution"))); if (NULL == pResolutionType) return E_INVALIDARG; *pResolutionType = (UserNameResolutionConstants)m_fOleAutoNameResolution; return NOERROR; } // // Dispatch method "Initialize" // // Initializes the quota control object for a given path and // access mode. See DiskQuotaControl::Initialize for details. // STDMETHODIMP DiskQuotaControlDisp::Initialize( BSTR path, VARIANT_BOOL bReadWrite ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::Initialize"))); // // No exception handling required. // DiskQuotaControl::Initialize handles exceptions. // return m_pQC->Initialize(reinterpret_cast(path), VARIANT_TRUE == bReadWrite); } // // Dispatch method "AddUser" // // Adds new user quota record. // See DiskQuotaControl::AddUserName for details. // STDMETHODIMP DiskQuotaControlDisp::AddUser( BSTR LogonName, DIDiskQuotaUser **ppUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::AddUser"))); // // No exception handling required. // DiskQuotaControl::AddUserName handles exceptions. // PDISKQUOTA_USER pUser = NULL; HRESULT hr = m_pQC->AddUserName(reinterpret_cast(LogonName), m_fOleAutoNameResolution, &pUser); if (SUCCEEDED(hr)) { // // Retrieve the user object's IDispatch interface. // hr = pUser->QueryInterface(IID_IDispatch, (LPVOID *)ppUser); pUser->Release(); } return hr; } // // Dispatch method "DeleteUser" // // Marks a user quota record for deletion. // See DiskQuotaControl::DeleteUser for details. // STDMETHODIMP DiskQuotaControlDisp::DeleteUser( DIDiskQuotaUser *pUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::DeleteUser"))); HRESULT hr = E_INVALIDARG; if (NULL != pUser) { // // No exception handling required. // DiskQuotaControl::DeleteUser handles exceptions. // PDISKQUOTA_USER pUserToDelete = NULL; hr = pUser->QueryInterface(IID_IDiskQuotaUser, (LPVOID *)&pUserToDelete); if (SUCCEEDED(hr)) { hr = m_pQC->DeleteUser(pUserToDelete); pUserToDelete->Release(); } } return hr; } // // Dispatch method "FindUser" // // Locates a user quota entry based on the user's name strings. // Creates a corresponding user object and returns it to the caller. // See DiskQuotaControl::FindUserName for details. // STDMETHODIMP DiskQuotaControlDisp::FindUser( BSTR LogonName, DIDiskQuotaUser **ppUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::FindUser"))); // // No exception handling required. // DiskQuotaControl::FindUserName handles exceptions. // HRESULT hr = NOERROR; LPCWSTR pszName = reinterpret_cast(LogonName); PSID psid = NULL; PDISKQUOTA_USER pUser = NULL; if (ConvertStringSidToSid(pszName, &psid)) { hr = m_pQC->FindUserSid(psid, m_fOleAutoNameResolution, &pUser); LocalFree(psid); psid = NULL; } else { hr = m_pQC->FindUserName(pszName, &pUser); } if (SUCCEEDED(hr)) { DBGASSERT((NULL != pUser)); // // Query for the user's IDispatch interface and release the pointer // we received from FindUserName. // hr = pUser->QueryInterface(IID_IDispatch, (LPVOID *)ppUser); pUser->Release(); } return hr; } // // Dispatch method "InvalidateSidNameCache" // // Invalidates the SID/Name cache. // See DiskQuotaControl::InvalidateSidNameCache for details. // STDMETHODIMP DiskQuotaControlDisp::InvalidateSidNameCache( void ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::InvalidateSidNameCache"))); // // No exception handling required. // DiskQuotaControl::InvalidateSidNameCache handles exceptions. // return m_pQC->InvalidateSidNameCache(); } // // Dispatch method "GiveUserNameResolutionPriority" // // Promotes a user object to the front of the SID/Name resolver's input queue. // See DiskQuotaControl::GiveUserNameResolutionPriority. // STDMETHODIMP DiskQuotaControlDisp::GiveUserNameResolutionPriority( DIDiskQuotaUser *pUser ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::GiveUserNameResolutionPriority"))); HRESULT hr = E_INVALIDARG; if (NULL != pUser) { // // No exception handling required. // DiskQuotaControl::GiveUserNameResolutionPriority handles exceptions. // PDISKQUOTA_USER pUserToPromote = NULL; hr = pUser->QueryInterface(IID_IDiskQuotaUser, (LPVOID *)&pUserToPromote); if (SUCCEEDED(hr)) { hr = m_pQC->GiveUserNameResolutionPriority(pUserToPromote); pUserToPromote->Release(); } } return hr; } // // This function is called by an automation controller when a new enumerator is // required. In particular, Visual Basic calls it when it encounters a // "for each" loop. The name "_NewEnum" is hard-wired and can't change. // STDMETHODIMP DiskQuotaControlDisp::_NewEnum( IDispatch **ppEnum ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::_NewEnum"))); HRESULT hr = E_INVALIDARG; if (NULL != ppEnum) { try { // // Create a Collection object using the current settings for // name resolution. // DiskQuotaUserCollection *pCollection = new DiskQuotaUserCollection(m_pQC, m_fOleAutoNameResolution); hr = pCollection->Initialize(); if (SUCCEEDED(hr)) { // // The caller of _NewEnum (probably VB) wants the IEnumVARIANT // interface. // hr = pCollection->QueryInterface(IID_IEnumVARIANT, (LPVOID *)ppEnum); } else { delete pCollection; } } catch(CAllocException& e) { DBGERROR((TEXT("Insufficient memory exception"))); hr = E_OUTOFMEMORY; } } return hr; } // // Shutdown the SID/Name resolver. Note that this happens automatically // when the control object is destroyed. // STDMETHODIMP DiskQuotaControlDisp::ShutdownNameResolution( VOID ) { DBGTRACE((DM_CONTROL, DL_HIGH, TEXT("DiskQuotaControlDisp::ShutdownNameResolution"))); return m_pQC->ShutdownNameResolution(); } // // Given a logon name in SAM-compatible or UPN format, translate // the name to the corresponding account's SID. The returned SID is // formatted as a string. // STDMETHODIMP DiskQuotaControlDisp::TranslateLogonNameToSID( BSTR LogonName, BSTR *psid ) { NTDS ntds; BYTE sid[MAX_SID_LEN]; SID_NAME_USE eSidUse; DWORD cbSid = ARRAYSIZE(sid); HRESULT hr = ntds.LookupAccountByName(NULL, reinterpret_cast(LogonName), NULL, NULL, sid, &cbSid, &eSidUse); if (SUCCEEDED(hr)) { LPTSTR pszSid = NULL; if (ConvertSidToStringSid((PSID)sid, &pszSid)) { *psid = SysAllocString(pszSid); if (NULL == *psid) { hr = E_OUTOFMEMORY; } LocalFree(pszSid); pszSid = NULL; } else { hr = HRESULT_FROM_WIN32(GetLastError()); } } return hr; }