]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioFsDxe/Helpers.c
8ffdabfd40f35ecb057c941097613d01c62bce7b
[mirror_edk2.git] / OvmfPkg / VirtioFsDxe / Helpers.c
1 /** @file
2 Initialization and helper routines for the Virtio Filesystem device.
3
4 Copyright (C) 2020, Red Hat, Inc.
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8
9 #include <Library/BaseLib.h> // StrLen()
10 #include <Library/BaseMemoryLib.h> // CopyMem()
11 #include <Library/MemoryAllocationLib.h> // AllocatePool()
12 #include <Library/VirtioLib.h> // Virtio10WriteFeatures()
13
14 #include "VirtioFsDxe.h"
15
16 /**
17 Read the Virtio Filesystem device configuration structure in full.
18
19 @param[in] Virtio The Virtio protocol underlying the VIRTIO_FS object.
20
21 @param[out] Config The fully populated VIRTIO_FS_CONFIG structure.
22
23 @retval EFI_SUCCESS Config has been filled in.
24
25 @return Error codes propagated from Virtio->ReadDevice(). The
26 contents of Config are indeterminate.
27 **/
28 STATIC
29 EFI_STATUS
30 VirtioFsReadConfig (
31 IN VIRTIO_DEVICE_PROTOCOL *Virtio,
32 OUT VIRTIO_FS_CONFIG *Config
33 )
34 {
35 UINTN Idx;
36 EFI_STATUS Status;
37
38 for (Idx = 0; Idx < VIRTIO_FS_TAG_BYTES; Idx++) {
39 Status = Virtio->ReadDevice (
40 Virtio, // This
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
45 );
46 if (EFI_ERROR (Status)) {
47 return Status;
48 }
49 }
50
51 Status = Virtio->ReadDevice (
52 Virtio, // This
53 OFFSET_OF (VIRTIO_FS_CONFIG, NumReqQueues), // FieldOffset
54 sizeof Config->NumReqQueues, // FieldSize
55 sizeof Config->NumReqQueues, // BufferSize
56 &Config->NumReqQueues // Buffer
57 );
58 return Status;
59 }
60
61 /**
62 Configure the Virtio Filesystem device underlying VirtioFs.
63
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
69 the device.
70
71 @retval EFI_SUCCESS Virtio machinery has been set up.
72
73 @retval EFI_UNSUPPORTED The host-side configuration of the Virtio Filesystem
74 is not supported by this driver.
75
76 @return Error codes from underlying functions.
77 **/
78 EFI_STATUS
79 VirtioFsInit (
80 IN OUT VIRTIO_FS *VirtioFs
81 )
82 {
83 UINT8 NextDevStat;
84 EFI_STATUS Status;
85 UINT64 Features;
86 VIRTIO_FS_CONFIG Config;
87 UINTN Idx;
88 UINT64 RingBaseShift;
89
90 //
91 // Execute virtio-v1.1-cs01-87fa6b5d8155, 3.1.1 Driver Requirements: Device
92 // Initialization.
93 //
94 // 1. Reset the device.
95 //
96 NextDevStat = 0;
97 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
98 if (EFI_ERROR (Status)) {
99 goto Failed;
100 }
101
102 //
103 // 2. Set the ACKNOWLEDGE status bit [...]
104 //
105 NextDevStat |= VSTAT_ACK;
106 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
107 if (EFI_ERROR (Status)) {
108 goto Failed;
109 }
110
111 //
112 // 3. Set the DRIVER status bit [...]
113 //
114 NextDevStat |= VSTAT_DRIVER;
115 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
116 if (EFI_ERROR (Status)) {
117 goto Failed;
118 }
119
120 //
121 // 4. Read device feature bits...
122 //
123 Status = VirtioFs->Virtio->GetDeviceFeatures (VirtioFs->Virtio, &Features);
124 if (EFI_ERROR (Status)) {
125 goto Failed;
126 }
127 if ((Features & VIRTIO_F_VERSION_1) == 0) {
128 Status = EFI_UNSUPPORTED;
129 goto Failed;
130 }
131 //
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.
135 //
136 Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM;
137
138 //
139 // ... and write the subset of feature bits understood by the [...] driver to
140 // the device. [...]
141 // 5. Set the FEATURES_OK status bit.
142 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
143 //
144 Status = Virtio10WriteFeatures (VirtioFs->Virtio, Features, &NextDevStat);
145 if (EFI_ERROR (Status)) {
146 goto Failed;
147 }
148
149 //
150 // 7. Perform device-specific setup, including discovery of virtqueues for
151 // the device, [...] reading [...] the device's virtio configuration space
152 //
153 Status = VirtioFsReadConfig (VirtioFs->Virtio, &Config);
154 if (EFI_ERROR (Status)) {
155 goto Failed;
156 }
157
158 //
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
162 // original label.
163 //
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;
167 goto Failed;
168 }
169 VirtioFs->Label[Idx] = Config.Tag[Idx];
170 }
171 VirtioFs->Label[Idx] = L'\0';
172
173 //
174 // 7.b. We need one queue for sending normal priority requests.
175 //
176 if (Config.NumReqQueues < 1) {
177 Status = EFI_UNSUPPORTED;
178 goto Failed;
179 }
180
181 //
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.
185 //
186 Status = VirtioFs->Virtio->SetQueueSel (VirtioFs->Virtio,
187 VIRTIO_FS_REQUEST_QUEUE);
188 if (EFI_ERROR (Status)) {
189 goto Failed;
190 }
191 Status = VirtioFs->Virtio->GetQueueNumMax (VirtioFs->Virtio,
192 &VirtioFs->QueueSize);
193 if (EFI_ERROR (Status)) {
194 goto Failed;
195 }
196 if (VirtioFs->QueueSize < 2) {
197 Status = EFI_UNSUPPORTED;
198 goto Failed;
199 }
200
201 //
202 // 7.d. [...] population of virtqueues [...]
203 //
204 Status = VirtioRingInit (VirtioFs->Virtio, VirtioFs->QueueSize,
205 &VirtioFs->Ring);
206 if (EFI_ERROR (Status)) {
207 goto Failed;
208 }
209
210 Status = VirtioRingMap (VirtioFs->Virtio, &VirtioFs->Ring, &RingBaseShift,
211 &VirtioFs->RingMap);
212 if (EFI_ERROR (Status)) {
213 goto ReleaseQueue;
214 }
215
216 Status = VirtioFs->Virtio->SetQueueAddress (VirtioFs->Virtio,
217 &VirtioFs->Ring, RingBaseShift);
218 if (EFI_ERROR (Status)) {
219 goto UnmapQueue;
220 }
221
222 //
223 // 8. Set the DRIVER_OK status bit.
224 //
225 NextDevStat |= VSTAT_DRIVER_OK;
226 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
227 if (EFI_ERROR (Status)) {
228 goto UnmapQueue;
229 }
230
231 return EFI_SUCCESS;
232
233 UnmapQueue:
234 VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
235
236 ReleaseQueue:
237 VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
238
239 Failed:
240 //
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). [...]
244 //
245 // Virtio access failure here should not mask the original error.
246 //
247 NextDevStat |= VSTAT_FAILED;
248 VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
249
250 return Status;
251 }
252
253 /**
254 De-configure the Virtio Filesystem device underlying VirtioFs.
255
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.
261 **/
262 VOID
263 VirtioFsUninit (
264 IN OUT VIRTIO_FS *VirtioFs
265 )
266 {
267 //
268 // Resetting the Virtio device makes it release its resources and forget its
269 // configuration.
270 //
271 VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
272 VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
273 VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
274 }
275
276 /**
277 ExitBootServices event notification function for a Virtio Filesystem object.
278
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.
282
283 @param[in] ExitBootEvent The VIRTIO_FS.ExitBoot event that has been
284 signaled.
285
286 @param[in] VirtioFsAsVoid Pointer to the VIRTIO_FS object, passed in as
287 (VOID*).
288 **/
289 VOID
290 EFIAPI
291 VirtioFsExitBoot (
292 IN EFI_EVENT ExitBootEvent,
293 IN VOID *VirtioFsAsVoid
294 )
295 {
296 VIRTIO_FS *VirtioFs;
297
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);
302 }
303
304 /**
305 Validate two VIRTIO_FS_SCATTER_GATHER_LIST objects -- list of request
306 buffers, list of response buffers -- together.
307
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.
313
314 On output (on successful return), the following fields will be
315 zero-initialized:
316 - VIRTIO_FS_IO_VECTOR.Mapped,
317 - VIRTIO_FS_IO_VECTOR.MappedAddress,
318 - VIRTIO_FS_IO_VECTOR.Mapping,
319 - VIRTIO_FS_IO_VECTOR.Transferred.
320
321 On output (on successful return), the following fields will be calculated:
322 - VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize.
323
324 The function may only be called after VirtioFsInit() returns successfully and
325 before VirtioFsUninit() is called.
326
327 @param[in] VirtioFs The Virtio Filesystem device that the
328 request-response exchange, expressed via
329 RequestSgList and ResponseSgList, will be
330 submitted to.
331
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.
336
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.
344
345 @retval EFI_SUCCESS RequestSgList and ResponseSgList have been
346 validated, output fields have been set.
347
348 @retval EFI_INVALID_PARAMETER RequestSgList is NULL.
349
350 @retval EFI_INVALID_PARAMETER On input, a
351 VIRTIO_FS_SCATTER_GATHER_LIST.IoVec field is
352 NULL, or a
353 VIRTIO_FS_SCATTER_GATHER_LIST.NumVec field is
354 zero.
355
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
358 is zero.
359
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).
366
367 @retval EFI_UNSUPPORTED One of the
368 VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize fields
369 would exceed MAX_UINT32.
370 **/
371 EFI_STATUS
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
376 )
377 {
378 VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];
379 UINT16 DescriptorsNeeded;
380 UINTN ListId;
381
382 if (RequestSgList == NULL) {
383 return EFI_INVALID_PARAMETER;
384 }
385
386 SgListParam[0] = RequestSgList;
387 SgListParam[1] = ResponseSgList;
388
389 DescriptorsNeeded = 0;
390 for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
391 VIRTIO_FS_SCATTER_GATHER_LIST *SgList;
392 UINT32 SgListTotalSize;
393 UINTN IoVecIdx;
394
395 SgList = SgListParam[ListId];
396 if (SgList == NULL) {
397 continue;
398 }
399 //
400 // Sanity-check SgList -- it must provide at least one IO Vector.
401 //
402 if (SgList->IoVec == NULL || SgList->NumVec == 0) {
403 return EFI_INVALID_PARAMETER;
404 }
405 //
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
408 // previously.
409 //
410 if (SgList->NumVec > (UINTN)(MAX_UINT16 - DescriptorsNeeded) ||
411 DescriptorsNeeded + SgList->NumVec > VirtioFs->QueueSize) {
412 return EFI_UNSUPPORTED;
413 }
414 DescriptorsNeeded += (UINT16)SgList->NumVec;
415
416 SgListTotalSize = 0;
417 for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
418 VIRTIO_FS_IO_VECTOR *IoVec;
419
420 IoVec = &SgList->IoVec[IoVecIdx];
421 //
422 // Sanity-check this IoVec -- it must describe a non-empty buffer.
423 //
424 if (IoVec->Buffer == NULL || IoVec->Size == 0) {
425 return EFI_INVALID_PARAMETER;
426 }
427 //
428 // Make sure the cumulative size of all IO Vectors in this SgList remains
429 // expressible as a UINT32.
430 //
431 if (IoVec->Size > MAX_UINT32 - SgListTotalSize) {
432 return EFI_UNSUPPORTED;
433 }
434 SgListTotalSize += (UINT32)IoVec->Size;
435
436 //
437 // Initialize those fields in this IO Vector that will be updated in
438 // relation to mapping / transfer.
439 //
440 IoVec->Mapped = FALSE;
441 IoVec->MappedAddress = 0;
442 IoVec->Mapping = NULL;
443 IoVec->Transferred = 0;
444 }
445
446 //
447 // Store the cumulative size of all IO Vectors that we have calculated in
448 // this SgList.
449 //
450 SgList->TotalSize = SgListTotalSize;
451 }
452
453 return EFI_SUCCESS;
454 }
455
456 /**
457 Submit a validated pair of (request buffer list, response buffer list) to the
458 Virtio Filesystem device.
459
460 On input, the pair of VIRTIO_FS_SCATTER_GATHER_LIST objects must have been
461 validated together, using the VirtioFsSgListsValidate() function.
462
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.
468
469 On output (on successful return), the following fields will be calculated:
470 - VIRTIO_FS_IO_VECTOR.Transferred.
471
472 The function may only be called after VirtioFsInit() returns successfully and
473 before VirtioFsUninit() is called.
474
475 @param[in,out] VirtioFs The Virtio Filesystem device that the
476 request-response exchange, expressed via
477 RequestSgList and ResponseSgList, should now
478 be submitted to.
479
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.
484
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.
491
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.
497
498 @retval EFI_DEVICE_ERROR The Virtio Filesystem device reported populating
499 more response bytes than ResponseSgList->TotalSize.
500
501 @return Error codes propagated from
502 VirtioMapAllBytesInSharedBuffer(), VirtioFlush(),
503 or VirtioFs->Virtio->UnmapSharedBuffer().
504 **/
505 EFI_STATUS
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
510 )
511 {
512 VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];
513 VIRTIO_MAP_OPERATION SgListVirtioMapOp[ARRAY_SIZE (SgListParam)];
514 UINT16 SgListDescriptorFlag[ARRAY_SIZE (SgListParam)];
515 UINTN ListId;
516 VIRTIO_FS_SCATTER_GATHER_LIST *SgList;
517 UINTN IoVecIdx;
518 VIRTIO_FS_IO_VECTOR *IoVec;
519 EFI_STATUS Status;
520 DESC_INDICES Indices;
521 UINT32 TotalBytesWrittenByDevice;
522 UINT32 BytesPermittedForWrite;
523
524 SgListParam[0] = RequestSgList;
525 SgListVirtioMapOp[0] = VirtioOperationBusMasterRead;
526 SgListDescriptorFlag[0] = 0;
527
528 SgListParam[1] = ResponseSgList;
529 SgListVirtioMapOp[1] = VirtioOperationBusMasterWrite;
530 SgListDescriptorFlag[1] = VRING_DESC_F_WRITE;
531
532 //
533 // Map all IO Vectors.
534 //
535 for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
536 SgList = SgListParam[ListId];
537 if (SgList == NULL) {
538 continue;
539 }
540 for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
541 IoVec = &SgList->IoVec[IoVecIdx];
542 //
543 // Map this IO Vector.
544 //
545 Status = VirtioMapAllBytesInSharedBuffer (
546 VirtioFs->Virtio,
547 SgListVirtioMapOp[ListId],
548 IoVec->Buffer,
549 IoVec->Size,
550 &IoVec->MappedAddress,
551 &IoVec->Mapping
552 );
553 if (EFI_ERROR (Status)) {
554 goto Unmap;
555 }
556 IoVec->Mapped = TRUE;
557 }
558 }
559
560 //
561 // Compose the descriptor chain.
562 //
563 VirtioPrepare (&VirtioFs->Ring, &Indices);
564 for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
565 SgList = SgListParam[ListId];
566 if (SgList == NULL) {
567 continue;
568 }
569 for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
570 UINT16 NextFlag;
571
572 IoVec = &SgList->IoVec[IoVecIdx];
573 //
574 // Set VRING_DESC_F_NEXT on all except the very last descriptor.
575 //
576 NextFlag = VRING_DESC_F_NEXT;
577 if (ListId == ARRAY_SIZE (SgListParam) - 1 &&
578 IoVecIdx == SgList->NumVec - 1) {
579 NextFlag = 0;
580 }
581 VirtioAppendDesc (
582 &VirtioFs->Ring,
583 IoVec->MappedAddress,
584 (UINT32)IoVec->Size,
585 SgListDescriptorFlag[ListId] | NextFlag,
586 &Indices
587 );
588 }
589 }
590
591 //
592 // Submit the descriptor chain.
593 //
594 Status = VirtioFlush (VirtioFs->Virtio, VIRTIO_FS_REQUEST_QUEUE,
595 &VirtioFs->Ring, &Indices, &TotalBytesWrittenByDevice);
596 if (EFI_ERROR (Status)) {
597 goto Unmap;
598 }
599
600 //
601 // Sanity-check: the Virtio Filesystem device should not have written more
602 // bytes than what we offered buffers for.
603 //
604 if (ResponseSgList == NULL) {
605 BytesPermittedForWrite = 0;
606 } else {
607 BytesPermittedForWrite = ResponseSgList->TotalSize;
608 }
609 if (TotalBytesWrittenByDevice > BytesPermittedForWrite) {
610 Status = EFI_DEVICE_ERROR;
611 goto Unmap;
612 }
613
614 //
615 // Update the transfer sizes in the IO Vectors.
616 //
617 for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
618 SgList = SgListParam[ListId];
619 if (SgList == NULL) {
620 continue;
621 }
622 for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
623 IoVec = &SgList->IoVec[IoVecIdx];
624 if (SgListVirtioMapOp[ListId] == VirtioOperationBusMasterRead) {
625 //
626 // We report that the Virtio Filesystem device has read all buffers in
627 // the request.
628 //
629 IoVec->Transferred = IoVec->Size;
630 } else {
631 //
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.
637 //
638 IoVec->Transferred = MIN ((UINTN)TotalBytesWrittenByDevice,
639 IoVec->Size);
640 TotalBytesWrittenByDevice -= (UINT32)IoVec->Transferred;
641 }
642 }
643 }
644
645 //
646 // By now, "TotalBytesWrittenByDevice" has been exhausted.
647 //
648 ASSERT (TotalBytesWrittenByDevice == 0);
649
650 //
651 // We've succeeded; fall through.
652 //
653 Unmap:
654 //
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.
658 //
659 ListId = ARRAY_SIZE (SgListParam);
660 while (ListId > 0) {
661 --ListId;
662 SgList = SgListParam[ListId];
663 if (SgList == NULL) {
664 continue;
665 }
666 IoVecIdx = SgList->NumVec;
667 while (IoVecIdx > 0) {
668 EFI_STATUS UnmapStatus;
669
670 --IoVecIdx;
671 IoVec = &SgList->IoVec[IoVecIdx];
672 //
673 // Unmap this IO Vector, if it has been mapped.
674 //
675 if (!IoVec->Mapped) {
676 continue;
677 }
678 UnmapStatus = VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio,
679 IoVec->Mapping);
680 //
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.
684 //
685 IoVec->Mapped = FALSE;
686 IoVec->MappedAddress = 0;
687 IoVec->Mapping = NULL;
688
689 //
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.
693 //
694 // The branch below can be taken at most once.
695 //
696 if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {
697 Status = UnmapStatus;
698 }
699 }
700 }
701
702 return Status;
703 }
704
705 /**
706 Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
707
708 The function may only be called after VirtioFsInit() returns successfully and
709 before VirtioFsUninit() is called.
710
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
715 incremented.
716
717 @param[out] Request The VIRTIO_FS_FUSE_REQUEST object whose fields are to
718 be set.
719
720 @param[in] RequestSize The total size of the request, including
721 sizeof(VIRTIO_FS_FUSE_REQUEST).
722
723 @param[in] Opcode The VIRTIO_FS_FUSE_OPCODE that identifies the command
724 to send.
725
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.
729
730 @retval EFI_INVALID_PARAMETER RequestSize is smaller than
731 sizeof(VIRTIO_FS_FUSE_REQUEST).
732
733 @retval EFI_OUT_OF_RESOURCES "VirtioFs->RequestId" is MAX_UINT64, and can
734 be incremented no more.
735
736 @retval EFI_SUCCESS Request has been populated,
737 "VirtioFs->RequestId" has been incremented.
738 **/
739 EFI_STATUS
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,
745 IN UINT64 NodeId
746 )
747 {
748 if (RequestSize < sizeof *Request) {
749 return EFI_INVALID_PARAMETER;
750 }
751
752 if (VirtioFs->RequestId == MAX_UINT64) {
753 return EFI_OUT_OF_RESOURCES;
754 }
755
756 Request->Len = RequestSize;
757 Request->Opcode = Opcode;
758 Request->Unique = VirtioFs->RequestId++;
759 Request->NodeId = NodeId;
760 Request->Uid = 0;
761 Request->Gid = 0;
762 Request->Pid = 1;
763 Request->Padding = 0;
764
765 return EFI_SUCCESS;
766 }
767
768 /**
769 Check the common FUSE response format.
770
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.
775
776 This function may only be called after VirtioFsSgListsSubmit() returns
777 successfully.
778
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
782 virtio transfer.
783
784 @param[in] RequestId The request identifier to which the response is
785 expected to belong.
786
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.
792
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.
796
797 @retval EFI_INVALID_PARAMETER The allocated size of the first buffer does
798 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
799
800 @retval EFI_PROTOCOL_ERROR The VIRTIO_FS_FUSE_RESPONSE structure in the
801 first buffer has not been fully populated.
802
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).
806
807 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
808 buffer does not equal RequestId.
809
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.
813
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.
819
820 @retval EFI_SUCCESS Verification successful.
821 **/
822 EFI_STATUS
823 VirtioFsFuseCheckResponse (
824 IN VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
825 IN UINT64 RequestId,
826 OUT UINTN *TailBufferFill
827 )
828 {
829 UINTN NumFixedSizeVec;
830 VIRTIO_FS_FUSE_RESPONSE *CommonResp;
831 UINT32 TotalTransferred;
832 UINTN Idx;
833
834 //
835 // Ensured by VirtioFsSgListsValidate().
836 //
837 ASSERT (ResponseSgList->NumVec > 0);
838
839 if (TailBufferFill == NULL) {
840 //
841 // All buffers are considered fixed size.
842 //
843 NumFixedSizeVec = ResponseSgList->NumVec;
844 } else {
845 //
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.
849 //
850 if (ResponseSgList->NumVec == 1) {
851 return EFI_INVALID_PARAMETER;
852 }
853 NumFixedSizeVec = ResponseSgList->NumVec - 1;
854 }
855
856 //
857 // The first buffer is supposed to carry a (fully populated)
858 // VIRTIO_FS_FUSE_RESPONSE structure.
859 //
860 if (ResponseSgList->IoVec[0].Size != sizeof *CommonResp) {
861 return EFI_INVALID_PARAMETER;
862 }
863 if (ResponseSgList->IoVec[0].Transferred != ResponseSgList->IoVec[0].Size) {
864 return EFI_PROTOCOL_ERROR;
865 }
866
867 //
868 // FUSE must report the same number of bytes, written by the Virtio
869 // Filesystem device, as the virtio transport does.
870 //
871 CommonResp = ResponseSgList->IoVec[0].Buffer;
872 TotalTransferred = 0;
873 for (Idx = 0; Idx < ResponseSgList->NumVec; Idx++) {
874 //
875 // Integer overflow and truncation are not possible, based on
876 // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
877 //
878 TotalTransferred += (UINT32)ResponseSgList->IoVec[Idx].Transferred;
879 }
880 if (CommonResp->Len != TotalTransferred) {
881 return EFI_PROTOCOL_ERROR;
882 }
883
884 //
885 // Enforce that FUSE match our request ID in the response.
886 //
887 if (CommonResp->Unique != RequestId) {
888 return EFI_PROTOCOL_ERROR;
889 }
890
891 //
892 // If there is an explicit error report, skip checking the transfer
893 // counts for the rest of the fixed size buffers.
894 //
895 if (CommonResp->Error != 0) {
896 return EFI_DEVICE_ERROR;
897 }
898
899 //
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).
903 //
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;
909 }
910 }
911
912 //
913 // If the last buffer is considered variable size, report its filled size.
914 //
915 if (TailBufferFill != NULL) {
916 *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;
917 }
918
919 return EFI_SUCCESS;
920 }
921
922 /**
923 An ad-hoc function for mapping FUSE (well, Linux) "errno" values to
924 EFI_STATUS.
925
926 @param[in] Errno The "VIRTIO_FS_FUSE_RESPONSE.Error" value, returned by the
927 Virtio Filesystem device. The value is expected to be
928 negative.
929
930 @return An EFI_STATUS error code that's deemed a passable
931 mapping for the Errno value.
932
933 @retval EFI_DEVICE_ERROR Fallback EFI_STATUS code for unrecognized Errno
934 values.
935 **/
936 EFI_STATUS
937 VirtioFsErrnoToEfiStatus (
938 IN INT32 Errno
939 )
940 {
941 switch (Errno) {
942 case -1: // EPERM Operation not permitted
943 return EFI_SECURITY_VIOLATION;
944
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;
955
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;
966
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;
975
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;
980
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;
1006
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;
1017
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;
1034
1035 case -13: // EACCES Permission denied
1036 return EFI_ACCESS_DENIED;
1037
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;
1044
1045 case -22: // EINVAL Invalid argument
1046 case -33: // EDOM Numerical argument out of domain
1047 return EFI_INVALID_PARAMETER;
1048
1049 case -28: // ENOSPC No space left on device
1050 case -54: // EXFULL Exchange full
1051 return EFI_VOLUME_FULL;
1052
1053 case -30: // EROFS Read-only file system
1054 return EFI_WRITE_PROTECTED;
1055
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
1074 return EFI_ABORTED;
1075
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;
1079
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;
1084
1085 case -62: // ETIME Timer expired
1086 case -110: // ETIMEDOUT Connection timed out
1087 case -127: // EKEYEXPIRED Key has expired
1088 return EFI_TIMEOUT;
1089
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;
1100
1101 case -71: // EPROTO Protocol error
1102 return EFI_PROTOCOL_ERROR;
1103
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;
1110
1111 case -89: // EDESTADDRREQ Destination address required
1112 case -107: // ENOTCONN Transport endpoint is not connected
1113 return EFI_NOT_STARTED;
1114
1115 default:
1116 break;
1117 }
1118
1119 return EFI_DEVICE_ERROR;
1120 }
1121
1122 //
1123 // Parser states for canonicalizing a POSIX pathname.
1124 //
1125 typedef enum {
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
1132 } PARSER_STATE;
1133
1134 /**
1135 Strip the trailing slash from the parser's output buffer, unless the trailing
1136 slash stands for the root directory.
1137
1138 @param[in] Buffer The parser's output buffer. Only used for
1139 sanity-checking.
1140
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
1148 has no effect.
1149 **/
1150 STATIC
1151 VOID
1152 ParserStripSlash (
1153 IN CHAR8 *Buffer,
1154 IN OUT UINTN *Position
1155 )
1156 {
1157 ASSERT (*Position >= 1);
1158 ASSERT (Buffer[*Position - 1] == '/');
1159 if (*Position == 1) {
1160 return;
1161 }
1162 (*Position)--;
1163 }
1164
1165 /**
1166 Produce one character in the parser's output buffer.
1167
1168 @param[out] Buffer The parser's output buffer. On return, Char8 will
1169 have been written.
1170
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
1174 incremented by one.
1175
1176 @param[in] Size Total allocated size of the parser's output buffer.
1177 Used for sanity-checking.
1178
1179 @param[in] Char8 The character to place in the output buffer.
1180 **/
1181 STATIC
1182 VOID
1183 ParserCopy (
1184 OUT CHAR8 *Buffer,
1185 IN OUT UINTN *Position,
1186 IN UINTN Size,
1187 IN CHAR8 Char8
1188 )
1189 {
1190 ASSERT (*Position < Size);
1191 Buffer[(*Position)++] = Char8;
1192 }
1193
1194 /**
1195 Rewind the last single-dot in the parser's output buffer.
1196
1197 @param[in] Buffer The parser's output buffer. Only used for
1198 sanity-checking.
1199
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.
1207 **/
1208 STATIC
1209 VOID
1210 ParserRewindDot (
1211 IN CHAR8 *Buffer,
1212 IN OUT UINTN *Position
1213 )
1214 {
1215 ASSERT (*Position >= 2);
1216 ASSERT (Buffer[*Position - 1] == '.');
1217 ASSERT (Buffer[*Position - 2] == '/');
1218 (*Position)--;
1219 }
1220
1221 /**
1222 Rewind the last dot-dot in the parser's output buffer.
1223
1224 @param[in] Buffer The parser's output buffer. Only used for
1225 sanity-checking.
1226
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
1234 buffer.
1235
1236 If this slash character stands for the root
1237 directory, then RootEscape is set to TRUE.
1238
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.
1246
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.
1250 **/
1251 STATIC
1252 VOID
1253 ParserRewindDotDot (
1254 IN CHAR8 *Buffer,
1255 IN OUT UINTN *Position,
1256 OUT BOOLEAN *RootEscape
1257
1258 )
1259 {
1260 ASSERT (*Position >= 3);
1261 ASSERT (Buffer[*Position - 1] == '.');
1262 ASSERT (Buffer[*Position - 2] == '.');
1263 ASSERT (Buffer[*Position - 3] == '/');
1264 (*Position) -= 2;
1265
1266 if (*Position == 1) {
1267 //
1268 // Root directory slash reached; don't try to climb higher.
1269 //
1270 *RootEscape = TRUE;
1271 return;
1272 }
1273
1274 //
1275 // Skip slash.
1276 //
1277 (*Position)--;
1278 //
1279 // Scan until next slash to the left.
1280 //
1281 do {
1282 ASSERT (*Position > 0);
1283 (*Position)--;
1284 } while (Buffer[*Position] != '/');
1285 (*Position)++;
1286 }
1287
1288 /**
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.
1292
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
1302 directory,
1303 - the length of ResultPath is at most VIRTIO_FS_MAX_PATHNAME_LENGTH.
1304
1305 Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
1306 reported through RootEscape, without failing the function call.
1307
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.
1311
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
1320 ResultPath8.
1321
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.
1325
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.
1329
1330 @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
1331 been output.
1332
1333 @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
1334
1335 @retval EFI_INVALID_PARAMETER RhsPath16 failed the
1336 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1337
1338 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1339
1340 @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
1341 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1342
1343 @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
1344 falls outside of the printable ASCII set, or
1345 is a forward slash.
1346 **/
1347 EFI_STATUS
1348 VirtioFsAppendPath (
1349 IN CHAR8 *LhsPath8,
1350 IN CHAR16 *RhsPath16,
1351 OUT CHAR8 **ResultPath8,
1352 OUT BOOLEAN *RootEscape
1353 )
1354 {
1355 UINTN RhsLen;
1356 CHAR8 *RhsPath8;
1357 UINTN Idx;
1358 EFI_STATUS Status;
1359 UINTN SizeToSanitize;
1360 CHAR8 *BufferToSanitize;
1361 CHAR8 *SanitizedBuffer;
1362 PARSER_STATE State;
1363 UINTN SanitizedPosition;
1364
1365 //
1366 // Appending an empty pathname is not allowed.
1367 //
1368 RhsLen = StrLen (RhsPath16);
1369 if (RhsLen == 0) {
1370 return EFI_INVALID_PARAMETER;
1371 }
1372 //
1373 // Enforce length restriction on RhsPath16.
1374 //
1375 if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
1376 return EFI_INVALID_PARAMETER;
1377 }
1378
1379 //
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.
1385 //
1386 RhsPath8 = AllocatePool (RhsLen + 1);
1387 if (RhsPath8 == NULL) {
1388 return EFI_OUT_OF_RESOURCES;
1389 }
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;
1394 goto FreeRhsPath8;
1395 }
1396 RhsPath8[Idx] = (CHAR8)((RhsPath16[Idx] == L'\\') ? L'/' : RhsPath16[Idx]);
1397 }
1398 RhsPath8[Idx++] = '\0';
1399
1400 //
1401 // Now prepare the input for the canonicalization (squashing of sequences of
1402 // forward slashes, and eliminating . (dot) and .. (dot-dot) pathname
1403 // components).
1404 //
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.
1408 //
1409 if (RhsPath8[0] == '/') {
1410 //
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.
1413 //
1414 SizeToSanitize = RhsLen + 1;
1415 BufferToSanitize = RhsPath8;
1416 } else {
1417 //
1418 // If the right hand side path is relative, then it is appended (naively)
1419 // to the left hand side.
1420 //
1421 UINTN LhsLen;
1422
1423 LhsLen = AsciiStrLen (LhsPath8);
1424 SizeToSanitize = LhsLen + 1 + RhsLen + 1;
1425 BufferToSanitize = AllocatePool (SizeToSanitize);
1426 if (BufferToSanitize == NULL) {
1427 Status = EFI_OUT_OF_RESOURCES;
1428 goto FreeRhsPath8;
1429 }
1430 CopyMem (BufferToSanitize, LhsPath8, LhsLen);
1431 BufferToSanitize[LhsLen] = '/';
1432 CopyMem (BufferToSanitize + LhsLen + 1, RhsPath8, RhsLen + 1);
1433 }
1434
1435 //
1436 // Allocate the output buffer.
1437 //
1438 SanitizedBuffer = AllocatePool (SizeToSanitize);
1439 if (SanitizedBuffer == NULL) {
1440 Status = EFI_OUT_OF_RESOURCES;
1441 goto FreeBufferToSanitize;
1442 }
1443
1444 //
1445 // State machine for parsing the input and producing the canonical output
1446 // follows.
1447 //
1448 *RootEscape = FALSE;
1449 Idx = 0;
1450 State = ParserInit;
1451 SanitizedPosition = 0;
1452 do {
1453 CHAR8 Chr8;
1454
1455 ASSERT (Idx < SizeToSanitize);
1456 Chr8 = BufferToSanitize[Idx++];
1457
1458 switch (State) {
1459 case ParserInit: // just starting
1460 ASSERT (Chr8 == '/');
1461 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1462 State = ParserSlash;
1463 break;
1464
1465 case ParserSlash: // slash(es) seen
1466 switch (Chr8) {
1467 case '\0':
1468 ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
1469 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1470 State = ParserEnd;
1471 break;
1472 case '/':
1473 //
1474 // skip & stay in same state
1475 //
1476 break;
1477 case '.':
1478 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1479 State = ParserDot;
1480 break;
1481 default:
1482 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1483 State = ParserNormal;
1484 break;
1485 }
1486 break;
1487
1488 case ParserDot: // one dot seen since last slash
1489 switch (Chr8) {
1490 case '\0':
1491 ParserRewindDot (SanitizedBuffer, &SanitizedPosition);
1492 ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
1493 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1494 State = ParserEnd;
1495 break;
1496 case '/':
1497 ParserRewindDot (SanitizedBuffer, &SanitizedPosition);
1498 State = ParserSlash;
1499 break;
1500 case '.':
1501 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1502 State = ParserDotDot;
1503 break;
1504 default:
1505 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1506 State = ParserNormal;
1507 break;
1508 }
1509 break;
1510
1511 case ParserDotDot: // two dots seen since last slash
1512 switch (Chr8) {
1513 case '\0':
1514 ParserRewindDotDot (SanitizedBuffer, &SanitizedPosition, RootEscape);
1515 ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
1516 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1517 State = ParserEnd;
1518 break;
1519 case '/':
1520 ParserRewindDotDot (SanitizedBuffer, &SanitizedPosition, RootEscape);
1521 State = ParserSlash;
1522 break;
1523 case '.':
1524 //
1525 // fall through
1526 //
1527 default:
1528 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1529 State = ParserNormal;
1530 break;
1531 }
1532 break;
1533
1534 case ParserNormal: // a different sequence seen
1535 switch (Chr8) {
1536 case '\0':
1537 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1538 State = ParserEnd;
1539 break;
1540 case '/':
1541 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1542 State = ParserSlash;
1543 break;
1544 case '.':
1545 //
1546 // fall through
1547 //
1548 default:
1549 //
1550 // copy and stay in same state
1551 //
1552 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1553 break;
1554 }
1555 break;
1556
1557 default:
1558 ASSERT (FALSE);
1559 break;
1560 }
1561 } while (State != ParserEnd);
1562
1563 //
1564 // Ensure length invariant on ResultPath8.
1565 //
1566 ASSERT (SanitizedPosition >= 2);
1567 if (SanitizedPosition - 1 > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
1568 Status = EFI_OUT_OF_RESOURCES;
1569 goto FreeSanitizedBuffer;
1570 }
1571
1572 *ResultPath8 = SanitizedBuffer;
1573 SanitizedBuffer = NULL;
1574 Status = EFI_SUCCESS;
1575 //
1576 // Fall through.
1577 //
1578 FreeSanitizedBuffer:
1579 if (SanitizedBuffer != NULL) {
1580 FreePool (SanitizedBuffer);
1581 }
1582
1583 FreeBufferToSanitize:
1584 if (RhsPath8[0] != '/') {
1585 FreePool (BufferToSanitize);
1586 }
1587
1588 FreeRhsPath8:
1589 FreePool (RhsPath8);
1590 return Status;
1591 }