#pragma noroot #include #include #include "session.h" #include "aspinterface.h" #include "dsi.h" #include "tcpconnection.h" #include "endian.h" #include "readtcp.h" #include "asmglue.h" #include "cmdproc.h" #include "installcmds.h" #include "atipmapping.h" typedef struct FPReadRec { Word CommandCode; /* includes pad byte */ Word OForkRefNum; LongWord Offset; LongWord ReqCount; Byte NewLineMask; Byte NewLineChar; } FPReadRec; #define kFPRead 27 static void EndSession(Session *sess, Boolean callAttnRoutine); static void DoSPGetStatus(Session *sess, ASPGetStatusRec *commandRec); static void DoSPOpenSession(Session *sess, ASPOpenSessionRec *commandRec); static void DoSPCloseSession(Session *sess, ASPCloseSessionRec *commandRec); static void DoSPCommand(Session *sess, ASPCommandRec *commandRec); static void DoSPWrite(Session *sess, ASPWriteRec *commandRec); Session sessionTbl[MAX_SESSIONS]; #pragma databank 1 LongWord DispatchASPCommand(SPCommandRec *commandRec) { Session *sess; unsigned int i; Word stateReg; stateReg = ForceRomIn(); if (commandRec->command == aspGetStatusCommand || commandRec->command==aspOpenSessionCommand) { if (((ASPGetStatusRec*)commandRec)->slsNet != atipMapping.networkNumber || ((ASPGetStatusRec*)commandRec)->slsNode != atipMapping.node || ((ASPGetStatusRec*)commandRec)->slsSocket != atipMapping.socket) { goto callOrig; } for (i = 0; i < MAX_SESSIONS; i++) { if (sessionTbl[i].dsiStatus == unused) break; } if (i == MAX_SESSIONS) { CompleteASPCommand(sess, aspTooManySessions); goto ret; } sess = &sessionTbl[i]; sess->spCommandRec = commandRec; if (!StartTCPConnection(sess)) { FlagFatalError(sess, commandRec->result); goto ret; } sess->dsiStatus = awaitingHeader; InitReadTCP(sess, DSI_HEADER_SIZE, &sess->reply); } else { if (commandRec->refNum < SESSION_NUM_START) { goto callOrig; } /* * Hack to avoid hangs/crashes related to the AppleShare FST's * asynchronous polling to check if volumes have been modified. * This effectively disables that mechanism (which is the only * real user of asynchronous ASP calls). * * TODO Identify and fix the underlying issue. */ if ((commandRec->async & AT_ASYNC) && commandRec->command == aspCommandCommand) { commandRec->result = atSyncErr; CallCompletionRoutine((void*)commandRec->completionPtr); goto ret; } i = commandRec->refNum - SESSION_NUM_START; sess = &sessionTbl[i]; sess->spCommandRec = commandRec; } // TODO properly handle all cases of getting a command while // one is in progress if (commandRec->command != aspCloseSessionCommand) { if (sess->commandPending) { CompleteASPCommand(sess, aspSessNumErr); goto ret; } sess->commandPending = TRUE; } switch (commandRec->command) { case aspGetStatusCommand: DoSPGetStatus(sess, (ASPGetStatusRec *)commandRec); break; case aspOpenSessionCommand: DoSPOpenSession(sess, (ASPOpenSessionRec *)commandRec); break; case aspCloseSessionCommand: DoSPCloseSession(sess, (ASPCloseSessionRec *)commandRec); break; case aspCommandCommand: DoSPCommand(sess, (ASPCommandRec *)commandRec); break; case aspWriteCommand: DoSPWrite(sess, (ASPWriteRec *)commandRec); break; } if ((commandRec->async & AT_ASYNC) && sess->commandPending) { commandRec->result = aspBusyErr; // indicate call in process goto ret; } // if we're here, the call is synchronous -- we must complete it if (commandRec->command == aspCloseSessionCommand) { FinishASPCommand(sess); } else { while (sess->commandPending) { PollForData(sess); } } ret: RestoreStateReg(stateReg); return 0; callOrig: RestoreStateReg(stateReg); return oldCmds[commandRec->command]; } #pragma databank 0 static void DoSPGetStatus(Session *sess, ASPGetStatusRec *commandRec) { static const Word kFPGetSrvrInfo = 15; sess->request.flags = DSI_REQUEST; sess->request.command = DSIGetStatus; sess->request.requestID = htons(sess->nextRequestID++); sess->request.writeOffset = 0; sess->request.totalDataLength = htonl(2); sess->replyBuf = (void*)commandRec->bufferAddr; sess->replyBufLen = commandRec->bufferLength; SendDSIMessage(sess, &sess->request, &kFPGetSrvrInfo, NULL); } static void DoSPOpenSession(Session *sess, ASPOpenSessionRec *commandRec) { sess->request.flags = DSI_REQUEST; sess->request.command = DSIOpenSession; sess->request.requestID = htons(sess->nextRequestID++); sess->request.writeOffset = 0; sess->request.totalDataLength = 0; sess->replyBuf = NULL; sess->replyBufLen = 0; SendDSIMessage(sess, &sess->request, NULL, NULL); } static void DoSPCloseSession(Session *sess, ASPCloseSessionRec *commandRec) { sess->request.flags = DSI_REQUEST; sess->request.command = DSICloseSession; sess->request.requestID = htons(sess->nextRequestID++); sess->request.writeOffset = 0; sess->request.totalDataLength = 0; sess->replyBuf = NULL; sess->replyBufLen = 0; SendDSIMessage(sess, &sess->request, NULL, NULL); } static void DoSPCommand(Session *sess, ASPCommandRec *commandRec) { sess->request.flags = DSI_REQUEST; sess->request.command = DSICommand; sess->request.requestID = htons(sess->nextRequestID++); sess->request.writeOffset = 0; sess->request.totalDataLength = htonl(commandRec->cmdBlkLength); sess->replyBuf = (void*)(commandRec->replyBufferAddr & 0x00FFFFFF); sess->replyBufLen = commandRec->replyBufferLen; /* * If the client is requesting to read more data than will fit in its * buffer, change the request to ask for only the amount that will fit. * * This is necessary because ASP request and reply bodies are limited * to a "quantum size" of 4624 bytes. Even if more data than that is * requested, only that amount will be returned in a single reply. * If the full read count requested is larger, it simply serves as a * hint about the total amount that will be read across a series of * FPRead requests. The AppleShare FST relies on this behavior and * only specifies a buffer size of 4624 bytes for such requests. * * DSI does not have this "quantum size" limitation, so the full * requested amount would be returned but then not fit in the buffer. * To avoid this, we modify the request to ask only for the amount of * data that will fit in the buffer. This removes the function of * hinting about the total read size, but should work OK in practice. * * A similar issue could arise with FPEnumerate requests, but it * actually doesn't in the case of the requests generated by the * AppleShare FST, so we don't try to address them for now. */ if (commandRec->cmdBlkLength == sizeof(FPReadRec) && ((FPReadRec*)commandRec->cmdBlkAddr)->CommandCode == kFPRead && ntohl(((FPReadRec*)commandRec->cmdBlkAddr)->ReqCount) > commandRec->replyBufferLen) { ((FPReadRec*)commandRec->cmdBlkAddr)->ReqCount = htonl(commandRec->replyBufferLen); } /* Mask off high byte of addresses because PFI (at least) may * put junk in them, and this can cause Marinetti errors. */ SendDSIMessage(sess, &sess->request, (void*)(commandRec->cmdBlkAddr & 0x00FFFFFF), NULL); } static void DoSPWrite(Session *sess, ASPWriteRec *commandRec) { sess->request.flags = DSI_REQUEST; sess->request.command = DSIWrite; sess->request.requestID = htons(sess->nextRequestID++); sess->request.writeOffset = htonl(commandRec->cmdBlkLength); sess->request.totalDataLength = htonl(commandRec->cmdBlkLength + commandRec->writeDataLength); sess->replyBuf = (void*)(commandRec->replyBufferAddr & 0x00FFFFFF); sess->replyBufLen = commandRec->replyBufferLen; SendDSIMessage(sess, &sess->request, (void*)(commandRec->cmdBlkAddr & 0x00FFFFFF), (void*)(commandRec->writeDataAddr & 0x00FFFFFF)); } void FlagFatalError(Session *sess, Word errorCode) { sess->dsiStatus = error; if (errorCode == 0) { // TODO deduce better error code from Marinetti errors? errorCode = aspNetworkErr; } if (sess->commandPending) { CompleteASPCommand(sess, errorCode); } EndSession(sess, TRUE); } // Fill in any necessary data in the ASP command rec for a successful return void FinishASPCommand(Session *sess) { LongWord dataLength; dataLength = ntohl(sess->reply.totalDataLength); if (dataLength > 0xFFFF) { // The IIgs ASP interfaces can't represent lengths over 64k. // This should be detected as an error earlier, but let's make sure. sess->spCommandRec->result = aspSizeErr; goto complete; } switch(sess->spCommandRec->command) { case aspGetStatusCommand: ((ASPGetStatusRec*)(sess->spCommandRec))->dataLength = dataLength; break; case aspOpenSessionCommand: ((ASPOpenSessionRec*)(sess->spCommandRec))->refNum = (sess - &sessionTbl[0]) + SESSION_NUM_START; break; case aspCloseSessionCommand: break; case aspCommandCommand: ((ASPCommandRec*)(sess->spCommandRec))->cmdResult = sess->reply.errorCode; ((ASPCommandRec*)(sess->spCommandRec))->replyLength = dataLength; break; case aspWriteCommand: ((ASPWriteRec*)(sess->spCommandRec))->cmdResult = sess->reply.errorCode; ((ASPWriteRec*)(sess->spCommandRec))->replyLength = dataLength; ((ASPWriteRec*)(sess->spCommandRec))->writtenLength = ((ASPWriteRec*)(sess->spCommandRec))->writeDataLength; break; } complete: CompleteASPCommand(sess, 0); } /* Actions to complete a command, whether successful or not */ void CompleteASPCommand(Session *sess, Word result) { SPCommandRec *commandRec; commandRec = sess->spCommandRec; if (sess->spCommandRec->command == aspGetStatusCommand || sess->spCommandRec->command == aspCloseSessionCommand) { EndSession(sess, FALSE); } else { sess->commandPending = FALSE; if (sess->dsiStatus != error) { sess->dsiStatus = awaitingHeader; InitReadTCP(sess, DSI_HEADER_SIZE, &sess->reply); } } commandRec->result = result; if ((commandRec->async & AT_ASYNC) && commandRec->completionPtr != NULL) { CallCompletionRoutine((void *)commandRec->completionPtr); } } static void EndSession(Session *sess, Boolean callAttnRoutine) { if (callAttnRoutine) { // TODO call the attention routine to report end of session } EndTCPConnection(sess); memset(sess, 0, sizeof(*sess)); } void PollAllSessions(void) { unsigned int i; for (i = 0; i < MAX_SESSIONS; i++) { if (sessionTbl[i].dsiStatus != unused) { PollForData(&sessionTbl[i]); } } }