]>
Commit | Line | Data |
---|---|---|
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 | |
29 | STATIC\r | |
30 | EFI_STATUS\r | |
31 | VirtioFsReadConfig (\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 | |
79 | EFI_STATUS\r | |
80 | VirtioFsInit (\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 | |
254 | UnmapQueue:\r | |
255 | VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);\r | |
256 | \r | |
257 | ReleaseQueue:\r | |
258 | VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);\r | |
259 | \r | |
260 | Failed:\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 | |
283 | VOID\r | |
284 | VirtioFsUninit (\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 | |
310 | VOID\r | |
311 | EFIAPI\r | |
312 | VirtioFsExitBoot (\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 | |
397 | EFI_STATUS\r | |
398 | VirtioFsSgListsValidate (\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 | |
537 | EFI_STATUS\r | |
538 | VirtioFsSgListsSubmit (\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 | |
699 | Unmap:\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 | |
789 | EFI_STATUS\r | |
790 | VirtioFsFuseNewRequest (\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 | |
872 | EFI_STATUS\r | |
873 | VirtioFsFuseCheckResponse (\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 | |
990 | EFI_STATUS\r | |
991 | VirtioFsErrnoToEfiStatus (\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 | |
1179 | typedef 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 | |
1204 | STATIC\r | |
1205 | VOID\r | |
1206 | ParserStripSlash (\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 | |
1236 | STATIC\r | |
1237 | VOID\r | |
1238 | ParserCopy (\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 | |
1263 | STATIC\r | |
1264 | VOID\r | |
1265 | ParserRewindDot (\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 | |
1306 | STATIC\r | |
1307 | VOID\r | |
1308 | ParserRewindDotDot (\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 | |
1403 | EFI_STATUS\r | |
1404 | VirtioFsAppendPath (\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 | |
1644 | FreeSanitizedBuffer:\r | |
1645 | if (SanitizedBuffer != NULL) {\r | |
1646 | FreePool (SanitizedBuffer);\r | |
1647 | }\r | |
1648 | \r | |
1649 | FreeBufferToSanitize:\r | |
1650 | if (RhsPath8[0] != '/') {\r | |
1651 | FreePool (BufferToSanitize);\r | |
1652 | }\r | |
1653 | \r | |
1654 | FreeRhsPath8:\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 | |
1700 | EFI_STATUS\r | |
1701 | VirtioFsLookupMostSpecificParentDir (\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 | |
1791 | ForgetNextDirNodeId:\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 | |
1822 | EFI_STATUS\r | |
1823 | VirtioFsGetBasename (\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 | |
1917 | EFI_STATUS\r | |
1918 | VirtioFsComposeRenameDestination (\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 | |
2058 | FreeDestSuffix16:\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 | |
2089 | EFI_STATUS\r | |
2090 | VirtioFsFuseAttrToEfiFileInfo (\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 | |
2218 | EFI_STATUS\r | |
2219 | VirtioFsFuseDirentPlusToEfiFileInfo (\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 | |
2289 | VOID\r | |
2290 | VirtioFsGetFuseSizeUpdate (\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 |
2354 | EFI_STATUS\r | |
2355 | VirtioFsGetFuseTimeUpdates (\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 | |
2458 | EFI_STATUS\r | |
2459 | VirtioFsGetFuseModeUpdate (\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 |