]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioFsDxe/Helpers.c
OvmfPkg: Apply uncrustify changes
[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
129 if ((Features & VIRTIO_F_VERSION_1) == 0) {
130 Status = EFI_UNSUPPORTED;
131 goto Failed;
132 }
133
134 //
135 // No device-specific feature bits have been defined in file "virtio-fs.tex"
136 // of the virtio spec at <https://github.com/oasis-tcs/virtio-spec.git>, as
137 // of commit 87fa6b5d8155.
138 //
139 Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM;
140
141 //
142 // ... and write the subset of feature bits understood by the [...] driver to
143 // the device. [...]
144 // 5. Set the FEATURES_OK status bit.
145 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
146 //
147 Status = Virtio10WriteFeatures (VirtioFs->Virtio, Features, &NextDevStat);
148 if (EFI_ERROR (Status)) {
149 goto Failed;
150 }
151
152 //
153 // 7. Perform device-specific setup, including discovery of virtqueues for
154 // the device, [...] reading [...] the device's virtio configuration space
155 //
156 Status = VirtioFsReadConfig (VirtioFs->Virtio, &Config);
157 if (EFI_ERROR (Status)) {
158 goto Failed;
159 }
160
161 //
162 // 7.a. Convert the filesystem label from UTF-8 to UCS-2. Only labels with
163 // printable ASCII code points (U+0020 through U+007E) are supported.
164 // NUL-terminate at either the terminator we find, or right after the
165 // original label.
166 //
167 for (Idx = 0; Idx < VIRTIO_FS_TAG_BYTES && Config.Tag[Idx] != '\0'; Idx++) {
168 if ((Config.Tag[Idx] < 0x20) || (Config.Tag[Idx] > 0x7E)) {
169 Status = EFI_UNSUPPORTED;
170 goto Failed;
171 }
172
173 VirtioFs->Label[Idx] = Config.Tag[Idx];
174 }
175
176 VirtioFs->Label[Idx] = L'\0';
177
178 //
179 // 7.b. We need one queue for sending normal priority requests.
180 //
181 if (Config.NumReqQueues < 1) {
182 Status = EFI_UNSUPPORTED;
183 goto Failed;
184 }
185
186 //
187 // 7.c. Fetch and remember the number of descriptors we can place on the
188 // queue at once. We'll need two descriptors per request, as a minimum --
189 // request header, response header.
190 //
191 Status = VirtioFs->Virtio->SetQueueSel (
192 VirtioFs->Virtio,
193 VIRTIO_FS_REQUEST_QUEUE
194 );
195 if (EFI_ERROR (Status)) {
196 goto Failed;
197 }
198
199 Status = VirtioFs->Virtio->GetQueueNumMax (
200 VirtioFs->Virtio,
201 &VirtioFs->QueueSize
202 );
203 if (EFI_ERROR (Status)) {
204 goto Failed;
205 }
206
207 if (VirtioFs->QueueSize < 2) {
208 Status = EFI_UNSUPPORTED;
209 goto Failed;
210 }
211
212 //
213 // 7.d. [...] population of virtqueues [...]
214 //
215 Status = VirtioRingInit (
216 VirtioFs->Virtio,
217 VirtioFs->QueueSize,
218 &VirtioFs->Ring
219 );
220 if (EFI_ERROR (Status)) {
221 goto Failed;
222 }
223
224 Status = VirtioRingMap (
225 VirtioFs->Virtio,
226 &VirtioFs->Ring,
227 &RingBaseShift,
228 &VirtioFs->RingMap
229 );
230 if (EFI_ERROR (Status)) {
231 goto ReleaseQueue;
232 }
233
234 Status = VirtioFs->Virtio->SetQueueAddress (
235 VirtioFs->Virtio,
236 &VirtioFs->Ring,
237 RingBaseShift
238 );
239 if (EFI_ERROR (Status)) {
240 goto UnmapQueue;
241 }
242
243 //
244 // 8. Set the DRIVER_OK status bit.
245 //
246 NextDevStat |= VSTAT_DRIVER_OK;
247 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
248 if (EFI_ERROR (Status)) {
249 goto UnmapQueue;
250 }
251
252 return EFI_SUCCESS;
253
254 UnmapQueue:
255 VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
256
257 ReleaseQueue:
258 VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
259
260 Failed:
261 //
262 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
263 // FAILED status bit to indicate that it has given up on the device (it can
264 // reset the device later to restart if desired). [...]
265 //
266 // Virtio access failure here should not mask the original error.
267 //
268 NextDevStat |= VSTAT_FAILED;
269 VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
270
271 return Status;
272 }
273
274 /**
275 De-configure the Virtio Filesystem device underlying VirtioFs.
276
277 @param[in] VirtioFs The VIRTIO_FS object for which Virtio communication
278 should be torn down. On input, the caller is responsible
279 for having called VirtioFsInit(). On output, Virtio
280 Filesystem commands (primitives) must no longer be
281 submitted to the device.
282 **/
283 VOID
284 VirtioFsUninit (
285 IN OUT VIRTIO_FS *VirtioFs
286 )
287 {
288 //
289 // Resetting the Virtio device makes it release its resources and forget its
290 // configuration.
291 //
292 VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
293 VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
294 VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
295 }
296
297 /**
298 ExitBootServices event notification function for a Virtio Filesystem object.
299
300 This function resets the VIRTIO_FS.Virtio device, causing it to release all
301 references to guest-side resources. The function may only be called after
302 VirtioFsInit() returns successfully and before VirtioFsUninit() is called.
303
304 @param[in] ExitBootEvent The VIRTIO_FS.ExitBoot event that has been
305 signaled.
306
307 @param[in] VirtioFsAsVoid Pointer to the VIRTIO_FS object, passed in as
308 (VOID*).
309 **/
310 VOID
311 EFIAPI
312 VirtioFsExitBoot (
313 IN EFI_EVENT ExitBootEvent,
314 IN VOID *VirtioFsAsVoid
315 )
316 {
317 VIRTIO_FS *VirtioFs;
318
319 VirtioFs = VirtioFsAsVoid;
320 DEBUG ((
321 DEBUG_VERBOSE,
322 "%a: VirtioFs=0x%p Label=\"%s\"\n",
323 __FUNCTION__,
324 VirtioFsAsVoid,
325 VirtioFs->Label
326 ));
327 VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
328 }
329
330 /**
331 Validate two VIRTIO_FS_SCATTER_GATHER_LIST objects -- list of request
332 buffers, list of response buffers -- together.
333
334 On input, the caller is required to populate the following fields:
335 - VIRTIO_FS_IO_VECTOR.Buffer,
336 - VIRTIO_FS_IO_VECTOR.Size,
337 - VIRTIO_FS_SCATTER_GATHER_LIST.IoVec,
338 - VIRTIO_FS_SCATTER_GATHER_LIST.NumVec.
339
340 On output (on successful return), the following fields will be
341 zero-initialized:
342 - VIRTIO_FS_IO_VECTOR.Mapped,
343 - VIRTIO_FS_IO_VECTOR.MappedAddress,
344 - VIRTIO_FS_IO_VECTOR.Mapping,
345 - VIRTIO_FS_IO_VECTOR.Transferred.
346
347 On output (on successful return), the following fields will be calculated:
348 - VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize.
349
350 The function may only be called after VirtioFsInit() returns successfully and
351 before VirtioFsUninit() is called.
352
353 @param[in] VirtioFs The Virtio Filesystem device that the
354 request-response exchange, expressed via
355 RequestSgList and ResponseSgList, will be
356 submitted to.
357
358 @param[in,out] RequestSgList The scatter-gather list that describes the
359 request part of the exchange -- the buffers
360 that should be sent to the Virtio Filesystem
361 device in the virtio transfer.
362
363 @param[in,out] ResponseSgList The scatter-gather list that describes the
364 response part of the exchange -- the buffers
365 that the Virtio Filesystem device should
366 populate in the virtio transfer. May be NULL
367 if the exchange with the Virtio Filesystem
368 device consists of a request only, with the
369 response part omitted altogether.
370
371 @retval EFI_SUCCESS RequestSgList and ResponseSgList have been
372 validated, output fields have been set.
373
374 @retval EFI_INVALID_PARAMETER RequestSgList is NULL.
375
376 @retval EFI_INVALID_PARAMETER On input, a
377 VIRTIO_FS_SCATTER_GATHER_LIST.IoVec field is
378 NULL, or a
379 VIRTIO_FS_SCATTER_GATHER_LIST.NumVec field is
380 zero.
381
382 @retval EFI_INVALID_PARAMETER On input, a VIRTIO_FS_IO_VECTOR.Buffer field
383 is NULL, or a VIRTIO_FS_IO_VECTOR.Size field
384 is zero.
385
386 @retval EFI_UNSUPPORTED (RequestSgList->NumVec +
387 ResponseSgList->NumVec) exceeds
388 VirtioFs->QueueSize, meaning that the total
389 list of buffers cannot be placed on the virtio
390 queue in a single descriptor chain (with one
391 descriptor per buffer).
392
393 @retval EFI_UNSUPPORTED One of the
394 VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize fields
395 would exceed MAX_UINT32.
396 **/
397 EFI_STATUS
398 VirtioFsSgListsValidate (
399 IN VIRTIO_FS *VirtioFs,
400 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
401 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
402 )
403 {
404 VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];
405 UINT16 DescriptorsNeeded;
406 UINTN ListId;
407
408 if (RequestSgList == NULL) {
409 return EFI_INVALID_PARAMETER;
410 }
411
412 SgListParam[0] = RequestSgList;
413 SgListParam[1] = ResponseSgList;
414
415 DescriptorsNeeded = 0;
416 for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
417 VIRTIO_FS_SCATTER_GATHER_LIST *SgList;
418 UINT32 SgListTotalSize;
419 UINTN IoVecIdx;
420
421 SgList = SgListParam[ListId];
422 if (SgList == NULL) {
423 continue;
424 }
425
426 //
427 // Sanity-check SgList -- it must provide at least one IO Vector.
428 //
429 if ((SgList->IoVec == NULL) || (SgList->NumVec == 0)) {
430 return EFI_INVALID_PARAMETER;
431 }
432
433 //
434 // Make sure that, for each IO Vector in this SgList, a virtio descriptor
435 // can be added to the virtio queue, after the other descriptors added
436 // previously.
437 //
438 if ((SgList->NumVec > (UINTN)(MAX_UINT16 - DescriptorsNeeded)) ||
439 (DescriptorsNeeded + SgList->NumVec > VirtioFs->QueueSize))
440 {
441 return EFI_UNSUPPORTED;
442 }
443
444 DescriptorsNeeded += (UINT16)SgList->NumVec;
445
446 SgListTotalSize = 0;
447 for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
448 VIRTIO_FS_IO_VECTOR *IoVec;
449
450 IoVec = &SgList->IoVec[IoVecIdx];
451 //
452 // Sanity-check this IoVec -- it must describe a non-empty buffer.
453 //
454 if ((IoVec->Buffer == NULL) || (IoVec->Size == 0)) {
455 return EFI_INVALID_PARAMETER;
456 }
457
458 //
459 // Make sure the cumulative size of all IO Vectors in this SgList remains
460 // expressible as a UINT32.
461 //
462 if (IoVec->Size > MAX_UINT32 - SgListTotalSize) {
463 return EFI_UNSUPPORTED;
464 }
465
466 SgListTotalSize += (UINT32)IoVec->Size;
467
468 //
469 // Initialize those fields in this IO Vector that will be updated in
470 // relation to mapping / transfer.
471 //
472 IoVec->Mapped = FALSE;
473 IoVec->MappedAddress = 0;
474 IoVec->Mapping = NULL;
475 IoVec->Transferred = 0;
476 }
477
478 //
479 // Store the cumulative size of all IO Vectors that we have calculated in
480 // this SgList.
481 //
482 SgList->TotalSize = SgListTotalSize;
483 }
484
485 return EFI_SUCCESS;
486 }
487
488 /**
489 Submit a validated pair of (request buffer list, response buffer list) to the
490 Virtio Filesystem device.
491
492 On input, the pair of VIRTIO_FS_SCATTER_GATHER_LIST objects must have been
493 validated together, using the VirtioFsSgListsValidate() function.
494
495 On output (on successful return), the following fields will be re-initialized
496 to zero (after temporarily setting them to different values):
497 - VIRTIO_FS_IO_VECTOR.Mapped,
498 - VIRTIO_FS_IO_VECTOR.MappedAddress,
499 - VIRTIO_FS_IO_VECTOR.Mapping.
500
501 On output (on successful return), the following fields will be calculated:
502 - VIRTIO_FS_IO_VECTOR.Transferred.
503
504 The function may only be called after VirtioFsInit() returns successfully and
505 before VirtioFsUninit() is called.
506
507 @param[in,out] VirtioFs The Virtio Filesystem device that the
508 request-response exchange, expressed via
509 RequestSgList and ResponseSgList, should now
510 be submitted to.
511
512 @param[in,out] RequestSgList The scatter-gather list that describes the
513 request part of the exchange -- the buffers
514 that should be sent to the Virtio Filesystem
515 device in the virtio transfer.
516
517 @param[in,out] ResponseSgList The scatter-gather list that describes the
518 response part of the exchange -- the buffers
519 that the Virtio Filesystem device should
520 populate in the virtio transfer. May be NULL
521 if and only if NULL was passed to
522 VirtioFsSgListsValidate() as ResponseSgList.
523
524 @retval EFI_SUCCESS Transfer complete. The caller should investigate
525 the VIRTIO_FS_IO_VECTOR.Transferred fields in
526 ResponseSgList, to ensure coverage of the relevant
527 response buffers. Subsequently, the caller should
528 investigate the contents of those buffers.
529
530 @retval EFI_DEVICE_ERROR The Virtio Filesystem device reported populating
531 more response bytes than ResponseSgList->TotalSize.
532
533 @return Error codes propagated from
534 VirtioMapAllBytesInSharedBuffer(), VirtioFlush(),
535 or VirtioFs->Virtio->UnmapSharedBuffer().
536 **/
537 EFI_STATUS
538 VirtioFsSgListsSubmit (
539 IN OUT VIRTIO_FS *VirtioFs,
540 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
541 IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
542 )
543 {
544 VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];
545 VIRTIO_MAP_OPERATION SgListVirtioMapOp[ARRAY_SIZE (SgListParam)];
546 UINT16 SgListDescriptorFlag[ARRAY_SIZE (SgListParam)];
547 UINTN ListId;
548 VIRTIO_FS_SCATTER_GATHER_LIST *SgList;
549 UINTN IoVecIdx;
550 VIRTIO_FS_IO_VECTOR *IoVec;
551 EFI_STATUS Status;
552 DESC_INDICES Indices;
553 UINT32 TotalBytesWrittenByDevice;
554 UINT32 BytesPermittedForWrite;
555
556 SgListParam[0] = RequestSgList;
557 SgListVirtioMapOp[0] = VirtioOperationBusMasterRead;
558 SgListDescriptorFlag[0] = 0;
559
560 SgListParam[1] = ResponseSgList;
561 SgListVirtioMapOp[1] = VirtioOperationBusMasterWrite;
562 SgListDescriptorFlag[1] = VRING_DESC_F_WRITE;
563
564 //
565 // Map all IO Vectors.
566 //
567 for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
568 SgList = SgListParam[ListId];
569 if (SgList == NULL) {
570 continue;
571 }
572
573 for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
574 IoVec = &SgList->IoVec[IoVecIdx];
575 //
576 // Map this IO Vector.
577 //
578 Status = VirtioMapAllBytesInSharedBuffer (
579 VirtioFs->Virtio,
580 SgListVirtioMapOp[ListId],
581 IoVec->Buffer,
582 IoVec->Size,
583 &IoVec->MappedAddress,
584 &IoVec->Mapping
585 );
586 if (EFI_ERROR (Status)) {
587 goto Unmap;
588 }
589
590 IoVec->Mapped = TRUE;
591 }
592 }
593
594 //
595 // Compose the descriptor chain.
596 //
597 VirtioPrepare (&VirtioFs->Ring, &Indices);
598 for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
599 SgList = SgListParam[ListId];
600 if (SgList == NULL) {
601 continue;
602 }
603
604 for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
605 UINT16 NextFlag;
606
607 IoVec = &SgList->IoVec[IoVecIdx];
608 //
609 // Set VRING_DESC_F_NEXT on all except the very last descriptor.
610 //
611 NextFlag = VRING_DESC_F_NEXT;
612 if ((ListId == ARRAY_SIZE (SgListParam) - 1) &&
613 (IoVecIdx == SgList->NumVec - 1))
614 {
615 NextFlag = 0;
616 }
617
618 VirtioAppendDesc (
619 &VirtioFs->Ring,
620 IoVec->MappedAddress,
621 (UINT32)IoVec->Size,
622 SgListDescriptorFlag[ListId] | NextFlag,
623 &Indices
624 );
625 }
626 }
627
628 //
629 // Submit the descriptor chain.
630 //
631 Status = VirtioFlush (
632 VirtioFs->Virtio,
633 VIRTIO_FS_REQUEST_QUEUE,
634 &VirtioFs->Ring,
635 &Indices,
636 &TotalBytesWrittenByDevice
637 );
638 if (EFI_ERROR (Status)) {
639 goto Unmap;
640 }
641
642 //
643 // Sanity-check: the Virtio Filesystem device should not have written more
644 // bytes than what we offered buffers for.
645 //
646 if (ResponseSgList == NULL) {
647 BytesPermittedForWrite = 0;
648 } else {
649 BytesPermittedForWrite = ResponseSgList->TotalSize;
650 }
651
652 if (TotalBytesWrittenByDevice > BytesPermittedForWrite) {
653 Status = EFI_DEVICE_ERROR;
654 goto Unmap;
655 }
656
657 //
658 // Update the transfer sizes in the IO Vectors.
659 //
660 for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
661 SgList = SgListParam[ListId];
662 if (SgList == NULL) {
663 continue;
664 }
665
666 for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
667 IoVec = &SgList->IoVec[IoVecIdx];
668 if (SgListVirtioMapOp[ListId] == VirtioOperationBusMasterRead) {
669 //
670 // We report that the Virtio Filesystem device has read all buffers in
671 // the request.
672 //
673 IoVec->Transferred = IoVec->Size;
674 } else {
675 //
676 // Regarding the response, calculate how much of the current IO Vector
677 // has been populated by the Virtio Filesystem device. In
678 // "TotalBytesWrittenByDevice", VirtioFlush() reported the total count
679 // across all device-writeable descriptors, in the order they were
680 // chained on the ring.
681 //
682 IoVec->Transferred = MIN (
683 (UINTN)TotalBytesWrittenByDevice,
684 IoVec->Size
685 );
686 TotalBytesWrittenByDevice -= (UINT32)IoVec->Transferred;
687 }
688 }
689 }
690
691 //
692 // By now, "TotalBytesWrittenByDevice" has been exhausted.
693 //
694 ASSERT (TotalBytesWrittenByDevice == 0);
695
696 //
697 // We've succeeded; fall through.
698 //
699 Unmap:
700 //
701 // Unmap all mapped IO Vectors on both the success and the error paths. The
702 // unmapping occurs in reverse order of mapping, in an attempt to avoid
703 // memory fragmentation.
704 //
705 ListId = ARRAY_SIZE (SgListParam);
706 while (ListId > 0) {
707 --ListId;
708 SgList = SgListParam[ListId];
709 if (SgList == NULL) {
710 continue;
711 }
712
713 IoVecIdx = SgList->NumVec;
714 while (IoVecIdx > 0) {
715 EFI_STATUS UnmapStatus;
716
717 --IoVecIdx;
718 IoVec = &SgList->IoVec[IoVecIdx];
719 //
720 // Unmap this IO Vector, if it has been mapped.
721 //
722 if (!IoVec->Mapped) {
723 continue;
724 }
725
726 UnmapStatus = VirtioFs->Virtio->UnmapSharedBuffer (
727 VirtioFs->Virtio,
728 IoVec->Mapping
729 );
730 //
731 // Re-set the following fields to the values they initially got from
732 // VirtioFsSgListsValidate() -- the above unmapping attempt is considered
733 // final, even if it fails.
734 //
735 IoVec->Mapped = FALSE;
736 IoVec->MappedAddress = 0;
737 IoVec->Mapping = NULL;
738
739 //
740 // If we are on the success path, but the unmapping failed, we need to
741 // transparently flip to the failure path -- the caller must learn they
742 // should not consult the response buffers.
743 //
744 // The branch below can be taken at most once.
745 //
746 if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {
747 Status = UnmapStatus;
748 }
749 }
750 }
751
752 return Status;
753 }
754
755 /**
756 Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
757
758 The function may only be called after VirtioFsInit() returns successfully and
759 before VirtioFsUninit() is called.
760
761 @param[in,out] VirtioFs The Virtio Filesystem device that the request is
762 being prepared for. The "VirtioFs->RequestId" field
763 will be copied into "Request->Unique". On output (on
764 successful return), "VirtioFs->RequestId" will be
765 incremented.
766
767 @param[out] Request The VIRTIO_FS_FUSE_REQUEST object whose fields are to
768 be set.
769
770 @param[in] RequestSize The total size of the request, including
771 sizeof(VIRTIO_FS_FUSE_REQUEST).
772
773 @param[in] Opcode The VIRTIO_FS_FUSE_OPCODE that identifies the command
774 to send.
775
776 @param[in] NodeId The inode number of the file that the request refers
777 to. When Opcode is VirtioFsFuseOpInit, NodeId is
778 ignored by the Virtio Filesystem device.
779
780 @retval EFI_INVALID_PARAMETER RequestSize is smaller than
781 sizeof(VIRTIO_FS_FUSE_REQUEST).
782
783 @retval EFI_OUT_OF_RESOURCES "VirtioFs->RequestId" is MAX_UINT64, and can
784 be incremented no more.
785
786 @retval EFI_SUCCESS Request has been populated,
787 "VirtioFs->RequestId" has been incremented.
788 **/
789 EFI_STATUS
790 VirtioFsFuseNewRequest (
791 IN OUT VIRTIO_FS *VirtioFs,
792 OUT VIRTIO_FS_FUSE_REQUEST *Request,
793 IN UINT32 RequestSize,
794 IN VIRTIO_FS_FUSE_OPCODE Opcode,
795 IN UINT64 NodeId
796 )
797 {
798 if (RequestSize < sizeof *Request) {
799 return EFI_INVALID_PARAMETER;
800 }
801
802 if (VirtioFs->RequestId == MAX_UINT64) {
803 return EFI_OUT_OF_RESOURCES;
804 }
805
806 Request->Len = RequestSize;
807 Request->Opcode = Opcode;
808 Request->Unique = VirtioFs->RequestId++;
809 Request->NodeId = NodeId;
810 Request->Uid = 0;
811 Request->Gid = 0;
812 Request->Pid = 1;
813 Request->Padding = 0;
814
815 return EFI_SUCCESS;
816 }
817
818 /**
819 Check the common FUSE response format.
820
821 The first buffer in the response scatter-gather list is assumed a
822 VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to
823 and excluding the last one, are assumed fixed size. The last response buffer
824 may or may not be fixed size, as specified by the caller.
825
826 This function may only be called after VirtioFsSgListsSubmit() returns
827 successfully.
828
829 @param[in] ResponseSgList The scatter-gather list that describes the
830 response part of the exchange -- the buffers that
831 the Virtio Filesystem device filled in during the
832 virtio transfer.
833
834 @param[in] RequestId The request identifier to which the response is
835 expected to belong.
836
837 @param[out] TailBufferFill If NULL, then the last buffer in ResponseSgList
838 is considered fixed size. Otherwise, the last
839 buffer is considered variable size, and on
840 successful return, TailBufferFill reports the
841 number of bytes in the last buffer.
842
843 @retval EFI_INVALID_PARAMETER TailBufferFill is not NULL (i.e., the last
844 buffer is considered variable size), and
845 ResponseSgList->NumVec is 1.
846
847 @retval EFI_INVALID_PARAMETER The allocated size of the first buffer does
848 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
849
850 @retval EFI_PROTOCOL_ERROR The VIRTIO_FS_FUSE_RESPONSE structure in the
851 first buffer has not been fully populated.
852
853 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Len" in the first
854 buffer does not equal the sum of the
855 individual buffer sizes (as populated).
856
857 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
858 buffer does not equal RequestId.
859
860 @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
861 buffer is zero, but a subsequent fixed size
862 buffer has not been fully populated.
863
864 @retval EFI_DEVICE_ERROR "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
865 buffer is nonzero. The caller may investigate
866 "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the
867 completeness of the subsequent fixed size
868 buffers is not verified in this case.
869
870 @retval EFI_SUCCESS Verification successful.
871 **/
872 EFI_STATUS
873 VirtioFsFuseCheckResponse (
874 IN VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
875 IN UINT64 RequestId,
876 OUT UINTN *TailBufferFill
877 )
878 {
879 UINTN NumFixedSizeVec;
880 VIRTIO_FS_FUSE_RESPONSE *CommonResp;
881 UINT32 TotalTransferred;
882 UINTN Idx;
883
884 //
885 // Ensured by VirtioFsSgListsValidate().
886 //
887 ASSERT (ResponseSgList->NumVec > 0);
888
889 if (TailBufferFill == NULL) {
890 //
891 // All buffers are considered fixed size.
892 //
893 NumFixedSizeVec = ResponseSgList->NumVec;
894 } else {
895 //
896 // If the last buffer is variable size, then we need that buffer to be
897 // different from the first buffer, which is considered a
898 // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.
899 //
900 if (ResponseSgList->NumVec == 1) {
901 return EFI_INVALID_PARAMETER;
902 }
903
904 NumFixedSizeVec = ResponseSgList->NumVec - 1;
905 }
906
907 //
908 // The first buffer is supposed to carry a (fully populated)
909 // VIRTIO_FS_FUSE_RESPONSE structure.
910 //
911 if (ResponseSgList->IoVec[0].Size != sizeof *CommonResp) {
912 return EFI_INVALID_PARAMETER;
913 }
914
915 if (ResponseSgList->IoVec[0].Transferred != ResponseSgList->IoVec[0].Size) {
916 return EFI_PROTOCOL_ERROR;
917 }
918
919 //
920 // FUSE must report the same number of bytes, written by the Virtio
921 // Filesystem device, as the virtio transport does.
922 //
923 CommonResp = ResponseSgList->IoVec[0].Buffer;
924 TotalTransferred = 0;
925 for (Idx = 0; Idx < ResponseSgList->NumVec; Idx++) {
926 //
927 // Integer overflow and truncation are not possible, based on
928 // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
929 //
930 TotalTransferred += (UINT32)ResponseSgList->IoVec[Idx].Transferred;
931 }
932
933 if (CommonResp->Len != TotalTransferred) {
934 return EFI_PROTOCOL_ERROR;
935 }
936
937 //
938 // Enforce that FUSE match our request ID in the response.
939 //
940 if (CommonResp->Unique != RequestId) {
941 return EFI_PROTOCOL_ERROR;
942 }
943
944 //
945 // If there is an explicit error report, skip checking the transfer
946 // counts for the rest of the fixed size buffers.
947 //
948 if (CommonResp->Error != 0) {
949 return EFI_DEVICE_ERROR;
950 }
951
952 //
953 // There was no error reported, so we require that the Virtio Filesystem
954 // device populate all fixed size buffers. We checked this for the very first
955 // buffer above; let's check the rest (if any).
956 //
957 ASSERT (NumFixedSizeVec >= 1);
958 for (Idx = 1; Idx < NumFixedSizeVec; Idx++) {
959 if (ResponseSgList->IoVec[Idx].Transferred !=
960 ResponseSgList->IoVec[Idx].Size)
961 {
962 return EFI_PROTOCOL_ERROR;
963 }
964 }
965
966 //
967 // If the last buffer is considered variable size, report its filled size.
968 //
969 if (TailBufferFill != NULL) {
970 *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;
971 }
972
973 return EFI_SUCCESS;
974 }
975
976 /**
977 An ad-hoc function for mapping FUSE (well, Linux) "errno" values to
978 EFI_STATUS.
979
980 @param[in] Errno The "VIRTIO_FS_FUSE_RESPONSE.Error" value, returned by the
981 Virtio Filesystem device. The value is expected to be
982 negative.
983
984 @return An EFI_STATUS error code that's deemed a passable
985 mapping for the Errno value.
986
987 @retval EFI_DEVICE_ERROR Fallback EFI_STATUS code for unrecognized Errno
988 values.
989 **/
990 EFI_STATUS
991 VirtioFsErrnoToEfiStatus (
992 IN INT32 Errno
993 )
994 {
995 switch (Errno) {
996 case -1:// EPERM Operation not permitted
997 return EFI_SECURITY_VIOLATION;
998
999 case -2: // ENOENT No such file or directory
1000 case -3: // ESRCH No such process
1001 case -6: // ENXIO No such device or address
1002 case -10: // ECHILD No child processes
1003 case -19: // ENODEV No such device
1004 case -49: // EUNATCH Protocol driver not attached
1005 case -65: // ENOPKG Package not installed
1006 case -79: // ELIBACC Can not access a needed shared library
1007 case -126: // ENOKEY Required key not available
1008 return EFI_NOT_FOUND;
1009
1010 case -4: // EINTR Interrupted system call
1011 case -11: // EAGAIN, EWOULDBLOCK Resource temporarily unavailable
1012 case -16: // EBUSY Device or resource busy
1013 case -26: // ETXTBSY Text file busy
1014 case -35: // EDEADLK, EDEADLOCK Resource deadlock avoided
1015 case -39: // ENOTEMPTY Directory not empty
1016 case -42: // ENOMSG No message of desired type
1017 case -61: // ENODATA No data available
1018 case -85: // ERESTART Interrupted system call should be restarted
1019 return EFI_NOT_READY;
1020
1021 case -5: // EIO Input/output error
1022 case -45: // EL2NSYNC Level 2 not synchronized
1023 case -46: // EL3HLT Level 3 halted
1024 case -47: // EL3RST Level 3 reset
1025 case -51: // EL2HLT Level 2 halted
1026 case -121: // EREMOTEIO Remote I/O error
1027 case -133: // EHWPOISON Memory page has hardware error
1028 return EFI_DEVICE_ERROR;
1029
1030 case -7: // E2BIG Argument list too long
1031 case -36: // ENAMETOOLONG File name too long
1032 case -90: // EMSGSIZE Message too long
1033 return EFI_BAD_BUFFER_SIZE;
1034
1035 case -8: // ENOEXEC Exec format error
1036 case -15: // ENOTBLK Block device required
1037 case -18: // EXDEV Invalid cross-device link
1038 case -20: // ENOTDIR Not a directory
1039 case -21: // EISDIR Is a directory
1040 case -25: // ENOTTY Inappropriate ioctl for device
1041 case -27: // EFBIG File too large
1042 case -29: // ESPIPE Illegal seek
1043 case -38: // ENOSYS Function not implemented
1044 case -59: // EBFONT Bad font file format
1045 case -60: // ENOSTR Device not a stream
1046 case -83: // ELIBEXEC Cannot exec a shared library directly
1047 case -88: // ENOTSOCK Socket operation on non-socket
1048 case -91: // EPROTOTYPE Protocol wrong type for socket
1049 case -92: // ENOPROTOOPT Protocol not available
1050 case -93: // EPROTONOSUPPORT Protocol not supported
1051 case -94: // ESOCKTNOSUPPORT Socket type not supported
1052 case -95: // ENOTSUP, EOPNOTSUPP Operation not supported
1053 case -96: // EPFNOSUPPORT Protocol family not supported
1054 case -97: // EAFNOSUPPORT Address family not supported by protocol
1055 case -99: // EADDRNOTAVAIL Cannot assign requested address
1056 case -118: // ENOTNAM Not a XENIX named type file
1057 case -120: // EISNAM Is a named type file
1058 case -124: // EMEDIUMTYPE Wrong medium type
1059 return EFI_UNSUPPORTED;
1060
1061 case -9: // EBADF Bad file descriptor
1062 case -14: // EFAULT Bad address
1063 case -44: // ECHRNG Channel number out of range
1064 case -48: // ELNRNG Link number out of range
1065 case -53: // EBADR Invalid request descriptor
1066 case -56: // EBADRQC Invalid request code
1067 case -57: // EBADSLT Invalid slot
1068 case -76: // ENOTUNIQ Name not unique on network
1069 case -84: // EILSEQ Invalid or incomplete multibyte or wide character
1070 return EFI_NO_MAPPING;
1071
1072 case -12: // ENOMEM Cannot allocate memory
1073 case -23: // ENFILE Too many open files in system
1074 case -24: // EMFILE Too many open files
1075 case -31: // EMLINK Too many links
1076 case -37: // ENOLCK No locks available
1077 case -40: // ELOOP Too many levels of symbolic links
1078 case -50: // ENOCSI No CSI structure available
1079 case -55: // ENOANO No anode
1080 case -63: // ENOSR Out of streams resources
1081 case -82: // ELIBMAX Attempting to link in too many shared libraries
1082 case -87: // EUSERS Too many users
1083 case -105: // ENOBUFS No buffer space available
1084 case -109: // ETOOMANYREFS Too many references: cannot splice
1085 case -119: // ENAVAIL No XENIX semaphores available
1086 case -122: // EDQUOT Disk quota exceeded
1087 return EFI_OUT_OF_RESOURCES;
1088
1089 case -13:// EACCES Permission denied
1090 return EFI_ACCESS_DENIED;
1091
1092 case -17: // EEXIST File exists
1093 case -98: // EADDRINUSE Address already in use
1094 case -106: // EISCONN Transport endpoint is already connected
1095 case -114: // EALREADY Operation already in progress
1096 case -115: // EINPROGRESS Operation now in progress
1097 return EFI_ALREADY_STARTED;
1098
1099 case -22: // EINVAL Invalid argument
1100 case -33: // EDOM Numerical argument out of domain
1101 return EFI_INVALID_PARAMETER;
1102
1103 case -28: // ENOSPC No space left on device
1104 case -54: // EXFULL Exchange full
1105 return EFI_VOLUME_FULL;
1106
1107 case -30:// EROFS Read-only file system
1108 return EFI_WRITE_PROTECTED;
1109
1110 case -32: // EPIPE Broken pipe
1111 case -43: // EIDRM Identifier removed
1112 case -67: // ENOLINK Link has been severed
1113 case -68: // EADV Advertise error
1114 case -69: // ESRMNT Srmount error
1115 case -70: // ECOMM Communication error on send
1116 case -73: // EDOTDOT RFS specific error
1117 case -78: // EREMCHG Remote address changed
1118 case -86: // ESTRPIPE Streams pipe error
1119 case -102: // ENETRESET Network dropped connection on reset
1120 case -103: // ECONNABORTED Software caused connection abort
1121 case -104: // ECONNRESET Connection reset by peer
1122 case -116: // ESTALE Stale file handle
1123 case -125: // ECANCELED Operation canceled
1124 case -128: // EKEYREVOKED Key has been revoked
1125 case -129: // EKEYREJECTED Key was rejected by service
1126 case -130: // EOWNERDEAD Owner died
1127 case -131: // ENOTRECOVERABLE State not recoverable
1128 return EFI_ABORTED;
1129
1130 case -34: // ERANGE Numerical result out of range
1131 case -75: // EOVERFLOW Value too large for defined data type
1132 return EFI_BUFFER_TOO_SMALL;
1133
1134 case -52: // EBADE Invalid exchange
1135 case -108: // ESHUTDOWN Cannot send after transport endpoint shutdown
1136 case -111: // ECONNREFUSED Connection refused
1137 return EFI_END_OF_FILE;
1138
1139 case -62: // ETIME Timer expired
1140 case -110: // ETIMEDOUT Connection timed out
1141 case -127: // EKEYEXPIRED Key has expired
1142 return EFI_TIMEOUT;
1143
1144 case -64: // ENONET Machine is not on the network
1145 case -66: // EREMOTE Object is remote
1146 case -72: // EMULTIHOP Multihop attempted
1147 case -100: // ENETDOWN Network is down
1148 case -101: // ENETUNREACH Network is unreachable
1149 case -112: // EHOSTDOWN Host is down
1150 case -113: // EHOSTUNREACH No route to host
1151 case -123: // ENOMEDIUM No medium found
1152 case -132: // ERFKILL Operation not possible due to RF-kill
1153 return EFI_NO_MEDIA;
1154
1155 case -71:// EPROTO Protocol error
1156 return EFI_PROTOCOL_ERROR;
1157
1158 case -74: // EBADMSG Bad message
1159 case -77: // EBADFD File descriptor in bad state
1160 case -80: // ELIBBAD Accessing a corrupted shared library
1161 case -81: // ELIBSCN .lib section in a.out corrupted
1162 case -117: // EUCLEAN Structure needs cleaning
1163 return EFI_VOLUME_CORRUPTED;
1164
1165 case -89: // EDESTADDRREQ Destination address required
1166 case -107: // ENOTCONN Transport endpoint is not connected
1167 return EFI_NOT_STARTED;
1168
1169 default:
1170 break;
1171 }
1172
1173 return EFI_DEVICE_ERROR;
1174 }
1175
1176 //
1177 // Parser states for canonicalizing a POSIX pathname.
1178 //
1179 typedef enum {
1180 ParserInit, // just starting
1181 ParserEnd, // finished
1182 ParserSlash, // slash(es) seen
1183 ParserDot, // one dot seen since last slash
1184 ParserDotDot, // two dots seen since last slash
1185 ParserNormal, // a different sequence seen
1186 } PARSER_STATE;
1187
1188 /**
1189 Strip the trailing slash from the parser's output buffer, unless the trailing
1190 slash stands for the root directory.
1191
1192 @param[in] Buffer The parser's output buffer. Only used for
1193 sanity-checking.
1194
1195 @param[in,out] Position On entry, points at the next character to produce
1196 (i.e., right past the end of the output written by
1197 the parser thus far). The last character in the
1198 parser's output buffer is a slash. On return, the
1199 slash is stripped, by decrementing Position by one.
1200 If this action would remove the slash character
1201 standing for the root directory, then the function
1202 has no effect.
1203 **/
1204 STATIC
1205 VOID
1206 ParserStripSlash (
1207 IN CHAR8 *Buffer,
1208 IN OUT UINTN *Position
1209 )
1210 {
1211 ASSERT (*Position >= 1);
1212 ASSERT (Buffer[*Position - 1] == '/');
1213 if (*Position == 1) {
1214 return;
1215 }
1216
1217 (*Position)--;
1218 }
1219
1220 /**
1221 Produce one character in the parser's output buffer.
1222
1223 @param[out] Buffer The parser's output buffer. On return, Char8 will
1224 have been written.
1225
1226 @param[in,out] Position On entry, points at the next character to produce
1227 (i.e., right past the end of the output written by
1228 the parser thus far). On return, Position is
1229 incremented by one.
1230
1231 @param[in] Size Total allocated size of the parser's output buffer.
1232 Used for sanity-checking.
1233
1234 @param[in] Char8 The character to place in the output buffer.
1235 **/
1236 STATIC
1237 VOID
1238 ParserCopy (
1239 OUT CHAR8 *Buffer,
1240 IN OUT UINTN *Position,
1241 IN UINTN Size,
1242 IN CHAR8 Char8
1243 )
1244 {
1245 ASSERT (*Position < Size);
1246 Buffer[(*Position)++] = Char8;
1247 }
1248
1249 /**
1250 Rewind the last single-dot in the parser's output buffer.
1251
1252 @param[in] Buffer The parser's output buffer. Only used for
1253 sanity-checking.
1254
1255 @param[in,out] Position On entry, points at the next character to produce
1256 (i.e., right past the end of the output written by
1257 the parser thus far); the parser's output buffer
1258 ends with the characters ('/', '.'). On return, the
1259 dot is rewound by decrementing Position by one; a
1260 slash character will reside at the new end of the
1261 parser's output buffer.
1262 **/
1263 STATIC
1264 VOID
1265 ParserRewindDot (
1266 IN CHAR8 *Buffer,
1267 IN OUT UINTN *Position
1268 )
1269 {
1270 ASSERT (*Position >= 2);
1271 ASSERT (Buffer[*Position - 1] == '.');
1272 ASSERT (Buffer[*Position - 2] == '/');
1273 (*Position)--;
1274 }
1275
1276 /**
1277 Rewind the last dot-dot in the parser's output buffer.
1278
1279 @param[in] Buffer The parser's output buffer. Only used for
1280 sanity-checking.
1281
1282 @param[in,out] Position On entry, points at the next character to produce
1283 (i.e., right past the end of the output written by
1284 the parser thus far); the parser's output buffer
1285 ends with the characters ('/', '.', '.'). On return,
1286 the ('.', '.') pair is rewound unconditionally, by
1287 decrementing Position by two; a slash character
1288 resides at the new end of the parser's output
1289 buffer.
1290
1291 If this slash character stands for the root
1292 directory, then RootEscape is set to TRUE.
1293
1294 Otherwise (i.e., if this slash character is not the
1295 one standing for the root directory), then the slash
1296 character, and the pathname component preceding it,
1297 are removed by decrementing Position further. In
1298 this case, the slash character preceding the removed
1299 pathname component will reside at the new end of the
1300 parser's output buffer.
1301
1302 @param[out] RootEscape Set to TRUE on output if the dot-dot component tries
1303 to escape the root directory, as described above.
1304 Otherwise, RootEscape is not modified.
1305 **/
1306 STATIC
1307 VOID
1308 ParserRewindDotDot (
1309 IN CHAR8 *Buffer,
1310 IN OUT UINTN *Position,
1311 OUT BOOLEAN *RootEscape
1312
1313 )
1314 {
1315 ASSERT (*Position >= 3);
1316 ASSERT (Buffer[*Position - 1] == '.');
1317 ASSERT (Buffer[*Position - 2] == '.');
1318 ASSERT (Buffer[*Position - 3] == '/');
1319 (*Position) -= 2;
1320
1321 if (*Position == 1) {
1322 //
1323 // Root directory slash reached; don't try to climb higher.
1324 //
1325 *RootEscape = TRUE;
1326 return;
1327 }
1328
1329 //
1330 // Skip slash.
1331 //
1332 (*Position)--;
1333 //
1334 // Scan until next slash to the left.
1335 //
1336 do {
1337 ASSERT (*Position > 0);
1338 (*Position)--;
1339 } while (Buffer[*Position] != '/');
1340
1341 (*Position)++;
1342 }
1343
1344 /**
1345 Append the UEFI-style RhsPath16 to the POSIX-style, canonical format
1346 LhsPath8. Output the POSIX-style, canonical format result in ResultPath, as a
1347 dynamically allocated string.
1348
1349 Canonicalization (aka sanitization) establishes the following properties:
1350 - ResultPath is absolute (starts with "/"),
1351 - dot (.) and dot-dot (..) components are resolved/eliminated in ResultPath,
1352 with the usual semantics,
1353 - ResultPath uses forward slashes,
1354 - sequences of slashes are squashed in ResultPath,
1355 - the printable ASCII character set covers ResultPath,
1356 - CHAR8 encoding is used in ResultPath,
1357 - no trailing slash present in ResultPath except for the standalone root
1358 directory,
1359 - the length of ResultPath is at most VIRTIO_FS_MAX_PATHNAME_LENGTH.
1360
1361 Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
1362 reported through RootEscape, without failing the function call.
1363
1364 @param[in] LhsPath8 Identifies the base directory. The caller is
1365 responsible for ensuring that LhsPath8 conform to
1366 the above canonical pathname format on entry.
1367
1368 @param[in] RhsPath16 Identifies the desired file with a UEFI-style CHAR16
1369 pathname. If RhsPath16 starts with a backslash, then
1370 RhsPath16 is considered absolute, and LhsPath8 is
1371 ignored; RhsPath16 is sanitized in isolation, for
1372 producing ResultPath8. Otherwise (i.e., if RhsPath16
1373 is relative), RhsPath16 is transliterated to CHAR8,
1374 and naively appended to LhsPath8. The resultant
1375 fused pathname is then sanitized, to produce
1376 ResultPath8.
1377
1378 @param[out] ResultPath8 The POSIX-style, canonical format pathname that
1379 leads to the file desired by the caller. After use,
1380 the caller is responsible for freeing ResultPath8.
1381
1382 @param[out] RootEscape Set to TRUE if at least one dot-dot component in
1383 RhsPath16 attempted to escape the root directory;
1384 set to FALSE otherwise.
1385
1386 @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
1387 been output.
1388
1389 @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
1390
1391 @retval EFI_INVALID_PARAMETER RhsPath16 failed the
1392 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1393
1394 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1395
1396 @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
1397 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1398
1399 @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
1400 falls outside of the printable ASCII set, or
1401 is a forward slash.
1402 **/
1403 EFI_STATUS
1404 VirtioFsAppendPath (
1405 IN CHAR8 *LhsPath8,
1406 IN CHAR16 *RhsPath16,
1407 OUT CHAR8 **ResultPath8,
1408 OUT BOOLEAN *RootEscape
1409 )
1410 {
1411 UINTN RhsLen;
1412 CHAR8 *RhsPath8;
1413 UINTN Idx;
1414 EFI_STATUS Status;
1415 UINTN SizeToSanitize;
1416 CHAR8 *BufferToSanitize;
1417 CHAR8 *SanitizedBuffer;
1418 PARSER_STATE State;
1419 UINTN SanitizedPosition;
1420
1421 //
1422 // Appending an empty pathname is not allowed.
1423 //
1424 RhsLen = StrLen (RhsPath16);
1425 if (RhsLen == 0) {
1426 return EFI_INVALID_PARAMETER;
1427 }
1428
1429 //
1430 // Enforce length restriction on RhsPath16.
1431 //
1432 if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
1433 return EFI_INVALID_PARAMETER;
1434 }
1435
1436 //
1437 // Transliterate RhsPath16 to RhsPath8 by:
1438 // - rejecting RhsPath16 if a character outside of printable ASCII is seen,
1439 // - rejecting RhsPath16 if a forward slash is seen,
1440 // - replacing backslashes with forward slashes,
1441 // - casting the characters from CHAR16 to CHAR8.
1442 //
1443 RhsPath8 = AllocatePool (RhsLen + 1);
1444 if (RhsPath8 == NULL) {
1445 return EFI_OUT_OF_RESOURCES;
1446 }
1447
1448 for (Idx = 0; RhsPath16[Idx] != L'\0'; Idx++) {
1449 if ((RhsPath16[Idx] < 0x20) || (RhsPath16[Idx] > 0x7E) ||
1450 (RhsPath16[Idx] == L'/'))
1451 {
1452 Status = EFI_UNSUPPORTED;
1453 goto FreeRhsPath8;
1454 }
1455
1456 RhsPath8[Idx] = (CHAR8)((RhsPath16[Idx] == L'\\') ? L'/' : RhsPath16[Idx]);
1457 }
1458
1459 RhsPath8[Idx++] = '\0';
1460
1461 //
1462 // Now prepare the input for the canonicalization (squashing of sequences of
1463 // forward slashes, and eliminating . (dot) and .. (dot-dot) pathname
1464 // components).
1465 //
1466 // The sanitized path can never be longer than the naive concatenation of the
1467 // left hand side and right hand side paths, so we'll use the catenated size
1468 // for allocating the sanitized output too.
1469 //
1470 if (RhsPath8[0] == '/') {
1471 //
1472 // If the right hand side path is absolute, then it is not appended to the
1473 // left hand side path -- it *replaces* the left hand side path.
1474 //
1475 SizeToSanitize = RhsLen + 1;
1476 BufferToSanitize = RhsPath8;
1477 } else {
1478 //
1479 // If the right hand side path is relative, then it is appended (naively)
1480 // to the left hand side.
1481 //
1482 UINTN LhsLen;
1483
1484 LhsLen = AsciiStrLen (LhsPath8);
1485 SizeToSanitize = LhsLen + 1 + RhsLen + 1;
1486 BufferToSanitize = AllocatePool (SizeToSanitize);
1487 if (BufferToSanitize == NULL) {
1488 Status = EFI_OUT_OF_RESOURCES;
1489 goto FreeRhsPath8;
1490 }
1491
1492 CopyMem (BufferToSanitize, LhsPath8, LhsLen);
1493 BufferToSanitize[LhsLen] = '/';
1494 CopyMem (BufferToSanitize + LhsLen + 1, RhsPath8, RhsLen + 1);
1495 }
1496
1497 //
1498 // Allocate the output buffer.
1499 //
1500 SanitizedBuffer = AllocatePool (SizeToSanitize);
1501 if (SanitizedBuffer == NULL) {
1502 Status = EFI_OUT_OF_RESOURCES;
1503 goto FreeBufferToSanitize;
1504 }
1505
1506 //
1507 // State machine for parsing the input and producing the canonical output
1508 // follows.
1509 //
1510 *RootEscape = FALSE;
1511 Idx = 0;
1512 State = ParserInit;
1513 SanitizedPosition = 0;
1514 do {
1515 CHAR8 Chr8;
1516
1517 ASSERT (Idx < SizeToSanitize);
1518 Chr8 = BufferToSanitize[Idx++];
1519
1520 switch (State) {
1521 case ParserInit: // just starting
1522 ASSERT (Chr8 == '/');
1523 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1524 State = ParserSlash;
1525 break;
1526
1527 case ParserSlash: // slash(es) seen
1528 switch (Chr8) {
1529 case '\0':
1530 ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
1531 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1532 State = ParserEnd;
1533 break;
1534 case '/':
1535 //
1536 // skip & stay in same state
1537 //
1538 break;
1539 case '.':
1540 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1541 State = ParserDot;
1542 break;
1543 default:
1544 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1545 State = ParserNormal;
1546 break;
1547 }
1548
1549 break;
1550
1551 case ParserDot: // one dot seen since last slash
1552 switch (Chr8) {
1553 case '\0':
1554 ParserRewindDot (SanitizedBuffer, &SanitizedPosition);
1555 ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
1556 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1557 State = ParserEnd;
1558 break;
1559 case '/':
1560 ParserRewindDot (SanitizedBuffer, &SanitizedPosition);
1561 State = ParserSlash;
1562 break;
1563 case '.':
1564 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1565 State = ParserDotDot;
1566 break;
1567 default:
1568 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1569 State = ParserNormal;
1570 break;
1571 }
1572
1573 break;
1574
1575 case ParserDotDot: // two dots seen since last slash
1576 switch (Chr8) {
1577 case '\0':
1578 ParserRewindDotDot (SanitizedBuffer, &SanitizedPosition, RootEscape);
1579 ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
1580 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1581 State = ParserEnd;
1582 break;
1583 case '/':
1584 ParserRewindDotDot (SanitizedBuffer, &SanitizedPosition, RootEscape);
1585 State = ParserSlash;
1586 break;
1587 case '.':
1588 //
1589 // fall through
1590 //
1591 default:
1592 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1593 State = ParserNormal;
1594 break;
1595 }
1596
1597 break;
1598
1599 case ParserNormal: // a different sequence seen
1600 switch (Chr8) {
1601 case '\0':
1602 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1603 State = ParserEnd;
1604 break;
1605 case '/':
1606 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1607 State = ParserSlash;
1608 break;
1609 case '.':
1610 //
1611 // fall through
1612 //
1613 default:
1614 //
1615 // copy and stay in same state
1616 //
1617 ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
1618 break;
1619 }
1620
1621 break;
1622
1623 default:
1624 ASSERT (FALSE);
1625 break;
1626 }
1627 } while (State != ParserEnd);
1628
1629 //
1630 // Ensure length invariant on ResultPath8.
1631 //
1632 ASSERT (SanitizedPosition >= 2);
1633 if (SanitizedPosition - 1 > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
1634 Status = EFI_OUT_OF_RESOURCES;
1635 goto FreeSanitizedBuffer;
1636 }
1637
1638 *ResultPath8 = SanitizedBuffer;
1639 SanitizedBuffer = NULL;
1640 Status = EFI_SUCCESS;
1641 //
1642 // Fall through.
1643 //
1644 FreeSanitizedBuffer:
1645 if (SanitizedBuffer != NULL) {
1646 FreePool (SanitizedBuffer);
1647 }
1648
1649 FreeBufferToSanitize:
1650 if (RhsPath8[0] != '/') {
1651 FreePool (BufferToSanitize);
1652 }
1653
1654 FreeRhsPath8:
1655 FreePool (RhsPath8);
1656 return Status;
1657 }
1658
1659 /**
1660 For a given canonical pathname (as defined at VirtioFsAppendPath()), look up
1661 the NodeId of the most specific parent directory, plus output a pointer to
1662 the last pathname component (which is therefore a direct child of said parent
1663 directory).
1664
1665 The function may only be called after VirtioFsFuseInitSession() returns
1666 successfully and before VirtioFsUninit() is called.
1667
1668 @param[in,out] VirtioFs The Virtio Filesystem device to send FUSE_LOOKUP
1669 and FUSE_FORGET requests to. On output, the FUSE
1670 request counter "VirtioFs->RequestId" will have
1671 been incremented several times.
1672
1673 @param[in,out] Path The canonical pathname (as defined in the
1674 description of VirtioFsAppendPath()) to split.
1675 Path is modified in-place temporarily; however, on
1676 return (successful or otherwise), Path reassumes
1677 its original contents.
1678
1679 @param[out] DirNodeId The NodeId of the most specific parent directory
1680 identified by Path. The caller is responsible for
1681 sending a FUSE_FORGET request to the Virtio
1682 Filesystem device for DirNodeId -- unless
1683 DirNodeId equals VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
1684 --, when DirNodeId's use ends.
1685
1686 @param[out] LastComponent A pointer into Path, pointing at the start of the
1687 last pathname component.
1688
1689 @retval EFI_SUCCESS Splitting successful.
1690
1691 @retval EFI_INVALID_PARAMETER Path is "/".
1692
1693 @retval EFI_ACCESS_DENIED One of the components on Path before the last
1694 is not a directory.
1695
1696 @return Error codes propagated from
1697 VirtioFsFuseLookup() and
1698 VirtioFsFuseAttrToEfiFileInfo().
1699 **/
1700 EFI_STATUS
1701 VirtioFsLookupMostSpecificParentDir (
1702 IN OUT VIRTIO_FS *VirtioFs,
1703 IN OUT CHAR8 *Path,
1704 OUT UINT64 *DirNodeId,
1705 OUT CHAR8 **LastComponent
1706 )
1707 {
1708 UINT64 ParentDirNodeId;
1709 CHAR8 *Slash;
1710 EFI_STATUS Status;
1711 UINT64 NextDirNodeId;
1712
1713 if (AsciiStrCmp (Path, "/") == 0) {
1714 return EFI_INVALID_PARAMETER;
1715 }
1716
1717 ParentDirNodeId = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
1718 Slash = Path;
1719 for ( ; ;) {
1720 CHAR8 *NextSlash;
1721 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
1722 EFI_FILE_INFO FileInfo;
1723
1724 //
1725 // Find the slash (if any) that terminates the next pathname component.
1726 //
1727 NextSlash = AsciiStrStr (Slash + 1, "/");
1728 if (NextSlash == NULL) {
1729 break;
1730 }
1731
1732 //
1733 // Temporarily replace the found slash character with a NUL in-place, for
1734 // easy construction of the single-component filename that we need to look
1735 // up.
1736 //
1737 *NextSlash = '\0';
1738 Status = VirtioFsFuseLookup (
1739 VirtioFs,
1740 ParentDirNodeId,
1741 Slash + 1,
1742 &NextDirNodeId,
1743 &FuseAttr
1744 );
1745 *NextSlash = '/';
1746
1747 //
1748 // We're done with the directory inode that was the basis for the lookup.
1749 //
1750 if (ParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
1751 VirtioFsFuseForget (VirtioFs, ParentDirNodeId);
1752 }
1753
1754 //
1755 // If we couldn't look up the next *non-final* pathname component, bail.
1756 //
1757 if (EFI_ERROR (Status)) {
1758 return Status;
1759 }
1760
1761 //
1762 // Lookup successful; now check if the next (non-final) component is a
1763 // directory. If not, bail.
1764 //
1765 Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
1766 if (EFI_ERROR (Status)) {
1767 goto ForgetNextDirNodeId;
1768 }
1769
1770 if ((FileInfo.Attribute & EFI_FILE_DIRECTORY) == 0) {
1771 Status = EFI_ACCESS_DENIED;
1772 goto ForgetNextDirNodeId;
1773 }
1774
1775 //
1776 // Advance.
1777 //
1778 ParentDirNodeId = NextDirNodeId;
1779 Slash = NextSlash;
1780 }
1781
1782 //
1783 // ParentDirNodeId corresponds to the last containing directory. The
1784 // remaining single-component filename represents a direct child under that
1785 // directory. Said filename starts at (Slash + 1).
1786 //
1787 *DirNodeId = ParentDirNodeId;
1788 *LastComponent = Slash + 1;
1789 return EFI_SUCCESS;
1790
1791 ForgetNextDirNodeId:
1792 VirtioFsFuseForget (VirtioFs, NextDirNodeId);
1793 return Status;
1794 }
1795
1796 /**
1797 Format the last component of a canonical pathname into a caller-provided
1798 CHAR16 array.
1799
1800 @param[in] Path The canonical pathname (as defined in the
1801 description of VirtioFsAppendPath()) to format
1802 the last component of.
1803
1804 @param[out] Basename If BasenameSize is zero on input, Basename may
1805 be NULL. Otherwise, Basename is allocated by the
1806 caller. On successful return, Basename contains
1807 the last component of Path, formatted as a
1808 NUL-terminated CHAR16 string. When Path is "/"
1809 on input, Basename is L"" on output.
1810
1811 @param[in,out] BasenameSize On input, the number of bytes the caller
1812 provides in Basename. On output, regardless of
1813 return value, the number of bytes required for
1814 formatting Basename, including the terminating
1815 L'\0'.
1816
1817 @retval EFI_SUCCESS Basename has been filled in.
1818
1819 @retval EFI_BUFFER_TOO_SMALL BasenameSize was too small on input; Basename
1820 has not been modified.
1821 **/
1822 EFI_STATUS
1823 VirtioFsGetBasename (
1824 IN CHAR8 *Path,
1825 OUT CHAR16 *Basename OPTIONAL,
1826 IN OUT UINTN *BasenameSize
1827 )
1828 {
1829 UINTN AllocSize;
1830 UINTN LastComponent;
1831 UINTN Idx;
1832 UINTN PathSize;
1833
1834 AllocSize = *BasenameSize;
1835
1836 LastComponent = MAX_UINTN;
1837 for (Idx = 0; Path[Idx] != '\0'; Idx++) {
1838 if (Path[Idx] == '/') {
1839 LastComponent = Idx;
1840 }
1841 }
1842
1843 PathSize = Idx + 1;
1844 ASSERT (LastComponent < MAX_UINTN);
1845 LastComponent++;
1846 *BasenameSize = (PathSize - LastComponent) * sizeof Basename[0];
1847
1848 if (*BasenameSize > AllocSize) {
1849 return EFI_BUFFER_TOO_SMALL;
1850 }
1851
1852 for (Idx = LastComponent; Idx < PathSize; Idx++) {
1853 Basename[Idx - LastComponent] = Path[Idx];
1854 }
1855
1856 return EFI_SUCCESS;
1857 }
1858
1859 /**
1860 Format the destination of a rename/move operation as a dynamically allocated
1861 canonical pathname.
1862
1863 Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
1864 reported through RootEscape, without failing the function call.
1865
1866 @param[in] LhsPath8 The source pathname operand of the rename/move
1867 operation, expressed as a canonical pathname (as
1868 defined in the description of VirtioFsAppendPath()).
1869 The root directory "/" cannot be renamed/moved, and
1870 will be rejected.
1871
1872 @param[in] RhsPath16 The destination pathname operand expressed as a
1873 UEFI-style CHAR16 pathname.
1874
1875 If RhsPath16 starts with a backslash, then RhsPath16
1876 is considered absolute. Otherwise, RhsPath16 is
1877 interpreted relative to the most specific parent
1878 directory found in LhsPath8.
1879
1880 Independently, if RhsPath16 ends with a backslash
1881 (i.e., RhsPath16 is given in the "move into
1882 directory" convenience form), then RhsPath16 is
1883 interpreted with the basename of LhsPath8 appended.
1884 Otherwise, the last pathname component of RhsPath16
1885 is taken as the last pathname component of the
1886 rename/move destination.
1887
1888 An empty RhsPath16 is rejected.
1889
1890 @param[out] ResultPath8 The POSIX-style, canonical format pathname that
1891 leads to the renamed/moved file. After use, the
1892 caller is responsible for freeing ResultPath8.
1893
1894 @param[out] RootEscape Set to TRUE if at least one dot-dot component in
1895 RhsPath16 attempted to escape the root directory;
1896 set to FALSE otherwise.
1897
1898 @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
1899 been output.
1900
1901 @retval EFI_INVALID_PARAMETER LhsPath8 is "/".
1902
1903 @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
1904
1905 @retval EFI_INVALID_PARAMETER RhsPath16 failed the
1906 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1907
1908 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1909
1910 @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
1911 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1912
1913 @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
1914 falls outside of the printable ASCII set, or
1915 is a forward slash.
1916 **/
1917 EFI_STATUS
1918 VirtioFsComposeRenameDestination (
1919 IN CHAR8 *LhsPath8,
1920 IN CHAR16 *RhsPath16,
1921 OUT CHAR8 **ResultPath8,
1922 OUT BOOLEAN *RootEscape
1923 )
1924 {
1925 //
1926 // Lengths are expressed as numbers of characters (CHAR8 or CHAR16),
1927 // excluding terminating NULs. Sizes are expressed as byte counts, including
1928 // the bytes taken up by terminating NULs.
1929 //
1930 UINTN RhsLen;
1931 UINTN LhsBasename16Size;
1932 EFI_STATUS Status;
1933 UINTN LhsBasenameLen;
1934 UINTN DestSuffix16Size;
1935 CHAR16 *DestSuffix16;
1936 CHAR8 *DestPrefix8;
1937
1938 //
1939 // An empty destination operand for the rename/move operation is not allowed.
1940 //
1941 RhsLen = StrLen (RhsPath16);
1942 if (RhsLen == 0) {
1943 return EFI_INVALID_PARAMETER;
1944 }
1945
1946 //
1947 // Enforce length restriction on RhsPath16.
1948 //
1949 if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
1950 return EFI_INVALID_PARAMETER;
1951 }
1952
1953 //
1954 // Determine the length of the basename of LhsPath8.
1955 //
1956 LhsBasename16Size = 0;
1957 Status = VirtioFsGetBasename (LhsPath8, NULL, &LhsBasename16Size);
1958 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
1959 ASSERT (LhsBasename16Size >= sizeof (CHAR16));
1960 ASSERT (LhsBasename16Size % sizeof (CHAR16) == 0);
1961 LhsBasenameLen = LhsBasename16Size / sizeof (CHAR16) - 1;
1962 if (LhsBasenameLen == 0) {
1963 //
1964 // The root directory cannot be renamed/moved.
1965 //
1966 return EFI_INVALID_PARAMETER;
1967 }
1968
1969 //
1970 // Resolve the "move into directory" convenience form in RhsPath16.
1971 //
1972 if (RhsPath16[RhsLen - 1] == L'\\') {
1973 //
1974 // Append the basename of LhsPath8 as a CHAR16 string to RhsPath16.
1975 //
1976 DestSuffix16Size = RhsLen * sizeof (CHAR16) + LhsBasename16Size;
1977 DestSuffix16 = AllocatePool (DestSuffix16Size);
1978 if (DestSuffix16 == NULL) {
1979 return EFI_OUT_OF_RESOURCES;
1980 }
1981
1982 CopyMem (DestSuffix16, RhsPath16, RhsLen * sizeof (CHAR16));
1983 Status = VirtioFsGetBasename (
1984 LhsPath8,
1985 DestSuffix16 + RhsLen,
1986 &LhsBasename16Size
1987 );
1988 ASSERT_EFI_ERROR (Status);
1989 } else {
1990 //
1991 // Just create a copy of RhsPath16.
1992 //
1993 DestSuffix16Size = (RhsLen + 1) * sizeof (CHAR16);
1994 DestSuffix16 = AllocateCopyPool (DestSuffix16Size, RhsPath16);
1995 if (DestSuffix16 == NULL) {
1996 return EFI_OUT_OF_RESOURCES;
1997 }
1998 }
1999
2000 //
2001 // If the destination operand is absolute, it will be interpreted relative to
2002 // the root directory.
2003 //
2004 // Otherwise (i.e., if the destination operand is relative), then create the
2005 // canonical pathname that the destination operand is interpreted relatively
2006 // to; that is, the canonical pathname of the most specific parent directory
2007 // found in LhsPath8.
2008 //
2009 if (DestSuffix16[0] == L'\\') {
2010 DestPrefix8 = AllocateCopyPool (sizeof "/", "/");
2011 if (DestPrefix8 == NULL) {
2012 Status = EFI_OUT_OF_RESOURCES;
2013 goto FreeDestSuffix16;
2014 }
2015 } else {
2016 UINTN LhsLen;
2017 UINTN DestPrefixLen;
2018
2019 //
2020 // Strip the basename of LhsPath8.
2021 //
2022 LhsLen = AsciiStrLen (LhsPath8);
2023 ASSERT (LhsBasenameLen < LhsLen);
2024 DestPrefixLen = LhsLen - LhsBasenameLen;
2025 ASSERT (LhsPath8[DestPrefixLen - 1] == '/');
2026 //
2027 // If we're not at the root directory, strip the slash too.
2028 //
2029 if (DestPrefixLen > 1) {
2030 DestPrefixLen--;
2031 }
2032
2033 DestPrefix8 = AllocatePool (DestPrefixLen + 1);
2034 if (DestPrefix8 == NULL) {
2035 Status = EFI_OUT_OF_RESOURCES;
2036 goto FreeDestSuffix16;
2037 }
2038
2039 CopyMem (DestPrefix8, LhsPath8, DestPrefixLen);
2040 DestPrefix8[DestPrefixLen] = '\0';
2041 }
2042
2043 //
2044 // Now combine DestPrefix8 and DestSuffix16 into the final canonical
2045 // pathname.
2046 //
2047 Status = VirtioFsAppendPath (
2048 DestPrefix8,
2049 DestSuffix16,
2050 ResultPath8,
2051 RootEscape
2052 );
2053
2054 FreePool (DestPrefix8);
2055 //
2056 // Fall through.
2057 //
2058 FreeDestSuffix16:
2059 FreePool (DestSuffix16);
2060
2061 return Status;
2062 }
2063
2064 /**
2065 Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
2066 corresponding fields in EFI_FILE_INFO.
2067
2068 @param[in] FuseAttr The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
2069 convert the relevant fields from.
2070
2071 @param[out] FileInfo The EFI_FILE_INFO structure to modify. Importantly, the
2072 FileInfo->Size and FileInfo->FileName fields are not
2073 overwritten.
2074
2075 @retval EFI_SUCCESS Conversion successful.
2076
2077 @retval EFI_UNSUPPORTED The allocated size of the file is inexpressible in
2078 EFI_FILE_INFO.
2079
2080 @retval EFI_UNSUPPORTED One of the file access times is inexpressible in
2081 EFI_FILE_INFO.
2082
2083 @retval EFI_UNSUPPORTED The file type is inexpressible in EFI_FILE_INFO.
2084
2085 @retval EFI_UNSUPPORTED The file is a regular file that has multiple names
2086 on the host side (i.e., its hard link count is
2087 greater than one).
2088 **/
2089 EFI_STATUS
2090 VirtioFsFuseAttrToEfiFileInfo (
2091 IN VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
2092 OUT EFI_FILE_INFO *FileInfo
2093 )
2094 {
2095 UINT64 EpochTime[3];
2096 EFI_TIME *ConvertedTime[ARRAY_SIZE (EpochTime)];
2097 UINTN Idx;
2098
2099 FileInfo->FileSize = FuseAttr->Size;
2100
2101 //
2102 // The unit for FuseAttr->Blocks is 512B.
2103 //
2104 if (FuseAttr->Blocks >= BIT55) {
2105 return EFI_UNSUPPORTED;
2106 }
2107
2108 FileInfo->PhysicalSize = LShiftU64 (FuseAttr->Blocks, 9);
2109
2110 //
2111 // Convert the timestamps. File creation time is not tracked by the Virtio
2112 // Filesystem device, so set FileInfo->CreateTime from FuseAttr->Mtime as
2113 // well.
2114 //
2115 EpochTime[0] = FuseAttr->Mtime;
2116 EpochTime[1] = FuseAttr->Atime;
2117 EpochTime[2] = FuseAttr->Mtime;
2118 ConvertedTime[0] = &FileInfo->CreateTime;
2119 ConvertedTime[1] = &FileInfo->LastAccessTime;
2120 ConvertedTime[2] = &FileInfo->ModificationTime;
2121
2122 for (Idx = 0; Idx < ARRAY_SIZE (EpochTime); Idx++) {
2123 //
2124 // EpochToEfiTime() takes a UINTN for seconds.
2125 //
2126 if (EpochTime[Idx] > MAX_UINTN) {
2127 return EFI_UNSUPPORTED;
2128 }
2129
2130 //
2131 // Set the following fields in the converted time: Year, Month, Day, Hour,
2132 // Minute, Second, Nanosecond.
2133 //
2134 EpochToEfiTime ((UINTN)EpochTime[Idx], ConvertedTime[Idx]);
2135 //
2136 // The times are all expressed in UTC. Consequently, they are not affected
2137 // by daylight saving.
2138 //
2139 ConvertedTime[Idx]->TimeZone = 0;
2140 ConvertedTime[Idx]->Daylight = 0;
2141 //
2142 // Clear the padding fields.
2143 //
2144 ConvertedTime[Idx]->Pad1 = 0;
2145 ConvertedTime[Idx]->Pad2 = 0;
2146 }
2147
2148 //
2149 // Set the attributes.
2150 //
2151 switch (FuseAttr->Mode & VIRTIO_FS_FUSE_MODE_TYPE_MASK) {
2152 case VIRTIO_FS_FUSE_MODE_TYPE_DIR:
2153 FileInfo->Attribute = EFI_FILE_DIRECTORY;
2154 break;
2155 case VIRTIO_FS_FUSE_MODE_TYPE_REG:
2156 FileInfo->Attribute = 0;
2157 break;
2158 default:
2159 //
2160 // Other file types are not supported.
2161 //
2162 return EFI_UNSUPPORTED;
2163 }
2164
2165 //
2166 // Report the regular file or directory as read-only if all classes lack
2167 // write permission.
2168 //
2169 if ((FuseAttr->Mode & (VIRTIO_FS_FUSE_MODE_PERM_WUSR |
2170 VIRTIO_FS_FUSE_MODE_PERM_WGRP |
2171 VIRTIO_FS_FUSE_MODE_PERM_WOTH)) == 0)
2172 {
2173 FileInfo->Attribute |= EFI_FILE_READ_ONLY;
2174 }
2175
2176 //
2177 // A hard link count greater than 1 is not supported for regular files.
2178 //
2179 if (((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0) && (FuseAttr->Nlink > 1)) {
2180 return EFI_UNSUPPORTED;
2181 }
2182
2183 return EFI_SUCCESS;
2184 }
2185
2186 /**
2187 Convert a VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE filename to an EFI_FILE_INFO
2188 filename.
2189
2190 @param[in] FuseDirent The VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE object to
2191 convert the filename byte array from. The caller is
2192 responsible for ensuring that FuseDirent->Namelen
2193 describe valid storage.
2194
2195 @param[in,out] FileInfo The EFI_FILE_INFO structure to modify. On input, the
2196 caller is responsible for setting FileInfo->Size
2197 according to the allocated size. On successful
2198 return, FileInfo->Size is reduced to reflect the
2199 filename converted into FileInfo->FileName.
2200 FileInfo->FileName is set from the filename byte
2201 array that directly follows the FuseDirent header
2202 object. Fields other than FileInfo->Size and
2203 FileInfo->FileName are not modified.
2204
2205 @retval EFI_SUCCESS Conversion successful.
2206
2207 @retval EFI_INVALID_PARAMETER VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE()
2208 returns zero for FuseDirent->Namelen.
2209
2210 @retval EFI_BUFFER_TOO_SMALL On input, FileInfo->Size does not provide
2211 enough room for converting the filename byte
2212 array from FuseDirent.
2213
2214 @retval EFI_UNSUPPORTED The FuseDirent filename byte array contains a
2215 byte that falls outside of the printable ASCII
2216 range, or is a forward slash or a backslash.
2217 **/
2218 EFI_STATUS
2219 VirtioFsFuseDirentPlusToEfiFileInfo (
2220 IN VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *FuseDirent,
2221 IN OUT EFI_FILE_INFO *FileInfo
2222 )
2223 {
2224 UINTN DirentSize;
2225 UINTN FileInfoSize;
2226 UINT8 *DirentName;
2227 UINT32 Idx;
2228
2229 DirentSize = VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (FuseDirent->Namelen);
2230 if (DirentSize == 0) {
2231 return EFI_INVALID_PARAMETER;
2232 }
2233
2234 //
2235 // We're now safe from overflow in the calculation below.
2236 //
2237 FileInfoSize = (OFFSET_OF (EFI_FILE_INFO, FileName) +
2238 ((UINTN)FuseDirent->Namelen + 1) * sizeof (CHAR16));
2239 if (FileInfoSize > FileInfo->Size) {
2240 return EFI_BUFFER_TOO_SMALL;
2241 }
2242
2243 //
2244 // Convert the name.
2245 //
2246 DirentName = (UINT8 *)(FuseDirent + 1);
2247 for (Idx = 0; Idx < FuseDirent->Namelen; Idx++) {
2248 UINT8 NameByte;
2249
2250 NameByte = DirentName[Idx];
2251 if ((NameByte < 0x20) || (NameByte > 0x7E) ||
2252 (NameByte == '/') || (NameByte == '\\'))
2253 {
2254 return EFI_UNSUPPORTED;
2255 }
2256
2257 FileInfo->FileName[Idx] = (CHAR16)NameByte;
2258 }
2259
2260 FileInfo->FileName[Idx++] = L'\0';
2261 //
2262 // Set the (possibly reduced) size.
2263 //
2264 FileInfo->Size = FileInfoSize;
2265
2266 return EFI_SUCCESS;
2267 }
2268
2269 /**
2270 Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
2271 call, determine whether updating the size of the file is necessary, relative
2272 to an EFI_FILE_INFO object describing the current state of the file.
2273
2274 @param[in] Info The EFI_FILE_INFO describing the current state of the
2275 file. The caller is responsible for populating Info on
2276 input with VirtioFsFuseAttrToEfiFileInfo(), from the
2277 current FUSE attributes of the file. The Info->Size and
2278 Info->FileName members are ignored.
2279
2280 @param[in] NewInfo The EFI_FILE_INFO object received in the
2281 EFI_FILE_PROTOCOL.SetInfo() call.
2282
2283 @param[out] Update Set to TRUE on output if the file size needs to be
2284 updated. Set to FALSE otherwise.
2285
2286 @param[out] Size If Update is set to TRUE, then Size provides the new file
2287 size to set. Otherwise, Size is not written to.
2288 **/
2289 VOID
2290 VirtioFsGetFuseSizeUpdate (
2291 IN EFI_FILE_INFO *Info,
2292 IN EFI_FILE_INFO *NewInfo,
2293 OUT BOOLEAN *Update,
2294 OUT UINT64 *Size
2295 )
2296 {
2297 BOOLEAN IsDirectory;
2298
2299 IsDirectory = (BOOLEAN)((Info->Attribute & EFI_FILE_DIRECTORY) != 0);
2300
2301 if (IsDirectory || (Info->FileSize == NewInfo->FileSize)) {
2302 *Update = FALSE;
2303 return;
2304 }
2305
2306 *Update = TRUE;
2307 *Size = NewInfo->FileSize;
2308 }
2309
2310 /**
2311 Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
2312 call, determine whether updating the last access time and/or the last
2313 modification time of the file is necessary, relative to an EFI_FILE_INFO
2314 object describing the current state of the file.
2315
2316 @param[in] Info The EFI_FILE_INFO describing the current state of
2317 the file. The caller is responsible for populating
2318 Info on input with VirtioFsFuseAttrToEfiFileInfo(),
2319 from the current FUSE attributes of the file. The
2320 Info->Size and Info->FileName members are ignored.
2321
2322 @param[in] NewInfo The EFI_FILE_INFO object received in the
2323 EFI_FILE_PROTOCOL.SetInfo() call.
2324
2325 @param[out] UpdateAtime Set to TRUE on output if the last access time needs
2326 to be updated. Set to FALSE otherwise.
2327
2328 @param[out] UpdateMtime Set to TRUE on output if the last modification time
2329 needs to be updated. Set to FALSE otherwise.
2330
2331 @param[out] Atime If UpdateAtime is set to TRUE, then Atime provides
2332 the last access timestamp to set (as seconds since
2333 the Epoch). Otherwise, Atime is not written to.
2334
2335 @param[out] Mtime If UpdateMtime is set to TRUE, then Mtime provides
2336 the last modification timestamp to set (as seconds
2337 since the Epoch). Otherwise, Mtime is not written
2338 to.
2339
2340 @retval EFI_SUCCESS Output parameters have been set successfully.
2341
2342 @retval EFI_INVALID_PARAMETER At least one of the CreateTime, LastAccessTime
2343 and ModificationTime fields in NewInfo
2344 represents an actual update relative to the
2345 current state of the file (expressed in Info),
2346 but does not satisfy the UEFI spec
2347 requirements on EFI_TIME.
2348
2349 @retval EFI_ACCESS_DENIED NewInfo requests changing both CreateTime and
2350 ModificationTime, but to values that differ
2351 from each other. The Virtio Filesystem device
2352 does not support this.
2353 **/
2354 EFI_STATUS
2355 VirtioFsGetFuseTimeUpdates (
2356 IN EFI_FILE_INFO *Info,
2357 IN EFI_FILE_INFO *NewInfo,
2358 OUT BOOLEAN *UpdateAtime,
2359 OUT BOOLEAN *UpdateMtime,
2360 OUT UINT64 *Atime,
2361 OUT UINT64 *Mtime
2362 )
2363 {
2364 EFI_TIME *Time[3];
2365 EFI_TIME *NewTime[ARRAY_SIZE (Time)];
2366 UINTN Idx;
2367 STATIC CONST EFI_TIME ZeroTime = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2368 BOOLEAN Change[ARRAY_SIZE (Time)];
2369 UINT64 Seconds[ARRAY_SIZE (Time)];
2370
2371 Time[0] = &Info->CreateTime;
2372 Time[1] = &Info->LastAccessTime;
2373 Time[2] = &Info->ModificationTime;
2374 NewTime[0] = &NewInfo->CreateTime;
2375 NewTime[1] = &NewInfo->LastAccessTime;
2376 NewTime[2] = &NewInfo->ModificationTime;
2377
2378 //
2379 // Determine which timestamps differ from the current state. (A zero time
2380 // means "don't update", per UEFI spec.) For each timestamp that's being
2381 // changed, calculate the seconds since the Epoch.
2382 //
2383 for (Idx = 0; Idx < ARRAY_SIZE (Time); Idx++) {
2384 if ((CompareMem (NewTime[Idx], &ZeroTime, sizeof (EFI_TIME)) == 0) ||
2385 (CompareMem (NewTime[Idx], Time[Idx], sizeof (EFI_TIME)) == 0))
2386 {
2387 Change[Idx] = FALSE;
2388 } else {
2389 if (!IsTimeValid (NewTime[Idx])) {
2390 return EFI_INVALID_PARAMETER;
2391 }
2392
2393 Change[Idx] = TRUE;
2394 Seconds[Idx] = EfiTimeToEpoch (NewTime[Idx]);
2395 }
2396 }
2397
2398 //
2399 // If a change is requested for exactly one of CreateTime and
2400 // ModificationTime, we'll change the last modification time. If changes are
2401 // requested for both, and to the same timestamp, we'll similarly update the
2402 // last modification time. If changes are requested for both, but to
2403 // different timestamps, we reject the request.
2404 //
2405 if (Change[0] && Change[2] && (Seconds[0] != Seconds[2])) {
2406 return EFI_ACCESS_DENIED;
2407 }
2408
2409 *UpdateAtime = FALSE;
2410 *UpdateMtime = FALSE;
2411
2412 if (Change[0]) {
2413 *UpdateMtime = TRUE;
2414 *Mtime = Seconds[0];
2415 }
2416
2417 if (Change[1]) {
2418 *UpdateAtime = TRUE;
2419 *Atime = Seconds[1];
2420 }
2421
2422 if (Change[2]) {
2423 *UpdateMtime = TRUE;
2424 *Mtime = Seconds[2];
2425 }
2426
2427 return EFI_SUCCESS;
2428 }
2429
2430 /**
2431 Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
2432 call, determine whether updating the file mode bits of the file is necessary,
2433 relative to an EFI_FILE_INFO object describing the current state of the file.
2434
2435 @param[in] Info The EFI_FILE_INFO describing the current state of the
2436 file. The caller is responsible for populating Info on
2437 input with VirtioFsFuseAttrToEfiFileInfo(), from the
2438 current FUSE attributes of the file. The Info->Size and
2439 Info->FileName members are ignored.
2440
2441 @param[in] NewInfo The EFI_FILE_INFO object received in the
2442 EFI_FILE_PROTOCOL.SetInfo() call.
2443
2444 @param[out] Update Set to TRUE on output if the file mode bits need to be
2445 updated. Set to FALSE otherwise.
2446
2447 @param[out] Mode If Update is set to TRUE, then Mode provides the file
2448 mode bits to set. Otherwise, Mode is not written to.
2449
2450 @retval EFI_SUCCESS Output parameters have been set successfully.
2451
2452 @retval EFI_ACCESS_DENIED NewInfo requests toggling an unknown bit in the
2453 Attribute bitmask.
2454
2455 @retval EFI_ACCESS_DENIED NewInfo requests toggling EFI_FILE_DIRECTORY in
2456 the Attribute bitmask.
2457 **/
2458 EFI_STATUS
2459 VirtioFsGetFuseModeUpdate (
2460 IN EFI_FILE_INFO *Info,
2461 IN EFI_FILE_INFO *NewInfo,
2462 OUT BOOLEAN *Update,
2463 OUT UINT32 *Mode
2464 )
2465 {
2466 UINT64 Toggle;
2467 BOOLEAN IsDirectory;
2468 BOOLEAN IsWriteable;
2469 BOOLEAN WillBeWriteable;
2470
2471 Toggle = Info->Attribute ^ NewInfo->Attribute;
2472 if ((Toggle & ~EFI_FILE_VALID_ATTR) != 0) {
2473 //
2474 // Unknown attribute requested.
2475 //
2476 return EFI_ACCESS_DENIED;
2477 }
2478
2479 if ((Toggle & EFI_FILE_DIRECTORY) != 0) {
2480 //
2481 // EFI_FILE_DIRECTORY cannot be toggled.
2482 //
2483 return EFI_ACCESS_DENIED;
2484 }
2485
2486 IsDirectory = (BOOLEAN)((Info->Attribute & EFI_FILE_DIRECTORY) != 0);
2487 IsWriteable = (BOOLEAN)((Info->Attribute & EFI_FILE_READ_ONLY) == 0);
2488 WillBeWriteable = (BOOLEAN)((NewInfo->Attribute & EFI_FILE_READ_ONLY) == 0);
2489
2490 if (IsWriteable == WillBeWriteable) {
2491 *Update = FALSE;
2492 return EFI_SUCCESS;
2493 }
2494
2495 if (IsDirectory) {
2496 if (WillBeWriteable) {
2497 *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RWXU |
2498 VIRTIO_FS_FUSE_MODE_PERM_RWXG |
2499 VIRTIO_FS_FUSE_MODE_PERM_RWXO);
2500 } else {
2501 *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
2502 VIRTIO_FS_FUSE_MODE_PERM_XUSR |
2503 VIRTIO_FS_FUSE_MODE_PERM_RGRP |
2504 VIRTIO_FS_FUSE_MODE_PERM_XGRP |
2505 VIRTIO_FS_FUSE_MODE_PERM_ROTH |
2506 VIRTIO_FS_FUSE_MODE_PERM_XOTH);
2507 }
2508 } else {
2509 if (WillBeWriteable) {
2510 *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
2511 VIRTIO_FS_FUSE_MODE_PERM_WUSR |
2512 VIRTIO_FS_FUSE_MODE_PERM_RGRP |
2513 VIRTIO_FS_FUSE_MODE_PERM_WGRP |
2514 VIRTIO_FS_FUSE_MODE_PERM_ROTH |
2515 VIRTIO_FS_FUSE_MODE_PERM_WOTH);
2516 } else {
2517 *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
2518 VIRTIO_FS_FUSE_MODE_PERM_RGRP |
2519 VIRTIO_FS_FUSE_MODE_PERM_ROTH);
2520 }
2521 }
2522
2523 *Update = TRUE;
2524 return EFI_SUCCESS;
2525 }