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