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