Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3104 lines
80 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
admin.c
Abstract:
This module implements the top level admin request routines. All
routines within this module execute in the context of the server
service (or equivalent calling user mode process).
A routine that can complete an entire admin request
in the caller's context will return an appropriate AFP error to
the admin dispatch layer above.
A routine that must queue a worker to the FSP Admin queue will return
STATUS_PENDING to the admin dispatch layer above. This will indicate
to the dispatch layer that it should queue up the appropriate request.
In these cases, the routine's job is to merely validate any appropriate
input and return the STATUS_PENDING error code.
Author:
Sue Adams (microsoft!suea)
Revision History:
25 Jun 1992 Initial Version
--*/
#define FILENUM FILE_ADMIN
#include <afp.h>
#include <afpadmin.h>
#include <secutil.h>
#include <fdparm.h>
#include <pathmap.h>
#include <afpinfo.h>
#include <access.h>
#include <secutil.h>
#include <gendisp.h>
// This is the duration that we sleep before rescanning for the enumerate apis
#define AFP_SLEEP_TIMER_TICK -(1*NUM_100ns_PER_SECOND/100) // 10ms
LOCAL
NTSTATUS
afpConvertAdminPathToMacPath(
IN PVOLDESC pVolDesc,
IN PUNICODE_STRING AdminPath,
OUT PANSI_STRING MacPath
);
LOCAL
PETCMAPINFO
afpGetNextFreeEtcMapEntry(
IN OUT PLONG StartIndex
);
LOCAL
VOID
afpEtcMapDelete(
PETCMAPINFO pEtcEntry
);
LOCAL
NTSTATUS
afpCopyMapInfo2ToMapInfo(
OUT PETCMAPINFO pEtcDest,
IN PETCMAPINFO2 pEtcSource
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfpAdminDeInit)
#pragma alloc_text( PAGE, AfpSleepAWhile)
#pragma alloc_text( PAGE, AfpAdmServiceStart)
#pragma alloc_text( PAGE, AfpAdmServiceStop)
#pragma alloc_text( PAGE, AfpAdmServicePause)
#pragma alloc_text( PAGE, AfpAdmServiceContinue)
#pragma alloc_text( PAGE, AfpAdmServerGetInfo)
#pragma alloc_text( PAGE, AfpAdmClearProfCounters)
#pragma alloc_text( PAGE, AfpAdmServerSetParms)
#pragma alloc_text( PAGE, AfpAdmServerAddEtc)
#pragma alloc_text( PAGE, AfpAdmServerSetEtc)
#pragma alloc_text( PAGE, AfpAdmServerDeleteEtc)
#pragma alloc_text( PAGE, AfpAdmServerAddIcon)
#pragma alloc_text( PAGE, AfpAdmVolumeAdd)
#pragma alloc_text( PAGE, AfpAdmWDirectoryGetInfo)
#pragma alloc_text( PAGE, AfpAdmWDirectorySetInfo)
#pragma alloc_text( PAGE, AfpAdmWFinderSetInfo)
#pragma alloc_text( PAGE, AfpLookupEtcMapEntry)
#pragma alloc_text( PAGE, afpEtcMapDelete)
#pragma alloc_text( PAGE, afpGetNextFreeEtcMapEntry)
#pragma alloc_text( PAGE, afpConvertAdminPathToMacPath)
#pragma alloc_text( PAGE_AFP, AfpAdmGetStatistics)
#pragma alloc_text( PAGE_AFP, AfpAdmClearStatistics)
#pragma alloc_text( PAGE_AFP, AfpAdmGetProfCounters)
#pragma alloc_text( PAGE_AFP, AfpAdmVolumeGetInfo)
#pragma alloc_text( PAGE_AFP, AfpAdmVolumeSetInfo)
#pragma alloc_text( PAGE_AFP, AfpAdmVolumeEnum)
#pragma alloc_text( PAGE_AFP, AfpAdmSessionEnum)
#pragma alloc_text( PAGE_AFP, AfpAdmConnectionEnum)
#pragma alloc_text( PAGE_AFP, AfpAdmForkEnum)
#pragma alloc_text( PAGE_AFP, AfpAdmMessageSend)
#endif
//
// macro to ensure that the extension in a type/creator mapping is padded
// with nulls by the server service so we don't end up in la-la land on a
// lookup by extension
//
#define afpIsValidExtension(ext) (((ext)[AFP_EXTENSION_LEN] == '\0') && \
((ext)[0] != '\0') )
//
// invalid entries in AfpEtcMaps table are denoted by a null extension field
//
#define afpIsValidEtcMapEntry(ext) ((ext)[0] != '\0')
#define afpCopyEtcMap(pdst,psrc) (RtlCopyMemory(pdst,psrc,sizeof(ETCMAPINFO)))
#define afpIsServerIcon(picon) ((picon)->icon_icontype == 0)
/*** AfpAdminDeInit
*
* De-initialize the data structures for admin APIs.
*/
VOID
AfpAdminDeInit(
VOID
)
{
PAGED_CODE( );
// Free memory for server icon
if (AfpServerIcon != NULL)
AfpFreeMemory(AfpServerIcon);
// Free memory used for global icons
AfpFreeGlobalIconList();
// Free memory used for ETC mappings
if (AfpEtcMaps != NULL)
{
AfpFreeMemory(AfpEtcMaps);
}
// Free memory used for server name
if (AfpServerName.Buffer != NULL)
{
AfpFreeMemory(AfpServerName.Buffer);
}
// Free any Server/Login Messages
if (AfpServerMsg != NULL)
{
AfpFreeMemory(AfpServerMsg);
}
if (AfpLoginMsg.Buffer != NULL)
{
AfpFreeMemory(AfpLoginMsg.Buffer);
}
if (AfpLoginMsgU.Buffer != NULL)
{
AfpFreeMemory(AfpLoginMsgU.Buffer);
}
// Free the memory allocated for the admin sid
if (AfpSidAdmins != NULL)
AfpFreeMemory(AfpSidAdmins);
// Free the memory allocated for the None sid (standalone only)
if (AfpSidNone != NULL)
AfpFreeMemory(AfpSidNone);
}
/*** AfpSleepAWhile
*
* Sleep for a multiple of AFP_SLEEP_TIMER_TICK ticks.
*/
VOID
AfpSleepAWhile(
IN DWORD SleepDuration
)
{
KTIMER SleepTimer;
LARGE_INTEGER TimerValue;
PAGED_CODE( );
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
KeInitializeTimer(&SleepTimer);
TimerValue.QuadPart = (SleepDuration * AFP_SLEEP_TIMER_TICK);
KeSetTimer(&SleepTimer,
TimerValue,
NULL);
AfpIoWait(&SleepTimer, NULL);
}
/*** AfpAdmServiceStart
*
* This is the service start code. The following is done as part of the service
* startup.
*
* Registration of NBP Name.
* Posting listens
* And finally the server status block is set.
*/
AFPSTATUS
AfpAdmServiceStart(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
AFPSTATUS Status = AFP_ERR_NONE;
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpAdmServiceStart entered\n"));
do
{
// make sure serversetinfo has been called
if ((AfpServerState != AFP_STATE_IDLE) ||
(AfpServerName.Length == 0))
{
Status = AFPERR_InvalidServerState;
break;
}
AfpServerState = AFP_STATE_START_PENDING;
if (AfpServerBoundToAsp || AfpServerBoundToTcp)
{
// Det the server status block
Status = AfpSetServerStatus();
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceStart: AfpSetServerStatus returned %lx\n",Status));
AFPLOG_ERROR(AFPSRVMSG_SET_STATUS, Status, NULL, 0, NULL);
break;
}
if (AfpServerBoundToAsp)
{
// Register our name on this address
Status = AfpSpRegisterName(&AfpServerName, True);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceStart: AfpSpRegisterName returned %lx\n",Status));
break;
}
}
// Enable listens now that we are ready for it.
AfpSpEnableListens();
// Set the server start time
AfpGetCurrentTimeInMacFormat((PAFPTIME)&AfpServerStatistics.stat_ServerStartTime);
}
// server is ready to go
AfpServerState = AFP_STATE_RUNNING;
} while (False);
if (!NT_SUCCESS(Status))
{
AfpServerState = AFP_STATE_IDLE; // Set state back to idle so we can be stopped
}
else
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR, ("SFM Service started\n"));
}
return Status;
}
/*** AfpAdmServiceStop
*
* This is the service stop code.
*/
AFPSTATUS
AfpAdmServiceStop(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
NTSTATUS Status;
AFP_SESSION_INFO SessInfo;
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceStop entered\n"));
do
{
if ((AfpServerState != AFP_STATE_RUNNING) &&
(AfpServerState != AFP_STATE_PAUSED) &&
(AfpServerState != AFP_STATE_IDLE))
{
Status = AFPERR_InvalidServerState;
break;
}
AfpServerState = AFP_STATE_STOP_PENDING;
if (AfpServerBoundToAsp)
{
// First de-register our name from the network
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpAdmServiceStop: De-registering Name\n"));
AfpSpRegisterName(&AfpServerName, False);
if (AfpTdiNotificationHandle)
{
Status = TdiDeregisterPnPHandlers(AfpTdiNotificationHandle);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceStop: TdiDeregisterNotificationHandler failed with %lx\n",Status));
}
AfpTdiNotificationHandle = NULL;
}
else
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceStop: BoundToAsp but no Tdi handle!!\n"));
ASSERT(0);
}
}
// Disable listens now that we are about to stop
AfpSpDisableListens();
// De-register our shutdown notification
IoUnregisterShutdownNotification(AfpDeviceObject);
// Now walk the list of active sessions and kill them
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpAdmServiceStop: Shutting down sessions\n"));
KeClearEvent(&AfpStopConfirmEvent);
SessInfo.afpsess_id = 0; // Shutdown all sessions
AfpAdmWSessionClose(&SessInfo, 0, NULL);
Status = STATUS_TIMEOUT;
// Wait for the sessions to complete, if there were active sessions
if (AfpNumSessions > 0) do
{
if (AfpNumSessions == 0)
{
break;
}
Status = AfpIoWait(&AfpStopConfirmEvent, &FiveSecTimeOut);
if (Status == STATUS_TIMEOUT)
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceStop: Timeout Waiting for %ld sessions to die, re-waiting\n",
AfpNumSessions));
}
} while (Status == STATUS_TIMEOUT);
// bring down the DSI-TCP interface
DsiDestroyAdapter();
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceStop: blocked, waiting for DsiDestroyAdapter to finish...\n"));
// wait until DSI cleans up its interface with TCP
AfpIoWait(&DsiShutdownEvent, NULL);
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceStop: ..... DsiDestroyAdapter finished.\n"));
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpAdmServiceStop: Stopping Volumes\n"));
// Set flag to indicate "net stop macfile" occured
// The volume will be re-indexed on startup
fAfpAdminStop = TRUE;
// Now tell each of the volume scavengers to shut-down
AfpVolumeStopAllVolumes();
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpAdmServiceStop: Stopping Security threads\n"));
// Release all security utility threads.
AfpTerminateSecurityUtility();
#ifdef OPTIMIZE_GUEST_LOGONS
// Close the 'cached' Guest token and security descriptor
if (AfpGuestToken != NULL)
{
NtClose(AfpGuestToken);
AfpGuestToken = NULL;
#ifndef INHERIT_DIRECTORY_PERMS
if (AfpGuestSecDesc->Dacl != NULL)
AfpFreeMemory(AfpGuestSecDesc->Dacl);
AfpFreeMemory(AfpGuestSecDesc);
AfpGuestSecDesc = NULL;
#endif
}
#endif
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpAdmServiceStop: All Done\n"));
// Now shutdown the appletalk socket
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_INFO,
("AfpAdmServerStop: Closing appletalk socket\n"));
if (AfpServerBoundToAsp)
{
AfpSpCloseAddress();
}
else
{
DBGPRINT(DBG_COMP_ADMINAPI, DBG_LEVEL_ERR,
("AfpAdmServerStop: No binding, so didn't close appletalk socket\n"));
}
// Make sure we do not have resource leaks
ASSERT(AfpServerStatistics.stat_CurrentFileLocks == 0);
ASSERT(AfpServerStatistics.stat_CurrentFilesOpen == 0);
ASSERT(AfpServerStatistics.stat_CurrentSessions == 0);
ASSERT(AfpServerStatistics.stat_CurrentInternalOpens == 0);
#ifdef PROFILING
// Make sure we do not have resource leaks
ASSERT(AfpServerProfile->perf_cAllocatedIrps == 0);
ASSERT(AfpServerProfile->perf_cAllocatedMdls == 0);
#endif
ASSERT(IsListEmpty(&AfpDebugDelAllocHead));
ASSERT(AfpDbgMdlsAlloced == 0);
ASSERT(AfpDbgIrpsAlloced == 0);
#if DBG
if ((AfpReadCMAlloced != 0) || (AfpWriteCMAlloced != 0))
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("WARNING: AfpReadCMAlloced = %ld, AfpWriteCMAlloced %ld\n",
AfpReadCMAlloced, AfpWriteCMAlloced));
}
#endif
AfpServerState = AFP_STATE_STOPPED;
} while (False);
return STATUS_SUCCESS;
}
/*** AfpAdmServicePause
*
* Pause the server. Disconnect all outstanding sessions.
*/
AFPSTATUS
AfpAdmServicePause(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpAdmServicePause entered\n"));
// make sure we are in the running state
if (AfpServerState != AFP_STATE_RUNNING)
{
return AFPERR_InvalidServerState;
}
AfpServerState = AFP_STATE_PAUSE_PENDING;
if (AfpServerBoundToAsp)
{
// Deregister our name on this address. Should we do this at all ? What
// if we cannot re-register ourselves on CONTINUE ?
AfpSpRegisterName(&AfpServerName, False);
}
// Disable listens now that we are paused
AfpSpDisableListens();
AfpServerState = AFP_STATE_PAUSED;
return STATUS_SUCCESS;
}
/*** AfpAdmServiceContinue
*
* Continue (release pause) the server. Just re-post all the listens that were
* disconnected when the server was paused.
*/
AFPSTATUS
AfpAdmServiceContinue(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
AFPSTATUS Status;
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpAdmServiceContinue entered\n"));
// make sure we are in the paused state
if (AfpServerState != AFP_STATE_PAUSED)
{
return AFPERR_InvalidServerState;
}
AfpServerState = AFP_STATE_RUNNING;
// Enable listens now that we are ready for it.
AfpSpEnableListens();
if (AfpServerBoundToAsp)
{
// Reregister our name on this address
Status = AfpSpRegisterName(&AfpServerName, True);
if (!NT_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_ERR,
("AfpAdmServiceContinue: AfpSpRegisterName fails %lx\n",Status));
return AFPERR_InvalidServerName;
}
}
return STATUS_SUCCESS;
}
/*** AfpAdmServerGetInfo
*
* Return the current setting of the server parameters.
*
* NOTE: The following fields are not returned:
* PagedLimit
* NonPagedLimit
* CodePage
*/
AFPSTATUS
AfpAdmServerGetInfo(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PAFP_SERVER_INFO pSrvrInfo = (PAFP_SERVER_INFO)OutBuf;
UNICODE_STRING us;
if ((DWORD)OutBufLen < (sizeof(AFP_SERVER_INFO) +
(AfpServerName.Length + 1)*sizeof(WCHAR) +
AfpLoginMsgU.MaximumLength))
return AFPERR_BufferSize;
pSrvrInfo->afpsrv_max_sessions = AfpServerMaxSessions;
pSrvrInfo->afpsrv_options = AfpServerOptions;
pSrvrInfo->afpsrv_name = NULL;
pSrvrInfo->afpsrv_login_msg = NULL;
if (AfpServerName.Length > 0)
{
pSrvrInfo->afpsrv_name = us.Buffer =
(LPWSTR)((PBYTE)pSrvrInfo + sizeof(AFP_SERVER_INFO));
us.MaximumLength = (AfpServerName.Length + 1) * sizeof(WCHAR);
AfpConvertStringToUnicode(&AfpServerName, &us);
POINTER_TO_OFFSET(pSrvrInfo->afpsrv_name, pSrvrInfo);
}
if ((AfpLoginMsgU.Length) > 0)
{
pSrvrInfo->afpsrv_login_msg = (PWCHAR)((PBYTE)pSrvrInfo + sizeof(AFP_SERVER_INFO) +
((AfpServerName.Length + 1) * sizeof(WCHAR)));
RtlCopyMemory(pSrvrInfo->afpsrv_login_msg,
AfpLoginMsgU.Buffer,
AfpLoginMsgU.Length);
pSrvrInfo->afpsrv_login_msg[AfpLoginMsgU.Length/sizeof(WCHAR)] = UNICODE_NULL;
POINTER_TO_OFFSET(pSrvrInfo->afpsrv_login_msg, pSrvrInfo);
}
return AFP_ERR_NONE;
}
/*** AfpAdmGetStatistics
*
* Return a copy of the server global statistics (NT 3.1 only) in the output buffer
*
* LOCKS: AfpStatisticsLock (SPIN)
*/
AFPSTATUS
AfpAdmGetStatistics(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
KIRQL OldIrql;
NTSTATUS Status = STATUS_SUCCESS;
AFPTIME TimeNow;
InBuf;
DBGPRINT(DBG_COMP_ADMINAPI_STAT, DBG_LEVEL_INFO,
("AfpAdmGetStatistics entered\n"));
if (OutBufLen >= sizeof(AFP_STATISTICS_INFO))
{
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
RtlCopyMemory(OutBuf, &AfpServerStatistics, sizeof(AFP_STATISTICS_INFO));
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
AfpGetCurrentTimeInMacFormat(&TimeNow);
((PAFP_STATISTICS_INFO)OutBuf)->stat_ServerStartTime =
TimeNow - ((PAFP_STATISTICS_INFO)OutBuf)->stat_ServerStartTime;
((PAFP_STATISTICS_INFO)OutBuf)->stat_TimeStamp =
TimeNow - ((PAFP_STATISTICS_INFO)OutBuf)->stat_TimeStamp;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
return Status;
}
/*** AfpAdmGetStatisticsEx
*
* Return a copy of the server global statistics in the output buffer
*
* LOCKS: AfpStatisticsLock (SPIN)
*/
AFPSTATUS
AfpAdmGetStatisticsEx(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
KIRQL OldIrql;
NTSTATUS Status = STATUS_SUCCESS;
AFPTIME TimeNow;
InBuf;
DBGPRINT(DBG_COMP_ADMINAPI_STAT, DBG_LEVEL_INFO,
("AfpAdmGetStatistics entered\n"));
if (OutBufLen >= sizeof(AFP_STATISTICS_INFO_EX))
{
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
RtlCopyMemory(OutBuf, &AfpServerStatistics, sizeof(AFP_STATISTICS_INFO_EX));
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
AfpGetCurrentTimeInMacFormat(&TimeNow);
((PAFP_STATISTICS_INFO_EX)OutBuf)->stat_ServerStartTime =
TimeNow - ((PAFP_STATISTICS_INFO_EX)OutBuf)->stat_ServerStartTime;
((PAFP_STATISTICS_INFO_EX)OutBuf)->stat_TimeStamp =
TimeNow - ((PAFP_STATISTICS_INFO_EX)OutBuf)->stat_TimeStamp;
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
return Status;
}
/*** AfpAdmClearStatistics
*
* Reset the server global statistics to their respective initial values
*/
AFPSTATUS
AfpAdmClearStatistics(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
KIRQL OldIrql;
DBGPRINT(DBG_COMP_ADMINAPI_STAT, DBG_LEVEL_INFO,
("AfpAdmClearStatistics entered\n"));
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
AfpServerStatistics.stat_Errors = 0;
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
return STATUS_SUCCESS;
}
/*** AfpAdmGetProfCounters
*
* Return a copy of the server profile counters.
*
* LOCKS: AfpStatisticsLock (SPIN)
*/
AFPSTATUS
AfpAdmGetProfCounters(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
NTSTATUS Status = STATUS_SUCCESS;
#ifdef PROFILING
KIRQL OldIrql;
DBGPRINT(DBG_COMP_ADMINAPI_STAT, DBG_LEVEL_INFO,
("AfpAdmGetProfCounters entered\n"));
if (OutBufLen >= sizeof(AFP_PROFILE_INFO))
{
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
RtlCopyMemory(OutBuf, AfpServerProfile, sizeof(AFP_PROFILE_INFO));
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
#else
RtlZeroMemory(OutBuf, sizeof(AFP_PROFILE_INFO));
#endif
return Status;
}
/*** AfpAdmClearProfCounters
*
* Reset the server profile counters
*/
AFPSTATUS
AfpAdmClearProfCounters(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
InBuf;
OutBufLen;
OutBuf;
// Currently a NOP
PAGED_CODE( );
DBGPRINT(DBG_COMP_ADMINAPI_STAT, DBG_LEVEL_INFO,
("AfpAdmClearProfCounters entered\n"));
return STATUS_SUCCESS;
}
/*** AfpAdmServerSetParms
*
* This routine sets various server globals with data supplied by the admin.
* The following server globals are set by this routine:
*
* - List of trusted domains and their Posix offsets.
*/
AFPSTATUS
AfpAdmServerSetParms(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PAFP_SID_OFFSET_DESC pSrvrParms = (PAFP_SID_OFFSET_DESC)InBuf;
PAGED_CODE( );
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmServerSetParms entered\n"));
return (AfpInitSidOffsets(pSrvrParms->CountOfSidOffsets,
pSrvrParms->SidOffsetPairs));
}
/*** AfpAdmServerAddEtc
*
* This routine adds a set of Extension/Type-Creator mappings to the global
* list. This list can be changed while the server is in any state. It is
* an error to add the default type creator mapping. The default mapping
* can only be modified with AfpAdmServerSetEtc, never added nor deleted.
* It is an error to try to add zero entries.
*
* This routine will complete in the context of the caller, and not be queued
* to a worker thread.
*
* LOCKS: AfpEtcMapLock (SWMR, Exclusive)
**/
AFPSTATUS
AfpAdmServerAddEtc(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
LONG NumToAdd = ((PSRVETCPKT)InBuf)->retc_NumEtcMaps;
PETCMAPINFO2 pEtcList = ((PSRVETCPKT)InBuf)->retc_EtcMaps;
PETCMAPINFO ptemptable,pnextfree;
LONG numfree, newtablesize, nextfreehint, i;
UNICODE_STRING udefaultext,ulookupext;
AFPSTATUS Status = AFPERR_InvalidParms;
BOOLEAN UnlockSwmr = False;
PAGED_CODE( );
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmServerAddEtc entered\n"));
if (NumToAdd != 0) do
{
//
// make sure all the entries passed have valid extensions. We want to
// add all or nothing, so we have to validate all of the data first thing.
//
RtlInitUnicodeString(&udefaultext, AFP_DEF_EXTENSION_W);
for (i = 0; i < NumToAdd; i++)
{
if (!afpIsValidExtension(pEtcList[i].etc_extension))
{
break;
}
RtlInitUnicodeString(&ulookupext,pEtcList[i].etc_extension);
if (RtlEqualUnicodeString(&udefaultext, &ulookupext,True))
{
break;
}
}
if (i != NumToAdd)
break;
AfpSwmrAcquireExclusive(&AfpEtcMapLock);
UnlockSwmr = True;
if ((NumToAdd + AfpEtcMapCount) > AFP_MAX_ETCMAP_ENTRIES)
{
Status = AFPERR_TooManyEtcMaps;
break;
}
if ((numfree = AfpEtcMapsSize - AfpEtcMapCount) < NumToAdd)
{
ASSERT(numfree >= 0);
//
// we need to add some room to the table
//
newtablesize = AfpEtcMapsSize +
((NumToAdd / AFP_MAX_FREE_ETCMAP_ENTRIES) + 1) * AFP_MAX_FREE_ETCMAP_ENTRIES;
if ((ptemptable = (PETCMAPINFO)AfpAllocZeroedPagedMemory(newtablesize * sizeof(ETCMAPINFO))) == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
if (AfpEtcMaps != NULL)
{
RtlCopyMemory(ptemptable, AfpEtcMaps, AfpEtcMapsSize * sizeof(ETCMAPINFO));
AfpFreeMemory(AfpEtcMaps);
}
AfpEtcMaps = ptemptable;
AfpEtcMapsSize = newtablesize;
}
nextfreehint = 0;
for (i = 0; i < NumToAdd; i++)
{
pnextfree = afpGetNextFreeEtcMapEntry(&nextfreehint);
ASSERT(pnextfree != NULL);
afpCopyMapInfo2ToMapInfo(pnextfree, &pEtcList[i]);
AfpEtcMapCount ++;
}
Status = STATUS_SUCCESS;
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmServerAddEtc successful\n"));
} while (False);
if (UnlockSwmr)
AfpSwmrRelease(&AfpEtcMapLock);
return Status;
}
/*** AfpAdmServerSetEtc
*
* This routine changes an existing entry in the server global
* Extension/Type-Creator mapping list for a given file extension, or the
* default type/creator mapping.
* An entry can be changed while the server is in any state.
*
* This routine will complete in the context of the caller, and not be queued
* to a worker thread.
*
* LOCKS: AfpEtcMapLock (SWMR, Exclusive)
*/
AFPSTATUS
AfpAdmServerSetEtc(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
// ignore the parmnum field
PETCMAPINFO2 pEtc = (PETCMAPINFO2)((PBYTE)InBuf+sizeof(SETINFOREQPKT));
PETCMAPINFO petcentry;
ETCMAPINFO TmpEtcEntry;
AFPSTATUS rc = STATUS_SUCCESS;
BOOLEAN setdefaultetc;
UNICODE_STRING ulookupext,udefaultext;
PAGED_CODE( );
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmServerSetEtc entered\n"));
if (!afpIsValidExtension(pEtc->etc_extension))
{
return AFPERR_InvalidExtension;
}
RtlInitUnicodeString(&udefaultext,AFP_DEF_EXTENSION_W);
RtlInitUnicodeString(&ulookupext,pEtc->etc_extension);
setdefaultetc = RtlEqualUnicodeString(&udefaultext, &ulookupext,True);
if (setdefaultetc)
{
petcentry = &AfpDefaultEtcMap;
}
AfpSwmrAcquireExclusive(&AfpEtcMapLock);
afpCopyMapInfo2ToMapInfo(&TmpEtcEntry,pEtc);
if (!setdefaultetc)
{
petcentry = AfpLookupEtcMapEntry(TmpEtcEntry.etc_extension);
if (petcentry == NULL)
{
AfpSwmrRelease(&AfpEtcMapLock);
return AFPERR_InvalidParms;
}
}
RtlCopyMemory(petcentry, &TmpEtcEntry, sizeof(ETCMAPINFO));
AfpSwmrRelease(&AfpEtcMapLock);
return rc;
}
/*** AfpAdmServerDeleteEtc
*
* This routine deletes the server global Extension/Type-Creator mapping entry
* for a given extension. The default type creator mapping can never be
* deleted (since it is not kept in the table).
*
* This routine will complete in the context of the caller, and not be queued
* to a worker thread.
*
* LOCKS: AfpEtcMapLock (SWMR, Exclusive)
*
*/
AFPSTATUS
AfpAdmServerDeleteEtc(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PETCMAPINFO2 petc = (PETCMAPINFO2)InBuf;
PETCMAPINFO petcentry;
ETCMAPINFO TmpEtcEntry;
AFPSTATUS rc = STATUS_SUCCESS;
PAGED_CODE( );
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmServerDeleteEtc entered\n"));
if (!afpIsValidExtension(petc->etc_extension))
{
return AFPERR_InvalidParms;
}
AfpSwmrAcquireExclusive(&AfpEtcMapLock);
afpCopyMapInfo2ToMapInfo(&TmpEtcEntry,petc);
petcentry = AfpLookupEtcMapEntry(TmpEtcEntry.etc_extension);
if (petcentry != NULL)
{
afpEtcMapDelete(petcentry);
}
else
{
rc = AFPERR_InvalidParms;
}
AfpSwmrRelease(&AfpEtcMapLock);
return rc;
}
// Mapping icon types to their sizes
LOCAL DWORD afpIconSizeTable[MAX_ICONTYPE] =
{
ICONSIZE_ICN ,
ICONSIZE_ICN ,
ICONSIZE_ICN4,
ICONSIZE_ICN8,
ICONSIZE_ICS ,
ICONSIZE_ICS4,
ICONSIZE_ICS8
};
/*** AfpAdmServerAddIcon
*
* This routine adds an icon of a given type, creator and icon type to the server
* desktop. This supplements the volume desktop of every volume. An icon type
* of 0 special cases to the server icon.
*
* This routine will complete in the context of the caller, and not be queued
* to a worker thread.
*
*/
AFPSTATUS
AfpAdmServerAddIcon(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
DWORD icontypeafp;
PSRVICONINFO pIcon = (PSRVICONINFO)InBuf;
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmServerAddIcon entered\n"));
if (pIcon->icon_icontype > MAX_ICONTYPE ||
afpIconSizeTable[pIcon->icon_icontype] != pIcon->icon_length)
{
return AFPERR_InvalidParms;
}
//
// check for the server icon (type is zero)
//
if (afpIsServerIcon(pIcon))
{
// Allocate memory for server icon
if ((AfpServerIcon == NULL) &&
(AfpServerIcon = AfpAllocNonPagedMemory(ICONSIZE_ICN)) == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory(AfpServerIcon,
(PBYTE)pIcon+sizeof(SRVICONINFO),
ICONSIZE_ICN);
return((AfpServerState != AFP_STATE_IDLE) ?
AfpSetServerStatus() : STATUS_SUCCESS);
}
else
{
icontypeafp = 1 << (pIcon->icon_icontype-1);
return(AfpAddIconToGlobalList(*(PDWORD)(&pIcon->icon_type),
*(PDWORD)(&pIcon->icon_creator),
icontypeafp,
pIcon->icon_length,
(PBYTE)pIcon+sizeof(SRVICONINFO)));
}
}
/*** AfpAdmVolumeAdd
*
* This routine adds a volume to the server global list of volumes headed by
* AfpVolumeList. The volume descriptor is created and initialized. The ID
* index is read in (or created). The same is true with the desktop.
*
* It is assumed that all volume info fields are set in the input buffer
*
* ADMIN QUEUE WORKER: AfpAdmWVolumeAdd
*
*/
AFPSTATUS
AfpAdmVolumeAdd(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
UNICODE_STRING uname,upwd;
ULONG ansinamelen, ansipwdlen;
PAFP_VOLUME_INFO pVolInfo = (PAFP_VOLUME_INFO)InBuf;
PAGED_CODE( );
DBGPRINT(DBG_COMP_ADMINAPI_VOL, DBG_LEVEL_INFO,
("AfpAdmVolumeAdd entered\n"));
//
// validate the input data
//
RtlInitUnicodeString(&uname, pVolInfo->afpvol_name);
ansinamelen = RtlUnicodeStringToAnsiSize(&uname) - 1;
//
// check length of volume name and that no ":" exist in the name
//
if ((ansinamelen > AFP_VOLNAME_LEN) || (ansinamelen == 0) ||
(wcschr(uname.Buffer, L':') != NULL))
{
return AFPERR_InvalidVolumeName;
}
if (pVolInfo->afpvol_props_mask & ~AFP_VOLUME_ALL)
return AFPERR_InvalidParms;
if ((pVolInfo->afpvol_max_uses == 0) ||
(pVolInfo->afpvol_max_uses > AFP_VOLUME_UNLIMITED_USES))
{
return AFPERR_InvalidParms_MaxVolUses;
}
RtlInitUnicodeString(&upwd, pVolInfo->afpvol_password);
ansipwdlen = RtlUnicodeStringToAnsiSize(&upwd) - 1;
if (ansipwdlen > AFP_VOLPASS_LEN)
{
return AFPERR_InvalidPassword;
}
else if (ansipwdlen > 0)
{
pVolInfo->afpvol_props_mask |= AFP_VOLUME_HASPASSWORD;
}
//
// Force this to be queued up to a worker thread.
//
return STATUS_PENDING;
}
/*** AfpAdmVolumeSetInfo
*
* The volume parameters that can be changed by this call are the volume
* password, max_uses and volume properties mask.
*
* LOCKS: AfpVolumeListLock (SPIN), vds_VolLock (SPIN)
* LOCK ORDER: vds_VolLock after AfpVolumeListLock
*
*/
AFPSTATUS
AfpAdmVolumeSetInfo(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
WCHAR upcasebuf[AFP_VOLNAME_LEN+1];
UNICODE_STRING upwd,uname, upcasename;
BYTE apwdbuf[AFP_VOLPASS_LEN+1];
ANSI_STRING apwd;
PVOLDESC pVolDesc;
AFPSTATUS status;
KIRQL OldIrql;
DWORD parmflags = ((PSETINFOREQPKT)InBuf)->sirqp_parmnum;
PAFP_VOLUME_INFO pVolInfo = (PAFP_VOLUME_INFO)((PCHAR)InBuf+sizeof(SETINFOREQPKT));
DBGPRINT(DBG_COMP_ADMINAPI_VOL, DBG_LEVEL_INFO,
("AfpAdmVolumeSetInfo entered\n"));
AfpSetEmptyAnsiString(&apwd, AFP_VOLPASS_LEN+1, apwdbuf);
AfpSetEmptyUnicodeString(&upcasename, sizeof(upcasebuf), upcasebuf);
if ((parmflags & ~AFP_VOL_PARMNUM_ALL) ||
((parmflags & AFP_VOL_PARMNUM_PROPSMASK) &&
(pVolInfo->afpvol_props_mask & ~AFP_VOLUME_ALL)) ||
((parmflags & AFP_VOL_PARMNUM_MAXUSES) &&
((pVolInfo->afpvol_max_uses == 0) ||
(pVolInfo->afpvol_max_uses > AFP_VOLUME_UNLIMITED_USES))))
{
return AFPERR_InvalidParms;
}
if (parmflags & AFP_VOL_PARMNUM_PASSWORD)
{
RtlInitUnicodeString(&upwd,pVolInfo->afpvol_password);
if ((!NT_SUCCESS(AfpConvertStringToAnsi(&upwd, &apwd))) ||
(apwd.Length > AFP_VOLPASS_LEN))
{
return AFPERR_InvalidPassword;
}
}
RtlInitUnicodeString(&uname, pVolInfo->afpvol_name);
if (!NT_SUCCESS(RtlUpcaseUnicodeString(&upcasename, &uname, False)))
{
return AFPERR_InvalidVolumeName;
}
// Will reference the volume if successful
if ((pVolDesc = AfpVolumeReferenceByUpCaseName(&upcasename)) == NULL)
{
return AFPERR_VolumeNonExist;
}
// Acquire the lock for the volume itself (we already have a reference)
ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
do
{
status = STATUS_SUCCESS;
if (parmflags & AFP_VOL_PARMNUM_PROPSMASK)
{
//
// set or clear the desired volume property bits
//
pVolDesc->vds_Flags = (USHORT)((pVolDesc->vds_Flags & ~AFP_VOLUME_ALL) |
(pVolInfo->afpvol_props_mask));
}
if (parmflags & AFP_VOL_PARMNUM_PASSWORD)
{
if (apwd.Length == 0)
{
pVolDesc->vds_MacPassword.Length = 0;
pVolDesc->vds_Flags &= ~AFP_VOLUME_HASPASSWORD;
pVolDesc->vds_MacPassword.Length = 0;
}
else
{
RtlZeroMemory(pVolDesc->vds_MacPassword.Buffer, AFP_VOLPASS_LEN);
AfpCopyAnsiString(&pVolDesc->vds_MacPassword, &apwd);
pVolDesc->vds_MacPassword.Length = AFP_VOLPASS_LEN;
pVolDesc->vds_Flags |= AFP_VOLUME_HASPASSWORD;
}
}
if (parmflags & AFP_VOL_PARMNUM_MAXUSES)
pVolDesc->vds_MaxUses = pVolInfo->afpvol_max_uses;
} while (False);
RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock,OldIrql);
AfpVolumeDereference(pVolDesc);
return status;
}
/*** AfpAdmVolumeGetInfo
*
*
* LOCKS: AfpVolumeListLock (SPIN), vds_VolLock (SPIN)
* LOCK ORDER: vds_VolLock after AfpVolumeListLock
*/
AFPSTATUS
AfpAdmVolumeGetInfo(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PVOLDESC pVolDesc;
AFPSTATUS Status;
KIRQL OldIrql;
PCHAR pCurStr;
WCHAR upcasebuf[AFP_VOLNAME_LEN+1];
UNICODE_STRING uvolpass, uname, upcasename;
PAFP_VOLUME_INFO pVolInfo = (PAFP_VOLUME_INFO)OutBuf;
BOOLEAN copypassword = False;
ANSI_STRING avolpass;
CHAR avolpassbuf[AFP_VOLPASS_LEN + 1];
USHORT extrabytes;
DBGPRINT(DBG_COMP_ADMINAPI_VOL, DBG_LEVEL_INFO,
("AfpAdmVolumeGetInfo entered\n"));
AfpSetEmptyUnicodeString(&upcasename, sizeof(upcasebuf), upcasebuf);
RtlInitUnicodeString(&uname, ((PAFP_VOLUME_INFO)InBuf)->afpvol_name);
if (!NT_SUCCESS(RtlUpcaseUnicodeString(&upcasename, &uname, False)))
{
return AFPERR_InvalidVolumeName;
}
// Will reference the volume if successful
if ((pVolDesc = AfpVolumeReferenceByUpCaseName(&upcasename)) == NULL)
{
return AFPERR_VolumeNonExist;
}
// Acquire the lock for the volume itself
ACQUIRE_SPIN_LOCK(&pVolDesc->vds_VolLock, &OldIrql);
do
{
if ((OutBufLen - sizeof(AFP_VOLUME_INFO)) <
(pVolDesc->vds_Name.Length + sizeof(UNICODE_NULL) +
(pVolDesc->vds_MacPassword.Length + 1) * sizeof(WCHAR) +
pVolDesc->vds_Path.Length +
(extrabytes =
(pVolDesc->vds_Path.Buffer[(pVolDesc->vds_Path.Length / sizeof(WCHAR)) - 2] == L':' ?
sizeof(WCHAR) : 0)) + sizeof(UNICODE_NULL)))
{
Status = AFPERR_BufferSize;
break;
}
Status = STATUS_SUCCESS;
pVolInfo->afpvol_max_uses = pVolDesc->vds_MaxUses;
pVolInfo->afpvol_props_mask = (pVolDesc->vds_Flags & AFP_VOLUME_ALL_DOWNLEVEL);
pVolInfo->afpvol_id = pVolDesc->vds_VolId;
pVolInfo->afpvol_curr_uses = pVolDesc->vds_UseCount;
pCurStr = (PBYTE)OutBuf + sizeof(AFP_VOLUME_INFO);
RtlCopyMemory(pCurStr, pVolDesc->vds_Name.Buffer,
pVolDesc->vds_Name.Length);
*(LPWSTR)(pCurStr + pVolDesc->vds_Name.Length) = UNICODE_NULL;
pVolInfo->afpvol_name = (LPWSTR)pCurStr;
POINTER_TO_OFFSET(pVolInfo->afpvol_name,pVolInfo);
pCurStr += pVolDesc->vds_Name.Length + sizeof(WCHAR);
RtlCopyMemory(pCurStr, pVolDesc->vds_Path.Buffer,
pVolDesc->vds_Path.Length);
// replace trailing backslash of path with a unicode null unless the
// next to last char is ':', then keep it and add a trailing null
*(LPWSTR)(pCurStr + pVolDesc->vds_Path.Length + extrabytes - sizeof(WCHAR)) = UNICODE_NULL;
pVolInfo->afpvol_path = (LPWSTR)pCurStr;
POINTER_TO_OFFSET(pVolInfo->afpvol_path,pVolInfo);
pCurStr += pVolDesc->vds_Path.Length + extrabytes;
copypassword = True;
uvolpass.Buffer = (LPWSTR)pCurStr;
uvolpass.MaximumLength = (pVolDesc->vds_MacPassword.Length + 1) * sizeof(WCHAR);
AfpSetEmptyAnsiString(&avolpass, sizeof(avolpassbuf), avolpassbuf);
AfpCopyAnsiString(&avolpass, &pVolDesc->vds_MacPassword);
} while(False);
RELEASE_SPIN_LOCK(&pVolDesc->vds_VolLock,OldIrql);
AfpVolumeDereference(pVolDesc);
if (copypassword == True)
{
AfpConvertStringToUnicode(&avolpass, &uvolpass);
*(LPWSTR)(pCurStr + uvolpass.Length) = UNICODE_NULL;
pVolInfo->afpvol_password = (LPWSTR)pCurStr;
POINTER_TO_OFFSET(pVolInfo->afpvol_password,pVolInfo);
}
return Status;
}
/*** AfpAdmVolumeEnum
*
* Enumerate the list of configured volumes.
*
* LOCKS: AfpVolumeListLock (SPIN)
*
*/
AFPSTATUS
AfpAdmVolumeEnum(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
LONG startindex = (LONG)(((PENUMREQPKT)InBuf)->erqp_Index);
PENUMRESPPKT pErsp = (PENUMRESPPKT)OutBuf;
PAFP_VOLUME_INFO pnextvol = (PAFP_VOLUME_INFO)((PBYTE)OutBuf+sizeof(ENUMRESPPKT));
PBYTE pCurStr = (PBYTE)OutBuf+OutBufLen; // 1 past eob
KIRQL OldIrql;
AFPSTATUS status = STATUS_SUCCESS;
PVOLDESC pVolDesc;
LONG bytesleft, curvolindex, nextvollen, deadvolumes = 0, extrabytes;
if (startindex == 0)
{
startindex ++;
}
else if (startindex < 0)
{
return AFPERR_InvalidParms;
}
pErsp->ersp_cInBuf = 0;
pErsp->ersp_hResume = 1;
ACQUIRE_SPIN_LOCK(&AfpVolumeListLock, &OldIrql);
if (startindex > afpLargestVolIdInUse)
{
RELEASE_SPIN_LOCK(&AfpVolumeListLock, OldIrql);
if (pErsp->ersp_cTotEnts != 0)
{
status = AFPERR_InvalidParms;
}
return status;
}
curvolindex = 1;
for (pVolDesc = AfpVolumeList;
pVolDesc != NULL;
curvolindex++, pVolDesc = pVolDesc->vds_Next)
{
ASSERT(pVolDesc != NULL);
ACQUIRE_SPIN_LOCK_AT_DPC(&pVolDesc->vds_VolLock);
if (pVolDesc->vds_Flags & (VOLUME_DELETED | VOLUME_STOPPED | VOLUME_INTRANSITION))
{
deadvolumes ++;
RELEASE_SPIN_LOCK_FROM_DPC(&pVolDesc->vds_VolLock);
continue;
}
if (curvolindex < startindex)
{
RELEASE_SPIN_LOCK_FROM_DPC(&pVolDesc->vds_VolLock);
continue;
}
bytesleft = (LONG)((PBYTE)pCurStr - (PBYTE)pnextvol);
nextvollen = sizeof(AFP_VOLUME_INFO) +
pVolDesc->vds_Name.MaximumLength +
// replace trailing backslash with a null when copying
// unless the next to last char is ':', then keep it and
// add a trailing null
pVolDesc->vds_Path.Length + (extrabytes =
(pVolDesc->vds_Path.Buffer[(pVolDesc->vds_Path.Length / sizeof(WCHAR)) - 2] == L':' ?
sizeof(WCHAR) : 0));
if (nextvollen > bytesleft)
{
if (pErsp->ersp_cInBuf == 0)
status = AFPERR_BufferSize;
RELEASE_SPIN_LOCK_FROM_DPC(&pVolDesc->vds_VolLock);
break;
}
pnextvol->afpvol_max_uses = pVolDesc->vds_MaxUses;
pnextvol->afpvol_props_mask = (pVolDesc->vds_Flags & AFP_VOLUME_ALL_DOWNLEVEL);
pnextvol->afpvol_id = pVolDesc->vds_VolId;
pnextvol->afpvol_curr_uses = pVolDesc->vds_UseCount;
pCurStr -= pVolDesc->vds_Path.Length + extrabytes;
RtlCopyMemory(pCurStr,pVolDesc->vds_Path.Buffer,
pVolDesc->vds_Path.Length);
*(LPWSTR)(pCurStr + pVolDesc->vds_Path.Length + extrabytes - sizeof(WCHAR)) = L'\0';
pnextvol->afpvol_path = (LPWSTR)pCurStr;
POINTER_TO_OFFSET(pnextvol->afpvol_path,pnextvol);
pnextvol->afpvol_password = NULL;
pCurStr -= pVolDesc->vds_Name.MaximumLength;
RtlCopyMemory(pCurStr,pVolDesc->vds_Name.Buffer,
pVolDesc->vds_Name.MaximumLength);
pnextvol->afpvol_name = (LPWSTR)pCurStr;
POINTER_TO_OFFSET(pnextvol->afpvol_name,pnextvol);
pnextvol++;
pErsp->ersp_cInBuf++;
RELEASE_SPIN_LOCK_FROM_DPC(&pVolDesc->vds_VolLock);
}
pErsp->ersp_cTotEnts = AfpVolCount - deadvolumes;
RELEASE_SPIN_LOCK(&AfpVolumeListLock, OldIrql);
if (curvolindex <= (LONG)pErsp->ersp_cTotEnts)
{
status = STATUS_MORE_ENTRIES;
pErsp->ersp_hResume = curvolindex;
}
else
pErsp->ersp_hResume = 1;
return status;
}
/*** AfpAdmSessionEnum
*
* Enumerate the list of active sessions. This is a linear list rooted
* at AfpSessionList and protected by AfpSdaLock. This list is potentially
* pretty long (Unlimited # of sessions with the super ASP stuff).
*
* The resume handle returned is the session id of the last session returned.
* Session Id of 0 implies restart scan.
*
* The output buffer is constructed as follows.
*
* +---------------------------+
* | Session_Info_1 |
* +---------------------------+
* | Session_Info_2 |
* +---------------------------+
* . .
* . .
* +---------------------------+
* | Session_Info_n |
* +---------------------------+
* . .
* . .
* +---------------------------+
* | |
* |...........................|
* | Strings |
* |...........................|
* | |
* | |
* +---------------------------+
*
* LOCKS: AfpSdaLock (SPIN)
*/
AFPSTATUS
AfpAdmSessionEnum(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PENUMRESPPKT pErsp = (PENUMRESPPKT)OutBuf;
PAFP_SESSION_INFO pSessInfo = (PAFP_SESSION_INFO)((PBYTE)OutBuf+sizeof(ENUMRESPPKT));
PSDA pSda;
PBYTE pString = (PBYTE)OutBuf+OutBufLen; // 1 past eob
DWORD StartId = (LONG)(((PENUMREQPKT)InBuf)->erqp_Index);
DWORD DeadSessions = 0;
KIRQL OldIrql;
AFPSTATUS Status = AFP_ERR_NONE;
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmSessionEnum entered\n"));
if (OutBufLen < (sizeof(ENUMRESPPKT) + sizeof(PAFP_SESSION_INFO)))
return AFPERR_BufferSize;
if (StartId == 0)
StartId = MAXULONG;
// Initialize the response packet header
pErsp->ersp_cInBuf = 0;
pErsp->ersp_hResume = 0;
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
for (pSda = AfpSessionList; pSda != NULL; pSda = pSda->sda_Next)
{
LONG BytesLeft;
LONG BytesNeeded;
// Skip entries that are marked to die
if ((pSda->sda_Flags & SDA_CLOSING) ||
!(pSda->sda_Flags & SDA_USER_LOGGEDIN))
{
DeadSessions++;
continue;
}
// Skip all entries we have looked at before
if (pSda->sda_SessionId > StartId)
continue;
// If there is not enough space in the buffer, abort now and
// initialize pErsp->ersp_hResume with the current session id
BytesLeft = (LONG)((PBYTE)pString - (PBYTE)pSessInfo);
BytesNeeded = sizeof(AFP_SESSION_INFO) +
pSda->sda_UserName.Length + sizeof(WCHAR) +
pSda->sda_WSName.Length + sizeof(WCHAR);
if ((BytesLeft <= 0) || (BytesNeeded > BytesLeft))
{
pErsp->ersp_hResume = pSda->sda_SessionId;
Status = STATUS_MORE_ENTRIES;
break;
}
StartId = pSda->sda_SessionId;
pSessInfo->afpsess_id = pSda->sda_SessionId;
pSessInfo->afpsess_num_cons = pSda->sda_cOpenVolumes;
pSessInfo->afpsess_num_opens = pSda->sda_cOpenForks;
pSessInfo->afpsess_logon_type = pSda->sda_ClientType;
AfpGetCurrentTimeInMacFormat(&pSessInfo->afpsess_time);
pSessInfo->afpsess_time -= pSda->sda_TimeLoggedOn;
// Copy the strings here
pSessInfo->afpsess_username = NULL;
pSessInfo->afpsess_ws_name = NULL;
if (pSda->sda_UserName.Length > 0)
{
pString -= (pSda->sda_UserName.Length + sizeof(WCHAR));
if (pSda->sda_UserName.Length > 0)
RtlCopyMemory(pString, pSda->sda_UserName.Buffer, pSda->sda_UserName.Length);
*(LPWSTR)(pString + pSda->sda_UserName.Length) = L'\0';
pSessInfo->afpsess_username = (LPWSTR)pString;
POINTER_TO_OFFSET(pSessInfo->afpsess_username, pSessInfo);
}
if ((pSda->sda_ClientType == SDA_CLIENT_MSUAM_V1) ||
(pSda->sda_ClientType == SDA_CLIENT_MSUAM_V2))
{
pString -= (pSda->sda_WSName.Length + sizeof(WCHAR));
if (pSda->sda_WSName.Length > 0)
RtlCopyMemory(pString, pSda->sda_WSName.Buffer, pSda->sda_WSName.Length);
*(LPWSTR)(pString + pSda->sda_WSName.Length) = L'\0';
pSessInfo->afpsess_ws_name = (LPWSTR)pString;
POINTER_TO_OFFSET(pSessInfo->afpsess_ws_name, pSessInfo);
}
pSessInfo ++;
pErsp->ersp_cInBuf ++;
}
// Fill up the response packet header
pErsp->ersp_cTotEnts = (DWORD)AfpNumSessions - DeadSessions;
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
return Status;
}
/*** AfpAdmConnectionEnum
*
* Enumerate the list of active connections. This is a linear list rooted
* at AfpConnList and protected by AfpConnLock. This list is potentially
* pretty long (Unlimited # of sessions with the super ASP stuff).
*
* For that reason once every pass we check to see if we must forego the lock
* and restart scan again. The assumption here is that the admin operation can
* take a hit.
*
* The resume handle returned is the connection id of the last connection
* returned. connection Id of 0 implies restart scan.
*
* The output buffer is constructed as follows.
*
* +---------------------------+
* | Connection_Info_1 |
* +---------------------------+
* | Connection_Info_2 |
* +---------------------------+
* . .
* . .
* +---------------------------+
* | Connection_Info_n |
* +---------------------------+
* . .
* . .
* +---------------------------+
* | |
* |...........................|
* | Strings |
* |...........................|
* | |
* | |
* +---------------------------+
*
* The connections can be filtered based on either sessions or volumes.
*
* LOCKS: AfpConnLock (SPIN)
*/
AFPSTATUS
AfpAdmConnectionEnum(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PENUMRESPPKT pErsp = (PENUMRESPPKT)OutBuf;
PENUMREQPKT pErqp = (PENUMREQPKT)InBuf;
PAFP_CONNECTION_INFO pConnInfo = (PAFP_CONNECTION_INFO)((PBYTE)OutBuf+sizeof(ENUMRESPPKT));
PCONNDESC pConnDesc;
PBYTE pString = (PBYTE)OutBuf+OutBufLen; // 1 past eob
LONG cTotal = 0;
DWORD DeadConns = 0;
KIRQL OldIrql;
AFPSTATUS Status = AFP_ERR_NONE;
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmConnectionEnum entered\n"));
if ((((pErqp->erqp_Filter == AFP_FILTER_ON_SESSION_ID) ||
(pErqp->erqp_Filter == AFP_FILTER_ON_VOLUME_ID)) &&
(pErqp->erqp_ID == 0)) ||
((pErqp->erqp_Filter != 0) &&
(pErqp->erqp_Filter != AFP_FILTER_ON_SESSION_ID) &&
(pErqp->erqp_Filter != AFP_FILTER_ON_VOLUME_ID)))
return AFPERR_InvalidParms;
if (OutBufLen < (sizeof(ENUMRESPPKT) + sizeof(PAFP_CONNECTION_INFO)))
return AFPERR_BufferSize;
if (pErqp->erqp_Index == 0)
pErqp->erqp_Index = MAXULONG;
// Initialize the response packet header
pErsp->ersp_cInBuf = 0;
pErsp->ersp_hResume = 0;
ACQUIRE_SPIN_LOCK(&AfpConnLock, &OldIrql);
for (pConnDesc = AfpConnList;
pConnDesc != NULL;
pConnDesc = pConnDesc->cds_NextGlobal)
{
PSDA pSda;
PVOLDESC pVolDesc;
LONG BytesLeft;
LONG BytesNeeded;
// We do not need to either lock or reference pSda and pVolDesc
// since we have implicit references to them via the pConnDesc.
pSda = pConnDesc->cds_pSda;
ASSERT(pSda != NULL);
pVolDesc = pConnDesc->cds_pVolDesc;
ASSERT(pVolDesc != NULL);
// If we are filtering, make sure we get the total count
// Skip this entry, if any filtering is requested and this does not
// match
if (pErqp->erqp_Filter != 0)
{
if (pErqp->erqp_Filter == AFP_FILTER_ON_SESSION_ID)
{
if (pSda->sda_SessionId != pErqp->erqp_ID)
continue;
cTotal = pSda->sda_cOpenVolumes;
}
else // if (pErqp->erqp_Filter == AFP_FILTER_ON_VOLUME_ID)
{
if (pVolDesc->vds_VolId != (LONG)pErqp->erqp_ID)
continue;
cTotal = pVolDesc->vds_UseCount;
}
}
else cTotal = AfpNumSessions;
// Skip all entries that are marked for death
if (pConnDesc->cds_Flags & CONN_CLOSING)
{
DeadConns++;
continue;
}
// Skip all entries we have looked at before
if (pConnDesc->cds_ConnId > pErqp->erqp_Index)
continue;
// If there is not enough space in the buffer, abort now and
// initialize pErsp->ersp_hResume with the current connection id
BytesLeft = (LONG)((PBYTE)pString - (PBYTE)pConnInfo);
BytesNeeded = sizeof(AFP_CONNECTION_INFO) +
pSda->sda_UserName.Length + sizeof(WCHAR) +
pVolDesc->vds_Name.Length + sizeof(WCHAR);
if ((BytesLeft <= 0) || (BytesNeeded > BytesLeft))
{
pErsp->ersp_hResume = pConnDesc->cds_ConnId;
Status = STATUS_MORE_ENTRIES;
break;
}
pErqp->erqp_Index = pConnDesc->cds_ConnId;
pConnInfo->afpconn_id = pConnDesc->cds_ConnId;
pConnInfo->afpconn_num_opens = pConnDesc->cds_cOpenForks;
AfpGetCurrentTimeInMacFormat((PAFPTIME)&pConnInfo->afpconn_time);
pConnInfo->afpconn_time -= pConnDesc->cds_TimeOpened;
// Copy the username name string
pConnInfo->afpconn_username = (LPWSTR)NULL;
if (pSda->sda_UserName.Length > 0)
{
pString -= (pSda->sda_UserName.Length + sizeof(WCHAR));
RtlCopyMemory(pString, pSda->sda_UserName.Buffer, pSda->sda_UserName.Length);
*(LPWSTR)(pString + pSda->sda_UserName.Length) = L'\0';
pConnInfo->afpconn_username = (LPWSTR)pString;
POINTER_TO_OFFSET(pConnInfo->afpconn_username, pConnInfo);
}
// Copy the volume name string
pString -= (pVolDesc->vds_Name.Length + sizeof(WCHAR));
RtlCopyMemory(pString, pVolDesc->vds_Name.Buffer, pVolDesc->vds_Name.Length);
*(LPWSTR)(pString + pVolDesc->vds_Name.Length) = L'\0';
pConnInfo->afpconn_volumename = (LPWSTR)pString;
POINTER_TO_OFFSET(pConnInfo->afpconn_volumename, pConnInfo);
pConnInfo ++;
pErsp->ersp_cInBuf ++;
}
// Fill up the response packet header
pErsp->ersp_cTotEnts = (DWORD)cTotal - DeadConns;
RELEASE_SPIN_LOCK(&AfpConnLock, OldIrql);
return Status;
}
/*** AfpAdmForkEnum
*
* Enumerate the list of open forks. This is a linear list rooted
* at AfpOpenForksList and protected by AfpForksLock. This list is potentially
* pretty long (Unlimited # of sessions with the super ASP stuff).
*
* The resume handle returned is the connection id of the last connection
* returned. connection Id of 0 implies restart scan.
*
* The output buffer is constructed as follows.
*
* +---------------------------+
* | File_Info_1 |
* +---------------------------+
* | File_Info_2 |
* +---------------------------+
* . .
* . .
* +---------------------------+
* | File_Info_n |
* +---------------------------+
* . .
* . .
* +---------------------------+
* | |
* |...........................|
* | Strings |
* |...........................|
* | |
* | |
* +---------------------------+
*
* LOCKS: AfpForksLock (SPIN)
*/
AFPSTATUS
AfpAdmForkEnum(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PENUMRESPPKT pErsp = (PENUMRESPPKT)OutBuf;
PAFP_FILE_INFO pFileInfo = (PAFP_FILE_INFO)((PBYTE)OutBuf+sizeof(ENUMRESPPKT));
POPENFORKENTRY pOpenForkEntry;
POPENFORKDESC pOpenForkDesc;
PBYTE pString = (PBYTE)OutBuf+OutBufLen; // 1 past eob
DWORD StartId = (LONG)(((PENUMREQPKT)InBuf)->erqp_Index);
DWORD DeadForks = 0;
KIRQL OldIrql;
AFPSTATUS Status = AFP_ERR_NONE;
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmForkEnum entered\n"));
if (OutBufLen < (sizeof(ENUMRESPPKT) + sizeof(PAFP_FILE_INFO)))
return AFPERR_BufferSize;
if (StartId == 0)
StartId = MAXULONG;
// Initialize the response packet header
pErsp->ersp_cInBuf = 0;
pErsp->ersp_hResume = 0;
ACQUIRE_SPIN_LOCK(&AfpForksLock, &OldIrql);
for (pOpenForkEntry = AfpOpenForksList; pOpenForkEntry != NULL;
pOpenForkEntry = pOpenForkEntry->ofe_Next)
{
LONG BytesLeft;
LONG BytesNeeded;
PSDA pSda;
PVOLDESC pVolDesc = pOpenForkEntry->ofe_pOpenForkDesc->ofd_pVolDesc;
// Skip all entries that are marked for death
if (pOpenForkEntry->ofe_Flags & OPEN_FORK_CLOSING)
{
DeadForks ++;
continue;
}
// Skip all entries we have looked at before
if (pOpenForkEntry->ofe_ForkId > StartId)
continue;
pSda = pOpenForkEntry->ofe_pSda;
pOpenForkDesc = pOpenForkEntry->ofe_pOpenForkDesc;
// If there is not enough space in the buffer, abort now and
// initialize pErsp->ersp_hResume with the current session id
BytesLeft = (LONG)((PBYTE)pString - (PBYTE)pFileInfo);
BytesNeeded = sizeof(AFP_FILE_INFO) + pSda->sda_UserName.Length +
sizeof(WCHAR) + /* NULL terminate username */
pVolDesc->vds_Path.Length +
pOpenForkDesc->ofd_FilePath.Length +
sizeof(WCHAR); /* NULL terminate path */
if ((BytesLeft <= 0) || (BytesNeeded > BytesLeft))
{
pErsp->ersp_hResume = pOpenForkEntry->ofe_ForkId;
Status = STATUS_MORE_ENTRIES;
break;
}
StartId = pOpenForkEntry->ofe_ForkId;
pFileInfo->afpfile_id = pOpenForkEntry->ofe_ForkId;
pFileInfo->afpfile_num_locks = pOpenForkEntry->ofe_cLocks;
pFileInfo->afpfile_fork_type = RESCFORK(pOpenForkEntry);
#if AFP_OPEN_MODE_NONE != FORK_OPEN_NONE
#error (AFP_OPEN_MODE_NONE != FORK_OPEN_NONE)
#endif
#if AFP_OPEN_MODE_READ != FORK_OPEN_READ
#error (AFP_OPEN_MODE_READ != FORK_OPEN_READ)
#endif
#if AFP_OPEN_MODE_WRITE != FORK_OPEN_WRITE
#error (AFP_OPEN_MODE_WRITE != FORK_OPEN_WRITE)
#endif
pFileInfo->afpfile_open_mode = (DWORD)pOpenForkEntry->ofe_OpenMode;
// Copy the strings here.
pFileInfo->afpfile_username = NULL;
pFileInfo->afpfile_path = NULL;
if (pSda->sda_UserName.Length > 0)
{
pString -= (pSda->sda_UserName.Length + sizeof(WCHAR));
RtlCopyMemory(pString, pSda->sda_UserName.Buffer, pSda->sda_UserName.Length);
*(LPWSTR)(pString + pSda->sda_UserName.Length) = L'\0';
pFileInfo->afpfile_username = (LPWSTR)pString;
POINTER_TO_OFFSET(pFileInfo->afpfile_username, pFileInfo);
}
if (pOpenForkDesc->ofd_FilePath.Length > 0)
{
pString -= pVolDesc->vds_Path.Length +
pOpenForkDesc->ofd_FilePath.Length +
sizeof(WCHAR);
pFileInfo->afpfile_path = (LPWSTR)pString;
POINTER_TO_OFFSET(pFileInfo->afpfile_path, pFileInfo);
RtlCopyMemory(pString, pVolDesc->vds_Path.Buffer,
pVolDesc->vds_Path.Length);
RtlCopyMemory(pString + pVolDesc->vds_Path.Length,
pOpenForkDesc->ofd_FilePath.Buffer,
pOpenForkDesc->ofd_FilePath.Length);
*(LPWSTR)(pString + pVolDesc->vds_Path.Length +
pOpenForkDesc->ofd_FilePath.Length) = L'\0';
}
pFileInfo ++;
pErsp->ersp_cInBuf ++;
}
// Fill up the response packet header
pErsp->ersp_cTotEnts = (DWORD)AfpNumOpenForks - DeadForks;
RELEASE_SPIN_LOCK(&AfpForksLock, OldIrql);
return Status;
}
/*** AfpAdmMessageSend
*
* Send a message to a specific session, or broadcast to all sessions.
* If session id is 0, this indicates a broadcast, and the message is copied
* to AfpServerMsg. Otherwise, the message is copied to the particular
* session's SDA. A message can be a max of 199 chars. It is an error to
* attempt to send a message of length 0. A message can only be sent to an
* AFP 2.1 client as a AFP 2.0 client has no capability to accept a message.
*
* LOCKS: AfpServerGlobalLock (SPIN)
*/
AFPSTATUS
AfpAdmMessageSend(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PAFP_MESSAGE_INFO pMsgInfo = (PAFP_MESSAGE_INFO)InBuf;
PSDA pSda;
UNICODE_STRING umsg;
PANSI_STRING amsg;
USHORT msglen;
DWORD SessId;
KIRQL OldIrql;
AFPSTATUS Status = AFP_ERR_NONE;
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmMessageSend entered\n"));
SessId = pMsgInfo->afpmsg_session_id;
RtlInitUnicodeString(&umsg, pMsgInfo->afpmsg_text);
msglen = (USHORT)RtlUnicodeStringToAnsiSize(&umsg)-1;
if ((msglen > AFP_MESSAGE_LEN) || (msglen == 0))
{
return AFPERR_InvalidParms;
}
if ((amsg =
(PANSI_STRING)AfpAllocNonPagedMemory(msglen + 1 + sizeof(ANSI_STRING))) == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
amsg->Length = msglen;
amsg->MaximumLength = msglen + 1;
amsg->Buffer = (PBYTE)amsg + sizeof(ANSI_STRING);
Status = RtlUnicodeStringToAnsiString(amsg, &umsg, False);
if (!NT_SUCCESS(Status))
{
return AFPERR_InvalidParms;
}
else AfpConvertHostAnsiToMacAnsi(amsg);
DBGPRINT(DBG_COMP_ADMINAPI_SRV, DBG_LEVEL_INFO,
("AfpAdmMessageSend: session id is 0x%x, message <%s>\n",
pMsgInfo->afpmsg_session_id, amsg->Buffer));
// If this is a broadcast message, initialize the global message
if (SessId == 0)
{
ACQUIRE_SPIN_LOCK(&AfpServerGlobalLock, &OldIrql);
// If there is a message there already, blow it
if (AfpServerMsg != NULL)
AfpFreeMemory(AfpServerMsg);
AfpServerMsg = amsg;
RELEASE_SPIN_LOCK(&AfpServerGlobalLock, OldIrql);
// Walk the session list and send attention to all AFP 2.1 clients
ACQUIRE_SPIN_LOCK(&AfpSdaLock, &OldIrql);
for (pSda = AfpSessionList; pSda != NULL; pSda = pSda->sda_Next)
{
ACQUIRE_SPIN_LOCK_AT_DPC(&pSda->sda_Lock);
if ((pSda->sda_ClientVersion >= AFP_VER_21) &&
((pSda->sda_Flags & (SDA_CLOSING | SDA_SESSION_CLOSED)) == 0))
{
// We are using the async version of AfpSpSendAttention since
// we are calling with spin-lock held.
AfpSpSendAttention(pSda, ATTN_SERVER_MESSAGE, False);
}
else if (pSda->sda_ClientVersion < AFP_VER_21)
{
Status = AFPERR_InvalidSessionType;
}
RELEASE_SPIN_LOCK_FROM_DPC(&pSda->sda_Lock);
}
RELEASE_SPIN_LOCK(&AfpSdaLock, OldIrql);
}
else
{
// Find the session matching the session id and, if found and the client is AFP v2.1,
// copy the message to the SDA and send an attention to the client.
// Error if the session either does not exist or it is not an AFP 2.1
Status = AFPERR_InvalidId;
if ((pSda = AfpSdaReferenceSessionById(SessId)) != NULL)
{
Status = AFPERR_InvalidSessionType;
if (pSda->sda_ClientVersion >= AFP_VER_21)
{
ACQUIRE_SPIN_LOCK(&pSda->sda_Lock, &OldIrql);
if (pSda->sda_Message != NULL)
AfpFreeMemory(pSda->sda_Message);
pSda->sda_Message = amsg;
AfpSpSendAttention(pSda, ATTN_SERVER_MESSAGE, False);
RELEASE_SPIN_LOCK(&pSda->sda_Lock, OldIrql);
Status = AFP_ERR_NONE;
}
AfpSdaDereferenceSession(pSda);
}
if (Status != AFP_ERR_NONE)
{
AfpFreeMemory(amsg);
}
}
return Status;
}
/*** AfpAdmWDirectoryGetInfo
*
* Query a directory's permissions.
*/
AFPSTATUS
AfpAdmWDirectoryGetInfo(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PAFP_DIRECTORY_INFO pDirInfo = (PAFP_DIRECTORY_INFO)OutBuf;
PSID pSid = (PSID)((PBYTE)OutBuf + sizeof(AFP_DIRECTORY_INFO));
UNICODE_STRING VolumePath;
ANSI_STRING MacAnsiDirPath;
SDA Sda;
CONNDESC ConnDesc;
PVOLDESC pVolDesc;
FILEDIRPARM FDParm;
PATHMAPENTITY PME;
AFPSTATUS Status;
PAGED_CODE( );
DBGPRINT(DBG_COMP_ADMINAPI_DIR, DBG_LEVEL_INFO,
("AfpAdmWDirectoryGetInfo entered for %ws\n",
((PAFP_DIRECTORY_INFO)InBuf)->afpdir_path));
// validate the output buffer length
if (OutBufLen < sizeof(AFP_DIRECTORY_INFO))
return AFPERR_BufferSize;
MacAnsiDirPath.Length = 0;
MacAnsiDirPath.MaximumLength = 0;
MacAnsiDirPath.Buffer = NULL;
OutBufLen -= sizeof(AFP_DIRECTORY_INFO);
// First find the volume that this directory is path of
RtlInitUnicodeString(&VolumePath, ((PAFP_DIRECTORY_INFO)InBuf)->afpdir_path);
if (!NT_SUCCESS(Status = AfpVolumeReferenceByPath(&VolumePath, &pVolDesc)))
{
DBGPRINT(DBG_COMP_ADMINAPI_DIR, DBG_LEVEL_ERR,
("AfpAdmWDirectoryGetInfo: AfpVolumeReferenceByPath returned error %ld\n",
Status));
return Status;
}
// Now get the volume relative path of the directory.
VolumePath.Buffer = (LPWSTR)((PBYTE)VolumePath.Buffer +
pVolDesc->vds_Path.Length);
VolumePath.Length -= pVolDesc->vds_Path.Length;
VolumePath.MaximumLength -= pVolDesc->vds_Path.Length;
if ((SHORT)(VolumePath.Length) < 0)
{
VolumePath.Length = 0;
VolumePath.MaximumLength = sizeof(WCHAR);
}
do
{
AfpInitializePME(&PME, 0, NULL);
if (!NT_SUCCESS(Status = afpConvertAdminPathToMacPath(pVolDesc,
&VolumePath,
&MacAnsiDirPath)))
{
Status = STATUS_OBJECT_PATH_NOT_FOUND;
break;
}
// AfpMapAfpPathForLookup requires an Sda to figure out User's
// permission. For this API, we do not really need the User's
// permission, so kludge it up. Note that it is important to
// set the client type to SDA_CLIENT_ADMIN to avoid references
// to other sda fields. See access.c/fdparm.c/afpinfo.c for details.
RtlZeroMemory(&Sda, sizeof(Sda));
#if DBG
Sda.Signature = SDA_SIGNATURE;
#endif
Sda.sda_ClientType = SDA_CLIENT_ADMIN;
Sda.sda_UserSid = &AfpSidWorld;
Sda.sda_GroupSid = &AfpSidWorld;
// pathmap requires a ConnDesc to determine the VolDesc and Sda, so
// kludge up a fake one here
RtlZeroMemory(&ConnDesc, sizeof(ConnDesc));
#if DBG
ConnDesc.Signature = CONNDESC_SIGNATURE;
#endif
ConnDesc.cds_pSda = &Sda;
ConnDesc.cds_pVolDesc = pVolDesc;
AfpInitializeFDParms(&FDParm);
Status = AfpMapAfpPathForLookup(&ConnDesc,
AFP_ID_ROOT,
&MacAnsiDirPath,
AFP_LONGNAME,
DFE_DIR,
FD_INTERNAL_BITMAP_OPENACCESS_ADMINGET |
DIR_BITMAP_ACCESSRIGHTS |
FD_BITMAP_ATTR,
&PME,
&FDParm);
if (!NT_SUCCESS(Status))
{
if (Status == AFP_ERR_ACCESS_DENIED)
{
Status = STATUS_ACCESS_DENIED;
}
else
{
Status = STATUS_OBJECT_PATH_NOT_FOUND;
}
break;
}
} while (False);
if (PME.pme_Handle.fsh_FileHandle != NULL)
AfpIoClose(&PME.pme_Handle);
if (MacAnsiDirPath.Buffer != NULL)
{
AfpFreeMemory(MacAnsiDirPath.Buffer);
}
AfpVolumeDereference(pVolDesc);
// All is hunky-dory so far. Now convert the information we have so far
// into the form accepted by the API
if (NT_SUCCESS(Status))
{
PSID pSidUG; // Sid of user or group
pDirInfo->afpdir_perms =
((FDParm._fdp_OwnerRights & ~DIR_ACCESS_OWNER) << OWNER_RIGHTS_SHIFT) +
((FDParm._fdp_GroupRights & ~DIR_ACCESS_OWNER) << GROUP_RIGHTS_SHIFT) +
((FDParm._fdp_WorldRights & ~DIR_ACCESS_OWNER) << WORLD_RIGHTS_SHIFT);
if ((FDParm._fdp_Attr &
(FD_BITMAP_ATTR_RENAMEINH | FD_BITMAP_ATTR_DELETEINH)) ==
(FD_BITMAP_ATTR_RENAMEINH | FD_BITMAP_ATTR_DELETEINH))
pDirInfo->afpdir_perms |= AFP_PERM_INHIBIT_MOVE_DELETE;
DBGPRINT(DBG_COMP_ADMINAPI_DIR, DBG_LEVEL_INFO,
("AfpAdmWDirectoryGetInfo: Perms %lx\n", pDirInfo->afpdir_perms));
pDirInfo->afpdir_path = NULL;
// Translate the owner and group ids to Sids. The name fields actually
// get the sids and the user mode code is responsible to convert it
// to names.
pDirInfo->afpdir_owner = NULL;
pDirInfo->afpdir_group = NULL;
do
{
LONG LengthSid;
//
// Convert the owner ID to SID
//
if (FDParm._fdp_OwnerId != 0)
{
Status = AfpMacIdToSid(FDParm._fdp_OwnerId, &pSidUG);
if (!NT_SUCCESS(Status))
{
Status = STATUS_NONE_MAPPED;
break;
}
AfpDumpSid("AfpAdmWDirectoryGetInfo: User Sid:", pSidUG);
LengthSid = RtlLengthSid(pSidUG);
if (OutBufLen < LengthSid)
Status = AFPERR_BufferSize;
else
{
RtlCopyMemory(pSid, pSidUG, LengthSid);
pDirInfo->afpdir_owner = pSid;
POINTER_TO_OFFSET(pDirInfo->afpdir_owner, pDirInfo);
pSid = (PSID)((PBYTE)pSid + LengthSid);
OutBufLen -= LengthSid;
}
if (!NT_SUCCESS(Status))
break;
}
//
// Convert the group ID to SID
//
if (FDParm._fdp_GroupId != 0)
{
Status = AfpMacIdToSid(FDParm._fdp_GroupId, &pSidUG);
if (!NT_SUCCESS(Status))
{
Status = STATUS_NONE_MAPPED;
break;
}
AfpDumpSid("AfpAdmWDirectoryGetInfo: Group Sid:", pSidUG);
LengthSid = RtlLengthSid(pSidUG);
if (OutBufLen < LengthSid)
Status = AFPERR_BufferSize;
else
{
RtlCopyMemory(pSid, pSidUG, LengthSid);
pDirInfo->afpdir_group = pSid;
POINTER_TO_OFFSET(pDirInfo->afpdir_group, pDirInfo);
// pSid = (PSID)((PBYTE)pSid + LengthSid);
// OutBufLen -= LengthSid;
}
}
} while (False);
}
return Status;
}
/*** AfpAdmWDirectorySetInfo
*
* Set a directory's permissions.
*/
AFPSTATUS
AfpAdmWDirectorySetInfo(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PAFP_DIRECTORY_INFO pDirInfo;
DWORD ParmNum, Bitmap = 0;
UNICODE_STRING VolumePath;
SDA Sda;
CONNDESC ConnDesc;
PVOLDESC pVolDesc;
AFPSTATUS Status;
BYTE ParmBlock[4 * sizeof(DWORD)];
FILEDIRPARM FDParm;
PAGED_CODE( );
ParmNum = ((PSETINFOREQPKT)InBuf)->sirqp_parmnum;
pDirInfo = (PAFP_DIRECTORY_INFO)((PBYTE)InBuf + sizeof(SETINFOREQPKT));
DBGPRINT(DBG_COMP_ADMINAPI_DIR, DBG_LEVEL_INFO,
("AfpAdmWDirectorySetInfo entered for %ws (%lx)\n",
pDirInfo->afpdir_path, ParmNum));
// Convert the parmnum to a bitmap for use by AfpSetFileDirParms
if (ParmNum & AFP_DIR_PARMNUM_PERMS)
Bitmap |= (DIR_BITMAP_ACCESSRIGHTS | FD_BITMAP_ATTR);
if (ParmNum & AFP_DIR_PARMNUM_OWNER)
{
if (pDirInfo->afpdir_owner == NULL)
return STATUS_INVALID_PARAMETER;
else
Bitmap |= DIR_BITMAP_OWNERID;
}
if (ParmNum & AFP_DIR_PARMNUM_GROUP)
{
if (pDirInfo->afpdir_group == NULL)
return STATUS_INVALID_PARAMETER;
else
Bitmap |= DIR_BITMAP_GROUPID;
}
// Find the volume that this directory is path of
RtlInitUnicodeString(&VolumePath, pDirInfo->afpdir_path);
if (!NT_SUCCESS(Status = AfpVolumeReferenceByPath(&VolumePath, &pVolDesc)))
return Status;
// Now get the volume relative path of the directory. Consume the leading
// '\' character
VolumePath.Buffer = (LPWSTR)((PBYTE)VolumePath.Buffer +
pVolDesc->vds_Path.Length);
VolumePath.Length -= pVolDesc->vds_Path.Length;
VolumePath.MaximumLength -= pVolDesc->vds_Path.Length;
if ((SHORT)(VolumePath.Length) < 0)
{
VolumePath.Length = 0;
VolumePath.MaximumLength = sizeof(WCHAR);
}
RtlZeroMemory(&Sda, sizeof(Sda));
if (Bitmap) do
{
if (!NT_SUCCESS(Status = afpConvertAdminPathToMacPath(pVolDesc,
&VolumePath,
&Sda.sda_Name1)))
{
Status = STATUS_OBJECT_PATH_NOT_FOUND;
break;
}
// Kludge up a FILEDIRPARMS structure to call AfpPackFDParms with
AfpInitializeFDParms(&FDParm);
if (Bitmap & FD_BITMAP_ATTR)
{
FDParm._fdp_Attr = FD_BITMAP_ATTR_RENAMEINH |
FD_BITMAP_ATTR_DELETEINH;
if (pDirInfo->afpdir_perms & AFP_PERM_INHIBIT_MOVE_DELETE)
{
FDParm._fdp_Attr |= FD_BITMAP_ATTR_SET;
}
DBGPRINT(DBG_COMP_ADMINAPI_DIR, DBG_LEVEL_INFO,
("AfpAdmWDirectorySetInfo: Changing Attributes to %lx\n",
FDParm._fdp_Attr));
}
if (Bitmap & DIR_BITMAP_ACCESSRIGHTS)
{
FDParm._fdp_OwnerRights = (BYTE)(pDirInfo->afpdir_perms >> OWNER_RIGHTS_SHIFT);
FDParm._fdp_GroupRights = (BYTE)(pDirInfo->afpdir_perms >> GROUP_RIGHTS_SHIFT);
FDParm._fdp_WorldRights = (BYTE)(pDirInfo->afpdir_perms >> WORLD_RIGHTS_SHIFT);
DBGPRINT(DBG_COMP_ADMINAPI_DIR, DBG_LEVEL_INFO,
("AfpAdmWDirectorySetInfo: Setting Permissions %x,%x,%x\n",
FDParm._fdp_OwnerRights,
FDParm._fdp_GroupRights,
FDParm._fdp_WorldRights));
}
// See if we need to change owner and group ids
if (Bitmap & DIR_BITMAP_OWNERID)
{
Status = AfpSidToMacId((PSID)(pDirInfo->afpdir_owner),
&FDParm._fdp_OwnerId);
if (!NT_SUCCESS(Status))
{
Status = STATUS_NONE_MAPPED;
break;
}
AfpDumpSid("AfpAdmWDirectorySetInfo: Changing Owner to:",
(PSID)(pDirInfo->afpdir_owner));
}
if (Bitmap & DIR_BITMAP_GROUPID)
{
Status = AfpSidToMacId((PSID)(pDirInfo->afpdir_group),
&FDParm._fdp_GroupId);
if (!NT_SUCCESS(Status))
{
Status = STATUS_NONE_MAPPED;
break;
}
AfpDumpSid("AfpAdmWDirectorySetInfo: Changing Group to:",
(PSID)(pDirInfo->afpdir_group));
}
FDParm._fdp_Flags = DFE_FLAGS_DIR;
AfpPackFileDirParms(&FDParm, Bitmap, ParmBlock);
// AfpQueryFileDirParms requires an Sda to figure out User's
// permission. For this API, we do not really need the User's
// permission, so kludge it up. Note that it is important to
// set the client type to SDA_CLIENT_ADMIN to avoid references
// to other sda fields. See access.c/fdparm.c/afpinfo.c for details.
Sda.sda_ClientType = SDA_CLIENT_ADMIN;
Sda.sda_UserSid = &AfpSidWorld;
Sda.sda_GroupSid = &AfpSidWorld;
*((PULONG_PTR)Sda.sda_ReqBlock) = (ULONG_PTR)&ConnDesc;
//if (sizeof (DWORD) != sizeof (ULONG_PTR))
#ifdef _WIN64
// Create 64-bit space at start of buffer to hold ConnDesc pointer
// 64-bit specifics
Sda.sda_ReqBlock[2] = AFP_ID_ROOT;
Sda.sda_ReqBlock[3] = Bitmap;
#else
Sda.sda_ReqBlock[1] = AFP_ID_ROOT;
Sda.sda_ReqBlock[2] = Bitmap;
#endif
Sda.sda_PathType = AFP_LONGNAME;
Sda.sda_Name2.Buffer = ParmBlock;
Sda.sda_Name2.Length = Sda.sda_Name2.MaximumLength = sizeof(ParmBlock);
// pathmap requires a ConnDesc to determine the VolDesc and Sda, so
// kludge up a fake one here
RtlZeroMemory(&ConnDesc, sizeof(ConnDesc));
#if DBG
ConnDesc.Signature = CONNDESC_SIGNATURE;
Sda.Signature = SDA_SIGNATURE;
#endif
ConnDesc.cds_pSda = &Sda;
ConnDesc.cds_pVolDesc = pVolDesc;
if (!NT_SUCCESS(Status = AfpFspDispSetDirParms(&Sda)))
{
DBGPRINT(DBG_COMP_ADMINAPI_DIR, DBG_LEVEL_INFO,
("AfpAdmWDirectorySetInfo: AfpFspDispSetDirParms failed 0x%lx\n",
Status));
if (Status == AFP_ERR_ACCESS_DENIED)
{
Status = STATUS_ACCESS_DENIED;
}
else
{
Status = STATUS_OBJECT_PATH_NOT_FOUND;
}
}
} while (False);
if (Sda.sda_Name1.Buffer != NULL)
{
AfpFreeMemory(Sda.sda_Name1.Buffer);
}
AfpVolumeDereference(pVolDesc);
return Status;
}
/*** AfpAdmWFinderSetInfo
*
* Set the type and/or creator of a file.
* (Note this routine can be expanded later to set other Finder info if
* needed)
*
* LOCKS: vds_IdDbAccessLock (SWMR, Exclusive);
*/
AFPSTATUS
AfpAdmWFinderSetInfo(
IN OUT PVOID InBuf OPTIONAL,
IN LONG OutBufLen OPTIONAL,
OUT PVOID OutBuf OPTIONAL
)
{
PAFP_FINDER_INFO pAdmFDInfo;
DWORD ParmNum, Bitmap = 0;
UNICODE_STRING VolumePath, UTypeCreatorString;
ANSI_STRING MacAnsiFileDirPath, ATypeCreatorString;
SDA Sda;
CONNDESC ConnDesc;
PVOLDESC pVolDesc;
AFPSTATUS Status;
FILEDIRPARM FDParm;
PATHMAPENTITY PME;
BYTE Type[AFP_TYPE_LEN] = " "; // Pad with spaces
BYTE Creator[AFP_CREATOR_LEN] = " "; // Pad with spaces
PAGED_CODE( );
pAdmFDInfo = (PAFP_FINDER_INFO)((PBYTE)InBuf + sizeof(SETINFOREQPKT));
ParmNum = ((PSETINFOREQPKT)InBuf)->sirqp_parmnum;
DBGPRINT(DBG_COMP_ADMINAPI_DIR, DBG_LEVEL_INFO,
("AfpAdmWFinderSetInfo entered for %ws (%lx)\n",
pAdmFDInfo->afpfd_path, ParmNum));
if ((ParmNum & ~AFP_FD_PARMNUM_ALL) || !ParmNum)
{
return AFPERR_InvalidParms;
}
// Convert the parmnum to a bitmap for use by pathmap to retrieve current
// settings of FinderInfo, and convert type and creator to space padded
// mac ansi
if (ParmNum & AFP_FD_PARMNUM_TYPE)
{
Bitmap |= FD_BITMAP_FINDERINFO;
RtlInitUnicodeString(&UTypeCreatorString, pAdmFDInfo->afpfd_type);
if ((UTypeCreatorString.Length == 0) ||
(UTypeCreatorString.Length/sizeof(WCHAR) > AFP_TYPE_LEN))
{
return AFPERR_InvalidParms;
}
ATypeCreatorString.Length = 0;
ATypeCreatorString.MaximumLength = sizeof(Type);
ATypeCreatorString.Buffer = Type;
Status = AfpConvertStringToAnsi(&UTypeCreatorString,
&ATypeCreatorString);
if (!NT_SUCCESS(Status))
{
return STATUS_UNSUCCESSFUL;
}
}
if (ParmNum & AFP_FD_PARMNUM_CREATOR)
{
Bitmap |= FD_BITMAP_FINDERINFO;
RtlInitUnicodeString(&UTypeCreatorString, pAdmFDInfo->afpfd_creator);
if ((UTypeCreatorString.Length == 0) ||
(UTypeCreatorString.Length/sizeof(WCHAR) > AFP_CREATOR_LEN))
{
return AFPERR_InvalidParms;
}
ATypeCreatorString.Length = 0;
ATypeCreatorString.MaximumLength = sizeof(Creator);
ATypeCreatorString.Buffer = Creator;
Status = AfpConvertStringToAnsi(&UTypeCreatorString,
&ATypeCreatorString);
if (!NT_SUCCESS(Status))
{
return STATUS_UNSUCCESSFUL;
}
}
MacAnsiFileDirPath.Length = 0;
MacAnsiFileDirPath.MaximumLength = 0;
MacAnsiFileDirPath.Buffer = NULL;
// First find the volume that this directory is path of
RtlInitUnicodeString(&VolumePath, pAdmFDInfo->afpfd_path);
if (!NT_SUCCESS(Status = AfpVolumeReferenceByPath(&VolumePath, &pVolDesc)))
return Status;
// Now get the volume relative path of the file/directory.
VolumePath.Buffer = (LPWSTR)((PBYTE)VolumePath.Buffer +
pVolDesc->vds_Path.Length);
VolumePath.Length -= pVolDesc->vds_Path.Length;
VolumePath.MaximumLength -= pVolDesc->vds_Path.Length;
if ((SHORT)(VolumePath.Length) < 0)
{
VolumePath.Length = 0;
VolumePath.MaximumLength = sizeof(WCHAR);
}
if (Bitmap) do
{
AfpInitializeFDParms(&FDParm);
AfpInitializePME(&PME, 0, NULL);
if (!NT_SUCCESS(Status = afpConvertAdminPathToMacPath(pVolDesc,
&VolumePath,
&MacAnsiFileDirPath)))
{
Status = STATUS_OBJECT_PATH_NOT_FOUND;
break;
}
// pathmap requires a ConnDesc to determine the VolDesc and Sda, so
// kludge up a fake one here
RtlZeroMemory(&ConnDesc, sizeof(ConnDesc));
#if DBG
ConnDesc.Signature = CONNDESC_SIGNATURE;
#endif
Sda.sda_ClientType = SDA_CLIENT_ADMIN;
ConnDesc.cds_pSda = &Sda;
ConnDesc.cds_pVolDesc = pVolDesc;
Status = AfpMapAfpPathForLookup(&ConnDesc, AFP_ID_ROOT,
&MacAnsiFileDirPath,
AFP_LONGNAME,
DFE_ANY,
FD_INTERNAL_BITMAP_OPENACCESS_ADMINGET |
FD_BITMAP_LONGNAME | Bitmap,
&PME,
&FDParm);
if (!NT_SUCCESS(Status))
{
if (Status == AFP_ERR_ACCESS_DENIED)
{
Status = STATUS_ACCESS_DENIED;
}
else
{
Status = STATUS_OBJECT_PATH_NOT_FOUND;
}
break;
}
// Copy the input Finder info into the FDParms structure
if (ParmNum & AFP_FD_PARMNUM_TYPE)
RtlCopyMemory(&FDParm._fdp_FinderInfo.fd_Type,
Type, AFP_TYPE_LEN);
if (ParmNum & AFP_FD_PARMNUM_CREATOR)
RtlCopyMemory(&FDParm._fdp_FinderInfo.fd_Creator,
Creator, AFP_CREATOR_LEN);
// Set the AfpInfo
AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);
Status = AfpSetAfpInfo(&PME.pme_Handle, Bitmap, &FDParm, pVolDesc, NULL);
AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
} while (False);
if (PME.pme_Handle.fsh_FileHandle != NULL)
AfpIoClose(&PME.pme_Handle);
if (MacAnsiFileDirPath.Buffer != NULL)
{
AfpFreeMemory(MacAnsiFileDirPath.Buffer);
}
AfpVolumeDereference(pVolDesc);
return Status;
}
/*** AfpLookupEtcMapEntry
*
* Lookup a type/creator mapping in the global table by comparing the
* extension to the desired extension. Note the default type creator
* mapping is not kept in the table.
*
* LOCKS_ASSUMED: AfpEtcMapLock (SWMR, Shared)
*/
PETCMAPINFO
AfpLookupEtcMapEntry(
PUCHAR pExt
)
{
PETCMAPINFO petc = NULL;
ANSI_STRING alookupext, atableext;
int i;
PAGED_CODE( );
if (AfpEtcMapCount == 0)
{
return NULL;
}
ASSERT ((AfpEtcMapsSize > 0) && (AfpEtcMaps != NULL));
RtlInitString(&alookupext,pExt);
for (i=0;i<AfpEtcMapsSize;i++)
{
RtlInitString(&atableext,AfpEtcMaps[i].etc_extension);
if (RtlEqualString(&atableext, &alookupext,True))
{
petc = &(AfpEtcMaps[i]);
break;
}
}
return petc;
}
/*** afpEtcMapDelete
*
* Mark the extension/type/creator table entry as deleted by setting the
* extension field to null. Decrement the count of valid entries. If
* the number of free entries goes above a certain level, shrink the
* table down to a reasonable size.
*
* LOCKS_ASSUMED: AfpEtcMapLock (SWMR, Exclusive)
*
*/
VOID
afpEtcMapDelete(
PETCMAPINFO pEtcEntry
)
{
PETCMAPINFO ptemptable;
LONG newtablesize, nextnewentry, i;
PAGED_CODE( );
//
// a null extension denotes an invalid ext/type/creator mapping table entry
//
pEtcEntry->etc_extension[0] = '\0';
AfpEtcMapCount --;
ASSERT (AfpEtcMapCount >= 0);
if ((AfpEtcMapsSize - AfpEtcMapCount) > AFP_MAX_FREE_ETCMAP_ENTRIES)
{
//
// shrink the type/creator table by AFP_MAX_FREE_ETCMAP_ENTRIES
//
newtablesize = (AfpEtcMapsSize - AFP_MAX_FREE_ETCMAP_ENTRIES);
if ((ptemptable = (PETCMAPINFO)AfpAllocZeroedPagedMemory(newtablesize * sizeof(ETCMAPINFO))) == NULL)
{
return;
}
nextnewentry = 0;
for (i=0;i<AfpEtcMapsSize;i++)
{
if (afpIsValidEtcMapEntry(AfpEtcMaps[i].etc_extension))
{
ASSERT(nextnewentry < AfpEtcMapCount);
RtlCopyMemory(&ptemptable[nextnewentry++], &AfpEtcMaps[i], sizeof(ETCMAPINFO));
}
}
AfpFreeMemory(AfpEtcMaps);
AfpEtcMaps = ptemptable;
AfpEtcMapsSize = newtablesize;
}
}
/*** afpGetNextFreeEtcMapEntry
*
* Look for an empty entry in the extension/type/creator table starting
* at the entry StartIndex.
*
* LOCKS_ASSUMED: AfpEtcMapLock (SWMR, Exclusive)
*/
PETCMAPINFO
afpGetNextFreeEtcMapEntry(
IN OUT PLONG StartIndex
)
{
PETCMAPINFO tempptr = NULL;
LONG i;
PAGED_CODE( );
for (i = *StartIndex; i < AfpEtcMapsSize; i++)
{
if (!afpIsValidEtcMapEntry(AfpEtcMaps[i].etc_extension))
{
tempptr = &AfpEtcMaps[i];
*StartIndex = i++;
break;
}
}
return tempptr;
}
/*** afpCopyMapInfo2ToMapInfo
*
* Copy the etc info structure given to us by the Service into our structure, after
* converting the etc_extension field from Unicode to Ansi.
*
*/
NTSTATUS
afpCopyMapInfo2ToMapInfo(
OUT PETCMAPINFO pEtcDest,
IN PETCMAPINFO2 pEtcSource
)
{
UCHAR ext[AFP_EXTENSION_LEN+1];
WCHAR wext[AFP_EXTENSION_LEN+1];
ANSI_STRING aext;
NTSTATUS Status;
UNICODE_STRING uext;
AfpSetEmptyAnsiString(&aext, sizeof(ext), ext);
uext.Length = uext.MaximumLength = sizeof(pEtcSource->etc_extension);
uext.Buffer = pEtcSource->etc_extension;
Status = AfpConvertMungedUnicodeToAnsi(&uext, &aext);
ASSERT(NT_SUCCESS(Status));
RtlCopyMemory(pEtcDest->etc_extension, aext.Buffer, AFP_EXTENSION_LEN);
pEtcDest->etc_extension[AFP_EXTENSION_LEN] = 0;
// Copy the other two fields as-is
RtlCopyMemory(pEtcDest->etc_type, pEtcSource->etc_type, AFP_TYPE_LEN);
RtlCopyMemory(pEtcDest->etc_creator, pEtcSource->etc_creator, AFP_CREATOR_LEN);
return STATUS_SUCCESS;
}
/*** afpConvertAdminPathToMacPath
*
* Convert an admin volume relative NTFS path which may contain
* components > 31 chars, or may contain shortnames, to the
* equivalent mac path (in mac ANSI) so that the path may be sent thru the
* pathmap code. Caller must free path buffer if success is returned.
*/
NTSTATUS
afpConvertAdminPathToMacPath(
IN PVOLDESC pVolDesc,
IN PUNICODE_STRING AdminPath,
OUT PANSI_STRING MacPath
)
{
USHORT tempAdminPathlen = 0, numchars, numcomponents, i;
WCHAR wbuf[AFP_LONGNAME_LEN + 1];
UNICODE_STRING component, component2;
UNICODE_STRING pathSoFar, pathToParent;
NTSTATUS Status = STATUS_SUCCESS;
CHAR abuf[AFP_LONGNAME_LEN + 1];
ANSI_STRING macansiComponent;
PWSTR tempptr;
FILESYSHANDLE hComponent;
BOOLEAN NTFSShortname;
PAGED_CODE( );
// ASSERT(IS_VOLUME_NTFS(pVolDesc));
// assert that the path does not begin with a backslash
ASSERT((AdminPath->Length == 0) || (AdminPath->Buffer[0] != L'\\'));
component2.Length = 0;
component2.MaximumLength = sizeof(wbuf);
component2.Buffer = wbuf;
macansiComponent.Length = 0;
macansiComponent.MaximumLength = sizeof(abuf);
macansiComponent.Buffer = abuf;
MacPath->Length = MacPath->MaximumLength = 0;
MacPath->Buffer = NULL;
// return success if no path components
if (AdminPath->Length == 0)
{
return STATUS_SUCCESS;
}
numchars = AdminPath->Length / sizeof(WCHAR);
// strip a trailing path separator if it exists
if (AdminPath->Buffer[numchars - 1] == L'\\')
{
AdminPath->Length -= sizeof(WCHAR);
}
for (numcomponents = 1, i = 0; i < numchars; i++)
{
if (AdminPath->Buffer[i] == L'\\')
{
numcomponents++;
}
}
// allocate a buffer to hold the mac (in mac ANSI) version of the path and
// path separators
MacPath->MaximumLength = numcomponents * AFP_LONGNAME_LEN + numcomponents;
if ((MacPath->Buffer = (PCHAR)AfpAllocPagedMemory(MacPath->MaximumLength))
== NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
pathSoFar = *AdminPath;
pathSoFar.Length = 0;
tempptr = AdminPath->Buffer;
while (numcomponents)
{
hComponent.fsh_FileHandle = NULL;
component.Buffer = tempptr;
component2.Length = macansiComponent.Length = 0;
NTFSShortname = False;
numchars = 0;
while (True)
{
if (tempptr[numchars] == L'~')
{
NTFSShortname = True;
}
if ((tempptr[numchars] == L'\\') ||
((numcomponents == 1) &&
(pathSoFar.Length + numchars * sizeof(WCHAR)
== AdminPath->Length)))
{
break;
}
numchars ++;
}
component.Length = component.MaximumLength = numchars * sizeof(WCHAR);
pathToParent = pathSoFar;
pathSoFar.Length += component.Length;
tempptr += numchars + 1;
if ((numchars > AFP_LONGNAME_LEN) || (NTFSShortname))
{
// open a handle to the directory so we can query the name;
// to query the shortname we need a handle to the actual
// directory; to query the longname, we need a handle to the
// parent directory because of the way we have to
// get the longname by enumerating the parent for one entry
// with the name we are looking for
if (NT_SUCCESS(Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
AFP_STREAM_DATA,
FILEIO_OPEN_DIR,
((numchars <= AFP_LONGNAME_LEN) && NTFSShortname) ?
&pathToParent : &pathSoFar,
FILEIO_ACCESS_NONE,
FILEIO_DENY_NONE,
False,
&hComponent)))
{
if (numchars > AFP_LONGNAME_LEN)
{
// query the shortname
Status = AfpIoQueryShortName(&hComponent, &macansiComponent);
}
else
{
// we saw a tilde and are assuming it is the shortname,
// and the path is 31 chars or less; query the longname
if (NT_SUCCESS(Status = AfpIoQueryLongName(&hComponent,
&component,
&component2)))
{
Status = AfpConvertMungedUnicodeToAnsi(&component2,
&macansiComponent);
}
}
AfpIoClose(&hComponent);
if (!NT_SUCCESS(Status))
{
break;
}
}
else
{
// open failed
break;
}
}
else
{
// use the component name as it was given by admin
if (!NT_SUCCESS(Status = AfpConvertMungedUnicodeToAnsi(&component,
&macansiComponent)))
{
break;
}
}
Status = RtlAppendStringToString(MacPath, &macansiComponent);
ASSERT(NT_SUCCESS(Status));
if (!NT_SUCCESS(Status))
{
break;
}
// include the path separator in the admin path seen so far
pathSoFar.Length += sizeof(WCHAR);
// add a path separator to the mac ansi path
MacPath->Buffer[MacPath->Length++] = AFP_PATHSEP;
ASSERT(MacPath->Length <= MacPath->MaximumLength);
numcomponents --;
} // while numcomponents
if (!NT_SUCCESS(Status) && (MacPath->Buffer != NULL))
{
AfpFreeMemory(MacPath->Buffer);
MacPath->Buffer = NULL;
}
return Status;
}