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