/*++ 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 #include #include #include #include #include #include #include #include // 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;ietc_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;ietc_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; }