/** Build then send a MTFTP data packet for the MTFTP upload session. @param Instance The MTFTP upload session. @param BlockNum The block number to send. @retval EFI_OUT_OF_RESOURCES Failed to build the packet. @retval EFI_ABORTED The consumer of this child directs to abort the transmission by return an error through PacketNeeded. @retval EFI_SUCCESS The data is sent. **/ EFI_STATUS Mtftp4WrqSendBlock ( IN OUT MTFTP4_PROTOCOL *Instance, IN UINT16 BlockNum ) { EFI_MTFTP4_PACKET *Packet; EFI_MTFTP4_TOKEN *Token; NET_BUF *UdpPacket; EFI_STATUS Status; UINT16 DataLen; UINT8 *DataBuf; UINT64 Start; // // Allocate a buffer to hold the user data // UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN); if (UdpPacket == NULL) { return EFI_OUT_OF_RESOURCES; } Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE); ASSERT (Packet != NULL); Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA); Packet->Data.Block = HTONS (BlockNum); // // Read the block from either the buffer or PacketNeeded callback // Token = Instance->Token; DataLen = Instance->BlkSize; if (Token->Buffer != NULL) { Start = MultU64x32 (BlockNum - 1, Instance->BlkSize); if (Token->BufferSize < Start + Instance->BlkSize) { DataLen = (UINT16) (Token->BufferSize - Start); Instance->LastBlock = BlockNum; Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum); } if (DataLen > 0) { NetbufAllocSpace (UdpPacket, DataLen, FALSE); CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen); } } else { // // Get data from PacketNeeded // DataBuf = NULL; Status = Token->PacketNeeded ( &Instance->Mtftp4, Token, &DataLen, (VOID **) &DataBuf ); if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { if (DataBuf != NULL) { FreePool (DataBuf); } if (UdpPacket != NULL) { NetbufFree (UdpPacket); } Mtftp4SendError ( Instance, EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, (UINT8 *) "User aborted the transfer" ); return EFI_ABORTED; } if (DataLen < Instance->BlkSize) { Instance->LastBlock = BlockNum; Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum); } if (DataLen > 0) { NetbufAllocSpace (UdpPacket, DataLen, FALSE); CopyMem (Packet->Data.Data, DataBuf, DataLen); FreePool (DataBuf); } } return Mtftp4SendPacket (Instance, UdpPacket); }
/** Deliver the received data block to the user, which can be saved in the user provide buffer or through the CheckPacket callback. @param Instance The Mtftp session @param Packet The received data packet @param Len The packet length @retval EFI_SUCCESS The data is saved successfully @retval EFI_ABORTED The user tells to abort by return an error through CheckPacket @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is updated to the actual buffer size needed. **/ EFI_STATUS Mtftp4RrqSaveBlock ( IN OUT MTFTP4_PROTOCOL *Instance, IN EFI_MTFTP4_PACKET *Packet, IN UINT32 Len ) { EFI_MTFTP4_TOKEN *Token; EFI_STATUS Status; UINT16 Block; UINT64 Start; UINT32 DataLen; UINT64 TotalBlock; BOOLEAN Completed; Completed = FALSE; Token = Instance->Token; Block = NTOHS (Packet->Data.Block); DataLen = Len - MTFTP4_DATA_HEAD_LEN; // // This is the last block, save the block no // if (DataLen < Instance->BlkSize) { Completed = TRUE; Instance->LastBlock = Block; Mtftp4SetLastBlockNum (&Instance->Blocks, Block); } // // Remove this block number from the file hole. If Mtftp4RemoveBlockNum // returns EFI_NOT_FOUND, the block has been saved, don't save it again. // Note that : For bigger files, allowing the block counter to roll over // to accept transfers of unlimited size. So TotalBlock is memorised as // continuous block counter. // Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &TotalBlock); if (Status == EFI_NOT_FOUND) { return EFI_SUCCESS; } else if (EFI_ERROR (Status)) { return Status; } if (Token->CheckPacket != NULL) { Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16) Len, Packet); if (EFI_ERROR (Status)) { Mtftp4SendError ( Instance, EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION, (UINT8 *) "User aborted download" ); return EFI_ABORTED; } } if (Token->Buffer != NULL) { Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize); if (Start + DataLen <= Token->BufferSize) { CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen); // // Update the file size when received the last block // if ((Instance->LastBlock == Block) && Completed) { Token->BufferSize = Start + DataLen; } } else if (Instance->LastBlock != 0) { // // Don't save the data if the buffer is too small, return // EFI_BUFFER_TOO_SMALL if received the last packet. This // will give a accurate file length. // Token->BufferSize = Start + DataLen; Mtftp4SendError ( Instance, EFI_MTFTP4_ERRORCODE_DISK_FULL, (UINT8 *) "User provided memory block is too small" ); return EFI_BUFFER_TOO_SMALL; } } return EFI_SUCCESS; }