3508 lines
102 KiB
C++
3508 lines
102 KiB
C++
//+-------------------------------------------------------------------------
|
||
//
|
||
// File: idfsvol.cxx
|
||
//
|
||
// Contents: Implementation of IDfsVolume interface. This interface
|
||
// supports the administrative functions on DFS.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
//#include <ntos.h>
|
||
//#include <ntrtl.h>
|
||
//#include <nturtl.h>
|
||
//#include <dfsfsctl.h>
|
||
//#include <windows.h>
|
||
|
||
#include "headers.hxx"
|
||
#pragma hdrstop
|
||
|
||
#include "dfsmsrv.h"
|
||
#include "dfsm.hxx"
|
||
#include "cdfsvol.hxx"
|
||
#include "jnpt.hxx"
|
||
#include "dfsmwml.h"
|
||
|
||
VOID
|
||
ComputeNewEntryPath(
|
||
PWCHAR oldPath,
|
||
PWCHAR newPath,
|
||
PWCHAR childPath,
|
||
PWCHAR *childNewPath
|
||
);
|
||
|
||
NTSTATUS
|
||
MoveFileOrJP(
|
||
IN PCWSTR pwszSrcName,
|
||
IN PCWSTR pwszTgtName
|
||
);
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Method: AddReplicaToObj, private
|
||
//
|
||
// Synopsis: This method adds a replica info structure to the volume
|
||
// object and returns after that.
|
||
//
|
||
// Arguments: [pReplicaInfo] -- The ReplicaInfo structure.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::AddReplicaToObj(
|
||
PDFS_REPLICA_INFO pReplicaInfo
|
||
)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CDfsService *pService = NULL;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::AddReplicaToObj()\n"));
|
||
|
||
//
|
||
// First before we even set recoveryProperties we want to make sure
|
||
// that this service does not already exist in the ServiceList.
|
||
//
|
||
if ((dwErr == ERROR_SUCCESS) &&
|
||
(_DfsSvcList.GetService(pReplicaInfo, &pService) == ERROR_SUCCESS)) {
|
||
dwErr = NERR_DfsDuplicateService;
|
||
}
|
||
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
//
|
||
// Argument validation also takes place right in this constructor.
|
||
// Also the ServiceName etc. gets converted to an ORG based name in
|
||
// the constructor. So we dont need to worry about that at all.
|
||
//
|
||
pService = new CDfsService(pReplicaInfo, TRUE, &dwErr);
|
||
|
||
if (pService == NULL)
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Set the new service properties on the volume object. This method
|
||
// will return an error code if the service already exists on the
|
||
// volume object. No explicit checking need be done here.
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
pService->SetCreateTime();
|
||
|
||
dwErr = _DfsSvcList.SetNewService(pService);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR, "Failed to Set new replica %08lx\n",dwErr));
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::AddReplicaToObj() exit\n"));
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::AddReplica, public
|
||
//
|
||
// Synopsis: This function associates a new service with an existing
|
||
// Volume Object. A new service is defined by the ServiceName,
|
||
// StorageId, Type of Service, and sometimes an AddressQualifier.
|
||
// For this operation to succeed the service being added should
|
||
// be available. Unavailability of the service will result in
|
||
// failure of this method. This operation will be atomic as far
|
||
// as all information on the DC is concerned. Either the operation
|
||
// will succeed or no irrelevant information will be left on the
|
||
// DC. However, there are no guarantees made regarding the state
|
||
// of the service involved in the face of Network Failures and
|
||
// remote service crashes etc. If you attempt to add the same
|
||
// service name again it will return an error.
|
||
//
|
||
// Arguments: [pReplicaInfo] -- The ServiceInfo here. Look at docs for details
|
||
//
|
||
// Returns: ERROR_SUCCESS -- If the operation succeeded.
|
||
//
|
||
// ERROR_OUTOFMEMORY -- Unable to allocate memory for operation.
|
||
//
|
||
// NERR_DfsVolumeIsOffline -- The volume is offline, can't do
|
||
// AddReplica operation on it.
|
||
//
|
||
// NERR_DfsDuplicateService --
|
||
// If the service already exists on this volume.
|
||
//
|
||
// NERR_DfsVolumeDataCorrupt --
|
||
// If the volume object to which this
|
||
// service is being added is corrupt.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
DWORD
|
||
CDfsVolume::AddReplica(
|
||
PDFS_REPLICA_INFO pReplicaInfo,
|
||
ULONG fCreateOptions
|
||
)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
PWCHAR ErrorStrs[3];
|
||
CDfsService *pService = NULL;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::AddReplica()\n"));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::AddReplica(%ws,%ws,0x%x)\n",
|
||
pReplicaInfo->pwszServerName,
|
||
pReplicaInfo->pwszShareName);
|
||
#endif
|
||
|
||
if (_State == DFS_VOLUME_STATE_OFFLINE) {
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::AddReplica exit NERR_DfsVolumeIsOffline\n");
|
||
#endif
|
||
return( NERR_DfsVolumeIsOffline );
|
||
}
|
||
|
||
//
|
||
// First before we even set recoveryProperties we want to make sure
|
||
// that this service does not already exist in the ServiceList.
|
||
//
|
||
if (_DfsSvcList.GetService(pReplicaInfo, &pService) == ERROR_SUCCESS) {
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::AddReplica: returning NERR_DfsDuplicateService\n");
|
||
#endif
|
||
pService = NULL; // So we don't try to delete
|
||
dwErr = NERR_DfsDuplicateService; // it later...
|
||
}
|
||
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
//
|
||
// Argument validation also takes place right in this constructor.
|
||
// Also the ServiceName etc. gets converted to an ORG based name in
|
||
// the constructor. So we dont need to worry about that at all.
|
||
//
|
||
pService = new CDfsService(pReplicaInfo, TRUE, &dwErr);
|
||
|
||
if (pService == NULL)
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
pService->SetCreateTime();
|
||
|
||
RECOVERY_TEST_POINT(L"AddReplica", 1);
|
||
|
||
//
|
||
// This is where we want to do some STATE changes. Register the
|
||
// intent of doing an AddReplicaoperation and mention the service
|
||
// name and record that we are in the Start State. RECOVERY
|
||
// If we cant set recovery props then something is really wrong.
|
||
// The following constructor will throw an exception and we will
|
||
// deal with it appropriately.
|
||
//
|
||
|
||
dwErr = _Recover.SetOperationStart(
|
||
DFS_RECOVERY_STATE_ADD_SERVICE,
|
||
pService);
|
||
|
||
RECOVERY_TEST_POINT(L"AddReplica", 2);
|
||
|
||
//
|
||
// Set the new service properties on the volume object. This method
|
||
// will return an error code if the service already exists on the
|
||
// volume object. No explicit checking need be done here.
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
dwErr = _DfsSvcList.SetNewService(pService);
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// 433532, SetNewService already deletes pservice in case of
|
||
// failure. Dont use pservice, and set it to NULL so it does
|
||
// not get freed twice!
|
||
|
||
pService = NULL;
|
||
|
||
//LogMessage( DEB_TRACE,
|
||
// &(pService->GetServiceName()),
|
||
// 1,
|
||
// DFS_CANNOT_SET_SERVICE_PROPERTY_MSG);
|
||
}
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
//
|
||
// Now we need to change the state to UpdatedSvcList RECOVERY
|
||
//
|
||
RECOVERY_TEST_POINT(L"AddReplica", 3);
|
||
|
||
_Recover.SetOperStage(DFS_OPER_STAGE_SVCLIST_UPDATED);
|
||
|
||
RECOVERY_TEST_POINT(L"AddReplica", 4);
|
||
|
||
//
|
||
// Let us now ask the remote machine to create a local volume.
|
||
// If we fail to do this for ANY reason, we fail the operation.
|
||
//
|
||
|
||
dwErr = pService->CreateLocalVolume(&_peid, _EntryType);
|
||
|
||
//
|
||
// If we failed, it might be because the server's state is not
|
||
// consistent with our state. See if this is the case, and
|
||
//
|
||
|
||
//
|
||
// If we failed we need to delete the entry from the serviceList.
|
||
// We have to use the servicename from the DfsSvc class since that
|
||
// is the ORG based name. If we get an error here as well then
|
||
// we will return that error (dwErr2) since it becomes more relevant
|
||
// suddenly. Else we will return the error we got above from
|
||
// CreateLocalVolume which will be returning a proper error to us.
|
||
//
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
DWORD dwErr2 = _DfsSvcList.DeleteService(pService, FALSE);
|
||
PWCHAR ErrorStrs[1];
|
||
|
||
ErrorStrs[0] = (pService->GetServiceName());
|
||
if (dwErr2 != ERROR_SUCCESS) {
|
||
LogMessage( DEB_ERROR,
|
||
ErrorStrs,
|
||
1,
|
||
DFS_CANNOT_DELETE_SERVICE_PROPERTY_MSG);
|
||
|
||
dwErr = NERR_DfsVolumeDataCorrupt;
|
||
|
||
} else {
|
||
|
||
//
|
||
// DeleteService() deleted this instance for us, so set the
|
||
// pointer to NULL
|
||
//
|
||
|
||
pService = NULL;
|
||
|
||
}
|
||
}
|
||
} //CreateLocalVolumeDone OR FAILED.
|
||
|
||
//
|
||
// Now we set state to DONE on vol object. It is of no concern whether
|
||
// the operation succeeded or failed.
|
||
//
|
||
RECOVERY_TEST_POINT(L"AddReplica", 5);
|
||
|
||
_Recover.SetOperationDone();
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
//
|
||
// Now we update the PKT with the new service. We get an
|
||
// appropriate Error Code from UpdatePktEntry.
|
||
//
|
||
|
||
dwErr = UpdatePktEntry(NULL);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// Why should this fail at all.
|
||
// An EVENT here too maybe.
|
||
//
|
||
LogMessage( DEB_ERROR,
|
||
&(_peid.Prefix.Buffer),
|
||
1,
|
||
DFS_FAILED_UPDATE_PKT_MSG);
|
||
IDfsVolInlineDebOut((DEB_ERROR, "UpdPktEntFailed %08lx\n", dwErr));
|
||
ASSERT(L"UpdatePktEntry Failed in AddService - WHY?");
|
||
}
|
||
|
||
} //UpdatePktEntry Block.
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
_Recover.SetOperationDone();
|
||
if (pService != NULL)
|
||
delete pService;
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::AddReplica() exit\n"));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::AddReplica exit %d\n", dwErr);
|
||
#endif
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::RemoveReplicaFromObj, public
|
||
//
|
||
// Synopsis: This operation removes a replica from a volume object and
|
||
// returns after that.
|
||
//
|
||
// Arguments: [pwszServiceName] -- Name of server to remove
|
||
//
|
||
// Returns:
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
DWORD
|
||
CDfsVolume::RemoveReplicaFromObj(
|
||
IN LPWSTR pwszServiceName)
|
||
{
|
||
DWORD dwErr;
|
||
CDfsService *pSvc;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::RemoveReplicaFromObj(%ws)\n", pwszServiceName));
|
||
|
||
dwErr = _DfsSvcList.GetServiceFromPrincipalName( pwszServiceName, &pSvc );
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// See if this is the last service. In that case, we cannot
|
||
// permit anyone to delete it.
|
||
//
|
||
|
||
if (_DfsSvcList.GetServiceCount() == 1) {
|
||
dwErr = NERR_DfsCantRemoveLastServerShare;
|
||
}
|
||
|
||
//
|
||
// Now delete the service from the service list
|
||
//
|
||
dwErr = _DfsSvcList.DeleteService(pSvc);
|
||
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::RemoveReplicaFromObj() exit\n"));
|
||
|
||
return( dwErr );
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::RemoveReplica, public
|
||
//
|
||
// Synopsis: This operation dissociates a service from a volume in the DFS
|
||
// namespace. On the DC this merely involves modifying the service
|
||
// List on the Volume object. At the same time the service involved
|
||
// has to be notified of this change. This operation will fail if
|
||
// for some reason the service refuses to delete its knowledge
|
||
// regarding this volume or if there are network failures
|
||
// during this operation. Of course if we fail the operation due
|
||
// to Network failures note the fact that the operation at the
|
||
// remote service might have succeeded and network failed after
|
||
// that - in which case we may have an INCONSISTENCY (very easy to
|
||
// detect this one).
|
||
//
|
||
// Arguments: [pwszServiceName] -- The name of the service to be deleted
|
||
// from the volume object.
|
||
//
|
||
// Returns: DFS_S_SUCCESS -- If the operation succeeded.
|
||
//
|
||
// NERR_DfsNoSuchShare -- If the specified server\share is not
|
||
// a service for this volume.
|
||
//
|
||
// NERR_DfsCantRemoveLastServerShare -- If the specified
|
||
// server\share is the only service for this volume.
|
||
//
|
||
// NERR_DfsVolumeDataCorrupt -- If the volume object could not
|
||
// be read.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::RemoveReplica(
|
||
PDFS_REPLICA_INFO pReplicaInfo,
|
||
ULONG fDeleteOptions
|
||
)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
DWORD dwErr2 = ERROR_SUCCESS;
|
||
PWCHAR orgServiceName = NULL;
|
||
CDfsService *pDfsSvc;
|
||
PWCHAR ErrorStrs[3];
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::RemoveReplica()\n"));
|
||
|
||
dwErr = _DfsSvcList.GetService(pReplicaInfo, &pDfsSvc);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
LogMessage( DEB_TRACE,
|
||
nullPtr,
|
||
0,
|
||
DFS_SERVICE_DOES_NOT_EXIST_MSG);
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS &&
|
||
_EntryType & DFS_VOL_TYPE_REFERRAL_SVC &&
|
||
!(_EntryType & DFS_VOL_TYPE_INTER_DFS)) {
|
||
dwErr = NERR_DfsCantRemoveDfsRoot;
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// See if this is the last service. In that case, we cannot
|
||
// permit anyone to delete it.
|
||
//
|
||
|
||
if (_DfsSvcList.GetServiceCount() == 1) {
|
||
dwErr = NERR_DfsCantRemoveLastServerShare;
|
||
}
|
||
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Next we register the intent to delete a specific service by
|
||
// marking the ServiceName on the object. RECOVERY.
|
||
//
|
||
RECOVERY_TEST_POINT(L"RemoveReplica", 1);
|
||
|
||
dwErr = _Recover.SetOperationStart(DFS_RECOVERY_STATE_REMOVE_SERVICE,
|
||
pDfsSvc);
|
||
|
||
RECOVERY_TEST_POINT(L"RemoveReplica", 2);
|
||
|
||
//
|
||
// Now that we know that such a service is actually registered.
|
||
// Let us request the remote service to delete LVOL knowledge.
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
dwErr = pDfsSvc->DeleteLocalVolume(&_peid);
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// We assume that if we got an error here we are in big trouble
|
||
// and we back out the operation infact. The DeleteLocalVolume
|
||
// method would have already taken care of filtering out the
|
||
// relevant errors for us.
|
||
//
|
||
LogMessage( DEB_TRACE,
|
||
&(_peid.Prefix.Buffer),
|
||
1,
|
||
DFS_DELETE_VOLUME_FAILED_MSG);
|
||
|
||
//
|
||
// Since we got this error we assume that we could not find
|
||
// the service. However, that need not be the reason at all.
|
||
// Raid: 455283. Need to resolve this later on.
|
||
//
|
||
dwErr = NERR_DfsNoSuchShare;
|
||
}
|
||
}
|
||
|
||
if ((dwErr == ERROR_SUCCESS) || (fDeleteOptions & DFS_OVERRIDE_FORCE)) {
|
||
|
||
//
|
||
// Now we write out RecoveryState to DFS_OPER_STAGE_INFORMED_SERVICE
|
||
//
|
||
RECOVERY_TEST_POINT(L"RemoveReplica", 3);
|
||
|
||
_Recover.SetOperStage(DFS_OPER_STAGE_INFORMED_SERVICE);
|
||
|
||
RECOVERY_TEST_POINT(L"RemoveReplica", 4);
|
||
|
||
//
|
||
// Now write out the new service list
|
||
//
|
||
dwErr2 = _DfsSvcList.DeleteService(pDfsSvc);
|
||
|
||
if (dwErr2 != ERROR_SUCCESS) {
|
||
//
|
||
// This should never happen. We probably had some security
|
||
// problems. Do we now go and back out the Previous Step. Raid 455283
|
||
//
|
||
ErrorStrs[1] = pDfsSvc->GetServiceName();
|
||
ErrorStrs[0] = _peid.Prefix.Buffer;
|
||
LogMessage( DEB_ERROR,
|
||
ErrorStrs,
|
||
2,
|
||
DFS_CANNOT_DELETE_SERVICE_PROPERTY_MSG);
|
||
ASSERT(L"Deleting and existing service FAILED in RemRepl");
|
||
}
|
||
}
|
||
|
||
//
|
||
// Done. The operation is committed if the last stage of deleting
|
||
// service succeeded, else we dont want to remove the recovery property.
|
||
// If we never got to the last stage of DeleteService dwErr2 will be
|
||
// ERROR_SUCCESS.
|
||
//
|
||
RECOVERY_TEST_POINT(L"RemoveReplica", 5);
|
||
|
||
if (dwErr2 == ERROR_SUCCESS)
|
||
_Recover.SetOperationDone();
|
||
|
||
if ((dwErr == ERROR_SUCCESS) || (fDeleteOptions & DFS_OVERRIDE_FORCE)) {
|
||
//
|
||
// Now update the PKT as well.
|
||
//
|
||
dwErr = UpdatePktEntry(NULL);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// Something is really messed up if we got here.
|
||
//
|
||
LogMessage(DEB_ERROR, nullPtr, 0, DFS_FAILED_UPDATE_PKT_MSG);
|
||
IDfsVolInlineDebOut((DEB_ERROR,
|
||
"UpdPktEntry in RemRepl failed %08lx\n", dwErr));
|
||
ASSERT(L"UpdatePktEntry in RemoveRepl failed\n");
|
||
}
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::RemoveReplica() exit\n"));
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::Delete, public
|
||
//
|
||
// Synopsis: This method deletes the volume object and deletes all knowledge
|
||
// of this volume at all the services that were supporting this
|
||
// volume in the namespace. At the same time the exit point at the
|
||
// parent volume is deleted. All of the services that support this
|
||
// volume are also advised to delete all information regarding this
|
||
// volume. There is an additional restriction that there should
|
||
// be only one service associated with a volume to be able to call
|
||
// method. This operation has problems due to its distributed
|
||
// nature. The moment the service supporting this volume has been
|
||
// informed to delete its local volume knowledge this operation is
|
||
// committed. In the case of Network failures while talking to one
|
||
// of the services involved, this operation continues to go on. If
|
||
// any such errors are encountered they are reported to the caller
|
||
// though the operation is declared to be a success. Note that this
|
||
// operation does not delete the storage and has nothing to do with
|
||
// that aspect. By not confirming the deletion of all ExitPoint
|
||
// info anywhere this operation can directly introduce an
|
||
// inconsistency of TOO MANY EXIT Points. This inconsistency is
|
||
// well understood and easy to deal with.
|
||
//
|
||
// Arguments: None
|
||
//
|
||
// Returns: DFS_S_SUCCESS -- If all went well.
|
||
//
|
||
// NERR_DfsNotALeafVolume --
|
||
// An attempt was made to delete a volume which
|
||
// has child volumes and hence the operation failed
|
||
//
|
||
// NERR_DfsVolumeDataCorrupt --
|
||
// The volume object seems to be corrupt due to
|
||
// which this operation cannot proceed at all.
|
||
//
|
||
// NERR_DfsVolumeHasMultipleServers --
|
||
// This operation will not succeed if there is
|
||
// more than one service assoicated with the vol.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::Delete(
|
||
ULONG fDeleteOptions)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CDfsVolume *parent = NULL;
|
||
BOOLEAN InconsistencyPossible = FALSE;
|
||
BOOLEAN ParentInconsistency = FALSE;
|
||
CDfsService *pDfsSvc;
|
||
ULONG rState = 0;
|
||
ULONG count = 0;
|
||
PWCHAR ErrorStrs[3];
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::Delete()\n"));
|
||
|
||
if (NotLeafVolume())
|
||
dwErr = NERR_DfsNotALeafVolume;
|
||
|
||
//
|
||
// We want to make sure that this is not the root volume
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS &&
|
||
_EntryType & DFS_VOL_TYPE_REFERRAL_SVC &&
|
||
!(_EntryType & DFS_VOL_TYPE_INTER_DFS)) {
|
||
dwErr = NERR_DfsCantRemoveDfsRoot;
|
||
}
|
||
|
||
//
|
||
// We next want to make sure that there is only one service associated
|
||
// with this volume else we will fail this operation right now.
|
||
//
|
||
if (dwErr == ERROR_SUCCESS)
|
||
if (_DfsSvcList.GetServiceCount()>1)
|
||
dwErr = NERR_DfsVolumeHasMultipleServers;
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
if(dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// we are going to need to talk with the parent once the
|
||
// child is gone, so we get a handle to it now.
|
||
// If we fail to bind to parent. Then it probably means that
|
||
// this is a top level volume object and hence cannot be deleted.
|
||
// We return the error to the caller.
|
||
//
|
||
|
||
dwErr = GetParent(&parent);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
LogMessage(DEB_TRACE, nullPtr, 0, DFS_CANT_GET_PARENT_MSG);
|
||
dwErr = NERR_DfsVolumeDataCorrupt;
|
||
}
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Before we take off on our operation. We should setup the
|
||
// State variable on the Volume object indicating that we are
|
||
// deleting this volume from the namespace.
|
||
//
|
||
|
||
RECOVERY_TEST_POINT(L"DeleteVolume", 1);
|
||
|
||
dwErr = _Recover.SetOperationStart(DFS_RECOVERY_STATE_DELETE, NULL);
|
||
|
||
RECOVERY_TEST_POINT(L"DeleteVolume", 2);
|
||
|
||
//
|
||
// Now we inform the service to delete its LocalVolume knowledge.
|
||
//
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
pDfsSvc = _DfsSvcList.GetFirstService();
|
||
|
||
if (pDfsSvc != NULL) {
|
||
dwErr = pDfsSvc->DeleteLocalVolume(&_peid);
|
||
//
|
||
// If we got an error here, we dont really want to go on??
|
||
//
|
||
// When we move to DWORDs we might want to return exactly
|
||
// which service did not get updated etc.
|
||
//
|
||
}
|
||
}
|
||
}
|
||
|
||
if ((dwErr == ERROR_SUCCESS) || (fDeleteOptions & DFS_OVERRIDE_FORCE)) {
|
||
|
||
//
|
||
// Now we set Recovery State to new value if we have informed
|
||
// all related services.
|
||
//
|
||
RECOVERY_TEST_POINT(L"DeleteVolume", 3);
|
||
|
||
_Recover.SetOperStage(DFS_OPER_STAGE_INFORMED_SERVICE);
|
||
|
||
RECOVERY_TEST_POINT(L"DeleteVolume", 4);
|
||
|
||
//
|
||
// We need to tell each service of the parent volume to delete the
|
||
// exit point. I think at this point we have to force the operation
|
||
// through. Even if we are not able to delete some exit points it
|
||
// is OK. We will return a success error code but will indicate
|
||
// that there could potentially be a possible inconsistency in the
|
||
// parent volume's knowledge.
|
||
//
|
||
|
||
pDfsSvc = parent->_DfsSvcList.GetFirstService();
|
||
while (pDfsSvc != NULL) {
|
||
|
||
//
|
||
// Ignore error codes from this. We dont care if we cant
|
||
// make some machine to delete the exit point.
|
||
// The possible things that could happen here are that the
|
||
// remote machine will say that it cannot delete the exit
|
||
// point since the GUIDs dont match OR the exit point does
|
||
// not exist (It got deleted due to reconciliation) or
|
||
// Network failures themselves. In all cases we dont care.
|
||
// We will return a status code which indicates that one
|
||
// of the parent services misbehaved and admin needs to
|
||
// make sure that nothing is wrong.
|
||
//
|
||
|
||
dwErr = pDfsSvc->DeleteExitPoint(&_peid, _EntryType);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
PWCHAR ErrorStrs[1];
|
||
ParentInconsistency = TRUE;
|
||
ErrorStrs[0] = pDfsSvc->GetServiceName();
|
||
LogMessage(DEB_ERROR,
|
||
ErrorStrs,
|
||
1,
|
||
DFS_CANT_DELETE_EXIT_POINT_MSG);
|
||
IDfsVolInlineDebOut((DEB_ERROR,
|
||
"ErrorCode from DeleteExitPoint %08lx\n",
|
||
dwErr));
|
||
}
|
||
dwErr = ERROR_SUCCESS;
|
||
pDfsSvc = parent->_DfsSvcList.GetNextService(pDfsSvc);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// We want to get rid of the parent pointer if we no longer need it.
|
||
//
|
||
if (parent != NULL) {
|
||
parent->Release();
|
||
parent = NULL;
|
||
}
|
||
|
||
//
|
||
// If the operation failed we want to set the properties to done else
|
||
// we are anyway going to go in and delete this object itself.
|
||
//
|
||
RECOVERY_TEST_POINT(L"DeleteVolume", 5);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
_Recover.SetOperationDone();
|
||
}
|
||
|
||
//
|
||
// If we successfully managed to do the deletion then we go ahead
|
||
// and delete the volume object from disk. Note that even if
|
||
// Network failures occur while talking to each of the services
|
||
// above we will go on. Once this deletion happens the operation is
|
||
// commited. Updating the PKT is not a part of the commit point.
|
||
//
|
||
|
||
if ((dwErr == ERROR_SUCCESS) || (fDeleteOptions & DFS_OVERRIDE_FORCE)) {
|
||
DeleteObject();
|
||
//
|
||
// We need to make this instance of the VolumeObject invalid or
|
||
// so that no one can do any more operations on this volume.
|
||
//
|
||
_Deleted = TRUE;
|
||
}
|
||
|
||
//
|
||
// Now we go and update the PKT with the deletion information.
|
||
// Once again we ignore errors here except to return an
|
||
// INCONSISTENCY status code.
|
||
//
|
||
if ((dwErr == ERROR_SUCCESS) || (fDeleteOptions & DFS_OVERRIDE_FORCE)) {
|
||
|
||
dwErr = DeletePktEntry(&_peid);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
LogMessage(
|
||
DEB_ERROR, &(_peid.Prefix.Buffer), 1,
|
||
DFS_CANT_DELETE_ENTRY_MSG);
|
||
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR, "DelPktEnt Failed%08lx\n", dwErr));
|
||
|
||
ASSERT(L"DeletePktEntry Failed in Deletion of Volume");
|
||
|
||
dwErr = ERROR_SUCCESS;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
if (parent != NULL)
|
||
parent->Release();
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::Delete() exit\n"));
|
||
|
||
return( dwErr );
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Method: CDfsVolume::CreateChildX, public
|
||
//
|
||
// Synopsis: This is one way of creating new volume objects.
|
||
// This operation involves the creation of the volume object,
|
||
// Updating the parent's knowledge regarding the new exit point
|
||
// and creating the exit point. The parent volume could have
|
||
// multiple replicas. In this case all the replicas are informed
|
||
// of the changes and are advised to create the relevant knowledge.
|
||
// This operation will roll back in the event of a failure during
|
||
// the operation before it is complete. If all of the parent's
|
||
// cannot create the exit point information then this operation
|
||
// will not proceed. Every attempt will be made to delete all
|
||
// information that was created on other replicas but no guarantees
|
||
// are made about this. However, all local knowledge will be
|
||
// deleted and knowledge synchronisation is supposed to eliminate
|
||
// any problems that might have arised because of inconsistencies
|
||
// created by this operation.
|
||
//
|
||
// Arguments: [pwszChildName] -- The last component of child Vol object Name.
|
||
//
|
||
// [pwszEntryPath] -- The entry path for the new volume in the namespace
|
||
//
|
||
// [ulVolType] -- The type of the volume.
|
||
// Values in the IDL file for this interface.
|
||
//
|
||
// [pChild] -- This is where the IDfsVolume reference is
|
||
// returned to caller.
|
||
//
|
||
// [pComment] -- Comment for this volume.
|
||
//
|
||
// Returns: ERROR_SUCCESS -- If the operation succeeded with NO problems.
|
||
//
|
||
// ERROR_INVALID_PARAMETER -- The child volume's prefix is not
|
||
// hierarchically subordinate to this volume's prefix.
|
||
//
|
||
// NERR_DfsVolumeIsInterDfs -- The volume is an inter-dfs one;
|
||
// can't create a child volume.
|
||
//
|
||
// NERR_DfsNotSupportedInServerDfs -- Can't have more than one
|
||
// level of hierarchy in Server Dfs.
|
||
//
|
||
// NERR_DfsLeafVolume -- This is a downlevel or leaf volume,
|
||
// can't create a child here.
|
||
//
|
||
// NERR_DfsCantCreateJunctionPoint -- Unable to create a
|
||
// junction point on any of the server-shares
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::CreateChildX(
|
||
WCHAR * childName,
|
||
WCHAR * pwszPrefix,
|
||
ULONG ulVolType,
|
||
LPWSTR pwszComment,
|
||
CDfsVolume **ppChild)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CDfsVolume *pChild = NULL;
|
||
CDfsService *pDfsSvc = NULL;
|
||
BOOLEAN CreatedExitPt = FALSE;
|
||
BOOLEAN InconsistencyPossible = FALSE;
|
||
PWCHAR pwszSuffix = NULL;
|
||
PWCHAR ErrorStrs[3];
|
||
ULONG ulen = 0;
|
||
LPWSTR wszChildShortName;
|
||
BOOLEAN GotShortName = FALSE;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::CreateChildX(%ws,%ws,0x%x,%s)\n",
|
||
childName, pwszPrefix, ulVolType, pwszComment));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("DfsVolume::CreateChildX(%ws,%ws,0x%x,%ws)\n",
|
||
childName,
|
||
pwszPrefix,
|
||
ulVolType,
|
||
pwszComment);
|
||
#endif
|
||
|
||
//
|
||
// Check for validity of Type field and for LEAF volume etc. Only the
|
||
// allowed TypeBits should be set and also one of the Three required Types
|
||
// bits should be set.
|
||
//
|
||
|
||
//
|
||
// We check to make sure that the prefix of the child volume meets
|
||
// our heirarchy requirements. It must be strictly a child of this
|
||
// volume's prefix, and must not cross inter-dfs boundaries.
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
if (_EntryType & DFS_VOL_TYPE_INTER_DFS) {
|
||
|
||
dwErr = NERR_DfsVolumeIsInterDfs;
|
||
|
||
} else if (ulDfsManagerType == DFS_MANAGER_SERVER &&
|
||
((_EntryType & DFS_VOL_TYPE_REFERRAL_SVC) == 0)) {
|
||
|
||
dwErr = NERR_DfsVolumeAlreadyExists;
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::CreateChildX: dwErr = NERR_DfsVolumeAlreadyExists\n");
|
||
#endif
|
||
|
||
} else if (_wcsnicmp(_peid.Prefix.Buffer, pwszPrefix,
|
||
wcslen(_peid.Prefix.Buffer)) != 0) {
|
||
|
||
dwErr = ERROR_INVALID_PARAMETER;
|
||
#if DBG
|
||
if (DfsSvcVerbose) {
|
||
DbgPrint("CDfsVolume::CreateChildX(1): [%ws]!=[%ws] (1st %d chars)\n",
|
||
_peid.Prefix.Buffer,
|
||
pwszPrefix,
|
||
wcslen(_peid.Prefix.Buffer));
|
||
DbgPrint("CDfsVolume::CreateChildX(1): dwErr = ERROR_INVALID_PARAMETER\n");
|
||
}
|
||
#endif
|
||
|
||
} else {
|
||
|
||
//
|
||
// In this case we want to make sure that the new EntryPath
|
||
// is also greater than the entrypath for this volume, and that
|
||
// it is a valid Win32 Path name
|
||
//
|
||
|
||
LPWSTR wszExitPath = &pwszPrefix[_peid.Prefix.Length/sizeof(WCHAR)];
|
||
|
||
if (wcslen(pwszPrefix) <= wcslen(_peid.Prefix.Buffer)) {
|
||
|
||
if (_wcsicmp(pwszPrefix,_peid.Prefix.Buffer) == 0) {
|
||
dwErr = NERR_DfsVolumeAlreadyExists;
|
||
} else {
|
||
dwErr = ERROR_INVALID_PARAMETER;
|
||
}
|
||
#if DBG
|
||
if (DfsSvcVerbose) {
|
||
DbgPrint("CDfsVolume::CreateChildX(2): dwErr = %d\n");
|
||
DbgPrint(" pwszPrefix=[%ws],_peid.Prefix.Buffer=[%ws]\n",
|
||
pwszPrefix,
|
||
_peid.Prefix.Buffer);
|
||
}
|
||
#endif
|
||
|
||
} else if (!IsValidWin32Path( wszExitPath )) {
|
||
|
||
dwErr = ERROR_BAD_PATHNAME;
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::CreateChildX: dwErr = ERROR_BAD_PATHNAME\n");
|
||
#endif
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// We first try to create the volume object. So that we can use
|
||
// it to go about our state management stuff for recovery purposes.
|
||
// It automatically sets recoveryState to being OPER_START etc.
|
||
// It will also set a NULL service list for now. This method will
|
||
// also pick a GUID for us. We dont need to pass it one.
|
||
//
|
||
|
||
dwErr = CreateChildPartition(
|
||
childName,
|
||
ulVolType,
|
||
pwszPrefix,
|
||
pwszComment,
|
||
NULL,
|
||
NULL,
|
||
&pChild);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR, "Unable to create child %ws\n under : %ws\n",
|
||
pwszPrefix, _peid.Prefix.Buffer));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
RECOVERY_TEST_POINT(L"CreateChildX", 1);
|
||
|
||
//
|
||
// We next try to create exit points on each of the services for
|
||
// the parent volume. However, we need to create an exit point on
|
||
// only one of the services for the parent volume. We rely on
|
||
// Replication to reconcile everything to other machines.
|
||
//
|
||
|
||
pDfsSvc = _DfsSvcList.GetFirstService();
|
||
|
||
while (pDfsSvc!=NULL) {
|
||
|
||
dwErr = pDfsSvc->CreateExitPoint(
|
||
&(pChild->_peid),
|
||
ulVolType);
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// This would have updated the short name. Save it...
|
||
//
|
||
|
||
CreatedExitPt = TRUE;
|
||
|
||
if (!GotShortName) {
|
||
|
||
DWORD dwErrUpdatedShortName;
|
||
|
||
dwErrUpdatedShortName = pChild->SaveShortName();
|
||
|
||
if (dwErrUpdatedShortName == ERROR_SUCCESS) {
|
||
|
||
GotShortName = TRUE;
|
||
|
||
IDfsVolInlineDebOut((
|
||
DEB_TRACE, "Setting short name to: %ws\n",
|
||
pChild->_peid.ShortPrefix.Buffer));
|
||
|
||
} else {
|
||
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR, "Error %08lx setting short name\n",
|
||
dwErrUpdatedShortName));
|
||
}
|
||
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
InconsistencyPossible = TRUE;
|
||
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR, "Failed to CreateExPt %08lx %ws\n",
|
||
dwErr, pChild->_peid.Prefix.Buffer));
|
||
|
||
}
|
||
|
||
dwErr = ERROR_SUCCESS;
|
||
|
||
pDfsSvc = _DfsSvcList.GetNextService(pDfsSvc);
|
||
|
||
}
|
||
|
||
//
|
||
// If we did create an exit point on atleast one machine we can go on.
|
||
// Also we can set the state to DONE!!
|
||
//
|
||
|
||
if (CreatedExitPt == TRUE) {
|
||
|
||
//
|
||
// We need to ClearUp Recovery Properties here!!
|
||
//
|
||
|
||
RECOVERY_TEST_POINT(L"CreateChildX", 2);
|
||
|
||
pChild->_Recover.SetOperationDone();
|
||
|
||
} else {
|
||
|
||
//
|
||
// We have to delete the object that we created. dwErr already
|
||
// has an error code in it. Let that go by us. Remember however,
|
||
// the exit point itself never got created as far as the DC is
|
||
// concerned and hence makes no sense to try to undo that stuff.
|
||
//
|
||
LogMessage(
|
||
DEB_ERROR,nullPtr,0,DFS_CANT_CREATE_ANY_EXIT_POINT_MSG);
|
||
|
||
pChild->DeleteObject();
|
||
|
||
dwErr = NERR_DfsCantCreateJunctionPoint;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now we merely need to update the PKT with the relevant information.
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
dwErr = pChild->CreateSubordinatePktEntry(NULL, &_peid, FALSE);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
LogMessage(
|
||
DEB_ERROR, &_peid.Prefix.Buffer, 1,
|
||
DFS_CANT_CREATE_SUBORDINATE_ENTRY_MSG);
|
||
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR, "Failed CreateSubordPktEntry %08lx\n", dwErr));
|
||
|
||
ASSERT(L"CreateSubordinateEntry Failed in CreateChild");
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
if (pChild != NULL)
|
||
pChild->Release();
|
||
|
||
*ppChild = NULL;
|
||
|
||
} else {
|
||
|
||
*ppChild = pChild;
|
||
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::CreateChildX() exit %d\n", dwErr));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::CreateChildX exit %d\n", dwErr);
|
||
#endif
|
||
|
||
return((dwErr));
|
||
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::CreateChild, public
|
||
//
|
||
// Synopsis: This is one way of creating new volume objects.
|
||
// This operation involves the creation of the volume object,
|
||
// Updating the parent's knowledge regarding the new exit point
|
||
// and creating the exit point. The parent volume could have
|
||
// multiple replicas. In this case all the replicas are informed
|
||
// of the changes and are advised to create the relevant knowledge.
|
||
// This operation will roll back in the event of a failure during
|
||
// the operation before it is complete. Every attempt will be made
|
||
// to delete all information that was created on other replicas
|
||
// but no guarantees are made about this. However, all local
|
||
// knowledge will be deleted and knowledge synchronisation is
|
||
// supposed to eliminate any problems that might have arised
|
||
// because of inconsistencies created by this operation. This
|
||
// operation also associates a SERVICE with the volume that it
|
||
// creates (unlike the CreateChild/CreateInActiveVolume) operation.
|
||
// Hence it also needs to communicate with the relevant service.
|
||
//
|
||
// Arguments: [pwszEntryPath] -- The entry path for the new volume in the namespace
|
||
// This entry path is relative to the parent's entrypath.
|
||
//
|
||
// [ulVolType] -- The type of the volume. Look in dfsh.idl
|
||
//
|
||
// [pReplInfo] -- The ServiceInfo for the first replica.
|
||
//
|
||
// [pChild] -- This is where the IDfsVolume reference is
|
||
// returned to caller.
|
||
//
|
||
// [pwszComment] -- Comment for this volume.
|
||
//
|
||
// Returns: Error code from CreateChildX() or AddReplica().
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
DWORD
|
||
CDfsVolume::CreateChild(
|
||
LPWSTR pwszPrefix,
|
||
ULONG ulVolType,
|
||
PDFS_REPLICA_INFO pReplicaInfo,
|
||
PWCHAR pwszComment,
|
||
ULONG fCreateOptions)
|
||
{
|
||
CDfsVolume *pChild;
|
||
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::CreateChild(%ws,0x%x,%ws,0x%x)\n",
|
||
pwszPrefix, ulVolType, pwszComment, fCreateOptions));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::CreateChild(%ws,0x%x,%ws,%ws,%ws,0x%x)\n",
|
||
pwszPrefix,
|
||
ulVolType,
|
||
pReplicaInfo->pwszServerName,
|
||
pReplicaInfo->pwszShareName,
|
||
pwszComment,
|
||
fCreateOptions);
|
||
#endif
|
||
|
||
|
||
if (VolumeOffLine()) {
|
||
|
||
return( NERR_DfsVolumeIsOffline );
|
||
}
|
||
|
||
//
|
||
// I will cheat for now but will come back and fix this later on. Raid 455283
|
||
// At this place I basically need to have a recovery mechanism to recover
|
||
// in between the operations etc. The entire operation should be done or
|
||
// undone. In the present scheme only the two suboperations below will
|
||
// be in some sense "atomic" but not this entire operation itself.
|
||
//
|
||
|
||
dwErr = CreateChildX(
|
||
NULL, // Dont give any child name.
|
||
pwszPrefix,
|
||
ulVolType,
|
||
pwszComment,
|
||
&pChild);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
return((dwErr));
|
||
}
|
||
|
||
//
|
||
// Now let us go ahead and associate a service with the volume object that
|
||
// we just created.
|
||
//
|
||
dwErr = pChild->AddReplica(pReplicaInfo, fCreateOptions);
|
||
|
||
//
|
||
// If AddServic failed then we have to delete the object itself and
|
||
// backout the entire operation.
|
||
//
|
||
|
||
if (dwErr != ERROR_SUCCESS)
|
||
pChild->Delete(DFS_OVERRIDE_FORCE);
|
||
|
||
pChild->Release();
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::CreateChild() exit\n"));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint("CDfsVolume::CreateChild exit %d\n", dwErr);
|
||
#endif
|
||
|
||
return((dwErr));
|
||
}
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::Move, public
|
||
//
|
||
// Synopsis: This operation moves a volume from one place in the namespace
|
||
// to another point in the namespace. Here we need the
|
||
// volume Name and the new entry Path. The old entryPath is
|
||
// already available to us since we can get it from the properties
|
||
// on the volume object. The volume to be moved should be a leaf
|
||
// volume (should have no children) else this operation will
|
||
// return an Error Code.
|
||
//
|
||
// Arguments: [pwszNewPrefix] -- The new EntryPath for this volume. This
|
||
// should be absolute path.
|
||
//
|
||
// Returns: ERROR_SUCCESS -- The operation succeeded.
|
||
//
|
||
// ERROR_INVALID_PARAMETER -- If pwszNewPrefix is a
|
||
// hierarchically below the current prefix!
|
||
//
|
||
// NERR_DfsVolumeAlreadyExists -- A Dfs volume with the same
|
||
// prefix as pwszNewPrefix already exists
|
||
//
|
||
// Error from various IDfsvol methods.
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
DWORD
|
||
CDfsVolume::Move(
|
||
LPWSTR pwszNewPrefix
|
||
)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::Move(%ws)\n", pwszNewPrefix));
|
||
|
||
CDfsVolume *pNewParentVol = NULL;
|
||
CDfsVolume *pChildVol = NULL;
|
||
CDfsVolume *pOldParentVol = NULL;
|
||
|
||
BOOLEAN CreatedExitPt = FALSE;
|
||
CDfsService *pService;
|
||
CDfsService *pNextService;
|
||
DFS_PKT_ENTRY_ID NewId;
|
||
|
||
LPWSTR pwszChildName = NULL;
|
||
LPWSTR pwszNewParentPrefix = NULL;
|
||
LPWSTR pwszNewParentObject = NULL;
|
||
ULONG volPrefixLen, prefixLen;
|
||
|
||
|
||
|
||
if (VolumeOffLine()) {
|
||
return(NERR_DfsVolumeIsOffline);
|
||
}
|
||
|
||
if (NotLeafVolume()) {
|
||
return(NERR_DfsNotALeafVolume);
|
||
}
|
||
|
||
//
|
||
// Get a hold of the new parent, and check the validity of pwszNewPrefix
|
||
// in the process
|
||
//
|
||
|
||
prefixLen = wcslen( pwszNewPrefix );
|
||
pwszNewParentPrefix = new WCHAR[prefixLen + 1];
|
||
prefixLen *= sizeof(WCHAR);
|
||
|
||
if (pwszNewParentPrefix == NULL) {
|
||
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
} else {
|
||
|
||
wcscpy( pwszNewParentPrefix, pwszNewPrefix );
|
||
|
||
dwErr = pDfsmStorageDirectory->GetObjectForPrefix(
|
||
pwszNewParentPrefix,
|
||
FALSE,
|
||
&pwszNewParentObject,
|
||
&volPrefixLen);
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
pwszNewParentPrefix[ volPrefixLen/sizeof(WCHAR) ] =
|
||
UNICODE_NULL;
|
||
if (prefixLen == volPrefixLen) {
|
||
dwErr = NERR_DfsVolumeAlreadyExists;
|
||
} else {
|
||
if (!_wcsnicmp(pwszNewParentPrefix,
|
||
_peid.Prefix.Buffer,
|
||
_peid.Prefix.Length/sizeof(WCHAR)))
|
||
{
|
||
//
|
||
// This means that we are trying to move somewhere
|
||
// below where we already are in the namespace. This
|
||
// is total nonsense. Return right now.
|
||
//
|
||
dwErr = ERROR_INVALID_PARAMETER;
|
||
|
||
}
|
||
}
|
||
|
||
} else {
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR, "Failed to FindVolPrefix %08lx %ws\n",
|
||
dwErr, pwszNewParentPrefix));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Instantiate the parent and cleanup strings used to get parent...
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
pNewParentVol = new CDfsVolume();
|
||
|
||
if (pNewParentVol != NULL) {
|
||
|
||
dwErr = pNewParentVol->LoadNoRegister(
|
||
pwszNewParentObject,
|
||
0);
|
||
|
||
} else {
|
||
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
delete [] pwszNewParentPrefix;
|
||
|
||
if (pwszNewParentObject != NULL) {
|
||
delete [] pwszNewParentObject;
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Now that we have a handle on the appropriate volume object we
|
||
// can go ahead and perform the move.
|
||
//
|
||
|
||
dwErr = pNewParentVol->CreateChildPartition(
|
||
NULL,
|
||
_EntryType,
|
||
pwszNewPrefix,
|
||
_pwszComment,
|
||
&_peid.Uid,
|
||
NULL,
|
||
&pChildVol);
|
||
|
||
delete [] pwszChildName;
|
||
|
||
RECOVERY_TEST_POINT(L"Move", 1);
|
||
|
||
}
|
||
|
||
//
|
||
// If we succeeded in creating the object then we need to go and
|
||
// set the service List appropriately. Note that we will not
|
||
// set any recovery properties for now. We will exploit the recovery
|
||
// props setup by CreateChildPartition.
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
pService = _DfsSvcList.GetFirstService();
|
||
while (pService != NULL) {
|
||
pNextService = _DfsSvcList.GetNextService(pService);
|
||
_DfsSvcList.RemoveService(pService);
|
||
dwErr = pChildVol->_DfsSvcList.SetNewService(pService);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Failed to Set SvcProp %08lx\n",dwErr));
|
||
break;
|
||
}
|
||
pService = pNextService;
|
||
}
|
||
|
||
//
|
||
// Now we need to carefully remove the services from inmemory
|
||
// instantiation of the new pChildVol
|
||
//
|
||
pService = pChildVol->_DfsSvcList.GetFirstService();
|
||
while (pService != NULL) {
|
||
pNextService = pChildVol->_DfsSvcList.GetNextService(pService);
|
||
pChildVol->_DfsSvcList.RemoveService(pService);
|
||
_DfsSvcList.InsertNewService(pService);
|
||
pService = pNextService;
|
||
}
|
||
|
||
//
|
||
// If we failed to set service list we delete the object and we
|
||
// get out.
|
||
//
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
pChildVol->DeleteObject();
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If we succeeded in creating the new object we go on to create the
|
||
// New ExitPoint
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Let us switch to new EntryPath. We want to avoid mem allocations
|
||
// here itself.
|
||
//
|
||
NewId = _peid;
|
||
NewId.Prefix.Length = wcslen(pwszNewPrefix)*sizeof(WCHAR);
|
||
NewId.Prefix.MaximumLength = NewId.Prefix.Length + sizeof(WCHAR);
|
||
NewId.Prefix.Buffer = pwszNewPrefix;
|
||
|
||
//
|
||
// Now we can create the exit point at the appropriate place.
|
||
//
|
||
pService = pNewParentVol->_DfsSvcList.GetFirstService();
|
||
ASSERT(pService != NULL);
|
||
|
||
while (pService != NULL) {
|
||
dwErr = pService->CreateExitPoint(&NewId, _EntryType);
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
CreatedExitPt = TRUE;
|
||
}
|
||
pService = pNewParentVol->_DfsSvcList.GetNextService(pService);
|
||
}
|
||
|
||
if (CreatedExitPt == TRUE) {
|
||
//
|
||
// This operation is committed now and we can set the
|
||
// Recovery Properties on the new object to DONE.
|
||
//
|
||
//
|
||
// Now before we set the recovery Properties to be done on the
|
||
// new object. We have to set the appropriate recovery props
|
||
// on the current object so that we can force this operation
|
||
// forward.
|
||
//
|
||
dwErr = _Recover.SetOperationStart(DFS_RECOVERY_STATE_MOVE, NULL);
|
||
|
||
//
|
||
// Now we set the recovery properties on the new object to
|
||
// DONE since there is nothing that needs to be done over there
|
||
// if we fail now. The only recovery (or roll forward) code will
|
||
// be triggered off this volume object itself.
|
||
//
|
||
RECOVERY_TEST_POINT(L"Move", 2);
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
pChildVol->_Recover.SetOperationDone();
|
||
}
|
||
}
|
||
else {
|
||
//
|
||
// Cant go on with this operation so let us get rid of the
|
||
// object that we created.
|
||
//
|
||
IDfsVolInlineDebOut((DEB_TRACE,
|
||
"Unable to create Exit Pt %ws for Move\n",
|
||
pwszNewPrefix));
|
||
pChildVol->DeleteObject();
|
||
dwErr = NERR_DfsCantCreateJunctionPoint;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Now if we passed the last stage then there is no stopping in this
|
||
// operation anymore.
|
||
//
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
//
|
||
// Let us get to the old parent and delete the exit point first.
|
||
//
|
||
dwErr = GetParent(&pOldParentVol);
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
pService = pOldParentVol->_DfsSvcList.GetFirstService();
|
||
ASSERT(pService != NULL);
|
||
//
|
||
// We ignore all errors here.
|
||
//
|
||
while(pService != NULL) {
|
||
dwErr = pService->DeleteExitPoint(&_peid, _EntryType);
|
||
pService = pOldParentVol->_DfsSvcList.GetNextService(pService);
|
||
}
|
||
dwErr = ERROR_SUCCESS;
|
||
}
|
||
else {
|
||
IDfsVolInlineDebOut((DEB_ERROR,
|
||
"Unable to get to the parent vol for %ws\n",
|
||
_pwzFileName));
|
||
}
|
||
RECOVERY_TEST_POINT(L"Move", 3);
|
||
//
|
||
// Now we merely need to do a modify Prefix operation on the current
|
||
// volume's services.
|
||
//
|
||
pService = _DfsSvcList.GetFirstService();
|
||
ASSERT(pService != NULL);
|
||
while (pService != NULL) {
|
||
dwErr = pService->ModifyPrefix(&NewId);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((DEB_ERROR,
|
||
"Unable to Modify prefix to %ws %08lx\n",
|
||
NewId.Prefix.Buffer, dwErr));
|
||
}
|
||
pService = _DfsSvcList.GetNextService(pService);
|
||
}
|
||
dwErr = ERROR_SUCCESS;
|
||
|
||
//
|
||
// We can now delete this object itself.
|
||
//
|
||
DeleteObject();
|
||
_Deleted = FALSE;
|
||
|
||
//
|
||
// We now need to change the _pwzFileName of this idfsvol to match
|
||
// the new child that we have created. Also need to change E.P.
|
||
//
|
||
ULONG uLen = wcslen(pChildVol->_pwzFileName);
|
||
|
||
if ((uLen > wcslen(_pwzFileName)) && (uLen > MAX_PATH)) {
|
||
if (_pwzFileName != _FileNameBuffer)
|
||
delete [] _pwzFileName;
|
||
_pwzFileName = new WCHAR[uLen + 1];
|
||
}
|
||
wcscpy(_pwzFileName, pChildVol->_pwzFileName);
|
||
|
||
//
|
||
// Now the entrypath prefix.
|
||
//
|
||
uLen = NewId.Prefix.Length;
|
||
if (uLen > _peid.Prefix.Length) {
|
||
if (_peid.Prefix.Buffer != _EntryPathBuffer)
|
||
delete [] _peid.Prefix.Buffer;
|
||
|
||
if (uLen >= MAX_PATH)
|
||
_peid.Prefix.Buffer = new WCHAR[(uLen+1)];
|
||
else
|
||
_peid.Prefix.Buffer = _EntryPathBuffer;
|
||
}
|
||
_peid.Prefix.Length = (USHORT)uLen;
|
||
_peid.Prefix.MaximumLength = _peid.Prefix.Length + sizeof(WCHAR);
|
||
wcscpy(_peid.Prefix.Buffer, NewId.Prefix.Buffer);
|
||
|
||
//
|
||
// Now we get the IStorage's etc. over to this idfsvol. We steal.
|
||
//
|
||
ASSERT((_pStorage == NULL) &&
|
||
(_Recover._pPSStg == NULL) &&
|
||
(_DfsSvcList._pPSStg == NULL));
|
||
|
||
_pStorage = pChildVol->_pStorage;
|
||
pChildVol->_pStorage = NULL;
|
||
_Recover._pPSStg = pChildVol->_Recover._pPSStg;
|
||
_DfsSvcList._pPSStg = pChildVol->_DfsSvcList._pPSStg;
|
||
pChildVol->_Recover._pPSStg = NULL;
|
||
pChildVol->_DfsSvcList._pPSStg = NULL;
|
||
|
||
//
|
||
// Copy over the child's registration id, so we can revoke that
|
||
// as well. We need to revoke it because it has been registered
|
||
// with the child's CDfsVol address.
|
||
//
|
||
|
||
_dwRotRegistration = pChildVol->_dwRotRegistration;
|
||
pChildVol->_dwRotRegistration = NULL;
|
||
|
||
//
|
||
// Now we have to update the DC's PKT entry itself.
|
||
//
|
||
dwErr = UpdatePktEntry(NULL);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((DEB_ERROR,
|
||
"Unable to updatePkt entry %08lx for %ws\n",
|
||
dwErr, pChildVol->_peid.Prefix.Buffer));
|
||
}
|
||
|
||
}
|
||
|
||
if (pNewParentVol != NULL) {
|
||
pNewParentVol->Release();
|
||
}
|
||
|
||
if (pChildVol != NULL) {
|
||
pChildVol->Release();
|
||
}
|
||
|
||
if (pOldParentVol != NULL) {
|
||
pOldParentVol->Release();
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::Move() exit\n"));
|
||
|
||
return((dwErr));
|
||
}
|
||
|
||
|
||
//+------------------------------------------------------------------------
|
||
//
|
||
// Method: CDfsVolume::InitializePkt, public
|
||
//
|
||
// Synopsis: This method initilaizes the PKT with info regarding this
|
||
// volume and also sets up the relational info for itself. It
|
||
// also continues to recursively call all the child volume
|
||
// objects' InitializePkt method.
|
||
//
|
||
// Arguments: None
|
||
//
|
||
// Notes: This is a recursive method. By calling this we can initialise
|
||
// the PKT with info regarding this volume and all its children
|
||
// since it is a recursive routine. This is a DEPTH-FIRST Traversal
|
||
//
|
||
// History: 09-Feb-93 SudK Created.
|
||
//
|
||
//-------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::InitializePkt(
|
||
HANDLE PktHandle)
|
||
{
|
||
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CEnumDirectory *pDir = NULL;
|
||
DFSMSTATDIR rgelt;
|
||
ULONG fetched = 0;
|
||
CDfsVolume *child = NULL;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::InitializePkt()\n"));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose & 0x80000000)
|
||
DbgPrint("CDfsVolume::InitializePkt()\n");
|
||
#endif
|
||
|
||
memset(&rgelt, 0, sizeof(DFSMSTATDIR));
|
||
|
||
//
|
||
// First, we get a hold of the CRegEnumDirectory interface to our own
|
||
// volume object.
|
||
//
|
||
|
||
dwErr = _pStorage->GetEnumDirectory(&pDir);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Failed to get IDirectory %08lx\n", dwErr));
|
||
return(dwErr);
|
||
}
|
||
|
||
//
|
||
// While there are children still to be handled we continue on.
|
||
//
|
||
while (TRUE) {
|
||
|
||
if (rgelt.pwcsName != NULL) {
|
||
delete [] rgelt.pwcsName;
|
||
rgelt.pwcsName = NULL;
|
||
}
|
||
|
||
dwErr = pDir->Next(&rgelt, &fetched);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Failed to Enumerate %08lx\n", dwErr));
|
||
break;
|
||
}
|
||
//
|
||
// If we did not get back any children we are done.
|
||
//
|
||
if (fetched == 0)
|
||
break;
|
||
|
||
IDfsVolInlineDebOut((
|
||
DEB_TRACE, "CDfsVolume::InitializePkt Name=%ws\n", rgelt.pwcsName));
|
||
|
||
//
|
||
// If the child name is . or .. we ignore it
|
||
//
|
||
if ((!wcscmp(rgelt.pwcsName, L".")) ||
|
||
(!wcscmp(rgelt.pwcsName, L"..")))
|
||
continue;
|
||
|
||
dwErr = GetDfsVolumeFromStg(&rgelt, &child);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// Log an EVENT here saying that volume object is corrupt and
|
||
// continue on. We dont want to stop just because we cant handle
|
||
// one particular object.
|
||
//
|
||
LogMessage( DEB_ERROR,
|
||
&(_peid.Prefix.Buffer),
|
||
1,
|
||
DFS_CANT_GET_TO_CHILD_IDFSVOL_MSG);
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Bad Object: %ws\n", rgelt.pwcsName));
|
||
//
|
||
// We dont want to give up as yet. So let us go on to the next
|
||
// object.
|
||
//
|
||
fetched = 0; //Do I need to do this??
|
||
continue;
|
||
}
|
||
|
||
if (DFS_GET_RECOVERY_STATE(child->_RecoveryState) != DFS_RECOVERY_STATE_NONE) {
|
||
//
|
||
// Now we need to call the recovery method to handle this stuff.
|
||
// What do I do with the error code that I get back out here.
|
||
//
|
||
LogMessage(DEB_ERROR,
|
||
&(child->_peid.Prefix.Buffer),
|
||
1,
|
||
DFS_RECOVERY_NECESSARY_MSG);
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Object To Recover From: %ws\n",
|
||
rgelt.pwcsName));
|
||
dwErr = child->RecoverFromFailure();
|
||
//
|
||
// We failed to recover from failure. The required messages would
|
||
// have already been logged so we dont need to bother about that
|
||
// here at all. But a failure in this process means we really cant
|
||
// go on here. We would get a failure from the above only if there
|
||
// was some drastic problem.
|
||
//
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Fatal RecoveryError \n"));
|
||
}
|
||
|
||
if (child->_Deleted == TRUE) {
|
||
//
|
||
// Due to above recovery the volume got deleted. No point
|
||
// going on along this path. Move on to next sibling.
|
||
//
|
||
child->Release();
|
||
fetched = 0;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Create the subordinate PKT entry for the child. This way we
|
||
// get to setup the links as well.
|
||
//
|
||
dwErr = child->CreateSubordinatePktEntry(PktHandle, &_peid, TRUE);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// This is a chance to write out an EVENT and then continue
|
||
// to the Initialization of further children. We need to
|
||
// continue on even though we could not get to finish this
|
||
// volume.
|
||
//
|
||
LogMessage(DEB_ERROR,
|
||
&(child->_peid.Prefix.Buffer),
|
||
1,
|
||
DFS_UNABLE_TO_CREATE_SUBORDINATE_ENTRY_MSG);
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Object For Above Error: %ws\n",
|
||
rgelt.pwcsName));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now that the subordinate entry has been created we can go ahead
|
||
// and call InitialisePkt on child. This makes a DepthFirst traversal.
|
||
// Even if we could not create the subordinate entry for some reason,
|
||
// we need to go on and attempt to initialize the rest. But we really
|
||
// cant do that here. Riad: 455283. We need a different approach here.
|
||
//
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
dwErr = child->InitializePkt(PktHandle);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// We ignore this error and also dont bother to write out an
|
||
// event since this should have been handled by some lower method.
|
||
// We maybe sitting many levels high up and this error came all
|
||
// the way back up.
|
||
//
|
||
IDfsVolInlineDebOut((DEB_ERROR, "InitPkt failed %08lx\n", dwErr));
|
||
}
|
||
}
|
||
|
||
child->Release();
|
||
//
|
||
// Let us now attempt to enumerate the next child.
|
||
//
|
||
fetched = 0;
|
||
|
||
} //While fetched!=0
|
||
|
||
//
|
||
// If this volume's state is offline, set the local DC's pkt to reflect
|
||
// that case
|
||
//
|
||
|
||
if (_State == DFS_VOLUME_STATE_OFFLINE) {
|
||
|
||
NTSTATUS Status;
|
||
CDfsService *psvc;
|
||
|
||
for (psvc = _DfsSvcList.GetFirstService();
|
||
psvc != NULL;
|
||
psvc = _DfsSvcList.GetNextService(psvc)) {
|
||
|
||
LPWSTR wszService;
|
||
|
||
wszService = psvc->GetServiceName();
|
||
|
||
Status = DfsSetServiceState(
|
||
&_peid,
|
||
wszService,
|
||
DFS_SERVICE_TYPE_OFFLINE);
|
||
|
||
}
|
||
|
||
Status = DfsDCSetVolumeState(
|
||
&_peid,
|
||
PKT_ENTRY_TYPE_OFFLINE);
|
||
|
||
|
||
}
|
||
|
||
if (rgelt.pwcsName != NULL)
|
||
delete [] rgelt.pwcsName;
|
||
|
||
if (pDir != NULL)
|
||
pDir->Release();
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::InitializePkt() exit\n"));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose & 0x80000000)
|
||
DbgPrint("CDfsVolume::InitializePkt exit %d\n", dwErr);
|
||
#endif
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: GetNetInfo
|
||
//
|
||
// Synopsis: Gets information about the volume.
|
||
//
|
||
// Arguments: [Level] -- Level of Information desired.
|
||
//
|
||
// [pInfo] -- Pointer to info struct to be filled. Pointer
|
||
// members will be allocated using MIDL_user_allocate.
|
||
// The type of this variable is LPDFS_INFO_3, but one
|
||
// can pass in pointers to lower levels, and only the
|
||
// fields appropriate for the level will be touched.
|
||
//
|
||
// [pcbInfo] -- On successful return, contains the size in
|
||
// bytes of the returned info. The returned size does
|
||
// not include the size of the DFS_INFO_3 struct itself.
|
||
//
|
||
// Returns: ERROR_SUCCESS -- Successfully returning info
|
||
//
|
||
// ERROR_OUTOFMEMORY -- Out of memory
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
DWORD
|
||
CDfsVolume::GetNetInfo(
|
||
DWORD Level,
|
||
LPDFS_INFO_3 pInfo,
|
||
LPDWORD pcbInfo)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
DWORD cbInfo = 0, cbItem;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::GetNetInfo(l=%d)\n", Level));
|
||
|
||
#if DBG
|
||
if (DfsSvcVerbose)
|
||
DbgPrint(" CDfsVolume::GetNetInfo(l=%d)\n", Level);
|
||
#endif
|
||
|
||
//
|
||
// See if this is a Level 100 or 101. If so, we handle them right away
|
||
// and return
|
||
|
||
if (Level == 100) {
|
||
|
||
LPDFS_INFO_100 pInfo100 = (LPDFS_INFO_100) pInfo;
|
||
|
||
cbItem = (wcslen(_pwszComment) + 1) * sizeof(WCHAR);
|
||
|
||
pInfo100->Comment = (LPWSTR) MIDL_user_allocate(cbItem);
|
||
|
||
if (pInfo100->Comment != NULL) {
|
||
|
||
wcscpy(pInfo100->Comment, _pwszComment);
|
||
|
||
*pcbInfo = cbItem;
|
||
|
||
return( ERROR_SUCCESS );
|
||
|
||
} else {
|
||
|
||
return( ERROR_OUTOFMEMORY );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (Level == 101) {
|
||
|
||
LPDFS_INFO_101 pInfo101 = (LPDFS_INFO_101) pInfo;
|
||
|
||
pInfo->State = _State;
|
||
|
||
return( ERROR_SUCCESS );
|
||
|
||
}
|
||
|
||
//
|
||
// level 4 isn't just an extension of 3, so handle it seperately
|
||
//
|
||
|
||
if (Level == 4) {
|
||
|
||
LPDFS_INFO_4 pInfo4 = (LPDFS_INFO_4) pInfo;
|
||
|
||
cbItem = sizeof(UNICODE_PATH_SEP) + _peid.Prefix.Length + sizeof(WCHAR);
|
||
|
||
pInfo4->EntryPath = (LPWSTR) MIDL_user_allocate(cbItem);
|
||
|
||
if (pInfo4->EntryPath != NULL) {
|
||
|
||
pInfo4->EntryPath[0] = UNICODE_PATH_SEP;
|
||
|
||
wcscpy(&pInfo4->EntryPath[1], _peid.Prefix.Buffer);
|
||
|
||
cbInfo += cbItem;
|
||
|
||
} else {
|
||
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
cbItem = (wcslen(_pwszComment)+1) * sizeof(WCHAR);
|
||
pInfo4->Comment = (LPWSTR) MIDL_user_allocate(cbItem);
|
||
|
||
if (pInfo4->Comment != NULL) {
|
||
|
||
wcscpy( pInfo4->Comment, _pwszComment );
|
||
|
||
cbInfo += cbItem;
|
||
|
||
} else {
|
||
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
pInfo4->State = _State;
|
||
|
||
pInfo4->Timeout = _Timeout;
|
||
|
||
pInfo4->Guid = _peid.Uid;
|
||
|
||
pInfo4->NumberOfStorages = _DfsSvcList.GetServiceCount();
|
||
|
||
cbItem = pInfo4->NumberOfStorages * sizeof(DFS_STORAGE_INFO);
|
||
pInfo4->Storage = (LPDFS_STORAGE_INFO) MIDL_user_allocate(cbItem);
|
||
|
||
if (pInfo4->Storage != NULL) {
|
||
|
||
ULONG i;
|
||
CDfsService *pSvc;
|
||
|
||
RtlZeroMemory(pInfo4->Storage, cbItem);
|
||
|
||
cbInfo += cbItem;
|
||
|
||
pSvc = _DfsSvcList.GetFirstService();
|
||
|
||
i = 0;
|
||
|
||
while (pSvc != NULL && dwErr == ERROR_SUCCESS) {
|
||
|
||
dwErr = pSvc->GetNetStorageInfo(&pInfo4->Storage[i], &cbItem);
|
||
|
||
cbInfo += cbItem;
|
||
|
||
i++;
|
||
|
||
pSvc = _DfsSvcList.GetNextService( pSvc );
|
||
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
for (; i > 0; i--) {
|
||
|
||
MIDL_user_free(pInfo4->Storage[i-1].ServerName);
|
||
MIDL_user_free(pInfo4->Storage[i-1].ShareName);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// See if we need to clean up...
|
||
//
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
if (pInfo4->EntryPath != NULL) {
|
||
|
||
MIDL_user_free(pInfo4->EntryPath);
|
||
|
||
}
|
||
|
||
if (pInfo4->Storage != NULL) {
|
||
|
||
MIDL_user_free(pInfo4->Storage);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
*pcbInfo = cbInfo;
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
//
|
||
// Level is 1,2 or 3
|
||
//
|
||
|
||
//
|
||
// Fill in the Level 1 stuff
|
||
//
|
||
|
||
cbItem = sizeof(UNICODE_PATH_SEP) + _peid.Prefix.Length + sizeof(WCHAR);
|
||
|
||
pInfo->EntryPath = (LPWSTR) MIDL_user_allocate(cbItem);
|
||
|
||
if (pInfo->EntryPath != NULL) {
|
||
|
||
pInfo->EntryPath[0] = UNICODE_PATH_SEP;
|
||
|
||
wcscpy(&pInfo->EntryPath[1], _peid.Prefix.Buffer);
|
||
|
||
cbInfo += cbItem;
|
||
|
||
} else {
|
||
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
//
|
||
// Fill in the Level 2 stuff if needed
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS && Level > 1) {
|
||
|
||
cbItem = (wcslen(_pwszComment)+1) * sizeof(WCHAR);
|
||
pInfo->Comment = (LPWSTR) MIDL_user_allocate(cbItem);
|
||
|
||
if (pInfo->Comment != NULL) {
|
||
|
||
wcscpy( pInfo->Comment, _pwszComment );
|
||
|
||
cbInfo += cbItem;
|
||
|
||
} else {
|
||
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS && Level > 1) {
|
||
|
||
pInfo->State = _State;
|
||
|
||
pInfo->NumberOfStorages = _DfsSvcList.GetServiceCount();
|
||
|
||
}
|
||
|
||
//
|
||
// Fill in the Level 3 stuff if needed
|
||
//
|
||
|
||
if (dwErr == ERROR_SUCCESS && Level > 2) {
|
||
|
||
cbItem = pInfo->NumberOfStorages * sizeof(DFS_STORAGE_INFO);
|
||
pInfo->Storage = (LPDFS_STORAGE_INFO) MIDL_user_allocate(cbItem);
|
||
|
||
if (pInfo->Storage != NULL) {
|
||
|
||
ULONG i;
|
||
CDfsService *pSvc;
|
||
|
||
RtlZeroMemory(pInfo->Storage, cbItem);
|
||
|
||
cbInfo += cbItem;
|
||
|
||
pSvc = _DfsSvcList.GetFirstService();
|
||
|
||
i = 0;
|
||
|
||
while (pSvc != NULL && dwErr == ERROR_SUCCESS) {
|
||
|
||
dwErr = pSvc->GetNetStorageInfo(&pInfo->Storage[i], &cbItem);
|
||
|
||
cbInfo += cbItem;
|
||
|
||
i++;
|
||
|
||
pSvc = _DfsSvcList.GetNextService( pSvc );
|
||
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
for (; i > 0; i--) {
|
||
|
||
MIDL_user_free(pInfo->Storage[i-1].ServerName);
|
||
MIDL_user_free(pInfo->Storage[i-1].ShareName);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// See if we need to clean up...
|
||
//
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
if (Level > 1) {
|
||
|
||
if (pInfo->EntryPath != NULL) {
|
||
|
||
MIDL_user_free(pInfo->EntryPath);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (Level > 2) {
|
||
|
||
if (pInfo->Storage != NULL) {
|
||
|
||
MIDL_user_free(pInfo->Storage);
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finally, we are done
|
||
//
|
||
|
||
*pcbInfo = cbInfo;
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
|
||
|
||
//+-----------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::SetComment, public
|
||
//
|
||
// Synopsis: Sets a comment on the volume object.
|
||
//
|
||
// Arguments: [pwszComment] -- The comment
|
||
//
|
||
// Returns:
|
||
//
|
||
// History: 05/10/93 SudK Created.
|
||
//
|
||
//------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::SetComment(
|
||
PWCHAR pwszComment
|
||
)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
SYSTEMTIME st;
|
||
FILETIME ftOld;
|
||
LPWSTR pwszOldComment;
|
||
ULONG ulen = wcslen(pwszComment);
|
||
|
||
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "SetComment: %ws\n", pwszComment));
|
||
|
||
ftOld = _ftComment;
|
||
pwszOldComment = _pwszComment;
|
||
_pwszComment = new WCHAR[ulen+1];
|
||
|
||
if (_pwszComment == NULL)
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
else {
|
||
wcscpy(_pwszComment, pwszComment);
|
||
|
||
GetSystemTime( &st );
|
||
SystemTimeToFileTime( &st, &_ftComment );
|
||
|
||
dwErr = SetIdProps( _EntryType,
|
||
_State,
|
||
_peid.Prefix.Buffer,
|
||
_peid.ShortPrefix.Buffer,
|
||
_peid.Uid,
|
||
pwszComment,
|
||
_Timeout,
|
||
_ftEntryPath,
|
||
_ftState,
|
||
_ftComment,
|
||
FALSE);
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
if (_pwszComment) {
|
||
delete [] _pwszComment;
|
||
}
|
||
_ftComment = ftOld;
|
||
_pwszComment = pwszOldComment;
|
||
}
|
||
|
||
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::FixServiceKnowledge
|
||
//
|
||
// Synopsis: This method is called for fixing knowledge inconsistencies.
|
||
// It checks to make sure that the given service does support
|
||
// this volume and then goes on to do a CreateLocalVol call on
|
||
// that service.
|
||
//
|
||
// Arguments: [pwszServiceName] -- The principal name of service to be
|
||
// fixed.
|
||
//
|
||
// History: 06-April-1993 SudK Created
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::FixServiceKnowledge(
|
||
PWCHAR pwszServiceName
|
||
)
|
||
{
|
||
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CDfsService *pService;
|
||
PWCHAR ErrorStrs[3];
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::FixServiceKnowledge(%s)\n",
|
||
pwszServiceName));
|
||
|
||
dwErr = _DfsSvcList.GetServiceFromPrincipalName(pwszServiceName, &pService);
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// We really dont care about errors since this is all heuristic. We
|
||
// will merely log an error that's all.
|
||
//
|
||
|
||
dwErr = pService->FixLocalVolume(&_peid, _EntryType);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Log the error and try to take the service off-line
|
||
//
|
||
|
||
DWORD dwErrOffLine;
|
||
|
||
ErrorStrs[0] = _peid.Prefix.Buffer;
|
||
ErrorStrs[1] = pwszServiceName;
|
||
|
||
LogMessage( DEB_ERROR,
|
||
ErrorStrs,
|
||
2,
|
||
DFS_CANNOT_SET_SERVICE_PROPERTY_MSG);
|
||
|
||
dwErrOffLine = pService->SetVolumeState(
|
||
&_peid,
|
||
DFS_SERVICE_TYPE_OFFLINE,
|
||
FALSE);
|
||
|
||
if (dwErrOffLine == ERROR_SUCCESS) {
|
||
|
||
dwErrOffLine = _DfsSvcList.SerializeSvcList();
|
||
}
|
||
|
||
if (dwErrOffLine == ERROR_SUCCESS) {
|
||
|
||
_DfsSvcList.SetServiceListProperty( FALSE );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR,
|
||
"Could not find Service %ws on Volume: %ws\n",
|
||
pwszServiceName,
|
||
_peid.Prefix.Buffer));
|
||
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::FixServiceKnowledge() exit\n"));
|
||
|
||
return((dwErr));
|
||
|
||
}
|
||
|
||
|
||
|
||
//+---------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::Rename, public
|
||
//
|
||
// Synopsis: This method should be called if an object/file/directory
|
||
// on this volume is to be renamed, and the name falls along an
|
||
// exit point off this volume. This method will perform the
|
||
// rename on the filesystem and then go on to call ModifyPrefix
|
||
// on all the child volume objects which are affected by the
|
||
// rename operation.
|
||
//
|
||
// Arguments: [oldPrefix] -- The oldPrefix that needs to be modified. Prefix
|
||
// [newPrefix] -- This should be ORG relative prefix.
|
||
//
|
||
// Returns: ERROR_SUCCESS -- If all went well.
|
||
//
|
||
// Notes: The old and newPaths should follow all requirements of a File
|
||
// System Rename operation. Should vary only in last component etc.
|
||
//
|
||
// History: 31 April 1993 SudK Created.
|
||
// 26 April 1993 SudK Fixed to work properly.
|
||
//
|
||
//----------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::Rename(
|
||
PWSTR oldPrefix,
|
||
PWSTR newPrefix
|
||
)
|
||
{
|
||
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CDfsVolume *child = NULL;
|
||
CEnumDirectory *pdir = NULL;
|
||
DFSMSTATDIR rgelt;
|
||
ULONG fetched = 0;
|
||
WCHAR wszOldPath[MAX_PATH], wszNewPath[MAX_PATH];
|
||
PWSTR pwszOldPath = wszOldPath;
|
||
PWSTR pwszNewPath = wszNewPath;
|
||
ULONG len = MAX_PATH;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::Rename(%ws,%ws)\n", oldPrefix, newPrefix));
|
||
|
||
//
|
||
// Need to check if this is the right idfsvol to call rename on.
|
||
//
|
||
ULONG ulen = _peid.Prefix.Length/sizeof(WCHAR);
|
||
|
||
if ((wcslen(oldPrefix) <= ulen) ||
|
||
(wcslen(newPrefix) <= ulen)) {
|
||
//
|
||
// Both old and new prefixes should be longer than the prefix of
|
||
// this volume. If they are not then we can return error right away.
|
||
//
|
||
dwErr = NERR_DfsBadRenamePath;
|
||
} else if ((_wcsnicmp(_peid.Prefix.Buffer, oldPrefix, ulen) != 0) ||
|
||
(_wcsnicmp(_peid.Prefix.Buffer, newPrefix, ulen) != 0)) {
|
||
|
||
//
|
||
// Now we need to check to make sure that this volume's prefix is a
|
||
// proper prefix of path being renamed.
|
||
//
|
||
dwErr = NERR_DfsBadRenamePath;
|
||
|
||
} else if (_wcsicmp(oldPrefix, newPrefix) == 0) {
|
||
//
|
||
// Someone has given identical rename path. Return right away.
|
||
//
|
||
dwErr = NERR_DfsBadRenamePath;
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
len = wcslen(oldPrefix);
|
||
|
||
if (len > (MAX_PATH - 2)) {
|
||
pwszOldPath = new WCHAR[ len + 2 ];
|
||
if (pwszOldPath == NULL) {
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
}
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
wcscpy( pwszOldPath, UNICODE_PATH_SEP_STR );
|
||
wcscat( pwszOldPath, oldPrefix );
|
||
}
|
||
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
len = wcslen(newPrefix);
|
||
|
||
if (len > (MAX_PATH - 2)) {
|
||
pwszNewPath = new WCHAR[ len + 2 ];
|
||
if (pwszNewPath == NULL) {
|
||
dwErr = ERROR_OUTOFMEMORY;
|
||
}
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
wcscpy( pwszNewPath, UNICODE_PATH_SEP_STR );
|
||
wcscat( pwszNewPath, newPrefix );
|
||
}
|
||
|
||
}
|
||
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
//
|
||
// Now do the Rename operation
|
||
//
|
||
NTSTATUS Status;
|
||
|
||
Status = MoveFileOrJP(pwszOldPath, pwszNewPath);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
dwErr = RtlNtStatusToDosError(Status);
|
||
}
|
||
if (pwszOldPath != wszOldPath) {
|
||
delete [] pwszOldPath;
|
||
}
|
||
if (pwszNewPath != wszNewPath) {
|
||
delete [] pwszNewPath;
|
||
}
|
||
}
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
dwErr = _pStorage->GetEnumDirectory(&pdir);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
return(dwErr);
|
||
}
|
||
|
||
//
|
||
// While there are children still to be handled we continue on.
|
||
//
|
||
|
||
rgelt.pwcsName = NULL;
|
||
|
||
while (TRUE) {
|
||
|
||
if (rgelt.pwcsName != NULL) {
|
||
delete [] rgelt.pwcsName;
|
||
rgelt.pwcsName = NULL;
|
||
}
|
||
|
||
dwErr = pdir->Next(&rgelt, &fetched);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Failed to Enumeraate\n", 0));
|
||
break;
|
||
}
|
||
//
|
||
// If we did not get back any children we are done.
|
||
//
|
||
if (fetched == 0)
|
||
break;
|
||
|
||
//
|
||
// If the child name is . or .. we ignore it
|
||
//
|
||
if ((!wcscmp(rgelt.pwcsName, L".")) ||
|
||
(!wcscmp(rgelt.pwcsName, L"..")))
|
||
continue;
|
||
|
||
dwErr = GetDfsVolumeFromStg(&rgelt, &child);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// Log an EVENT here saying that volume object is corrupt and
|
||
// continue on. We dont want to stop just because we cant handle
|
||
// one particular object.
|
||
//
|
||
LogMessage( DEB_ERROR,
|
||
&(_peid.Prefix.Buffer),
|
||
1,
|
||
DFS_CANT_GET_TO_CHILD_IDFSVOL_MSG);
|
||
//
|
||
// We dont want to give up as yet. So let us go on to the next
|
||
// object.
|
||
//
|
||
fetched = 0;
|
||
continue;
|
||
}
|
||
|
||
dwErr = child->ModifyEntryPath(oldPrefix, newPrefix);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// We ignore this err since it would be logged further down.
|
||
//
|
||
}
|
||
|
||
child->Release();
|
||
|
||
//
|
||
// Let us now attempt to enumerate the next child.
|
||
//
|
||
fetched = 0;
|
||
|
||
} //While TRUE
|
||
|
||
if (pdir != NULL)
|
||
pdir->Release();
|
||
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::Rename() exit\n"));
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Method: CDfsVolume::ModifyLocalEntryPath,private
|
||
//
|
||
// Synopsis: This method modifies the entry path on the volume object,
|
||
// and modifies the local PKT to reflect the change.
|
||
//
|
||
// Arguments:
|
||
//
|
||
// Returns:
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
DWORD
|
||
CDfsVolume::ModifyLocalEntryPath(
|
||
PWCHAR newEntryPath,
|
||
FILETIME ftEntryPath,
|
||
BOOL fUpdatePkt)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CUnicodeString oldString, newString;
|
||
|
||
dwErr = SetIdProps(
|
||
_EntryType,
|
||
_State,
|
||
newEntryPath,
|
||
_peid.ShortPrefix.Buffer,
|
||
_peid.Uid,
|
||
_pwszComment,
|
||
_Timeout,
|
||
ftEntryPath,
|
||
_ftState,
|
||
_ftComment,
|
||
FALSE);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// We want to return right away. Can we hope that the private section
|
||
// variable will get deleted and return or should we fix the private
|
||
// _peid since this operation never succeeded.
|
||
//
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR, "Unable To modify Prefix to [%ws]\n", newEntryPath));
|
||
|
||
return(dwErr); //Raid: 455283 cant return this error here.
|
||
}
|
||
|
||
//
|
||
// Now that the object has changed let us modify the Private variables.
|
||
//
|
||
oldString.Copy(_peid.Prefix.Buffer);
|
||
|
||
newString.Copy(newEntryPath);
|
||
if (_peid.Prefix.Buffer != _EntryPathBuffer)
|
||
delete [] _peid.Prefix.Buffer;
|
||
newString.Transfer(&(_peid.Prefix));
|
||
|
||
if (fUpdatePkt) {
|
||
dwErr = UpdatePktEntry(NULL);
|
||
}
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
DWORD dwErr2;
|
||
delete [] _peid.Prefix.Buffer;
|
||
oldString.Transfer(&(_peid.Prefix));
|
||
dwErr2 = SetIdProps(_EntryType,
|
||
_State,
|
||
_peid.Prefix.Buffer,
|
||
_peid.ShortPrefix.Buffer,
|
||
_peid.Uid,
|
||
_pwszComment,
|
||
_Timeout,
|
||
_ftEntryPath,
|
||
_ftState,
|
||
_ftComment,
|
||
FALSE);
|
||
|
||
if (dwErr2 != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((
|
||
DEB_ERROR,
|
||
"Error restoring id props in failed rename operation %08lx\n",
|
||
dwErr2));
|
||
}
|
||
|
||
} else {
|
||
|
||
_ftEntryPath = ftEntryPath;
|
||
|
||
}
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
|
||
|
||
//+---------------------------------------------------------------------
|
||
//
|
||
// Method: CDfsVolume::ModifyEntryPath,public
|
||
//
|
||
// Synopsis: This method modifies the entry path on this volume object
|
||
// and at the same time modifies the entry paths on all the
|
||
// services by issuing FSCTRLs. It then turns around and calls
|
||
// the same API on all the children since their entrypaths are
|
||
// also affected by this change.
|
||
//
|
||
// Arguments: [oldPrefix] -- The old Prefix on the Parent volume object.
|
||
// [newPrefix] -- The new Prefix on the Parent volume object.
|
||
//
|
||
// Returns: ERROR_SUCCESS -- If all went well.
|
||
//
|
||
// Notes: This method does not bother to check for the validity of
|
||
// the new entryPath etc. THat should have happened way before
|
||
// we got here.
|
||
//
|
||
// History: 31 April 1993 SudK Created.
|
||
//
|
||
//----------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::ModifyEntryPath(
|
||
LPWSTR oldPrefix,
|
||
LPWSTR newPrefix)
|
||
{
|
||
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CDfsService *pService;
|
||
PWCHAR ErrorStrs[2];
|
||
PWCHAR childNewPrefix;
|
||
CEnumDirectory *pdir;
|
||
DFSMSTATDIR rgelt;
|
||
ULONG fetched = 0;
|
||
CDfsVolume *child = NULL;
|
||
PWCHAR newEntryPath;
|
||
SYSTEMTIME st;
|
||
FILETIME ftEntryPath;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "ModifyEntryPath(%s,%s)\n", oldPrefix, newPrefix));
|
||
|
||
//
|
||
// First, see if we need to modify anything at all. If the oldPrefix
|
||
// is *not* a prefix of our name, then our name is *not* changing, and
|
||
// we just return.
|
||
//
|
||
if (_wcsnicmp(_peid.Prefix.Buffer, oldPrefix, wcslen(oldPrefix)) != 0) {
|
||
IDfsVolInlineDebOut((DEB_TRACE, "ModifyEntryPath() exit\n"));
|
||
return( ERROR_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// Next, compute the new entry path
|
||
//
|
||
ComputeNewEntryPath( oldPrefix, newPrefix, _peid.Prefix.Buffer, &newEntryPath );
|
||
|
||
if (newEntryPath == NULL) {
|
||
return( ERROR_OUTOFMEMORY );
|
||
}
|
||
|
||
//
|
||
// Let us first modify the prefix locally on the object. We trust the
|
||
// arguments since they could have been passed by our own code. So we
|
||
// will not bother with a TRY/CATCH etc. out here.
|
||
//
|
||
|
||
GetSystemTime( &st );
|
||
SystemTimeToFileTime( &st, &ftEntryPath );
|
||
dwErr = ModifyLocalEntryPath( newEntryPath, ftEntryPath, TRUE );
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
|
||
delete [] newEntryPath;
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
//
|
||
// Now that the prefix has been modified locally we need to update all the
|
||
// services.
|
||
//
|
||
pService = _DfsSvcList.GetFirstService();
|
||
|
||
while (pService != NULL) {
|
||
|
||
dwErr = pService->ModifyPrefix(&_peid);
|
||
|
||
//
|
||
// If we fail to modify the prefix we really dont want to stop. We go
|
||
// on and update the other services and volumes.
|
||
//
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
ErrorStrs[0] = _peid.Prefix.Buffer;
|
||
ErrorStrs[1] = pService->GetServiceName();
|
||
LogMessage(DEB_ERROR,
|
||
ErrorStrs,
|
||
2,
|
||
DFS_MODIFY_PREFIX_FAILED_MSG);
|
||
|
||
}
|
||
dwErr = ERROR_SUCCESS;
|
||
pService = _DfsSvcList.GetNextService(pService);
|
||
|
||
}
|
||
|
||
//
|
||
// Now we have updated all the services of this volume. Let us now get to
|
||
// all our children and call this method on them again.
|
||
//
|
||
|
||
dwErr = _pStorage->GetEnumDirectory(&pdir);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
delete [] newEntryPath;
|
||
return(dwErr);
|
||
}
|
||
|
||
//
|
||
// While there are children still to be handled we continue on.
|
||
//
|
||
rgelt.pwcsName = NULL;
|
||
|
||
while (TRUE) {
|
||
|
||
if (rgelt.pwcsName != NULL) {
|
||
delete [] rgelt.pwcsName;
|
||
rgelt.pwcsName = NULL;
|
||
}
|
||
|
||
dwErr = pdir->Next(&rgelt, &fetched);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
IDfsVolInlineDebOut((DEB_ERROR, "Failed to Enumeraate\n", 0));
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If we did not get back any children we are done.
|
||
//
|
||
if (fetched == 0)
|
||
break;
|
||
|
||
//
|
||
// If the child name is . or .. we ignore it
|
||
//
|
||
if ((!wcscmp(rgelt.pwcsName, L".")) ||
|
||
(!wcscmp(rgelt.pwcsName, L"..")))
|
||
continue;
|
||
|
||
dwErr = GetDfsVolumeFromStg(&rgelt, &child);
|
||
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// Log an EVENT here saying that volume object is corrupt and
|
||
// continue on. We dont want to stop just because we cant handle
|
||
// one particular object.
|
||
//
|
||
LogMessage( DEB_ERROR,
|
||
&(_peid.Prefix.Buffer),
|
||
1,
|
||
DFS_CANT_GET_TO_CHILD_IDFSVOL_MSG);
|
||
//
|
||
// We dont want to give up as yet. So let us go on to the next
|
||
// object.
|
||
//
|
||
fetched = 0;
|
||
continue;
|
||
}
|
||
|
||
dwErr = child->ModifyEntryPath(oldPrefix, newPrefix);
|
||
if (dwErr != ERROR_SUCCESS) {
|
||
//
|
||
// We ignore this error and also dont bother to write out an
|
||
// event since this should have been handled by some lower method.
|
||
// We maybe sitting many levels high up and this error came all
|
||
// the way back up.
|
||
}
|
||
|
||
child->Release();
|
||
//
|
||
// Let us now attempt to enumerate the next child.
|
||
//
|
||
fetched = 0;
|
||
|
||
} //While TRUE
|
||
|
||
if (pdir != NULL)
|
||
pdir->Release();
|
||
|
||
delete [] newEntryPath;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "ModifyEntryPath() exit\n"));
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
|
||
//+-----------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::GetReplicaSetID, public
|
||
//
|
||
// Synopsis: This method returns the replica set ID (if any) stored with
|
||
// this volume object.
|
||
//
|
||
// Arguments: [pguidRsid] -- This is where the replica set ID is returned.
|
||
//
|
||
// Returns: If there is no replica set ID then caller gets NullGuid.
|
||
//
|
||
// History: 16 Aug 1993 Alanw Created
|
||
// 19 Oct 1993 SudK Implemented
|
||
//
|
||
//------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::GetReplicaSetID(
|
||
GUID *pguidRsid
|
||
)
|
||
{
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::GetReplicaSetID()\n"));
|
||
|
||
(*pguidRsid) = _DfsSvcList._ReplicaSetID;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::GetReplicaSetID() exit\n"));
|
||
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
//+-----------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::SetReplicaSetID, public
|
||
//
|
||
// Synopsis: This method sets the replica set ID associated with
|
||
// this volume object.
|
||
//
|
||
// Arguments: [pguidRsid] -- A pointer to the replica set ID.
|
||
//
|
||
// Returns:
|
||
//
|
||
// History: 16 Aug 1993 Alanw Created
|
||
// 19 Oct 1993 SudK Implemented
|
||
//
|
||
//------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::SetReplicaSetID(
|
||
GUID *pguidRsid
|
||
)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::SetReplicaSetID()\n"));
|
||
|
||
_DfsSvcList._ReplicaSetID = *pguidRsid;
|
||
//
|
||
// I am assuming here that the service List is always setup if someone
|
||
// called this method. For now this is correct.
|
||
//
|
||
dwErr = _DfsSvcList.SetServiceListProperty(FALSE);
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::SetReplicaSetID() exit\n"));
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
|
||
//+-----------------------------------------------------------------------
|
||
//
|
||
// Method: CDfsVolume::ChangeStorageID
|
||
//
|
||
// Synopsis: Modify the storage ID on the replica mentioned.
|
||
//
|
||
// Arguments: [pwszMachineName] -- The replica whose state needs to be
|
||
// changed.
|
||
// [pwszStorageId] -- The new storageId on the above replica
|
||
//
|
||
// Returns:
|
||
//
|
||
// History: 7/21/94 SudK Created
|
||
//
|
||
//------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::ChangeStorageID(
|
||
LPWSTR pwszMachineName,
|
||
LPWSTR pwszNetStorageId
|
||
)
|
||
{
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
|
||
//+-----------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::SetReplicaState, public
|
||
//
|
||
// Synopsis: Change the state of the replica indicated.
|
||
//
|
||
// Arguments: [pwszMachineName] -- The server whose state needs to be
|
||
// changed.
|
||
// [pwszShareName] -- The share on server whose state needs to
|
||
// be changed.
|
||
// [fState] -- The State to set on this.
|
||
//
|
||
// Returns:
|
||
//
|
||
// History: 7/21/94 SudK Created
|
||
//
|
||
//------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::SetReplicaState(
|
||
LPWSTR pwszMachineName,
|
||
LPWSTR pwszShareName,
|
||
ULONG fState
|
||
)
|
||
{
|
||
DWORD dwErr = ERROR_SUCCESS;
|
||
CDfsService *psvc;
|
||
ULONG svcState;
|
||
PWCHAR pMachineName;
|
||
PWCHAR pShareName;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::SetReplicaState(%ws,%ws)\n",
|
||
pwszMachineName, pwszShareName));
|
||
|
||
switch (fState) {
|
||
case DFS_STORAGE_STATE_OFFLINE:
|
||
svcState = DFS_SERVICE_TYPE_OFFLINE;
|
||
break;
|
||
|
||
case DFS_STORAGE_STATE_ONLINE:
|
||
svcState = 0;
|
||
break;
|
||
|
||
default:
|
||
return( ERROR_INVALID_PARAMETER );
|
||
break;
|
||
|
||
}
|
||
|
||
for (psvc = _DfsSvcList.GetFirstService();
|
||
psvc != NULL;
|
||
psvc = _DfsSvcList.GetNextService(psvc)) {
|
||
|
||
pMachineName = psvc->GetServiceName();
|
||
pShareName = psvc->GetShareName();
|
||
|
||
if (pMachineName != NULL && _wcsicmp(pwszMachineName, pMachineName) == 0 &&
|
||
pShareName != NULL && _wcsicmp(pwszShareName, pShareName) == 0) {
|
||
|
||
dwErr = psvc->SetVolumeState( &_peid, svcState, TRUE );
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Store the udpated svclist to the volume object. We don't
|
||
// revert if we fail to save to disk. Instead, we return the
|
||
// error, and let the admin try again if she wishes.
|
||
//
|
||
|
||
dwErr = _DfsSvcList.SerializeSvcList();
|
||
|
||
if (dwErr == ERROR_SUCCESS) {
|
||
|
||
dwErr = _DfsSvcList.SetServiceListProperty( FALSE );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::SetReplicaState() exit\n"));
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
|
||
|
||
//+-----------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::SetVolumeState, public
|
||
//
|
||
// Synopsis: Change the state of the volume indicated.
|
||
//
|
||
// Arguments: [fState] -- The State to set on this.
|
||
//
|
||
// Returns:
|
||
//
|
||
// History: 7/21/94 SudK Created
|
||
//
|
||
//------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::SetVolumeState(
|
||
ULONG fState
|
||
)
|
||
{
|
||
DWORD dwErr;
|
||
NTSTATUS status;
|
||
CDfsService *psvc;
|
||
ULONG svcState, volState;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::SetVolumeState(0x%x)\n", fState));
|
||
|
||
switch (fState) {
|
||
case DFS_VOLUME_STATE_OFFLINE:
|
||
svcState = DFS_SERVICE_TYPE_OFFLINE;
|
||
volState = PKT_ENTRY_TYPE_OFFLINE;
|
||
break;
|
||
|
||
case DFS_VOLUME_STATE_ONLINE:
|
||
svcState = 0;
|
||
volState = 0;
|
||
break;
|
||
|
||
default:
|
||
return( ERROR_INVALID_PARAMETER );
|
||
break;
|
||
|
||
}
|
||
|
||
status = DfsDCSetVolumeState( &_peid, volState );
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
SYSTEMTIME st;
|
||
|
||
_State = fState;
|
||
|
||
GetSystemTime( &st );
|
||
SystemTimeToFileTime( &st, &_ftState );
|
||
|
||
dwErr = SetIdProps(_EntryType,
|
||
_State,
|
||
_peid.Prefix.Buffer,
|
||
_peid.ShortPrefix.Buffer,
|
||
_peid.Uid,
|
||
_pwszComment,
|
||
_Timeout,
|
||
_ftEntryPath,
|
||
_ftState,
|
||
_ftComment,
|
||
FALSE);
|
||
|
||
} else {
|
||
|
||
dwErr = RtlNtStatusToDosError(status);
|
||
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::SetVolumeState()\n"));
|
||
|
||
return(dwErr);
|
||
|
||
}
|
||
|
||
//+-----------------------------------------------------------------------
|
||
//
|
||
// Member: CDfsVolume::SetVolumeTimeout, public
|
||
//
|
||
// Synopsis: Change the timeout of the volume
|
||
//
|
||
// Arguments: [Timeout] -- The new timeout to set on this.
|
||
//
|
||
// Returns:
|
||
//
|
||
//------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::SetVolumeTimeout(
|
||
ULONG Timeout
|
||
)
|
||
{
|
||
DWORD dwErr = 0;
|
||
NTSTATUS status;
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::SetVolumeTimeout(0x%x)\n", Timeout));
|
||
|
||
status = DfsSetVolumeTimeout( &_peid, Timeout );
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
_Timeout = Timeout;
|
||
|
||
dwErr = SetIdProps(_EntryType,
|
||
_State,
|
||
_peid.Prefix.Buffer,
|
||
_peid.ShortPrefix.Buffer,
|
||
_peid.Uid,
|
||
_pwszComment,
|
||
_Timeout,
|
||
_ftEntryPath,
|
||
_ftState,
|
||
_ftComment,
|
||
FALSE);
|
||
|
||
} else {
|
||
|
||
dwErr = RtlNtStatusToDosError(status);
|
||
|
||
}
|
||
|
||
IDfsVolInlineDebOut((DEB_TRACE, "CDfsVolume::SetVolumeTimeout()\n"));
|
||
|
||
return(dwErr);
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: CDfsVolume::GetObjectID
|
||
//
|
||
// Synopsis: Return the volume object guid
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
DWORD
|
||
CDfsVolume::GetObjectID(LPGUID guidVolume)
|
||
{
|
||
*guidVolume = _peid.Uid;
|
||
|
||
return(ERROR_SUCCESS);
|
||
}
|
||
|
||
//+---------------------------------------------------------------------
|
||
//
|
||
// Function: ComputeNewEntryPath, private
|
||
//
|
||
// Synopsis: This function is a helper function for the following functions
|
||
// to be able to compute the entry paths for a child volume given
|
||
// the change in their entrypath's and the child's current path.
|
||
//
|
||
// Arguments: [oldPath] -- OldPath of currentVolume. Prefix
|
||
// [newPath] -- NewPath of currentVOlume. Prefix
|
||
// [childPath] -- CurrentPath of child. Prefix
|
||
// [childNewPath] -- NewPath of child. Prefix
|
||
//
|
||
// Returns: NOTHING.
|
||
//
|
||
// History: 05-01-93 SudK Created.
|
||
//
|
||
//----------------------------------------------------------------------
|
||
VOID
|
||
ComputeNewEntryPath(
|
||
PWCHAR oldPath,
|
||
PWCHAR newPath,
|
||
PWCHAR childPath,
|
||
PWCHAR *childNewPath
|
||
)
|
||
{
|
||
|
||
ULONG newLen, oldLen;
|
||
PWCHAR childComponent;
|
||
|
||
oldLen = wcslen(oldPath);
|
||
newLen = wcslen(newPath);
|
||
newLen += wcslen(childPath) - oldLen;
|
||
|
||
*childNewPath = new WCHAR[newLen + sizeof(WCHAR)];
|
||
|
||
if (*childNewPath != NULL) {
|
||
|
||
wcscpy(*childNewPath, newPath);
|
||
|
||
childComponent = childPath + oldLen;
|
||
|
||
wcscat(*childNewPath, childComponent);
|
||
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
//+----------------------------------------------------------------------------
|
||
//
|
||
// Function: MoveFileOrJP, private
|
||
//
|
||
// Synopsis: Similar to Win32 MoveFile, except if the named src is a JP,
|
||
// then the JP will get renamed.
|
||
//
|
||
// Arguments: [pwszSrcName] -- Name of the Src object.
|
||
// [pwszTgtName] -- New name of the object.
|
||
//
|
||
// Returns: [STATUS_SUCCESS] -- Rename succeeded.
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
MoveFileOrJP(
|
||
IN PCWSTR pwszSrcName,
|
||
IN PCWSTR pwszTgtName)
|
||
{
|
||
UNICODE_STRING ustrSrc = {0}, ustrTgt = {0};
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
if (!RtlDosPathNameToNtPathName_U( pwszSrcName, &ustrSrc, NULL, NULL)) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
DFSM_TRACE_HIGH(ERROR, MoveFileOrJP_Error1, LOGSTATUS(Status));
|
||
|
||
} else if (!RtlDosPathNameToNtPathName_U(
|
||
pwszTgtName, &ustrTgt, NULL, NULL)) {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
DFSM_TRACE_HIGH(ERROR, MoveFileOrJP_Error2, LOGSTATUS(Status));
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
OBJECT_ATTRIBUTES oaSrc;
|
||
HANDLE hSrc;
|
||
IO_STATUS_BLOCK iosb;
|
||
|
||
|
||
InitializeObjectAttributes(
|
||
&oaSrc,
|
||
&ustrSrc,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL);
|
||
|
||
Status = NtCreateFile(
|
||
&hSrc,
|
||
FILE_WRITE_ATTRIBUTES |
|
||
FILE_READ_ATTRIBUTES |
|
||
DELETE |
|
||
SYNCHRONIZE,
|
||
&oaSrc,
|
||
&iosb,
|
||
NULL,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
FILE_SHARE_READ,
|
||
FILE_OPEN,
|
||
FILE_DIRECTORY_FILE |
|
||
FILE_SYNCHRONOUS_IO_NONALERT,
|
||
pOpenIfJPEa,
|
||
cbOpenIfJPEa);
|
||
|
||
DFSM_TRACE_ERROR_HIGH(Status, ALL_ERROR, MoveFileOrJP_Error_NtCreateFile, LOGSTATUS(Status));
|
||
|
||
if (Status == STATUS_DFS_EXIT_PATH_FOUND) {
|
||
|
||
Status = NtCreateFile(
|
||
&hSrc,
|
||
FILE_READ_ATTRIBUTES |
|
||
FILE_WRITE_ATTRIBUTES |
|
||
DELETE |
|
||
SYNCHRONIZE,
|
||
&oaSrc,
|
||
&iosb,
|
||
NULL,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
FILE_SHARE_READ,
|
||
FILE_OPEN,
|
||
FILE_SYNCHRONOUS_IO_NONALERT,
|
||
pOpenIfJPEa,
|
||
cbOpenIfJPEa);
|
||
|
||
DFSM_TRACE_ERROR_HIGH(Status, ALL_ERROR, MoveFileOrJP_Error_NtCreateFile2, LOGSTATUS(Status));
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
Status = iosb.Status;
|
||
|
||
}
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
PFILE_RENAME_INFORMATION pRenameInfo;
|
||
ULONG cbRenameInfo;
|
||
|
||
cbRenameInfo = sizeof(FILE_RENAME_INFORMATION) + ustrTgt.Length;
|
||
|
||
pRenameInfo = (PFILE_RENAME_INFORMATION) new BYTE [cbRenameInfo];
|
||
|
||
if (pRenameInfo != NULL) {
|
||
|
||
pRenameInfo->ReplaceIfExists = FALSE;
|
||
pRenameInfo->RootDirectory = NULL;
|
||
pRenameInfo->FileNameLength = ustrTgt.Length;
|
||
CopyMemory(
|
||
&pRenameInfo->FileName[0],
|
||
ustrTgt.Buffer,
|
||
ustrTgt.Length);
|
||
|
||
Status = NtSetInformationFile(
|
||
hSrc,
|
||
&iosb,
|
||
pRenameInfo,
|
||
cbRenameInfo,
|
||
FileRenameInformation);
|
||
|
||
DFSM_TRACE_ERROR_HIGH(Status, ALL_ERROR, MoveFileOrJP_Error_NtSetInformationFile, LOGSTATUS(Status));
|
||
delete [] pRenameInfo;
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
Status = iosb.Status;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
DFSM_TRACE_HIGH(ERROR, MoveFileOrJP_Error3, LOGSTATUS(Status));
|
||
}
|
||
|
||
NtClose( hSrc );
|
||
|
||
} // end if successfully opened src
|
||
|
||
} // end if successfull converted dos names to nt names.
|
||
|
||
if (ustrSrc.Buffer != NULL) {
|
||
|
||
RtlFreeUnicodeString( &ustrSrc );
|
||
|
||
}
|
||
|
||
if (ustrTgt.Buffer != NULL) {
|
||
|
||
RtlFreeUnicodeString( &ustrTgt );
|
||
|
||
}
|
||
|
||
return( Status );
|
||
|
||
}
|