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/TimeBaseLib.h> // EpochToEfiTime()
13 #include <Library/VirtioLib.h> // Virtio10WriteFeatures()
15 #include "VirtioFsDxe.h"
18 Read the Virtio Filesystem device configuration structure in full.
20 @param[in] Virtio The Virtio protocol underlying the VIRTIO_FS object.
22 @param[out] Config The fully populated VIRTIO_FS_CONFIG structure.
24 @retval EFI_SUCCESS Config has been filled in.
26 @return Error codes propagated from Virtio->ReadDevice(). The
27 contents of Config are indeterminate.
32 IN VIRTIO_DEVICE_PROTOCOL
*Virtio
,
33 OUT VIRTIO_FS_CONFIG
*Config
39 for (Idx
= 0; Idx
< VIRTIO_FS_TAG_BYTES
; Idx
++) {
40 Status
= Virtio
->ReadDevice (
42 OFFSET_OF (VIRTIO_FS_CONFIG
, Tag
[Idx
]), // FieldOffset
43 sizeof Config
->Tag
[Idx
], // FieldSize
44 sizeof Config
->Tag
[Idx
], // BufferSize
45 &Config
->Tag
[Idx
] // Buffer
47 if (EFI_ERROR (Status
)) {
52 Status
= Virtio
->ReadDevice (
54 OFFSET_OF (VIRTIO_FS_CONFIG
, NumReqQueues
), // FieldOffset
55 sizeof Config
->NumReqQueues
, // FieldSize
56 sizeof Config
->NumReqQueues
, // BufferSize
57 &Config
->NumReqQueues
// Buffer
63 Configure the Virtio Filesystem device underlying VirtioFs.
65 @param[in,out] VirtioFs The VIRTIO_FS object for which Virtio communication
66 should be set up. On input, the caller is
67 responsible for VirtioFs->Virtio having been
68 initialized. On output, synchronous Virtio
69 Filesystem commands (primitives) may be submitted to
72 @retval EFI_SUCCESS Virtio machinery has been set up.
74 @retval EFI_UNSUPPORTED The host-side configuration of the Virtio Filesystem
75 is not supported by this driver.
77 @return Error codes from underlying functions.
81 IN OUT VIRTIO_FS
*VirtioFs
87 VIRTIO_FS_CONFIG Config
;
92 // Execute virtio-v1.1-cs01-87fa6b5d8155, 3.1.1 Driver Requirements: Device
95 // 1. Reset the device.
98 Status
= VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
99 if (EFI_ERROR (Status
)) {
104 // 2. Set the ACKNOWLEDGE status bit [...]
106 NextDevStat
|= VSTAT_ACK
;
107 Status
= VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
108 if (EFI_ERROR (Status
)) {
113 // 3. Set the DRIVER status bit [...]
115 NextDevStat
|= VSTAT_DRIVER
;
116 Status
= VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
117 if (EFI_ERROR (Status
)) {
122 // 4. Read device feature bits...
124 Status
= VirtioFs
->Virtio
->GetDeviceFeatures (VirtioFs
->Virtio
, &Features
);
125 if (EFI_ERROR (Status
)) {
128 if ((Features
& VIRTIO_F_VERSION_1
) == 0) {
129 Status
= EFI_UNSUPPORTED
;
133 // No device-specific feature bits have been defined in file "virtio-fs.tex"
134 // of the virtio spec at <https://github.com/oasis-tcs/virtio-spec.git>, as
135 // of commit 87fa6b5d8155.
137 Features
&= VIRTIO_F_VERSION_1
| VIRTIO_F_IOMMU_PLATFORM
;
140 // ... and write the subset of feature bits understood by the [...] driver to
142 // 5. Set the FEATURES_OK status bit.
143 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
145 Status
= Virtio10WriteFeatures (VirtioFs
->Virtio
, Features
, &NextDevStat
);
146 if (EFI_ERROR (Status
)) {
151 // 7. Perform device-specific setup, including discovery of virtqueues for
152 // the device, [...] reading [...] the device's virtio configuration space
154 Status
= VirtioFsReadConfig (VirtioFs
->Virtio
, &Config
);
155 if (EFI_ERROR (Status
)) {
160 // 7.a. Convert the filesystem label from UTF-8 to UCS-2. Only labels with
161 // printable ASCII code points (U+0020 through U+007E) are supported.
162 // NUL-terminate at either the terminator we find, or right after the
165 for (Idx
= 0; Idx
< VIRTIO_FS_TAG_BYTES
&& Config
.Tag
[Idx
] != '\0'; Idx
++) {
166 if (Config
.Tag
[Idx
] < 0x20 || Config
.Tag
[Idx
] > 0x7E) {
167 Status
= EFI_UNSUPPORTED
;
170 VirtioFs
->Label
[Idx
] = Config
.Tag
[Idx
];
172 VirtioFs
->Label
[Idx
] = L
'\0';
175 // 7.b. We need one queue for sending normal priority requests.
177 if (Config
.NumReqQueues
< 1) {
178 Status
= EFI_UNSUPPORTED
;
183 // 7.c. Fetch and remember the number of descriptors we can place on the
184 // queue at once. We'll need two descriptors per request, as a minimum --
185 // request header, response header.
187 Status
= VirtioFs
->Virtio
->SetQueueSel (VirtioFs
->Virtio
,
188 VIRTIO_FS_REQUEST_QUEUE
);
189 if (EFI_ERROR (Status
)) {
192 Status
= VirtioFs
->Virtio
->GetQueueNumMax (VirtioFs
->Virtio
,
193 &VirtioFs
->QueueSize
);
194 if (EFI_ERROR (Status
)) {
197 if (VirtioFs
->QueueSize
< 2) {
198 Status
= EFI_UNSUPPORTED
;
203 // 7.d. [...] population of virtqueues [...]
205 Status
= VirtioRingInit (VirtioFs
->Virtio
, VirtioFs
->QueueSize
,
207 if (EFI_ERROR (Status
)) {
211 Status
= VirtioRingMap (VirtioFs
->Virtio
, &VirtioFs
->Ring
, &RingBaseShift
,
213 if (EFI_ERROR (Status
)) {
217 Status
= VirtioFs
->Virtio
->SetQueueAddress (VirtioFs
->Virtio
,
218 &VirtioFs
->Ring
, RingBaseShift
);
219 if (EFI_ERROR (Status
)) {
224 // 8. Set the DRIVER_OK status bit.
226 NextDevStat
|= VSTAT_DRIVER_OK
;
227 Status
= VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
228 if (EFI_ERROR (Status
)) {
235 VirtioFs
->Virtio
->UnmapSharedBuffer (VirtioFs
->Virtio
, VirtioFs
->RingMap
);
238 VirtioRingUninit (VirtioFs
->Virtio
, &VirtioFs
->Ring
);
242 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
243 // FAILED status bit to indicate that it has given up on the device (it can
244 // reset the device later to restart if desired). [...]
246 // Virtio access failure here should not mask the original error.
248 NextDevStat
|= VSTAT_FAILED
;
249 VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, NextDevStat
);
255 De-configure the Virtio Filesystem device underlying VirtioFs.
257 @param[in] VirtioFs The VIRTIO_FS object for which Virtio communication
258 should be torn down. On input, the caller is responsible
259 for having called VirtioFsInit(). On output, Virtio
260 Filesystem commands (primitives) must no longer be
261 submitted to the device.
265 IN OUT VIRTIO_FS
*VirtioFs
269 // Resetting the Virtio device makes it release its resources and forget its
272 VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, 0);
273 VirtioFs
->Virtio
->UnmapSharedBuffer (VirtioFs
->Virtio
, VirtioFs
->RingMap
);
274 VirtioRingUninit (VirtioFs
->Virtio
, &VirtioFs
->Ring
);
278 ExitBootServices event notification function for a Virtio Filesystem object.
280 This function resets the VIRTIO_FS.Virtio device, causing it to release all
281 references to guest-side resources. The function may only be called after
282 VirtioFsInit() returns successfully and before VirtioFsUninit() is called.
284 @param[in] ExitBootEvent The VIRTIO_FS.ExitBoot event that has been
287 @param[in] VirtioFsAsVoid Pointer to the VIRTIO_FS object, passed in as
293 IN EFI_EVENT ExitBootEvent
,
294 IN VOID
*VirtioFsAsVoid
299 VirtioFs
= VirtioFsAsVoid
;
300 DEBUG ((DEBUG_VERBOSE
, "%a: VirtioFs=0x%p Label=\"%s\"\n", __FUNCTION__
,
301 VirtioFsAsVoid
, VirtioFs
->Label
));
302 VirtioFs
->Virtio
->SetDeviceStatus (VirtioFs
->Virtio
, 0);
306 Validate two VIRTIO_FS_SCATTER_GATHER_LIST objects -- list of request
307 buffers, list of response buffers -- together.
309 On input, the caller is required to populate the following fields:
310 - VIRTIO_FS_IO_VECTOR.Buffer,
311 - VIRTIO_FS_IO_VECTOR.Size,
312 - VIRTIO_FS_SCATTER_GATHER_LIST.IoVec,
313 - VIRTIO_FS_SCATTER_GATHER_LIST.NumVec.
315 On output (on successful return), the following fields will be
317 - VIRTIO_FS_IO_VECTOR.Mapped,
318 - VIRTIO_FS_IO_VECTOR.MappedAddress,
319 - VIRTIO_FS_IO_VECTOR.Mapping,
320 - VIRTIO_FS_IO_VECTOR.Transferred.
322 On output (on successful return), the following fields will be calculated:
323 - VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize.
325 The function may only be called after VirtioFsInit() returns successfully and
326 before VirtioFsUninit() is called.
328 @param[in] VirtioFs The Virtio Filesystem device that the
329 request-response exchange, expressed via
330 RequestSgList and ResponseSgList, will be
333 @param[in,out] RequestSgList The scatter-gather list that describes the
334 request part of the exchange -- the buffers
335 that should be sent to the Virtio Filesystem
336 device in the virtio transfer.
338 @param[in,out] ResponseSgList The scatter-gather list that describes the
339 response part of the exchange -- the buffers
340 that the Virtio Filesystem device should
341 populate in the virtio transfer. May be NULL
342 if the exchange with the Virtio Filesystem
343 device consists of a request only, with the
344 response part omitted altogether.
346 @retval EFI_SUCCESS RequestSgList and ResponseSgList have been
347 validated, output fields have been set.
349 @retval EFI_INVALID_PARAMETER RequestSgList is NULL.
351 @retval EFI_INVALID_PARAMETER On input, a
352 VIRTIO_FS_SCATTER_GATHER_LIST.IoVec field is
354 VIRTIO_FS_SCATTER_GATHER_LIST.NumVec field is
357 @retval EFI_INVALID_PARAMETER On input, a VIRTIO_FS_IO_VECTOR.Buffer field
358 is NULL, or a VIRTIO_FS_IO_VECTOR.Size field
361 @retval EFI_UNSUPPORTED (RequestSgList->NumVec +
362 ResponseSgList->NumVec) exceeds
363 VirtioFs->QueueSize, meaning that the total
364 list of buffers cannot be placed on the virtio
365 queue in a single descriptor chain (with one
366 descriptor per buffer).
368 @retval EFI_UNSUPPORTED One of the
369 VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize fields
370 would exceed MAX_UINT32.
373 VirtioFsSgListsValidate (
374 IN VIRTIO_FS
*VirtioFs
,
375 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST
*RequestSgList
,
376 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST
*ResponseSgList OPTIONAL
379 VIRTIO_FS_SCATTER_GATHER_LIST
*SgListParam
[2];
380 UINT16 DescriptorsNeeded
;
383 if (RequestSgList
== NULL
) {
384 return EFI_INVALID_PARAMETER
;
387 SgListParam
[0] = RequestSgList
;
388 SgListParam
[1] = ResponseSgList
;
390 DescriptorsNeeded
= 0;
391 for (ListId
= 0; ListId
< ARRAY_SIZE (SgListParam
); ListId
++) {
392 VIRTIO_FS_SCATTER_GATHER_LIST
*SgList
;
393 UINT32 SgListTotalSize
;
396 SgList
= SgListParam
[ListId
];
397 if (SgList
== NULL
) {
401 // Sanity-check SgList -- it must provide at least one IO Vector.
403 if (SgList
->IoVec
== NULL
|| SgList
->NumVec
== 0) {
404 return EFI_INVALID_PARAMETER
;
407 // Make sure that, for each IO Vector in this SgList, a virtio descriptor
408 // can be added to the virtio queue, after the other descriptors added
411 if (SgList
->NumVec
> (UINTN
)(MAX_UINT16
- DescriptorsNeeded
) ||
412 DescriptorsNeeded
+ SgList
->NumVec
> VirtioFs
->QueueSize
) {
413 return EFI_UNSUPPORTED
;
415 DescriptorsNeeded
+= (UINT16
)SgList
->NumVec
;
418 for (IoVecIdx
= 0; IoVecIdx
< SgList
->NumVec
; IoVecIdx
++) {
419 VIRTIO_FS_IO_VECTOR
*IoVec
;
421 IoVec
= &SgList
->IoVec
[IoVecIdx
];
423 // Sanity-check this IoVec -- it must describe a non-empty buffer.
425 if (IoVec
->Buffer
== NULL
|| IoVec
->Size
== 0) {
426 return EFI_INVALID_PARAMETER
;
429 // Make sure the cumulative size of all IO Vectors in this SgList remains
430 // expressible as a UINT32.
432 if (IoVec
->Size
> MAX_UINT32
- SgListTotalSize
) {
433 return EFI_UNSUPPORTED
;
435 SgListTotalSize
+= (UINT32
)IoVec
->Size
;
438 // Initialize those fields in this IO Vector that will be updated in
439 // relation to mapping / transfer.
441 IoVec
->Mapped
= FALSE
;
442 IoVec
->MappedAddress
= 0;
443 IoVec
->Mapping
= NULL
;
444 IoVec
->Transferred
= 0;
448 // Store the cumulative size of all IO Vectors that we have calculated in
451 SgList
->TotalSize
= SgListTotalSize
;
458 Submit a validated pair of (request buffer list, response buffer list) to the
459 Virtio Filesystem device.
461 On input, the pair of VIRTIO_FS_SCATTER_GATHER_LIST objects must have been
462 validated together, using the VirtioFsSgListsValidate() function.
464 On output (on successful return), the following fields will be re-initialized
465 to zero (after temporarily setting them to different values):
466 - VIRTIO_FS_IO_VECTOR.Mapped,
467 - VIRTIO_FS_IO_VECTOR.MappedAddress,
468 - VIRTIO_FS_IO_VECTOR.Mapping.
470 On output (on successful return), the following fields will be calculated:
471 - VIRTIO_FS_IO_VECTOR.Transferred.
473 The function may only be called after VirtioFsInit() returns successfully and
474 before VirtioFsUninit() is called.
476 @param[in,out] VirtioFs The Virtio Filesystem device that the
477 request-response exchange, expressed via
478 RequestSgList and ResponseSgList, should now
481 @param[in,out] RequestSgList The scatter-gather list that describes the
482 request part of the exchange -- the buffers
483 that should be sent to the Virtio Filesystem
484 device in the virtio transfer.
486 @param[in,out] ResponseSgList The scatter-gather list that describes the
487 response part of the exchange -- the buffers
488 that the Virtio Filesystem device should
489 populate in the virtio transfer. May be NULL
490 if and only if NULL was passed to
491 VirtioFsSgListsValidate() as ResponseSgList.
493 @retval EFI_SUCCESS Transfer complete. The caller should investigate
494 the VIRTIO_FS_IO_VECTOR.Transferred fields in
495 ResponseSgList, to ensure coverage of the relevant
496 response buffers. Subsequently, the caller should
497 investigate the contents of those buffers.
499 @retval EFI_DEVICE_ERROR The Virtio Filesystem device reported populating
500 more response bytes than ResponseSgList->TotalSize.
502 @return Error codes propagated from
503 VirtioMapAllBytesInSharedBuffer(), VirtioFlush(),
504 or VirtioFs->Virtio->UnmapSharedBuffer().
507 VirtioFsSgListsSubmit (
508 IN OUT VIRTIO_FS
*VirtioFs
,
509 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST
*RequestSgList
,
510 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST
*ResponseSgList OPTIONAL
513 VIRTIO_FS_SCATTER_GATHER_LIST
*SgListParam
[2];
514 VIRTIO_MAP_OPERATION SgListVirtioMapOp
[ARRAY_SIZE (SgListParam
)];
515 UINT16 SgListDescriptorFlag
[ARRAY_SIZE (SgListParam
)];
517 VIRTIO_FS_SCATTER_GATHER_LIST
*SgList
;
519 VIRTIO_FS_IO_VECTOR
*IoVec
;
521 DESC_INDICES Indices
;
522 UINT32 TotalBytesWrittenByDevice
;
523 UINT32 BytesPermittedForWrite
;
525 SgListParam
[0] = RequestSgList
;
526 SgListVirtioMapOp
[0] = VirtioOperationBusMasterRead
;
527 SgListDescriptorFlag
[0] = 0;
529 SgListParam
[1] = ResponseSgList
;
530 SgListVirtioMapOp
[1] = VirtioOperationBusMasterWrite
;
531 SgListDescriptorFlag
[1] = VRING_DESC_F_WRITE
;
534 // Map all IO Vectors.
536 for (ListId
= 0; ListId
< ARRAY_SIZE (SgListParam
); ListId
++) {
537 SgList
= SgListParam
[ListId
];
538 if (SgList
== NULL
) {
541 for (IoVecIdx
= 0; IoVecIdx
< SgList
->NumVec
; IoVecIdx
++) {
542 IoVec
= &SgList
->IoVec
[IoVecIdx
];
544 // Map this IO Vector.
546 Status
= VirtioMapAllBytesInSharedBuffer (
548 SgListVirtioMapOp
[ListId
],
551 &IoVec
->MappedAddress
,
554 if (EFI_ERROR (Status
)) {
557 IoVec
->Mapped
= TRUE
;
562 // Compose the descriptor chain.
564 VirtioPrepare (&VirtioFs
->Ring
, &Indices
);
565 for (ListId
= 0; ListId
< ARRAY_SIZE (SgListParam
); ListId
++) {
566 SgList
= SgListParam
[ListId
];
567 if (SgList
== NULL
) {
570 for (IoVecIdx
= 0; IoVecIdx
< SgList
->NumVec
; IoVecIdx
++) {
573 IoVec
= &SgList
->IoVec
[IoVecIdx
];
575 // Set VRING_DESC_F_NEXT on all except the very last descriptor.
577 NextFlag
= VRING_DESC_F_NEXT
;
578 if (ListId
== ARRAY_SIZE (SgListParam
) - 1 &&
579 IoVecIdx
== SgList
->NumVec
- 1) {
584 IoVec
->MappedAddress
,
586 SgListDescriptorFlag
[ListId
] | NextFlag
,
593 // Submit the descriptor chain.
595 Status
= VirtioFlush (VirtioFs
->Virtio
, VIRTIO_FS_REQUEST_QUEUE
,
596 &VirtioFs
->Ring
, &Indices
, &TotalBytesWrittenByDevice
);
597 if (EFI_ERROR (Status
)) {
602 // Sanity-check: the Virtio Filesystem device should not have written more
603 // bytes than what we offered buffers for.
605 if (ResponseSgList
== NULL
) {
606 BytesPermittedForWrite
= 0;
608 BytesPermittedForWrite
= ResponseSgList
->TotalSize
;
610 if (TotalBytesWrittenByDevice
> BytesPermittedForWrite
) {
611 Status
= EFI_DEVICE_ERROR
;
616 // Update the transfer sizes in the IO Vectors.
618 for (ListId
= 0; ListId
< ARRAY_SIZE (SgListParam
); ListId
++) {
619 SgList
= SgListParam
[ListId
];
620 if (SgList
== NULL
) {
623 for (IoVecIdx
= 0; IoVecIdx
< SgList
->NumVec
; IoVecIdx
++) {
624 IoVec
= &SgList
->IoVec
[IoVecIdx
];
625 if (SgListVirtioMapOp
[ListId
] == VirtioOperationBusMasterRead
) {
627 // We report that the Virtio Filesystem device has read all buffers in
630 IoVec
->Transferred
= IoVec
->Size
;
633 // Regarding the response, calculate how much of the current IO Vector
634 // has been populated by the Virtio Filesystem device. In
635 // "TotalBytesWrittenByDevice", VirtioFlush() reported the total count
636 // across all device-writeable descriptors, in the order they were
637 // chained on the ring.
639 IoVec
->Transferred
= MIN ((UINTN
)TotalBytesWrittenByDevice
,
641 TotalBytesWrittenByDevice
-= (UINT32
)IoVec
->Transferred
;
647 // By now, "TotalBytesWrittenByDevice" has been exhausted.
649 ASSERT (TotalBytesWrittenByDevice
== 0);
652 // We've succeeded; fall through.
656 // Unmap all mapped IO Vectors on both the success and the error paths. The
657 // unmapping occurs in reverse order of mapping, in an attempt to avoid
658 // memory fragmentation.
660 ListId
= ARRAY_SIZE (SgListParam
);
663 SgList
= SgListParam
[ListId
];
664 if (SgList
== NULL
) {
667 IoVecIdx
= SgList
->NumVec
;
668 while (IoVecIdx
> 0) {
669 EFI_STATUS UnmapStatus
;
672 IoVec
= &SgList
->IoVec
[IoVecIdx
];
674 // Unmap this IO Vector, if it has been mapped.
676 if (!IoVec
->Mapped
) {
679 UnmapStatus
= VirtioFs
->Virtio
->UnmapSharedBuffer (VirtioFs
->Virtio
,
682 // Re-set the following fields to the values they initially got from
683 // VirtioFsSgListsValidate() -- the above unmapping attempt is considered
684 // final, even if it fails.
686 IoVec
->Mapped
= FALSE
;
687 IoVec
->MappedAddress
= 0;
688 IoVec
->Mapping
= NULL
;
691 // If we are on the success path, but the unmapping failed, we need to
692 // transparently flip to the failure path -- the caller must learn they
693 // should not consult the response buffers.
695 // The branch below can be taken at most once.
697 if (!EFI_ERROR (Status
) && EFI_ERROR (UnmapStatus
)) {
698 Status
= UnmapStatus
;
707 Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
709 The function may only be called after VirtioFsInit() returns successfully and
710 before VirtioFsUninit() is called.
712 @param[in,out] VirtioFs The Virtio Filesystem device that the request is
713 being prepared for. The "VirtioFs->RequestId" field
714 will be copied into "Request->Unique". On output (on
715 successful return), "VirtioFs->RequestId" will be
718 @param[out] Request The VIRTIO_FS_FUSE_REQUEST object whose fields are to
721 @param[in] RequestSize The total size of the request, including
722 sizeof(VIRTIO_FS_FUSE_REQUEST).
724 @param[in] Opcode The VIRTIO_FS_FUSE_OPCODE that identifies the command
727 @param[in] NodeId The inode number of the file that the request refers
728 to. When Opcode is VirtioFsFuseOpInit, NodeId is
729 ignored by the Virtio Filesystem device.
731 @retval EFI_INVALID_PARAMETER RequestSize is smaller than
732 sizeof(VIRTIO_FS_FUSE_REQUEST).
734 @retval EFI_OUT_OF_RESOURCES "VirtioFs->RequestId" is MAX_UINT64, and can
735 be incremented no more.
737 @retval EFI_SUCCESS Request has been populated,
738 "VirtioFs->RequestId" has been incremented.
741 VirtioFsFuseNewRequest (
742 IN OUT VIRTIO_FS
*VirtioFs
,
743 OUT VIRTIO_FS_FUSE_REQUEST
*Request
,
744 IN UINT32 RequestSize
,
745 IN VIRTIO_FS_FUSE_OPCODE Opcode
,
749 if (RequestSize
< sizeof *Request
) {
750 return EFI_INVALID_PARAMETER
;
753 if (VirtioFs
->RequestId
== MAX_UINT64
) {
754 return EFI_OUT_OF_RESOURCES
;
757 Request
->Len
= RequestSize
;
758 Request
->Opcode
= Opcode
;
759 Request
->Unique
= VirtioFs
->RequestId
++;
760 Request
->NodeId
= NodeId
;
764 Request
->Padding
= 0;
770 Check the common FUSE response format.
772 The first buffer in the response scatter-gather list is assumed a
773 VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to
774 and excluding the last one, are assumed fixed size. The last response buffer
775 may or may not be fixed size, as specified by the caller.
777 This function may only be called after VirtioFsSgListsSubmit() returns
780 @param[in] ResponseSgList The scatter-gather list that describes the
781 response part of the exchange -- the buffers that
782 the Virtio Filesystem device filled in during the
785 @param[in] RequestId The request identifier to which the response is
788 @param[out] TailBufferFill If NULL, then the last buffer in ResponseSgList
789 is considered fixed size. Otherwise, the last
790 buffer is considered variable size, and on
791 successful return, TailBufferFill reports the
792 number of bytes in the last buffer.
794 @retval EFI_INVALID_PARAMETER TailBufferFill is not NULL (i.e., the last
795 buffer is considered variable size), and
796 ResponseSgList->NumVec is 1.
798 @retval EFI_INVALID_PARAMETER The allocated size of the first buffer does
799 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
801 @retval EFI_PROTOCOL_ERROR The VIRTIO_FS_FUSE_RESPONSE structure in the
802 first buffer has not been fully populated.
804 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Len" in the first
805 buffer does not equal the sum of the
806 individual buffer sizes (as populated).
808 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
809 buffer does not equal RequestId.
811 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
812 buffer is zero, but a subsequent fixed size
813 buffer has not been fully populated.
815 @retval EFI_DEVICE_ERROR "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
816 buffer is nonzero. The caller may investigate
817 "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the
818 completeness of the subsequent fixed size
819 buffers is not verified in this case.
821 @retval EFI_SUCCESS Verification successful.
824 VirtioFsFuseCheckResponse (
825 IN VIRTIO_FS_SCATTER_GATHER_LIST
*ResponseSgList
,
827 OUT UINTN
*TailBufferFill
830 UINTN NumFixedSizeVec
;
831 VIRTIO_FS_FUSE_RESPONSE
*CommonResp
;
832 UINT32 TotalTransferred
;
836 // Ensured by VirtioFsSgListsValidate().
838 ASSERT (ResponseSgList
->NumVec
> 0);
840 if (TailBufferFill
== NULL
) {
842 // All buffers are considered fixed size.
844 NumFixedSizeVec
= ResponseSgList
->NumVec
;
847 // If the last buffer is variable size, then we need that buffer to be
848 // different from the first buffer, which is considered a
849 // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.
851 if (ResponseSgList
->NumVec
== 1) {
852 return EFI_INVALID_PARAMETER
;
854 NumFixedSizeVec
= ResponseSgList
->NumVec
- 1;
858 // The first buffer is supposed to carry a (fully populated)
859 // VIRTIO_FS_FUSE_RESPONSE structure.
861 if (ResponseSgList
->IoVec
[0].Size
!= sizeof *CommonResp
) {
862 return EFI_INVALID_PARAMETER
;
864 if (ResponseSgList
->IoVec
[0].Transferred
!= ResponseSgList
->IoVec
[0].Size
) {
865 return EFI_PROTOCOL_ERROR
;
869 // FUSE must report the same number of bytes, written by the Virtio
870 // Filesystem device, as the virtio transport does.
872 CommonResp
= ResponseSgList
->IoVec
[0].Buffer
;
873 TotalTransferred
= 0;
874 for (Idx
= 0; Idx
< ResponseSgList
->NumVec
; Idx
++) {
876 // Integer overflow and truncation are not possible, based on
877 // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
879 TotalTransferred
+= (UINT32
)ResponseSgList
->IoVec
[Idx
].Transferred
;
881 if (CommonResp
->Len
!= TotalTransferred
) {
882 return EFI_PROTOCOL_ERROR
;
886 // Enforce that FUSE match our request ID in the response.
888 if (CommonResp
->Unique
!= RequestId
) {
889 return EFI_PROTOCOL_ERROR
;
893 // If there is an explicit error report, skip checking the transfer
894 // counts for the rest of the fixed size buffers.
896 if (CommonResp
->Error
!= 0) {
897 return EFI_DEVICE_ERROR
;
901 // There was no error reported, so we require that the Virtio Filesystem
902 // device populate all fixed size buffers. We checked this for the very first
903 // buffer above; let's check the rest (if any).
905 ASSERT (NumFixedSizeVec
>= 1);
906 for (Idx
= 1; Idx
< NumFixedSizeVec
; Idx
++) {
907 if (ResponseSgList
->IoVec
[Idx
].Transferred
!=
908 ResponseSgList
->IoVec
[Idx
].Size
) {
909 return EFI_PROTOCOL_ERROR
;
914 // If the last buffer is considered variable size, report its filled size.
916 if (TailBufferFill
!= NULL
) {
917 *TailBufferFill
= ResponseSgList
->IoVec
[NumFixedSizeVec
].Transferred
;
924 An ad-hoc function for mapping FUSE (well, Linux) "errno" values to
927 @param[in] Errno The "VIRTIO_FS_FUSE_RESPONSE.Error" value, returned by the
928 Virtio Filesystem device. The value is expected to be
931 @return An EFI_STATUS error code that's deemed a passable
932 mapping for the Errno value.
934 @retval EFI_DEVICE_ERROR Fallback EFI_STATUS code for unrecognized Errno
938 VirtioFsErrnoToEfiStatus (
943 case -1: // EPERM Operation not permitted
944 return EFI_SECURITY_VIOLATION
;
946 case -2: // ENOENT No such file or directory
947 case -3: // ESRCH No such process
948 case -6: // ENXIO No such device or address
949 case -10: // ECHILD No child processes
950 case -19: // ENODEV No such device
951 case -49: // EUNATCH Protocol driver not attached
952 case -65: // ENOPKG Package not installed
953 case -79: // ELIBACC Can not access a needed shared library
954 case -126: // ENOKEY Required key not available
955 return EFI_NOT_FOUND
;
957 case -4: // EINTR Interrupted system call
958 case -11: // EAGAIN, EWOULDBLOCK Resource temporarily unavailable
959 case -16: // EBUSY Device or resource busy
960 case -26: // ETXTBSY Text file busy
961 case -35: // EDEADLK, EDEADLOCK Resource deadlock avoided
962 case -39: // ENOTEMPTY Directory not empty
963 case -42: // ENOMSG No message of desired type
964 case -61: // ENODATA No data available
965 case -85: // ERESTART Interrupted system call should be restarted
966 return EFI_NOT_READY
;
968 case -5: // EIO Input/output error
969 case -45: // EL2NSYNC Level 2 not synchronized
970 case -46: // EL3HLT Level 3 halted
971 case -47: // EL3RST Level 3 reset
972 case -51: // EL2HLT Level 2 halted
973 case -121: // EREMOTEIO Remote I/O error
974 case -133: // EHWPOISON Memory page has hardware error
975 return EFI_DEVICE_ERROR
;
977 case -7: // E2BIG Argument list too long
978 case -36: // ENAMETOOLONG File name too long
979 case -90: // EMSGSIZE Message too long
980 return EFI_BAD_BUFFER_SIZE
;
982 case -8: // ENOEXEC Exec format error
983 case -15: // ENOTBLK Block device required
984 case -18: // EXDEV Invalid cross-device link
985 case -20: // ENOTDIR Not a directory
986 case -21: // EISDIR Is a directory
987 case -25: // ENOTTY Inappropriate ioctl for device
988 case -27: // EFBIG File too large
989 case -29: // ESPIPE Illegal seek
990 case -38: // ENOSYS Function not implemented
991 case -59: // EBFONT Bad font file format
992 case -60: // ENOSTR Device not a stream
993 case -83: // ELIBEXEC Cannot exec a shared library directly
994 case -88: // ENOTSOCK Socket operation on non-socket
995 case -91: // EPROTOTYPE Protocol wrong type for socket
996 case -92: // ENOPROTOOPT Protocol not available
997 case -93: // EPROTONOSUPPORT Protocol not supported
998 case -94: // ESOCKTNOSUPPORT Socket type not supported
999 case -95: // ENOTSUP, EOPNOTSUPP Operation not supported
1000 case -96: // EPFNOSUPPORT Protocol family not supported
1001 case -97: // EAFNOSUPPORT Address family not supported by protocol
1002 case -99: // EADDRNOTAVAIL Cannot assign requested address
1003 case -118: // ENOTNAM Not a XENIX named type file
1004 case -120: // EISNAM Is a named type file
1005 case -124: // EMEDIUMTYPE Wrong medium type
1006 return EFI_UNSUPPORTED
;
1008 case -9: // EBADF Bad file descriptor
1009 case -14: // EFAULT Bad address
1010 case -44: // ECHRNG Channel number out of range
1011 case -48: // ELNRNG Link number out of range
1012 case -53: // EBADR Invalid request descriptor
1013 case -56: // EBADRQC Invalid request code
1014 case -57: // EBADSLT Invalid slot
1015 case -76: // ENOTUNIQ Name not unique on network
1016 case -84: // EILSEQ Invalid or incomplete multibyte or wide character
1017 return EFI_NO_MAPPING
;
1019 case -12: // ENOMEM Cannot allocate memory
1020 case -23: // ENFILE Too many open files in system
1021 case -24: // EMFILE Too many open files
1022 case -31: // EMLINK Too many links
1023 case -37: // ENOLCK No locks available
1024 case -40: // ELOOP Too many levels of symbolic links
1025 case -50: // ENOCSI No CSI structure available
1026 case -55: // ENOANO No anode
1027 case -63: // ENOSR Out of streams resources
1028 case -82: // ELIBMAX Attempting to link in too many shared libraries
1029 case -87: // EUSERS Too many users
1030 case -105: // ENOBUFS No buffer space available
1031 case -109: // ETOOMANYREFS Too many references: cannot splice
1032 case -119: // ENAVAIL No XENIX semaphores available
1033 case -122: // EDQUOT Disk quota exceeded
1034 return EFI_OUT_OF_RESOURCES
;
1036 case -13: // EACCES Permission denied
1037 return EFI_ACCESS_DENIED
;
1039 case -17: // EEXIST File exists
1040 case -98: // EADDRINUSE Address already in use
1041 case -106: // EISCONN Transport endpoint is already connected
1042 case -114: // EALREADY Operation already in progress
1043 case -115: // EINPROGRESS Operation now in progress
1044 return EFI_ALREADY_STARTED
;
1046 case -22: // EINVAL Invalid argument
1047 case -33: // EDOM Numerical argument out of domain
1048 return EFI_INVALID_PARAMETER
;
1050 case -28: // ENOSPC No space left on device
1051 case -54: // EXFULL Exchange full
1052 return EFI_VOLUME_FULL
;
1054 case -30: // EROFS Read-only file system
1055 return EFI_WRITE_PROTECTED
;
1057 case -32: // EPIPE Broken pipe
1058 case -43: // EIDRM Identifier removed
1059 case -67: // ENOLINK Link has been severed
1060 case -68: // EADV Advertise error
1061 case -69: // ESRMNT Srmount error
1062 case -70: // ECOMM Communication error on send
1063 case -73: // EDOTDOT RFS specific error
1064 case -78: // EREMCHG Remote address changed
1065 case -86: // ESTRPIPE Streams pipe error
1066 case -102: // ENETRESET Network dropped connection on reset
1067 case -103: // ECONNABORTED Software caused connection abort
1068 case -104: // ECONNRESET Connection reset by peer
1069 case -116: // ESTALE Stale file handle
1070 case -125: // ECANCELED Operation canceled
1071 case -128: // EKEYREVOKED Key has been revoked
1072 case -129: // EKEYREJECTED Key was rejected by service
1073 case -130: // EOWNERDEAD Owner died
1074 case -131: // ENOTRECOVERABLE State not recoverable
1077 case -34: // ERANGE Numerical result out of range
1078 case -75: // EOVERFLOW Value too large for defined data type
1079 return EFI_BUFFER_TOO_SMALL
;
1081 case -52: // EBADE Invalid exchange
1082 case -108: // ESHUTDOWN Cannot send after transport endpoint shutdown
1083 case -111: // ECONNREFUSED Connection refused
1084 return EFI_END_OF_FILE
;
1086 case -62: // ETIME Timer expired
1087 case -110: // ETIMEDOUT Connection timed out
1088 case -127: // EKEYEXPIRED Key has expired
1091 case -64: // ENONET Machine is not on the network
1092 case -66: // EREMOTE Object is remote
1093 case -72: // EMULTIHOP Multihop attempted
1094 case -100: // ENETDOWN Network is down
1095 case -101: // ENETUNREACH Network is unreachable
1096 case -112: // EHOSTDOWN Host is down
1097 case -113: // EHOSTUNREACH No route to host
1098 case -123: // ENOMEDIUM No medium found
1099 case -132: // ERFKILL Operation not possible due to RF-kill
1100 return EFI_NO_MEDIA
;
1102 case -71: // EPROTO Protocol error
1103 return EFI_PROTOCOL_ERROR
;
1105 case -74: // EBADMSG Bad message
1106 case -77: // EBADFD File descriptor in bad state
1107 case -80: // ELIBBAD Accessing a corrupted shared library
1108 case -81: // ELIBSCN .lib section in a.out corrupted
1109 case -117: // EUCLEAN Structure needs cleaning
1110 return EFI_VOLUME_CORRUPTED
;
1112 case -89: // EDESTADDRREQ Destination address required
1113 case -107: // ENOTCONN Transport endpoint is not connected
1114 return EFI_NOT_STARTED
;
1120 return EFI_DEVICE_ERROR
;
1124 // Parser states for canonicalizing a POSIX pathname.
1127 ParserInit
, // just starting
1128 ParserEnd
, // finished
1129 ParserSlash
, // slash(es) seen
1130 ParserDot
, // one dot seen since last slash
1131 ParserDotDot
, // two dots seen since last slash
1132 ParserNormal
, // a different sequence seen
1136 Strip the trailing slash from the parser's output buffer, unless the trailing
1137 slash stands for the root directory.
1139 @param[in] Buffer The parser's output buffer. Only used for
1142 @param[in,out] Position On entry, points at the next character to produce
1143 (i.e., right past the end of the output written by
1144 the parser thus far). The last character in the
1145 parser's output buffer is a slash. On return, the
1146 slash is stripped, by decrementing Position by one.
1147 If this action would remove the slash character
1148 standing for the root directory, then the function
1155 IN OUT UINTN
*Position
1158 ASSERT (*Position
>= 1);
1159 ASSERT (Buffer
[*Position
- 1] == '/');
1160 if (*Position
== 1) {
1167 Produce one character in the parser's output buffer.
1169 @param[out] Buffer The parser's output buffer. On return, Char8 will
1172 @param[in,out] Position On entry, points at the next character to produce
1173 (i.e., right past the end of the output written by
1174 the parser thus far). On return, Position is
1177 @param[in] Size Total allocated size of the parser's output buffer.
1178 Used for sanity-checking.
1180 @param[in] Char8 The character to place in the output buffer.
1186 IN OUT UINTN
*Position
,
1191 ASSERT (*Position
< Size
);
1192 Buffer
[(*Position
)++] = Char8
;
1196 Rewind the last single-dot in the parser's output buffer.
1198 @param[in] Buffer The parser's output buffer. Only used for
1201 @param[in,out] Position On entry, points at the next character to produce
1202 (i.e., right past the end of the output written by
1203 the parser thus far); the parser's output buffer
1204 ends with the characters ('/', '.'). On return, the
1205 dot is rewound by decrementing Position by one; a
1206 slash character will reside at the new end of the
1207 parser's output buffer.
1213 IN OUT UINTN
*Position
1216 ASSERT (*Position
>= 2);
1217 ASSERT (Buffer
[*Position
- 1] == '.');
1218 ASSERT (Buffer
[*Position
- 2] == '/');
1223 Rewind the last dot-dot in the parser's output buffer.
1225 @param[in] Buffer The parser's output buffer. Only used for
1228 @param[in,out] Position On entry, points at the next character to produce
1229 (i.e., right past the end of the output written by
1230 the parser thus far); the parser's output buffer
1231 ends with the characters ('/', '.', '.'). On return,
1232 the ('.', '.') pair is rewound unconditionally, by
1233 decrementing Position by two; a slash character
1234 resides at the new end of the parser's output
1237 If this slash character stands for the root
1238 directory, then RootEscape is set to TRUE.
1240 Otherwise (i.e., if this slash character is not the
1241 one standing for the root directory), then the slash
1242 character, and the pathname component preceding it,
1243 are removed by decrementing Position further. In
1244 this case, the slash character preceding the removed
1245 pathname component will reside at the new end of the
1246 parser's output buffer.
1248 @param[out] RootEscape Set to TRUE on output if the dot-dot component tries
1249 to escape the root directory, as described above.
1250 Otherwise, RootEscape is not modified.
1254 ParserRewindDotDot (
1256 IN OUT UINTN
*Position
,
1257 OUT BOOLEAN
*RootEscape
1261 ASSERT (*Position
>= 3);
1262 ASSERT (Buffer
[*Position
- 1] == '.');
1263 ASSERT (Buffer
[*Position
- 2] == '.');
1264 ASSERT (Buffer
[*Position
- 3] == '/');
1267 if (*Position
== 1) {
1269 // Root directory slash reached; don't try to climb higher.
1280 // Scan until next slash to the left.
1283 ASSERT (*Position
> 0);
1285 } while (Buffer
[*Position
] != '/');
1290 Append the UEFI-style RhsPath16 to the POSIX-style, canonical format
1291 LhsPath8. Output the POSIX-style, canonical format result in ResultPath, as a
1292 dynamically allocated string.
1294 Canonicalization (aka sanitization) establishes the following properties:
1295 - ResultPath is absolute (starts with "/"),
1296 - dot (.) and dot-dot (..) components are resolved/eliminated in ResultPath,
1297 with the usual semantics,
1298 - ResultPath uses forward slashes,
1299 - sequences of slashes are squashed in ResultPath,
1300 - the printable ASCII character set covers ResultPath,
1301 - CHAR8 encoding is used in ResultPath,
1302 - no trailing slash present in ResultPath except for the standalone root
1304 - the length of ResultPath is at most VIRTIO_FS_MAX_PATHNAME_LENGTH.
1306 Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
1307 reported through RootEscape, without failing the function call.
1309 @param[in] LhsPath8 Identifies the base directory. The caller is
1310 responsible for ensuring that LhsPath8 conform to
1311 the above canonical pathname format on entry.
1313 @param[in] RhsPath16 Identifies the desired file with a UEFI-style CHAR16
1314 pathname. If RhsPath16 starts with a backslash, then
1315 RhsPath16 is considered absolute, and LhsPath8 is
1316 ignored; RhsPath16 is sanitized in isolation, for
1317 producing ResultPath8. Otherwise (i.e., if RhsPath16
1318 is relative), RhsPath16 is transliterated to CHAR8,
1319 and naively appended to LhsPath8. The resultant
1320 fused pathname is then sanitized, to produce
1323 @param[out] ResultPath8 The POSIX-style, canonical format pathname that
1324 leads to the file desired by the caller. After use,
1325 the caller is responsible for freeing ResultPath8.
1327 @param[out] RootEscape Set to TRUE if at least one dot-dot component in
1328 RhsPath16 attempted to escape the root directory;
1329 set to FALSE otherwise.
1331 @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
1334 @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
1336 @retval EFI_INVALID_PARAMETER RhsPath16 failed the
1337 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1339 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1341 @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
1342 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1344 @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
1345 falls outside of the printable ASCII set, or
1349 VirtioFsAppendPath (
1351 IN CHAR16
*RhsPath16
,
1352 OUT CHAR8
**ResultPath8
,
1353 OUT BOOLEAN
*RootEscape
1360 UINTN SizeToSanitize
;
1361 CHAR8
*BufferToSanitize
;
1362 CHAR8
*SanitizedBuffer
;
1364 UINTN SanitizedPosition
;
1367 // Appending an empty pathname is not allowed.
1369 RhsLen
= StrLen (RhsPath16
);
1371 return EFI_INVALID_PARAMETER
;
1374 // Enforce length restriction on RhsPath16.
1376 if (RhsLen
> VIRTIO_FS_MAX_PATHNAME_LENGTH
) {
1377 return EFI_INVALID_PARAMETER
;
1381 // Transliterate RhsPath16 to RhsPath8 by:
1382 // - rejecting RhsPath16 if a character outside of printable ASCII is seen,
1383 // - rejecting RhsPath16 if a forward slash is seen,
1384 // - replacing backslashes with forward slashes,
1385 // - casting the characters from CHAR16 to CHAR8.
1387 RhsPath8
= AllocatePool (RhsLen
+ 1);
1388 if (RhsPath8
== NULL
) {
1389 return EFI_OUT_OF_RESOURCES
;
1391 for (Idx
= 0; RhsPath16
[Idx
] != L
'\0'; Idx
++) {
1392 if (RhsPath16
[Idx
] < 0x20 || RhsPath16
[Idx
] > 0x7E ||
1393 RhsPath16
[Idx
] == L
'/') {
1394 Status
= EFI_UNSUPPORTED
;
1397 RhsPath8
[Idx
] = (CHAR8
)((RhsPath16
[Idx
] == L
'\\') ? L
'/' : RhsPath16
[Idx
]);
1399 RhsPath8
[Idx
++] = '\0';
1402 // Now prepare the input for the canonicalization (squashing of sequences of
1403 // forward slashes, and eliminating . (dot) and .. (dot-dot) pathname
1406 // The sanitized path can never be longer than the naive concatenation of the
1407 // left hand side and right hand side paths, so we'll use the catenated size
1408 // for allocating the sanitized output too.
1410 if (RhsPath8
[0] == '/') {
1412 // If the right hand side path is absolute, then it is not appended to the
1413 // left hand side path -- it *replaces* the left hand side path.
1415 SizeToSanitize
= RhsLen
+ 1;
1416 BufferToSanitize
= RhsPath8
;
1419 // If the right hand side path is relative, then it is appended (naively)
1420 // to the left hand side.
1424 LhsLen
= AsciiStrLen (LhsPath8
);
1425 SizeToSanitize
= LhsLen
+ 1 + RhsLen
+ 1;
1426 BufferToSanitize
= AllocatePool (SizeToSanitize
);
1427 if (BufferToSanitize
== NULL
) {
1428 Status
= EFI_OUT_OF_RESOURCES
;
1431 CopyMem (BufferToSanitize
, LhsPath8
, LhsLen
);
1432 BufferToSanitize
[LhsLen
] = '/';
1433 CopyMem (BufferToSanitize
+ LhsLen
+ 1, RhsPath8
, RhsLen
+ 1);
1437 // Allocate the output buffer.
1439 SanitizedBuffer
= AllocatePool (SizeToSanitize
);
1440 if (SanitizedBuffer
== NULL
) {
1441 Status
= EFI_OUT_OF_RESOURCES
;
1442 goto FreeBufferToSanitize
;
1446 // State machine for parsing the input and producing the canonical output
1449 *RootEscape
= FALSE
;
1452 SanitizedPosition
= 0;
1456 ASSERT (Idx
< SizeToSanitize
);
1457 Chr8
= BufferToSanitize
[Idx
++];
1460 case ParserInit
: // just starting
1461 ASSERT (Chr8
== '/');
1462 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1463 State
= ParserSlash
;
1466 case ParserSlash
: // slash(es) seen
1469 ParserStripSlash (SanitizedBuffer
, &SanitizedPosition
);
1470 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1475 // skip & stay in same state
1479 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1483 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1484 State
= ParserNormal
;
1489 case ParserDot
: // one dot seen since last slash
1492 ParserRewindDot (SanitizedBuffer
, &SanitizedPosition
);
1493 ParserStripSlash (SanitizedBuffer
, &SanitizedPosition
);
1494 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1498 ParserRewindDot (SanitizedBuffer
, &SanitizedPosition
);
1499 State
= ParserSlash
;
1502 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1503 State
= ParserDotDot
;
1506 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1507 State
= ParserNormal
;
1512 case ParserDotDot
: // two dots seen since last slash
1515 ParserRewindDotDot (SanitizedBuffer
, &SanitizedPosition
, RootEscape
);
1516 ParserStripSlash (SanitizedBuffer
, &SanitizedPosition
);
1517 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1521 ParserRewindDotDot (SanitizedBuffer
, &SanitizedPosition
, RootEscape
);
1522 State
= ParserSlash
;
1529 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1530 State
= ParserNormal
;
1535 case ParserNormal
: // a different sequence seen
1538 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1542 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1543 State
= ParserSlash
;
1551 // copy and stay in same state
1553 ParserCopy (SanitizedBuffer
, &SanitizedPosition
, SizeToSanitize
, Chr8
);
1562 } while (State
!= ParserEnd
);
1565 // Ensure length invariant on ResultPath8.
1567 ASSERT (SanitizedPosition
>= 2);
1568 if (SanitizedPosition
- 1 > VIRTIO_FS_MAX_PATHNAME_LENGTH
) {
1569 Status
= EFI_OUT_OF_RESOURCES
;
1570 goto FreeSanitizedBuffer
;
1573 *ResultPath8
= SanitizedBuffer
;
1574 SanitizedBuffer
= NULL
;
1575 Status
= EFI_SUCCESS
;
1579 FreeSanitizedBuffer
:
1580 if (SanitizedBuffer
!= NULL
) {
1581 FreePool (SanitizedBuffer
);
1584 FreeBufferToSanitize
:
1585 if (RhsPath8
[0] != '/') {
1586 FreePool (BufferToSanitize
);
1590 FreePool (RhsPath8
);
1595 For a given canonical pathname (as defined at VirtioFsAppendPath()), look up
1596 the NodeId of the most specific parent directory, plus output a pointer to
1597 the last pathname component (which is therefore a direct child of said parent
1600 The function may only be called after VirtioFsFuseInitSession() returns
1601 successfully and before VirtioFsUninit() is called.
1603 @param[in,out] VirtioFs The Virtio Filesystem device to send FUSE_LOOKUP
1604 and FUSE_FORGET requests to. On output, the FUSE
1605 request counter "VirtioFs->RequestId" will have
1606 been incremented several times.
1608 @param[in,out] Path The canonical pathname (as defined in the
1609 description of VirtioFsAppendPath()) to split.
1610 Path is modified in-place temporarily; however, on
1611 return (successful or otherwise), Path reassumes
1612 its original contents.
1614 @param[out] DirNodeId The NodeId of the most specific parent directory
1615 identified by Path. The caller is responsible for
1616 sending a FUSE_FORGET request to the Virtio
1617 Filesystem device for DirNodeId -- unless
1618 DirNodeId equals VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
1619 --, when DirNodeId's use ends.
1621 @param[out] LastComponent A pointer into Path, pointing at the start of the
1622 last pathname component.
1624 @retval EFI_SUCCESS Splitting successful.
1626 @retval EFI_INVALID_PARAMETER Path is "/".
1628 @retval EFI_ACCESS_DENIED One of the components on Path before the last
1631 @return Error codes propagated from
1632 VirtioFsFuseLookup() and
1633 VirtioFsFuseAttrToEfiFileInfo().
1636 VirtioFsLookupMostSpecificParentDir (
1637 IN OUT VIRTIO_FS
*VirtioFs
,
1639 OUT UINT64
*DirNodeId
,
1640 OUT CHAR8
**LastComponent
1643 UINT64 ParentDirNodeId
;
1646 UINT64 NextDirNodeId
;
1648 if (AsciiStrCmp (Path
, "/") == 0) {
1649 return EFI_INVALID_PARAMETER
;
1652 ParentDirNodeId
= VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
;
1656 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr
;
1657 EFI_FILE_INFO FileInfo
;
1660 // Find the slash (if any) that terminates the next pathname component.
1662 NextSlash
= AsciiStrStr (Slash
+ 1, "/");
1663 if (NextSlash
== NULL
) {
1668 // Temporarily replace the found slash character with a NUL in-place, for
1669 // easy construction of the single-component filename that we need to look
1673 Status
= VirtioFsFuseLookup (VirtioFs
, ParentDirNodeId
, Slash
+ 1,
1674 &NextDirNodeId
, &FuseAttr
);
1678 // We're done with the directory inode that was the basis for the lookup.
1680 if (ParentDirNodeId
!= VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
) {
1681 VirtioFsFuseForget (VirtioFs
, ParentDirNodeId
);
1685 // If we couldn't look up the next *non-final* pathname component, bail.
1687 if (EFI_ERROR (Status
)) {
1692 // Lookup successful; now check if the next (non-final) component is a
1693 // directory. If not, bail.
1695 Status
= VirtioFsFuseAttrToEfiFileInfo (&FuseAttr
, &FileInfo
);
1696 if (EFI_ERROR (Status
)) {
1697 goto ForgetNextDirNodeId
;
1699 if ((FileInfo
.Attribute
& EFI_FILE_DIRECTORY
) == 0) {
1700 Status
= EFI_ACCESS_DENIED
;
1701 goto ForgetNextDirNodeId
;
1707 ParentDirNodeId
= NextDirNodeId
;
1712 // ParentDirNodeId corresponds to the last containing directory. The
1713 // remaining single-component filename represents a direct child under that
1714 // directory. Said filename starts at (Slash + 1).
1716 *DirNodeId
= ParentDirNodeId
;
1717 *LastComponent
= Slash
+ 1;
1720 ForgetNextDirNodeId
:
1721 VirtioFsFuseForget (VirtioFs
, NextDirNodeId
);
1726 Format the last component of a canonical pathname into a caller-provided
1729 @param[in] Path The canonical pathname (as defined in the
1730 description of VirtioFsAppendPath()) to format
1731 the last component of.
1733 @param[out] Basename If BasenameSize is zero on input, Basename may
1734 be NULL. Otherwise, Basename is allocated by the
1735 caller. On successful return, Basename contains
1736 the last component of Path, formatted as a
1737 NUL-terminated CHAR16 string. When Path is "/"
1738 on input, Basename is L"" on output.
1740 @param[in,out] BasenameSize On input, the number of bytes the caller
1741 provides in Basename. On output, regardless of
1742 return value, the number of bytes required for
1743 formatting Basename, including the terminating
1746 @retval EFI_SUCCESS Basename has been filled in.
1748 @retval EFI_BUFFER_TOO_SMALL BasenameSize was too small on input; Basename
1749 has not been modified.
1752 VirtioFsGetBasename (
1754 OUT CHAR16
*Basename OPTIONAL
,
1755 IN OUT UINTN
*BasenameSize
1759 UINTN LastComponent
;
1763 AllocSize
= *BasenameSize
;
1765 LastComponent
= MAX_UINTN
;
1766 for (Idx
= 0; Path
[Idx
] != '\0'; Idx
++) {
1767 if (Path
[Idx
] == '/') {
1768 LastComponent
= Idx
;
1772 ASSERT (LastComponent
< MAX_UINTN
);
1774 *BasenameSize
= (PathSize
- LastComponent
) * sizeof Basename
[0];
1776 if (*BasenameSize
> AllocSize
) {
1777 return EFI_BUFFER_TOO_SMALL
;
1780 for (Idx
= LastComponent
; Idx
< PathSize
; Idx
++) {
1781 Basename
[Idx
- LastComponent
] = Path
[Idx
];
1787 Format the destination of a rename/move operation as a dynamically allocated
1790 Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
1791 reported through RootEscape, without failing the function call.
1793 @param[in] LhsPath8 The source pathname operand of the rename/move
1794 operation, expressed as a canonical pathname (as
1795 defined in the description of VirtioFsAppendPath()).
1796 The root directory "/" cannot be renamed/moved, and
1799 @param[in] RhsPath16 The destination pathname operand expressed as a
1800 UEFI-style CHAR16 pathname.
1802 If RhsPath16 starts with a backslash, then RhsPath16
1803 is considered absolute. Otherwise, RhsPath16 is
1804 interpreted relative to the most specific parent
1805 directory found in LhsPath8.
1807 Independently, if RhsPath16 ends with a backslash
1808 (i.e., RhsPath16 is given in the "move into
1809 directory" convenience form), then RhsPath16 is
1810 interpreted with the basename of LhsPath8 appended.
1811 Otherwise, the last pathname component of RhsPath16
1812 is taken as the last pathname component of the
1813 rename/move destination.
1815 An empty RhsPath16 is rejected.
1817 @param[out] ResultPath8 The POSIX-style, canonical format pathname that
1818 leads to the renamed/moved file. After use, the
1819 caller is responsible for freeing ResultPath8.
1821 @param[out] RootEscape Set to TRUE if at least one dot-dot component in
1822 RhsPath16 attempted to escape the root directory;
1823 set to FALSE otherwise.
1825 @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
1828 @retval EFI_INVALID_PARAMETER LhsPath8 is "/".
1830 @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
1832 @retval EFI_INVALID_PARAMETER RhsPath16 failed the
1833 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1835 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1837 @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
1838 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1840 @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
1841 falls outside of the printable ASCII set, or
1845 VirtioFsComposeRenameDestination (
1847 IN CHAR16
*RhsPath16
,
1848 OUT CHAR8
**ResultPath8
,
1849 OUT BOOLEAN
*RootEscape
1853 // Lengths are expressed as numbers of characters (CHAR8 or CHAR16),
1854 // excluding terminating NULs. Sizes are expressed as byte counts, including
1855 // the bytes taken up by terminating NULs.
1858 UINTN LhsBasename16Size
;
1860 UINTN LhsBasenameLen
;
1861 UINTN DestSuffix16Size
;
1862 CHAR16
*DestSuffix16
;
1866 // An empty destination operand for the rename/move operation is not allowed.
1868 RhsLen
= StrLen (RhsPath16
);
1870 return EFI_INVALID_PARAMETER
;
1873 // Enforce length restriction on RhsPath16.
1875 if (RhsLen
> VIRTIO_FS_MAX_PATHNAME_LENGTH
) {
1876 return EFI_INVALID_PARAMETER
;
1880 // Determine the length of the basename of LhsPath8.
1882 LhsBasename16Size
= 0;
1883 Status
= VirtioFsGetBasename (LhsPath8
, NULL
, &LhsBasename16Size
);
1884 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
1885 ASSERT (LhsBasename16Size
>= sizeof (CHAR16
));
1886 ASSERT (LhsBasename16Size
% sizeof (CHAR16
) == 0);
1887 LhsBasenameLen
= LhsBasename16Size
/ sizeof (CHAR16
) - 1;
1888 if (LhsBasenameLen
== 0) {
1890 // The root directory cannot be renamed/moved.
1892 return EFI_INVALID_PARAMETER
;
1896 // Resolve the "move into directory" convenience form in RhsPath16.
1898 if (RhsPath16
[RhsLen
- 1] == L
'\\') {
1900 // Append the basename of LhsPath8 as a CHAR16 string to RhsPath16.
1902 DestSuffix16Size
= RhsLen
* sizeof (CHAR16
) + LhsBasename16Size
;
1903 DestSuffix16
= AllocatePool (DestSuffix16Size
);
1904 if (DestSuffix16
== NULL
) {
1905 return EFI_OUT_OF_RESOURCES
;
1907 CopyMem (DestSuffix16
, RhsPath16
, RhsLen
* sizeof (CHAR16
));
1908 Status
= VirtioFsGetBasename (LhsPath8
, DestSuffix16
+ RhsLen
,
1909 &LhsBasename16Size
);
1910 ASSERT_EFI_ERROR (Status
);
1913 // Just create a copy of RhsPath16.
1915 DestSuffix16Size
= (RhsLen
+ 1) * sizeof (CHAR16
);
1916 DestSuffix16
= AllocateCopyPool (DestSuffix16Size
, RhsPath16
);
1917 if (DestSuffix16
== NULL
) {
1918 return EFI_OUT_OF_RESOURCES
;
1923 // If the destination operand is absolute, it will be interpreted relative to
1924 // the root directory.
1926 // Otherwise (i.e., if the destination operand is relative), then create the
1927 // canonical pathname that the destination operand is interpreted relatively
1928 // to; that is, the canonical pathname of the most specific parent directory
1929 // found in LhsPath8.
1931 if (DestSuffix16
[0] == L
'\\') {
1932 DestPrefix8
= AllocateCopyPool (sizeof "/", "/");
1933 if (DestPrefix8
== NULL
) {
1934 Status
= EFI_OUT_OF_RESOURCES
;
1935 goto FreeDestSuffix16
;
1939 UINTN DestPrefixLen
;
1942 // Strip the basename of LhsPath8.
1944 LhsLen
= AsciiStrLen (LhsPath8
);
1945 ASSERT (LhsBasenameLen
< LhsLen
);
1946 DestPrefixLen
= LhsLen
- LhsBasenameLen
;
1947 ASSERT (LhsPath8
[DestPrefixLen
- 1] == '/');
1949 // If we're not at the root directory, strip the slash too.
1951 if (DestPrefixLen
> 1) {
1954 DestPrefix8
= AllocatePool (DestPrefixLen
+ 1);
1955 if (DestPrefix8
== NULL
) {
1956 Status
= EFI_OUT_OF_RESOURCES
;
1957 goto FreeDestSuffix16
;
1959 CopyMem (DestPrefix8
, LhsPath8
, DestPrefixLen
);
1960 DestPrefix8
[DestPrefixLen
] = '\0';
1964 // Now combine DestPrefix8 and DestSuffix16 into the final canonical
1967 Status
= VirtioFsAppendPath (DestPrefix8
, DestSuffix16
, ResultPath8
,
1970 FreePool (DestPrefix8
);
1975 FreePool (DestSuffix16
);
1981 Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
1982 corresponding fields in EFI_FILE_INFO.
1984 @param[in] FuseAttr The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
1985 convert the relevant fields from.
1987 @param[out] FileInfo The EFI_FILE_INFO structure to modify. Importantly, the
1988 FileInfo->Size and FileInfo->FileName fields are not
1991 @retval EFI_SUCCESS Conversion successful.
1993 @retval EFI_UNSUPPORTED The allocated size of the file is inexpressible in
1996 @retval EFI_UNSUPPORTED One of the file access times is inexpressible in
1999 @retval EFI_UNSUPPORTED The file type is inexpressible in EFI_FILE_INFO.
2001 @retval EFI_UNSUPPORTED The file is a regular file that has multiple names
2002 on the host side (i.e., its hard link count is
2006 VirtioFsFuseAttrToEfiFileInfo (
2007 IN VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE
*FuseAttr
,
2008 OUT EFI_FILE_INFO
*FileInfo
2011 UINT64 EpochTime
[3];
2012 EFI_TIME
*ConvertedTime
[ARRAY_SIZE (EpochTime
)];
2015 FileInfo
->FileSize
= FuseAttr
->Size
;
2018 // The unit for FuseAttr->Blocks is 512B.
2020 if (FuseAttr
->Blocks
>= BIT55
) {
2021 return EFI_UNSUPPORTED
;
2023 FileInfo
->PhysicalSize
= LShiftU64 (FuseAttr
->Blocks
, 9);
2026 // Convert the timestamps. File creation time is not tracked by the Virtio
2027 // Filesystem device, so set FileInfo->CreateTime from FuseAttr->Mtime as
2030 EpochTime
[0] = FuseAttr
->Mtime
;
2031 EpochTime
[1] = FuseAttr
->Atime
;
2032 EpochTime
[2] = FuseAttr
->Mtime
;
2033 ConvertedTime
[0] = &FileInfo
->CreateTime
;
2034 ConvertedTime
[1] = &FileInfo
->LastAccessTime
;
2035 ConvertedTime
[2] = &FileInfo
->ModificationTime
;
2037 for (Idx
= 0; Idx
< ARRAY_SIZE (EpochTime
); Idx
++) {
2039 // EpochToEfiTime() takes a UINTN for seconds.
2041 if (EpochTime
[Idx
] > MAX_UINTN
) {
2042 return EFI_UNSUPPORTED
;
2045 // Set the following fields in the converted time: Year, Month, Day, Hour,
2046 // Minute, Second, Nanosecond.
2048 EpochToEfiTime ((UINTN
)EpochTime
[Idx
], ConvertedTime
[Idx
]);
2050 // The times are all expressed in UTC. Consequently, they are not affected
2051 // by daylight saving.
2053 ConvertedTime
[Idx
]->TimeZone
= 0;
2054 ConvertedTime
[Idx
]->Daylight
= 0;
2056 // Clear the padding fields.
2058 ConvertedTime
[Idx
]->Pad1
= 0;
2059 ConvertedTime
[Idx
]->Pad2
= 0;
2063 // Set the attributes.
2065 switch (FuseAttr
->Mode
& VIRTIO_FS_FUSE_MODE_TYPE_MASK
) {
2066 case VIRTIO_FS_FUSE_MODE_TYPE_DIR
:
2067 FileInfo
->Attribute
= EFI_FILE_DIRECTORY
;
2069 case VIRTIO_FS_FUSE_MODE_TYPE_REG
:
2070 FileInfo
->Attribute
= 0;
2074 // Other file types are not supported.
2076 return EFI_UNSUPPORTED
;
2079 // Report the regular file or directory as read-only if all classes lack
2080 // write permission.
2082 if ((FuseAttr
->Mode
& (VIRTIO_FS_FUSE_MODE_PERM_WUSR
|
2083 VIRTIO_FS_FUSE_MODE_PERM_WGRP
|
2084 VIRTIO_FS_FUSE_MODE_PERM_WOTH
)) == 0) {
2085 FileInfo
->Attribute
|= EFI_FILE_READ_ONLY
;
2089 // A hard link count greater than 1 is not supported for regular files.
2091 if ((FileInfo
->Attribute
& EFI_FILE_DIRECTORY
) == 0 && FuseAttr
->Nlink
> 1) {
2092 return EFI_UNSUPPORTED
;
2099 Convert a VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE filename to an EFI_FILE_INFO
2102 @param[in] FuseDirent The VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE object to
2103 convert the filename byte array from. The caller is
2104 responsible for ensuring that FuseDirent->Namelen
2105 describe valid storage.
2107 @param[in,out] FileInfo The EFI_FILE_INFO structure to modify. On input, the
2108 caller is responsible for setting FileInfo->Size
2109 according to the allocated size. On successful
2110 return, FileInfo->Size is reduced to reflect the
2111 filename converted into FileInfo->FileName.
2112 FileInfo->FileName is set from the filename byte
2113 array that directly follows the FuseDirent header
2114 object. Fields other than FileInfo->Size and
2115 FileInfo->FileName are not modified.
2117 @retval EFI_SUCCESS Conversion successful.
2119 @retval EFI_INVALID_PARAMETER VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE()
2120 returns zero for FuseDirent->Namelen.
2122 @retval EFI_BUFFER_TOO_SMALL On input, FileInfo->Size does not provide
2123 enough room for converting the filename byte
2124 array from FuseDirent.
2126 @retval EFI_UNSUPPORTED The FuseDirent filename byte array contains a
2127 byte that falls outside of the printable ASCII
2128 range, or is a forward slash or a backslash.
2131 VirtioFsFuseDirentPlusToEfiFileInfo (
2132 IN VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE
*FuseDirent
,
2133 IN OUT EFI_FILE_INFO
*FileInfo
2141 DirentSize
= VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (FuseDirent
->Namelen
);
2142 if (DirentSize
== 0) {
2143 return EFI_INVALID_PARAMETER
;
2146 // We're now safe from overflow in the calculation below.
2148 FileInfoSize
= (OFFSET_OF (EFI_FILE_INFO
, FileName
) +
2149 ((UINTN
)FuseDirent
->Namelen
+ 1) * sizeof (CHAR16
));
2150 if (FileInfoSize
> FileInfo
->Size
) {
2151 return EFI_BUFFER_TOO_SMALL
;
2155 // Convert the name.
2157 DirentName
= (UINT8
*)(FuseDirent
+ 1);
2158 for (Idx
= 0; Idx
< FuseDirent
->Namelen
; Idx
++) {
2161 NameByte
= DirentName
[Idx
];
2162 if (NameByte
< 0x20 || NameByte
> 0x7E ||
2163 NameByte
== '/' || NameByte
== '\\') {
2164 return EFI_UNSUPPORTED
;
2166 FileInfo
->FileName
[Idx
] = (CHAR16
)NameByte
;
2168 FileInfo
->FileName
[Idx
++] = L
'\0';
2170 // Set the (possibly reduced) size.
2172 FileInfo
->Size
= FileInfoSize
;