You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2355 lines
69 KiB
2355 lines
69 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 2000, Microsoft Corporation
|
|
//
|
|
// File: DfsApiFrontEnd.cxx
|
|
//
|
|
// Contents: Contains the support routines for the administering DFS
|
|
// The DFS api server implementation uses this.
|
|
// Also, any other admin tool that does not use the API
|
|
// can use the facilities provided here to administer DFS.
|
|
//
|
|
// Classes: none.
|
|
//
|
|
// History: Feb. 8 2000, Author: udayh
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
#include "DfsRootFolder.hxx"
|
|
#include "DfsReferral.hxx"
|
|
#include "lm.h"
|
|
#include "lmdfs.h"
|
|
#include "DfsRegistryStore.hxx"
|
|
#include "DfsADBlobStore.hxx"
|
|
#include "DfsADBlobRootFolder.hxx"
|
|
#include "DfsEnterpriseStore.hxx"
|
|
#include "DfsInit.hxx"
|
|
#include "netdfs.h"
|
|
#include "DomainControllerSupport.hxx"
|
|
#include "dfsfsctl.h"
|
|
//
|
|
// logging specific includes
|
|
//
|
|
#include "DfsApiFrontEnd.tmh"
|
|
|
|
DFSSTATUS
|
|
DfsDirectApiAllocContext(
|
|
OUT PDFS_DIRECT_API_CONTEXT *ppApiContext,
|
|
IN PUNICODE_STRING pDfsPathName,
|
|
IN PUNICODE_STRING pServerName,
|
|
IN PUNICODE_STRING pShareName);
|
|
|
|
VOID
|
|
DfsDirectApiFreeContext(
|
|
PDFS_DIRECT_API_CONTEXT pApiContext);
|
|
|
|
|
|
|
|
//
|
|
//dfsdev: validate input arguments.
|
|
//
|
|
#define API_VALIDATE_ARGUMENTS(_a, _b, _c, _d) \
|
|
{ \
|
|
if ((((_b) != NULL) && (IS_VALID_SERVER_TOKEN(_b, wcslen(_b)) == FALSE))) \
|
|
{ \
|
|
(_d) = ERROR_INVALID_NAME; \
|
|
} \
|
|
else \
|
|
{ \
|
|
(_d) = ERROR_SUCCESS; \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// Valid link states as published by lmdfs.h.
|
|
// dfsdev: migrate this to the header file eventually.
|
|
//
|
|
inline BOOLEAN
|
|
IsValidReplicaState( DWORD State )
|
|
{
|
|
return ( State == DFS_STORAGE_STATE_OFFLINE ||
|
|
State == DFS_STORAGE_STATE_ONLINE );
|
|
}
|
|
|
|
//
|
|
// Unfortunately these values are such that bitwise OR isn't
|
|
// sufficient.
|
|
//
|
|
inline BOOLEAN
|
|
IsValidLinkState( DWORD State )
|
|
{
|
|
return ( State == DFS_VOLUME_STATE_OK ||
|
|
State == DFS_VOLUME_STATE_OFFLINE ||
|
|
State == DFS_VOLUME_STATE_ONLINE );
|
|
}
|
|
|
|
inline BOOLEAN
|
|
IsValidGetInfoLevel( DWORD Level )
|
|
{
|
|
return ((Level >= 100 && Level <= 102) ||
|
|
(Level >= 1 && Level <= 4));
|
|
}
|
|
|
|
inline BOOLEAN
|
|
IsValidSetInfoLevel( DWORD Level )
|
|
{
|
|
return (Level >= 100 && Level <= 102);
|
|
}
|
|
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsAddValidate(
|
|
LPWSTR DfsPathName,
|
|
LPWSTR ServerName,
|
|
LPWSTR ShareName,
|
|
LPWSTR Comment,
|
|
DWORD Flags )
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
|
|
API_VALIDATE_ARGUMENTS( DfsPathName, ServerName, ShareName, Status);
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Null or empty server/share names are illegal.
|
|
// The DfsPathName needs to have all three components \\x\y\z
|
|
// but that'll get indirectly validated when we do DfsGetRootFolder.
|
|
//
|
|
if ((IsEmptyString( ServerName )) ||
|
|
(IsEmptyString( ShareName )) ||
|
|
(IsEmptyString( DfsPathName )))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Check for valid flags.
|
|
//
|
|
if (Flags &&
|
|
(Flags & ~(DFS_ADD_VOLUME | DFS_RESTORE_VOLUME)))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
StripTrailingSpacesFromPath(DfsPathName);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsAdd
|
|
//
|
|
// Arguments: DfsPathName - the pathname that is being added or updated
|
|
// ServerName - Name of server being added as target
|
|
// ShareName - Name of share on ServerName backing the link
|
|
// Comment - Comment associated if this is a new link
|
|
// Flags - If DFS_ADD_VOLUME, fail this request if link exists
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsAdd(
|
|
LPWSTR DfsPathName,
|
|
LPWSTR ServerName,
|
|
LPWSTR ShareName,
|
|
LPWSTR Comment,
|
|
DWORD Flags )
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsRootFolder *pRoot = NULL;
|
|
UNICODE_STRING DfsPath, LinkName;
|
|
|
|
DFS_TRACE_NORM(API, "[%!FUNC!]DfsAdd for path %ws, Server %ws Share %ws\n",
|
|
DfsPathName, ServerName, ShareName);
|
|
|
|
|
|
//
|
|
// Null or empty server/share names are illegal.
|
|
// The DfsPathName needs to have all three components \\x\y\z
|
|
// but that'll get indirectly validated when we do DfsGetRootFolder.
|
|
//
|
|
if ((IsEmptyString( ServerName )) ||
|
|
(IsEmptyString( ShareName )) ||
|
|
(IsEmptyString( DfsPathName )))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// One or two leading '\'s are valid components of a ServerName here.
|
|
//
|
|
if (ServerName[0] == UNICODE_PATH_SEP)
|
|
{
|
|
// advance past a max of two '\'s.
|
|
ServerName++;
|
|
if (ServerName[0] == UNICODE_PATH_SEP)
|
|
{
|
|
ServerName++;
|
|
}
|
|
}
|
|
|
|
API_VALIDATE_ARGUMENTS( DfsPathName, ServerName, ShareName, Status);
|
|
if (Status != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
|
|
//
|
|
// Check for valid flags.
|
|
//
|
|
if (Flags &&
|
|
(Flags & ~(DFS_ADD_VOLUME | DFS_RESTORE_VOLUME)))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
StripTrailingSpacesFromPath(DfsPathName);
|
|
|
|
|
|
Status = DfsRtlInitUnicodeStringEx( &DfsPath, DfsPathName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Get a root folder for the passed in path name.
|
|
//
|
|
Status = DfsGetRootFolder( &DfsPath,
|
|
&LinkName,
|
|
&pRoot );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Let the root folder know that a new api request is coming in
|
|
// This will give it a chance to let us know if api requests
|
|
// are allowed, and to synchronize with the metadata as necessary.
|
|
//
|
|
// For callers linking with the server library directly, this is a no-op.
|
|
// This is because they are supposed to perform a Initialize, Open,
|
|
// Synchronize, Close sequence.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
Status = pRoot->RootApiRequestPrologue( TRUE );
|
|
}
|
|
|
|
//
|
|
// If we got a root folder, see if there is a link that matches
|
|
// the rest of the name beyond the root.
|
|
//
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Check if this is a valid link name. A valid link is one that
|
|
// either is a perfect match of the LinkName, or the LinkName
|
|
// does not encompass as existing link.
|
|
//
|
|
Status = pRoot->ValidateLinkName( &LinkName );
|
|
|
|
//
|
|
// If validate link name succeeds, we can add the servername
|
|
// and share name as a target to this link
|
|
//
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// If someone wanted to create this as a new link, we fail the request here.
|
|
// the link already exists.
|
|
//
|
|
if (Flags & DFS_ADD_VOLUME)
|
|
{
|
|
Status = ERROR_FILE_EXISTS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Add the target to this link.
|
|
//
|
|
Status = pRoot->AddMetadataLinkReplica( &LinkName,
|
|
ServerName,
|
|
ShareName );
|
|
}
|
|
}
|
|
else if (Status == ERROR_NOT_FOUND)
|
|
{
|
|
//
|
|
// If the link was not found, at this point we are assured that this link is
|
|
// neither a prefix of an existing link not is any link a prefix of this link
|
|
// name (validate prefix above provides that assurance)
|
|
// We add a new link to this root.
|
|
//
|
|
Status = pRoot->AddMetadataLink( &DfsPath,
|
|
ServerName,
|
|
ShareName,
|
|
Comment );
|
|
}
|
|
|
|
//
|
|
// We are done with this request, so release our reference on the root folder.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
pRoot->RootApiRequestEpilogue( TRUE, Status );
|
|
}
|
|
}
|
|
|
|
|
|
pRoot->ReleaseReference();
|
|
}
|
|
done:
|
|
DFS_TRACE_ERROR_NORM(Status, API, "[%!FUNC!]for path %ws, Status %x\n",
|
|
DfsPathName, Status );
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsRemove
|
|
//
|
|
// Arguments: DfsPathName - the pathname that is being added or updated
|
|
// ServerName - Name of server being added as target
|
|
// ShareName - Name of share on ServerName backing the link
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsRemove(
|
|
LPWSTR DfsPathName,
|
|
LPWSTR ServerName,
|
|
LPWSTR ShareName )
|
|
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsRootFolder *pRoot = NULL;
|
|
BOOLEAN LastReplica = FALSE;
|
|
UNICODE_STRING DfsPath, LinkName;
|
|
|
|
DFS_TRACE_NORM(API, "[%!FUNC!] for path %ws, Server=%ws Share=%ws \n", DfsPathName, ServerName, ShareName);
|
|
|
|
//
|
|
// One or two leading '\'s are valid components of a ServerName here.
|
|
//
|
|
if ((ServerName != NULL) && (ServerName[0] == UNICODE_PATH_SEP))
|
|
{
|
|
// advance past a max of two '\'s.
|
|
ServerName++;
|
|
if (ServerName[0] == UNICODE_PATH_SEP)
|
|
{
|
|
ServerName++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// It is legal for the ServerName and the ShareName to be NULL. DfsPathName should
|
|
// have all its \\x\y\z components, but that gets validated below in DfsGetRootFolder.
|
|
//
|
|
API_VALIDATE_ARGUMENTS( DfsPathName, ServerName, ShareName, Status);
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
Status = DfsRtlInitUnicodeStringEx( &DfsPath, DfsPathName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
if (IsEmptyUnicodeString( &DfsPath ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
if (((ServerName == NULL) && (ShareName != NULL)) ||
|
|
((ServerName != NULL) && (ShareName == NULL)))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
//
|
|
//Get the root folder matching this name. We get a referenced root folder.
|
|
//
|
|
Status = DfsGetRootFolder( &DfsPath,
|
|
&LinkName,
|
|
&pRoot );
|
|
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Let the root folder know that a new api request is coming in
|
|
// This will give it a chance to let us know if api requests
|
|
// are allowed, and to synchronize with the metadata as necessary.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
Status = pRoot->RootApiRequestPrologue( TRUE );
|
|
}
|
|
|
|
//
|
|
// If we got a root folder, see if there is a link that matches
|
|
// the rest of the name beyond the root.
|
|
//
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if ((ServerName != NULL) && (ShareName != NULL))
|
|
{
|
|
//
|
|
// If the servername/sharename was provided, we just remove the matching target.
|
|
// If the serername/sharename was not provided, we remove the link itself.
|
|
//
|
|
Status = pRoot->RemoveMetadataLinkReplica( &LinkName,
|
|
ServerName,
|
|
ShareName,
|
|
&LastReplica );
|
|
//
|
|
// DFSDEV: REMOVE THIS FROM THE API!!!
|
|
//
|
|
if ((Status == ERROR_LAST_ADMIN) && (LastReplica == TRUE))
|
|
{
|
|
Status = pRoot->RemoveMetadataLink( &LinkName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = pRoot->RemoveMetadataLink( &LinkName );
|
|
}
|
|
//
|
|
// We now release the reference that we have on the root.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
pRoot->RootApiRequestEpilogue( TRUE, Status );
|
|
}
|
|
}
|
|
|
|
pRoot->ReleaseReference();
|
|
}
|
|
|
|
done:
|
|
DFS_TRACE_ERROR_NORM(Status, API, "DfsRemove for path %ws, Server=%ws Share=%ws, LastReplica=%d, Status %x\n",
|
|
DfsPathName, ServerName, ShareName, LastReplica, Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsEnumerate
|
|
//
|
|
// Arguments:
|
|
// LPWSTR DfsPathName - the pathname that is being added or updated
|
|
// DWORD Level - the level of information requested
|
|
// DWORD PrefMaxLen - a hint as to how many items to enumerate.
|
|
// LPBYTE pBuffer - the buffer to fill, passed in by the caller.
|
|
// LONG BufferSize, - the size of passed in buffer.
|
|
// LPDWORD pEntriesRead - the number of entries read, set on return
|
|
// LPDWORD pResumeHandle - the entry to start from, set to new value on return
|
|
// PLONG pNextSizeRequired - size required to successfully complete this call.
|
|
// usefule when this call return buffer overflow.
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsEnumerate(
|
|
LPWSTR DfsPathName,
|
|
DWORD Level,
|
|
DWORD PrefMaxLen,
|
|
LPBYTE pBuffer,
|
|
LONG BufferSize,
|
|
LPDWORD pEntriesRead,
|
|
LPDWORD pResumeHandle,
|
|
PLONG pNextSizeRequired )
|
|
{
|
|
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsRootFolder *pRoot = NULL;
|
|
LONG LevelInfoSize = 0;
|
|
DWORD EntriesToRead = 0;
|
|
UNICODE_STRING DfsPath, LinkName;
|
|
UNICODE_STRING DfsServer, DfsShare;
|
|
|
|
DFS_TRACE_NORM(API, "Dfs Enumerate for path %ws Level %d ResumeHandle %d PrefMaxLen %d Size %d\n",
|
|
DfsPathName, Level, pResumeHandle ? *pResumeHandle : 0, PrefMaxLen, BufferSize);
|
|
|
|
|
|
//
|
|
// validate the input parameters.
|
|
//
|
|
API_VALIDATE_ARGUMENTS( DfsPathName, NULL, NULL, Status);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Get the unicode dfs path.
|
|
//
|
|
Status = DfsRtlInitUnicodeStringEx( &DfsPath, DfsPathName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
if(!pEntriesRead || !pNextSizeRequired || !pBuffer)
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
Status = DfsApiSizeLevelHeader(Level, &LevelInfoSize);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
Status = DfsGetPathComponents( &DfsPath,
|
|
&DfsServer,
|
|
&DfsShare,
|
|
NULL );
|
|
|
|
//
|
|
// For legacy reasons, we need to support this call coming in with a null name.
|
|
// This is where the server would host a single root, and we always knew which root
|
|
// we wanted.
|
|
// To workaround this situation, if we get a null pathname, we get the first root
|
|
// folder and use that.
|
|
//
|
|
|
|
if (DfsShare.Length != 0)
|
|
{
|
|
Status = DfsGetRootFolder( &DfsPath,
|
|
&LinkName,
|
|
&pRoot );
|
|
}
|
|
else
|
|
{
|
|
Status = DfsGetOnlyRootFolder( &pRoot );
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Let the root folder know that a new api request is coming in
|
|
// This will give it a chance to let us know if api requests
|
|
// are allowed, and to synchronize with the metadata as necessary.
|
|
//
|
|
// For callers linking with the server library directly, this is a no-op.
|
|
// This is because they are supposed to perform a Initialize, Open,
|
|
// Synchronize, Close sequence.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
Status = pRoot->RootApiRequestPrologue( FALSE );
|
|
}
|
|
|
|
//
|
|
// If we got a root folder, see if there is a link that matches
|
|
// the rest of the name beyond the root.
|
|
//
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// If PrefMAxLen is 0xffffffff, we need to read all entries. OTherwise we need
|
|
// to read a subset of the entries. Let PrefMaxLen decide the number in the array
|
|
// of info parameters we pass back. (This is how we document our NEtDfsEnum api)
|
|
//
|
|
|
|
EntriesToRead = PrefMaxLen / LevelInfoSize;
|
|
if (EntriesToRead == 0) {
|
|
EntriesToRead = 1;
|
|
}
|
|
|
|
//
|
|
// Things could be in a state of flux: if the request is for more than twice what we
|
|
// already know about, limit it to 2 times our link count.
|
|
//
|
|
if (EntriesToRead > pRoot->RootEnumerationCount() * 2)
|
|
{
|
|
EntriesToRead = pRoot->RootEnumerationCount() * 2;
|
|
}
|
|
*pEntriesRead = EntriesToRead;
|
|
|
|
//
|
|
// Now enumerate the entries in the root in the passed in buffer.
|
|
//
|
|
|
|
Status = pRoot->EnumerateApiLinks( DfsPathName,
|
|
Level,
|
|
pBuffer,
|
|
BufferSize,
|
|
pEntriesRead,
|
|
pResumeHandle,
|
|
pNextSizeRequired );
|
|
|
|
//
|
|
// Release the root reference, and return status back to the caller.
|
|
//
|
|
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
pRoot->RootApiRequestEpilogue( FALSE, Status );
|
|
}
|
|
}
|
|
pRoot->ReleaseReference();
|
|
}
|
|
|
|
done:
|
|
DFS_TRACE_ERROR_NORM(Status, API, "Dfs Enumerate for path %ws EntriesRead %d ResumeHandle %d Status %x Size %d\n",
|
|
DfsPathName, pEntriesRead ? *pEntriesRead : 0,
|
|
pResumeHandle ? *pResumeHandle : 0,
|
|
Status,
|
|
pNextSizeRequired ? *pNextSizeRequired : 0);
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsGetInfo
|
|
//
|
|
// Arguments:
|
|
// LPWSTR DfsPathName - the pathname that is of interest.
|
|
// DWORD Level - the level of information requested
|
|
// LPBYTE pBuffer - the buffer to fill, passed in by caller
|
|
// LONG BufferSize, - the size of passed in buffer.
|
|
// PLONG pSizeRequired - size required to successfully complete this call.
|
|
// usefule when this call return buffer overflow.
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsGetInfo(
|
|
LPWSTR DfsPathName,
|
|
DWORD Level,
|
|
LPBYTE pBuffer,
|
|
LONG BufferSize,
|
|
PLONG pSizeRequired )
|
|
{
|
|
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
UNICODE_STRING DfsPath, LinkName;
|
|
DfsRootFolder *pRoot = NULL;
|
|
|
|
DFS_TRACE_NORM(API, "Dfs get info for path %ws Level %d\n",
|
|
DfsPathName, Level );
|
|
|
|
Status = DfsRtlInitUnicodeStringEx( &DfsPath, DfsPathName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
//
|
|
// Validate the input parameters.
|
|
//
|
|
API_VALIDATE_ARGUMENTS( DfsPathName, NULL, NULL, Status);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
if (!pSizeRequired || !pBuffer || (IsEmptyUnicodeString( &DfsPath )))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Level has to be 100...102 or 1...4 currently.
|
|
//
|
|
if (!IsValidGetInfoLevel( Level ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Get a referenced root folder for the passed in path.
|
|
//
|
|
Status = DfsGetRootFolder( &DfsPath,
|
|
&LinkName,
|
|
&pRoot );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
Status = pRoot->RootApiRequestPrologue( FALSE );
|
|
}
|
|
|
|
//
|
|
// If we got a root folder, see if there is a link that matches
|
|
// the rest of the name beyond the root.
|
|
//
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
//
|
|
// If we got a root folder, get the requested information into the passed
|
|
// in buffer.
|
|
//
|
|
Status = pRoot->GetApiInformation( &DfsPath,
|
|
&LinkName,
|
|
Level,
|
|
pBuffer,
|
|
BufferSize,
|
|
pSizeRequired );
|
|
//
|
|
//WE are done: release our reference on the root.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
pRoot->RootApiRequestEpilogue( FALSE, Status );
|
|
}
|
|
}
|
|
pRoot->ReleaseReference();
|
|
}
|
|
|
|
done:
|
|
DFS_TRACE_ERROR_NORM(Status, API, "Dfs get info for path %ws Status %x\n",
|
|
DfsPathName, Status );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsSetInfo
|
|
//
|
|
// Arguments:
|
|
// LPWSTR DfsPathName - the pathname that is being updated
|
|
// LPWSTR Server - the servername (optional) whose info is being set.
|
|
// LPWSTR Share - the share on the server.
|
|
// DWORD Level - the level of information being set
|
|
// LPBYTE pBuffer - the buffer holding the information to be set
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
DFSSTATUS
|
|
DfsSetInfo(
|
|
LPWSTR DfsPathName,
|
|
LPWSTR Server,
|
|
LPWSTR Share,
|
|
DWORD Level,
|
|
LPBYTE pBuffer)
|
|
{
|
|
return DfsSetInfoCheckAccess( DfsPathName,
|
|
Server,
|
|
Share,
|
|
Level,
|
|
pBuffer,
|
|
ERROR_SUCCESS );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsSetInfoCheckAccess
|
|
//
|
|
// Arguments:
|
|
// LPWSTR DfsPathName - the pathname that is being updated
|
|
// LPWSTR Server - the servername (optional) whose info is being set.
|
|
// LPWSTR Share - the share on the server.
|
|
// DWORD Level - the level of information being set
|
|
// LPBYTE pBuffer - the buffer holding the information to be set
|
|
// DFSSTATUS Status - access status if any.
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsSetInfoCheckAccess(
|
|
LPWSTR DfsPathName,
|
|
LPWSTR Server,
|
|
LPWSTR Share,
|
|
DWORD Level,
|
|
LPBYTE pBuffer,
|
|
DFSSTATUS AccessCheckStatus )
|
|
{
|
|
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsRootFolder *pRoot = NULL;
|
|
UNICODE_STRING DfsPath, LinkName;
|
|
|
|
DFS_TRACE_NORM(API, "[%!FUNC!]Dfs set info for path %ws AccessCheck Status %x Level %d\n",
|
|
DfsPathName, AccessCheckStatus, Level);
|
|
|
|
Status = DfsRtlInitUnicodeStringEx( &DfsPath, DfsPathName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
//
|
|
//validate the input arguments.
|
|
//
|
|
API_VALIDATE_ARGUMENTS( DfsPathName, NULL, NULL, Status);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// It is legal for the Server and Share args to be NULL,
|
|
// but the DfsPath must have \\x\y\z components. DfsGetRootFolder
|
|
// will validate that.
|
|
//
|
|
if (!pBuffer || !(*(PULONG)pBuffer) || (IsEmptyUnicodeString( &DfsPath )))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
// Server may have leading \\.
|
|
if ((Server != NULL) && (Server[0] == UNICODE_PATH_SEP))
|
|
{
|
|
// advance past a max of two '\'s.
|
|
Server++;
|
|
if (Server[0] == UNICODE_PATH_SEP)
|
|
{
|
|
Server++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Level has to be 100...102 currently.
|
|
//
|
|
if (!IsValidSetInfoLevel( Level ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Get a referenced root folder for the passed in path.
|
|
//
|
|
Status = DfsGetRootFolder( &DfsPath,
|
|
&LinkName,
|
|
&pRoot );
|
|
|
|
//
|
|
// For cluster, checkpointing may add stuff to the registry which
|
|
// we had not found when we started off. So, make an attempt
|
|
// to look into the registry.
|
|
// In future, this should work across all the roots, but for now
|
|
// this is sort of a quick means of getting clusters going.
|
|
//
|
|
|
|
if (Status == ERROR_NOT_FOUND)
|
|
{
|
|
DfsRegistryStore *pRegStore = NULL;
|
|
UNICODE_STRING DfsServer, DfsShare, NewDfsShare;
|
|
|
|
Status = DfsGetRegistryStore( &pRegStore );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGetPathComponents( &DfsPath,
|
|
&DfsServer,
|
|
&DfsShare,
|
|
NULL );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsCreateUnicodeString( &NewDfsShare,
|
|
&DfsShare );
|
|
}
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = pRegStore->LookupNewRootByName( NewDfsShare.Buffer,
|
|
&pRoot );
|
|
|
|
DfsFreeUnicodeString( &NewDfsShare );
|
|
}
|
|
|
|
pRegStore->ReleaseReference();
|
|
}
|
|
}
|
|
|
|
//
|
|
// dfsdev: special case master and standby here.
|
|
//
|
|
// We call into the service with the appropriate bits to
|
|
// set the root in a standby mode or a master mode.
|
|
//
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
BOOLEAN Done = FALSE;
|
|
if (Level == 101)
|
|
{
|
|
PDFS_INFO_101 pInfo101 = (PDFS_INFO_101)*((PULONG_PTR)pBuffer);
|
|
DFS_TRACE_NORM(API, "Dfs set info for path %ws Level %d State is %d\n",
|
|
DfsPathName, Level, pInfo101->State);
|
|
|
|
if (pInfo101->State == DFS_VOLUME_STATE_RESYNCHRONIZE)
|
|
{
|
|
DFS_TRACE_NORM(API, "[%!FUNC!]Root folder set to Master\n");
|
|
|
|
//
|
|
// Check if resynchronize should succeed of fail, based
|
|
// on the passed in access check status.
|
|
//
|
|
Status = pRoot->CheckResynchronizeAccess(AccessCheckStatus);
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = pRoot->SetRootResynchronize();
|
|
}
|
|
Done = TRUE;
|
|
}
|
|
else if (pInfo101->State == DFS_VOLUME_STATE_STANDBY)
|
|
{
|
|
ASSERT(Status == ERROR_SUCCESS);
|
|
Status = AccessCheckStatus;
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (DfsIsMachineCluster())
|
|
{
|
|
DFS_TRACE_NORM(API, "[%!FUNC!]Root folder set to standby\n");
|
|
pRoot->SetRootStandby();
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ideally, we should purge the referral(s) for this root and all its links.
|
|
// For that we'll have to iterate over all its links (among other things).
|
|
// That can be expensive and might even lead to DoS attacks. However
|
|
//
|
|
Done = TRUE;
|
|
|
|
}
|
|
else
|
|
{
|
|
if ((Server == NULL) && (Share == NULL))
|
|
{
|
|
if (!IsValidLinkState(pInfo101->State))
|
|
{
|
|
Done = TRUE;
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!IsValidReplicaState(pInfo101->State))
|
|
{
|
|
Done = TRUE;
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Done)
|
|
{
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point, if we are to be denied access, we should not
|
|
// proceed.
|
|
//
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = AccessCheckStatus;
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Check if the root folder is available for api calls. If not,
|
|
// we return an error back to the caller:
|
|
// dfsdev: check if this error is a valid one to return.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
Status = pRoot->RootApiRequestPrologue( TRUE );
|
|
}
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// If we successfully got a root folder, set the specifed information
|
|
// on the passed in path/server/share combination.
|
|
//
|
|
Status = pRoot->SetApiInformation( &LinkName,
|
|
Server,
|
|
Share,
|
|
Level,
|
|
pBuffer );
|
|
//
|
|
// release our reference on the root folder.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
pRoot->RootApiRequestEpilogue( TRUE, Status );
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
if(pRoot)
|
|
{
|
|
pRoot->ReleaseReference();
|
|
}
|
|
|
|
DFS_TRACE_ERROR_NORM(Status, API, "Dfs set info for path %ws Status %x\n",
|
|
DfsPathName, Status );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsAddStandaloneRoot
|
|
//
|
|
// Arguments:
|
|
// LPWSTR ShareName - the share name to use
|
|
// LPWSTR Comment - the comment for this root
|
|
// DWORD Flags - the flags for this root
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsAddStandaloneRoot(
|
|
LPWSTR MachineName,
|
|
LPWSTR ShareName,
|
|
LPWSTR Comment,
|
|
DWORD Flags )
|
|
{
|
|
|
|
DFS_TRACE_NORM(API, "Dfs add standalone root Machine Share %ws\n",
|
|
ShareName);
|
|
|
|
//
|
|
// dfsdev: use these parameters.
|
|
//
|
|
UNREFERENCED_PARAMETER(Comment);
|
|
UNREFERENCED_PARAMETER(Flags);
|
|
|
|
DfsRegistryStore *pRegStore;
|
|
DFSSTATUS Status;
|
|
|
|
//
|
|
// It's legal to get a NULL MachineName to signify '.', but the
|
|
// ShareName can't be empty.
|
|
//
|
|
if (IsEmptyString( ShareName ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
|
|
Status = DfsCheckServerRootHandlingCapability();
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
//
|
|
// This is a registry store specific function. So finf the registry store.
|
|
// This gives us a referenced registry store pointer.
|
|
//
|
|
Status = DfsGetRegistryStore( &pRegStore );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Create the standalone root, and release our reference on the registry
|
|
// store.
|
|
//
|
|
Status = pRegStore->CreateStandaloneRoot( MachineName,
|
|
ShareName,
|
|
Comment );
|
|
pRegStore->ReleaseReference();
|
|
}
|
|
|
|
done:
|
|
DFS_TRACE_ERROR_NORM(Status, API, "Dfs add standalone root status %x Machine Share %ws\n",
|
|
Status, ShareName);
|
|
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsDeleteStandaloneRoot
|
|
//
|
|
// Arguments:
|
|
// LPWSTR ShareName - the root to delete
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsDeleteStandaloneRoot(
|
|
LPWSTR ServerName,
|
|
LPWSTR ShareName )
|
|
{
|
|
DfsRegistryStore *pRegStore;
|
|
DFSSTATUS Status;
|
|
|
|
DFS_TRACE_NORM(API, "Dfs delete standalone root Machine Share %ws\n",
|
|
ShareName);
|
|
|
|
if (IsEmptyString( ServerName ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
return Status;
|
|
}
|
|
|
|
if (IsEmptyString( ShareName ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// This is registry store specific function, so get the registry store.
|
|
//
|
|
Status = DfsGetRegistryStore( &pRegStore );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Delete the standalone root specified by the passed in sharename
|
|
// and release our reference on the registry store.
|
|
//
|
|
Status = pRegStore->DeleteStandaloneRoot( NULL,
|
|
ShareName );
|
|
pRegStore->ReleaseReference();
|
|
}
|
|
DFS_TRACE_ERROR_NORM(Status, API, "Dfs delete standalone root status %x Machine Share %ws\n",
|
|
Status, ShareName);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsEnumerateLocalRoots(
|
|
LPWSTR MachineName,
|
|
BOOLEAN DomainRoots,
|
|
LPBYTE pBuffer,
|
|
ULONG BufferSize,
|
|
PULONG pEntriesRead,
|
|
DWORD MaxEntriesToRead,
|
|
LPDWORD pResumeHandle,
|
|
PULONG pSizeRequired )
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(MachineName);
|
|
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
ULONG TotalSize = 0;
|
|
ULONG EntriesRead = 0;
|
|
ULONG_PTR CurrentBuffer = (ULONG_PTR)pBuffer;
|
|
ULONG CurrentSize = BufferSize;
|
|
DfsStore *pStore;
|
|
ULONG CurrentNumRoots = 0;
|
|
|
|
BOOLEAN OverFlow = FALSE;
|
|
ASSERT(pBuffer != NULL);
|
|
ASSERT(pEntriesRead != NULL);
|
|
ASSERT(pSizeRequired != NULL);
|
|
|
|
DFS_TRACE_NORM(API, "[%!FUNC!]for path %ws\n", MachineName);
|
|
|
|
//
|
|
// Call the store enumerator of each registered store.
|
|
//
|
|
for (pStore = DfsServerGlobalData.pRegisteredStores;
|
|
(pStore != NULL) && (Status == ERROR_SUCCESS);
|
|
pStore = pStore->pNextRegisteredStore) {
|
|
|
|
Status = pStore->EnumerateRoots( DomainRoots,
|
|
&CurrentBuffer,
|
|
&CurrentSize,
|
|
&EntriesRead,
|
|
MaxEntriesToRead,
|
|
&CurrentNumRoots,
|
|
pResumeHandle,
|
|
&TotalSize );
|
|
if (Status == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
OverFlow = TRUE;
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (OverFlow == TRUE)
|
|
{
|
|
Status = ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
else if (EntriesRead == 0)
|
|
{
|
|
//
|
|
// We should never return SUCCESS if we aren't
|
|
// returning entries.
|
|
//
|
|
Status = ERROR_NO_MORE_ITEMS;
|
|
}
|
|
else if (pResumeHandle)
|
|
{
|
|
*pResumeHandle = CurrentNumRoots;
|
|
}
|
|
|
|
*pEntriesRead = EntriesRead;
|
|
*pSizeRequired = TotalSize;
|
|
}
|
|
|
|
DFS_TRACE_ERROR_NORM(Status, API, "[%!FUNC!]for path %ws, Status %x\n",
|
|
MachineName, Status );
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsEnumerateRoots
|
|
//
|
|
// Arguments:
|
|
// LPBYTE pBuffer - the buffer to fill, passed in by teh caller
|
|
// LONG BufferSize, - the size of passed in buffer.
|
|
// LPDWORD pEntriesRead - the number of entries read, set on return
|
|
// LPDWORD pResumeHandle - the entry to start from, set to new value on return
|
|
// PLONG pSizeRequired - size required to successfully complete this call.
|
|
// usefule when this call return buffer overflow.
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsEnumerateRoots(
|
|
IN LPWSTR DfsName,
|
|
IN BOOLEAN DomainRoots,
|
|
IN DWORD PrefMaxLen,
|
|
IN LPBYTE pBuffer,
|
|
IN ULONG BufferSize,
|
|
OUT PULONG pEntriesRead,
|
|
IN OUT PULONG pResumeHandle,
|
|
OUT PULONG pSizeRequired )
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
UNICODE_STRING DfsPath;
|
|
UNICODE_STRING NameContext;
|
|
UNICODE_STRING RemainingPart;
|
|
DWORD MaxEntriesToRead = 0;
|
|
LONG LevelInfoSize = 0;
|
|
|
|
DFS_TRACE_NORM(API, "Dfs enumerate roots: resume handle %d\n",
|
|
pResumeHandle ? *pResumeHandle : 0);
|
|
|
|
if(!pEntriesRead || !pSizeRequired || !pBuffer)
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
Status = DfsRtlInitUnicodeStringEx( &DfsPath, DfsName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
if (IsEmptyUnicodeString( &DfsPath ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
Status = DfsGetFirstComponent( &DfsPath,
|
|
&NameContext,
|
|
&RemainingPart );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (RemainingPart.Length != 0)
|
|
{
|
|
//
|
|
// return not found for now... dfsdev: fix on client.
|
|
//
|
|
Status = ERROR_PATH_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
Status = DfsApiSizeLevelHeader( (DomainRoots == TRUE) ? 200 : 300, &LevelInfoSize);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// If PrefMAxLen is 0xffffffff, we need to read all entries. OTherwise we need
|
|
// to read a subset of the entries. Let PrefMaxLen decide the number in the array
|
|
// of info parameters we pass back. (This is how we document our NEtDfsEnum api)
|
|
//
|
|
MaxEntriesToRead = PrefMaxLen / LevelInfoSize;
|
|
if (MaxEntriesToRead == 0) {
|
|
MaxEntriesToRead = 1;
|
|
}
|
|
|
|
//
|
|
// Things could be in a state of flux: if the request is for more than the maximum
|
|
// number of roots allowed on a single server, then cap it right there.
|
|
//
|
|
else if (MaxEntriesToRead > MAX_DFS_NAMESPACES)
|
|
{
|
|
MaxEntriesToRead = MAX_DFS_NAMESPACES;
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (DfsIsMachineDC() &&
|
|
DfsIsNameContextDomainName(&NameContext))
|
|
{
|
|
|
|
if (DomainRoots != TRUE)
|
|
{
|
|
Status = ERROR_INVALID_NAME;
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = DfsDcEnumerateRoots( NULL,
|
|
pBuffer,
|
|
BufferSize,
|
|
pEntriesRead,
|
|
MaxEntriesToRead,
|
|
pResumeHandle,
|
|
pSizeRequired);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (DomainRoots == TRUE)
|
|
{
|
|
Status = ERROR_INVALID_NAME;
|
|
}
|
|
else
|
|
{
|
|
Status = DfsEnumerateLocalRoots( NULL,
|
|
DomainRoots,
|
|
pBuffer,
|
|
BufferSize,
|
|
pEntriesRead,
|
|
MaxEntriesToRead,
|
|
pResumeHandle,
|
|
pSizeRequired );
|
|
}
|
|
}
|
|
}
|
|
DFS_TRACE_ERROR_NORM(Status, API, "Dfs enumerate roots, status %x\n",
|
|
Status );
|
|
|
|
done:
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsAddADBlobRoot
|
|
//
|
|
// Arguments:
|
|
// LPWSTR MachineName,
|
|
// LPWSTR DcName,
|
|
// LPWSTR ShareName,
|
|
// LPWSTR LogicalShare,
|
|
// LPWSTR Comment,
|
|
// DWORD Flags,
|
|
// PDFSM_ROOT_LIST *ppRootList )
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsAddADBlobRoot(
|
|
LPWSTR MachineName,
|
|
LPWSTR DcName,
|
|
LPWSTR ShareName,
|
|
LPWSTR LogicalShare,
|
|
LPWSTR Comment,
|
|
BOOLEAN NewFtDfs,
|
|
DWORD Flags,
|
|
PVOID ppList )
|
|
{
|
|
|
|
DFS_TRACE_NORM(API, "Dfs add ad blob root Machine Share %ws\n",
|
|
ShareName);
|
|
|
|
PDFSM_ROOT_LIST *ppRootList = (PDFSM_ROOT_LIST *)ppList;
|
|
DfsADBlobStore *pADBlobStore = NULL;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
UNICODE_STRING UseMachineName;
|
|
size_t LogicalShareLength = 0;
|
|
|
|
Status = DfsRtlInitUnicodeStringEx(&UseMachineName, NULL);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// dfsdev: use these parameters.
|
|
//
|
|
|
|
UNREFERENCED_PARAMETER(MachineName);
|
|
UNREFERENCED_PARAMETER(Comment);
|
|
UNREFERENCED_PARAMETER(Flags);
|
|
|
|
//
|
|
// MachineName and ppList passed in are unused. ShareName
|
|
// and LogicalShare must be valid args.
|
|
//
|
|
if (IsEmptyString( ShareName ) ||
|
|
IsEmptyString( LogicalShare ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
Status = DfsStringCchLength( LogicalShare, MAXUSHORT, &LogicalShareLength );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Logical share cannot have illegal characters. These are currently the
|
|
// STANDARD_ILLEGAL_CHARS. Therefore, SPACE is a valid character.
|
|
//
|
|
if (!IS_VALID_TOKEN( LogicalShare, LogicalShareLength ))
|
|
{
|
|
Status = ERROR_INVALID_NAME;
|
|
goto done;
|
|
}
|
|
|
|
|
|
//
|
|
// This is a registry store specific function. So find the registry store.
|
|
// This gives us a referenced registry store pointer.
|
|
//
|
|
Status = DfsGetADBlobStore( &pADBlobStore );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGetMachineName( &UseMachineName);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
Status = pADBlobStore->CreateADBlobRoot( UseMachineName.Buffer,
|
|
DcName,
|
|
ShareName,
|
|
LogicalShare,
|
|
Comment,
|
|
NewFtDfs);
|
|
DfsReleaseMachineName(&UseMachineName);
|
|
}
|
|
pADBlobStore->ReleaseReference();
|
|
}
|
|
|
|
done:
|
|
|
|
DFS_TRACE_ERROR_NORM(Status, API, "Dfs add ad blob root status %x Machine Share %ws\n",
|
|
Status, ShareName);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsDeleteADBlobRoot
|
|
//
|
|
// Arguments:
|
|
// LPWSTR MachineName,
|
|
// LPWSTR DcName,
|
|
// LPWSTR ShareName,
|
|
// LPWSTR LogicalShare,
|
|
// DWORD Flags,
|
|
// PDFSM_ROOT_LIST *ppRootList )
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code other wise.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsDeleteADBlobRoot(
|
|
LPWSTR MachineName,
|
|
LPWSTR DcName,
|
|
LPWSTR ShareName,
|
|
LPWSTR LogicalShare,
|
|
DWORD Flags,
|
|
PVOID ppList )
|
|
{
|
|
DFS_TRACE_NORM(API, "Dfs delete ad blob root Machine Share %ws\n",
|
|
ShareName);
|
|
|
|
UNREFERENCED_PARAMETER(ppList);
|
|
|
|
DfsADBlobStore *pADBlobStore = NULL;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
|
|
UNICODE_STRING UseMachineName;
|
|
|
|
Status = DfsRtlInitUnicodeStringEx(&UseMachineName, NULL);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// MachineName and ppList passed in are unused. ShareName
|
|
// and LogicalShare must be valid args.
|
|
//
|
|
if (IsEmptyString( ShareName ) ||
|
|
IsEmptyString( LogicalShare ))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// This gives us a referenced ad blob store pointer.
|
|
//
|
|
Status = DfsGetADBlobStore( &pADBlobStore );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
|
|
if ((Flags & DFS_FORCE_REMOVE) == DFS_FORCE_REMOVE)
|
|
{
|
|
if ((DfsServerGlobalData.IsDc == FALSE) ||
|
|
(IsEmptyString(MachineName)))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
Status = pADBlobStore->DeleteADBlobRootForced( MachineName,
|
|
DcName,
|
|
ShareName,
|
|
LogicalShare );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = DfsGetMachineName( &UseMachineName);
|
|
if (Status == ERROR_SUCCESS) {
|
|
Status = pADBlobStore->DeleteADBlobRoot( UseMachineName.Buffer,
|
|
DcName,
|
|
ShareName,
|
|
LogicalShare );
|
|
DfsReleaseMachineName(&UseMachineName);
|
|
}
|
|
}
|
|
pADBlobStore->ReleaseReference();
|
|
}
|
|
|
|
done:
|
|
|
|
DFS_TRACE_ERROR_NORM(Status, API, "Dfs delete ad blob root status %x Machine Share %ws\n",
|
|
Status, ShareName);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsClean
|
|
//
|
|
// Arguments:
|
|
// HostServerName - target server name to contact
|
|
// ShareNameToClean - name of the unwanted to share
|
|
//
|
|
//
|
|
// Returns: Status:
|
|
//
|
|
// Description: This function contacts the registry of a given machine to remove
|
|
// its references to an obsolete root name in its registry. The root
|
|
// can be standalone or fault-tolerant.
|
|
// This is primarily a utility function. We don't assume that the roots
|
|
// are recognized or even that the stores registered. CleanRegEntry
|
|
// are static functions.
|
|
//+-------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsClean(
|
|
LPWSTR HostServerName,
|
|
LPWSTR ShareNameToClean
|
|
)
|
|
{
|
|
DFSSTATUS Status = ERROR_NOT_FOUND;
|
|
DFSSTATUS CleanStatus;
|
|
|
|
DFS_TRACE_LOW(REFERRAL_SERVER, "DfsClean: \\%ws\\%ws\n",
|
|
HostServerName, ShareNameToClean);
|
|
|
|
if ( IsEmptyString(HostServerName) ||
|
|
IsEmptyString(ShareNameToClean))
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Ask each store registered to remove this share
|
|
// from this server's registry. Each store will take care of
|
|
// \Domain \Standalone and \Enterprise portions of the
|
|
// reg keys.
|
|
//
|
|
CleanStatus = DfsADBlobStore::CleanRegEntry( HostServerName,
|
|
ShareNameToClean );
|
|
|
|
//
|
|
// Return SUCCESS if at least one store managed to do
|
|
// the clean successfully.
|
|
//
|
|
if (CleanStatus == ERROR_SUCCESS)
|
|
{
|
|
Status = CleanStatus;
|
|
}
|
|
|
|
CleanStatus = DfsRegistryStore::CleanRegEntry( HostServerName,
|
|
ShareNameToClean );
|
|
if (CleanStatus == ERROR_SUCCESS)
|
|
{
|
|
Status = CleanStatus;
|
|
}
|
|
|
|
// no-op.
|
|
CleanStatus = DfsEnterpriseStore::CleanRegEntry( HostServerName,
|
|
ShareNameToClean );
|
|
if (CleanStatus == ERROR_SUCCESS)
|
|
{
|
|
Status = CleanStatus;
|
|
}
|
|
|
|
DFS_TRACE_ERROR_NORM(Status, API, "DfsClean: \\%ws\\%ws Status = %x\n",
|
|
HostServerName, ShareNameToClean, Status );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsEnum(
|
|
IN LPWSTR DfsName,
|
|
IN DWORD Level,
|
|
IN DWORD PrefMaxLen,
|
|
OUT LPBYTE *pBuffer,
|
|
OUT LPDWORD pEntriesRead,
|
|
IN OUT LPDWORD pResumeHandle)
|
|
|
|
{
|
|
LONG BufferSize = 0;
|
|
LONG SizeRequired = 0;
|
|
ULONG MaxRetry = 0;
|
|
ULONG EntriesRead = 0;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
LPDFS_INFO_1 pInfo1 = NULL;
|
|
|
|
DFS_TRACE_LOW( API, "DfsEnum from ApiFrontEnd %ws\n", DfsName);
|
|
|
|
MaxRetry = 5;
|
|
BufferSize = sizeof(DFS_INFO_1);
|
|
|
|
|
|
if(!pEntriesRead || !pBuffer || (Level == 0))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
|
|
do
|
|
{
|
|
pInfo1 = (LPDFS_INFO_1) MIDL_user_allocate( BufferSize );
|
|
if (pInfo1 == NULL)
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
if (Level != 200 && Level != 300)
|
|
{
|
|
Status = DfsEnumerate( DfsName,
|
|
Level,
|
|
PrefMaxLen,
|
|
(LPBYTE)pInfo1,
|
|
BufferSize,
|
|
&EntriesRead,
|
|
pResumeHandle,
|
|
&SizeRequired );
|
|
} else {
|
|
//
|
|
// Level 200 takes in a domain and 300, the root server.
|
|
//
|
|
Status = DfsEnumerateRoots( DfsName,
|
|
(Level == 200) ? TRUE : FALSE,
|
|
PrefMaxLen,
|
|
(LPBYTE)pInfo1,
|
|
BufferSize,
|
|
&EntriesRead,
|
|
pResumeHandle,
|
|
(PULONG)&SizeRequired ); // xxx supw
|
|
}
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
MIDL_user_free( pInfo1 );
|
|
}
|
|
|
|
if (Status == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
BufferSize = SizeRequired;
|
|
}
|
|
|
|
} while ( (Status == ERROR_BUFFER_OVERFLOW) && (MaxRetry--) );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
*pEntriesRead = EntriesRead;
|
|
*pBuffer = (LPBYTE)pInfo1;
|
|
}
|
|
|
|
done:
|
|
|
|
|
|
DFS_TRACE_ERROR_HIGH( Status, API, "DfsEnum from ApiFrontEnd %ws, Status %x\n", DfsName, Status);
|
|
return Status;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsRenameLinks
|
|
//
|
|
// Arguments: DfsPathName - the root that is affected
|
|
// OldName - Old domain name
|
|
// NewName - new domain name
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code otherwise.
|
|
//
|
|
// Description: Given the old domain name and a new one to replace it with
|
|
// this iterates through all the links in the given namespace and
|
|
// fixes obsolete references to the old domain.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsRenameLinks(
|
|
IN LPWSTR DfsPathString,
|
|
IN LPWSTR OldDomainName,
|
|
IN LPWSTR NewDomainName)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsRootFolder *pRoot = NULL;
|
|
UNICODE_STRING DfsPathName;
|
|
|
|
Status = DfsRtlInitUnicodeStringEx( &DfsPathName, DfsPathString);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
API_VALIDATE_ARGUMENTS( DfsPathName, OldDomainName, NewDomainName, Status );
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Null or empty server/share names are illegal.
|
|
// The DfsPathName needs to have all three components \\x\y\z
|
|
// but that'll get indirectly validated when we do DfsGetRootFolder.
|
|
//
|
|
if ((IsEmptyUnicodeString( &DfsPathName )) ||
|
|
(IsEmptyString( OldDomainName )) ||
|
|
(IsEmptyString( NewDomainName )))
|
|
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get the root folder matching the given namespace.
|
|
// We get a referenced root folder.
|
|
//
|
|
Status = DfsGetRootFolder( &DfsPathName,
|
|
NULL,
|
|
&pRoot );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Let the root folder know that a new api request is coming in
|
|
// This will give it a chance to let us know if api requests
|
|
// are allowed, and to synchronize with the metadata as necessary.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
Status = pRoot->RootApiRequestPrologue( TRUE );
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Replace all references to OldDomainName with the new.
|
|
//
|
|
Status = pRoot->RenameLinks( OldDomainName,
|
|
NewDomainName );
|
|
//
|
|
// We are done: release our reference on the root.
|
|
//
|
|
if (!DfsCheckDirectMode())
|
|
{
|
|
pRoot->RootApiRequestEpilogue( FALSE, Status );
|
|
}
|
|
}
|
|
pRoot->ReleaseReference();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsDirectApiOpen
|
|
//
|
|
// Arguments: DfsPath - the root that is affected
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code otherwise.
|
|
//
|
|
// Description: The most important task this does is to perform LdapConnect.
|
|
// Callers are supposed to perform a corresponding DirectApiClose when done.
|
|
// This protocol is handy because direct-mode users may execute many
|
|
// api requests in between an DirectApiOpen and its DirectApiClose. There's no
|
|
// need to synchronize per each such request.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsDirectApiOpen(
|
|
IN LPWSTR DfsNameSpace,
|
|
IN LPWSTR DCName,
|
|
OUT PVOID *pLibHandle)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsRootFolder *pRoot = NULL;
|
|
PDFS_DIRECT_API_CONTEXT pApiContext = NULL;
|
|
UNICODE_STRING DfsPathName;
|
|
UNICODE_STRING ServerName, ShareName;
|
|
|
|
LPWSTR UseDCName = NULL;
|
|
UNICODE_STRING DomainName;
|
|
DfsString *pPDC;
|
|
|
|
Status = DfsRtlInitUnicodeStringEx( &DfsPathName, DfsNameSpace);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
*pLibHandle = NULL;
|
|
|
|
do {
|
|
|
|
//
|
|
// We must have a valid-looking root.
|
|
//
|
|
Status = DfsGetPathComponents(&DfsPathName,
|
|
&ServerName,
|
|
&ShareName,
|
|
NULL );
|
|
|
|
if ( Status != ERROR_SUCCESS ||
|
|
ServerName.Length == 0 || ShareName.Length == 0 )
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
|
|
Status = DfsCreateUnicodeString( &DomainName, &ServerName);
|
|
|
|
|
|
//
|
|
// tread very carefully here:
|
|
// this is to fix a bug that we should fix in a better
|
|
// way in future, by changing the core of DFS to handle
|
|
// any domain name, instead of assuming all accesses are
|
|
// to the local domain
|
|
//
|
|
// all of dfs code assumes that we are running in the local
|
|
// domain. This does not work well for dfsutil.
|
|
// Here we are working around this by doing a couple of things:
|
|
// 1) we force a DC for the domain of interest. This gets stored
|
|
// in our global data structures, for all other accesses.
|
|
// 2) We force a new ad name context for this domain, again
|
|
// this gets stored in our global data structures, for all
|
|
// other accesses.
|
|
//
|
|
// Once we have created the object and have cached it we dont
|
|
// need this information anymore. The next direct open will
|
|
// overwrite this global information.
|
|
//
|
|
// this code is based on certain assumptions as to how the
|
|
// rest of the code works. If those assumptions are violated,
|
|
// things will break.
|
|
//
|
|
// assumption 1: RootApiPrologue will accept a DC name, and not
|
|
// forcefully get a new dc name.
|
|
// assumption 2: RootApiPrologue will cache the ldap connection
|
|
// to the DC, for the entire request till we call epilogue.
|
|
// assumption 3: Once we have created a root in this code,
|
|
// no other global information will be necessary when we
|
|
// flush out the information at a later point.
|
|
//
|
|
//
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (DfsIsThisADomainName(DomainName.Buffer) == ERROR_SUCCESS)
|
|
{
|
|
if (DCName != NULL)
|
|
{
|
|
Status = DfsSetBlobPDCName( DCName,
|
|
&pPDC);
|
|
}
|
|
else
|
|
{
|
|
Status = DfsGetBlobPDCName( &pPDC,
|
|
DFS_FORCE_DC_QUERY,
|
|
DomainName.Buffer);
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
UseDCName = pPDC->GetString();
|
|
}
|
|
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
LPWSTR ADContext;
|
|
|
|
extern LPWSTR DfsGetDfsAdNameContextStringForDomain(LPWSTR UseDC);
|
|
ADContext = DfsGetDfsAdNameContextStringForDomain(UseDCName);
|
|
}
|
|
}
|
|
DfsFreeUnicodeString(&DomainName);
|
|
}
|
|
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = DfsDirectApiAllocContext( &pApiContext,
|
|
&DfsPathName,
|
|
&ServerName,
|
|
&ShareName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now see if any of the stores recognizes this root.
|
|
// The store will read in the root's metadata in to the cache.
|
|
//
|
|
Status = DfsRecognize( pApiContext->ServerName.Buffer, &pApiContext->ShareName );
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get the root folder matching the given namespace.
|
|
// We get a referenced root folder.
|
|
//
|
|
Status = DfsGetRootFolder( &pApiContext->RootName,
|
|
NULL,
|
|
&pRoot );
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// ApiRequestPrologue will open AD object
|
|
// and get us ready for incoming API requests. All direct
|
|
// mode opens are considered WRITE requests, although currently,
|
|
// that makes no difference as CommonRequestPrologue ignores it.
|
|
//
|
|
Status = pRoot->RootApiRequestPrologue( TRUE,
|
|
UseDCName );
|
|
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
pRoot->ReleaseReference();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Return a valid 'handle' on success.
|
|
//
|
|
pApiContext->pObject = (PVOID)pRoot;
|
|
pApiContext->IsInitialized = TRUE;
|
|
pApiContext->IsWriteable = FALSE;
|
|
*pLibHandle = (PVOID)pApiContext;
|
|
|
|
} while (FALSE);
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Clean up the DirectApiContext if we hit errors.
|
|
// We have been careful not to do any operations that might fail
|
|
// after we do the RootApiRequestPrologue.
|
|
//
|
|
if (pApiContext != NULL)
|
|
{
|
|
DfsDirectApiFreeContext( pApiContext );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsDirectApiCommitChanges(
|
|
IN PVOID Handle)
|
|
{
|
|
DFS_SERVER_LIB_HANDLE LibHandle = (DFS_SERVER_LIB_HANDLE)Handle;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
PDFS_DIRECT_API_CONTEXT pApiContext = (PDFS_DIRECT_API_CONTEXT)LibHandle;
|
|
DfsRootFolder *pRoot;
|
|
|
|
if ((pApiContext == NULL) ||
|
|
(pApiContext->IsInitialized == FALSE) ||
|
|
(pApiContext->pObject == NULL))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
return Status;
|
|
}
|
|
|
|
if (pApiContext->IsWriteable == FALSE)
|
|
{
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
pRoot = (DfsRootFolder *)pApiContext->pObject;
|
|
|
|
Status = pRoot->Flush();
|
|
|
|
return Status;
|
|
}
|
|
|
|
DFSSTATUS
|
|
DfsDirectApiClose(
|
|
IN PVOID Handle)
|
|
{
|
|
DFS_SERVER_LIB_HANDLE LibHandle = (DFS_SERVER_LIB_HANDLE)Handle;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsRootFolder *pRoot;
|
|
PDFS_DIRECT_API_CONTEXT pApiContext = (PDFS_DIRECT_API_CONTEXT)LibHandle;
|
|
|
|
do {
|
|
//
|
|
// Check for valid handle
|
|
//
|
|
if ((pApiContext == NULL) ||
|
|
(pApiContext->IsInitialized == FALSE) ||
|
|
(pApiContext->pObject == NULL))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
pRoot = (DfsRootFolder *)pApiContext->pObject;
|
|
|
|
pRoot->RootApiRequestEpilogue( TRUE, Status );
|
|
pRoot->ReleaseReference();
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Free the DirectApiContext on our way out.
|
|
//
|
|
if (pApiContext != NULL)
|
|
{
|
|
DfsDirectApiFreeContext( pApiContext );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
DFSSTATUS
|
|
DfsDirectApiAllocContext(
|
|
OUT PDFS_DIRECT_API_CONTEXT *ppApiContext,
|
|
IN PUNICODE_STRING pDfsPathName,
|
|
IN PUNICODE_STRING pServerName,
|
|
IN PUNICODE_STRING pShareName)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
PDFS_DIRECT_API_CONTEXT pApiContext = NULL;
|
|
do {
|
|
|
|
pApiContext = (PDFS_DIRECT_API_CONTEXT) MIDL_user_allocate(sizeof(DFS_DIRECT_API_CONTEXT));
|
|
if (pApiContext == NULL)
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory( pApiContext, sizeof( DFS_DIRECT_API_CONTEXT ));
|
|
|
|
Status = DfsCreateUnicodeString( &pApiContext->RootName, pDfsPathName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// dfsdev: we really need to standardize on the LPWSTR vs. UNICODE issue.
|
|
//
|
|
Status = DfsCreateUnicodeString( &pApiContext->ServerName, pServerName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Status = DfsCreateUnicodeString( &pApiContext->ShareName, pShareName );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
*ppApiContext = pApiContext;
|
|
} while (FALSE);
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
if (pApiContext != NULL)
|
|
{
|
|
DfsDirectApiFreeContext( pApiContext );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
DfsDirectApiFreeContext(
|
|
PDFS_DIRECT_API_CONTEXT pApiContext)
|
|
{
|
|
if (pApiContext != NULL)
|
|
{
|
|
if (pApiContext->RootName.Buffer != NULL)
|
|
{
|
|
DfsFreeUnicodeString( &pApiContext->RootName );
|
|
RtlInitUnicodeString( &pApiContext->RootName, NULL );
|
|
}
|
|
|
|
if (pApiContext->ServerName.Buffer != NULL)
|
|
{
|
|
DfsFreeUnicodeString( &pApiContext->ServerName );
|
|
RtlInitUnicodeString( &pApiContext->ServerName, NULL );
|
|
}
|
|
|
|
if (pApiContext->ShareName.Buffer != NULL)
|
|
{
|
|
DfsFreeUnicodeString( &pApiContext->ShareName );
|
|
RtlInitUnicodeString( &pApiContext->ShareName, NULL );
|
|
}
|
|
|
|
pApiContext->pObject = NULL;
|
|
pApiContext->IsInitialized = FALSE;
|
|
pApiContext->IsWriteable = FALSE;
|
|
|
|
MIDL_user_free( pApiContext );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsExtendedRootAttributes(
|
|
IN PVOID Handle,
|
|
PULONG pAttr,
|
|
PUNICODE_STRING pRemaining,
|
|
BOOLEAN Set )
|
|
{
|
|
DFS_SERVER_LIB_HANDLE LibHandle = (DFS_SERVER_LIB_HANDLE)Handle;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
PDFS_DIRECT_API_CONTEXT pApiContext = (PDFS_DIRECT_API_CONTEXT)LibHandle;
|
|
DfsRootFolder *pRoot;
|
|
|
|
if ((pApiContext == NULL) ||
|
|
(pApiContext->IsInitialized == FALSE) ||
|
|
(pApiContext->pObject == NULL))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
return Status;
|
|
}
|
|
pRoot = (DfsRootFolder *)pApiContext->pObject;
|
|
|
|
Status = pRoot->ExtendedRootAttributes( pAttr, pRemaining, Set);
|
|
|
|
return Status;
|
|
}
|
|
//
|
|
// Get the size of the blob.
|
|
//
|
|
DFSSTATUS
|
|
DfsGetBlobSize(
|
|
IN PVOID Handle,
|
|
OUT PULONG pBlobSize )
|
|
{
|
|
|
|
DFS_SERVER_LIB_HANDLE LibHandle = (DFS_SERVER_LIB_HANDLE)Handle;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
PDFS_DIRECT_API_CONTEXT pApiContext = (PDFS_DIRECT_API_CONTEXT)LibHandle;
|
|
DfsRootFolder *pRoot;
|
|
|
|
if ((pApiContext == NULL) ||
|
|
(pApiContext->IsInitialized == FALSE) ||
|
|
(pApiContext->pObject == NULL))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
return Status;
|
|
}
|
|
|
|
pRoot = (DfsRootFolder *)pApiContext->pObject;
|
|
//
|
|
// Now ask the root get the blob from the cache.
|
|
//
|
|
*pBlobSize = pRoot->GetBlobSize();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Get the size of the blob.
|
|
//
|
|
DFSSTATUS
|
|
DfsGetSiteBlob(
|
|
IN PVOID Handle,
|
|
OUT PVOID *ppBlob,
|
|
OUT PULONG pBlobSize )
|
|
{
|
|
|
|
DFS_SERVER_LIB_HANDLE LibHandle = (DFS_SERVER_LIB_HANDLE)Handle;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
PDFS_DIRECT_API_CONTEXT pApiContext = (PDFS_DIRECT_API_CONTEXT)LibHandle;
|
|
DfsRootFolder *pRoot;
|
|
|
|
if ((pApiContext == NULL) ||
|
|
(pApiContext->IsInitialized == FALSE) ||
|
|
(pApiContext->pObject == NULL))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
return Status;
|
|
}
|
|
|
|
pRoot = (DfsRootFolder *)pApiContext->pObject;
|
|
//
|
|
// Now ask the root get the blob from the cache.
|
|
//
|
|
Status = pRoot->GetSiteBlob(ppBlob, pBlobSize);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the size of the blob.
|
|
//
|
|
DFSSTATUS
|
|
DfsSetSiteBlob(
|
|
IN PVOID Handle,
|
|
IN PVOID pBlob,
|
|
IN ULONG BlobSize )
|
|
{
|
|
|
|
DFS_SERVER_LIB_HANDLE LibHandle = (DFS_SERVER_LIB_HANDLE)Handle;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
PDFS_DIRECT_API_CONTEXT pApiContext = (PDFS_DIRECT_API_CONTEXT)LibHandle;
|
|
DfsRootFolder *pRoot;
|
|
|
|
if ((pApiContext == NULL) ||
|
|
(pApiContext->IsInitialized == FALSE) ||
|
|
(pApiContext->pObject == NULL))
|
|
{
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
return Status;
|
|
}
|
|
|
|
pRoot = (DfsRootFolder *)pApiContext->pObject;
|
|
//
|
|
// Now ask the root get the blob from the cache.
|
|
//
|
|
Status = pRoot->SetSiteBlob(pBlob, BlobSize);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|