+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Link;\r
+ LIST_ENTRY *NextLink;\r
+ FAT_SUBTASK *Subtask;\r
+\r
+ //\r
+ // Sometimes the Task doesn't contain any subtasks, signal the event directly.\r
+ //\r
+ if (IsListEmpty (&Task->Subtasks)) {\r
+ Task->FileIoToken->Status = EFI_SUCCESS;\r
+ gBS->SignalEvent (Task->FileIoToken->Event);\r
+ FreePool (Task);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ EfiAcquireLock (&FatTaskLock);\r
+ InsertTailList (&IFile->Tasks, &Task->Link);\r
+ EfiReleaseLock (&FatTaskLock);\r
+\r
+ Status = EFI_SUCCESS;\r
+ //\r
+ // Use NextLink to store the next link of the list, because Link might be remove from the\r
+ // doubly-linked list and get freed in the end of current loop.\r
+ //\r
+ // Also, list operation APIs like IsNull() and GetNextNode() are avoided during the loop, since\r
+ // they may check the validity of doubly-linked lists by traversing them. These APIs cannot\r
+ // handle list elements being removed during the traverse.\r
+ //\r
+ for ( Link = GetFirstNode (&Task->Subtasks), NextLink = GetNextNode (&Task->Subtasks, Link)\r
+ ; Link != &Task->Subtasks\r
+ ; Link = NextLink, NextLink = Link->ForwardLink\r
+ ) {\r
+ Subtask = CR (Link, FAT_SUBTASK, Link, FAT_SUBTASK_SIGNATURE);\r
+ if (Subtask->Write) {\r
+\r
+ Status = IFile->OFile->Volume->DiskIo2->WriteDiskEx (\r
+ IFile->OFile->Volume->DiskIo2,\r
+ IFile->OFile->Volume->MediaId,\r
+ Subtask->Offset,\r
+ &Subtask->DiskIo2Token,\r
+ Subtask->BufferSize,\r
+ Subtask->Buffer\r
+ );\r
+ } else {\r
+ Status = IFile->OFile->Volume->DiskIo2->ReadDiskEx (\r
+ IFile->OFile->Volume->DiskIo2,\r
+ IFile->OFile->Volume->MediaId,\r
+ Subtask->Offset,\r
+ &Subtask->DiskIo2Token,\r
+ Subtask->BufferSize,\r
+ Subtask->Buffer\r
+ );\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+ }\r