+/**\r
+ Validate a buffer that the EFI_FILE_PROTOCOL.SetInfo() caller passes in for a\r
+ particular InformationType GUID.\r
+\r
+ The structure to be validated is supposed to end with a variable-length,\r
+ NUL-terminated CHAR16 Name string.\r
+\r
+ @param[in] SizeByProtocolCaller The BufferSize parameter as provided by the\r
+ EFI_FILE_PROTOCOL.SetInfo() caller.\r
+\r
+ @param[in] MinimumStructSize The minimum structure size that is required\r
+ for the given InformationType GUID,\r
+ including a single CHAR16 element from the\r
+ trailing Name field.\r
+\r
+ @param[in] IsSizeByInfoPresent TRUE if and only if the expected structure\r
+ starts with a UINT64 Size field that reports\r
+ the actual structure size.\r
+\r
+ @param[in] Buffer The Buffer parameter as provided by the\r
+ EFI_FILE_PROTOCOL.SetInfo() caller.\r
+\r
+ @retval EFI_SUCCESS Validation successful, Buffer is well-formed.\r
+\r
+ @retval EFI_BAD_BUFFER_SIZE The EFI_FILE_PROTOCOL.SetInfo()\r
+ caller provided a BufferSize that is smaller\r
+ than the minimum structure size required for\r
+ the given InformationType GUID.\r
+\r
+ @retval EFI_INVALID_PARAMETER IsSizeByInfoPresent is TRUE, and the leading\r
+ UINT64 Size field does not match the\r
+ EFI_FILE_PROTOCOL.SetInfo() caller-provided\r
+ BufferSize.\r
+\r
+ @retval EFI_INVALID_PARAMETER The trailing Name field does not consist of a\r
+ whole multiple of CHAR16 elements.\r
+\r
+ @retval EFI_INVALID_PARAMETER The trailing Name field is not NUL-terminated.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+ValidateInfoStructure (\r
+ IN UINTN SizeByProtocolCaller,\r
+ IN UINTN MinimumStructSize,\r
+ IN BOOLEAN IsSizeByInfoPresent,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ UINTN NameFieldByteOffset;\r
+ UINTN NameFieldBytes;\r
+ UINTN NameFieldChar16s;\r
+ CHAR16 *NameField;\r
+\r
+ //\r
+ // Make sure the internal function asking for validation passes in sane\r
+ // values.\r
+ //\r
+ ASSERT (MinimumStructSize >= sizeof (CHAR16));\r
+ NameFieldByteOffset = MinimumStructSize - sizeof (CHAR16);\r
+\r
+ if (IsSizeByInfoPresent) {\r
+ ASSERT (MinimumStructSize >= sizeof (UINT64) + sizeof (CHAR16));\r
+ ASSERT (NameFieldByteOffset >= sizeof (UINT64));\r
+ }\r
+\r
+ //\r
+ // Check whether the protocol caller provided enough bytes for the minimum\r
+ // size of this info structure.\r
+ //\r
+ if (SizeByProtocolCaller < MinimumStructSize) {\r
+ return EFI_BAD_BUFFER_SIZE;\r
+ }\r
+\r
+ //\r
+ // If the info structure starts with a UINT64 Size field, check if that\r
+ // agrees with the protocol caller-provided size.\r
+ //\r
+ if (IsSizeByInfoPresent) {\r
+ UINT64 *SizeByInfo;\r
+\r
+ SizeByInfo = Buffer;\r
+ if (*SizeByInfo != SizeByProtocolCaller) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ //\r
+ // The CHAR16 Name field at the end of the structure must have an even number\r
+ // of bytes.\r
+ //\r
+ // The subtraction below cannot underflow, and yields at least\r
+ // sizeof(CHAR16).\r
+ //\r
+ ASSERT (SizeByProtocolCaller >= NameFieldByteOffset);\r
+ NameFieldBytes = SizeByProtocolCaller - NameFieldByteOffset;\r
+ ASSERT (NameFieldBytes >= sizeof (CHAR16));\r
+ if (NameFieldBytes % sizeof (CHAR16) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // The CHAR16 Name field at the end of the structure must be NUL-terminated.\r
+ //\r
+ NameFieldChar16s = NameFieldBytes / sizeof (CHAR16);\r
+ ASSERT (NameFieldChar16s >= 1);\r
+\r
+ NameField = (CHAR16 *)((UINT8 *)Buffer + NameFieldByteOffset);\r
+ if (NameField[NameFieldChar16s - 1] != L'\0') {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Process an EFI_FILE_SYSTEM_INFO setting request.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SetFileSystemInfo (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ VIRTIO_FS_FILE *VirtioFsFile;\r
+ VIRTIO_FS *VirtioFs;\r
+ EFI_STATUS Status;\r
+ EFI_FILE_SYSTEM_INFO *FileSystemInfo;\r
+\r
+ VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);\r
+ VirtioFs = VirtioFsFile->OwnerFs;\r
+\r
+ //\r
+ // Validate if Buffer passes as EFI_FILE_SYSTEM_INFO.\r
+ //\r
+ Status = ValidateInfoStructure (\r
+ BufferSize, // SizeByProtocolCaller\r
+ OFFSET_OF (EFI_FILE_SYSTEM_INFO,\r
+ VolumeLabel) + sizeof (CHAR16), // MinimumStructSize\r
+ TRUE, // IsSizeByInfoPresent\r
+ Buffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ FileSystemInfo = Buffer;\r
+\r
+ //\r
+ // EFI_FILE_SYSTEM_INFO fields other than VolumeLabel cannot be changed, per\r
+ // spec.\r
+ //\r
+ // If the label is being changed to its current value, report success;\r
+ // otherwise, reject the request, as the Virtio Filesystem device does not\r
+ // support changing the label.\r
+ //\r
+ if (StrCmp (FileSystemInfo->VolumeLabel, VirtioFs->Label) == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ return EFI_WRITE_PROTECTED;\r
+}\r
+\r
+/**\r
+ Process an EFI_FILE_SYSTEM_VOLUME_LABEL setting request.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+SetFileSystemVolumeLabelInfo (\r
+ IN EFI_FILE_PROTOCOL *This,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ VIRTIO_FS_FILE *VirtioFsFile;\r
+ VIRTIO_FS *VirtioFs;\r
+ EFI_STATUS Status;\r
+ EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;\r
+\r
+ VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);\r
+ VirtioFs = VirtioFsFile->OwnerFs;\r
+\r
+ //\r
+ // Validate if Buffer passes as EFI_FILE_SYSTEM_VOLUME_LABEL.\r
+ //\r
+ Status = ValidateInfoStructure (\r
+ BufferSize, // SizeByProtocolCaller\r
+ OFFSET_OF (EFI_FILE_SYSTEM_VOLUME_LABEL,\r
+ VolumeLabel) + sizeof (CHAR16), // MinimumStructSize\r
+ FALSE, // IsSizeByInfoPresent\r
+ Buffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ FileSystemVolumeLabel = Buffer;\r
+\r
+ //\r
+ // If the label is being changed to its current value, report success;\r
+ // otherwise, reject the request, as the Virtio Filesystem device does not\r
+ // support changing the label.\r
+ //\r
+ if (StrCmp (FileSystemVolumeLabel->VolumeLabel, VirtioFs->Label) == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+ return EFI_WRITE_PROTECTED;\r
+}\r
+\r