2 Initialization and helper routines for the Virtio Filesystem device.
4 Copyright (C) 2020, Red Hat, Inc.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <Library/BaseLib.h> // StrLen()
10 #include <Library/BaseMemoryLib.h> // CopyMem()
11 #include <Library/MemoryAllocationLib.h> // AllocatePool()
12 #include <Library/VirtioLib.h> // Virtio10WriteFeatures()
14 #include "VirtioFsDxe.h"
17 Read the Virtio Filesystem device configuration structure in full.
19 @param[in] Virtio The Virtio protocol underlying the VIRTIO_FS object.
21 @param[out] Config The fully populated VIRTIO_FS_CONFIG structure.
23 @retval EFI_SUCCESS Config has been filled in.
25 @return Error codes propagated from Virtio->ReadDevice(). The
26 contents of Config are indeterminate.
31 IN VIRTIO_DEVICE_PROTOCOL
*Virtio
,
32 OUT VIRTIO_FS_CONFIG
*Config
38 for (Idx
= 0; Idx
< VIRTIO_FS_TAG_BYTES
; Idx
++) {
39 Status
= Virtio
->ReadDevice (
41 OFFSET_OF (VIRTIO_FS_CONFIG
, Tag
[Idx
]), // FieldOffset
42 sizeof Config
->Tag
[Idx
], // FieldSize
43 sizeof Config
->Tag
[Idx
], // BufferSize
44 &Config
->Tag
[Idx
] // Buffer
46 if (EFI_ERROR (Status
)) {
51 Status
= Virtio
->ReadDevice (
53 OFFSET_OF (VIRTIO_FS_CONFIG
, NumReqQueues
), // FieldOffset
54 sizeof Config
->NumReqQueues
, // FieldSize
55 sizeof Config
->NumReqQueues
, // BufferSize
56 &Config
->NumReqQueues
// Buffer
62 Configure the Virtio Filesystem device underlying VirtioFs.
64 @param[in,out] VirtioFs The VIRTIO_FS object for which Virtio communication
65 should be set up. On input, the caller is
66 responsible for VirtioFs->Virtio having been
67 initialized. On output, synchronous Virtio
68 Filesystem commands (primitives) may be submitted to
71 @retval EFI_SUCCESS Virtio machinery has been set up.
73 @retval EFI_UNSUPPORTED The host-side configuration of the Virtio Filesystem
74 is not supported by this driver.
76 @return Error codes from underlying functions.
80 IN OUT VIRTIO_FS
*VirtioFs
86 VIRTIO_FS_CONFIG Config
;
91 // Execute virtio-v1.1-cs01-87fa6b5d8155, 3.1.1 Driver Requirements: Device
94 // 1. Reset the device.
97 Status
= VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
98 if (EFI_ERROR (Status
)) {
103 // 2. Set the ACKNOWLEDGE status bit [...]
105 NextDevStat
|= VSTAT_ACK
;
106 Status
= VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
107 if (EFI_ERROR (Status
)) {
112 // 3. Set the DRIVER status bit [...]
114 NextDevStat
|= VSTAT_DRIVER
;
115 Status
= VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
116 if (EFI_ERROR (Status
)) {
121 // 4. Read device feature bits...
123 Status
= VirtioFs
->Virtio
->GetDeviceFeatures (VirtioFs
->Virtio
, &Features
);
124 if (EFI_ERROR (Status
)) {
127 if ((Features
& VIRTIO_F_VERSION_1
) == 0) {
128 Status
= EFI_UNSUPPORTED
;
132 // No device-specific feature bits have been defined in file "virtio-fs.tex"
133 // of the virtio spec at <https://github.com/oasis-tcs/virtio-spec.git>, as
134 // of commit 87fa6b5d8155.
136 Features
&= VIRTIO_F_VERSION_1
| VIRTIO_F_IOMMU_PLATFORM
;
139 // ... and write the subset of feature bits understood by the [...] driver to
141 // 5. Set the FEATURES_OK status bit.
142 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
144 Status
= Virtio10WriteFeatures (VirtioFs
->Virtio
, Features
, &NextDevStat
);
145 if (EFI_ERROR (Status
)) {
150 // 7. Perform device-specific setup, including discovery of virtqueues for
151 // the device, [...] reading [...] the device's virtio configuration space
153 Status
= VirtioFsReadConfig (VirtioFs
->Virtio
, &Config
);
154 if (EFI_ERROR (Status
)) {
159 // 7.a. Convert the filesystem label from UTF-8 to UCS-2. Only labels with
160 // printable ASCII code points (U+0020 through U+007E) are supported.
161 // NUL-terminate at either the terminator we find, or right after the
164 for (Idx
= 0; Idx
< VIRTIO_FS_TAG_BYTES
&& Config
.Tag
[Idx
] != '\0'; Idx
++) {
165 if (Config
.Tag
[Idx
] < 0x20 || Config
.Tag
[Idx
] > 0x7E) {
166 Status
= EFI_UNSUPPORTED
;
169 VirtioFs
->Label
[Idx
] = Config
.Tag
[Idx
];
171 VirtioFs
->Label
[Idx
] = L
'\0';
174 // 7.b. We need one queue for sending normal priority requests.
176 if (Config
.NumReqQueues
< 1) {
177 Status
= EFI_UNSUPPORTED
;
182 // 7.c. Fetch and remember the number of descriptors we can place on the
183 // queue at once. We'll need two descriptors per request, as a minimum --
184 // request header, response header.
186 Status
= VirtioFs
->Virtio
->SetQueueSel (VirtioFs
->Virtio
,
187 VIRTIO_FS_REQUEST_QUEUE
);
188 if (EFI_ERROR (Status
)) {
191 Status
= VirtioFs
->Virtio
->GetQueueNumMax (VirtioFs
->Virtio
,
192 &VirtioFs
->QueueSize
);
193 if (EFI_ERROR (Status
)) {
196 if (VirtioFs
->QueueSize
< 2) {
197 Status
= EFI_UNSUPPORTED
;
202 // 7.d. [...] population of virtqueues [...]
204 Status
= VirtioRingInit (VirtioFs
->Virtio
, VirtioFs
->QueueSize
,
206 if (EFI_ERROR (Status
)) {
210 Status
= VirtioRingMap (VirtioFs
->Virtio
, &VirtioFs
->Ring
, &RingBaseShift
,
212 if (EFI_ERROR (Status
)) {
216 Status
= VirtioFs
->Virtio
->SetQueueAddress (VirtioFs
->Virtio
,
217 &VirtioFs
->Ring
, RingBaseShift
);
218 if (EFI_ERROR (Status
)) {
223 // 8. Set the DRIVER_OK status bit.
225 NextDevStat
|= VSTAT_DRIVER_OK
;
226 Status
= VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
227 if (EFI_ERROR (Status
)) {
234 VirtioFs
->Virtio
->UnmapSharedBuffer (VirtioFs
->Virtio
, VirtioFs
->RingMap
);
237 VirtioRingUninit (VirtioFs
->Virtio
, &VirtioFs
->Ring
);
241 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
242 // FAILED status bit to indicate that it has given up on the device (it can
243 // reset the device later to restart if desired). [...]
245 // Virtio access failure here should not mask the original error.
247 NextDevStat
|= VSTAT_FAILED
;
248 VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
254 De-configure the Virtio Filesystem device underlying VirtioFs.
256 @param[in] VirtioFs The VIRTIO_FS object for which Virtio communication
257 should be torn down. On input, the caller is responsible
258 for having called VirtioFsInit(). On output, Virtio
259 Filesystem commands (primitives) must no longer be
260 submitted to the device.
264 IN OUT VIRTIO_FS
*VirtioFs
268 // Resetting the Virtio device makes it release its resources and forget its
271 VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, 0);
272 VirtioFs
->Virtio
->UnmapSharedBuffer (VirtioFs
->Virtio
, VirtioFs
->RingMap
);
273 VirtioRingUninit (VirtioFs
->Virtio
, &VirtioFs
->Ring
);
277 ExitBootServices event notification function for a Virtio Filesystem object.
279 This function resets the VIRTIO_FS.Virtio device, causing it to release all
280 references to guest-side resources. The function may only be called after
281 VirtioFsInit() returns successfully and before VirtioFsUninit() is called.
283 @param[in] ExitBootEvent The VIRTIO_FS.ExitBoot event that has been
286 @param[in] VirtioFsAsVoid Pointer to the VIRTIO_FS object, passed in as
292 IN EFI_EVENT ExitBootEvent
,
293 IN VOID
*VirtioFsAsVoid
298 VirtioFs
= VirtioFsAsVoid
;
299 DEBUG ((DEBUG_VERBOSE
, "%a: VirtioFs=0x%p Label=\"%s\"\n", __FUNCTION__
,
300 VirtioFsAsVoid
, VirtioFs
->Label
));
301 VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, 0);
305 Validate two VIRTIO_FS_SCATTER_GATHER_LIST objects -- list of request
306 buffers, list of response buffers -- together.
308 On input, the caller is required to populate the following fields:
309 - VIRTIO_FS_IO_VECTOR.Buffer,
310 - VIRTIO_FS_IO_VECTOR.Size,
311 - VIRTIO_FS_SCATTER_GATHER_LIST.IoVec,
312 - VIRTIO_FS_SCATTER_GATHER_LIST.NumVec.
314 On output (on successful return), the following fields will be
316 - VIRTIO_FS_IO_VECTOR.Mapped,
317 - VIRTIO_FS_IO_VECTOR.MappedAddress,
318 - VIRTIO_FS_IO_VECTOR.Mapping,
319 - VIRTIO_FS_IO_VECTOR.Transferred.
321 On output (on successful return), the following fields will be calculated:
322 - VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize.
324 The function may only be called after VirtioFsInit() returns successfully and
325 before VirtioFsUninit() is called.
327 @param[in] VirtioFs The Virtio Filesystem device that the
328 request-response exchange, expressed via
329 RequestSgList and ResponseSgList, will be
332 @param[in,out] RequestSgList The scatter-gather list that describes the
333 request part of the exchange -- the buffers
334 that should be sent to the Virtio Filesystem
335 device in the virtio transfer.
337 @param[in,out] ResponseSgList The scatter-gather list that describes the
338 response part of the exchange -- the buffers
339 that the Virtio Filesystem device should
340 populate in the virtio transfer. May be NULL
341 if the exchange with the Virtio Filesystem
342 device consists of a request only, with the
343 response part omitted altogether.
345 @retval EFI_SUCCESS RequestSgList and ResponseSgList have been
346 validated, output fields have been set.
348 @retval EFI_INVALID_PARAMETER RequestSgList is NULL.
350 @retval EFI_INVALID_PARAMETER On input, a
351 VIRTIO_FS_SCATTER_GATHER_LIST.IoVec field is
353 VIRTIO_FS_SCATTER_GATHER_LIST.NumVec field is
356 @retval EFI_INVALID_PARAMETER On input, a VIRTIO_FS_IO_VECTOR.Buffer field
357 is NULL, or a VIRTIO_FS_IO_VECTOR.Size field
360 @retval EFI_UNSUPPORTED (RequestSgList->NumVec +
361 ResponseSgList->NumVec) exceeds
362 VirtioFs->QueueSize, meaning that the total
363 list of buffers cannot be placed on the virtio
364 queue in a single descriptor chain (with one
365 descriptor per buffer).
367 @retval EFI_UNSUPPORTED One of the
368 VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize fields
369 would exceed MAX_UINT32.
372 VirtioFsSgListsValidate (
373 IN VIRTIO_FS
*VirtioFs
,
374 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST
*RequestSgList
,
375 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST
*ResponseSgList OPTIONAL
378 VIRTIO_FS_SCATTER_GATHER_LIST
*SgListParam
[2];
379 UINT16 DescriptorsNeeded
;
382 if (RequestSgList
== NULL
) {
383 return EFI_INVALID_PARAMETER
;
386 SgListParam
[0] = RequestSgList
;
387 SgListParam
[1] = ResponseSgList
;
389 DescriptorsNeeded
= 0;
390 for (ListId
= 0; ListId
< ARRAY_SIZE (SgListParam
); ListId
++) {
391 VIRTIO_FS_SCATTER_GATHER_LIST
*SgList
;
392 UINT32 SgListTotalSize
;
395 SgList
= SgListParam
[ListId
];
396 if (SgList
== NULL
) {
400 // Sanity-check SgList -- it must provide at least one IO Vector.
402 if (SgList
->IoVec
== NULL
|| SgList
->NumVec
== 0) {
403 return EFI_INVALID_PARAMETER
;
406 // Make sure that, for each IO Vector in this SgList, a virtio descriptor
407 // can be added to the virtio queue, after the other descriptors added
410 if (SgList
->NumVec
> (UINTN
)(MAX_UINT16
- DescriptorsNeeded
) ||
411 DescriptorsNeeded
+ SgList
->NumVec
> VirtioFs
->QueueSize
) {
412 return EFI_UNSUPPORTED
;
414 DescriptorsNeeded
+= (UINT16
)SgList
->NumVec
;
417 for (IoVecIdx
= 0; IoVecIdx
< SgList
->NumVec
; IoVecIdx
++) {
418 VIRTIO_FS_IO_VECTOR
*IoVec
;
420 IoVec
= &SgList
->IoVec
[IoVecIdx
];
422 // Sanity-check this IoVec -- it must describe a non-empty buffer.
424 if (IoVec
->Buffer
== NULL
|| IoVec
->Size
== 0) {
425 return EFI_INVALID_PARAMETER
;
428 // Make sure the cumulative size of all IO Vectors in this SgList remains
429 // expressible as a UINT32.
431 if (IoVec
->Size
> MAX_UINT32
- SgListTotalSize
) {
432 return EFI_UNSUPPORTED
;
434 SgListTotalSize
+= (UINT32
)IoVec
->Size
;
437 // Initialize those fields in this IO Vector that will be updated in
438 // relation to mapping / transfer.
440 IoVec
->Mapped
= FALSE
;
441 IoVec
->MappedAddress
= 0;
442 IoVec
->Mapping
= NULL
;
443 IoVec
->Transferred
= 0;
447 // Store the cumulative size of all IO Vectors that we have calculated in
450 SgList
->TotalSize
= SgListTotalSize
;
457 Submit a validated pair of (request buffer list, response buffer list) to the
458 Virtio Filesystem device.
460 On input, the pair of VIRTIO_FS_SCATTER_GATHER_LIST objects must have been
461 validated together, using the VirtioFsSgListsValidate() function.
463 On output (on successful return), the following fields will be re-initialized
464 to zero (after temporarily setting them to different values):
465 - VIRTIO_FS_IO_VECTOR.Mapped,
466 - VIRTIO_FS_IO_VECTOR.MappedAddress,
467 - VIRTIO_FS_IO_VECTOR.Mapping.
469 On output (on successful return), the following fields will be calculated:
470 - VIRTIO_FS_IO_VECTOR.Transferred.
472 The function may only be called after VirtioFsInit() returns successfully and
473 before VirtioFsUninit() is called.
475 @param[in,out] VirtioFs The Virtio Filesystem device that the
476 request-response exchange, expressed via
477 RequestSgList and ResponseSgList, should now
480 @param[in,out] RequestSgList The scatter-gather list that describes the
481 request part of the exchange -- the buffers
482 that should be sent to the Virtio Filesystem
483 device in the virtio transfer.
485 @param[in,out] ResponseSgList The scatter-gather list that describes the
486 response part of the exchange -- the buffers
487 that the Virtio Filesystem device should
488 populate in the virtio transfer. May be NULL
489 if and only if NULL was passed to
490 VirtioFsSgListsValidate() as ResponseSgList.
492 @retval EFI_SUCCESS Transfer complete. The caller should investigate
493 the VIRTIO_FS_IO_VECTOR.Transferred fields in
494 ResponseSgList, to ensure coverage of the relevant
495 response buffers. Subsequently, the caller should
496 investigate the contents of those buffers.
498 @retval EFI_DEVICE_ERROR The Virtio Filesystem device reported populating
499 more response bytes than ResponseSgList->TotalSize.
501 @return Error codes propagated from
502 VirtioMapAllBytesInSharedBuffer(), VirtioFlush(),
503 or VirtioFs->Virtio->UnmapSharedBuffer().
506 VirtioFsSgListsSubmit (
507 IN OUT VIRTIO_FS
*VirtioFs
,
508 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST
*RequestSgList
,
509 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST
*ResponseSgList OPTIONAL
512 VIRTIO_FS_SCATTER_GATHER_LIST
*SgListParam
[2];
513 VIRTIO_MAP_OPERATION SgListVirtioMapOp
[ARRAY_SIZE (SgListParam
)];
514 UINT16 SgListDescriptorFlag
[ARRAY_SIZE (SgListParam
)];
516 VIRTIO_FS_SCATTER_GATHER_LIST
*SgList
;
518 VIRTIO_FS_IO_VECTOR
*IoVec
;
520 DESC_INDICES Indices
;
521 UINT32 TotalBytesWrittenByDevice
;
522 UINT32 BytesPermittedForWrite
;
524 SgListParam
[0] = RequestSgList
;
525 SgListVirtioMapOp
[0] = VirtioOperationBusMasterRead
;
526 SgListDescriptorFlag
[0] = 0;
528 SgListParam
[1] = ResponseSgList
;
529 SgListVirtioMapOp
[1] = VirtioOperationBusMasterWrite
;
530 SgListDescriptorFlag
[1] = VRING_DESC_F_WRITE
;
533 // Map all IO Vectors.
535 for (ListId
= 0; ListId
< ARRAY_SIZE (SgListParam
); ListId
++) {
536 SgList
= SgListParam
[ListId
];
537 if (SgList
== NULL
) {
540 for (IoVecIdx
= 0; IoVecIdx
< SgList
->NumVec
; IoVecIdx
++) {
541 IoVec
= &SgList
->IoVec
[IoVecIdx
];
543 // Map this IO Vector.
545 Status
= VirtioMapAllBytesInSharedBuffer (
547 SgListVirtioMapOp
[ListId
],
550 &IoVec
->MappedAddress
,
553 if (EFI_ERROR (Status
)) {
556 IoVec
->Mapped
= TRUE
;
561 // Compose the descriptor chain.
563 VirtioPrepare (&VirtioFs
->Ring
, &Indices
);
564 for (ListId
= 0; ListId
< ARRAY_SIZE (SgListParam
); ListId
++) {
565 SgList
= SgListParam
[ListId
];
566 if (SgList
== NULL
) {
569 for (IoVecIdx
= 0; IoVecIdx
< SgList
->NumVec
; IoVecIdx
++) {
572 IoVec
= &SgList
->IoVec
[IoVecIdx
];
574 // Set VRING_DESC_F_NEXT on all except the very last descriptor.
576 NextFlag
= VRING_DESC_F_NEXT
;
577 if (ListId
== ARRAY_SIZE (SgListParam
) - 1 &&
578 IoVecIdx
== SgList
->NumVec
- 1) {
583 IoVec
->MappedAddress
,
585 SgListDescriptorFlag
[ListId
] | NextFlag
,
592 // Submit the descriptor chain.
594 Status
= VirtioFlush (VirtioFs
->Virtio
, VIRTIO_FS_REQUEST_QUEUE
,
595 &VirtioFs
->Ring
, &Indices
, &TotalBytesWrittenByDevice
);
596 if (EFI_ERROR (Status
)) {
601 // Sanity-check: the Virtio Filesystem device should not have written more
602 // bytes than what we offered buffers for.
604 if (ResponseSgList
== NULL
) {
605 BytesPermittedForWrite
= 0;
607 BytesPermittedForWrite
= ResponseSgList
->TotalSize
;
609 if (TotalBytesWrittenByDevice
> BytesPermittedForWrite
) {
610 Status
= EFI_DEVICE_ERROR
;
615 // Update the transfer sizes in the IO Vectors.
617 for (ListId
= 0; ListId
< ARRAY_SIZE (SgListParam
); ListId
++) {
618 SgList
= SgListParam
[ListId
];
619 if (SgList
== NULL
) {
622 for (IoVecIdx
= 0; IoVecIdx
< SgList
->NumVec
; IoVecIdx
++) {
623 IoVec
= &SgList
->IoVec
[IoVecIdx
];
624 if (SgListVirtioMapOp
[ListId
] == VirtioOperationBusMasterRead
) {
626 // We report that the Virtio Filesystem device has read all buffers in
629 IoVec
->Transferred
= IoVec
->Size
;
632 // Regarding the response, calculate how much of the current IO Vector
633 // has been populated by the Virtio Filesystem device. In
634 // "TotalBytesWrittenByDevice", VirtioFlush() reported the total count
635 // across all device-writeable descriptors, in the order they were
636 // chained on the ring.
638 IoVec
->Transferred
= MIN ((UINTN
)TotalBytesWrittenByDevice
,
640 TotalBytesWrittenByDevice
-= (UINT32
)IoVec
->Transferred
;
646 // By now, "TotalBytesWrittenByDevice" has been exhausted.
648 ASSERT (TotalBytesWrittenByDevice
== 0);
651 // We've succeeded; fall through.
655 // Unmap all mapped IO Vectors on both the success and the error paths. The
656 // unmapping occurs in reverse order of mapping, in an attempt to avoid
657 // memory fragmentation.
659 ListId
= ARRAY_SIZE (SgListParam
);
662 SgList
= SgListParam
[ListId
];
663 if (SgList
== NULL
) {
666 IoVecIdx
= SgList
->NumVec
;
667 while (IoVecIdx
> 0) {
668 EFI_STATUS UnmapStatus
;
671 IoVec
= &SgList
->IoVec
[IoVecIdx
];
673 // Unmap this IO Vector, if it has been mapped.
675 if (!IoVec
->Mapped
) {
678 UnmapStatus
= VirtioFs
->Virtio
->UnmapSharedBuffer (VirtioFs
->Virtio
,
681 // Re-set the following fields to the values they initially got from
682 // VirtioFsSgListsValidate() -- the above unmapping attempt is considered
683 // final, even if it fails.
685 IoVec
->Mapped
= FALSE
;
686 IoVec
->MappedAddress
= 0;
687 IoVec
->Mapping
= NULL
;
690 // If we are on the success path, but the unmapping failed, we need to
691 // transparently flip to the failure path -- the caller must learn they
692 // should not consult the response buffers.
694 // The branch below can be taken at most once.
696 if (!EFI_ERROR (Status
) && EFI_ERROR (UnmapStatus
)) {
697 Status
= UnmapStatus
;
706 Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
708 The function may only be called after VirtioFsInit() returns successfully and
709 before VirtioFsUninit() is called.
711 @param[in,out] VirtioFs The Virtio Filesystem device that the request is
712 being prepared for. The "VirtioFs->RequestId" field
713 will be copied into "Request->Unique". On output (on
714 successful return), "VirtioFs->RequestId" will be
717 @param[out] Request The VIRTIO_FS_FUSE_REQUEST object whose fields are to
720 @param[in] RequestSize The total size of the request, including
721 sizeof(VIRTIO_FS_FUSE_REQUEST).
723 @param[in] Opcode The VIRTIO_FS_FUSE_OPCODE that identifies the command
726 @param[in] NodeId The inode number of the file that the request refers
727 to. When Opcode is VirtioFsFuseOpInit, NodeId is
728 ignored by the Virtio Filesystem device.
730 @retval EFI_INVALID_PARAMETER RequestSize is smaller than
731 sizeof(VIRTIO_FS_FUSE_REQUEST).
733 @retval EFI_OUT_OF_RESOURCES "VirtioFs->RequestId" is MAX_UINT64, and can
734 be incremented no more.
736 @retval EFI_SUCCESS Request has been populated,
737 "VirtioFs->RequestId" has been incremented.
740 VirtioFsFuseNewRequest (
741 IN OUT VIRTIO_FS
*VirtioFs
,
742 OUT VIRTIO_FS_FUSE_REQUEST
*Request
,
743 IN UINT32 RequestSize
,
744 IN VIRTIO_FS_FUSE_OPCODE Opcode
,
748 if (RequestSize
< sizeof *Request
) {
749 return EFI_INVALID_PARAMETER
;
752 if (VirtioFs
->RequestId
== MAX_UINT64
) {
753 return EFI_OUT_OF_RESOURCES
;
756 Request
->Len
= RequestSize
;
757 Request
->Opcode
= Opcode
;
758 Request
->Unique
= VirtioFs
->RequestId
++;
759 Request
->NodeId
= NodeId
;
763 Request
->Padding
= 0;
769 Check the common FUSE response format.
771 The first buffer in the response scatter-gather list is assumed a
772 VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to
773 and excluding the last one, are assumed fixed size. The last response buffer
774 may or may not be fixed size, as specified by the caller.
776 This function may only be called after VirtioFsSgListsSubmit() returns
779 @param[in] ResponseSgList The scatter-gather list that describes the
780 response part of the exchange -- the buffers that
781 the Virtio Filesystem device filled in during the
784 @param[in] RequestId The request identifier to which the response is
787 @param[out] TailBufferFill If NULL, then the last buffer in ResponseSgList
788 is considered fixed size. Otherwise, the last
789 buffer is considered variable size, and on
790 successful return, TailBufferFill reports the
791 number of bytes in the last buffer.
793 @retval EFI_INVALID_PARAMETER TailBufferFill is not NULL (i.e., the last
794 buffer is considered variable size), and
795 ResponseSgList->NumVec is 1.
797 @retval EFI_INVALID_PARAMETER The allocated size of the first buffer does
798 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
800 @retval EFI_PROTOCOL_ERROR The VIRTIO_FS_FUSE_RESPONSE structure in the
801 first buffer has not been fully populated.
803 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Len" in the first
804 buffer does not equal the sum of the
805 individual buffer sizes (as populated).
807 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
808 buffer does not equal RequestId.
810 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
811 buffer is zero, but a subsequent fixed size
812 buffer has not been fully populated.
814 @retval EFI_DEVICE_ERROR "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
815 buffer is nonzero. The caller may investigate
816 "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the
817 completeness of the subsequent fixed size
818 buffers is not verified in this case.
820 @retval EFI_SUCCESS Verification successful.
823 VirtioFsFuseCheckResponse (
824 IN VIRTIO_FS_SCATTER_GATHER_LIST
*ResponseSgList
,
826 OUT UINTN
*TailBufferFill
829 UINTN NumFixedSizeVec
;
830 VIRTIO_FS_FUSE_RESPONSE
*CommonResp
;
831 UINT32 TotalTransferred
;
835 // Ensured by VirtioFsSgListsValidate().
837 ASSERT (ResponseSgList
->NumVec
> 0);
839 if (TailBufferFill
== NULL
) {
841 // All buffers are considered fixed size.
843 NumFixedSizeVec
= ResponseSgList
->NumVec
;
846 // If the last buffer is variable size, then we need that buffer to be
847 // different from the first buffer, which is considered a
848 // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.
850 if (ResponseSgList
->NumVec
== 1) {
851 return EFI_INVALID_PARAMETER
;
853 NumFixedSizeVec
= ResponseSgList
->NumVec
- 1;
857 // The first buffer is supposed to carry a (fully populated)
858 // VIRTIO_FS_FUSE_RESPONSE structure.
860 if (ResponseSgList
->IoVec
[0].Size
!= sizeof *CommonResp
) {
861 return EFI_INVALID_PARAMETER
;
863 if (ResponseSgList
->IoVec
[0].Transferred
!= ResponseSgList
->IoVec
[0].Size
) {
864 return EFI_PROTOCOL_ERROR
;
868 // FUSE must report the same number of bytes, written by the Virtio
869 // Filesystem device, as the virtio transport does.
871 CommonResp
= ResponseSgList
->IoVec
[0].Buffer
;
872 TotalTransferred
= 0;
873 for (Idx
= 0; Idx
< ResponseSgList
->NumVec
; Idx
++) {
875 // Integer overflow and truncation are not possible, based on
876 // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
878 TotalTransferred
+= (UINT32
)ResponseSgList
->IoVec
[Idx
].Transferred
;
880 if (CommonResp
->Len
!= TotalTransferred
) {
881 return EFI_PROTOCOL_ERROR
;
885 // Enforce that FUSE match our request ID in the response.
887 if (CommonResp
->Unique
!= RequestId
) {
888 return EFI_PROTOCOL_ERROR
;
892 // If there is an explicit error report, skip checking the transfer
893 // counts for the rest of the fixed size buffers.
895 if (CommonResp
->Error
!= 0) {
896 return EFI_DEVICE_ERROR
;
900 // There was no error reported, so we require that the Virtio Filesystem
901 // device populate all fixed size buffers. We checked this for the very first
902 // buffer above; let's check the rest (if any).
904 ASSERT (NumFixedSizeVec
>= 1);
905 for (Idx
= 1; Idx
< NumFixedSizeVec
; Idx
++) {
906 if (ResponseSgList
->IoVec
[Idx
].Transferred
!=
907 ResponseSgList
->IoVec
[Idx
].Size
) {
908 return EFI_PROTOCOL_ERROR
;
913 // If the last buffer is considered variable size, report its filled size.
915 if (TailBufferFill
!= NULL
) {
916 *TailBufferFill
= ResponseSgList
->IoVec
[NumFixedSizeVec
].Transferred
;
923 An ad-hoc function for mapping FUSE (well, Linux) "errno" values to
926 @param[in] Errno The "VIRTIO_FS_FUSE_RESPONSE.Error" value, returned by the
927 Virtio Filesystem device. The value is expected to be
930 @return An EFI_STATUS error code that's deemed a passable
931 mapping for the Errno value.
933 @retval EFI_DEVICE_ERROR Fallback EFI_STATUS code for unrecognized Errno
937 VirtioFsErrnoToEfiStatus (
942 case -1: // EPERM Operation not permitted
943 return EFI_SECURITY_VIOLATION
;
945 case -2: // ENOENT No such file or directory
946 case -3: // ESRCH No such process
947 case -6: // ENXIO No such device or address
948 case -10: // ECHILD No child processes
949 case -19: // ENODEV No such device
950 case -49: // EUNATCH Protocol driver not attached
951 case -65: // ENOPKG Package not installed
952 case -79: // ELIBACC Can not access a needed shared library
953 case -126: // ENOKEY Required key not available
954 return EFI_NOT_FOUND
;
956 case -4: // EINTR Interrupted system call
957 case -11: // EAGAIN, EWOULDBLOCK Resource temporarily unavailable
958 case -16: // EBUSY Device or resource busy
959 case -26: // ETXTBSY Text file busy
960 case -35: // EDEADLK, EDEADLOCK Resource deadlock avoided
961 case -39: // ENOTEMPTY Directory not empty
962 case -42: // ENOMSG No message of desired type
963 case -61: // ENODATA No data available
964 case -85: // ERESTART Interrupted system call should be restarted
965 return EFI_NOT_READY
;
967 case -5: // EIO Input/output error
968 case -45: // EL2NSYNC Level 2 not synchronized
969 case -46: // EL3HLT Level 3 halted
970 case -47: // EL3RST Level 3 reset
971 case -51: // EL2HLT Level 2 halted
972 case -121: // EREMOTEIO Remote I/O error
973 case -133: // EHWPOISON Memory page has hardware error
974 return EFI_DEVICE_ERROR
;
976 case -7: // E2BIG Argument list too long
977 case -36: // ENAMETOOLONG File name too long
978 case -90: // EMSGSIZE Message too long
979 return EFI_BAD_BUFFER_SIZE
;
981 case -8: // ENOEXEC Exec format error
982 case -15: // ENOTBLK Block device required
983 case -18: // EXDEV Invalid cross-device link
984 case -20: // ENOTDIR Not a directory
985 case -21: // EISDIR Is a directory
986 case -25: // ENOTTY Inappropriate ioctl for device
987 case -27: // EFBIG File too large
988 case -29: // ESPIPE Illegal seek
989 case -38: // ENOSYS Function not implemented
990 case -59: // EBFONT Bad font file format
991 case -60: // ENOSTR Device not a stream
992 case -83: // ELIBEXEC Cannot exec a shared library directly
993 case -88: // ENOTSOCK Socket operation on non-socket
994 case -91: // EPROTOTYPE Protocol wrong type for socket
995 case -92: // ENOPROTOOPT Protocol not available
996 case -93: // EPROTONOSUPPORT Protocol not supported
997 case -94: // ESOCKTNOSUPPORT Socket type not supported
998 case -95: // ENOTSUP, EOPNOTSUPP Operation not supported
999 case -96: // EPFNOSUPPORT Protocol family not supported
1000 case -97: // EAFNOSUPPORT Address family not supported by protocol
1001 case -99: // EADDRNOTAVAIL Cannot assign requested address
1002 case -118: // ENOTNAM Not a XENIX named type file
1003 case -120: // EISNAM Is a named type file
1004 case -124: // EMEDIUMTYPE Wrong medium type
1005 return EFI_UNSUPPORTED
;
1007 case -9: // EBADF Bad file descriptor
1008 case -14: // EFAULT Bad address
1009 case -44: // ECHRNG Channel number out of range
1010 case -48: // ELNRNG Link number out of range
1011 case -53: // EBADR Invalid request descriptor
1012 case -56: // EBADRQC Invalid request code
1013 case -57: // EBADSLT Invalid slot
1014 case -76: // ENOTUNIQ Name not unique on network
1015 case -84: // EILSEQ Invalid or incomplete multibyte or wide character
1016 return EFI_NO_MAPPING
;
1018 case -12: // ENOMEM Cannot allocate memory
1019 case -23: // ENFILE Too many open files in system
1020 case -24: // EMFILE Too many open files
1021 case -31: // EMLINK Too many links
1022 case -37: // ENOLCK No locks available
1023 case -40: // ELOOP Too many levels of symbolic links
1024 case -50: // ENOCSI No CSI structure available
1025 case -55: // ENOANO No anode
1026 case -63: // ENOSR Out of streams resources
1027 case -82: // ELIBMAX Attempting to link in too many shared libraries
1028 case -87: // EUSERS Too many users
1029 case -105: // ENOBUFS No buffer space available
1030 case -109: // ETOOMANYREFS Too many references: cannot splice
1031 case -119: // ENAVAIL No XENIX semaphores available
1032 case -122: // EDQUOT Disk quota exceeded
1033 return EFI_OUT_OF_RESOURCES
;
1035 case -13: // EACCES Permission denied
1036 return EFI_ACCESS_DENIED
;
1038 case -17: // EEXIST File exists
1039 case -98: // EADDRINUSE Address already in use
1040 case -106: // EISCONN Transport endpoint is already connected
1041 case -114: // EALREADY Operation already in progress
1042 case -115: // EINPROGRESS Operation now in progress
1043 return EFI_ALREADY_STARTED
;
1045 case -22: // EINVAL Invalid argument
1046 case -33: // EDOM Numerical argument out of domain
1047 return EFI_INVALID_PARAMETER
;
1049 case -28: // ENOSPC No space left on device
1050 case -54: // EXFULL Exchange full
1051 return EFI_VOLUME_FULL
;
1053 case -30: // EROFS Read-only file system
1054 return EFI_WRITE_PROTECTED
;
1056 case -32: // EPIPE Broken pipe
1057 case -43: // EIDRM Identifier removed
1058 case -67: // ENOLINK Link has been severed
1059 case -68: // EADV Advertise error
1060 case -69: // ESRMNT Srmount error
1061 case -70: // ECOMM Communication error on send
1062 case -73: // EDOTDOT RFS specific error
1063 case -78: // EREMCHG Remote address changed
1064 case -86: // ESTRPIPE Streams pipe error
1065 case -102: // ENETRESET Network dropped connection on reset
1066 case -103: // ECONNABORTED Software caused connection abort
1067 case -104: // ECONNRESET Connection reset by peer
1068 case -116: // ESTALE Stale file handle
1069 case -125: // ECANCELED Operation canceled
1070 case -128: // EKEYREVOKED Key has been revoked
1071 case -129: // EKEYREJECTED Key was rejected by service
1072 case -130: // EOWNERDEAD Owner died
1073 case -131: // ENOTRECOVERABLE State not recoverable
1076 case -34: // ERANGE Numerical result out of range
1077 case -75: // EOVERFLOW Value too large for defined data type
1078 return EFI_BUFFER_TOO_SMALL
;
1080 case -52: // EBADE Invalid exchange
1081 case -108: // ESHUTDOWN Cannot send after transport endpoint shutdown
1082 case -111: // ECONNREFUSED Connection refused
1083 return EFI_END_OF_FILE
;
1085 case -62: // ETIME Timer expired
1086 case -110: // ETIMEDOUT Connection timed out
1087 case -127: // EKEYEXPIRED Key has expired
1090 case -64: // ENONET Machine is not on the network
1091 case -66: // EREMOTE Object is remote
1092 case -72: // EMULTIHOP Multihop attempted
1093 case -100: // ENETDOWN Network is down
1094 case -101: // ENETUNREACH Network is unreachable
1095 case -112: // EHOSTDOWN Host is down
1096 case -113: // EHOSTUNREACH No route to host
1097 case -123: // ENOMEDIUM No medium found
1098 case -132: // ERFKILL Operation not possible due to RF-kill
1099 return EFI_NO_MEDIA
;
1101 case -71: // EPROTO Protocol error
1102 return EFI_PROTOCOL_ERROR
;
1104 case -74: // EBADMSG Bad message
1105 case -77: // EBADFD File descriptor in bad state
1106 case -80: // ELIBBAD Accessing a corrupted shared library
1107 case -81: // ELIBSCN .lib section in a.out corrupted
1108 case -117: // EUCLEAN Structure needs cleaning
1109 return EFI_VOLUME_CORRUPTED
;
1111 case -89: // EDESTADDRREQ Destination address required
1112 case -107: // ENOTCONN Transport endpoint is not connected
1113 return EFI_NOT_STARTED
;
1119 return EFI_DEVICE_ERROR
;
1123 // Parser states for canonicalizing a POSIX pathname.
1126 ParserInit
, // just starting
1127 ParserEnd
, // finished
1128 ParserSlash
, // slash(es) seen
1129 ParserDot
, // one dot seen since last slash
1130 ParserDotDot
, // two dots seen since last slash
1131 ParserNormal
, // a different sequence seen
1135 Strip the trailing slash from the parser's output buffer, unless the trailing
1136 slash stands for the root directory.
1138 @param[in] Buffer The parser's output buffer. Only used for
1141 @param[in,out] Position On entry, points at the next character to produce
1142 (i.e., right past the end of the output written by
1143 the parser thus far). The last character in the
1144 parser's output buffer is a slash. On return, the
1145 slash is stripped, by decrementing Position by one.
1146 If this action would remove the slash character
1147 standing for the root directory, then the function
1154 IN OUT UINTN
*Position
1157 ASSERT (*Position
>= 1);
1158 ASSERT (Buffer
[*Position
- 1] == '/');
1159 if (*Position
== 1) {
1166 Produce one character in the parser's output buffer.
1168 @param[out] Buffer The parser's output buffer. On return, Char8 will
1171 @param[in,out] Position On entry, points at the next character to produce
1172 (i.e., right past the end of the output written by
1173 the parser thus far). On return, Position is
1176 @param[in] Size Total allocated size of the parser's output buffer.
1177 Used for sanity-checking.
1179 @param[in] Char8 The character to place in the output buffer.
1185 IN OUT UINTN
*Position
,
1190 ASSERT (*Position
< Size
);
1191 Buffer
[(*Position
)++] = Char8
;
1195 Rewind the last single-dot in the parser's output buffer.
1197 @param[in] Buffer The parser's output buffer. Only used for
1200 @param[in,out] Position On entry, points at the next character to produce
1201 (i.e., right past the end of the output written by
1202 the parser thus far); the parser's output buffer
1203 ends with the characters ('/', '.'). On return, the
1204 dot is rewound by decrementing Position by one; a
1205 slash character will reside at the new end of the
1206 parser's output buffer.
1212 IN OUT UINTN
*Position
1215 ASSERT (*Position
>= 2);
1216 ASSERT (Buffer
[*Position
- 1] == '.');
1217 ASSERT (Buffer
[*Position
- 2] == '/');
1222 Rewind the last dot-dot in the parser's output buffer.
1224 @param[in] Buffer The parser's output buffer. Only used for
1227 @param[in,out] Position On entry, points at the next character to produce
1228 (i.e., right past the end of the output written by
1229 the parser thus far); the parser's output buffer
1230 ends with the characters ('/', '.', '.'). On return,
1231 the ('.', '.') pair is rewound unconditionally, by
1232 decrementing Position by two; a slash character
1233 resides at the new end of the parser's output
1236 If this slash character stands for the root
1237 directory, then RootEscape is set to TRUE.
1239 Otherwise (i.e., if this slash character is not the
1240 one standing for the root directory), then the slash
1241 character, and the pathname component preceding it,
1242 are removed by decrementing Position further. In
1243 this case, the slash character preceding the removed
1244 pathname component will reside at the new end of the
1245 parser's output buffer.
1247 @param[out] RootEscape Set to TRUE on output if the dot-dot component tries
1248 to escape the root directory, as described above.
1249 Otherwise, RootEscape is not modified.
1253 ParserRewindDotDot (
1255 IN OUT UINTN
*Position
,
1256 OUT BOOLEAN
*RootEscape
1260 ASSERT (*Position
>= 3);
1261 ASSERT (Buffer
[*Position
- 1] == '.');
1262 ASSERT (Buffer
[*Position
- 2] == '.');
1263 ASSERT (Buffer
[*Position
- 3] == '/');
1266 if (*Position
== 1) {
1268 // Root directory slash reached; don't try to climb higher.
1279 // Scan until next slash to the left.
1282 ASSERT (*Position
> 0);
1284 } while (Buffer
[*Position
] != '/');
1289 Append the UEFI-style RhsPath16 to the POSIX-style, canonical format
1290 LhsPath8. Output the POSIX-style, canonical format result in ResultPath, as a
1291 dynamically allocated string.
1293 Canonicalization (aka sanitization) establishes the following properties:
1294 - ResultPath is absolute (starts with "/"),
1295 - dot (.) and dot-dot (..) components are resolved/eliminated in ResultPath,
1296 with the usual semantics,
1297 - ResultPath uses forward slashes,
1298 - sequences of slashes are squashed in ResultPath,
1299 - the printable ASCII character set covers ResultPath,
1300 - CHAR8 encoding is used in ResultPath,
1301 - no trailing slash present in ResultPath except for the standalone root
1303 - the length of ResultPath is at most VIRTIO_FS_MAX_PATHNAME_LENGTH.
1305 Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
1306 reported through RootEscape, without failing the function call.
1308 @param[in] LhsPath8 Identifies the base directory. The caller is
1309 responsible for ensuring that LhsPath8 conform to
1310 the above canonical pathname format on entry.
1312 @param[in] RhsPath16 Identifies the desired file with a UEFI-style CHAR16
1313 pathname. If RhsPath16 starts with a backslash, then
1314 RhsPath16 is considered absolute, and LhsPath8 is
1315 ignored; RhsPath16 is sanitized in isolation, for
1316 producing ResultPath8. Otherwise (i.e., if RhsPath16
1317 is relative), RhsPath16 is transliterated to CHAR8,
1318 and naively appended to LhsPath8. The resultant
1319 fused pathname is then sanitized, to produce
1322 @param[out] ResultPath8 The POSIX-style, canonical format pathname that
1323 leads to the file desired by the caller. After use,
1324 the caller is responsible for freeing ResultPath8.
1326 @param[out] RootEscape Set to TRUE if at least one dot-dot component in
1327 RhsPath16 attempted to escape the root directory;
1328 set to FALSE otherwise.
1330 @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
1333 @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
1335 @retval EFI_INVALID_PARAMETER RhsPath16 failed the
1336 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1338 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1340 @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
1341 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1343 @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
1344 falls outside of the printable ASCII set, or
1348 VirtioFsAppendPath (
1350 IN CHAR16
*RhsPath16
,
1351 OUT CHAR8
**ResultPath8
,
1352 OUT BOOLEAN
*RootEscape
1359 UINTN SizeToSanitize
;
1360 CHAR8
*BufferToSanitize
;
1361 CHAR8
*SanitizedBuffer
;
1363 UINTN SanitizedPosition
;
1366 // Appending an empty pathname is not allowed.
1368 RhsLen
= StrLen (RhsPath16
);
1370 return EFI_INVALID_PARAMETER
;
1373 // Enforce length restriction on RhsPath16.
1375 if (RhsLen
> VIRTIO_FS_MAX_PATHNAME_LENGTH
) {
1376 return EFI_INVALID_PARAMETER
;
1380 // Transliterate RhsPath16 to RhsPath8 by:
1381 // - rejecting RhsPath16 if a character outside of printable ASCII is seen,
1382 // - rejecting RhsPath16 if a forward slash is seen,
1383 // - replacing backslashes with forward slashes,
1384 // - casting the characters from CHAR16 to CHAR8.
1386 RhsPath8
= AllocatePool (RhsLen
+ 1);
1387 if (RhsPath8
== NULL
) {
1388 return EFI_OUT_OF_RESOURCES
;
1390 for (Idx
= 0; RhsPath16
[Idx
] != L
'\0'; Idx
++) {
1391 if (RhsPath16
[Idx
] < 0x20 || RhsPath16
[Idx
] > 0x7E ||
1392 RhsPath16
[Idx
] == L
'/') {
1393 Status
= EFI_UNSUPPORTED
;
1396 RhsPath8
[Idx
] = (CHAR8
)((RhsPath16
[Idx
] == L
'\\') ? L
'/' : RhsPath16
[Idx
]);
1398 RhsPath8
[Idx
++] = '\0';
1401 // Now prepare the input for the canonicalization (squashing of sequences of
1402 // forward slashes, and eliminating . (dot) and .. (dot-dot) pathname
1405 // The sanitized path can never be longer than the naive concatenation of the
1406 // left hand side and right hand side paths, so we'll use the catenated size
1407 // for allocating the sanitized output too.
1409 if (RhsPath8
[0] == '/') {
1411 // If the right hand side path is absolute, then it is not appended to the
1412 // left hand side path -- it *replaces* the left hand side path.
1414 SizeToSanitize
= RhsLen
+ 1;
1415 BufferToSanitize
= RhsPath8
;
1418 // If the right hand side path is relative, then it is appended (naively)
1419 // to the left hand side.
1423 LhsLen
= AsciiStrLen (LhsPath8
);
1424 SizeToSanitize
= LhsLen
+ 1 + RhsLen
+ 1;
1425 BufferToSanitize
= AllocatePool (SizeToSanitize
);
1426 if (BufferToSanitize
== NULL
) {
1427 Status
= EFI_OUT_OF_RESOURCES
;
1430 CopyMem (BufferToSanitize
, LhsPath8
, LhsLen
);
1431 BufferToSanitize
[LhsLen
] = '/';
1432 CopyMem (BufferToSanitize
+ LhsLen
+ 1, RhsPath8
, RhsLen
+ 1);
1436 // Allocate the output buffer.
1438 SanitizedBuffer
= AllocatePool (SizeToSanitize
);
1439 if (SanitizedBuffer
== NULL
) {
1440 Status
= EFI_OUT_OF_RESOURCES
;
1441 goto FreeBufferToSanitize
;
1445 // State machine for parsing the input and producing the canonical output
1448 *RootEscape
= FALSE
;
1451 SanitizedPosition
= 0;
1455 ASSERT (Idx
< SizeToSanitize
);
1456 Chr8
= BufferToSanitize
[Idx
++];
1459 case ParserInit
: // just starting
1460 ASSERT (Chr8
== '/');
1461 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1462 State
= ParserSlash
;
1465 case ParserSlash
: // slash(es) seen
1468 ParserStripSlash (SanitizedBuffer
, &SanitizedPosition
);
1469 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1474 // skip & stay in same state
1478 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1482 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1483 State
= ParserNormal
;
1488 case ParserDot
: // one dot seen since last slash
1491 ParserRewindDot (SanitizedBuffer
, &SanitizedPosition
);
1492 ParserStripSlash (SanitizedBuffer
, &SanitizedPosition
);
1493 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1497 ParserRewindDot (SanitizedBuffer
, &SanitizedPosition
);
1498 State
= ParserSlash
;
1501 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1502 State
= ParserDotDot
;
1505 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1506 State
= ParserNormal
;
1511 case ParserDotDot
: // two dots seen since last slash
1514 ParserRewindDotDot (SanitizedBuffer
, &SanitizedPosition
, RootEscape
);
1515 ParserStripSlash (SanitizedBuffer
, &SanitizedPosition
);
1516 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1520 ParserRewindDotDot (SanitizedBuffer
, &SanitizedPosition
, RootEscape
);
1521 State
= ParserSlash
;
1528 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1529 State
= ParserNormal
;
1534 case ParserNormal
: // a different sequence seen
1537 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1541 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1542 State
= ParserSlash
;
1550 // copy and stay in same state
1552 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1561 } while (State
!= ParserEnd
);
1564 // Ensure length invariant on ResultPath8.
1566 ASSERT (SanitizedPosition
>= 2);
1567 if (SanitizedPosition
- 1 > VIRTIO_FS_MAX_PATHNAME_LENGTH
) {
1568 Status
= EFI_OUT_OF_RESOURCES
;
1569 goto FreeSanitizedBuffer
;
1572 *ResultPath8
= SanitizedBuffer
;
1573 SanitizedBuffer
= NULL
;
1574 Status
= EFI_SUCCESS
;
1578 FreeSanitizedBuffer
:
1579 if (SanitizedBuffer
!= NULL
) {
1580 FreePool (SanitizedBuffer
);
1583 FreeBufferToSanitize
:
1584 if (RhsPath8
[0] != '/') {
1585 FreePool (BufferToSanitize
);
1589 FreePool (RhsPath8
);