]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioFsDxe/Helpers.c
OvmfPkg/VirtioFsDxe: call IsTimeValid() before EfiTimeToEpoch()
[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 For a given canonical pathname (as defined at VirtioFsAppendPath()), look up
1596 the NodeId of the most specific parent directory, plus output a pointer to
1597 the last pathname component (which is therefore a direct child of said parent
1598 directory).
1599
1600 The function may only be called after VirtioFsFuseInitSession() returns
1601 successfully and before VirtioFsUninit() is called.
1602
1603 @param[in,out] VirtioFs The Virtio Filesystem device to send FUSE_LOOKUP
1604 and FUSE_FORGET requests to. On output, the FUSE
1605 request counter "VirtioFs->RequestId" will have
1606 been incremented several times.
1607
1608 @param[in,out] Path The canonical pathname (as defined in the
1609 description of VirtioFsAppendPath()) to split.
1610 Path is modified in-place temporarily; however, on
1611 return (successful or otherwise), Path reassumes
1612 its original contents.
1613
1614 @param[out] DirNodeId The NodeId of the most specific parent directory
1615 identified by Path. The caller is responsible for
1616 sending a FUSE_FORGET request to the Virtio
1617 Filesystem device for DirNodeId -- unless
1618 DirNodeId equals VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
1619 --, when DirNodeId's use ends.
1620
1621 @param[out] LastComponent A pointer into Path, pointing at the start of the
1622 last pathname component.
1623
1624 @retval EFI_SUCCESS Splitting successful.
1625
1626 @retval EFI_INVALID_PARAMETER Path is "/".
1627
1628 @retval EFI_ACCESS_DENIED One of the components on Path before the last
1629 is not a directory.
1630
1631 @return Error codes propagated from
1632 VirtioFsFuseLookup() and
1633 VirtioFsFuseAttrToEfiFileInfo().
1634 **/
1635 EFI_STATUS
1636 VirtioFsLookupMostSpecificParentDir (
1637 IN OUT VIRTIO_FS *VirtioFs,
1638 IN OUT CHAR8 *Path,
1639 OUT UINT64 *DirNodeId,
1640 OUT CHAR8 **LastComponent
1641 )
1642 {
1643 UINT64 ParentDirNodeId;
1644 CHAR8 *Slash;
1645 EFI_STATUS Status;
1646 UINT64 NextDirNodeId;
1647
1648 if (AsciiStrCmp (Path, "/") == 0) {
1649 return EFI_INVALID_PARAMETER;
1650 }
1651
1652 ParentDirNodeId = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
1653 Slash = Path;
1654 for (;;) {
1655 CHAR8 *NextSlash;
1656 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
1657 EFI_FILE_INFO FileInfo;
1658
1659 //
1660 // Find the slash (if any) that terminates the next pathname component.
1661 //
1662 NextSlash = AsciiStrStr (Slash + 1, "/");
1663 if (NextSlash == NULL) {
1664 break;
1665 }
1666
1667 //
1668 // Temporarily replace the found slash character with a NUL in-place, for
1669 // easy construction of the single-component filename that we need to look
1670 // up.
1671 //
1672 *NextSlash = '\0';
1673 Status = VirtioFsFuseLookup (VirtioFs, ParentDirNodeId, Slash + 1,
1674 &NextDirNodeId, &FuseAttr);
1675 *NextSlash = '/';
1676
1677 //
1678 // We're done with the directory inode that was the basis for the lookup.
1679 //
1680 if (ParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
1681 VirtioFsFuseForget (VirtioFs, ParentDirNodeId);
1682 }
1683
1684 //
1685 // If we couldn't look up the next *non-final* pathname component, bail.
1686 //
1687 if (EFI_ERROR (Status)) {
1688 return Status;
1689 }
1690
1691 //
1692 // Lookup successful; now check if the next (non-final) component is a
1693 // directory. If not, bail.
1694 //
1695 Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
1696 if (EFI_ERROR (Status)) {
1697 goto ForgetNextDirNodeId;
1698 }
1699 if ((FileInfo.Attribute & EFI_FILE_DIRECTORY) == 0) {
1700 Status = EFI_ACCESS_DENIED;
1701 goto ForgetNextDirNodeId;
1702 }
1703
1704 //
1705 // Advance.
1706 //
1707 ParentDirNodeId = NextDirNodeId;
1708 Slash = NextSlash;
1709 }
1710
1711 //
1712 // ParentDirNodeId corresponds to the last containing directory. The
1713 // remaining single-component filename represents a direct child under that
1714 // directory. Said filename starts at (Slash + 1).
1715 //
1716 *DirNodeId = ParentDirNodeId;
1717 *LastComponent = Slash + 1;
1718 return EFI_SUCCESS;
1719
1720 ForgetNextDirNodeId:
1721 VirtioFsFuseForget (VirtioFs, NextDirNodeId);
1722 return Status;
1723 }
1724
1725 /**
1726 Format the last component of a canonical pathname into a caller-provided
1727 CHAR16 array.
1728
1729 @param[in] Path The canonical pathname (as defined in the
1730 description of VirtioFsAppendPath()) to format
1731 the last component of.
1732
1733 @param[out] Basename If BasenameSize is zero on input, Basename may
1734 be NULL. Otherwise, Basename is allocated by the
1735 caller. On successful return, Basename contains
1736 the last component of Path, formatted as a
1737 NUL-terminated CHAR16 string. When Path is "/"
1738 on input, Basename is L"" on output.
1739
1740 @param[in,out] BasenameSize On input, the number of bytes the caller
1741 provides in Basename. On output, regardless of
1742 return value, the number of bytes required for
1743 formatting Basename, including the terminating
1744 L'\0'.
1745
1746 @retval EFI_SUCCESS Basename has been filled in.
1747
1748 @retval EFI_BUFFER_TOO_SMALL BasenameSize was too small on input; Basename
1749 has not been modified.
1750 **/
1751 EFI_STATUS
1752 VirtioFsGetBasename (
1753 IN CHAR8 *Path,
1754 OUT CHAR16 *Basename OPTIONAL,
1755 IN OUT UINTN *BasenameSize
1756 )
1757 {
1758 UINTN AllocSize;
1759 UINTN LastComponent;
1760 UINTN Idx;
1761 UINTN PathSize;
1762
1763 AllocSize = *BasenameSize;
1764
1765 LastComponent = MAX_UINTN;
1766 for (Idx = 0; Path[Idx] != '\0'; Idx++) {
1767 if (Path[Idx] == '/') {
1768 LastComponent = Idx;
1769 }
1770 }
1771 PathSize = Idx + 1;
1772 ASSERT (LastComponent < MAX_UINTN);
1773 LastComponent++;
1774 *BasenameSize = (PathSize - LastComponent) * sizeof Basename[0];
1775
1776 if (*BasenameSize > AllocSize) {
1777 return EFI_BUFFER_TOO_SMALL;
1778 }
1779
1780 for (Idx = LastComponent; Idx < PathSize; Idx++) {
1781 Basename[Idx - LastComponent] = Path[Idx];
1782 }
1783 return EFI_SUCCESS;
1784 }
1785
1786 /**
1787 Format the destination of a rename/move operation as a dynamically allocated
1788 canonical pathname.
1789
1790 Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
1791 reported through RootEscape, without failing the function call.
1792
1793 @param[in] LhsPath8 The source pathname operand of the rename/move
1794 operation, expressed as a canonical pathname (as
1795 defined in the description of VirtioFsAppendPath()).
1796 The root directory "/" cannot be renamed/moved, and
1797 will be rejected.
1798
1799 @param[in] RhsPath16 The destination pathname operand expressed as a
1800 UEFI-style CHAR16 pathname.
1801
1802 If RhsPath16 starts with a backslash, then RhsPath16
1803 is considered absolute. Otherwise, RhsPath16 is
1804 interpreted relative to the most specific parent
1805 directory found in LhsPath8.
1806
1807 Independently, if RhsPath16 ends with a backslash
1808 (i.e., RhsPath16 is given in the "move into
1809 directory" convenience form), then RhsPath16 is
1810 interpreted with the basename of LhsPath8 appended.
1811 Otherwise, the last pathname component of RhsPath16
1812 is taken as the last pathname component of the
1813 rename/move destination.
1814
1815 An empty RhsPath16 is rejected.
1816
1817 @param[out] ResultPath8 The POSIX-style, canonical format pathname that
1818 leads to the renamed/moved file. After use, the
1819 caller is responsible for freeing ResultPath8.
1820
1821 @param[out] RootEscape Set to TRUE if at least one dot-dot component in
1822 RhsPath16 attempted to escape the root directory;
1823 set to FALSE otherwise.
1824
1825 @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
1826 been output.
1827
1828 @retval EFI_INVALID_PARAMETER LhsPath8 is "/".
1829
1830 @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
1831
1832 @retval EFI_INVALID_PARAMETER RhsPath16 failed the
1833 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1834
1835 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1836
1837 @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
1838 VIRTIO_FS_MAX_PATHNAME_LENGTH check.
1839
1840 @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
1841 falls outside of the printable ASCII set, or
1842 is a forward slash.
1843 **/
1844 EFI_STATUS
1845 VirtioFsComposeRenameDestination (
1846 IN CHAR8 *LhsPath8,
1847 IN CHAR16 *RhsPath16,
1848 OUT CHAR8 **ResultPath8,
1849 OUT BOOLEAN *RootEscape
1850 )
1851 {
1852 //
1853 // Lengths are expressed as numbers of characters (CHAR8 or CHAR16),
1854 // excluding terminating NULs. Sizes are expressed as byte counts, including
1855 // the bytes taken up by terminating NULs.
1856 //
1857 UINTN RhsLen;
1858 UINTN LhsBasename16Size;
1859 EFI_STATUS Status;
1860 UINTN LhsBasenameLen;
1861 UINTN DestSuffix16Size;
1862 CHAR16 *DestSuffix16;
1863 CHAR8 *DestPrefix8;
1864
1865 //
1866 // An empty destination operand for the rename/move operation is not allowed.
1867 //
1868 RhsLen = StrLen (RhsPath16);
1869 if (RhsLen == 0) {
1870 return EFI_INVALID_PARAMETER;
1871 }
1872 //
1873 // Enforce length restriction on RhsPath16.
1874 //
1875 if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
1876 return EFI_INVALID_PARAMETER;
1877 }
1878
1879 //
1880 // Determine the length of the basename of LhsPath8.
1881 //
1882 LhsBasename16Size = 0;
1883 Status = VirtioFsGetBasename (LhsPath8, NULL, &LhsBasename16Size);
1884 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
1885 ASSERT (LhsBasename16Size >= sizeof (CHAR16));
1886 ASSERT (LhsBasename16Size % sizeof (CHAR16) == 0);
1887 LhsBasenameLen = LhsBasename16Size / sizeof (CHAR16) - 1;
1888 if (LhsBasenameLen == 0) {
1889 //
1890 // The root directory cannot be renamed/moved.
1891 //
1892 return EFI_INVALID_PARAMETER;
1893 }
1894
1895 //
1896 // Resolve the "move into directory" convenience form in RhsPath16.
1897 //
1898 if (RhsPath16[RhsLen - 1] == L'\\') {
1899 //
1900 // Append the basename of LhsPath8 as a CHAR16 string to RhsPath16.
1901 //
1902 DestSuffix16Size = RhsLen * sizeof (CHAR16) + LhsBasename16Size;
1903 DestSuffix16 = AllocatePool (DestSuffix16Size);
1904 if (DestSuffix16 == NULL) {
1905 return EFI_OUT_OF_RESOURCES;
1906 }
1907 CopyMem (DestSuffix16, RhsPath16, RhsLen * sizeof (CHAR16));
1908 Status = VirtioFsGetBasename (LhsPath8, DestSuffix16 + RhsLen,
1909 &LhsBasename16Size);
1910 ASSERT_EFI_ERROR (Status);
1911 } else {
1912 //
1913 // Just create a copy of RhsPath16.
1914 //
1915 DestSuffix16Size = (RhsLen + 1) * sizeof (CHAR16);
1916 DestSuffix16 = AllocateCopyPool (DestSuffix16Size, RhsPath16);
1917 if (DestSuffix16 == NULL) {
1918 return EFI_OUT_OF_RESOURCES;
1919 }
1920 }
1921
1922 //
1923 // If the destination operand is absolute, it will be interpreted relative to
1924 // the root directory.
1925 //
1926 // Otherwise (i.e., if the destination operand is relative), then create the
1927 // canonical pathname that the destination operand is interpreted relatively
1928 // to; that is, the canonical pathname of the most specific parent directory
1929 // found in LhsPath8.
1930 //
1931 if (DestSuffix16[0] == L'\\') {
1932 DestPrefix8 = AllocateCopyPool (sizeof "/", "/");
1933 if (DestPrefix8 == NULL) {
1934 Status = EFI_OUT_OF_RESOURCES;
1935 goto FreeDestSuffix16;
1936 }
1937 } else {
1938 UINTN LhsLen;
1939 UINTN DestPrefixLen;
1940
1941 //
1942 // Strip the basename of LhsPath8.
1943 //
1944 LhsLen = AsciiStrLen (LhsPath8);
1945 ASSERT (LhsBasenameLen < LhsLen);
1946 DestPrefixLen = LhsLen - LhsBasenameLen;
1947 ASSERT (LhsPath8[DestPrefixLen - 1] == '/');
1948 //
1949 // If we're not at the root directory, strip the slash too.
1950 //
1951 if (DestPrefixLen > 1) {
1952 DestPrefixLen--;
1953 }
1954 DestPrefix8 = AllocatePool (DestPrefixLen + 1);
1955 if (DestPrefix8 == NULL) {
1956 Status = EFI_OUT_OF_RESOURCES;
1957 goto FreeDestSuffix16;
1958 }
1959 CopyMem (DestPrefix8, LhsPath8, DestPrefixLen);
1960 DestPrefix8[DestPrefixLen] = '\0';
1961 }
1962
1963 //
1964 // Now combine DestPrefix8 and DestSuffix16 into the final canonical
1965 // pathname.
1966 //
1967 Status = VirtioFsAppendPath (DestPrefix8, DestSuffix16, ResultPath8,
1968 RootEscape);
1969
1970 FreePool (DestPrefix8);
1971 //
1972 // Fall through.
1973 //
1974 FreeDestSuffix16:
1975 FreePool (DestSuffix16);
1976
1977 return Status;
1978 }
1979
1980 /**
1981 Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
1982 corresponding fields in EFI_FILE_INFO.
1983
1984 @param[in] FuseAttr The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
1985 convert the relevant fields from.
1986
1987 @param[out] FileInfo The EFI_FILE_INFO structure to modify. Importantly, the
1988 FileInfo->Size and FileInfo->FileName fields are not
1989 overwritten.
1990
1991 @retval EFI_SUCCESS Conversion successful.
1992
1993 @retval EFI_UNSUPPORTED The allocated size of the file is inexpressible in
1994 EFI_FILE_INFO.
1995
1996 @retval EFI_UNSUPPORTED One of the file access times is inexpressible in
1997 EFI_FILE_INFO.
1998
1999 @retval EFI_UNSUPPORTED The file type is inexpressible in EFI_FILE_INFO.
2000
2001 @retval EFI_UNSUPPORTED The file is a regular file that has multiple names
2002 on the host side (i.e., its hard link count is
2003 greater than one).
2004 **/
2005 EFI_STATUS
2006 VirtioFsFuseAttrToEfiFileInfo (
2007 IN VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
2008 OUT EFI_FILE_INFO *FileInfo
2009 )
2010 {
2011 UINT64 EpochTime[3];
2012 EFI_TIME *ConvertedTime[ARRAY_SIZE (EpochTime)];
2013 UINTN Idx;
2014
2015 FileInfo->FileSize = FuseAttr->Size;
2016
2017 //
2018 // The unit for FuseAttr->Blocks is 512B.
2019 //
2020 if (FuseAttr->Blocks >= BIT55) {
2021 return EFI_UNSUPPORTED;
2022 }
2023 FileInfo->PhysicalSize = LShiftU64 (FuseAttr->Blocks, 9);
2024
2025 //
2026 // Convert the timestamps. File creation time is not tracked by the Virtio
2027 // Filesystem device, so set FileInfo->CreateTime from FuseAttr->Mtime as
2028 // well.
2029 //
2030 EpochTime[0] = FuseAttr->Mtime;
2031 EpochTime[1] = FuseAttr->Atime;
2032 EpochTime[2] = FuseAttr->Mtime;
2033 ConvertedTime[0] = &FileInfo->CreateTime;
2034 ConvertedTime[1] = &FileInfo->LastAccessTime;
2035 ConvertedTime[2] = &FileInfo->ModificationTime;
2036
2037 for (Idx = 0; Idx < ARRAY_SIZE (EpochTime); Idx++) {
2038 //
2039 // EpochToEfiTime() takes a UINTN for seconds.
2040 //
2041 if (EpochTime[Idx] > MAX_UINTN) {
2042 return EFI_UNSUPPORTED;
2043 }
2044 //
2045 // Set the following fields in the converted time: Year, Month, Day, Hour,
2046 // Minute, Second, Nanosecond.
2047 //
2048 EpochToEfiTime ((UINTN)EpochTime[Idx], ConvertedTime[Idx]);
2049 //
2050 // The times are all expressed in UTC. Consequently, they are not affected
2051 // by daylight saving.
2052 //
2053 ConvertedTime[Idx]->TimeZone = 0;
2054 ConvertedTime[Idx]->Daylight = 0;
2055 //
2056 // Clear the padding fields.
2057 //
2058 ConvertedTime[Idx]->Pad1 = 0;
2059 ConvertedTime[Idx]->Pad2 = 0;
2060 }
2061
2062 //
2063 // Set the attributes.
2064 //
2065 switch (FuseAttr->Mode & VIRTIO_FS_FUSE_MODE_TYPE_MASK) {
2066 case VIRTIO_FS_FUSE_MODE_TYPE_DIR:
2067 FileInfo->Attribute = EFI_FILE_DIRECTORY;
2068 break;
2069 case VIRTIO_FS_FUSE_MODE_TYPE_REG:
2070 FileInfo->Attribute = 0;
2071 break;
2072 default:
2073 //
2074 // Other file types are not supported.
2075 //
2076 return EFI_UNSUPPORTED;
2077 }
2078 //
2079 // Report the regular file or directory as read-only if all classes lack
2080 // write permission.
2081 //
2082 if ((FuseAttr->Mode & (VIRTIO_FS_FUSE_MODE_PERM_WUSR |
2083 VIRTIO_FS_FUSE_MODE_PERM_WGRP |
2084 VIRTIO_FS_FUSE_MODE_PERM_WOTH)) == 0) {
2085 FileInfo->Attribute |= EFI_FILE_READ_ONLY;
2086 }
2087
2088 //
2089 // A hard link count greater than 1 is not supported for regular files.
2090 //
2091 if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0 && FuseAttr->Nlink > 1) {
2092 return EFI_UNSUPPORTED;
2093 }
2094
2095 return EFI_SUCCESS;
2096 }
2097
2098 /**
2099 Convert a VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE filename to an EFI_FILE_INFO
2100 filename.
2101
2102 @param[in] FuseDirent The VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE object to
2103 convert the filename byte array from. The caller is
2104 responsible for ensuring that FuseDirent->Namelen
2105 describe valid storage.
2106
2107 @param[in,out] FileInfo The EFI_FILE_INFO structure to modify. On input, the
2108 caller is responsible for setting FileInfo->Size
2109 according to the allocated size. On successful
2110 return, FileInfo->Size is reduced to reflect the
2111 filename converted into FileInfo->FileName.
2112 FileInfo->FileName is set from the filename byte
2113 array that directly follows the FuseDirent header
2114 object. Fields other than FileInfo->Size and
2115 FileInfo->FileName are not modified.
2116
2117 @retval EFI_SUCCESS Conversion successful.
2118
2119 @retval EFI_INVALID_PARAMETER VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE()
2120 returns zero for FuseDirent->Namelen.
2121
2122 @retval EFI_BUFFER_TOO_SMALL On input, FileInfo->Size does not provide
2123 enough room for converting the filename byte
2124 array from FuseDirent.
2125
2126 @retval EFI_UNSUPPORTED The FuseDirent filename byte array contains a
2127 byte that falls outside of the printable ASCII
2128 range, or is a forward slash or a backslash.
2129 **/
2130 EFI_STATUS
2131 VirtioFsFuseDirentPlusToEfiFileInfo (
2132 IN VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *FuseDirent,
2133 IN OUT EFI_FILE_INFO *FileInfo
2134 )
2135 {
2136 UINTN DirentSize;
2137 UINTN FileInfoSize;
2138 UINT8 *DirentName;
2139 UINT32 Idx;
2140
2141 DirentSize = VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (FuseDirent->Namelen);
2142 if (DirentSize == 0) {
2143 return EFI_INVALID_PARAMETER;
2144 }
2145 //
2146 // We're now safe from overflow in the calculation below.
2147 //
2148 FileInfoSize = (OFFSET_OF (EFI_FILE_INFO, FileName) +
2149 ((UINTN)FuseDirent->Namelen + 1) * sizeof (CHAR16));
2150 if (FileInfoSize > FileInfo->Size) {
2151 return EFI_BUFFER_TOO_SMALL;
2152 }
2153
2154 //
2155 // Convert the name.
2156 //
2157 DirentName = (UINT8 *)(FuseDirent + 1);
2158 for (Idx = 0; Idx < FuseDirent->Namelen; Idx++) {
2159 UINT8 NameByte;
2160
2161 NameByte = DirentName[Idx];
2162 if (NameByte < 0x20 || NameByte > 0x7E ||
2163 NameByte == '/' || NameByte == '\\') {
2164 return EFI_UNSUPPORTED;
2165 }
2166 FileInfo->FileName[Idx] = (CHAR16)NameByte;
2167 }
2168 FileInfo->FileName[Idx++] = L'\0';
2169 //
2170 // Set the (possibly reduced) size.
2171 //
2172 FileInfo->Size = FileInfoSize;
2173
2174 return EFI_SUCCESS;
2175 }
2176
2177 /**
2178 Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
2179 call, determine whether updating the size of the file is necessary, relative
2180 to an EFI_FILE_INFO object describing the current state of the file.
2181
2182 @param[in] Info The EFI_FILE_INFO describing the current state of the
2183 file. The caller is responsible for populating Info on
2184 input with VirtioFsFuseAttrToEfiFileInfo(), from the
2185 current FUSE attributes of the file. The Info->Size and
2186 Info->FileName members are ignored.
2187
2188 @param[in] NewInfo The EFI_FILE_INFO object received in the
2189 EFI_FILE_PROTOCOL.SetInfo() call.
2190
2191 @param[out] Update Set to TRUE on output if the file size needs to be
2192 updated. Set to FALSE otherwise.
2193
2194 @param[out] Size If Update is set to TRUE, then Size provides the new file
2195 size to set. Otherwise, Size is not written to.
2196 **/
2197 VOID
2198 VirtioFsGetFuseSizeUpdate (
2199 IN EFI_FILE_INFO *Info,
2200 IN EFI_FILE_INFO *NewInfo,
2201 OUT BOOLEAN *Update,
2202 OUT UINT64 *Size
2203 )
2204 {
2205 BOOLEAN IsDirectory;
2206
2207 IsDirectory = (BOOLEAN)((Info->Attribute & EFI_FILE_DIRECTORY) != 0);
2208
2209 if (IsDirectory || Info->FileSize == NewInfo->FileSize) {
2210 *Update = FALSE;
2211 return;
2212 }
2213 *Update = TRUE;
2214 *Size = NewInfo->FileSize;
2215 }
2216
2217 /**
2218 Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
2219 call, determine whether updating the last access time and/or the last
2220 modification time of the file is necessary, relative to an EFI_FILE_INFO
2221 object describing the current state of the file.
2222
2223 @param[in] Info The EFI_FILE_INFO describing the current state of
2224 the file. The caller is responsible for populating
2225 Info on input with VirtioFsFuseAttrToEfiFileInfo(),
2226 from the current FUSE attributes of the file. The
2227 Info->Size and Info->FileName members are ignored.
2228
2229 @param[in] NewInfo The EFI_FILE_INFO object received in the
2230 EFI_FILE_PROTOCOL.SetInfo() call.
2231
2232 @param[out] UpdateAtime Set to TRUE on output if the last access time needs
2233 to be updated. Set to FALSE otherwise.
2234
2235 @param[out] UpdateMtime Set to TRUE on output if the last modification time
2236 needs to be updated. Set to FALSE otherwise.
2237
2238 @param[out] Atime If UpdateAtime is set to TRUE, then Atime provides
2239 the last access timestamp to set (as seconds since
2240 the Epoch). Otherwise, Atime is not written to.
2241
2242 @param[out] Mtime If UpdateMtime is set to TRUE, then Mtime provides
2243 the last modification timestamp to set (as seconds
2244 since the Epoch). Otherwise, Mtime is not written
2245 to.
2246
2247 @retval EFI_SUCCESS Output parameters have been set successfully.
2248
2249 @retval EFI_INVALID_PARAMETER At least one of the CreateTime, LastAccessTime
2250 and ModificationTime fields in NewInfo
2251 represents an actual update relative to the
2252 current state of the file (expressed in Info),
2253 but does not satisfy the UEFI spec
2254 requirements on EFI_TIME.
2255
2256 @retval EFI_ACCESS_DENIED NewInfo requests changing both CreateTime and
2257 ModificationTime, but to values that differ
2258 from each other. The Virtio Filesystem device
2259 does not support this.
2260 **/
2261 EFI_STATUS
2262 VirtioFsGetFuseTimeUpdates (
2263 IN EFI_FILE_INFO *Info,
2264 IN EFI_FILE_INFO *NewInfo,
2265 OUT BOOLEAN *UpdateAtime,
2266 OUT BOOLEAN *UpdateMtime,
2267 OUT UINT64 *Atime,
2268 OUT UINT64 *Mtime
2269 )
2270 {
2271 EFI_TIME *Time[3];
2272 EFI_TIME *NewTime[ARRAY_SIZE (Time)];
2273 UINTN Idx;
2274 STATIC CONST EFI_TIME ZeroTime = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2275 BOOLEAN Change[ARRAY_SIZE (Time)];
2276 UINT64 Seconds[ARRAY_SIZE (Time)];
2277
2278 Time[0] = &Info->CreateTime;
2279 Time[1] = &Info->LastAccessTime;
2280 Time[2] = &Info->ModificationTime;
2281 NewTime[0] = &NewInfo->CreateTime;
2282 NewTime[1] = &NewInfo->LastAccessTime;
2283 NewTime[2] = &NewInfo->ModificationTime;
2284
2285 //
2286 // Determine which timestamps differ from the current state. (A zero time
2287 // means "don't update", per UEFI spec.) For each timestamp that's being
2288 // changed, calculate the seconds since the Epoch.
2289 //
2290 for (Idx = 0; Idx < ARRAY_SIZE (Time); Idx++) {
2291 if (CompareMem (NewTime[Idx], &ZeroTime, sizeof (EFI_TIME)) == 0 ||
2292 CompareMem (NewTime[Idx], Time[Idx], sizeof (EFI_TIME)) == 0) {
2293 Change[Idx] = FALSE;
2294 } else {
2295 if (!IsTimeValid (NewTime[Idx])) {
2296 return EFI_INVALID_PARAMETER;
2297 }
2298 Change[Idx] = TRUE;
2299 Seconds[Idx] = EfiTimeToEpoch (NewTime[Idx]);
2300 }
2301 }
2302
2303 //
2304 // If a change is requested for exactly one of CreateTime and
2305 // ModificationTime, we'll change the last modification time. If changes are
2306 // requested for both, and to the same timestamp, we'll similarly update the
2307 // last modification time. If changes are requested for both, but to
2308 // different timestamps, we reject the request.
2309 //
2310 if (Change[0] && Change[2] && Seconds[0] != Seconds[2]) {
2311 return EFI_ACCESS_DENIED;
2312 }
2313
2314 *UpdateAtime = FALSE;
2315 *UpdateMtime = FALSE;
2316
2317 if (Change[0]) {
2318 *UpdateMtime = TRUE;
2319 *Mtime = Seconds[0];
2320 }
2321 if (Change[1]) {
2322 *UpdateAtime = TRUE;
2323 *Atime = Seconds[1];
2324 }
2325 if (Change[2]) {
2326 *UpdateMtime = TRUE;
2327 *Mtime = Seconds[2];
2328 }
2329
2330 return EFI_SUCCESS;
2331 }
2332
2333 /**
2334 Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
2335 call, determine whether updating the file mode bits of the file is necessary,
2336 relative to an EFI_FILE_INFO object describing the current state of the file.
2337
2338 @param[in] Info The EFI_FILE_INFO describing the current state of the
2339 file. The caller is responsible for populating Info on
2340 input with VirtioFsFuseAttrToEfiFileInfo(), from the
2341 current FUSE attributes of the file. The Info->Size and
2342 Info->FileName members are ignored.
2343
2344 @param[in] NewInfo The EFI_FILE_INFO object received in the
2345 EFI_FILE_PROTOCOL.SetInfo() call.
2346
2347 @param[out] Update Set to TRUE on output if the file mode bits need to be
2348 updated. Set to FALSE otherwise.
2349
2350 @param[out] Mode If Update is set to TRUE, then Mode provides the file
2351 mode bits to set. Otherwise, Mode is not written to.
2352
2353 @retval EFI_SUCCESS Output parameters have been set successfully.
2354
2355 @retval EFI_ACCESS_DENIED NewInfo requests toggling an unknown bit in the
2356 Attribute bitmask.
2357
2358 @retval EFI_ACCESS_DENIED NewInfo requests toggling EFI_FILE_DIRECTORY in
2359 the Attribute bitmask.
2360 **/
2361 EFI_STATUS
2362 VirtioFsGetFuseModeUpdate (
2363 IN EFI_FILE_INFO *Info,
2364 IN EFI_FILE_INFO *NewInfo,
2365 OUT BOOLEAN *Update,
2366 OUT UINT32 *Mode
2367 )
2368 {
2369 UINT64 Toggle;
2370 BOOLEAN IsDirectory;
2371 BOOLEAN IsWriteable;
2372 BOOLEAN WillBeWriteable;
2373
2374 Toggle = Info->Attribute ^ NewInfo->Attribute;
2375 if ((Toggle & ~EFI_FILE_VALID_ATTR) != 0) {
2376 //
2377 // Unknown attribute requested.
2378 //
2379 return EFI_ACCESS_DENIED;
2380 }
2381 if ((Toggle & EFI_FILE_DIRECTORY) != 0) {
2382 //
2383 // EFI_FILE_DIRECTORY cannot be toggled.
2384 //
2385 return EFI_ACCESS_DENIED;
2386 }
2387
2388 IsDirectory = (BOOLEAN)((Info->Attribute & EFI_FILE_DIRECTORY) != 0);
2389 IsWriteable = (BOOLEAN)((Info->Attribute & EFI_FILE_READ_ONLY) == 0);
2390 WillBeWriteable = (BOOLEAN)((NewInfo->Attribute & EFI_FILE_READ_ONLY) == 0);
2391
2392 if (IsWriteable == WillBeWriteable) {
2393 *Update = FALSE;
2394 return EFI_SUCCESS;
2395 }
2396
2397 if (IsDirectory) {
2398 if (WillBeWriteable) {
2399 *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RWXU |
2400 VIRTIO_FS_FUSE_MODE_PERM_RWXG |
2401 VIRTIO_FS_FUSE_MODE_PERM_RWXO);
2402 } else {
2403 *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
2404 VIRTIO_FS_FUSE_MODE_PERM_XUSR |
2405 VIRTIO_FS_FUSE_MODE_PERM_RGRP |
2406 VIRTIO_FS_FUSE_MODE_PERM_XGRP |
2407 VIRTIO_FS_FUSE_MODE_PERM_ROTH |
2408 VIRTIO_FS_FUSE_MODE_PERM_XOTH);
2409 }
2410 } else {
2411 if (WillBeWriteable) {
2412 *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
2413 VIRTIO_FS_FUSE_MODE_PERM_WUSR |
2414 VIRTIO_FS_FUSE_MODE_PERM_RGRP |
2415 VIRTIO_FS_FUSE_MODE_PERM_WGRP |
2416 VIRTIO_FS_FUSE_MODE_PERM_ROTH |
2417 VIRTIO_FS_FUSE_MODE_PERM_WOTH);
2418 } else {
2419 *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
2420 VIRTIO_FS_FUSE_MODE_PERM_RGRP |
2421 VIRTIO_FS_FUSE_MODE_PERM_ROTH);
2422 }
2423 }
2424 *Update = TRUE;
2425 return EFI_SUCCESS;
2426 }